<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0"><channel><title>qiaofugui</title><link>https://blog.qiaofugui.cn</link><atom:link href="https://blog.qiaofugui.cn/rss.xml" rel="self" type="application/rss+xml"/><description>qiaofugui</description><generator>Halo v2.22.3</generator><language>zh-cn</language><image><url>https://blog.qiaofugui.cn/wp-content/uploads/2024/05/cropped-logo.png</url><title>qiaofugui</title><link>https://blog.qiaofugui.cn</link></image><lastBuildDate>Wed, 20 May 2026 23:14:52 GMT</lastBuildDate><item><title><![CDATA[UniApp 踩坑 持续记录]]></title><link>https://blog.qiaofugui.cn/archives/UniApp%20%E8%B8%A9%E5%9D%91%20%E6%8C%81%E7%BB%AD%E8%AE%B0%E5%BD%95</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=UniApp%20%E8%B8%A9%E5%9D%91%20%E6%8C%81%E7%BB%AD%E8%AE%B0%E5%BD%95&amp;url=/archives/UniApp%20%E8%B8%A9%E5%9D%91%20%E6%8C%81%E7%BB%AD%E8%AE%B0%E5%BD%95" width="1" height="1" alt="" style="opacity:0;">
<ol>
 <li>
  <p style=""><code>uni-popup</code> 里面的 <code>uni-popup-dialog</code> 组件属性是 <code>mode="input"</code> 时，在 H5 中无异常，但是在微信小程序中只要打开的页面使用了<code>uni-popup-dialog</code> 组件并且 <code>mode="input"</code>，IOS端的软键盘就会自动弹起！！！解决： <code>uni-popup-dialog</code> 使用 <code>v-if</code> 控制。</p>
 </li>
 <li>
  <p style=""><span style="font-size: 16px; color: rgb(37, 41, 51)">uni-app 编译到微信小程序时，默认存在</span><strong>组件样式隔离</strong><span style="font-size: 16px; color: rgb(37, 41, 51)">，导致样式穿透（深度选择器）失效</span>
   <br>
   <span style="font-size: 16px; color: rgb(37, 41, 51)">解决方法：在组件的 </span><code>script</code><span style="font-size: 16px; color: rgb(37, 41, 51)"> 部分添加 </span><code>options: { styleIsolation: 'shared' }</code><span style="font-size: 16px; color: rgb(37, 41, 51)">，解除样式隔离：</span>
   <br></p>
 </li>
</ol>
<pre><code>// Vue3
defineOptions({
  options: {
    styleIsolation: 'shared' // 允许样式穿透
  }
})

// Vue2
export default {
  options: {
    styleIsolation: 'shared' // 允许样式穿透
  },
  data() { return {} },
  methods: {}
}</code></pre>
<p style="">
 <br>
</p>
<p style=""></p>
<p style=""></p>]]></description><guid isPermaLink="false">/archives/UniApp%20%E8%B8%A9%E5%9D%91%20%E6%8C%81%E7%BB%AD%E8%AE%B0%E5%BD%95</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Tue, 12 Aug 2025 09:11:19 GMT</pubDate></item><item><title><![CDATA[快速管理 Storage 浏览器插件 批量编辑 复制 搜索]]></title><link>https://blog.qiaofugui.cn/archives/%E5%BF%AB%E9%80%9F%E7%AE%A1%E7%90%86%20Storage%20%E6%B5%8F%E8%A7%88%E5%99%A8%E6%8F%92%E4%BB%B6%20%E6%89%B9%E9%87%8F%E7%BC%96%E8%BE%91%20%E5%A4%8D%E5%88%B6%20%E6%90%9C%E7%B4%A2</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=%E5%BF%AB%E9%80%9F%E7%AE%A1%E7%90%86%20Storage%20%E6%B5%8F%E8%A7%88%E5%99%A8%E6%8F%92%E4%BB%B6%20%E6%89%B9%E9%87%8F%E7%BC%96%E8%BE%91%20%E5%A4%8D%E5%88%B6%20%E6%90%9C%E7%B4%A2&amp;url=/archives/%E5%BF%AB%E9%80%9F%E7%AE%A1%E7%90%86%20Storage%20%E6%B5%8F%E8%A7%88%E5%99%A8%E6%8F%92%E4%BB%B6%20%E6%89%B9%E9%87%8F%E7%BC%96%E8%BE%91%20%E5%A4%8D%E5%88%B6%20%E6%90%9C%E7%B4%A2" width="1" height="1" alt="" style="opacity:0;">
<p style="text-align: center"><img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=%2Fupload%2Fpreview-nDqX.png&amp;size=m" width="100%" height="100%" style="display: inline-block"></p>
<p style="text-align: center"></p>
<p style="text-align: center"></p>
<p style="">✨ 在介绍功能之前，先看看这款插件解决了哪些实际开发痛点：</p>
<ul>
 <li>
  <p style=""><strong>批量操作效率</strong>：一次性完成多个数据项的增删改查，告别重复劳动</p>
 </li>
 <li>
  <p style=""><strong>复杂JSON处理</strong>：内置强大编辑器，轻松处理嵌套对象和数组</p>
 </li>
 <li>
  <p style=""><strong>现代化界面</strong>：直观易用的UI设计，降低学习成本</p>
 </li>
</ul>
<h2 style="" id="%E2%9C%A8-%E6%A0%B8%E5%BF%83%E5%8A%9F%E8%83%BD%E4%BA%AE%E7%82%B9">✨ 核心功能亮点</h2>
<h3 style="" id="%F0%9F%93%8A-%E5%AD%98%E5%82%A8%E6%94%AF%E6%8C%81">📊 存储支持</h3>
<p style="">Storage Manager支持localStorage存储类型：</p>
<ul>
 <li>
  <p style=""><strong>持久化存储</strong>：完整管理localStorage数据，支持页面刷新后的数据保留</p>
 </li>
 <li>
  <p style=""><strong>自动检测</strong>：打开插件即自动读取当前页面的所有存储数据，无需手动刷新</p>
 </li>
</ul>
<h3 style="" id="%F0%9F%94%8D-%E5%BC%BA%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%93%8D%E4%BD%9C%EF%BC%8C%E5%BE%97%E5%BF%83%E5%BA%94%E6%89%8B">🔍 强大数据操作，得心应手</h3>
<p style="">插件提供了全方位的数据管理功能，满足日常开发的各种需求：</p>
<ul>
 <li>
  <p style=""><strong>实时搜索过滤</strong>：支持按键名或值内容快速定位数据，支持模糊匹配</p>
 </li>
 <li>
  <p style=""><strong>直观编辑界面</strong>：点击即编辑，无需复杂操作，修改立即生效</p>
 </li>
 <li>
  <p style=""><strong>批量管理</strong>：一键清空所有数据，或批量编辑多个数据项</p>
 </li>
 <li>
  <p style=""><strong>安全操作</strong>：所有修改都有即时反馈，防止误操作导致的数据丢失</p>
 </li>
</ul>
<h3 style="" id="%F0%9F%93%9D-json%E7%BC%96%E8%BE%91%EF%BC%8C%E5%A4%8D%E6%9D%82%E6%95%B0%E6%8D%AE%E8%BD%BB%E6%9D%BE%E5%BA%94%E5%AF%B9">📝 JSON编辑，复杂数据轻松应对</h3>
<p style="">针对开发中常见的复杂JSON数据，Storage Manager内置了编辑器：</p>
<ul>
 <li>
  <p style=""><strong>多模式编辑</strong>：支持树形、代码、表单、文本和预览五种编辑模式</p>
 </li>
 <li>
  <p style=""><strong>智能格式化</strong>：自动格式化JSON数据，缩进清晰，结构一目了然</p>
 </li>
 <li>
  <p style=""><strong>实时验证</strong>：即时检测JSON格式错误，提供明确的错误提示</p>
 </li>
</ul>
<h3 style="" id="%F0%9F%93%8B-%E9%AB%98%E6%95%88%E6%95%B0%E6%8D%AE%E4%BA%A4%E6%8D%A2%EF%BC%8C%E6%97%A0%E7%BC%9D%E8%A1%94%E6%8E%A5%E5%B7%A5%E4%BD%9C%E6%B5%81">📋 高效数据交换，无缝衔接工作流</h3>
<p style="">开发过程中经常需要在不同环境间复制数据，Storage Manager让这一过程变得无比简单：</p>
<ul>
 <li>
  <p style=""><strong>一键复制</strong>：单个数据项或全部数据一键复制为标准JSON格式</p>
 </li>
 <li>
  <p style=""><strong>智能粘贴</strong>：支持从剪贴板粘贴JSON数据，自动解析并添加</p>
 </li>
 <li>
  <p style=""><strong>重复检测</strong>：粘贴时自动检测重复键名，防止意外覆盖</p>
 </li>
 <li>
  <p style=""><strong>批量修改</strong>：支持一次性修改多个键值对，快速构建测试数据</p>
 </li>
</ul>
<h2 style="" id="%F0%9F%93%A6-%E5%AE%89%E8%A3%85%E4%B8%8E%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97">📦 安装与使用指南</h2>
<h3 style="" id="%E6%94%AF%E6%8C%81%E6%B5%8F%E8%A7%88%E5%99%A8">支持浏览器</h3>
<p style="">Storage Manager兼容主流现代浏览器：</p>
<ul>
 <li>
  <p style="">- Google Chrome</p>
 </li>
 <li>
  <p style="">- Microsoft Edge</p>
 </li>
</ul>
<h3 style="" id="%E5%BF%AB%E9%80%9F%E5%AE%89%E8%A3%85%E6%AD%A5%E9%AA%A4">快速安装步骤</h3>
<p style=""><a href="https://microsoftedge.microsoft.com/addons/detail/storage-manager/gkcakimmaopjpnpmdpcifiaigpolinpk" target="_blank" rel="">已上架 Edge 扩展商店</a></p>
<ol>
 <li>
  <p style=""><strong>获取扩展文件 </strong><a href="https://wwfo.lanzoue.com/iINB733wge3c">StorageManage.zip - 蓝奏云</a></p>
 </li>
 <li>
  <p style=""><strong>在浏览器中安装</strong></p>
  <p style="">- <strong>Chrome/Edge</strong>：</p>
  <p style="">- 打开扩展管理页面（chrome://extensions/ 或 edge://extensions/）</p>
  <p style="">- 开启"开发者模式"</p>
  <p style="">- 将压缩包拖到"扩展"页面</p>
 </li>
</ol>
<h3 style="" id="%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8%E6%B5%81%E7%A8%8B">基本使用流程</h3>
<p style="">1. 在需要调试的网页上点击Storage Manager扩展图标</p>
<p style="">2. 在打开的面板中选择要管理的存储类型（localStorage）</p>
<p style="">3. 浏览、搜索、编辑或删除数据</p>
<p style=""></p>]]></description><guid isPermaLink="false">/archives/%E5%BF%AB%E9%80%9F%E7%AE%A1%E7%90%86%20Storage%20%E6%B5%8F%E8%A7%88%E5%99%A8%E6%8F%92%E4%BB%B6%20%E6%89%B9%E9%87%8F%E7%BC%96%E8%BE%91%20%E5%A4%8D%E5%88%B6%20%E6%90%9C%E7%B4%A2</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Thu, 3 Jul 2025 09:03:36 GMT</pubDate></item><item><title><![CDATA[NuxtJS、NextJS、NestJS - 区分区别]]></title><link>https://blog.qiaofugui.cn/archives/nuxtjs%E3%80%81nextjs%E3%80%81nestjs-%E5%8C%BA%E5%88%86%E5%8C%BA%E5%88%AB</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=NuxtJS%E3%80%81NextJS%E3%80%81NestJS%20-%20%E5%8C%BA%E5%88%86%E5%8C%BA%E5%88%AB&amp;url=/archives/nuxtjs%E3%80%81nextjs%E3%80%81nestjs-%E5%8C%BA%E5%88%86%E5%8C%BA%E5%88%AB" width="1" height="1" alt="" style="opacity:0;"><!-- wp:heading -->
<h2 class="wp-block-heading">NuxtJS - 构建优雅的 Vue 应用</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Nuxt.js 是一个用于构建优雅的 Vue.js 应用程序的框架。它专注于提供一种无缝的开发体验，同时允许你在服务器端渲染 Vue 应用。Nuxt.js 提供了以下功能：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><strong>服务器端渲染（SSR）</strong>： Nuxt.js 允许在服务器端渲染 Vue 应用，提供更好的性能和 SEO。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>自动生成路由</strong>： Nuxt.js 可以根据项目结构自动生成路由配置，减少了手动配置的工作。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>插件和模块</strong>： 你可以使用插件和模块来扩展 Nuxt.js 应用的功能。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>异步数据加载</strong>： Nuxt.js 支持在页面加载之前异步获取数据，以提供更快的初始加载速度。</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:paragraph -->
<p>官方网址：<code>https://nuxtjs.org</code></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">NextJS - 构建现代化的 React 应用</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Next.js 是一个用于构建现代化 React 应用程序的框架。它强调性能、开发体验和 SEO 优化，是许多 React 开发者的首选。Next.js 提供了许多功能，包括：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><strong>服务器渲染(SSR)</strong>： Next.js 允许在服务器端渲染 React 应用程序，从而提高了应用程序的性能和 SEO。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>静态网站生成(SSG)</strong>： 你可以使用 Next.js 生成静态网站，以提供更快的加载速度和更好的用户体验。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>热模块替换(HMR)</strong>： Next.js 支持热模块替换，使开发者可以在不刷新页面的情况下实时预览更改。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>路由和数据预取</strong>： Next.js 提供了简单易用的路由系统，并支持数据预取以优化页面加载。</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:paragraph -->
<p>官方网址：<code>https://nextjs.org</code></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">NestJS - 构建高效的后端应用</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>NestJS 是一个用于构建高效、可扩展后端应用的框架。它基于 Node.js 和 TypeScript，并受到 Angular 的启发。NestJS 提供了以下功能：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><strong>模块化架构</strong>： NestJS 鼓励使用模块来组织代码，使应用程序更易于维护和扩展。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>依赖注入</strong>： NestJS 使用依赖注入来管理组件之间的依赖关系，提高了代码的可测试性和可维护性。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>中间件和管道</strong>： 你可以使用中间件和管道来处理请求、验证数据以及执行其他任务。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>强大的路由系统</strong>： NestJS 提供了强大的路由系统，使你能够轻松定义 API 端点和处理不同的 HTTP 请求。</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:paragraph -->
<p>官方网址：<code>https://nestjs.com</code></p>
<!-- /wp:paragraph -->]]></description><guid isPermaLink="false">/archives/nuxtjs%E3%80%81nextjs%E3%80%81nestjs-%E5%8C%BA%E5%88%86%E5%8C%BA%E5%88%AB</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Mon, 1 Jul 2024 01:59:10 GMT</pubDate></item><item><title><![CDATA[git-repo 简单使用]]></title><link>https://blog.qiaofugui.cn/archives/git-repo-%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=git-repo%20%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8&amp;url=/archives/git-repo-%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8" width="1" height="1" alt="" style="opacity:0;"><!-- wp:paragraph -->
<p>[文档](<a href="https://git-repo.info/zh_cn/docs/getting-started/quickstart/">快速开始 – 阿里git客户端工具 (git-repo.info)</a>)</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>把下载的文件放到安装 <code>git</code> 目录下的 <code>cmd</code> 文件夹里
 <br>
 提PR到云效</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>第一步 基于主分支创建 功能/缺陷 分支，编写完成正常 <code>git add .</code> 和 <code>git commit -m ''</code></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>第二步 完成第一步后，切换到需要提PR的分支，先把 “基于主分支创建 功能/缺陷 分支” 合并到需要提PR的分支，正常 <code>git add .</code> 和 <code>git commit -m ''</code> 后，<strong>执行 <code>git pr</code></strong>，就可以在云效上查看合并请求了</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><a href="https://help.aliyun.com/document_detail/153798.html?spm=a2c4g.312681.0.0.33172961w8Vfst">如何通过git-repo提交代码评审_云效(Apsara Devops)-阿里云帮助中心 (aliyun.com)</a></p>
<!-- /wp:paragraph -->]]></description><guid isPermaLink="false">/archives/git-repo-%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Tue, 21 May 2024 07:24:11 GMT</pubDate></item><item><title><![CDATA[环境变量 &amp; Git]]></title><link>https://blog.qiaofugui.cn/archives/%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F-git</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%20%26amp%3B%20Git&amp;url=/archives/%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F-git" width="1" height="1" alt="" style="opacity:0;"><!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">环境变量</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>实际开发中，有多种环境，如：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>development</code> 模式用于 <code>vue-cli-service serve</code></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>production</code> 模式用于 <code>vue-cli-service build</code> 和 <code>vue-cli-service test:e2e</code></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>test</code> 模式用于 <code>vue-cli-service test:unit</code>
  <br>
  ##注意点：</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>一个模式可以包含多个环境变量</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>每个模式都会将环境变量中 <code>NODE_ENV</code> 的值设置为模式的名称</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>可以通过为 <code>.env</code> 文件增加后缀来设置某个模式下特有的环境变量</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>为一个特定模式准备的环境文件（如：<code>.env.production</code>）将会比一般的环境文件（如：<code>.env</code>）拥有更高的优先级</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>.env                # 在所有环境中被载入
.env.local          # 在所有环境中被载入,但会被 git 忽略
.env.[mode]         # 只在特定的模式中被载入,优先级高于 .env 和 .env.local
.env.[mode].local   # 只在特定的模式中被载入,但会被 git 忽略，优先级高于 .env 和 .env.local</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>    // .env.development 文件

    NODE_ENV=devlopment
    VUE_APP_BASE_URL='http://localhost'

    // 使用 process.env.VUE_APP_BASE_URL</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>    // .env.production 文件

    NODE_ENV=production 
    VUE_APP_BASE_URL='http://127.0.0.1'

    // 使用 process.env.VUE_APP_BASE_URL</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">配置启动命令</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// package.json 文件

...
"script":{
  "serve":"vue-cli-service serve",
  "serve:pro":"vue-cli-service serve --mode production ",
  "build":"vue-cli-service build",
}
...</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">Git</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">配置 ssh 公钥</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><a href="https://help.gitee.com/base/account/SSH%E5%85%AC%E9%92%A5%E8%AE%BE%E7%BD%AE">https://help.gitee.com/base/account/SSH%E5%85%AC%E9%92%A5%E8%AE%BE%E7%BD%AE</a></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">基本命令</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":389,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fhuanjingbianliang01-1024x289.png&amp;size=m" alt="" class="wp-image-389">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">版本回滚</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>使用 <code>git switch -c dev1</code> 创建新的 dev1 分支，稍微修改 dev1 分支中的文件，并且提交到远程仓库</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>提交完成后，发现不想用当前的代码，想回滚到上一次的代码</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code># 查看当前项目提交过的所有版本（含所有分支的操作）
git log

# git log 会出现很详细的信息，如果只想简单看看版本号，可以使用
git log --pretty=oneline

# 如果只想回滚到指定版本，可以使用
git reset --hard [版本号]

# 最新写的那个丢失了，又想回去
git reflog     #查看版本号，包括回滚了的版本号
git reset --hard [版本号]</code></pre>
<!-- /wp:code -->]]></description><guid isPermaLink="false">/archives/%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F-git</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Tue, 21 May 2024 07:23:19 GMT</pubDate></item><item><title><![CDATA[Taro-入门]]></title><link>https://blog.qiaofugui.cn/archives/taro-%E5%85%A5%E9%97%A8</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=Taro-%E5%85%A5%E9%97%A8&amp;url=/archives/taro-%E5%85%A5%E9%97%A8" width="1" height="1" alt="" style="opacity:0;"><!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">简介</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>Taro</strong> 是一个遵循 React 语法规范的开放式跨端跨框架解决方案，支持使用 React/Vue/Nerv 等框架来开发微信/京东/百度/支付宝/字节跳动/ QQ 小程序/H5 等应用，内置了UI组件，还有物料市场，只编写一套代码就能够适配到多端。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>Tara 遵循 React 语法，集成的是 <a href="https://nervjs.github.io/docs/">Nerv</a> 框架。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>Nerv 是一款基于 virtual dom 技术的类 React UI 框架，它基于 React 标准，拥有和 React 一致的 API 与生命周期。得益于与 React 保持一致 API 的特性，Nerv 可以无缝兼容到 React 的相关生态，例如 <a href="https://github.com/remix-run/react-router">react-router</a>，<a href="https://github.com/reduxjs/react-redux">react-redux</a>，以及使用 React 开发的组件，所以对于旧的 React 项目，可以无痛地将 React 替换成 Nerv。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">环境准备</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Taro 仅提供一种开发方式：安装 Taro 命令行工具（Taro CLI）进行开发。
 <br>
 在终端输入命令 <code>npm i -g @tarojs/cli</code> 安装 Taro CLI。
 <br>
 Taro CLI 依赖于 Node.js 环境（&gt;=8.0.0）。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">框架</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">项目目录结构</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":263,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Ftaro01-1024x728.png&amp;size=m" alt="" class="wp-image-263">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">编译配置</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>编译配置存放于项目根目录下 <code>config</code> 目录中，包含三个文件:</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><strong><code>index.js</code></strong>: 通用配置</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong><code>dev.js</code></strong>: 开发环境配置</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong><code>prod.js</code></strong>: 生产环境配置</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">入口文件</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>每一个 Taro 应用都需要一个入口组件用来注册应用，入口文件默认是 <code>src/app.js</code>。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">页面文件</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>页面组件是每一项路由将会渲染的页面，Taro 的页面默认放在 <code>src/pages</code> 中，每一个 <code>Taro</code> 项目至少有一个页面组件。页面创建好后如果需要渲染展示，需要在项目入口文件 <code>app.config.js</code> 的 <code>pages</code> 数组中进行指定。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">文件配置</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>**全局配置：**可以通过 <code>app.config.js</code> 的文件进行全局配置， 配置页面文件的路径、窗口表现、设置网络超时时间、设置多 <strong>tab</strong> 等。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>一个简单的全局配置如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>    // app.config.js
    export default {
      pages: [
        'pages/index/index',
        'pages/list/index'
      ],
      window: {
        backgroundTextStyle: 'light',
        navigationBarBackgroundColor: '#fff',
      }
    }</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>未指定 <code>entryPagePath</code> 时，数组的第一项代表初始页面。</p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>新增/减少页面，都需要对 pages 数组进行修改。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:paragraph -->
<p>**页面配置：**每一个页面组件（例如 <code>index.js</code>）也会有一个页面配置（例如 <code>index.config.js</code>），可以在页面配置文件中设置页面的导航栏、背景颜色等参数。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>一个最简单的页面配置如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>    // ./pages/index/index.jsx
    export default {
      navigationBarTitleText: '首页'
    }</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>配置规范基于微信小程序的<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/config.html#%E5%85%A8%E5%B1%80%E9%85%8D%E7%BD%AE">全局配置</a>进行制定，所有平台进行统一。在配置文件中，Taro 并不关心框架的区别，Taro CLI 会直接在编译时在 Node.js 环境直接执行全局配置的代码，并把 <code>export default</code> 导出的对象序列化为一个 JSON 文件。因此，我们必须保证配置文件是在 Node.js 环境中是可以执行的，不能使用一些在 H5 环境或小程序环境才能运行的包或者代码，否则编译将会失败。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">项目配置</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>为了能够适配到各个小程序平台，满足不同小程序平台配置文件不同的情况， Taro 支持为各个小程序平台添加不同的项目配置文件。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>通过 Taro 模板创建的项目都会默认拥有 <code>project.config.json</code> 文件，若要兼容到其他小程序平台，按如下对应规则来增加相应平台的配置文件，其配置与各自小程序平台要求的一致。</p>
<!-- /wp:paragraph -->
<!-- wp:table -->
<figure class="wp-block-table">
 <table>
  <thead>
   <tr>
    <th>小程序平台</th>
    <th>配置文件</th>
    <th>说明</th>
   </tr>
  </thead>
  <tbody>
   <tr>
    <td>微信小程序</td>
    <td>project.config.json</td>
    <td></td>
   </tr>
   <tr>
    <td>百度小程序</td>
    <td>project.swan.json</td>
    <td></td>
   </tr>
   <tr>
    <td>头条小程序</td>
    <td>project.tt.json</td>
    <td></td>
   </tr>
   <tr>
    <td>QQ小程序</td>
    <td>project.qq.json</td>
    <td></td>
   </tr>
   <tr>
    <td>快应用</td>
    <td>project.quickapp.json</td>
    <td>配置文件中请勿配置 <code>router</code> 与 <code>display</code>，这两个配置将由 Taro 生成</td>
   </tr>
  </tbody>
 </table>
</figure>
<!-- /wp:table -->
<!-- wp:heading -->
<h2 class="wp-block-heading">组件生命周期与事件处理函数</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>以Hooks为例：</p>
<!-- /wp:paragraph -->
<!-- wp:image {"id":264,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Ftaro02-1024x622.png&amp;size=m" alt="" class="wp-image-264">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">css 工具</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在 Taro 中，我们可以自由地使用 CSS 预处理器和后处理器，使用的方法也非常简单，只要在编译配置添加相关的插件即可：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// config/index.js
const config = {
  designWidth: 750,
  sourceRoot: 'src',
  outputRoot: 'dist',
  plugins: [
    '@tarojs/plugin-sass', // 使用 Sass
    // '@tarojs/plugin-less', // 使用 Less
    // '@tarojs/plugin-stylus', // 使用 Stylus
  ],
  defineConstants: {},
  mini: {},
  h5: {}
}

module.exports = function (merge) {
  if (process.env.NODE_ENV === 'development') {
    return merge({}, config, require('./dev'))
  }
  return merge({}, config, require('./prod'))
}</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>除了 CSS 预处理器之外，Taro 还支持<a href="https://taro-docs.jd.com/docs/css-modules">css-modules</a>和<a href="https://taro-docs.jd.com/docs/css-in-js">CSS-in-JS</a>。 通过<a href="https://taro-docs.jd.com/taro/docs/guide/#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BC%96%E8%AF%91">自定义编译</a>，还可以支持更多 CSS 工具。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">路由功能</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在 Taro 中，路由功能是默认自带的，不需要开发者进行额外的路由配置。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><strong>入口组件和页面组件是通过配置文件来交互的</strong>，我们只需要在入口文件的 <code>config</code> 配置中指定好 <code>pages</code>，然后就可以在代码中通过 Taro 提供的 API 来跳转到目的页面，例如：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 跳转到目的页面，打开新页面
Taro.navigateTo({
  url: '/pages/page/path/name'
})

// 跳转到目的页面，在当前页面打开
Taro.redirectTo({
  url: '/pages/page/path/name'
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">尺寸单位</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在 Taro 中, 尺寸单位建议使用 <code>px</code>, <code>%</code>，Taro 默认会对所有单位进行转换。当转成微信小程序的时候，尺寸单位将默认转换以 <code>rpx</code> 为单位，当转成 H5 时将默认转换以 <code>rem</code> 为单位。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>如果你希望部分 <code>px</code> 单位不被转换成 <code>rpx</code> 或者 <code>rem</code> ，最简单的做法就是在 <code>px</code> 单位中增加一个大写字母，例如 <code>Px</code> 或者 <code>PX</code> 这样，则会被转换插件忽略。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>Taro 默认以 <code>750px</code> 作为换算尺寸标准，如果设计稿不是以 <code>750px</code> 为标准，则需要在项目配置 <code>config/index.js</code> 中进行设置，例如设计稿尺寸是 <code>640px</code>，则需要修改项目配置 <code>config/index.js</code> 中的 <code>designWidth</code> 配置为 <code>640</code>：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const config = {
  designWidth: 640,
}</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>目前 Taro 支持 <code>750</code>、 <code>640</code> 、 <code>828</code> 三种尺寸设计稿，他们的换算规则如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const DEVICE_RATIO = {
  '640': 2.34 / 2,
  '750': 1,
  '828': 1.81 / 2
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">多端开发</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>由于不同的平台之间还是存在一些无法消除的差异，所以为了更好的实现跨平台开发，Taro 中提供了两种解决方案：</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">1. 内置环境变量： process.env.TARO_ENV</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>process.env.TARO_ENV 用于判断当前编译类型，目前有 <code>weapp</code> / <code>swan</code> / <code>alipay</code> / <code>h5</code> / <code>rn</code> / <code>tt</code> / <code>qq</code> / <code>quickapp</code> 八个取值。通过这个变量来写对应一些不同环境下的代码，在编译时会将不属于当前编译类型的代码去掉，只保留当前编译类型下的代码。</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>render () {
  return (
    &lt;View&gt;
      {process.env.TARO_ENV === 'weapp' &amp;&amp; &lt;ScrollViewWeapp /&gt;}
      {process.env.TARO_ENV === 'h5' &amp;&amp; &lt;ScrollViewH5 /&gt;}
    &lt;/View&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>**缺点：**虽然可以解决大部分跨端的问题，但是会让代码中充斥着逻辑判断，影响代码的可维护性，而且也让代码变得愈发丑陋。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">2. 统一接口的多端文件</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>针对一项功能，如果多个端之间都有差异，开发者可以通过将文件修改成 原文件名.端类型 的命名形式实现多端文件。Taro 在编译时，会根据编译平台类型，将加载的文件变更为带有对应端类型文件名的文件，从而达到不同的端加载对应文件的目的。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><strong>使用要点</strong></p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>不同端的对应文件一定要对外暴露统一接口，统一调用方式，接受一致的参数</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>最好有一个平台无关的默认文件，这样在使用 ts 的时候也不会出现报错</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>引用文件的时候，只需要写默认文件名，不用带文件后缀</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:paragraph -->
<p><strong>多端文件简单的例子：</strong></p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>多端组件</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 1. 定义多端组件
-test
--test.js // Test组件默认的形式，编译到微信小程序和H5之外的端
--test.h5.js // Test 组件的 H5 版本
--test.weapp.js // Test 组件的 微信小程序 版本

// 2. 引用组件
import Test from '../../components/test';

&lt;Test argA={1} argA={2} /&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:list {"ordered":true,"start":2} -->
<ol start="2">
 <!-- wp:list-item -->
 <li>多端方法</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 1.定义多端方法
-utils
--set_title.js
--set_title.h5.js
--set_title.weapp.js

// 2. 使用方法
import setTitle from '../utils/set_title'

setTitle('页面标题')</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">跨平台组件库</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>相对于我们熟悉的 <code>div</code>、<code>span</code> 元素而言，在 Taro 中我们要全部使用跨平台组件进行开发。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>Taro 以 [微信小程序组件库][10] 为标准，结合 <code>jsx</code> 语法规范，定制了一套自己的组件库规范。基于这个原则，在小程序端，可以使用所有的小程序原生组件，在其他端，Taro提供了对应的组件库实现:</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>H5 端: <code>@tarojs/components</code>，需要引入的默认标准组件库</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>RN 端: <code>@tarojs/components-rn</code></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">开发规范</h3>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>首字母大写与驼峰式命名</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>组件的事件传递都要以 on 开头</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">组件种类</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":265,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Ftaro03.png&amp;size=m" alt="" class="wp-image-265">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">使用</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在使用时，我们需要先从 Taro 标准组件库 <code>@tarojs/components</code> 引用组件，再进行使用。</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import Taro, { Component } from '@tarojs/taro';
// 使用 &lt;View /&gt;、 &lt;Text /&gt; 组件
import { View, Text } from '@tarojs/components';

export default class C extends Component {
  render () {
    return (
      &lt;View className='c'&gt;
        &lt;Text&gt;c component&lt;/Text&gt;
      &lt;/View&gt;
    )
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">Taro UI</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Taro UI 是一款基于 Taro 框架开发的多端 UI 组件库。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">特性</h2>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>基于 Taro 开发 UI 组件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>一套组件可以在 微信小程序，支付宝小程序，百度小程序，H5 多端适配运行（ReactNative 端暂不支持）</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>提供友好的 API，可灵活的使用组件</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">组件种类</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":266,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Ftaro04.png&amp;size=m" alt="" class="wp-image-266">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">使用</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">1. 引入组件样式</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>引入组件样式有三种方式：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>全局引入（JS中）： 在入口文件 <code>app.js</code> 中引入 <code>taro-ui</code> 所有的样式</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import 'taro-ui/dist/style/index.scss' // 引入组件样式</code></pre>
<!-- /wp:code -->
<!-- wp:list {"ordered":true,"start":2} -->
<ol start="2">
 <!-- wp:list-item -->
 <li>全局引入（CSS中）： 在 <code>app.scss</code> 样式文件中 <code>import</code> 组件样式并按照文档说明使用</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>@import "~taro-ui/dist/style/index.scss"; // 引入组件样式</code></pre>
<!-- /wp:code -->
<!-- wp:list {"ordered":true,"start":3} -->
<ol start="3">
 <!-- wp:list-item -->
 <li>按需引入： 在页面样式或全局样式中 <code>import</code> 需要的组件样式</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>@import "~taro-ui/dist/style/components/button.scss"; // 引入所需的组件样式</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">2. 引入所需组件</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// index.js
import { AtButton } from 'taro-ui';</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>一个使用AtButton的完整例子：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// src/pages/index/index.tsx
import Taro, { Component, Config } from '@tarojs/taro'
import { View } from '@tarojs/components'
import { AtButton } from 'taro-ui'
import './index.scss';

export default class Index extends Component {
  config: Config = {
    navigationBarTitleText: '首页'
  }

  render () {
    return (
      &lt;View className='index'&gt;
         &lt;AtButton type='primary'&gt;按钮文案&lt;/AtButton&gt;
      &lt;/View&gt;
    )
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>    /* app.scss*/
    @import "~taro-ui/dist/style/index.scss"; </code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">自定义主题</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Taro UI 目前只有一套默认的主题配色，为满足业务和品牌上多样化的视觉需求，UI 库支持一定程度的样式定制。（请确保微信基础库版本在 v2.2.3 以上）</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>目前支持三种自定义主题的方式，可以进行不同程度的样式自定义：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>SCSS 变量覆盖</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>globalClass 全局样式类</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>配置 customStyle 属性（仅有部分组件支持，请查看组件文档，不建议使用）</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">SCSS 主题变量覆盖</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Taro UI 的组件样式是使用 SCSS 编写的，如果你的项目中也使用了 SCSS，那么可以直接在项目中改变 Taro UI 的样式变量。</p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:list {"ordered":true} -->
 <ol>
  <!-- wp:list-item -->
  <li>覆写的变量，需要在引入 taro ui 默认样式之前定义 <a href="https://github.com/NervJS/taro-ui/blob/dev/src/style/variables/default.scss">默认主题变量命名</a>。</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>Slider, Switch 组件暂时不支持 SCSS 变量覆盖的方式自定义主题。</li>
  <!-- /wp:list-item -->
 </ol>
 <!-- /wp:list -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>在 <code>app.scss</code> 文件写入以下内容：</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// app.scss
$color-brand: #6190E8; /* 改变主题变量 */

@import "~taro-ui/dist/style/index.scss"; /* 引入 Taro UI 默认样式 */</code></pre>
<!-- /wp:code -->
<!-- wp:list {"ordered":true,"start":2} -->
<ol start="2">
 <!-- wp:list-item -->
 <li>在 <code>app.js</code> 中引入以上的样式文件即可</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// app.js
import './app.scss';</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>改变主题变量前后对比图：</p>
<!-- /wp:paragraph -->
<!-- wp:image {"id":260,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Ftaro05-1024x782.png&amp;size=m" alt="" class="wp-image-260">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">全局样式类</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>全局样式类是微信小程序定义的一套用于修改组件内部样式的方案。如果希望组件外样式类能够影响组件内部，可以在组件构造器中的 <code>options.addGlobalClass</code> 字段设置为 <code>true</code>（Taro UI 的组件均开启了此特性）。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><strong><code>addGlobalClass</code> 只对 Page 上的 class 起作用。换言之，如果在自定义的组件中使用 taro-ui，是无法在自定义组件内部通过 全局样式类 的方式去更改组件样式的。</strong></p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>当开放了全局样式类，存在外部样式无意间污染组件样式的风险。由于 Taro UI 的组件样式采用 BEM 的命名方式，从一定程度上避免了样式污染的问题。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:code -->
<pre class="wp-block-code"><code>/* page/index/index.js */
import Taro from '@tarojs/taro'
import { AtButton } from 'taro-ui'
import "./index.scss"

export default IndexPage extends Taro.Component {  
  render () {
    return &lt;AtButton className='my-button' /&gt;
  }
}

/**
 * page/index/index.scss 必须在 Page 上
 * .at-button 为组件内部类名，只需要写一样的类名去覆盖即可
 **/
.my-button .at-button {
  color: red;
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">设计思想与编译原理</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在 Taro 中先按照 Nerv语法编写一份源代码，然后通过编译工具将源代码编译成对应的代码。编译工具采用的是编译原理的思想，所谓编译原理，就是一个对输入的源代码进行语法分析，语法树构建，随后对语法树进行转换操作再解析生成目标代码的过程。</p>
<!-- /wp:paragraph -->
<!-- wp:image {"id":261,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Ftaro06-859x1024.png&amp;size=m" alt="" class="wp-image-261">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">Taro标准：抹平多端差异</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>基于编译原理，可以将 Taro 源码编译成不同端上可以运行的代码了，但是这对于实现多端开发还是远远不够。不同的平台都有自己的特性，每一个平台都不尽相同，这些差异主要体现在不同的组件标准与不同的 API 标准以及不同的运行机制上。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>以小程序和 Web 端为例：</p>
<!-- /wp:paragraph -->
<!-- wp:image {"id":262,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Ftaro07-1000x1024.png&amp;size=m" alt="" class="wp-image-262">
</figure>
<!-- /wp:image -->
<!-- wp:paragraph -->
<p>这一套标准主要以三个部分组成，包括标准运行时框架、标准基础组件库、标准端能力 API，其中运行时框架和 API 对应 <code>@taro/taro</code>，组件库对应 <code>@tarojs/components</code>，通过在不同端实现这些标准，从而达到去差异化的目的。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">taro build命令</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>taro build</code> 命令是整个 taro 项目的灵魂和核心，主要负责 <strong>多端代码编译</strong>（h5，小程序，React Native等）。不同的平台，编译传参不同，编译规则不同。</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// package.json 
"scripts": {
    // 微信小程序 weapp
    "build:weapp": "taro build --type weapp",
    // h5
    "build:h5": "taro build --type h5",
    // 支付宝小程序 alipay
    "build:alipay": "taro build --type alipay",
    // ....
  }</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">编译工作流与抽象语法树（AST）</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>一般来说，将一种结构化语言的代码编译成另一种类似的结构化语言的代码包括以下几个步骤：</p>
<!-- /wp:paragraph -->
<!-- wp:image {"id":256,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Ftaro08-1024x319.png&amp;size=m" alt="" class="wp-image-256">
</figure>
<!-- /wp:image -->
<!-- wp:paragraph -->
<p>首先是 parse，将代码 <code>解析（Parse）</code>成 <code>抽象语法树（Abstract Syntex Tree）</code>，然后对抽象语法树 AST 进行 <code>遍历（traverse）</code>和 <code>替换(replace)</code>（可以类比 DOM 树的操作），最后是 <code>生成（generate）</code>，根据新的 AST 生成编译后的代码。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">Babel模块</h4>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>Babel</code> 是一个通用的多功能的 JavaScript编译器，更确切地说是源码到源码的编译器，通常也叫做转换编译器（transpiler）。 Taro 项目的代码编译部分就是基于 Babel 的以下模块实现：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><a href="https://link.segmentfault.com/?enc=8eUGnY%2BcBfGys3R1Mn62FA%3D%3D.NPubVgzc8Hm6Np8wbX%2FWh%2BtHpg6IGpD5L3lTLmNZ%2FXiEjVko4%2FpfLRub2neBqK8L">babylon</a>&nbsp;Babylon 是 Babel 的解析器。最初是 从Acorn项目fork出来的。Acorn非常快，易于使用，并且针对非标准特性(以及那些未来的标准特性) 设计了一个基于插件的架构。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><a href="https://link.segmentfault.com/?enc=aH90Wjat7J508hkVoRn4Vg%3D%3D.UnkjHUhiHmkC0F9bsaiKrvTricyxYgpv30sIBgfF1CjWMHuFv4LDYR2%2BAxW3FKahi0sU0eBMZdnOxL9z1gO4IFPnHUcDiz3kX3ZWWAQDwb8%3D">babel-traverse</a>&nbsp;Babel Traverse（遍历）模块维护了整棵树的状态，并且负责替换、移除和添加节点。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><a href="https://link.segmentfault.com/?enc=Subg1KGLF7J4N4TUVIpbDQ%3D%3D.M%2FVGMXWRIfweHLxTwh8J0IUndkPKXItzv0L9UDKwO4DSZADE5lWa9p0G3qVpyC464WqX5wCNanmoZi9ydrX0fQ%3D%3D">babel-types</a>&nbsp;Babel Types模块是一个用于 AST 节点的 Lodash 式工具库， 它包含了构造、验证以及变换 AST 节点的方法。 该工具库包含考虑周到的工具方法，对编写处理AST逻辑非常有用。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><a href="https://link.segmentfault.com/?enc=JUNTbIhBAKsYwuHNNyi29w%3D%3D.xOapVxF3ppSAMcEL3r5NSITOR7MwOyc%2FNoZrzqHQK2wuzSQTuN5OPuqLlOI8lZJn5n0kj1QcHAZPP65VEaFfoqoYrN3bTwfMuiMCfWCi3XQ%3D">babel-generator</a>&nbsp;Babel Generator模块是 Babel 的代码生成器，它读取AST并将其转换为代码和源码映射（sourcemaps）。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><a href="https://link.segmentfault.com/?enc=hbn%2B5umbR41Ed7X3K84izg%3D%3D.dNz%2FR%2FpePDT8XdgWMAAZPp44QgzeiQjTJ0itSqDSc2%2B63hCGYL5ojPIGe%2BzSJg2LbQNXdAqbggpnzCjFHVCM%2FtFxKRtPg8g2v99%2Bnzkg218%3D">babel-template</a>&nbsp;babel-template 是另一个虽然很小但却非常有用的模块。 它能让你编写字符串形式且带有占位符的代码来代替手动编码， 尤其是生成的大规模 AST的时候。 在计算机科学中，这种能力被称为准引用（quasiquotes）。</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>转载自：<a href="https://segmentfault.com/a/1190000039033249">https://segmentfault.com/a/1190000039033249</a></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->]]></description><guid isPermaLink="false">/archives/taro-%E5%85%A5%E9%97%A8</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Tue, 21 May 2024 07:21:14 GMT</pubDate></item><item><title><![CDATA[Umi3]]></title><link>https://blog.qiaofugui.cn/archives/umi3</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=Umi3&amp;url=/archives/umi3" width="1" height="1" alt="" style="opacity:0;"><!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">框架环境和基本使用</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Umi 是蚂蚁金服的底层前端框架， 是可扩展的企业级前端应用框架， 内置了路由、构建、部署、测试， 包含组件打包、文档工具、请求库、hooks 库、数据流等 ， 通过框架的方式简化 React 开发</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><strong>知识结构图</strong></p>
<!-- /wp:paragraph -->
<!-- wp:image {"id":227,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fumi3-01.png&amp;size=m" alt="" class="wp-image-227">
</figure>
<!-- /wp:image -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>官网：https://v3.umijs.org/zh-CN</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">环境准备，快速上手</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>准备工作：由于国内网络和前端的特殊性，在安装依赖等方面可能会失败或导致无法启动，浪费大量的时间，推荐使用 yarn 作为包管理器，并且使用国内镜像，推荐 yrm 这个工具管理 yarn 镜像</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code># 安装
npm install -g yrm

# 查看 yarn 镜像源
yrm ls

# 切换源
yrm use taobao</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">项目初始化</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>新建一个空目录，在空目录里面执行命令</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code># 使用 yarn 安装 umi 环境
yarn create @umijs/umi-app

# 安装依赖
cd 目录
yarn

# 启动项目
yarn start</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>在浏览器里打开 <code>http://localhost:8000</code>，能看到以下界面:</p>
<!-- /wp:paragraph -->
<!-- wp:image {"id":228,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fumi3-02-1024x632.png&amp;size=m" alt="" class="wp-image-228">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">目录结构</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>├── dist                        // 默认的 build 输出目录
├── mock                        // mock 文件所在目录，基于 express
├── config
    ├── config.js               // umi 配置，同 .umirc.js，二选一
├── public                      // 变通的数据资源目录和一些无需打包的资源
└── src                         // 源码目录
    ├── layouts/index.js        // 全局布局
    ├── models                  // 数据流
    ├── wrappers                // 权限管理
    ├── pages                   // 页面目录，里面的文件即路由
        ├── .umi                // dev 临时目录，需添加到 .gitignore
        ├── .umi-production     // build 临时目录，会自动删除
        ├── document.ejs        // HTML 模板
        ├── 404.js              // 404 页面
        ├── page1.js            // 页面 1，任意命名，导出 react 组件
        ├── page1.test.js       // 测试用例文件
        └── page2               // 页面 2，内部可含有
    ├── global.css              // 约定的全局样式文件，自动引入，也可以用 global.less
    ├── global.js               // 可以在这里加入 polyfill
    ├── app.js                  // 运行时配置文件
├── .umirc.js                   // umi 配置，同 config/config.js，二选一
├── .env                        // 环境变量
└── package.json</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">构建时配置</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":220,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fumi3-03.png&amp;size=m" alt="" class="wp-image-220">
</figure>
<!-- /wp:image -->
<!-- wp:paragraph -->
<p>构建时是对开发环境配置，如果项目的配置不复杂，推荐在 <code>.umirc.ts</code> 中写配置； 如果项目的配置比较复杂，可以将配置写在<code>config/config.ts</code> 中，并把配置的一部分拆分出去，现实往往是复杂的所以推荐 <code>config/config</code>，两种配置方式二选一，<code>.umirc.ts</code> 优先级更高，采用 config 配置时，一般删除 <code>.umirc.ts</code></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { defineConfig } from 'umi';
import proxy from './proxy';
import routes from './routes';
import theme from './theme'

export default defineConfig({
  nodeModulesTransform: { // node_modules 目录下依赖文件的编译方式
    type: 'none', // all 慢 兼容性好 none 快 兼容性一般
  },
  mfsu: {}, // 打包提速
  fastRefresh: {}, // 快速刷新 可以保持组件状态，同时编辑提供即时反馈
  title:'UMI3', // 配置标题。
  mountElementId: 'app', // 指定 react app 渲染到的 HTML 元素 id
  favicon: '/favicon.ico', // 使用本地的图片，图片请放到 public 目录

  routes: routes,

  proxy:proxy, // 配置反向代理

  // 启用按需加载
  dynamicImport: {
    loading: '@/components/loading', // 按需加载时指定的 loading 组件
  },

  theme, // 配置主题，实际上是配 less 变量
  devServer: {
    port: 8666, // .env 里面权限更高一些
    // https:true, // 启用 https 安全访问，于对应协议服务器通讯
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">模板约定</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>umi 内部默认没有 html，会自行生成，如果需要修改， 新建 <code>src/pages/document.ejs</code></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;!doctype html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;meta charset="utf-8" /&gt;
  &lt;meta name="viewport" content="width=device-width"&gt;
  &lt;title&gt;Your App&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;div id="app"&gt;&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">antd, antd-mobile 使用</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":221,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fumi3-04.png&amp;size=m" alt="" class="wp-image-221">
</figure>
<!-- /wp:image -->
<!-- wp:paragraph -->
<p>umi 已整合 antd 组件库，可通过 <code>import {Xxx} from 'antd'</code> 使用</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>使用指定版本组件库，<code>yarn add xx-xx@x.x.x</code> 后，会优先使用指定版本</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">antd 主题设定</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>找到 <code>config/theme</code></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export default {
  "@primary-color": "pink" // antd 全局样式 copy 过来统一修改
};</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>antd 样式变量：https://ant.design/docs/react/customize-theme-cn</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">antd-mobile</h3>
<!-- /wp:heading -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>更新 <code>@umijs/preset-react</code> 到最新版本
  <br>
  umi 已整合 <code>antd-mobile</code> 组件库，可通过 <code>import {Xxx} from 'antd-mobile'</code> 使用 v5 版本，可通过 <code>import {Xxx} from 'antd-mobile-v2'</code> 使用 v2 版本，使用指定版本组件库，<code>yarn add xx-xx@x.x.x</code> 后，会优先使用指定版本，推荐使用 v5</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:paragraph -->
<p>使用 v5 版本报错，找不到被使用的组件时尝试：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><strong>删除 <code>.umi</code></strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>更新 <code>@umijs/preset-react</code> 包</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>关闭 <code>mfsu</code></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>重启</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">antd-mobile v5 主题修改</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>/* src/global.less */
:root:root {
  --adm-color-primary: hotpink; // antd-mobile 全局样式 copy 过来统一修改
}</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>antd-moblid 提供的全局变量：https://mobile.ant.design/zh/guide/theming/</p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>antd-mobile v2 主题修改和 antd 一样</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">图片和其他资源引入</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":222,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fumi3-05.png&amp;size=m" alt="" class="wp-image-222">
</figure>
<!-- /wp:image -->
<!-- wp:paragraph -->
<p><strong>项目中使用图片有两种方式：</strong></p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>先把图片传到 cdn，然后在 JS 和 CSS 中使用图片的绝对路径</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>把图片放在项目里，然后在 JS 和 CSS 中通过相对路径的方式使用</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>前者趋向数据图片，后者趋向写死的图片，通过相对路径引入图片的时候，如果图片小于 10K，会被编译为 Base64 嵌入网页</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// src/pages/index.tsx
import styles from './index.less';
import { Button } from 'antd';
import { Button as V2Button } from 'antd-mobile-v2';
import { Button as V5Button } from 'antd-mobile';

import user from '../assets/images/userBj.png'

export default function IndexPage() {
  return (
    &lt;div&gt;
      &lt;h1 className={styles.title}&gt;Page index&lt;/h1&gt;
      &lt;Button type='primary'&gt;Button&lt;/Button&gt;

      &lt;V2Button type='primary' size='small'&gt;V2Button&lt;/V2Button&gt;

      &lt;V5Button color='primary'&gt;Purple&lt;/V5Button&gt;

      &lt;div&gt;
        &lt;h3&gt;写死&lt;/h3&gt;
        &lt;img src={user} /&gt;
        &lt;img src={require('../assets/images/userBj.png')} /&gt;

        &lt;h3&gt;动态&lt;/h3&gt;
        {/*动态图片 丢到服务器 推荐,或临时指向 public 不推荐*/}
        &lt;img src="/img/bg.jpg" alt="" style={{ width: 100 }} /&gt;

        &lt;h3&gt;样式内部使用&lt;/h3&gt;
        &lt;div className={styles.test} style={{ height: 100 }}&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// src/pages/index.less
.title {
  background: rgb(121, 242, 157);
}

.test{
  // background: url('../assets/images/bg.jpg');
  // 别名效果 在 css 里面指向 src 目录需要使用 ~@
  background: url('~@/assets/images/bg.jpg');
  // background: url('cdn 链接');
  // background: url('/img/bg.jpg');
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">组书写风格与页面跳转</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">Less 变量，混合，嵌套，父选择器</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":223,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fumi3-06.png&amp;size=m" alt="" class="wp-image-223">
</figure>
<!-- /wp:image -->
<!-- wp:paragraph -->
<p>框架 自带了 less，css 及模块化的解析工具 ，推荐模块化使用 <code>lessimport styles from 'xx.less</code> 避免了全局污染 和 选择器复杂</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>模块化的基本原理很简单，就是对每个类名（非 :global 声明的）按照一定规则进行转换，保证它的唯一性。如果在浏览器里查看这个示例的 dom 结构，你会发现实际渲染出来是这样的：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;div class="title___3TqAx"&gt;title&lt;/div&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>类名被自动添加了一个 hash 值，这保证了它的唯一性。</p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>实际开发中简单的样式我们并不推荐写 css，推荐使用模板组件来进行开发，或者直接写行内 css。css 并没有很好的依赖关系，很多项目都有冗余的 css，但是却不敢删除</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">全局变量</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// src/global.less

{/* title 为 global.less 配置的整体样式 */}
&lt;h2 className="title"&gt;全局样式&lt;/h2&gt;


@import '~antd/es/style/themes/default.less';
// 使用 default 的变量
.myLink {
  color: @primary-color;
  font-size: @font-size-base;
}

&lt;div className='myLink'&gt;测试&lt;/div&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">局部变量</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>@width: 100px;
@height: @width - 80px;

.header {
  width: @width;
  height: @height;
  background: pink;
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">混合</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>.bordered (@topW: 1px, @bottomW: 2px) {
  border-top: dotted @topW black;
  border-bottom: solid @bottomW black;
}

#a1 {
  color: #111;
  .bordered();
}

.a2 {
  color: red;
  .bordered(2px, 4px);
  // border-bottom: solid 5px black; // 覆盖混合
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">嵌套</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>.nesting {
  color: blue;
  h3 {
    color: coral;
  }
  p {
    color: aqua;
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">:global</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>/* 定义多个全局样式 */
.bars_right {
  font-weight: bold;
  :global {
    .ant-btn {
      border: 0;
    }
    .title {
      background: #fafafa;
    }
  }
}

&lt;div className={styles.bars_right}&gt;
    &lt;button className={`ant-btn`}&gt;按钮&lt;/button&gt;
&lt;/div&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">hooks + 函数式编写组件</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>function 组件(){}
const 组件 = (props) = &gt;{

  // 使用 hooks

  // 定义 函数 变量

  return jsx
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">useContext</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>组件上下文共享，越级传递数据，响应式</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 创建上下文 context.jsx 
import {createContext} from 'react'
const AppContext = createContext({})
export default AppContext;

// 祖先组件提供上文 parent.jsx
function 父组件() {
  const [msg, setMsg] = useState('hooks 组件数据')
  return (
      &lt;AppContext.Provider value={{ msg, setMsg }}&gt;
      ....
      &lt;子组件 /&gt;
      ...
    &lt;/AppContext.Provider&gt;
  )
}



// 后代组件接受下文 child.jsx
import { useContext } from "react";
import AppContext from "../context";

function 后代组件(){
  const {msg,setMsg} = useContext(AppContext);
  return (
    &lt;&gt;
      &lt;div&gt;{msg}&lt;/div&gt;
      &lt;button onClick={setMsg}&gt;按钮&lt;/button&gt;
    &lt;/&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">useMemo</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>hooks 出来之后，我们能够使用 function 的形式来创建包含内部 state 的组件。但是，使用 function 的形式，失去 shouldComponentUpdate，我们无法通过判断前后状态来决定是否更新。在函数组件中，react 不再区分 mount 和 update 两个状态，函数组件的每一次调用都会执行其内部的所有逻辑，如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export default function Xxx() {

  const [count, setCount] = useState(1);
  const [value, setValue] = useState('');

  function getNum() {
    console.log("getNum");
    return count * 100
  }

  return (
    {/* 组件任何一条数据变化，getNum 函数重复调用 */}
    &lt;div&gt;getNum：{getNum()}&lt;/div&gt;
    &lt;button onClick={() =&gt; setCount(count + 1)}&gt;+1&lt;/button&gt;
    &lt;input value={value} onChange={ev =&gt; setValue(ev.target.value)} /&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>那么会带来较大的性能损耗。useMemo 可指定依赖的数据变化才渲染，类似 vue 计算属性，返回缓存后的值数据，可拿去渲染</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export default function Xxx() {

  const [count, setCount] = useState(1);
  const [value, setValue] = useState('');

  const getNumMemo = useMemo(() =&gt; {
    // 可执行一些没有副作用的业务，比如同步重新计算 count
    return count * 100
  }, [count])

  return (
    {/* 只有 count 数据变化，getNumMemo 函数才会调用 */}
    &lt;div&gt;getNum：{getNumMemo}&lt;/div&gt;
    &lt;button onClick={() =&gt; setCount(count + 1)}&gt;+1&lt;/button&gt;
    &lt;input value={value} onChange={ev =&gt; setValue(ev.target.value)} /&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">memo</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>react 父组件更新未传递给子的数据，子组件也会更新，如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 修改 count 或者 value 时，child 组件都会更新
&lt;button onClick={() =&gt; setCount(count + 1)}&gt;+1&lt;/button&gt;
&lt;input value={value} onChange={ev =&gt; setValue(ev.target.value)} /&gt;
&lt;Child count={count} /&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>memo 可以协助子组件只依赖传递过来的数据变化时才更新</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import {memo} from 'react'

function Child({count}){
  const show = () =&gt; console.log('child 组件渲染')
  return (
    &lt;&gt;
      &lt;h3&gt;Child2 组件&lt;/h3&gt;
      &lt;div&gt;{show()}&lt;/div&gt;
      &lt;div&gt;{count}&lt;/div&gt;
    &lt;/&gt;
  )
}
// memo 修饰当前组件 依赖父的数据变化时，才渲染
export default memo(Child)

// 不包装的情况下，父任意数据更新子都会更新
// export default Child</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">useCallback</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>由于组件内的业务函数传递给子组件时，每次都会是新的引用，会导致子组件无故更新，如下：</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>父组件</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 修改 count 或者 value 时，child 组件都会更新
&lt;button onClick={() =&gt; setCount(count + 1)}&gt;+1&lt;/button&gt;
&lt;input value={value} onChange={ev =&gt; setValue(ev.target.value)} /&gt;
&lt;Child updateCount=(()=&gt;console.log('业务')) /&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>子组件</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import {memo} from 'react'
function Child({updateCount}){
  const show = () =&gt; console.log('child 组件渲染')
  return (
    &lt;&gt;
      &lt;h3&gt;Child3 组件&lt;/h3&gt;
      &lt;div&gt;{show()}&lt;/div&gt;
      &lt;button onClick={updateCount}&gt;测&lt;/button&gt;
    &lt;/&gt;
  )
}
// memo 修饰当前组件 依赖父的数据变化时，才渲染 但依赖父的是个函数时 memo 无效
export default memo(Child)</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>useCallback 可以将函数缓存起来，节省性能，指定某个被依赖的数据变化才更新函数，子组件配合 memo 实现，如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export default () =&gt; {

  const updateCount = useCallback(()=&gt;{
    // 业务
  },[])

  return (
     //修改 count 或者 value 时，child 组件都会更新
    &lt;button onClick={() =&gt; setCount(count + 1)}&gt;+1&lt;/button&gt;
    &lt;input value={value} onChange={ev =&gt; setValue(ev.target.value)} /&gt;
    &lt;Child updateCount={updateCount} /&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">useLayoutEffect</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>useLayoutEffect 早于类组件早于 useEffect</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><strong>挂载时</strong></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>类 render → 函数 render → useLayoutEffect→ 类 didmount → useEffect</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><strong>更新时</strong></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>类 render 渲染 → 函数 render → useLayoutEffect 销毁→ useLayoutEffect 执行→ 类 didUpdate → useEffect 销毁… → useEffect 执行</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">路由，权限，动态，约定式</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":224,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fumi3-07-1024x510.png&amp;size=m" alt="" class="wp-image-224">
</figure>
<!-- /wp:image -->
<!-- wp:paragraph -->
<p>页面地址的跳转都是在浏览器端完成的，不会重新请求服务端获取 html，html 只在应用初始化时加载一次 ，页面由不同的组件构成，页面的切换其实就是不同组件的切换， 只需要在把不同的路由路径和对应的组件关联上 ，实现方式如下两种</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>配置型路由（<code>在配置文件写入相关配置代码</code>），配置型存在时，约定式失效</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>约定式（<code>约定文件位置名称与格式无需写代码配置</code>）
  <br>
  约定式是理想型方案，实际开发一般会向现实低头，推荐采用配置型路由</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:paragraph -->
<p>配置 <code>config/config</code> 的 <code>routes</code> 属性，接受数组，一般单独写一个 <code>routes</code> 模块文件如下:</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>//  config/routes
export default [
  { path: '/less', component: 'less' }, // 不写路径从 src/pages找组件
  { path: '/antd', component: './antd' }, // 当前指向 pages
  { component: '@/pages/404' }, // @ 指向 src
]</code></pre>
<!-- /wp:code -->
<!-- wp:image {"id":215,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fumi3-08-1024x540.png&amp;size=m" alt="" class="wp-image-215">
</figure>
<!-- /wp:image -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export default [
    { path: '/login', component: 'login' },
    { path: '/reg', component: './reg' },

    {
      path: '/',
      component: '@/layouts/layout1',// layout 组件
      routes: [
        { path: '/less', component: 'less' },
        { path: '/antd', component: 'antd' },
        {  path:'/', redirect: '/antd' },
        { component: '@/pages/404' },
      ],
    },

    { component: '@/pages/404' },


  ]</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p><code>[^routes]</code>: 配置子路由，通常在需要为多个路径增加 layout 组件时使用</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// layouts/布局组件

// 可引入一些 components 下的公共组件来完成公共布局
import Nav1 from '../../components/nav1'
import styles from './index.less'

const Layout1 = props =&gt; {
  return (
    &lt;&gt;
      {props.children}
      &lt;div className={styles['adm-tab-bar']}&gt;&lt;Nav1/&gt;&lt;/div&gt;
    &lt;/&gt;
  )
}

export default Layout1;</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>{ path: '/user', component: 'user',wrappers:['@/wrappers/auth']}, // 路由守卫</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// wrappers/auth
import { Redirect } from 'umi'

export default (props) =&gt; {
  if (Math.random() &lt; .5) {
    return &lt;div&gt;{props.children}&lt;/div&gt;;
  } else {
    return &lt;Redirect to="/login" /&gt;;
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">多级子路由</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>{ 
  path: '/goods', 
  component: '@/layouts/layout2', // 展示区
  routes:[
    // { path: '/goods', component: 'goods' },
    // { path:'/goods', redirect: '/goods/2' }, // 这里的
    { path: '/goods/:id?', component: 'goods/goods-detail' }, // 动态可选路由
    { path: '/goods/:id/comment', component: 'goods/comment' }, // 不配 routes，占用当前展示区
    { path: '/goods/:id/comment/:cid', component: 'goods/comment/comment-detail' },
    { component: '@/pages/404' },
  ]
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">页面跳转，参数接收</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":216,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fumi3-09.png&amp;size=m" alt="" class="wp-image-216">
</figure>
<!-- /wp:image -->
<!-- wp:image {"id":217,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fumi3-10.png&amp;size=m" alt="" class="wp-image-217">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">声明式跳转 + 传参</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const nav1 = ()=&gt;{
  return (
      &lt;NavLink activeClassName={styles.xx} to="/antd"&gt;antd&lt;/NavLink&gt;
      &lt;NavLink activeStyle={{color:'#399'}} to="/antd"&gt;antd&lt;/NavLink&gt;
      &lt;Link to="/antd"&gt;antd&lt;/Link&gt;
      &lt;Link to={{pathname:'/antd',search:'?a=1',query:{a:1}}}&gt;antd&lt;/Link&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">编程式跳转 + 传参</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// history 可以导入或者上下文获取
import { history } from 'umi';
const 组件({history})=&gt;{}

// 跳转到指定路由
history.push('/list');

// 带参数跳转到指定路由
history.push('/list?a=b');
history.push({
  pathname: '/list',
  query: {
    a: 'b',
  },
});

// 跳转到上一个路由
history.go(-1);</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">参数接收</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 可以从组件上下文获取
const 组件 = ({location,match})=&gt;{}

// 如果没有上下文可以 withRouter 包装组件
import { withRouter } from 'umi';
const withRouter({location,match})=&gt;{}

// 可以直接使用 umi 的 hooks 获取
import { useLocation,useParams,useRouteMatch} from 'umi';
const 组件 = ()=&gt;{
  const params = useParams();
  params.id | params.cid
  const location = useLocation();
  location.search | location.query
}</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>相关 API：https://v3.umijs.org/zh-CN/api</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">数据生成与请求</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">数据模拟 umi-mock</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":218,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fumi3-11.png&amp;size=m" alt="" class="wp-image-218">
</figure>
<!-- /wp:image -->
<!-- wp:paragraph -->
<p>Mock 数据是前端开发过程中必不可少的一环，是分离前后端开发的关键链路。通过预先跟服务器端约定好的接口，模拟请求数据甚至逻辑，能够让前端开发独立自主，不会被服务端的开发所阻塞</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>UMI3 里约定 mock 文件夹下的文件或者 page(s) 文件夹下的 _mock 文件即 mock 文件，文件导出接口定义，支持基于 <code>require</code> 动态分析的实时刷新，支持 ES6 语法，以及友好的出错提示。</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export default {
  // 支持值为 Object 和 Array
  'GET /api/users': { users: [1, 2] },

  // GET 可省略
  '/api/users/1': { id: 1 },

  // 支持自定义函数，API 参考 express@4，可完成业务
  'POST /api/users/create': (req, res) =&gt; {res.end('OK'); },
};</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>当客户端（浏览器）发送请求，如：<code>GET /api/users</code>，那么本地启动的 <code>umi dev</code> 会跟此配置文件匹配请求路径以及方法，如果匹配到了，就会将请求通过配置处理，就可以像样例一样，你可以直接返回数据</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>Mock.js 辅助生成自然且多条数据</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import Mock from 'mockjs';

export default {
  // 使用 mockjs 等三方库
  'GET /api/tags': Mock.mock({
    'list|100': [{ name: '@city', 'value|1-100': 50, 'type|0-2': 1 }],
  }),
};</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>对于整个系统来说，请求接口是复杂并且繁多的，为了处理大量模拟请求的场景，我们通常把每一个数据模型抽象成一个文件，统一放在 <code>mock</code> 的文件夹中，然后他们会自动被引入。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>为了更加真实的模拟网络数据请求，往往需要模拟网络延迟时间，可以通过第三方插件来简化这个问题，如：<a href="https://github.com/nikogu/roadhog-api-doc/blob/master/lib/utils.js#L5">roadhog-api-doc#delay</a>。</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { delay } from 'roadhog-api-doc'; // 模拟延时

export default delay(
  {
    // 支持值为 Object 和 Array
    '/umi/goods': [
      { id: 1, name: '韭菜' },
      { id: 2, name: '西红柿' },
    ],
  },
  2000,
); // 延时</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">鉴权</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// mock/auth
export default {
  'POST /umi/login': (req, res) =&gt; {
    const { username, password } = req.body;
    if (username === 'joe' &amp;&amp; password === 'qqqqqq') {
      res.send({
        err: 0,
        msg: '成功',
        currentAuthority: 'user',
      });
    } else if (username === 'admin' &amp;&amp; password === 'admin123') {
      res.send({
        err: 0,
        msg: '成功',
        currentAuthority: 'admin',
      });
    } else {
      res.send({
        err: 1,
        msg: '失败',
      });
    }
  },
};</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">分页</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 查分页
// 指定页数范围内显示全数据，超过只显示两条
'GET /umi/list': (req, res) =&gt; {
  const { _page = 1, _limit = 3 } = req.query;

  const totalPage = 3; // 设定总页数
  const lastPageLimit = 2; // 设定尾页条数
  const total = _limit * (totalPage - 1) + lastPageLimit; // 计算总条数

  res.send({
    code: 0,
    data: {
      _page,
      _limit,
      total,
      // 控制 data 键，后面数组的条数
      ...Mock.mock({
        [`data|${_page &gt;= totalPage ? lastPageLimit : _limit}`]: [
          {
            'id|+1': 1,
            create_at: '@date("yyyy-MM-dd HHss")',
            'type_str|1': [
              '中转费明细',
              '调整单明细',
              '代收到付明细',
              '客户运费明细',
              '导入失败记录',
            ],
            name: function () {
              return [
                Mock.mock('@datetime("MMdd")'),
                Mock.mock('@county()'),
                this.operator,
              ].join('-');
            },
            path: 'http://xxx/shop/quotation/导入失败列表.xlsx',
            operator: '@cname',
            'status|1': ['0', '1', '2', '3'],
          },
        ],
      }),
    },
  });
},</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">增删改</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 增
'POST /umi/list': (req, res) =&gt; {
  console.log(req.body);
  res.send(
    Mock.mock({
      'data|1': [
        {
          code: 0,
          data: { ...req.body, a: 2 },
          msg: '成功',
        },
        {
          code: 1,
          msg: '失败',
        },
      ],
    }).data,
  );
},

// 删
'DELETE /umi/list/:id': (req, res) =&gt; {
  console.log(req.params.id);
  res.send(
    Mock.mock({
      'data|1': [
        {
          code: 0,
          data: { task_id: '123' },
          msg: '成功',
        },
        {
          code: 1,
          msg: '失败',
        },
      ],
    }).data,
  );
},

// 改
'PATCH /umi/list/:id': (req, res) =&gt; {
  console.log(req.body);
  res.send(
    Mock.mock({
      'data|1': [
        {
          code: 0,
          data: { ...req.body },
          msg: '成功',
        },
        {
          code: 1,
          msg: '失败',
        },
      ],
    }).data,
  );
},</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">数据模拟 json-server</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>一款第三方模拟服务器和数据的包，支持 json 文件存本地被修改，自动生成 resut 风格可操作的接口，有效的 CURD 操作，对数据要求高时，推荐使用</p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p><a href="https://www.npmjs.com/package/json-server?activeTab=readme">https://www.npmjs.com/package/json-server?activeTab=readme</a></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// jsonserver/db.js
// 用 mockjs 模拟生成数据
const Mock = require('mockjs')
let mr = Mock.Random // 提取 mock 的随机对象

module.exports = () =&gt; 
  Mock.mock({
    'goods|3': [{
      'id|+1': 100,
      title: '@ctitle(6,10)',
      des: 'csentence(10,20)',
      time: 'integer()',
      detail: {
        auth: '@cname()',
        auth_icon: mr.image('50x50',mr.color(),mr.cword(1))
      }
    }]
  })

// 使用 app.js
module.exports = {
  ...Mock.mock({
    'goods|3': [{
      'id|+1': 100,
      title: '@ctitle(6,10)',
      des: 'csentence(10,20)',
      time: 'integer()',
      detail: {
        auth: '@cname()',
        auth_icon: mr.image('50x50',mr.color(),mr.cword(1))
      }
    }]
  })
}</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>启动 json-server 服务 <code>json-server ./jsonserver/db.js</code></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// jsonserver/app.js
const jsonServer = require('json-server'); // 在 node 里面使用 json-server 包
const db = require('./db.js'); // 引入 mockjs 配置模块  需要暴露一个对象
const path = require('path');
const Mock = require('mockjs');

let port = 3333; // 端口
let mock = '/mock' // 接口别名

// 创建服务器
const server = jsonServer.create(); // 创建 jsonserver 服务对象  ~~ express()


// 配置 jsonserver 服务器 中间件
server.use(jsonServer.defaults({
  static: path.join(__dirname, '/public'), // 静态资源托管
}));

server.use(jsonServer.bodyParser); // 抓取 body 数据使用 json-server 中间件


// 响应
server.use((request, res, next) =&gt; { // 可选 统一修改请求方式
  // console.log(1)
  // request.method = 'GET';
  // 校验token
  next();
});

// 登录注册校验 模拟 db.js 接口 ， 多出逻辑
let mr = Mock.Random;//提取mock的随机对象
server.post(mock + '/login', (req, res) =&gt; {

  let username = req.body.username;
  let password = req.body.password;
  (username === 'joe' &amp;&amp; password === 'qqqqqq') ?
    res.jsonp({ // json-server 返回数据的一个 api 而已
      "err": 0,
      "msg": "登录成功",
      "data": {
        "follow": mr.integer(1, 5),
        "fans": mr.integer(1, 5),
        "nikename": mr.cname(),
        "icon": mr.image('20x20', mr.color(), mr.cword(1)),
        "time": mr.integer(13, 13),
        "token": mr.integer(25)
      }
    }) :
    res.jsonp({
      "err": 1,
      "msg": "登录失败",
    })

});
server.post(mock + '/reg', (req, res) =&gt; {

  let username = req.body.username;
  (username !== 'alex') ?
    res.jsonp({
      "err": 0,
      "msg": "注册成功",
      "data": {
        "follow": mr.integer(0, 0),
        "fans": mr.integer(0, 0),
        "nikename": mr.cname(),
        "icon": mr.image('20x20', mr.color(), mr.cword(1)),
        "time": mr.integer(13, 13)
      }
    }) :
    res.jsonp({
      "err": 1,
      "msg": "注册失败",
    })

});

// 响应 mock 接口 自定义返回结构 定义 mock 接口别名

const router = jsonServer.router(db); // 创建路由对象 db 为 mock 接口路由配置  db==object


router.render = (req, res) =&gt; { // 自定义返回结构
  let len = Object.keys(res.locals.data).length; // 判断数据是不是空数组和空对象
  // console.log(len);

  setTimeout(() =&gt; { // 模拟服务器延时
    res.jsonp({
      err: len !== 0 ? 0 : 1,
      msg: len !== 0 ? '成功' : '失败',
      data: res.locals.data
    })
  }, 1000)

  // res.jsonp(res.locals.data)

};


server.use(jsonServer.rewriter({ // 路由自定义别名
  [mock + "/*"]: "/$1",
  "/course_category\\?uid=:id": "/course_category/:id",
  "/posts/:category": "/posts?category=:category",
  "/articles\\?id=:id": "/posts/:id"

}));

server.use(router); // 路由响应



// 开启 jsonserver 服务
server.listen(port, () =&gt; {
  console.log('mock server is running')
});</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>启动 app.js 文件 <code>node ./jsonserver/app.js</code></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">反向代理</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":219,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fumi3-12-1024x123.png&amp;size=m" alt="" class="wp-image-219">
</figure>
<!-- /wp:image -->
<!-- wp:paragraph -->
<p>工作中前后端是分离式开发，需要访问本地或者线上真实服务器时，跨域也成了每个前端工程师都需要了解的基本知识，解决方案有前端或者后端开发人员来解决</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>在 UMI3 中配置 <code>config/config</code> 的 <code>proxy</code> 键，接受一个对象，可单独做一个模块到 <code>config/proxy</code>，并暴露出来</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export default {
  '/api/': {
    // 要代理的真实服务器地址
    target: 'https://localhost:9001',
    // 配置了这个可以从 http 代理到 https
    https:true
    // 依赖 origin 的功能可能需要这个，比如 cookie
    changeOrigin: true,
    pathRewrite: { '^/api': '' }, // 路径替换
  } 
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">fetch 请求</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>fetch是原生的数据请求方法，返回 promise</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const getData = async () =&gt; {
    //let res = await fetch('/umi/goods/home?_limit=3'); 
        let res = await fetch(
    '/umi/login',{
      method:'post',
      headers:{"Content-type":"application/x-www-form-urlencoded"},
      body:'username=alex&amp;password=alex123'
    });
    let data = await res.json();
    console.log(data);
};</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">umi-request 请求</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>通过 <code>import { request } from 'umi';</code> 你可以使用内置的请求方法。第一个参数是 url，第二个参数是请求的 options。options 具体格式参考 <a href="https://github.com/umijs/umi-request/blob/master/README_zh-CN.md">umi-request</a>，也和 axios 用法基本一致</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const getData = async () =&gt; {
  let res = await request('/umi/goods',{params:{_limit:1}})
  // let res = await request('/api/goods/home',{params:{_limit:1}})
  console.log(res)
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">useRequest 请求</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>useRequest 是最佳实践中内置的一个 Hook ，默认对数据要求必须返回一个 data 字段，如果不希望有此限定，可以构建时配置一下 <code>config/config</code></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>request: {
  dataField: '',
},</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>在组件初次加载时， 自动触发该函数执行。同时 useRequest 会自动管理异步请求的 <code>loading</code>，<code>data</code>，<code>error</code> 等状态。</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import {useRequest} from 'umi'

export default function  RequestHooks(){

  // 用法 1
  const { data, error, loading } = useRequest('/umi/goods');

    // 用法 2
  const { data, error, loading } = useRequest({
    url: '/umi/goods',
    params:{_limit:1}
  });

    // 用法 4
  const { data, loading, run } = useRequest((_limit) =&gt; ({
    url: '/umi/goods',
    params: { _limit }
  }), {
    manual: true, // 手动通过运行 run 触发
  });

  // 轮询
  const { data, loading, run } = useRequest((_limit) =&gt; ({
    url: '/umi/goods',
    params: { _limit }
  }), {
    manual: true, // 手动通过运行 run 触发
    pollingInterval:1000, // 轮询 一秒读一次
    pollingWhenHidden:false, // 屏幕不可见时，暂停轮询
  });

  if (error) {
    return &lt;div&gt;failed to load&lt;/div&gt;
  }

  if (loading) {
    return &lt;div&gt;loading...&lt;/div&gt;
  }

  return (
    &lt;div&gt;{JSON.stringify(data)}&lt;/div&gt;
    &lt;button onClick={()=&gt;run(1)}&gt;手动&lt;/button&gt;
  );
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">状态管理</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">dva 介绍</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>dva 首先是一个基于 <a href="https://github.com/reduxjs/redux">redux</a> 和 <a href="https://github.com/redux-saga/redux-saga">redux-saga</a> 的数据流方案，被 umi 以插件的方式内置，无需安装直接使用，在原有 redux 使用基础上更加简化和高效</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>dva 里面有关状态管理（数据流）的角色和redux的对比如下：</p>
<!-- /wp:paragraph -->
<!-- wp:table -->
<figure class="wp-block-table">
 <table>
  <thead>
   <tr>
    <th></th>
    <th>redux</th>
    <th>dva</th>
   </tr>
  </thead>
  <tbody>
   <tr>
    <td>状态数据</td>
    <td>state</td>
    <td>state</td>
   </tr>
   <tr>
    <td>行为描述</td>
    <td>action</td>
    <td>action</td>
   </tr>
   <tr>
    <td>无副作用业务</td>
    <td>reducer</td>
    <td>reducer</td>
   </tr>
   <tr>
    <td>有副作用业务</td>
    <td>creators</td>
    <td>effect</td>
   </tr>
   <tr>
    <td>通讯请求修改状态函数</td>
    <td>dispatch</td>
    <td>dispatch</td>
   </tr>
   <tr>
    <td>通讯请求获取状态函数</td>
    <td>connect</td>
    <td>connect</td>
   </tr>
   <tr>
    <td>从 <strong>源</strong> 获取数据</td>
    <td>无</td>
    <td>subscription</td>
   </tr>
  </tbody>
 </table>
</figure>
<!-- /wp:table -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>[^状态数据]</code>: javascript 对象，存公共状态的仓库</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>[^行为描述]</code>: javascript 对象，必须带有 <code>type</code> 属性指明具体的行为，其它字段可以自定义</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>[^dispatch]</code>: 用于触发 action 的函数</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>[^无副作用业务]</code>: 一个纯函数，处理公共状态时的一些同步业务</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>[^有副作用业务]</code>: 处理公共状态时的一些异步业务</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">数据流向</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>数据的改变发生通常是通过用户交互行为或者浏览器行为（如路由跳转等）触发的，当此类行为会改变数据的时候可以通过 <code>dispatch</code> 发起一个 <code>action</code>，如果是同步行为会直接通过 <code>Reducers</code> 改变 <code>State</code> ，如果是异步行为（副作用）会先触发 <code>Effects</code> 然后流向 <code>Reducers</code> 最终改变 <code>State</code>，最后 <code>State</code> 的数据再流回组件页面
 <br>
 <img class="wp-image-211" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fumi3-13.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><strong>定义一个 dva 的 Model 如下：</strong></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export default {
  namespace:'Model名', // 省略不写，认定文件名为 Model 名
  state:{公共状态数据},
  reducers:{一堆纯函数},
  effects:{一堆异步副作用函数},
  subscription:{一堆监听函数}
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">全局数据&amp;页面数据获取和修改</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>全局数据定义在 <code>src/models/XX</code>，所有页面都可以访问，同步业务的处理交给 <code>reducers</code></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { history, request } from 'umi';
import key from 'keymaster';

export default {

  namespace: 'global', // 所有 models 里面的 namespace 不能重名

  // 初始化全局数据
  state: {
    title:'全局 title',
    text: '全局 text',
    login: false,
    a:'全局 models aaaa',
  },

  // 处理同步业务  接收 dispatch({type:'global/setText',...
  reducers: {
    setText(state) {
      // copy 更新并返回
      return {
        ...state,
        text: '全局设置 后的 text'+Math.random().toFixed(2),
      };
    },
    setTitle(state,action) { // action 接受到的参数
      return {
        ...state,
        title: `全局设置 后的 title${action.payload.a}/${action.payload.b}`,
      };
    },
    signin:(state)=&gt;({
      ...state,
      login: true,
    }),
  },

};</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p><strong>组件内部获取和修改全局数据</strong></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import {connect} from 'umi'

const 组件 = (props) =&gt; {
  return (
    &lt;&gt;
      &lt;h3 className="title"&gt;获取全局 state  &lt;/h3&gt;

      &lt;div&gt;text:{props.text}&lt;/div&gt;
      &lt;div&gt;title:{props.title}&lt;/div&gt;
      &lt;div&gt;a:{props.A}&lt;/div&gt;
      {
        props.isLogin ? &lt;div&gt;已登录&lt;/div&gt; : &lt;div&gt;未登录&lt;/div&gt;
      }

      &lt;h3 className="title"&gt;修改全局 state&lt;/h3&gt;
      &lt;button
        onClick={() =&gt; {
          props.dispatch({
            type: 'global/setText',
          });
        }}
      &gt;
        修改全局 text，不传参
      &lt;/button&gt;

      &lt;button
        onClick={() =&gt; {
          props.dispatch({
            type: 'global/setTitle',
            payload:{a:1,b:2}
          });
        }}
      &gt;
        修改全局 text，传参
      &lt;/button&gt;
    &lt;/&gt;
  );
}

export default connect(state =&gt; ({
  // 抓取全局,重命名
  text: state.global.text,
  title: state.global.title,
  A: state.global.a,
  isLogin: state.global.login,
}))(组件);</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p><strong>页面数据获取和修改</strong></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>页面数据定义在 <code>pages/页面目录/model.js</code> 或者 <code>pages/ 页面目录/models/*.js</code>，当前页面目录只分配一个数据文件时，使用 <code>model.js</code>，当前页面目录分配多个数据文件时，使用 <code>models/*.js</code></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>页面目录 <code>/*.jsx</code> 可访问当前页面目录 <code>/model.js</code> 及当前页面目录 <code>/models/*.js</code>，也可向上访问，<strong>但子集页面目录和同级页面目录数据不可访问</strong></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// pages/*/model.js
export default {
  namespace: 'dva',
  state: 'bmw', 
  reducers: {
    setStr(state) {
      return 'qq';
    },
  }
};

// pages/*/models/a.js
export default {
  namespace: 'a',
  state: 'page model a',
};</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p><strong>组件内部获取和修改页面数据</strong></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import {connect,getDvaApp} from 'umi'
import Child from './child'

const Dva = (props) =&gt; {
  return (
    &lt;&gt;
      &lt;h3 className="title"&gt;获取页面 models&lt;/h3&gt;
      &lt;p&gt;model.js 里面的数据:{props.dva}&lt;/p&gt;
      &lt;p&gt;models 目录里面的的数据:{props.a}&lt;/p&gt;
      &lt;p&gt;models 目录里面的的数据:{props.b}&lt;/p&gt;
      &lt;hr/&gt;
      &lt;h3 className='title'&gt;修改页面 model 数据&lt;/h3&gt;
      &lt;button onClick={()=&gt;props.dispatch({type:'dva/setStr'})}&gt;修改&lt;/button&gt;
    &lt;/&gt;
  );
}

export default connect(state =&gt; ({
  // 抓取页面级别
  dva:state.dva,
  a: state.a,
  b: state.b,
}))(Dva);

// connect 不传参不获取数据，dispatch 默认传给组件
// export default connect()(Dva);</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">异步处理逻辑</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>effects处理异步等一些有副作用的逻辑，如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { request } from 'umi';
export default {
  namespace: 'global', // 所有 models 里面的 namespace 不能重名
  state: {
    login: false,
  },
  reducers: { // 处理同步 左 key 接收 dispatch({type:key
    signin:(state,{type,payload})=&gt;({
      ...state,
      login: true, // payload 实际数据决定 login 的值
    }),
  },
  effects: {
    // 接收来自 dispatch({type:'global/login'...
    *login(action, { call, put, select }) {
      const data = yield call(request,'/umi/login',{method:'post',data:{username:action.payload.username,password:action.payload.password}})
      yield put({
        type: 'signin',
        payload:data
      });
    },
  }
};</code></pre>
<!-- /wp:code -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>[^put]</code>: 发出一个 Action，给 reducers</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>[^select]</code>: 从 state 里获取数据，如 <code>const todos = yield select(state =&gt; state.todos)</code></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>[^yield]</code>: 状态机语法，类似 await，同步书写异步代码</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>[^action]</code>: 可获取发送请求时的类型，携带的负载</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export default {
  namespace: 'count', // 命名空间，用于区分不同的模块
  state: 0, // 跨组件共享的数据
  reducers: {
    // 处理同步操作
    increment(state) {
      return state + 1;
    },
    decrement(state) {
      return state - 1;
    },
    incrementStep(state, action) {
      return state + action.payload;
    },
    decrementStep(state, action) {
      return state - action.payload;
    },
  },
  effects: {
    // 处理异步操作
    *incrementAsync(action, { put, call }) {
      yield call(delay, 2000); // 调用异步方法
      yield put({
        // 触发 reducers
        type: 'increment',
      });
    },
    *decrementAsync(action, { put, call }) {
      yield call(delay, 1000);
      yield put({
        type: 'decrement',
      });
    },
  },
};

const delay = (ms) =&gt; {
  // 模拟异步请求
  return new Promise((resolve, reject) =&gt; {
    setTimeout(() =&gt; {
      console.log('delay');
      resolve();
    }, ms);
  });
};</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React from 'react';
import { connect } from 'umi';
import { Button, Space } from 'antd';

function ComponentA(props) {
  return (
    &lt;div&gt;
      ComponentA --- {props.count}
      &lt;br /&gt;
      &lt;Space&gt;
        &lt;Button
          type="primary"
          onClick={() =&gt; props.dispatch({ type: 'count/increment' })}
        &gt;
          + 1
        &lt;/Button&gt;
        &lt;Button
          type="primary"
          onClick={() =&gt;
            props.dispatch({ type: 'count/incrementStep', payload: 5 })
          }
        &gt;
          + 5
        &lt;/Button&gt;
        &lt;Button
          type="primary"
          onClick={() =&gt; props.dispatch({ type: 'count/incrementAsync' })}
        &gt;
          async + 1
        &lt;/Button&gt;
      &lt;/Space&gt;
    &lt;/div&gt;
  );
}

const mapStateToProps = (state) =&gt; {
  return {
    count: state.count,
  };
};
export default connect(mapStateToProps)(ComponentA);</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">丢弃 connect 高级组件，转投 hooks</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { useDispatch, useSelector } from 'umi';

const 组件 = () =&gt; {
  const dispatch = useDispatch();
  const { dva } = useSelector((state) =&gt; ({ dva: state.dva }));
  return (
    &lt;&gt;
      &lt;h3 className="title"&gt;子组件3&lt;/h3&gt;
      &lt;div&gt;{dva}&lt;/div&gt;
      &lt;div&gt;
        &lt;button
          onClick={() =&gt; {
            dispatch({ type: 'global/setTitle', payload: { a: 11, b: 22 } });
          }}
        &gt;
          修改全局 model
        &lt;/button&gt;
      &lt;/div&gt;
    &lt;/&gt;
  );
};

export default 组件;</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">subscriptions 源<code>*</code>获取</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>订阅一个数据 “源” 的变化，使用场景如：服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import key from 'keymaster';

export default {
  namespace: 'global',
  state: {
  },
  subscriptions: {
    listenRoute({ dispatch,history}) {
      history.listen(({ pathname, query }) =&gt; {
        console.log('global subscriptions',pathname,query);
      });
    },
    listenKeyboard({dispatch}) { // 监听键盘
      key('⌘+i, ctrl+i', () =&gt; { dispatch({type:'setText'}) });
    },
    listenResize({dispatch}) { // 监听窗口变化
      window.onresize = function(){
        console.log('onresize')
      }
    },
    listenScroll({dispatch,history}){
      window.onscroll=function () {
        console.log('onscroll')
      }
    }
  },
};</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">运行时配置</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>构建时配置 <code>config/config</code> 能覆盖大量场景，但有一些却是编译时很难触及的。比如：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>在出错时显示个 message 提示用户</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>在加载和路由切换时显示个 loading</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>页面载入完成时请求后端，根据响应动态修改路由
  <br>
  运行时配置和构建时配置的区别是他跑在浏览器端，基于此，我们可以在这里写函数、jsx、import 浏览器端依赖等等，注意不要引入 node 依赖。</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:paragraph -->
<p><strong>配置方式</strong></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>约定 <code>src/app.jsx</code> 定义并暴露一些固定名称的函数，他们会在浏览器端择机运行，全局执行一些业务</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">渲染前的权限校验</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":212,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fumi3-14-1024x335.png&amp;size=m" alt="" class="wp-image-212">
</figure>
<!-- /wp:image -->
<!-- wp:paragraph -->
<p>render 函数， 用于改写把整个应用 render 到 dom 树里， 覆写 render，接受一个 oldRender 函数，最后用 oldRender 来渲染 dom， 需至少被调用一次</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export const render = async (oldRender) =&gt; {
  const { isLogin} = await request('/umi/auth');
  if (!isLogin) {
    history.push('/login');
  }
  // oldRender， Function，原始 render 方法，需至少被调用一次
  oldRender();
}  </code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// mock/auth  
'GET /umi/auth': (req, res) =&gt; {
  res.send({
    isLogin: true,
  });
},</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">动态路由读取、添加</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":213,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fumi3-15.png&amp;size=m" alt="" class="wp-image-213">
</figure>
<!-- /wp:image -->
<!-- wp:paragraph -->
<p><code>patchRoutes</code> 函数提供了在运行时，动态修改路由的入口，一般可以和 render 函数配合， 请求服务端根据响应动态更新路由</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export function patchRoutes({ routes }) {
    // routes 为当前路由
  routes.unshift({
    path: '/foo',
    exact: true,
    component: require('@/pages/foo').default,
  });
}</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p><strong>需要注意的地方是：</strong></p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>动态路由的 compnent 要的是一个组件不是一段地址，可通过 require 引入</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>动态路由读取后，跳转后不显示，需要关闭 <code>mfsu: {}</code></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>子路由不跳转，除了 layout 组件，其他需要添加 exact，构建时的配置在编译后都会自动加，而动态路由如果路由数据没有 exact 会导致不跳转</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>数据数据里面不可以有 require，数据需要过滤，require（非空字符拼接+变量）</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>document.ejs 报错，需要 require 拼接时找到 index.jsx 目前 umi3 有这个问题</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">模拟路由数据</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// mock/auth

'GET /umi/menus': (req, res) =&gt; {
    res.send([
      {
        path: '/',
        component: 'layouts/layout1',
        routes: [
          {
            title: '资源引入',
            path: '/resources',
            component: 'pages/css-img',
          },
          { path: '/less', component: 'pages/less' },
          {
            path: '/goods',
            component: 'layouts/layout2',
            routes: [
              { path: '/goods/:id?', component: 'pages/goods/goods-detail' },
              {
                path: '/goods/:id/comment',
                component: 'pages/goods/comment',
              },
              {
                path: '/goods/:id/comment/:cid',
                component: 'pages/goods/comment/comment-detail',
              },
              { component: 'pages/404' },
            ],
          },
          { path: '/data-interaction', component: 'pages/data-interaction' },
          { path: '/dva', component: 'pages/dva' },
          { path: '/antd', component: 'pages/antd' },
          { path: '/hooks', component: 'pages/hooks' },
          {
            path: '/user',
            component: 'pages/user',
            wrappers: ['wrappers/auth'],
          },

          { path: '/', redirect: '/antd' },
          { component: 'pages/404' },
        ],
      },
      { component: 'pages/404' },
    ]);
  },</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">读取路由数据并添加路由</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// src/app

let routesData = []; // 模块变量用来存储路由数据

// render 函数里面读取路由数据
export const render = async (oldRender) =&gt; {
  const { isLogin } = await http('/umi/auth');
  if (isLogin) {
    // 获取路由数据
    routesData = await http('/umi/menus');
  } else {
    history.push('/login');
  }
  oldRender();
};

export function patchRoutes({ routes }) {
  filterRoutes(routesData); // 处理数据，添加 exact，指定 index.js，拼接 require
  routesData.map((item) =&gt; routes.push(item)); // 动态添加路由
}

const filterRoutes = (routesData) =&gt; {
  routesData.map((item) =&gt; {

    // exact 处理
    if (item.routes &amp;&amp; item.routes.length &gt; 0) {
      filterRoutes(item.routes); // 含 routes 键的需要递归处理
    } else {
      item.exact = true; // 不含 routes 键的都添加 exact
    }

    // component 地址拼接处理
    if (!item.redirect) { // 不处理带有 redirect 字段
      if (item.component.includes('404')) { // 404 没有 index 文件结构
        item.component = require('@/' + item.component + '.jsx').default;
      } else {
        // 其他页面都指向 index 结构，避免 umi3 的 document.ejs 报错
        item.component = require('@/' + item.component + '/index.jsx').default;
      }
         // 部分需要授权路由的拼接
      if (item.wrappers &amp;&amp; item.wrappers.length &gt; 0) {
        item.wrappers.map((str, index) =&gt; {
          item.wrappers[index] = require('@/' + str + '.jsx').default;
        });
      }
    }
  });
};</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 动态路由地址要 require 引入
// require(需要字符拼接+变量)
// document.ejs报错，需要找到index.jsx

const filterRoutes = (routesData) =&gt; {
  routesData.map((item) =&gt; {
    if (item.routes &amp;&amp; item.routes.length &gt; 0) {
      filterRoutes(item.routes);
    } else {
      item.exact = true;
    }
    if (!item.redirect) {
      if (item.component.includes('404')) {
        item.component = require('@/' + item.component + '.jsx').default;
      } else {
        item.component = require('@/' + item.component + '/index.jsx').default;
      }
      if (item.wrappers &amp;&amp; item.wrappers.length &gt; 0) {
        item.wrappers.map((str, index) =&gt; {
          item.wrappers[index] = require('@/' + str + '.jsx').default;
        });
      }
    }
  });
};
export { filterRoutes };</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { request as http, history } from 'umi';
import { filterRoutes } from './utils/filterRoutes';

let routesData = [];

export function patchRoutes ({ routes }) {
  // 动态添加路由
  console.log('pathroutes', routes);

  /*   routes.push({
    path: '/',
    component: require('@/layouts/layout1').default,
    routes: [
      {
        exact: true,
        title: '资源引入',
        path: '/resources',
        component: require('@/pages/css-img').default,
      },
      {
        exact: true,
        path: '/less',
        component: require('@/pages/less').default,
      },
      {
        path: '/goods',
        component: require('@/layouts/layout2').default, //展示区
        routes: [
          {
            exact: true,
            path: '/goods/:id?',
            component: require('@/pages/goods/goods-detail').default,
          },
          {
            exact: true,
            path: '/goods/:id/comment',
            component: require('@/pages/goods/comment').default,
          },
          {
            exact: true,
            path: '/goods/:id/comment/:cid',
            component: require('@/pages/goods/comment/comment-detail').default,
          },
          { exact: true, component: require('@/pages/404').default },
        ],
      },
      {
        exact: true,
        path: '/data-interaction',
        component: require('@/pages/data-interaction').default,
      },
      {
        exact: true,
        path: '/dva',
        component: require('@/pages/dva').default,
      },
      {
        exact: true,
        path: '/antd',
        component: require('@/pages/antd').default,
      },
      {
        exact: true,
        path: '/hooks',
        component: require('@/pages/hooks').default,
      },
      {
        exact: true,
        path: '/user',
        component: 'user',
        wrappers: [require('@/wrappers/auth').default],
      }, // 路由守卫

      { exact: true, path: '/', redirect: '/antd' },
      { exact: true, component: require('@/pages/404').default },
    ],
  });
  routes.push({ exact: true, component: require('@/pages/404').default }); */
  filterRoutes(routesData);
  routesData.map((item) =&gt; routes.push(item));
  console.log('patchRoutes', routes);
}

export const render = async (oldRender) =&gt; {
  //只运行首次
  /* console.log(
    'render 渲染之前做一些权限校验，读取路由数据，在patchRoutes之前运行，',
  ); */

  const { isLogin } = await http('/umi/auth');
  if (isLogin) {
    // 获取路由数据
    routesData = await http('/umi/menus');
    // console.log(111, routesData);
  } else {
    history.push('/login');
  }

  // oldRender， Function，原始 render 方法，需至少被调用一次
  oldRender();

  /* fetch('/api/auth').then(auth =&gt; {
    if (auth.isLogin) { 获取路由数据 let routesData = auth.routes;oldRender() }
    else { history.push('/login'); }
  }); */
};

export function onRouteChange ({ matchedRoutes, location, routes, action }) {
  console.log('routes', routes); // 路由集合
  console.log('matchedRoutes', matchedRoutes); // 当前匹配的路由及其子路由
  console.log('location', location); // location及其参数
  console.log('action', action); // 当前跳转执行的操作
  // console.log('初始加载和路由切换时的逻辑')
  // 初始加载和路由切换时的逻辑,用于路由监听, action 是路由切换方式如：push
  // 用于做埋点统计
  // 动态设置标题
  document.title =
    matchedRoutes[matchedRoutes.length - 1].route.title || 'heheda';
}
export const request = {
  // timeout: 1000,
  // errorConfig: {},
  // middlewares: [],
  requestInterceptors: [
    (url, options) =&gt; {
      // 请求地址 配置项
      options.headers = {
        token:
          'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFsZXgiLCJfaWQiOiI1ZThhMGQ2MzczNDg2MDIzYTRmZDY4ZGYiLCJpYXQiOjE1ODkxMDQ4NjcsImV4cCI6MTU4OTE5MTI2N30.e0GWBDhYILXOuCpoCXq75T4PeBiNFgSab54sMe6yTk4',
      };
      // console.log('请求地址 配置项');
      return { url, options };
    },
  ],
  responseInterceptors: [
    (response, options) =&gt; {
      // 响应体 请求时的配置项
      // console.log('响应体 请求时的配置项');
      return response;
    },
  ],
};</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">路由监听，埋点统计</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":214,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fumi3-16.png&amp;size=m" alt="" class="wp-image-214">
</figure>
<!-- /wp:image -->
<!-- wp:paragraph -->
<p><code>onRouteChange</code> 函数内部可以设置，在初始加载和路由切换时做一些事情，比如埋点，设置动态标题等操作</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export function onRouteChange({ matchedRoutes, location, routes, action }) {
  // 动态设置标题
  document.title = matchedRoutes[matchedRoutes.length - 1].route.title || '默认标题'
}</code></pre>
<!-- /wp:code -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>[^location]</code>: history 提供的 location 对象</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>[^routes]</code>: 路由集合</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>[^action]</code>: PUSH|POP|REPLACE|undefined，初次加载时为 undefined</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export function onRouteChange({ matchedRoutes, location, routes, action }) {
  console.log('routes', routes) // 路由集合
  console.log('matchedRoutes', matchedRoutes) // 当前匹配的路由及其子路由
  console.log('location', location) // location 及其参数
  console.log('action', action) // 当前跳转执行的操作

  // 动态设置标题
  document.title = matchedRoutes[matchedRoutes.length - 1].route.title || '默认标题'
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">拦截器</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":207,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fumi3-17.png&amp;size=m" alt="" class="wp-image-207">
</figure>
<!-- /wp:image -->
<!-- wp:paragraph -->
<p>umi 内置的 request 和 useRequest 在发送请求之前和数据返回后，可以做一些通用的配置业务，这个时候考虑配置拦截器，参考<a href="https://v3.umijs.org/plugins/plugin-request">插件配置</a></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export const request = {
  requestInterceptors: [
    (url, options) =&gt; {
      // 请求地址 配置项
      options.headers = {
        token:'..',
      };
      return { url, options };
    },
  ],
  responseInterceptors: [],
};</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// src/app.js
export const request = {
  // timeout: 1000, // 延时
  // errorConfig: {}, // 错误处理
  // middlewares: [], // 使用中间件
  requestInterceptors: [ // 请求拦截器，数组里面是函数
    // 参数是请求地址和配置项
    (url, options) =&gt; {
      options.headers = {token: 'xxx'}
      return {url, options}
    }
  ], 
  responseInterceptors: [ // 响应拦截器
    // 参数是响应体和请求时配置项
    (response, options) =&gt; {
      return response
    }
  ] 
}</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>umi3 一般适合开发一些 h5 端的各类 web 应用，如果考虑开发中台管理系统，可以去看看蚂蚁系提供的 <a href="https://pro.ant.design/zh-CN/">antd-pro</a> 框架</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->]]></description><guid isPermaLink="false">/archives/umi3</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Tue, 21 May 2024 07:16:35 GMT</pubDate></item><item><title><![CDATA[Mock]]></title><link>https://blog.qiaofugui.cn/archives/mock</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=Mock&amp;url=/archives/mock" width="1" height="1" alt="" style="opacity:0;"><!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">mock 的使用</h1>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>安装 mockjs
  <br>
  <code>bower install --save mockjs</code></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>文件中导入 mockjs 返回有个 mock 对象
  <br>
  <code>const Mock = require('mockjs')</code></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>mock 对象中有一个 mock 函数 mock 会根据自己编写的模板自动生成模拟数据</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>官网：http://mockjs.com</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">初体验</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code> const Mock = require('mockjs')

/**
 * 返回的模拟数据都是对象类型
 *  返回对象的 key list
 */
const data = Mock.mock({
  // 生成的随机数组的长度是 5-10
  "list|5-10": [
    // 数组中的元素是对象类型 "id|+1": 1 生成的 id 是从 1 开始递增的
    { "id|+1": 1 }
  ]
})

console.log(data)
/*
{
  list: [
    { id: 1 }, { id: 2 }, { id: 3 },
    { id: 4 }, { id: 5 }, { id: 6 },
    { id: 7 }, { id: 8 }, { id: 9 }
  ]
}
*/   </code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">Mock 语法规范</h1>
<!-- /wp:heading -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>文档：https://github.com/nuysoft/Mock/wiki</p>
 <!-- /wp:paragraph -->
 <!-- wp:list {"ordered":true} -->
 <ol>
  <!-- wp:list-item -->
  <li>数据模板定义规范（Data Template Definition，DTD）</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>数据占位符定义规范（Data Placeholder Definition，DPD）</li>
  <!-- /wp:list-item -->
 </ol>
 <!-- /wp:list -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">数据模板定义规范 DTD</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>数据模板中的每个属性由 3 部分构成：属性名、生成规则、属性值：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 属性名   name
// 生成规则 rule
// 属性值   value
'name|rule': value

'id|+1':1 // 属性名是 id，规则是自增 1，从 1 开始</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p><strong>注意：</strong></p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>属性名 和 生成规则 之间用竖线 <code>|</code> 分隔。</p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>生成规则 是可选的。</p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>生成规则 有 7 种格式：</p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>I. <code>'name|min-max': value</code></p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>II. <code>'name|count': value</code></p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>III. <code>'name|min-max.dmin-dmax': value</code></p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>IV. <code>'name|min-max.dcount': value</code></p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>V. <code>'name|count.dmin-dmax': value</code></p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>VI. <code>'name|count.dcount': value</code></p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>VII. <code>'name|+step': value</code></p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>生成规则 的 含义 需要依赖 属性值的类型 才能确定。</p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p><em>属性值 中可以含有 <code>@占位符</code>。</em></p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p><em>属性值 还指定了最终值的初始值和类型。</em></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">生成规则和示例：</h3>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">1. 属性值是字符串 String</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><code>'name|min-max': string</code>
  <br>
  通过重复 <code>string</code> 生成一个字符串，重复次数大于等于 <code>min</code>，小于等于 <code>max</code>。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>'name|count': string</code>
  <br>
  通过重复 <code>string</code> 生成一个字符串，重复次数等于 <code>count</code>。</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">2. 属性值是数字 Number</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><code>'name|+1': number</code>
  <br>
  属性值自动加 1，初始值为<code>number</code>。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>'name|min-max': number</code>
  <br>
  生成一个大于等于 <code>min</code>、小于等于 <code>max</code> 的整数，属性值 <code>number</code> 只是用来确定类型。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>'name|min-max.dmin-dmax': number</code>
  <br>
  生成一个浮点数，整数部分大于等于 <code>min</code>、小于等于 <code>max</code>，小数部分保留 <code>dmin</code> 到 <code>dmax</code> 位。</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Mock.mock({
    'number1|1-100.1-10': 1,
    'number2|123.1-10': 1,
    'number3|123.3': 1,
    'number4|123.10': 1.123
})
// =&gt;
{
    "number1": 12.92,
    "number2": 123.51,
    "number3": 123.777,
    "number4": 123.1231091814
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">3. 属性值是布尔型 Boolean</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><code>'name|1': boolean</code>
  <br>
  随机生成一个布尔值，值为 true 的概率是 1/2，值为 false 的概率同样是 1/2。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>'name|min-max': value</code>
  <br>
  随机生成一个布尔值，值为 <code>value</code> 的概率是 <code>min / (min + max)</code>，值为 <code>!value</code> 的概率是 <code>max / (min + max)</code>。</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">4. 属性值是对象 Object</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>'name|count': object
  <br>
  从属性值 <code>object</code> 中随机选取 <code>count</code> 个属性。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>'name|min-max': object</code>
  <br>
  从属性值 <code>object</code> 中随机选取 <code>min</code> 到 <code>max</code> 个属性。</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">5. 属性值是数组 Array</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><code>'name|1': array</code>
  <br>
  从属性值 <code>array</code> 中随机选取 1 个元素，作为最终值。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>'name|+1': array</code>
  <br>
  从属性值 <code>array</code> 中顺序选取 1 个元素，作为最终值。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>'name|min-max': array</code>
  <br>
  通过重复属性值 <code>array</code> 生成一个新数组，重复次数大于等于 <code>min</code>，小于等于 <code>max</code>。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>'name|count': array</code>
  <br>
  通过重复属性值 <code>array</code> 生成一个新数组，重复次数为 <code>count</code>。</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">6. 属性值是函数 函数</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><code>'name': function</code>
  <br>
  执行函数 <code>function</code>，取其返回值作为最终的属性值，函数的上下文为属性 <code>'name'</code> 所在的对象。</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">7. 属性值是正则表达式 RegExp</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><code>'name': regexp</code>
  <br>
  根据正则表达式 <code>regexp</code> 反向生成可以匹配它的字符串。用于生成自定义格式的字符串。</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Mock.mock({
    'regexp1': /[a-z][A-Z][0-9]/,
    'regexp2': /\w\W\s\S\d\D/,
    'regexp3': /\d{5,10}/
})
// =&gt;
{
    "regexp1": "pJ7",
    "regexp2": "F)\fp1G",
    "regexp3": "561659409"
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">数据占位符定义规范 DPD</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><em>占位符 只是在属性值字符串中占个位置，并不出现在最终的属性值中。</em></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><em>占位符 的格式为：</em></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>@占位符
@占位符(参数 [, 参数])</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>注意：</p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>用 <code>@</code> 来标识其后的字符串是 占位符。</p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p><em>占位符</em> 引用的是 <code>Mock.Random</code> 中的方法。</p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>通过 <code>Mock.Random.extend()</code> 来扩展自定义占位符。</p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p><em>占位符</em> 也可以引用 <em>数据模板</em> 中的属性。</p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p><em>占位符</em> 会优先引用 <em>数据模板</em> 中的属性。</p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p><em>占位符</em> 支持 <em>相对路径</em> 和 <em>绝对路径</em>。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Mock.mock({
    name: {
        first: '@FIRST',
        middle: '@FIRST',
        last: '@LAST',
        full: '@first @middle @last'
    }
})
// =&gt;
{
    "name": {
        "first": "Charles",
        "middle": "Brenda",
        "last": "Lopez",
        "full": "Charles Brenda Lopez"
    }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">生成模拟数据模板</h1>
<!-- /wp:heading -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>示例：http://mockjs.com/examples.html</p>
 <!-- /wp:paragraph -->
 <!-- wp:heading -->
 <h2 class="wp-block-heading">字符串和数值一共有 7 种生成规则</h2>
 <!-- /wp:heading -->
 <!-- wp:table -->
 <figure class="wp-block-table">
  <table>
   <thead>
    <tr>
     <th>生成规则</th>
     <th>说明</th>
     <th>示例</th>
    </tr>
   </thead>
   <tbody>
    <tr>
     <td><code>min-max</code></td>
     <td>生成 min-max 之间的字符串</td>
     <td><code>'list|1-10'</code></td>
    </tr>
    <tr>
     <td><code>count</code></td>
     <td>生成 count 个字符串</td>
     <td><code>'list|5'</code></td>
    </tr>
    <tr>
     <td><code>min-max.dmin-dmax</code></td>
     <td>生成 min-max 之间的浮点数，小数点后面保留 dmin-dmax 位</td>
     <td><code>'id|1-10.1-3'：1</code></td>
    </tr>
    <tr>
     <td><code>count.dcount</code></td>
     <td>生成 count 个浮点数，小数点保留 dcount 位</td>
     <td><code>'id|10.2'：1</code></td>
    </tr>
    <tr>
     <td><code>min-max.dcount</code></td>
     <td>同上</td>
     <td><code>'id|1-10.3':1</code></td>
    </tr>
    <tr>
     <td><code>count.dmix-dmax</code></td>
     <td>同上</td>
     <td><code>'id|10.1-3':1</code></td>
    </tr>
    <tr>
     <td><code>+step</code></td>
     <td>每次累计 step 值</td>
     <td><code>'id|+1':1</code></td>
    </tr>
   </tbody>
  </table>
 </figure>
 <!-- /wp:table -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">布尔值，对象和数组的生成规则</h2>
<!-- /wp:heading -->
<!-- wp:table -->
<figure class="wp-block-table">
 <table>
  <thead>
   <tr>
    <th>生成规则</th>
    <th>说明</th>
    <th>示例</th>
   </tr>
  </thead>
  <tbody>
   <tr>
    <td>布尔值</td>
    <td>生成概率</td>
    <td><code>'flag|1':true</code></td>
   </tr>
   <tr>
    <td>布尔值 <code>min-max</code></td>
    <td>生成概率为 min/(min+max)</td>
    <td><code>'flag|1-10':true</code></td>
   </tr>
   <tr>
    <td>对象 <code>count</code></td>
    <td>从对象中随机抽取 count 个属性</td>
    <td><code>'obj|2':obj</code></td>
   </tr>
   <tr>
    <td>对象 <code>min-max</code></td>
    <td>从对象中随机抽取 min-max 个属性</td>
    <td><code>'obj|1-5':obj</code></td>
   </tr>
   <tr>
    <td>数组 <code>1</code></td>
    <td>获取一次数组</td>
    <td><code>'arr|1':arr</code></td>
   </tr>
   <tr>
    <td>数组 <code>+1</code></td>
    <td>累加</td>
    <td><code>'arr|+1':arr</code></td>
   </tr>
   <tr>
    <td>数组 <code>min-max</code></td>
    <td>重复 min-max 次组成一个新的数组</td>
    <td><code>'arr|1-5':arr</code></td>
   </tr>
  </tbody>
 </table>
</figure>
<!-- /wp:table -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>更多：官方文档 https://github.com/nuysoft/Mock/wiki</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">随机生成字符串</h1>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

const data = Mock.mock({
  "a": "#", // 返回数据对象的 key 就是 a，值是 #
  "b|3":"#",
  "c|1-3":"#", // 返回 1-3 个 #
})

console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">随机生成数字</h1>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

const data = Mock.mock({
  "a": 1, // 返回数据对象的 key 就是 a，值是 1
  "b|1-100": 1, // 随机生成 1-100 中数值
})

console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">随机生成布尔值</h1>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

const data = Mock.mock({
  "a": true, // 返回数据对象的 key 就是 a，值是 true
  "b|1-2": false, // 随机返回 true 或 false
})

console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">随机生成对象</h1>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

const cities = {
  "bj": "北京",
  "sh": "上海",
  "gz": "广州",
  "sz": "深圳",
  "xm": "厦门"
}
const data = Mock.mock({
  "a|3": cities, // 随机生成一个只有三个元素的对象，从 cities 里面
  "b|2-5": cities, // 随机生成一个有 2 个到 5 个元素的对象，从 cities 里面
})

console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">随机生成数组</h1>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

const arr = ["北京", "上海", "广州"]
const data = Mock.mock({
  // 匿名数组：生成有几个元素到几个的元素数组
  "a|3": ["北京"], // 生成一个只有三个元素的数组
  "b|2-5": ["北京"], // 随机生成 2 个到 5 个元素的数组
  // 命名数组：将数组的元素生成几次
  "c|2": arr, // 将 arr 重复 2 次生成新数组
  "d|1-3": arr // 将 arr 重复 1-3 次生成新数组
})

console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">数据占位符定义</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">随机生成字符串</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

const data = Mock.mock({
  "a": Mock.Random.string(), // 随机生成字符串
  "b": Mock.mock("@string"), // 模板方式，没有参数时可以不用加括号
  "c": Mock.mock("@string(6)"), // 随机生成 6 位字符串
  "d": Mock.mock("@string(upper,8)"), // 随机都是大写，长度为 8 位的字符串
  "e": Mock.mock("@string(4,8)"), // 获取指定长度范围的字符串
  "f": Mock.mock("@string(lower,4,8)"), // 获取都是小写指定长度范围的字符串
})
console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">随机获取整数元素数组</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

const data = Mock.mock({
  "a": Mock.mock("@range(10)"), // 获取长度为 10 的数组，数组元素从 0 开始，每个元素递增 1
  "b": Mock.mock("@range(5,11)"), // 获取里面元素是 5~10 的数组
  "c": Mock.mock("@range(1,10,2)"), // [ 1, 3, 5, 7, 9 ] 步长是 2
})
console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">随机获取时间</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

const data = Mock.mock({
  "a": Mock.mock('@date'), // 随机获取时间，年-月-日
  "b": Mock.mock('@date(yyyy/MM/dd HH:mm:ss)'), // 随机获取格式化后的时间，年/月/日 hh:mm:ss
})
console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">随机获取图片</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

// Random.image( size?, background?, foreground?, format?, text? )
const data = Mock.mock({
  "a": Mock.mock('@image'), // 随机获取一张图片
  "b": Mock.mock('@image(300x300)'), // 随机获取指定大小的图片
  "c": Mock.mock('@image(300x300,#ffc0cb)'), // 随机获取指定大小指定背景的图片
  "d": Mock.mock('@image(300x300,#ffc0cb,#ff0000,xxx)'), // 随机获取指定大小指定背景指定文字颜色的图片
  "e": Mock.mock('@image(300x300,#ffc0cb,#ff0000,gif,xxx)'), // 随机获取指定大小指定背景指定文字颜色指定图片类型的图片
})
console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">随机获取颜色</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

const data = Mock.mock({
  "a": Mock.mock('@color'), // 16 进制表示的颜色
  "b": Mock.mock('@hex'), // 16 进制表示的颜色
  "c": Mock.mock('@rgb'), // rgb 的颜色
  "d": Mock.mock('@rgba'), // rgba 的颜色
})
console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">随机获取文本</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">cparagraph</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

const data = Mock.mock({
  "a": Mock.mock('@cparagraph'), // 随机生成一段中文段落
  "b": Mock.mock('@cparagraph(2)'), // 生成的段落包含 2 句话
  "c": Mock.mock('@cparagraph(1,3)'), // 生成的段落包含 1~3 句话
})
console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">csentence</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

const data = Mock.mock({
  "a": Mock.mock('@csentence'), // 随机生成一句中文句子
  "b": Mock.mock('@csentence(5)'), // 随机生成含有 5 个字符的中文句子
  "c": Mock.mock('@csentence(5,10)'), // 随机生成含有 5~10 个字符的中文句
})
console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">cword</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

const data = Mock.mock({
  "a": Mock.mock('@cword'), // 随机生成一个汉字
  "b": Mock.mock('@cword(5)'), // 随机生成 5 个汉字
  "c": Mock.mock('@cword(3,5)'), // 随机生成 3~5 个汉字
  "d": Mock.mock('@cword(零一二三四五六七八九十)'), // 从该字符串中随机取得一个字符
  "e": Mock.mock('@cword(零一二三四五六七八九十,3)'), // 从该字符串中随机取得 3 个字符
  "f": Mock.mock('@cword(零一二三四五六七八九十,3,5)'), // 从该字符串中随机取得 3~5 个字符
})
console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">ctitle</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

const data = Mock.mock({
  // min 不能小于3，max 不能大于7
  "a": Mock.mock('@ctitle'), // 随机生成一个标题，3~7 之间
  "b": Mock.mock('@ctitle(5)'), // 随机生成一个 5 个字符的标题，3~7 之间
  "c": Mock.mock('@ctitle(3,7)'), // 随机生成 3~7 个字符的标题
})
console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">随机获取名字</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

const data = Mock.mock({
  "a": Mock.mock('@cfirst'), // 随机生成一个姓氏
  "b": Mock.mock('@clast'), // 随机生成一个名字
  "d": Mock.mock('@cname'), // 随机生成一个姓名
})
console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">随机获取 web 相关数据</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

const data = Mock.mock({
  a: Mock.Random.protocol(), // 随机生成一个协议
  b: Mock.Random.domain(), // 随机生成一个域名
  d: Mock.Random.tld(), // 随机生成一个顶级域名
  e: Mock.Random.email(), // 随机生成一个邮箱
  f: Mock.Random.email('qq.com'), // 随机生成一个带有域名的邮箱
  g: Mock.Random.ip(), // 随机生成一个 ip 地址
  h: Mock.Random.url(), // 协议+域名
  i: Mock.Random.url('https'), // https 协议的域名
  j: Mock.Random.url('https','www.qq.com'), // https 协议的 www.qq.com 域名
})
console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">随机获取地域信息</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

const data = Mock.mock({
  a: Mock.Random.region(), // 随机生成一个中国的大区域
  b: Mock.Random.province(), // 随机生成一个中国的省
  c: Mock.Random.city(), // 随机生成一个中国的市
  d: Mock.Random.city(true), // 随机生成一个中国的市，带前缀
  e: Mock.Random.county(), // 随机生成一个中国的县
  f: Mock.Random.county(true), // 随机生成一个中国的县, 带前缀
  g: Mock.Random.zip(), // 随机生成一个邮编
})
console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">帮助函数</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

const data = Mock.mock({
  a: Mock.Random.capitalize('hello'), // 首字母大写 Hello
  b: Mock.Random.upper('Hello'), // 全部大写 HELLO
  c: Mock.Random.lower('Hello'), // 全部小写 hello
  d: Mock.Random.pick(['张三', '李四', '王五']), // 数组中随机取一个
  e: Mock.Random.shuffle(['张三', '李四', '王五']), // 数组中随机打乱
})
console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">生成混杂数据</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const Mock = require('mockjs')

const data = Mock.mock({
  a: Mock.Random.guid(), // 随机生成一个 GUID
  b: Mock.Random.id(), // 随机生成一个 18 位身份证
  c: Mock.Random.increment(), // 生成一个全局的自增整数
  d: Mock.Random.increment(2), // 生成一个全局的自增整，数整数自增的步长为 2
})
console.log(data)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">搭建服务器</h1>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>全局安装 <code>mock-server</code> 服务器包
  <br>
  <code>npm i @shymean/mock-server -g</code></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>通过 <code>mock</code> 命令启动服务器 js 文件
  <br>
  <code>mock -p 9999 -f 文件名.js</code></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>通过浏览器 postman 工具访问该服务器从而获取数据</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p><a href="https://www.npmjs.com/package/@shymean/mock-server">https://www.npmjs.com/package/@shymean/mock-server</a></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// mock.js
const Mock = require('mockjs')

/**
 * 参数1：请求路径
 * 参数2：请求方法
 * 参数3：返回数据
 */
Mock.mock('/home', 'get', {
  code: 200,
  data: {
    title: Mock.Random.ctitle(5),
    img: Mock.Random.image('200x200'),
    arr: Mock.Random.pick(['张三', '李四', '王五'])
  }
})

// mock -p 9999 -f mock.js</code></pre>
<!-- /wp:code -->]]></description><guid isPermaLink="false">/archives/mock</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Tue, 21 May 2024 07:11:43 GMT</pubDate></item><item><title><![CDATA[Uni-App]]></title><link>https://blog.qiaofugui.cn/archives/uni-app</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=Uni-App&amp;url=/archives/uni-app" width="1" height="1" alt="" style="opacity:0;"><!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">uni-app 简介</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>uni-app 是一个使用 Vue.js 开发所有前端应用的框架。开发者编写一套代码，可发布到 iOS、Android、H5、以及各种小程序（微信/支付宝/百度/头条/QQ/钉钉/淘宝）、快应用等多个平台。</p>
<!-- /wp:paragraph -->
<!-- wp:image {"id":208,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Funiapp01-1024x314.png&amp;size=m" alt="" class="wp-image-208">
</figure>
<!-- /wp:image -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>详细的 uni-app 官方文档，请翻阅 https://uniapp.dcloud.net.cn</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">开发工具</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>uni-app 官方推荐使用 HBuilderX 来开发 uni-app 类型的项目。主要好处：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>模板丰富</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>完善的智能提示</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>一键运行</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>当然，你依然可以根据自己的喜好，选择使用 VS Code、Sublime、记事本… 等自己喜欢的编辑器！</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">安装 scss/sass 编译</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>为了方便编写样式（例如：<code>&lt;style lang="scss"&gt;&lt;/style&gt;</code>），建议安装 scss/sass 编译 插件：https://ext.dcloud.net.cn/plugin?name=compile-node-sass</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">新建 uni-app 项目</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>填写项目基本信息</p>
<!-- /wp:paragraph -->
<!-- wp:image {"id":209,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Funiapp02.png&amp;size=m" alt="" class="wp-image-209">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">目录结构</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>一个 uni-app 项目，默认包含如下目录及文件：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>┌─components          uni-app组件目录
│  └─comp-a.vue       可复用的a组件
├─pages               业务页面文件存放的目录
│  ├─index
│  │  └─index.vue     index页面
│  └─list
│     └─list.vue      list页面
├─static              存放应用引用静态资源（如图片、视频等）的目录，注意：静态资源只能存放于此
├─main.js             Vue初始化入口文件
├─App.vue             应用配置，用来配置小程序的全局样式、生命周期函数等
├─manifest.json       配置应用名称、appid、logo、版本等打包信息
└─pages.json          配置页面路径、页面窗口样式、tabBar、navigationBar 等页面类信息</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">把项目运行到微信开发者工具</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>填写自己的微信小程序的 AppID：</p>
<!-- /wp:paragraph -->
<!-- wp:image {"id":210,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Funiapp03-1024x463.png&amp;size=m" alt="" class="wp-image-210">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">在 HBuilderX 中，配置“微信开发者工具”的安装路径：</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":203,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Funiapp04-1024x417.png&amp;size=m" alt="" class="wp-image-203">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">在微信开发者工具中，通过 设置 -&gt; 安全设置 面板，开启“微信开发者工具”的服务端口：</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":204,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Funiapp05.png&amp;size=m" alt="" class="wp-image-204">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">在 HBuilderX 中，点击菜单栏中的 运行 -&gt; 运行到小程序模拟器 -&gt; 微信开发者工具，将当前 uni-app 项目编译之后，自动运行到微信开发者工具中，从而方便查看项目效果与调试：</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":205,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Funiapp06-1024x542.png&amp;size=m" alt="" class="wp-image-205">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">使用 Git 管理项目</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在项目根目录中新建 <code>.gitignore</code> 忽略文件，并配置如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code># 忽略 node_modules 目录
/node_modules
/unpackage/dist</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>由于我们忽略了 <code>unpackage</code> 目录中仅有的 dist 目录，因此默认情况下，<code>unpackage</code> 目录不会被 Git 追踪</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:paragraph -->
<p>此时，为了让 Git 能够正常追踪 <code>unpackage</code> 目录，按照惯例，<strong>我们可以在 <code>unpackage</code> 目录下创建一个叫做 <code>.gitkeep</code> 的文件进行占位</strong></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>打开终端，切换到项目根目录中，运行如下的命令，初始化本地 Git 仓库：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>git init</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>将所有文件都加入到暂存区：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>git add .</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>本地提交更新：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>git commit -m "init project"</code></pre>
<!-- /wp:code -->]]></description><guid isPermaLink="false">/archives/uni-app</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Tue, 21 May 2024 07:10:46 GMT</pubDate></item><item><title><![CDATA[小程序（基础加强 二）]]></title><link>https://blog.qiaofugui.cn/archives/%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E5%9F%BA%E7%A1%80%E5%8A%A0%E5%BC%BA-%E4%BA%8C%EF%BC%89</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E5%9F%BA%E7%A1%80%E5%8A%A0%E5%BC%BA%20%E4%BA%8C%EF%BC%89&amp;url=/archives/%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E5%9F%BA%E7%A1%80%E5%8A%A0%E5%BC%BA-%E4%BA%8C%EF%BC%89" width="1" height="1" alt="" style="opacity:0;"><!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">使用 npm 包</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">小程序对 npm 的支持与限制</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>目前，小程序中已经支持使用 npm 安装第三方包，从而来提高小程序的开发效率。但是，在小程序中使用 npm 包有如下 3 个限制：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>不支持依赖于 <strong>Node.js 内置库</strong>的包</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>不支持依赖于<strong>浏览器内置对象</strong>的包</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>不支持依赖于 <strong>C++ 插件</strong>的包</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>虽然 npm 上的包有千千万，但是能供小程序使用的包却 “为数不多”。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">Vant Weapp</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Vant Weapp 是有赞前端团队开源的一套<strong>小程序 UI 组件库</strong>，助力开发者快速搭建小程序应用。它所<strong>使用的是 MIT 开源许可协议</strong>，对商业使用比较友好。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>官方文档地址：https://youzan.github.io/vant-weapp</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>扫描下方的小程序二维码，体验组件库示例：</p>
<!-- /wp:paragraph -->
<!-- wp:image {"id":95,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv60.jpg&amp;size=m" alt="" class="wp-image-95">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">安装 Vant 组件库</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在小程序项目中，安装 Vant 组件库主要分为如下 3 步：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>通过 npm 安装</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>构建 npm 包</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>修改 app.json</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>详细的操作步骤，可以参考 Vant 官方提供的快速上手教程：https://youzan.github.io/vant-weapp/#/quickstart#an-zhuang</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">使用 Vant 组件</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>安装完 Vant 组件库之后，可以在 <code>app.json</code> 的 <code>usingComponents</code> 节点中引入需要的组件，即可在 wxml 中直接使用组件。示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// app.json
"usingComponents": {
  "van-button": "vant-weapp/button"
}

// 页面的 .wxml 结构
&lt;van-button type="primary"&gt;主要按钮&lt;/van-button&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">定制全局主题样式</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Vant Weapp 使用 <strong>CSS 变量</strong>来实现定制主题。 关于 CSS 变量的基本用法，请参考 MDN 文档：https://developer.mozilla.org/zh-CN/docs/Web/CSS/Using_CSS_custom_properties</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">定制全局主题样式</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在 <code>app.wxss</code> 中，写入 CSS 变量，即可对全局生效：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>/* app.wxss */
page {
  /* 定制警告按钮的背景颜色和边框颜色 */
  --button-danger-background-color: #BBFFAA;
  --button-danger-border-color: #FF0000;
}</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>所有可用的<strong>颜色变量</strong>，请参考 Vant 官方提供的配置文件：https://github.com/youzan/vant-weapp/blob/dev/packages/common/style/var.less</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">API Promise 化</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>API Promise 化</strong>，指的是<strong>通过额外的配置</strong>，将官方提供的、基于回调函数的异步 API，<strong>升级改造为基于 Promise 的异步 API</strong>，从而提高代码的可读性、维护性，避免回调地狱的问题。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">基于回调函数的异步 API 的缺点</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>默认情况下，小程序官方提供的<strong>异步 API</strong> 都是基于<strong>回调函数</strong>实现的，例如，网络请求的 API 需要按照如下的方式调用：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>wx.request({
  method: '',
  url: '',
  data: { },
  success: () =&gt; { },  // 请求成功的回调函数
  fail: () =&gt; { },     // 请求失败的回调函数
  complete: () =&gt; { }, // 请求完成的回调函数
})</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>缺点：容易造成<strong>回调地狱</strong>的问题，代码的<strong>可读性、维护性</strong>差！</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">实现 API Promise 化</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在小程序中，实现 API Promise 化主要依赖于 <code>miniprogram-api-promise</code> 这个第三方的 npm 包。它的安装和使用步骤如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>npm install --save miniprogram-api-promise</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 在小程序入口文件中（app.js），只需调用一次 promisifyAll() 方法
// 即可实现异步 API 的Promise 化
import { promisifyAll } from 'miniprogram-api-promise'

const wxp = wx.p = {}
// promisify all wx's api
promisifyAll(wx, wxp)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">调用 Promise 化之后的异步 API</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 页面的 .wxml 结构
&lt;van-button type="primary" bindtap="getInfo"&gt;主要按钮&lt;/van-button&gt;

// 在页面的 .js 文件中，定义对应的 getInfo 事件处理函数
async getInfo() {
  const { data:res } = await wx.p.request({
    method: 'GET',
    url: 'https://www.escook.cn/api/get',
    data: { name: '张三', age: 18 }
  })
  console.log(res)
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">全局数据共享</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>全局数据共享</strong>（又叫做：状态管理）是为了解决<strong>组件之间数据共享</strong>的问题。开发中常用的全局数据共享方案有：Vuex、Redux、MobX 等。</p>
<!-- /wp:paragraph -->
<!-- wp:image {"id":96,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv61.png&amp;size=m" alt="" class="wp-image-96">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">小程序中的全局数据共享方案</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在小程序中，可使用 <code>mobx-miniprogram</code> 配合 <code>mobx-miniprogram-bindings</code> 实现全局数据共享。其中：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>mobx-miniprogram 用来<strong>创建 Store 实例对象</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>mobx-miniprogram-bindings 用来<strong>把 Store 中的共享数据或方法，绑定到组件或页面中使用</strong></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:image {"id":97,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv62.png&amp;size=m" alt="" class="wp-image-97">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">MobX</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>安装 MobX 相关的包，在项目中运行如下的命令，安装 MobX 相关的包：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>npm install --save mobx-miniprogram mobx-miniprogram-bindings</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>MobX 相关的包安装完毕之后，<strong>记得重新构建 npm。</strong></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">创建 MobX 的 Store 实例</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// store/store.js
import { observable, action } from "mobx-miniprogram";

export const store = observable({
  // 数据字段
  numA: 1,
  numB: 2,

  // 计算属性
  get sum() {
    return this.numA + this.numB;
  },

  // actions 方法，用来修改 store 中的数据
  updateNumA: action(function (step) {
    this.numA = this.numA + step;
  }),
  updateNumB: action(function (step) {
    this.numB = this.numB + step;
  }),
});</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">将 Store 中的成员绑定到页面中</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 在 Page 构造器中使用
// 页面 .js 文件
import { createStoreBindings } from "mobx-miniprogram-bindings";
import { store } from "../../store/store";

Page({
  data: {
    someData: "...",
  },
  // 生命周期函数 - 监听页面加载
  onLoad() {
    this.storeBindings = createStoreBindings(this, {
      store,
      fields: ["numA", "numB", "sum"],
      actions: ["updateNumA", "updateNumB"],
    });
  },
  // 生命周期函数 - 监听页面卸载
  onUnload() {
    this.storeBindings.destroyStoreBindings();
  },
  myMethod() {
    this.data.sum; // 来自于 MobX store 的字段
  },
});</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">在页面上使用 Store 中的成员</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 页面的 .wxml 结构
&lt;view&gt;{{numA}} + {{numB}} = {{sum}}&lt;/view&gt;
&lt;van-button type="primary" bindtap="btnHandler1" data-step="{{1}}"&gt;numA + 1&lt;/van-button&gt;
&lt;van-button type="primary" bindtap="btnHandler1" data-step="{{-1}}"&gt;numA - 1&lt;/van-button&gt;

// 按钮 btnHandler1 事件处理函数
btnHandler1(e) {
  this.updateNumA(e.target.dataset.step)
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">将 Store 中的成员绑定到组件中</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 在 Component 构造器中使用
// 页面 .js 文件
import { storeBindingsBehavior } from "mobx-miniprogram-bindings";
import { store } from "../../store/store";

Component({
  // 通过 storeBindingsBehavior 来实现自动绑定
  behaviors: [storeBindingsBehavior],
  data: {
    someData: "...",
  },
  storeBindings: {
    // 指定要绑定的 Store
    store,
    // 指定要绑定的字段数据
    fields: {
      numA: () =&gt; store.numA,      // 绑定字段的第 1 种方式
      numB: (store) =&gt; store.numB, // 绑定字段的第 2 种方式
      sum: "sum",                  // 绑定字段的第 3 种方式
    },
    // 指定要绑定的方法
    actions: {
      updateNumB: "updateNumB",
    },
  },
  methods: {
    myMethod() {
      this.data.sum; // 来自于 MobX store 的字段
    },
  },
});</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">在组件中使用 Store 中的成员</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 组件的 .wxml 结构
&lt;view&gt;{{numA}} + {{numB}} = {{sum}}&lt;/view&gt;
&lt;van-button type="primary" bindtap="btnHandler2" data-step="{{1}}"&gt;numB + 1&lt;/van-button&gt;
&lt;van-button type="primary" bindtap="btnHandler2" data-step="{{-1}}"&gt;numB - 1&lt;/van-button&gt;

// 组件的方法列表
methods: {
  btnHandler2(e) {
    this.updateNumB(e.target.dataset.step)
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">分包</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>分包指的是把一个<strong>完整的小程序项目</strong>，按照需求<strong>划分为不同的子包</strong>，在构建时打包成不同的分包，用户在使用时<strong>按需进行加载。</strong></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">基础概念</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">分包的好处</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>对小程序进行分包的好处主要有以下两点：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>可以<strong>优化小程序首次启动的下载时间</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>在<strong>多团队共同开发</strong>时可以更好的<strong>解耦协作</strong></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">分包前项目的构成</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>分包前，小程序项目中<strong>所有的页面和资源</strong>都被打包到了一起，导致整个<strong>项目体积过大</strong>，影响小程序<strong>首次启动的下载时间</strong>。</p>
<!-- /wp:paragraph -->
<!-- wp:image {"id":98,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv63.png&amp;size=m" alt="" class="wp-image-98">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">分包后项目的构成</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>分包后，小程序项目由 <strong>1 个主包 + 多个分包</strong>组成：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>主包：一般只包含项目的<strong>启动页面</strong>或 <strong>TabBar 页面</strong>、以及所有分包都需要用到的一些<strong>公共资源</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>分包：只包含和当前分包有关的页面和私有资源</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:image {"id":90,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv64.png&amp;size=m" alt="" class="wp-image-90">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">分包的加载规则</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>在小程序启动时，默认会下<strong>载主包并启动主包内页面</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>tabBar 页面需要放到主包中</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>当用户进入分包内某个页面时，<strong>客户端会把对应分包下载下来</strong>，下载完成后再进行展示</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>非 tabBar 页面可以按照功能的不同，划分为不同的分包之后，进行按需下载</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">分包的体积限制</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>目前，小程序分包的大小有以下两个限制：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>整个小程序所有分包大小不超过 <strong>20M</strong>（主包 + 所有分包）</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>单个分包/主包大小不能超过 <strong>2M</strong></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">使用分包</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">配置方法</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":91,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv65.png&amp;size=m" alt="" class="wp-image-91">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">打包原则</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>小程序会按 <code>subpackages</code> 的配置进行分包，<code>subpackages</code> 之外的目录将被打包到主包中</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>主包也可以有自己的 pages（即最外层的 pages 字段）</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>tabBar 页面必须在主包内</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>分包之间不能互相嵌套</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">引用原则</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>主包<strong>无法引用</strong>分包内的私有资源</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>分包之间<strong>不能相互引用</strong>私有资源</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>分包<strong>可以引用</strong>主包内的公共资源</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:image {"id":92,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv66.png&amp;size=m" alt="" class="wp-image-92">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">独立分包</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>独立分包<strong>本质上也是分包</strong>，只不过它比较特殊，<strong>可以独立于主包和其他分包而单独运行。</strong></p>
<!-- /wp:paragraph -->
<!-- wp:image {"id":93,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv67.png&amp;size=m" alt="" class="wp-image-93">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">独立分包和普通分包的区别</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>最主要的区别：<strong>是否依赖于主包才能运行</strong></p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>普通分包必须依赖于主包才能运行</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>独立分包可以在不下载主包的情况下，独立运行</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">独立分包的应用场景</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>开发者可以按需，将某些<strong>具有一定功能独立性的页面配置到独立分包中</strong>。原因如下：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>当小程序从普通的分包页面启动时，需要首先下载主包</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>而独立分包<strong>不依赖主包</strong>即可运行，<strong>可以很大程度上提升分包页面的启动速度</strong></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>一个小程序中可以有多个独立分包。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">独立分包的配置方法</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":94,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv68.png&amp;size=m" alt="" class="wp-image-94">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">引用原则</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>独立分包和普通分包以及主包之间，是<strong>相互隔绝的</strong>，<strong>不能相互引用彼此的资源</strong>！例如：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>主包<strong>无法引用</strong>独立分包内的私有资源</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>独立分包之间，<strong>不能相互引用</strong>私有资源</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>独立分包和普通分包之间，<strong>不能相互引用</strong>私有资源</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>特别注意：独立分包中不能引用主包内的公共资源</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">分包预下载</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>分包预下载指的是：在进入小程序的某个页面时，<strong>由框架自动预下载可能需要的分包</strong>，从而提升进入后续分包页面时的启动速度。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">配置分包的预下载</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>预下载分包的行为，会在进入指定的页面时触发</strong>。在 app.json 中，使用 <code>preloadRule</code> 节点定义分包的预下载规则，示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>{
  // 分包预下载的规则
  "preloadRule": {
    // 触发分包预下载的页面路径
    "pages/contact/contact": {
      // network 表示在指定网络下进行预下载
      // 可选择为：all（不限网络）和 wifi（仅 wifi 模式下进行预下载）默认值为：wifi
      "network": "all",
      // packages 表示进入页面后，预下载哪些分包
      // 可以通过 root 或 name 指定下载哪些分包
      "packages": [
        "pkgA"
      ]
    }
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">分包预下载的限制</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>同一个分包中的页面享有共同的预下载大小限额 <strong>2M</strong>，例如：</p>
<!-- /wp:paragraph -->
<!-- wp:image {"id":89,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv69.png&amp;size=m" alt="" class="wp-image-89">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">自定义 tabBar</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>自定义 tabBar 分为 3 大步骤，分别是：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>配置信息</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>添加 tabBar 代码文件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>编写 tabBar 代码
  <br>
  详细步骤，可以参考小程序官方给出的文档：https://developers.weixin.qq.com/miniprogram/dev/framework/ability/custom-tabbar.html</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">总结</h1>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>如何安装和配置 vant-weapp 组件库</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>参考 Vant 的官方文档</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>如何使用 MobX 实现全局数据共享</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>安装包、创建 Store、参考官方文档进行使用</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>如何对小程序的 API 进行 Promise 化</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>安装包、在 app.js 中进行配置</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>如何实现自定义 tabBar 的效果</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>Vant 组件库 + 自定义组件 + 全局数据共享</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->]]></description><guid isPermaLink="false">/archives/%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E5%9F%BA%E7%A1%80%E5%8A%A0%E5%BC%BA-%E4%BA%8C%EF%BC%89</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Tue, 21 May 2024 07:08:32 GMT</pubDate></item><item><title><![CDATA[小程序（基础加强 一）]]></title><link>https://blog.qiaofugui.cn/archives/%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E5%9F%BA%E7%A1%80%E5%8A%A0%E5%BC%BA-%E4%B8%80%EF%BC%89</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E5%9F%BA%E7%A1%80%E5%8A%A0%E5%BC%BA%20%E4%B8%80%EF%BC%89&amp;url=/archives/%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E5%9F%BA%E7%A1%80%E5%8A%A0%E5%BC%BA-%E4%B8%80%EF%BC%89" width="1" height="1" alt="" style="opacity:0;"><!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">自定义组件</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">创建组件</h2>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>在项目的根目录中，鼠标右键，创建 <code>components -&gt; test</code> 文件夹</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>在新建的 components -&gt; test 文件夹上，鼠标右键，点击 <strong>“新建 Component”</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>键入组件的名称之后回车，会自动生成组件对应的 4 个文件，后缀名分别为 .js，.json， .wxml 和 .wxss</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:paragraph -->
<p><strong>为了保证目录结构的清晰，建议把不同的组件，存放到单独目录中，例如：</strong>
 <br>
 <img class="wp-image-105" style="width: 150px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv52.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">引用组件</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>组件的引用方式分为 <strong>“局部引用”</strong> 和 <strong>“全局引用”</strong>，顾名思义：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>局部引用：组件只能在当前被引用的页面内使用</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>全局引用：组件可以在每个小程序页面中使用</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">局部引用组件</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在页面的 .json 配置文件中引用组件的方式，叫做 “局部引用”。示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 在页面的 .json 文件中，引入组件
{
  "usingComponents": {
    "my-test1": "/components/test1/test1"
  }
}

// 在页面的 .wxml 文件中，使用组件
&lt;my-test1&gt;&lt;/my-test1&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">全局引用组件</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在 app.json 全局配置文件中引用组件的方式，叫做 “全局引用”。示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 在根目录的 app.json 文件中，引入组件
{
  "pages": [ /* 省略不必要的代码 */ ],
  "window": { /* 省略不必要的代码 */ },
  "usingComponents": {
    "my-test2": "/components/test2/test2"
  }
}

// 在页面的 .wxml 文件中，使用组件
&lt;my-test2&gt;&lt;/my-test2&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">全局引用 VS 局部引用</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>根据组件的<strong>使用频率和范围</strong>，来选择合适的引用方式：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>如果某组件<strong>在多个页面中经常被用到</strong>，建议进行“全局引用”</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>如果某组件<strong>只在特定的页面中被用到</strong>，建议进行“局部引用”</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">组件和页面的区别</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>从表面来看，组件和页面都是由 .js、.json、.wxml 和 .wxss 这四个文件组成的。但是，组件和页面的 .js 与 .json 文件有明显的不同：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>组件的 .json 文件中需要声明 <code>"component": true</code> 属性</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>组件的 .js 文件中调用的是 <code>Component()</code> 函数</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>组件的事件处理函数需要定义到 <code>methods</code> 节点中</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">组件样式隔离</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>默认情况下，自定义组件的样式只对当前组件生效，不会影响到组件之外的 UI 结构，如图所示：
 <br>
 <img class="wp-image-106" style="width: 150px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv53.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>组件 A 的样式<strong>不会影响</strong>组件 C 的样式</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>组件 A 的样式<strong>不会影响</strong>小程序页面的样式</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>小程序页面的样式<strong>不会影响</strong>组件 A 和 C 的样式
  <br>
  好处：</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>防止外界的样式影响组件内部的样式</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>防止组件的样式破坏外界的样式</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">组件样式隔离的注意点</h3>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>app.wxss 中的全局样式对组件无效</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>只有 class 选择器会有样式隔离效果，id 选择器、属性选择器、标签选择器不受样式隔离的影响</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>建议：在<strong>组件和引用组件的页面中</strong>建议使用 class 选择器，<strong>不要使用 id、属性、标签选择器！</strong></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">修改组件的样式隔离选项</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>默认情况下，自定义组件的样式隔离特性能够防止组件内外样式互相干扰的问题。但有时，我们希望在外界能够控制组件内部的样式，此时，可以通过 <code>styleIsolation</code> 修改组件的样式隔离选项，用法如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 在组件的 .js 文件中新增 options 配置
Component({
  options: {
    styleIsolation: 'isolated'
  },
})

// 或在组件的 .json 文件中新增以下配置
{
  "styleIsolation": 'isolated'
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">styleIsolation 的可选值</h4>
<!-- /wp:heading -->
<!-- wp:image {"id":107,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv54.png&amp;size=m" alt="" class="wp-image-107">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">data 数据</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在小程序组件中，<strong>用于组件模板渲染的私有数据，需要定义到 data 节点中</strong>，示例如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Component({
  /**
   * 组件的初始数据
   */
  data: {
    count: 0
  },
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">methods 方法</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在小程序组件中，<strong>事件处理函数和自定义方法需要定义到 methods 节点中</strong>，示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Component({
  /**
   * 组件的方法列表【包含事件处理函数和自定义方法】
   */
  methods: {
    addCount() {
      this.setData({ count: this.data.count + 1 })
      // 通过 this 直接调用自定义方法
      this._showCount()
    },
    // 自定义方法建议以 _ 开头
    _showCount() {
      wx.showToast({
        title: 'count 值为：' + this.data.count,
        icon: 'none'
      })
    }
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">properties 属性</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在小程序组件中，properties 是组件的对外属性，<strong>用来接收外界传递到组件中的数据</strong>，示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 组件 .js 文件
Component({
    /**
   * 组件的属性列表
   */
  properties: {
    max: {           // 完整定义属性的方式【当需要指定属性默认值时，建议用此方式】
      type: Number,  // 属性的数据类型
      value: 10      // 属性的默认值
    },
    max: Number      // 简化定义属性的方法【不需要指定属性默认值时，可以使用简化方式】
  },
})

// 使用组件
&lt;my-test max="10"&gt;&lt;/my-test&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">data 和 properties 的区别</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在小程序的组件中，properties 属性和 data 数据的用法相同，<strong>它们都是可读可写的</strong>，只不过：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>data 更倾向于<strong>存储组件的私有数据</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>properties 更倾向于<strong>存储外界传递到组件中的数据</strong></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">使用 setData 修改 properties 的值</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>由于 <strong>data 数据</strong>和 <strong>properties 属性</strong>在本质上没有任何区别，因此 properties 属性的值也可以用于页面渲染，或使用 setData 为 properties 中的属性重新赋值，示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 在组件 .wxml 文件中使用 properties 属性的值
&lt;view&gt;max 属性值为：{{max}}&lt;/view&gt;

// 组件 .js 文件
Component({
  properties: { max: Number }, // 定义属性
  methods: {
    addCount() {
      this.setData({ max: this.properties.max + 1 }) // 使用 setData 修改属性的值
    }
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">数据监听器</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>数据监听器<strong>用于监听和响应任何属性和数据字段的变化，从而执行特定的操作</strong>。它的作用类似于 vue 中的 watch 侦听器。在小程序组件中，数据监听器的基本语法格式如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 组件 .js 文件
Component({
  observers: {
    '字段A, 字段B': function(字段A的新值, 字段B的新值) {
      // do something
    }
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p><strong>不要使用箭头函数，使用的话 this 的指向就丢了</strong></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">数据监听器的基本用法</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>组件的 UI 结构如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 组件的 UI 结构
&lt;view&gt;{{n1}} + {{n2}} = {{sum}}&lt;/view&gt;
&lt;button type="primary" bindtap="addN1"&gt;n1 自增&lt;/button&gt;
&lt;button type="primary" bindtap="addN1"&gt;n2 自增&lt;/button&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>组件的 .js 文件代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Component({
  // 数据节点
  data: {
    n1: 0,
    n2: 0,
    sum: 0
  },
  // 方法列表
  methods: {
    addN1() {
      this.setData({ n1: this.data.n1 + 1 })
    },
    addN2() {
      this.setData({ n2: this.data.n2 + 1 })
    }
  },
  // 数据监听节点
  observers: {
    // 监听 n1 和 n2 的数据变化
    'n1, n2': function(n1, n2) {
      // 通过监听器，自动计算 sum 的值
      this.setData({ sum: n1 + n2 })
    }
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">监听对象属性的变化</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>数据监听器支持监听<strong>对象中单个或多个属性的变化</strong>，示例语法如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 组件 .js 文件
Component({
  observers: {
    '对象.属性A, 对象.属性B': function(属性A的新值, 属性B的新值) {
      // 触发此监听器的 3 种情况：
      // 【为属性A赋值】使用 setData 设置 this.data.对象.属性A 时触发
      // 【为属性B赋值】使用 setData 设置 this.data.对象.属性B 时触发
      // 【直接为对象赋值】使用 setData 设置 this.data.对象 时触发
      // do something...
    }
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">监听对象中所有属性的变化</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>如果某个对象中需要被监听的属性太多，为了方便，可以使用<strong>通配符 <code>**</code> 来监听对象中所有属性的变化</strong>，示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Component({
  observers: {
    // 使用 通配符 ** 监听对象中所有属性的变化
    'rgb.**': function(obj) {
      fullColor: `${obj.r}, ${obj.g}, ${obj.b}`
    }
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">纯数据字段</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>概念：纯数据字段指的是那些<strong>不用于界面渲染的 data 字段。</strong></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>应用场景：例如有些情况下，某些 data 中的字段<strong>既不会展示在界面上，也不会传递给其他组件</strong>，仅仅在当前组件内部使用。带有这种特性的 data 字段适合被设置为纯数据字段。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>好处：纯数据字段<strong>有助于提升页面更新的性能。</strong></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">使用规则</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在 Component 构造器的 options 节点中，指定 <strong><code>pureDataPattern</code> 为一个正则表达式</strong>，字段名符合这个正则表达式的字段将成为纯数据字段，示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Component({
  options: {
    // 指定所以 _ 开头的数据字段为纯数据字段
    pureDataPattern: /^_/
  },
  data: {
    a: true, // 普通数据字段
    _b: true // 纯数据字段
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Component({
  options: {
    // 指定所以 _ 开头的数据字段为纯数据字段
    pureDataPattern: /^_/
  },
  data: {
    _rgb: {
      r: 0,
      g: 0,
      b: 0
    },
    fullColor: '0, 0, 0'
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">组件的生命周期</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>小程序组件可用的全部生命周期如下表所示：
 <br>
 <img class="wp-image-99" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv55.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">组件主要的生命周期函数</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在小程序组件中，最重要的生命周期函数有 3 个，分别是 <strong><code>created</code>、<code>attached</code>、<code>detached</code></strong>。它们各自的特点如下：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>组件实例<strong>刚被创建好</strong>的时候，<code>created</code> 生命周期函数会被触发</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>此时还不能调用 setData</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>通常在这个生命周期函数中，只应该用于给组件的 this 添加一些自定义的属性字段</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>在组件<strong>完全初始化完毕、进入页面节点树后</strong>， <code>attached</code> 生命周期函数会被触发</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>此时， this.data 已被初始化完毕</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>这个生命周期很有用，绝大多数初始化的工作可以在这个时机进行（例如发请求获取初始数据）</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>在组件<strong>离开页面节点树后</strong>， <code>detached</code> 生命周期函数会被触发</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>退出一个页面时，会触发页面内每个自定义组件的 <code>detached</code> 生命周期函数</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>此时适合做一些清理性质的工作</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">lifetimes 节点</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在小程序组件中，生命周期函数可以直接定义在 Component 构造器的第一级参数中，可以在 <code>lifetimes</code> 字段内进行声明<strong>（这是推荐的方式，其优先级最高）</strong>。示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Component({
  // 推荐用法
  lifetimes: {
    attached() { }, // 在组件实例进入页面节点时执行
    attached() { }, // 在组件实例被从页面节点树移除时执行
  },

  // 以下是旧式的定义方法
  attached() { }, // 在组件实例进入页面节点时执行
  attached() { }, // 在组件实例被从页面节点树移除时执行
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">组件所在页面的生命周期</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>有时，<strong>自定义组件的行为依赖于页面状态的变化</strong>，此时就需要用到<strong>组件所在页面的生命周期。</strong></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>例如：每当触发页面的 show 生命周期函数的时候，我们希望能够重新生成一个随机的 RGB 颜色值。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>在自定义组件中，组件所在页面的生命周期函数有如下 3 个，分别是：
 <br>
 <img class="wp-image-100" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv56.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">pageLifetimes 节点</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>组件所在页面的生命周期函数，需要定义在 <code>pageLifetimes</code> 节点中，示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Component({
  pageLifetimes: {
    show: function() { }, // 页面被展示
    hide: function() { }, // 页面被隐藏
    resize: function(size) { } // 页面尺寸变化
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">插槽</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在自定义组件的 wxml 结构中，可以提供一个 <code>&lt;slot&gt;</code> 节点（插槽），<strong>用于承载组件使用者提供的 wxml 结构。</strong>
 <br>
 <img class="wp-image-101" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv57.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">单个插槽</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在小程序中，默认每个自定义组件中只允许使用一个 <code>&lt;slot&gt;</code> 进行占位，这种个数上的限制叫做单个插槽。</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;!-- 组件的封装者 component-tag-name 组件 --&gt;
&lt;view&gt;
  &lt;view&gt;这里是组件的内部节点&lt;/view&gt;
  &lt;!-- 对于不确定的内容，可以使用 &lt;slot&gt; 进行占位，具体的内容由组件的使用者决定 --&gt;
  &lt;slot&gt;&lt;/slot&gt;
&lt;/view&gt;

&lt;!-- 组件的使用者 --&gt;
&lt;component-tag-name&gt;
  &lt;!-- 这部分内容将被放置在组件 &lt;slot&gt; 的位置上 --&gt;
  &lt;view&gt;这里是插入到组件 slot 中的内容&lt;/view&gt;
&lt;/component-tag-name&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">启用多个插槽</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在小程序的自定义组件中，需要使用多 <code>&lt;slot&gt;</code> 插槽时，可以在组件的 .js 文件中，通过如下方式进行启用。示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Component({
  options: {
    // 在组件定义时ide选项中启用多 slot 支持
    multipleSlots: true
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">定义多个插槽</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>可以在组件的 .wxml 中使用多个 <code>&lt;slot&gt;</code> 标签，以不同的 name 来区分不同的插槽。示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;!-- 组件模板 --&gt;
&lt;view&gt;
  &lt;!-- name 为 before 的第一个 slot 插槽--&gt;
  &lt;slot name="before"&gt;&lt;/slot&gt;
  &lt;view&gt;这是一段固定的文本内容&lt;/view&gt;
  &lt;!-- name 为 after 的第二个 slot 插槽--&gt;
  &lt;slot name="after"&gt;&lt;/slot&gt;
&lt;/view&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">使用多个插槽</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在使用带有多个插槽的自定义组件时，<strong>需要用 slot 属性</strong>来将节点插入到不同的 <code>&lt;slot&gt;</code> 中。示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;!-- 引用组件的页面模板 --&gt;
&lt;component-tag-name&gt;
  &lt;!-- 这部分内容将被放置在组件 &lt;slot name="before"&gt; 的位置上 --&gt;
  &lt;view slot="before"&gt;这里是插入到组件 slot name="before" 中的内容&lt;/view&gt;
  &lt;!-- 这部分内容将被放置在组件 &lt;slot name="after"&gt; 的位置上 --&gt;
  &lt;view slot="after"&gt;这里是插入到组件 slot name="after" 中的内容&lt;/view&gt;
&lt;/component-tag-name&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">父子组件之间的通信</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>父子组件之间通信的 3 种方式</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>属性绑定</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>用于父组件向子组件的指定属性设置数据，仅能设置 JSON 兼容的数据</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>事件绑定</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>用于子组件向父组件传递数据，可以传递任意数据</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>获取组件实例</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>父组件还可以通过 <code>this.selectComponent()</code> 获取子组件实例对象</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>这样就可以直接访问子组件的任意数据和方法</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">属性绑定</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>属性绑定用于<strong>实现父向子传值</strong>，而且<strong>只能传递普通类型的数据</strong>，无法将方法传递给子组件。父组件的示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 父组件的 data 节点
data: {
  count: 0
}

// 父组件的 wxml 结构
&lt;view&gt;父组件中，count 值为：{{count}}&lt;/view&gt;
&lt;view&gt;-----&lt;/view&gt;
&lt;my-test3 count="{{count}}"&gt;&lt;/my-test3&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>子组件在 <code>properties</code> 节点中声明对应的属性并使用。示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 子组件的 properties 节点
properties: {
  count: Number
}

// 子组件的 wxml 结构
&lt;text&gt;子组件中，count 值为：{{count}}&lt;/text&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">事件绑定</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>事件绑定用于实现子向父传值</strong>，可以传递任何类型的数据。使用步骤如下：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>在<strong>父组件</strong>的 js 中，定义一个函数，这个函数<strong>即将</strong>通过自定义事件的形式，传递给子组件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>在<strong>父组件</strong>的 wxml 中，通过自定义事件的形式，将步骤 1 中定义的函数引用，传递给子组件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>在<strong>子组件</strong>的 js 中，通过调用 <code>this.triggerEvent('自定义事件名称', { /* 参数对象 */ })</code>，将数据发送到父组件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>在<strong>父组件</strong>的 js 中，通过 <code>e.detail</code> 获取到子组件传递过来的数据</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">步骤1：在<strong>父组件</strong>的 js 中，定义一个函数，这个函数<strong>即将</strong>通过自定义事件的形式，传递给子组件。</h4>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 在父组件中定义 syncCount 方法
// 将来，这个方法会被传递给子组件，供子组件进行调用
syncCount() {
  console.log('syncCount')
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">步骤2：在<strong>父组件</strong>的 wxml 中，通过<strong>自定义事件</strong>的形式，将步骤 1 中定义的函数引用，传递给子组件。</h4>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;!-- 使用 bind:自定义事件名称（推荐，结构清晰） --&gt;
&lt;my-test count="{{count}}" bind:sync="syncCount"&gt;&lt;/my-test&gt;
&lt;!-- 或在 bind后面直接写上自定义事件名称 --&gt;
&lt;my-test count="{{count}}" bindsync="syncCount"&gt;&lt;/my-test&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">步骤3：在<strong>子组件</strong>的 js 中，通过调用 <code>this.triggerEvent(‘自定义事件名称’, { /* 参数对象 */ })</code>，将数据发送到父组件。</h4>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 子组件的 wxml 结构
&lt;text&gt;子组件中，count 值为：{{count}}&lt;/text&gt;
&lt;button type="primary" bindtap="addCount"&gt;+1&lt;/button&gt;

// 子组件的 js 代码
methods: {
  addCount() {
    this.setData({
      count: this.properties.count + 1
    })
    this.triggerEvent('sync', {value: this.properties.count})
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">步骤4：在<strong>父组件</strong>的 js 中，通过 <code>e.detail</code> 获取到子组件传递过来的数据。</h4>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>syncCount(e) {
  // console.log(e.detail.value)
  this.setData({
    count: e.detail.value
  })
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">获取组件实例</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>可在父组件里调用 <code>this.selectComponent("id或class选择器")</code>，获取子组件的实例对象，从而直接访问子组件的任意数据和方法。调用时需要<strong>传入一个选择器</strong>，例如 <code>this.selectComponent(".my-component")</code>。</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// wxml 结构
&lt;my-test count="{{count}}" bind:sync="syncCount" class="customA" id="cA"&gt;&lt;/my-test&gt;
&lt;button type="primary" bindtap="getCHild"&gt;获取子组件实例&lt;/button&gt;

// 按钮的时间处理函数
// 切记项目参数不能传递标签选择器 “my-test”，不然返回的是 null
const child = this.selectComponent('.customA') // 也可以传递 id 选择器 #cA
child.setData({ count: child.properties.count + 1 }) // 调用子组件的 setData 方法
child.addCount() // 调用子组件的 addCount 方法</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">behaviors</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>behaviors 是小程序中，<strong>用于实现组件间代码共享的特性</strong>，类似于 Vue.js 中的 “mixins”。
 <br>
 <img class="wp-image-102" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv58.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">behaviors 的工作方式</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>每个 behavior 可以包含一组<strong>属性、数据、生命周期函数和方法</strong>。组件引用它时，它的属性、数据和方法<strong>会被合并到组件中</strong>。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>每个组件可以引用多个 behavior，behavior 也可以引用其它 behavior。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">创建 behavior</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>调用 <code>Behavior(Object object)</code> 方法即可创建一个<strong>共享的 behavior 实例对象</strong>，供所有的组件使用：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// behavior/my-behavior.js
// 调用 Behavior() 方法，创建实例对象
// 并使用 module.exports 将 behavior 实例对象共享出去
module.exports = Behavior({
  // 属性节点
  properties: { },
  // 私有数据节点
  data: { username: 'Joe' },
  // 事件处理函数和自定义方法节点
  methods: { },
  // 其他节点...
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">导入并使用 behavior</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在组件中，使用 <code>require()</code> 方法导入需要的 behavior，<strong>挂载后即可访问 behavior 中的数据或方法</strong>，示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 1. 使用 require() 导入需要的自定义 behavior 模块
const myBehavior = require('../../behavior/my-behavior')

Component({
  // 2. 将导入的 behavior 实例对象，挂载到 behaviors 数组节点中，即可生效
  behaviors: [myBehavior],

  // 组件的其他节点
})</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 在 .wxml 中十使用 挂载的 behavior
&lt;view&gt;在 behavior 中定义的用户名是：{{username}}&lt;/view&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">behavior 中所有可用的节点</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":103,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv59.png&amp;size=m" alt="" class="wp-image-103">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">同名字段的覆盖和组合规则*</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>组件和它引用的 behavior 中<strong>可以包含同名的字段</strong>，此时可以参考如下 3 种同名时的处理规则：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>同名的数据字段 (<code>data</code>)</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>同名的属性 (<code>properties</code>) 或方法 (<code>methods</code>)</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>同名的<strong>生命周期函数</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>关于详细的覆盖和组合规则，大家可以参考微信小程序官方文档给出的说明:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">总结</h1>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>创建并引用组件</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>全局引用、局部引用、<code>usingComponents</code></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>修改组件的样式隔离选项</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>options -&gt; styleIsolation（ isolated, apply-shared, shared）</code></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>定义和使用数据监听器</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>observers</code></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>定义和使用纯数据字段</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>options -&gt; pureDataPattern</code></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>实现组件父子通信的 3 种方式</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>属性绑定、事件绑定、<code>this.selectComponent(' id或class选择器')</code></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>定义和使用 behaviors</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>调用 <code>Behavior()</code> 构造器方法</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->]]></description><guid isPermaLink="false">/archives/%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E5%9F%BA%E7%A1%80%E5%8A%A0%E5%BC%BA-%E4%B8%80%EF%BC%89</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Tue, 21 May 2024 07:05:56 GMT</pubDate></item><item><title><![CDATA[小程序（视图与逻辑）]]></title><link>https://blog.qiaofugui.cn/archives/%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E8%A7%86%E5%9B%BE%E4%B8%8E%E9%80%BB%E8%BE%91%EF%BC%89</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E8%A7%86%E5%9B%BE%E4%B8%8E%E9%80%BB%E8%BE%91%EF%BC%89&amp;url=/archives/%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E8%A7%86%E5%9B%BE%E4%B8%8E%E9%80%BB%E8%BE%91%EF%BC%89" width="1" height="1" alt="" style="opacity:0;"><!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">页面导航</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>页面导航指的是<strong>页面之间的相互跳转</strong>。例如，浏览器中实现页面导航的方式有如下两种：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><code>&lt;a&gt;</code> 链接</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>location.href</code></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">小程序中实现页面导航的两种方式</h2>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>声明式导航</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>在页面上声明一个 <code>&lt;navigator&gt;</code> 导航组件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>通过点击 <code>&lt;navigator&gt;</code> 组件实现页面跳转</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>编程式导航</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>调用小程序的导航 API，实现页面的跳转</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">声明式导航</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">导航到 tabBar 页面</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>tabBar 页面</strong>指的是被配置为 tabBar 的页面。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>在使用 <code>&lt;navigator&gt;</code> 组件跳转到指定的 tabBar 页面时，需要指定 <code>url</code> 属性和 <code>open-type</code> 属性，其中：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>url</code> 表示要跳转的<strong>页面的地址</strong>，必须以 <code>/</code> 开头</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>open-type</code> 表示<strong>跳转的方式</strong>，必须为 <code>switchTab</code>
  <br>
  示例代码如下：</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;navigator url="/pages/message/message" open-type="switchTab"&gt;导航到消息页面&lt;/navigator&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">导航到非 tabBar 页面</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>非 tabBar 页面</strong>指的是没有被配置为 tabBar 的页面。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>在使用 <code>&lt;navigator&gt;</code> 组件跳转到普通的非 tabBar 页面时，则需要指定 <code>url</code> 属性和 <code>open-type</code> 属性，其中：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>url</code> 表示要跳转的<strong>页面的地址</strong>，必须以 <code>/</code> 开头</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>open-type</code> 表示<strong>跳转的方式</strong>，必须为 <code>navigate</code>
  <br>
  示例代码如下：</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;navigator url="/pages/info/info" open-type="switchTab"&gt;导航到消息页面&lt;/navigator&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>为了简便，在导航到非 tabBar 页面时，<code>open-type="navigate"</code> 属性可以省略。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">后退导航</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>如果要后退到上一页面或多级页面，则需要指定 <code>open-type</code> 属性和 <code>delta</code> 属性，其中：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>open-type</code> 的值必须是 <code>navigateBack</code>，表示要进行后退导航</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>delta</code> 的值必须是数字，表示要后退的层级
  <br>
  示例代码如下：</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;navigator open-type="navigateBack" delta="1"&gt;返回上一页&lt;/navigator&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>为了简便，如果只是后退到上一页面，则<strong>可以省略 <code>delta</code> 属性</strong>，因为其<strong>默认值就是 1</strong></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">编程式导航</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">导航到 tabBar 页面</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>调用 <code>wx.switchTab(Object object)</code> 方法。其中 Object <strong>参数对象</strong>的属性列表如下：
 <br>
 <img class="wp-image-108" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv47.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 页面结构
&lt;button bindtab="gotoMessage"&gt;跳转到消息页面&lt;/button&gt;

// .js 文件 通过编程式导航，跳转到 message 页面
Page({
  gotoMessage() {
    wx.switchTab({
      url: '/page/message/message'
    })
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">导航到非 tabBar 页面</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>调用 <code>wx.navigateTo(Object object)</code> 方法。其中 Object <strong>参数对象</strong>的属性列表如下：
 <br>
 <img class="wp-image-109" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv48.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 页面结构
&lt;button bindtab="gotoInfo"&gt;跳转到info页面&lt;/button&gt;

// .js 文件 通过编程式导航，跳转到 info 页面
Page({
  gotoInfo() {
    wx.navigateTo({
      url: '/page/info/info'
    })
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">后退导航</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>调用 <code>wx.navigateBack(Object object)</code> 方法，可以返回上一页面或多级页面。其中 Object <strong>参数对象</strong>可选的属性列表如下：
 <br>
 <img class="wp-image-110" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv49.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 页面结构
&lt;button bindtab="gotoback"&gt;后退&lt;/button&gt;

// .js 文件 通过编程式导航，跳转到 info 页面
Page({
  gotBack() {
    wx.navigateBack()
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">导航传参</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">声明式导航传参</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>navigator</code> 组件的 <code>url</code> 属性用来指定将要跳转到的页面的路径。同时，<strong>路径的后面还可以携带参数：</strong></p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>参数与路径之间使用 <code>?</code> 分隔</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>参数键与参数值用 <code>=</code> 相连</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>不同参数用 <code>&amp;</code> 分隔
  <br>
  代码示例如下：</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;navigator url="/pages/info/info?name=zs&amp;age=18"&gt;跳转到info页面&lt;/navigator&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">编程式导航传参</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>调用 <code>wx.navigateTo(Object object)</code> 方法跳转页面时，也可以携带参数，代码示例如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 页面结构
&lt;button bindtab="gotoInfo"&gt;跳转到info页面&lt;/button&gt;

// .js 文件 通过编程式导航，跳转到 info 页面，并携带参数
Page({
  gotoInfo() {
    wx.switchTab({
      url: '/page/info/info?name=张三&amp;age=18'
    })
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">在 onLoad 中接收导航参数</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>通过<strong>声明式导航传参或编程式导航传参</strong>所携带的参数，<strong>可以直接在 <code>onLoad</code> 事件中直接获取到，一般获取到要转存到 data 里面</strong>，示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>/**
 * 页面的初始数据
 */
data: {
  // 导航传递过来的参数对象
  query: {}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
  // options 就是导航传递过来的参数对象
  console.log(options)
  this.setData({
    query: options
  })
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">页面事件</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">下拉刷新事件</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>下拉刷新</strong>是移动端的专有名词，指的是通过手指在屏幕上的下拉滑动操作，从而<strong>重新加载页面数据</strong>的行为。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">启用下拉刷新</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>启用下拉刷新有两种方式：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>全局开启下拉刷新</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>在 app.json 的 window 节点中，将 <code>enablePullDownRefresh</code> 设置为 true</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>局部开启下拉刷新</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>在页面的 .json 配置文件中，将 <code>enablePullDownRefresh</code> 设置为 true</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>在实际开发中，推荐使用第 2 种方式，<strong>为需要的页面单独开启下拉刷新的效果。</strong></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">配置下拉刷新窗口的样式</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在全局或页面的 .json 配置文件中，通过 <code>backgroundColor</code> 和 <code>backgroundTextStyle</code> 来配置下拉刷新窗口的样式，其中：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>backgroundColor</code> 用来配置下拉刷新<strong>窗口的背景颜色，仅支持 16 进制的颜色值</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>backgroundTextStyle</code> 用来配置<strong>下拉刷新 loading 的样式，仅支持 dark 和 light</strong></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">监听页面的下拉刷新事件</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在页面的 .js 文件中，通过 <code>onPullDownRefresh()</code> 函数即可监听当前页面的下拉刷新事件。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>例如，在页面的 wxml 中有如下的 UI 结构，点击按钮可以让 count 值自增 +1：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 页面结构
&lt;view&gt;count值为：{{count}}&lt;/view&gt;
&lt;button bindtab="countAdd"&gt;+1&lt;/button&gt;

// .js 文件 +1 按钮的点击事件处理函数
Page({
  data: {
    count: 0
  },

  countAdd() {
    this.setData({
      count: this.data.count + 1
    })
  },

  // 在触发页面的下拉刷新事件的时候，如果要把 count 的值重置为 0
  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {
    this.setData({
      count: 0
    })
  },
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">停止下拉刷新的效果</h4>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>当处理完下拉刷新后，下拉刷新的 loading 效果会一直显示，<strong>不会主动消失</strong>，所以需要手动隐藏下拉刷新的 loading 效果。此时，调用 <code>wx.stopPullDownRefresh()</code> 可以停止当前页面的下拉刷新。示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Page({
  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {
    this.setData({
      count: 0
    })
    // 当数据重置成功之后，调用此函数，关闭下拉刷新的效果
    wx.stopPullDownRefresh()
  },
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">上拉触底事件</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>上拉触底</strong>是移动端的专有名词，通过手指在屏幕上的上拉滑动操作，从而<strong>加载更多数据</strong>的行为。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">监听页面的上拉触底事件</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在页面的 .js 文件中，通过 <code>onReachBottom()</code> 函数即可监听当前页面的上拉触底事件。示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Page({
  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom() {
    // 要做节流处理
    console.log('触发了上拉触底的事件')
  },
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">配置上拉触底距离</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>上拉触底距离指的是<strong>触发上拉触底事件时，滚动条距离页面底部的距离。</strong></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>可以在全局或页面的 .json 配置文件中，通过 <code>onReachBottomDistance</code> 属性来配置上拉触底的距离。</p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>小程序默认的触底距离是 50px，在实际开发中，可以根据自己的需求修改这个默认值。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">添加 loading 提示效果</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Page({
  getColors() {
    // 1. 展示 loading 效果
    wx.showLoading({
      title: '加载中...'
    })

    // 发起请求，回去随机颜色的数组
    wx.request({
      url: 'https://www.escook.cn/api/color',
      success: (res) =&gt; {
        this.setData({
          colorList: [...this.data.colorList, ...res.data.data],
        })
      },
      complete: () =&gt; {
        // 2. 请求成功后隐藏 loading 效果
        wx.hideLoading()
      }
    })
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">扩展-自定义编译模式</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":111,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv50.png&amp;size=m" alt="" class="wp-image-111">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">生命周期</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>生命周期（Life Cycle）</strong>是指一个对象从 <strong>创建 -&gt; 运行 -&gt; 销毁</strong> 的整个阶段，<strong>强调的是一个时间段</strong>。例如：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>张三<strong>出生</strong>，表示这个人<strong>生命周期的开始</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>张三<strong>离世</strong>，表示这个人<strong>生命周期的结束</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>中间张三的一生，就是张三的生命周期
  <br>
  可以把每个小程序运行的过程，也概括为生命周期：</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>小程序的<strong>启动</strong>，表示<strong>生命周期的开始</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>小程序的<strong>关闭</strong>，表示<strong>生命周期的结束</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>中间小程序运行的过程，就是小程序的生命周期</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">生命周期的分类</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在小程序中，生命周期分为两类，分别是：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>应用生命周期</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>特指小程序从启动 -&gt; 运行 -&gt; 销毁的过程</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>页面生命周期</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>特指小程序中，每个页面的加载 -&gt; 渲染 -&gt; 销毁的过程</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:paragraph -->
<p>其中，<strong>页面</strong>的生命周期<strong>范围较小</strong>，<strong>应用程序</strong>的生命周期<strong>范围较大</strong>，如图所示：
 <br>
 <img class="wp-image-104" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv51.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">什么是生命周期函数</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>生命周期函数</strong>：是由小程序框架提供的<strong>内置函数</strong>，会伴随着生命周期，<strong>自动按次序执行</strong>。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><strong>生命周期函数的作用</strong>：允许程序员在<strong>特定的时间点，执行某些特定的操作</strong>。例如，页面刚加载的时候，可以在 <code>onLoad</code> 生命周期函数中初始化页面的数据。</p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p><strong>生命周期</strong>强调的是<strong>时间段</strong>，<strong>生命周期函数</strong>强调的是<strong>时间点</strong>。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">生命周期函数的分类</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>小程序中的生命周期函数分为两类，分别是：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>应用的生命周期函数</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>特指小程序从启动 -&gt; 运行 -&gt; 销毁期间依次调用的那些函数</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>页面的生命周期函数</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>特指小程序中，每个页面从加载 -&gt; 渲染 -&gt; 销毁期间依次调用的那些函数</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">应用的生命周期函数</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>小程序的<strong>应用生命周期函数</strong>需要在 <code>app.js</code> 文件中进行声明，示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// app.js 文件
App({
  /**
   * 当小程序初始化完成时，会触发 onLaunch（全局只触发一次）
   */
  onLaunch: function () { },

  /**
   * 当小程序启动，或从后台进入前台显示，会触发 onShow
   */
  onShow: function (options) { },

  /**
   * 当小程序从前台进入后台，会触发 onHide
   */
  onHide: function () { },

  /**
   * 当小程序发生脚本错误，或者 api 调用失败时，会触发 onError 并带上错误信息
   */
  onError: function (msg) { }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">页面的生命周期函数</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>小程序的<strong>页面生命周期函数</strong>需要在页面的 <code>.js</code> 文件中进行声明，示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 页面的 .js 文件
Page({
  /**
   * 页面的初始数据
   */
  data: { },

  /**
   * 生命周期函数--监听页面加载 - 只调用一次
   */
  onLoad: function (options) { },

  /**
   * 生命周期函数--监听页面初次渲染完成 - 只调用一次
   */
  onReady: function () { },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () { },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () { },

  /**
   * 生命周期函数--监听页面卸载 - 只调用一次
   */
  onUnload: function () { },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () { },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () { },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () { }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">WXS 脚本</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>WXS（WeiXin Script）</strong>是<strong>小程序独有的一套脚本语言</strong>，结合 WXML，可以构建出页面的结构。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">wxs 的应用场景</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>wxml 中无法调用在页面的 .js 中定义的函数</strong>，但是，wxml 中可以调用 wxs 中定义的函数。因此，小程序中 wxs 的<strong>典型应用场景就是 “过滤器”。</strong></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">wxs 和 JavaScript 的关系*</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>虽然 wxs 的语法类似于 JavaScript，但是 wxs 和 JavaScript 是完全不同的两种语言：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>wxs 有自己的数据类型</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>number</code> 数值类型、<code>string</code> 字符串类型、<code>boolean</code> 布尔类型、<code>object</code> 对象类型、</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>function</code> 函数类型、<code>array</code> 数组类型、<code>date</code> 日期类型、<code>regexp</code> 正则</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>wxs 不支持类似于 ES6 及以上的语法形式</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><strong>不支持</strong>：let、const、解构赋值、展开运算符、箭头函数、对象属性简写、etc…</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>支持</strong>：var 定义变量、普通 function 函数等类似于 ES5 的语法</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>wxs 遵循 CommonJS 规范</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>module</code> 对象</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>require()</code> 函数</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>module.exports</code> 对象</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">基础语法</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">内嵌 wxs 脚本</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>wxs 代码可以编写在 wxml 文件中的 <code>&lt;wxs&gt;</code> 标签内，就像 Javascript 代码可以编写在 html 文件中的 <code>&lt;script&gt;</code> 标签内一样。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>wxml 文件中的每个 <code>&lt;wxs&gt;&lt;/wxs&gt;</code> 标签，<strong>必须提供 <code>module</code> 属性，用来指定当前 wxs 的模块名称</strong>，方便在 wxml 中访问模块中的成员：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;view&gt;{{m1.toUpper(username)}}&lt;/view&gt;

&lt;wxs modele="m1"&gt;
  // 将文本转为大写形式 zs -&gt; ZS
  module.exports.toUpper = function(str) {
    return str.toUpperCase()
  }
&lt;/wxs&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">定义外联的 wxs 脚本</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>wxs 代码还可以编写在<strong>以 <code>.wxs</code> 为后缀名的文件内</strong>，就像 javascript 代码可以编写在以 <code>.js</code> 为后缀名的文件中一样。示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// tools.wsx 文件
function toLower(str) {
  return str.toLowerCase()
}

module.exports = {
  toLower: toLower
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">使用外联的 wxs 脚本</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在 wxml 中引入外联的 wxs 脚本时，<strong>必须为 <code>&lt;wxs&gt;</code> 标签添加 <code>module</code> 和 <code>src</code> 属性</strong>，其中：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>module</code> 用来指定模块的名称</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>src</code> 用来指定要引入的脚本的路径，<strong>且必须是相对路径</strong>
  <br>
  示例代码如下：</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;!-- 调用 m2 模块中的方法 --&gt;
&lt;view&gt;{{m2.toLower(str)}}&lt;/view&gt;

&lt;!-- 引用外联的 toold.wsx 脚本，并命名为 m2 --&gt;
&lt;wsx src="../../utils/tools.wsx" module="m2"&gt;&lt;/wsx&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">WXS 的特点</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">与 JavaScript 不同</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>为了降低 <strong>wxs（WeiXin Script）</strong>的学习成本， wxs 语言在设计时借大量鉴了 JavaScript 的语法。但是本质上，wxs 和 JavaScript 是完全不同的两种语言！</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">不能作为组件的事件回调</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>wxs 典型的应用场景就是 <strong>“过滤器”，经常配合 Mustache 语法进行使用</strong>，例如：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;view&gt;{{m2.toLower(str)}}&lt;/view&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>但是，在 wsx 中定义的函数<strong>不能作为组件的事件回调函数</strong>。例如，下面用法是错误的：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;buton bintap="{{m2.toLower}}"&gt;按钮&lt;/button&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">隔离性</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>隔离性指的是 wxs 的运行环境和其他 JavaScript 代码是隔离的</strong>。体现在如下两方面：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>wxs 不能调用 js 中定义的函数</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>wxs 不能调用小程序提供的 API</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">性能好</h3>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>在 <strong>iOS 设备</strong>上，<strong>小程序内的 WXS 会比 JavaScript 代码快 2 ~ 20 倍</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>在 <strong>android 设备</strong>上，<strong>二者的运行效率无差异</strong></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">总结</h1>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>如何实现页面之间的导航跳转</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>声明式导航、编程式导航</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>能够知道如何实现下拉刷新效果</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>enablePullDownRefresh</code>、<strong><code>onPullDownRefresh</code></strong></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>能够知道如何实现上拉加载更多效果</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>onReachBottomDistance</code>、<strong><code>onReachBottom</code></strong></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>能够知道小程序中常用的生命周期函数</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>应用生命周期函数：<strong><code>onLaunch</code></strong>, <code>onShow</code>, <code>onHide</code></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>页面生命周期函数：<strong><code>onLoad</code></strong>, <code>onShow</code>, <strong><code>onReady</code></strong>, <code>onHide</code>, <code>onUnloa</code></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->]]></description><guid isPermaLink="false">/archives/%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E8%A7%86%E5%9B%BE%E4%B8%8E%E9%80%BB%E8%BE%91%EF%BC%89</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Tue, 21 May 2024 07:03:18 GMT</pubDate></item><item><title><![CDATA[小程序（模板与配置）]]></title><link>https://blog.qiaofugui.cn/archives/%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E6%A8%A1%E6%9D%BF%E4%B8%8E%E9%85%8D%E7%BD%AE%EF%BC%89</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E6%A8%A1%E6%9D%BF%E4%B8%8E%E9%85%8D%E7%BD%AE%EF%BC%89&amp;url=/archives/%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E6%A8%A1%E6%9D%BF%E4%B8%8E%E9%85%8D%E7%BD%AE%EF%BC%89" width="1" height="1" alt="" style="opacity:0;"><!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">WXML 模板语法</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">数据绑定</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">数据绑定的基本原则 在 data 中定义数据</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在页面对应的 .js 文件中，把数据定义到 data 对象中即可：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// pages/index.js
Page({
  data: {
    // 字符串类型数据
    info: 'init data',
    // 数组类型的数据
    msgList: [
      { msg: 'hello' },
      { msg: 'world' }
    ],
    imgSrc: 'https://res.wx.qq.com/wxdoc/dist/assets/img/0.4cb08bb4.jpg'，
    randomNum1: Math.random() * 10 // 生成 10 以内的随机数
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Mustache 语法的格式 在 WXML 中使用数据</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Mustache 语法的舒雅应用场景如下：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>绑定内容</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>绑定属性</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>运算（三元运算、算数运算等）
  <br>
  把 data 中的数据绑定到页面中渲染，使用 <strong>Mustache 语法（双大括号）</strong>将变量抱起来即可。语法格式为：</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;view&gt;{{ 要绑定的数据名称 }}&lt;/view&gt;

// pages/index.wxml
// 动态绑定数据
{{info}}

// 动态绑定属性
&lt;image src="{{imgSrc}}"&gt;&lt;/image&gt;

// 三元运算
{{randomNum1 &gt;= 5 ? '随机数大于等于5' : '随机数小于5'}}

// 算数运算
&lt;text&gt;生成100以内的随机数：{{randomNum2 * 100}}&lt;/text&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">事件绑定</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>事件是<strong>渲染层到逻辑层的通讯方式</strong>。通过事件可以将用户在渲染层产生的行为，反馈到逻辑层进行业务的处理。
 <br>
 <img class="wp-image-131" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv25.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">小程序中常用事件</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":132,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv26.png&amp;size=m" alt="" class="wp-image-132">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">事件对象的属性列表</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>当事件回调触发的时候，会收到一个事件对象 event，它的详细属性如下表所示：
 <br>
 <img class="wp-image-133" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv27.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">target 和 currentTarget 的区别</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>target</code> 是<strong>触发该事件的源头组件</strong>，而 <code>currentTarget</code> 则是<strong>当前事件所绑定的组件</strong>。举例如下：
 <br>
 <img class="wp-image-134" style="width: 150px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv28.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>点击内部的按钮时，点击事件以<strong>冒泡</strong>的方式向外扩散，也会触发外层 view 的 tap 事件处理函数。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>此时，对于外层的 view 来说：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>e.target 指向的是触发事件的源头组件，因此，<strong><code>e.target</code> 是内部的按钮组件</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>e.currentTarget 指向的是当前正在触发事件的那个组件，因此，<strong><code>e.currentTarget</code> 是当前的 view 组件</strong></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">bindtap 的语法格式</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在小程序中，不存在 HTML 中的 onclick 鼠标点击事件，而是通过 <strong>tap 事件</strong>来响应用户的触摸行为。</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>通过 <code>bindtap</code>，可以为组件绑定 tap 触摸事件，语法如下：</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;button type="primary" bindtap="btnTabHandler"&gt;按钮&lt;/button&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:list {"ordered":true,"start":2} -->
<ol start="2">
 <!-- wp:list-item -->
 <li>在页面的 .js 文件中定义对应的事件处理函数，事件参数通过形参 <strong><code>event</code>（一般简写成 e）</strong> 来接收：</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Page({
  btnTabHandler(e) { // 按钮的 tab 事件处理函数
    console.log(e) // 事件参数对象 e
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">在事件处理函数中为 data 中的数据赋值</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>通过调用 <code>this.setData(dataObject)</code> 方法，可以给页面 data 中的数据重新赋值，示例如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 页面的 .js 文件
Page({
  data: {
    count: 0
  },

  // 修改 count 的值
  changeCount() {
    this.setData({
      count: this.data.count + 1
    })
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">事件传参</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>小程序中的事件传参比较特殊，不能在绑定事件的同时为事件处理函数传递参数。<strong>例如，下面的代码将不能正常工作：</strong></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;button type="primary" bindtab="btnHandler(123)"&gt;&lt;/button&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>因为小程序会把 bindtap 的属性值，统一当作事件名称来处理，<strong>相当于要调用一个名称为 btnHandler(123) 的事件处理函数</strong>。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>可以为组件提供 <code>data-*</code> 自定义属性传参，其中 <code>*</code> 代表的是参数的名字，示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;button type="primary" bindtab="btnHandler" data-info="{{3}}"&gt;事件传参&lt;/button&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>最终：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>info 会被解析为参数的名字</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>数值 3 会被解析为参数的值</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:paragraph -->
<p>在事件处理函数中，通过 <code>event.target.dataset.参数名</code> 即可获取到具体参数的值，示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Page({
  btnHandler(e) {
    // dataset 是一个对象，包含了所有通过 data-* 传递过来的参数项
    console.log(e.target.dataset)
    // 通过 dataset 可以访问到具体参数的值
    console.log(e.target.dataset.info)
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">bindinput 的语法格式</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在小程序中，通过 <strong><code>input</code> 事件</strong>来响应文本框的输入事件，语法格式如下：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>通过 <code>bindinput</code>，可以为文本框绑定输入事件：</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;input type="text" bindinput="inputHandler" /&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:list {"ordered":true,"start":2} -->
<ol start="2">
 <!-- wp:list-item -->
 <li>在页面的 .js 文件中定义事件处理函数：</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Page({
  inputHandler(e) {
    // e.datail.value 是变化过后，文本框最新的值
    console.log(e.datail.value)
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">实现文本框和 data 之间的数据同步</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>实现步骤：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>定义数据</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Page({
  data: {
    msg: 'Hello'
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:list {"ordered":true,"start":2} -->
<ol start="2">
 <!-- wp:list-item -->
 <li>渲染结构</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;input type="text" value="{{msg}}" bindinput="inputHandler"/&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:list {"ordered":true,"start":3} -->
<ol start="3">
 <!-- wp:list-item -->
 <li>美化样式</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>input {
  border: 1px solid #ccc;
  margin:5px;
  padding: 5px;
  border-radius: 3px;
}</code></pre>
<!-- /wp:code -->
<!-- wp:list {"ordered":true,"start":4} -->
<ol start="4">
 <!-- wp:list-item -->
 <li>绑定 input 事件处理函数</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Page({
  data: {
    msg: 'Hello'
  },

  // 文本框内容改变的事件
  inputHandler(e) {
    this.setData({
      // 通过 e.detail.value 获取到文本框最新的值
      msg: e.detail.value
    })
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">页面事件</h3>
<!-- /wp:heading -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">下拉刷新事件通过 <code>onPullDownRefresh()</code> 函数即可监听当前页面的下拉刷新事件</h4>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>当处理完下拉刷新后，下拉刷新的 loading 效果会一直显示，不会主动消失，所以需要手动隐藏 loading 效果此时调用 <code>wx.stopPullDownRefresh()</code> 可以停止页面的下拉刷新</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Page({
  // 页面相关事件处理函数 -- 监听用户下拉动作
  onPullDownRefresh: function () {
    // code...
    // 当数据重置成功之后，调用函数，关闭下拉刷新的效果
    wx.stopPullDownRefresh()
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">上拉触底事件通过 <code>onReachBottom()</code> 函数即可监听当前页面的上拉触底事件</h4>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>上拉触底距离是指触发上拉触底事件时，滚动条距离页面底部的距离可以在全局或页面的 <code>.json</code> 配置文件中 ，通过 <code>onReachBottomDistance</code> 属性来设置上拉触底的距离</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Page({
  // 页面上拉触底事件的处理函数
  onReachBottom: function () {
    // code...
    console.log('触发了上拉触底的事件')
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">添加 loading 提示效果</h4>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>wx.showLoading({ title: '数据加载...' }) // 展示 loading 效果
wx.hideLoading() // 隐藏 loading 效果</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">条件渲染</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">wx:if</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在小程序中，使用 <code>wx:if="{{condition}}"</code> 来判断是否需要渲染该代码块：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;view wx:if="{{condition}}"&gt; True &lt;/view&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>也可以用 <code>wx:elif</code> 和 <code>wx:else</code> 来添加 else 判断：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;view wx:if="{{type === 1}}"&gt; 男 &lt;/view&gt;
&lt;view wx:elif="{{type === 2}}"&gt; 女 &lt;/view&gt;
&lt;view wx:else&gt; 保密 &lt;/view&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">结合 <code>&lt;block&gt;</code> 使用 wx:if</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>如果要<strong>一次性控制多个组件的展示与隐藏</strong>，可以使用一个 <code>&lt;block&gt;&lt;/block&gt;</code> 标签将多个组件包装起来，并在
 <br>
 <code>&lt;block&gt;</code> 标签上使用 <code>wx:if</code> 控制属性，示例如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;block wx:if="{{true}}"&gt;
  &lt;view&gt; view1 &lt;/view&gt;
  &lt;view&gt; view2 &lt;/view&gt;
&lt;/block&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>注意： <code>&lt;block&gt;</code> 并不是一个组件，它只是一个包裹性质的容器，不会在页面中做任何渲染。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">hidden</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在小程序中，直接使用 <code>hidden="{{ condition }}"</code> 也能控制元素的显示与隐藏：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;view hidden="{{condition}}"&gt; 条件为 true 隐藏，条件为 false 显示 &lt;/view&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">wx:if 与 hidden 的对比</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>运行方式不同</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>wx:if 以<strong>动态创建和移除元素</strong>的方式，控制元素的展示与隐藏</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>hidden 以<strong>切换样式</strong>的方式（display: none/block;），控制元素的显示与隐藏</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>使用建议</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><strong>频繁切换时，建议使用 hidden</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>控制条件复杂时，建议使用 wx:if 搭配 wx:elif、wx:else 进行展示与隐藏的切换</strong></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">列表渲染</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">wx:for</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>通过 wx:for 可以根据指定的数组，循环渲染重复的组件结构，语法示例如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;view wx:for="{{array}}"&gt;
  索引是：{{index}} 当前项是：{{item}}
&lt;/view&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>默认情况下，当前循环项的<strong>索引用 index 表示；当前循环项用 item 表示。</strong></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">手动指定索引和当前项的变量名</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>使用 <code>wx:for-index</code> 可以指定当前循环项的索引的变量名，使用 wx:for-item 可以指定当前项的变量名。示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName"&gt;
  索引是：{{idx}} 当前项是：{{itemName}}
&lt;/view&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">wx:key 的使用</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>类似于 Vue 列表渲染中的 <code>:key</code>，小程序在实现列表渲染时，也建议为渲染出来的列表项指定唯一的 key 值，从而<strong>提高渲染的效率</strong>，示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// data 数据
data: {
  userList: [
    { id: 1, name: '张三' },
    { id: 2, name: '李四' },
    { id: 3, name: '王五' }
  ]
}

// wxml 结构
&lt;view wx:for="{{userList}}" wx:key="id"&gt;{{item.name}}&lt;/view&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">WXSS 模板样式</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>WXSS (WeiXin Style Sheets)是<strong>一套样式语言</strong>，用于美化 WXML 的组件样式，类似于网页开发中的 CSS。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">WXSS 和 CSS 的关系</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>WXSS 具有 CSS 大部分特性，同时，WXSS 还对 CSS 进行了扩充以及修改，以适应微信小程序的开发。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>与 CSS 相比，WXSS 扩展的特性有：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>rpx</code> 尺寸单位</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>@import</code> 样式导入</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:image {"id":126,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv29.png&amp;size=m" alt="" class="wp-image-126">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">rpx</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>rpx（responsive pixel）</strong>是微信小程序独有的，用来<strong>解决屏适配的尺寸单位。</strong></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">rpx 的实现原理</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>rpx 的实现原理非常简单：鉴于不同设备屏幕的大小不同，为了实现屏幕的自动适配，rpx 把所有设备的屏幕，在宽度上<strong>等分为 750 份（即：当前屏幕的总宽度为 750rpx）。</strong></p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>在<strong>较小</strong>的设备上，<strong>1rpx 所代表的宽度较小</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>在<strong>较大</strong>的设备上，<strong>1rpx 所代表的宽度较大</strong></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>小程序在不同设备上运行的时候，会自动把 rpx 的样式单位换算成对应的像素单位来渲染，从而实现屏幕适配。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">rpx 与 px 之间的单位换算</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在 iPhone6 上，屏幕宽度为375px，共有 750 个物理像素，等分为 750rpx。则：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>750rpx = 375px = 750 物理像素</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>1rpx = 0.5px = 1 物理像素</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:image {"id":127,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv30.png&amp;size=m" alt="" class="wp-image-127">
</figure>
<!-- /wp:image -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>官方建议：开发微信小程序时，设计师可以用 <strong>iPhone6</strong> 作为<strong>视觉稿的标准。</strong></p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>开发举例：在 iPhone6 上如果要绘制宽 <strong>100px，高 20px</strong> 的盒子，换算成 rpx 单位，宽高分别为 <strong>200rpx 和 40rpx。</strong></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">样式导入</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>使用 WXSS 提供的 <code>@import</code> 语法，可以导入外联的样式表。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">@import 的语法格式</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>@import</code> 后跟需要导入的外联样式表的相对路径，用 <code>;</code> 表示语句结束。示例如下：
 <br>
 <img class="wp-image-128" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv31.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">全局样式和局部样式</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>定义在 <code>app.wxss</code> 中的样式为全局样式，<strong>作用于每一个页面。</strong></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">局部样式</h4>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在页面的 <code>.wxss</code> 文件中定义的样式为局部样式，<strong>只作用于当前页面。</strong></p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>当局部样式和全局样式冲突时，根据<strong>就近原则</strong>，局部样式<strong>会覆盖</strong>全局样式</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>当局部样式的<strong>权重大于或等于</strong>全局样式的权重时，<strong>才会覆盖</strong>全局的样式</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">全局配置</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>小程序根目录下的 <code>app.json</code> 文件是小程序的全局配置文件。常用的配置项如下：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>pages</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>记录当前小程序所有页面的存放路径</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>window</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>全局设置小程序窗口的外观</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>tabBar</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>设置小程序底部的 tabBar 效果</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>style</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>是否启用新版的组件样式</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">window</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">小程序窗口的组成部分</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":129,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv32.png&amp;size=m" alt="" class="wp-image-129">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">了解 window 节点常用的配置项</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":121,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv33.png&amp;size=m" alt="" class="wp-image-121">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">设置导航栏的标题</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>设置步骤：<code>app.json -&gt; window -&gt; navigationBarTitleText</code></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>需求：把导航栏上的标题，从默认的 “WeChat” 修改为 “qiaofugui”，效果如图所示：
 <br>
 <img class="wp-image-122" style="width: 300px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv34.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">设置导航栏的背景色</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>设置步骤：<code>app.json -&gt; window -&gt; navigationBarBackgroundColor</code></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>需求：把导航栏标题的背景色，从默认的 #fff 修改为 #2b4b6b，效果如图所示：
 <br>
 <img class="wp-image-123" style="width: 300px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv35.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">设置导航栏的标题颜色</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>设置步骤：<code>app.json -&gt; window -&gt; navigationBarTextStyle</code></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>需求：把导航栏上的标题颜色，从默认的 black 修改为 white ，效果如图所示：
 <br>
 <img class="wp-image-124" style="width: 300px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv36.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p><code>navigationBarTextStyle</code> 的可选值只有 <code>black</code> 和 <code>white</code></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">全局开启下拉刷新功能</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>概念：<strong>下拉刷新</strong>是移动端的专有名词，指的是通过手指在屏幕上的下拉滑动操作，从而<strong>重新加载页面数据</strong>的行为。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>设置步骤：<code>app.json -&gt; window -&gt;</code> 把 <code>enablePullDownRefresh</code> 的值设置为 true</p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>注意：在 app.json 中启用下拉刷新功能，会作用于每个小程序页面！</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">设置下拉刷新时窗口的背景色</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>当全局开启下拉刷新功能之后，默认的窗口背景为白色。如果自定义下拉刷新窗口背景色，设置步骤为:</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><code>app.json -&gt; window -&gt;</code> 为 <code>backgroundColor</code> 指定16进制的颜色值 #efefef。效果如下：
 <br>
 <img class="wp-image-125" style="width: 300px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv37.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">设置下拉刷新时 loading 的样式</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>当全局开启下拉刷新功能之后，默认窗口的 loading 样式为白色，如果要更改 loading 样式的效果，设置步骤为 <code>app.json -&gt; window -&gt;</code> 为 <code>backgroundTextStyle</code> 指定 dark 值。效果如下：
 <br>
 <img class="wp-image-117" style="width: 300px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv38.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p><code>backgroundTextStyle</code> 的可选值只有 <code>light</code> 和 <code>dark</code></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">设置上拉触底的距离</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>概念：<strong>上拉触底</strong>是移动端的专有名词，通过手指在屏幕上的上拉滑动操作，从而<strong>加载更多数据</strong>的行为。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>设置步骤： <code>app.json -&gt; window -&gt;</code> 为 <code>onReachBottomDistance</code> 设置新的数值</p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>注意：<strong>默认距离为50px</strong>，如果没有特殊需求，建议使用默认值即可。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">tabBar</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>tabBar</code> 是移动端应用常见的页面效果，<strong>用于实现多页面的快速切换。</strong>小程序中通常将其分为：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>底部 tabBar</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>顶部 tabBar</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:image {"id":118,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv39.png&amp;size=m" alt="" class="wp-image-118">
</figure>
<!-- /wp:image -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>注意：</p>
 <!-- /wp:paragraph -->
 <!-- wp:list -->
 <ul>
  <!-- wp:list-item -->
  <li>tabBar中只能配置<strong>最少 2 个、最多 5 个</strong> tab 页签</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>当渲染<strong>顶部 tabBar</strong> 时，<strong>不显示 icon</strong>，只显示文本</li>
  <!-- /wp:list-item -->
 </ul>
 <!-- /wp:list -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">tabBar 的 6 个组成部分</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><code>backgroundColor</code>：tabBar 的背景色</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>selectedIconPath</code>：选中时的图片路径</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>borderStyle</code>：tabBar 上边框的颜色</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>iconPath</code>：未选中时的图片路径</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>selectedColor</code>：tab 上的文字选中时的颜色</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>color</code>：tab 上文字的默认（未选中）颜色</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:image {"id":119,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv40.png&amp;size=m" alt="" class="wp-image-119">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">tabBar 节点的配置项</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":120,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv41.png&amp;size=m" alt="" class="wp-image-120">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">tabBar 节点 list 节点的每个 tab 项的配置选项</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":112,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv42.png&amp;size=m" alt="" class="wp-image-112">
</figure>
<!-- /wp:image -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// app.json
{
  "tabBar": {
    "list": [
      {
        "pagePath": "pages/home/home",
        "text": "首页",
        "iconPath": "/images/tabs/home.png",
        "selectedIconPath": "/images/tabs/home-active.png"
      },
      {
        "pagePath": "pages/message/message",
        "text": "消息",
        "iconPath": "/images/tabs/message.png",
        "selectedIconPath": "/images/tabs/message-active.png"
      },
      {
        "pagePath": "pages/contact/contact",
        "text": "联系我们",
        "iconPath": "/images/tabs/contact.png",
        "selectedIconPath": "/images/tabs/contact-active.png"
      },
    ]
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">页面配置</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>页面配置文件的作用：小程序中，每个页面都有自己的 .json 配置文件，用来对<strong>当前页面</strong>的窗口外观、页面效果等进行配置。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">页面配置和全局配置的关系</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>小程序中，app.json 中的 window 节点，可以<strong>全局配置</strong>小程序中每个页面的<strong>窗口表现。</strong></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>如果某些小程序页面<strong>想要拥有特殊的窗口表现</strong>，此时，<strong>“页面级别的 .json 配置文件”</strong> 就可以实现这种需求。</p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>当页面配置与全局配置<strong>冲突</strong>时，根据<strong>就近原则</strong>，最终的效果<strong>以页面配置为准</strong>。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">页面配置中常用的配置项</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>页面配置 .json 文件时，不用加 <code>window</code> 节点</strong>
 <br>
 <img class="wp-image-113" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv43.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">网络数据请求</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">小程序中网络数据请求的限制</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>出于<strong>安全性</strong>方面的考虑，小程序官方对<strong>数据接口的请求</strong>做出了如下两个限制：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>只能请求 <strong>HTTPS</strong> 类型的接口</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>必须将<strong>接口的域名添加到信任列表中</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:image {"id":114,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv44.png&amp;size=m" alt="" class="wp-image-114">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">配置 request 合法域名</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>需求描述：假设在自己的微信小程序中，希望请求 <code>https://www.qiaofugui.cn</code> 域名下的接口</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>配置步骤：登录微信小程序<strong>管理后台 -&gt; 开发 -&gt; 开发管理 -&gt; 开发设置 -&gt; 服务器域名 -&gt; 修改 request 合法域名</strong></p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>域名只支持 https 协议</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>域名不能使用 IP 地址或 localhost</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>域名必须经过 ICP 备案</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>服务器域名一个月内最多可申请 50 次修改</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">发起 GET 请求</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>wx</code> 是一个顶级对象，可以理解为浏览器的 <code>window</code> 顶级对象</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>调用微信小程序提供的 <code>wx.request()</code> 方法，可以发起 GET 数据请求，示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Page({
  onGet() {
    wx.request({
      url: 'https://qiaofugui.cn/api/get', // 请求的接口地址，必须基于 https 协议
      method: 'GET', // 请求的方式
      data: { // 发送到服务器的数据
        name: '张三',
        age: 18
      },
      success: (res) =&gt; { // 请求成功之后的回调函数
        console.log(res)
      }
    })
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">发起 POST 请求</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>调用微信小程序提供的 <code>wx.request()</code> 方法，可以发起 POST 数据请求，示例代码如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Page({
  onGet() {
    wx.request({
      url: 'https://qiaofugui.cn/api/post', // 请求的接口地址，必须基于 https 协议
      method: 'POST', // 请求的方式
      data: { // 发送到服务器的数据
        name: '张三',
        dender: '男'
      },
      success: (res) =&gt; { // 请求成功之后的回调函数
        console.log(res)
      }
    })
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">在页面刚加载时请求数据</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在很多情况下，我们需要<strong>在页面刚加载的时候，自动请求一些初始化的数据</strong>。此时需要在页面的 <code>onLoad</code> 事件中调用获取数据的函数，示例代码如下：
 <br>
 <img class="wp-image-115" style="width: 300px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv45.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">跳过 request 合法域名校验</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>如果后端程序员仅仅提供了 http 协议的接口、暂时没有提供 https 协议的接口。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>此时为了不耽误开发的进度，我们可以在微信开发者工具中 <strong>详情 -&gt; 本地设置，临时开启 「不校验合法域名、web-view（业务域名）、TLS 版本以及 HTTPS 证书」 选项</strong>，跳过 request 合法域名的校验。
 <br>
 <img class="wp-image-116" style="width: 300px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv46.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>跳过 request 合法域名校验的选项，仅限在开发与调试阶段使用！</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">关于跨域和 Ajax 的说明</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>跨域问题<strong>只存在于基于浏览器的 Web 开发中</strong>。由于小程序的宿主环境<strong>不是</strong>浏览器，<strong>而是微信客户端</strong>，所以<strong>小程序中不存在跨域的问题。</strong>
 <br>
 Ajax 技术的核心是依赖于浏览器中的 XMLHttpRequest 这个对象，由于<strong>小程序的宿主环境是微信客户端</strong>，所以小程序中<strong>不能叫做</strong> “发起 Ajax 请求”，<strong>而是叫做 “发起网络数据请求”</strong>。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">总结</h1>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>使用 WXML 模板语法渲染页面结构</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>wx:if</code>、<code>wx:elif</code>、<code>wx:else</code>、<code>hidden</code>、<code>wx:for</code>、<code>wx:key</code></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>使用 WXSS 样式美化页面结构</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>rpx</code> 尺寸单位、<code>@import</code> 样式导入、全局样式和局部样式</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>使用 app.json 对小程序进行全局性配置</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>pages</code>、<strong><code>window</code></strong>、<strong><code>tabBar</code></strong>、<code>style</code></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>使用 page.json 对小程序页面进行个性化配置</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>对单个页面进行个性化配置、<strong>就近原则</strong></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>发起网络数据请求</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>wx.request()</code> 方法、<code>onLoad()</code> 事件</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->]]></description><guid isPermaLink="false">/archives/%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E6%A8%A1%E6%9D%BF%E4%B8%8E%E9%85%8D%E7%BD%AE%EF%BC%89</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Tue, 21 May 2024 07:01:09 GMT</pubDate></item><item><title><![CDATA[小程序（基础）]]></title><link>https://blog.qiaofugui.cn/archives/%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E5%9F%BA%E7%A1%80%EF%BC%89</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E5%9F%BA%E7%A1%80%EF%BC%89&amp;url=/archives/%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E5%9F%BA%E7%A1%80%EF%BC%89" width="1" height="1" alt="" style="opacity:0;"><!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">小程序简介</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>文档：https://developers.weixin.qq.com/miniprogram/dev/framework/quickstart/</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">小程序与普通网页开发区别</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">1. 运行环境不同</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>网页运行在<strong>浏览器环境中</strong>；小程序运行在<strong>微信环境中</strong></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">2. API 不同</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>由于运行环境的不同，所以小程序中，<strong>无法调用 DOM 和 BOM 的 API。</strong></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>但是，小程序中可以调用微信环境提供的各种 API，例如：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>地理定位</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>扫码</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>支付</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">3. 开发模式不同</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>网页的开发模式：<strong>浏览器 + 代码编辑器</strong></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>小程序有自己的一套标准开发模式：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>申请小程序开发账号</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>安装小程序开发者工具</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>创建和配置小程序项目</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">第一个小程序</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">注册开发账号</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><a href="https://mp.weixin.qq.com">https://mp.weixin.qq.com</a></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">获取小程序 AppID</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>首页 --&gt; 设置 --&gt; 基本设置 --&gt; 帐号信息</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">安装微信开发者工具</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/stable.html">https://developers.weixin.qq.com/miniprogram/dev/devtools/stable.html</a></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><strong>微信开发者工具</strong>是官方推荐使用的小程序开发工具，它提供的主要功能如下：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>快速创建小程序项目</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>代码的查看和编辑</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>对小程序功能进行调试</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>小程序的预览和发布</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">小程序代码的构成</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">项目的基本组成结构</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":153,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv01.png&amp;size=m" alt="" class="wp-image-153">
</figure>
<!-- /wp:image -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>pages 用来存放所有小程序的页面</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>utils 用来存放工具性质的模块（例如：格式化时间的自定义模块）</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>app.js 小程序项目的入口文件</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>app.json 小程序项目的全局配置文件</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>app.wxss 小程序项目的全局样式文件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>project.config.json 项目的配置文件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>sitemap.json 用来配置小程序及其页面是否允许被微信索引</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">小程序页面组成部分</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>小程序官方建议把所有小程序的页面，都存放在 <strong>pages 目录</strong>中，<strong>以单独的文件夹</strong>存在，如图所示：
 <br>
 <img class="wp-image-154" style="width: 300px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv02.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>其中，每个页面<strong>由 4 个基本文件组成</strong>，它们分别是：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><code>.js</code> 文件（页面的脚本文件，存放页面的数据、事件处理函数等）</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>.json</code> 文件（当前页面的配置文件，配置窗口的外观、表现等）</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>.wxml</code> 文件（页面的模板结构文件）</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>.wxss</code> 文件（当前页面的样式表文件）</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">JSON 配置文件的作用</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>JSON 是一种数据格式，在实际开发中，JSON 总是<strong>以配置文件</strong>的形式出现。小程序项目中也不例外：通过不同的 <code>.json</code> 配置文件，可以对小程序项目进行不同级别的配置。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>小程序项目中有 4 种 json 配置文件，分别是：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>项目根目录中的 app.json 配置文件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>项目根目录中的 project.config.json 配置文件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>项目根目录中的 sitemap.json 配置文件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>每个页面文件夹中的 .json 配置文件</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">app.json 文件</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>app.json 是当前小程序的<strong>全局配置</strong>，包括了小程序的所有页面路径、窗口外观、界面表现、底部 tab 等。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>Demo 项目里边的 app.json 配置内容如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>{
  "pages":[
    "pages/index/index",
    "pages/logs/logs"
  ],
  "window":{
    "backgroundTextStyle":"light",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "Weixin",
    "navigationBarTextStyle":"black"
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json"
}</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>简单了解下这 4 个配置项的作用：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>pages：用来记录当前小程序所有页面的路径</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>window：全局定义小程序所有页面的背景色、文字颜色等</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>style：全局定义小程序组件所使用的样式版本</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>sitemapLocation：用来指明 sitemap.json 的位置</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">project.config.json 文件</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>project.config.json 是项目配置文件，用来记录我们对<strong>小程序开发工具所做的个性化配置</strong>，例如：</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>setting 中保存了编<strong>译相关的配置</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>projectname 中保存的是<strong>项目名称</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>appid 中保存的是<strong>小程序的账号 ID</strong></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">sitemap.json 文件</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>微信现已开放<strong>小程序内搜索</strong>，效果类似于 PC 网页的 SEO。sitemap.json 文件用来<strong>配置小程序页面是否允许微信索引</strong>。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>当开发者允许微信索引时，微信会通过爬虫的形式，为小程序的页面内容建立索引。当用户的搜索关键字和页面的索引匹配成功的时候，小程序的页面将可能展示在搜索结果中。
 <br>
 <img class="wp-image-155" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv03.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>注意：sitemap 的索引提示是默认开启的，如需要关闭 sitemap 的索引提示，可在小程序项目配置文件
  <br>
  <code>project.config.json</code> 的 <code>setting</code> 中配置字段 <code>checkSiteMap</code> 为 false</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">页面的 .json 配置文件</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>小程序中的每一个页面，可以使用 .json 文件来对本页面的窗口外观进行配置，<strong>页面中的配置项会覆盖 app.json 的 window 中相同的配置项</strong>。例如：
 <br>
 <img class="wp-image-156" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv04.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">新建小程序页面</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>只需要在 <code>app.json -&gt; pages</code> 中新增页面的存放路径，小程序开发者工具即可帮我们自动创建对应的页面文件，如图所示：
 <br>
 <img class="wp-image-157" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv05.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">修改项目首页</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>只需要调整 <code>app.json -&gt; pages</code> 数组中页面路径的前后顺序，即可修改项目的首页。小程序会把排在第一位的页面，当作项目首页进行渲染，如图所示：
 <br>
 <img class="wp-image-149" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv06.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">WXML 模板</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">什么是 WXML</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>WXML（WeiXin Markup Language）是小程序框架设计的一套<strong>标签语言，用来构建小程序页面的结构</strong>，其作
 <br>
 用类似于网页开发中的 HTML。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">WXML 和 HTML 的区别</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>标签名称不同</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>HTML （div, span, img, a）</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>WXML（view, text, image, navigator）</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>属性节点不同</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><code>&lt;a href="#"&gt;超链接&lt;/a&gt;</code></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>&lt;navigator url="/pages/home/home"&gt;&lt;/navigator&gt;</code></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>提供了类似于 Vue 中的模板语法</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>数据绑定</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>列表渲染</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>条件渲染</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">WXSS 样式</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">什么是 WXSS</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>WXSS (WeiXin Style Sheets)是一套<strong>样式语言</strong>，用于描述 WXML 的组件样式，类似于网页开发中的 CSS。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">WXSS 和 CSS 的区别</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>新增了 rpx 尺寸单位</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>CSS 中需要手动进行像素单位换算，例如 rem</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>WXSS 在底层支持新的尺寸单位 rpx，在不同大小的屏幕上小程序会自动进行换算</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>提供了全局的样式和局部样式</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>项目根目录中的 app.wxss 会作用于所有小程序页面</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>局部页面的 .wxss 样式仅对当前页面生效</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>WXSS 仅支持部分 CSS 选择器</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>.class 和 #id</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>element</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>并集选择器、后代选择器</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>::after 和 ::before 等伪类选择器</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">JS 逻辑交互</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">小程序中的 .js 文件</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>一个项目仅仅提供界面展示是不够的，在小程序中，我们通过 .js 文件来处理用户的操作。例如：响应用户的点击、获取用户的位置等等。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">小程序中 .js 文件的分类</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>小程序中的 JS 文件分为三大类，分别是：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>app.js</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>是<strong>整个小程序项目的入口文件</strong>，通过调用 <strong><code>App()</code> 函数</strong>来启动整个小程序</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>页面的 .js 文件</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>是<strong>页面的入口文件</strong>，通过调用 <strong><code>Page()</code> 函数</strong>来创建并运行页面</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>普通的 .js 文件</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>是<strong>普通的功能模块文件</strong>，用来封装<strong>公共的函数或属性</strong>供页面使用</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">小程序的宿主环境</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">什么是宿主环境</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>宿主环境</strong>（host environment）指的是<strong>程序运行所必须的依赖环境</strong>。例如：<strong>Android 系统</strong>和 <strong>iOS 系统</strong>是两个不同的宿主环境。安卓版的微信 App 是不能在 iOS 环境下运行的，所以，Android 是安卓软件的宿主环境，<strong>脱离了宿主环境的软件是没有任何意义的！</strong>
 <br>
 <img class="wp-image-150" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv07.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">小程序的宿主环境</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>手机微信是小程序的宿主环境</strong>，如图所示：
 <br>
 <img class="wp-image-151" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv08.png&amp;size=m" alt="">
 <br>
 小程序<strong>借助宿主环境提供的能力</strong>，可以完成许多普通网页无法完成的功能，例如：微信扫码、微信支付、微信登录、地理定位、etc…</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">小程序宿主环境包含的内容</h2>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>通信模型</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>运行机制</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>组件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>API</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">通讯模型</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>通信的主体</strong>
 <br>
 小程序中通信的主体是<strong>渲染层</strong>和<strong>逻辑层</strong>，其中：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>WXML 模板和 WXSS 样式工作在渲染层</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>JS 脚本工作在逻辑层</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:image {"id":152,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv09.png&amp;size=m" alt="" class="wp-image-152">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">小程序的通信模型</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>小程序中的通信模型分为两部分：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>渲染层</strong>和<strong>逻辑层</strong>之间的通信</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>由微信客户端进行转发</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>逻辑层</strong>和<strong>第三方服务器</strong>之间的通信</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>由微信客户端进行转发</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:image {"id":144,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv10.png&amp;size=m" alt="" class="wp-image-144">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">运行机制</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">小程序启动的过程</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>把小程序的代码包下载到本地</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>解析 app.json 全局配置文件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>执行 app.js 小程序入口文件，<strong>调用 <code>App()</code> 创建小程序实例</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>渲染小程序首页</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>小程序启动完成</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">页面渲染的过程</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>加载解析页面的 .json 配置文件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>加载页面的 .wxml 模板和 .wxss 样式</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>执行页面的 .js 文件，<strong>调用 <code>Page()</code> 创建页面实例</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>页面渲染完成</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">组件</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">小程序中组件的分类</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>小程序中的组件也是由宿主环境提供的，开发者可以基于组件快速搭建出漂亮的页面结构。官方把小程序的组件分为了 9 大类，分别是：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>视图容器</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>基础内容</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>表单组件</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>导航组件</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>媒体组件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>map 地图组件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>canvas 画布组件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>开放能力</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>无障碍访问</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading"><strong>常用的视图容器类组件</strong></h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>view</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>普通视图区域</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>类似于 HTML 中的 div，是一个块级元素</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>常用来实现页面的布局效果</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>scroll-view</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>可滚动的视图区域</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>常用来实现滚动列表效果</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>swiper 和 swiper-item</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>轮播图容器组件 和 轮播图 item 组件</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">view 组件的基本使用</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>实现如图的 flex 横向布局效果：
 <br>
 <img class="wp-image-145" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv11.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">scroll-view 组件的基本使用</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>实现如图的纵向滚动效果：
 <br>
 <img class="wp-image-146" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv12.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">swiper 和 swiper-item 组件的基本使用</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>实现如图的轮播图效果：
 <br>
 <img class="wp-image-147" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv13.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">swiper 组件的常用属性</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":148,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv14.png&amp;size=m" alt="" class="wp-image-148">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading"><strong>常用的基础内容组件</strong></h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>text</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>文本组件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>类似于 HTML 中的 span 标签，是一个行内元素</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>rich-text</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>富文本组件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>支持把 HTML 字符串渲染为 WXML 结构</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">text 组件的基本使用</h4>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>通过 text 组件的 <code>user-select</code> 属性，实现长按选中文本内容的效果：
 <br>
 <img class="wp-image-140" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv15.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">rich-text 组件的基本使用</h4>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>通过 rich-text 组件的 <code>nodes</code> 属性节点，<strong>把 HTML 字符串渲染为对应的 UI 结构</strong>：
 <br>
 <img class="wp-image-141" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv16.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">其它常用组件</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>button</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>按钮组件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>功能比 HTML 中的 button 按钮丰富</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>通过 open-type 属性可以调用微信提供的各种功能（客服、转发、获取用户授权、获取用户信息等）</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>image</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>图片组件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>image 组件默认宽度约 300px、高度约 240px</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>navigator</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>页面导航组件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>类似于 HTML 中的 a 链接</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">button 按钮的基本使用</h4>
<!-- /wp:heading -->
<!-- wp:image {"id":142,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv17-1024x467.png&amp;size=m" alt="" class="wp-image-142">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">image 组件的基本使用</h4>
<!-- /wp:heading -->
<!-- wp:image {"id":143,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv18-1024x388.png&amp;size=m" alt="" class="wp-image-143">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">image 组件的 mode 属性</h4>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>image 组件的 <code>mode</code> 属性用来指定图片的<strong>裁剪和缩放</strong>模式，常用的 mode 属性值如下：
 <br>
 <img class="wp-image-135" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv19.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">API</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">小程序 API 概述</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>小程序中的 API 是由宿主环境提供的</strong>，通过这些丰富的小程序 API，开发者可以方便的调用微信提供的能力，例如：获取用户信息、本地存储、支付功能等。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">小程序 API 的 3 大分类</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>事件监听 API</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>特点：以 <code>on</code> 开头，用来<strong>监听某些事件的触发</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>举例：<code>wx.onWindowResize(function callback)</code> 监听窗口尺寸变化的事件</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>同步 API</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>特点1：以 <code>Sync</code> 结尾的 API 都是同步 API</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>特点2：同步 API 的执行结果，可以通过函数返回值直接获取，如果执行出错会抛出异常</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>举例：<code>wx.setStorageSync('key', 'value')</code> 向本地存储中写入内容</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>异步 API</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>特点：类似于 jQuery 中的 <code>$.ajax(options)</code> 函数，需要通过 success、fail、complete 接收调用的结果</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>举例：<code>wx.request()</code> 发起网络数据请求，通过 success 回调函数接收数据</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">协同工作和发布</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">了解权限管理需求</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在中大型的公司里，人员的分工非常仔细：同一个小程序项目，一般会有不同岗位、不同角色的员工同时参与设计与开发。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>此时出于管理需要，我们<strong>迫切需要对不同岗位</strong>、<strong>不同角色的员工的权限进行边界的划分</strong>，使他们能够高效的进行协同工作。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">了解项目成员的组织结构</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":136,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv20.png&amp;size=m" alt="" class="wp-image-136">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">小程序的开发流程</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":137,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv21-1024x248.png&amp;size=m" alt="" class="wp-image-137">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">成员管理的两个方面</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>小程序成员管理体现在<strong>管理员</strong>对小程序<strong>项目成员</strong>及<strong>体验成员</strong>的管理：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>项目成员：</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>表示参与小程序开发、运营的成员</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>可登录小程序管理后台</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>管理员可以添加、删除项目成员，并设置项目成员的角色</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>体验成员：</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>表示参与小程序内测体验的成员</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>可使用体验版小程序，但不属于项目成员</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>管理员及项目成员均可添加、删除体验成员</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:image {"id":138,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv22.png&amp;size=m" alt="" class="wp-image-138">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">不同项目成员对应的权限</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":139,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv23-1024x413.png&amp;size=m" alt="" class="wp-image-139">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">开发者的权限说明</h2>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>开发者权限：</strong>可使用小程序开发者工具及对小程序的功能进行代码开发</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>体验者权限：</strong>可使用体验版小程序</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>登录权限：</strong>可登录小程序管理后台，无需管理员确认</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>开发设置：</strong>设置小程序服务器域名、消息推送及扫描普通链接二维码打开小程序</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>腾讯云管理：云开发相关设置</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">添加项目成员和体验成员</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>登录小程序管理后台 <strong>管理 -&gt; 成员管理</strong></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">软件开发过程中的不同版本</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在软件开发过程中，根据时间节点的不同，会产出不同的软件版本，例如：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>开发者编写代码的同时，对项目代码进行自测<strong>（开发版本）</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>直到程序达到一个稳定可体验的状态时，开发者把体验版本给到产品经理和测试人员进行<strong>体验测试</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>最后修复完程序的 Bug 后，<strong>发布正式</strong>版供外部用户使用</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">小程序的版本</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":130,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fxiaochengxv24.png&amp;size=m" alt="" class="wp-image-130">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">小程序发布上线的整体步骤</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>一个小程序的发布上线，一般要经过 <strong>上传代码 -&gt; 提交审核 -&gt; 发布</strong> 这三个步骤。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">上传代码</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>点击开发者工具顶部工具栏中的 <strong>“上传”</strong> 按钮</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>填写<strong>版本号</strong>以及<strong>项目备注</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">在后台查看上传之后的版本</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>登录小程序管理后台 -&gt; 管理 -&gt; 版本管理 -&gt; 开发版本，即可查看刚才提交上传的版本了</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">提交审核</h3>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>为什么需要提交审核：为了保证小程序的质量，以及符合相关的规范，小程序的发布是需要经过腾讯官方审核的。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>提交审核的方式：在开发版本的列表中，点击“提交审核”按钮之后，按照页面提示填写相关的信息，就能把小程序提交到腾讯官方进行审核。</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">发布</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>审核通过之后，管理员的微信中会收到小程序通过审核的通知，此时在审核版本的列表中，点击 <strong>“发布”</strong> 按钮之后，即可把 <strong>“审核通过”</strong> 的版本发布为 <strong>“线上版本”</strong>，供所有小程序用户访问和使用。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">基于小程序码进行推广</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>相对于普通二维码来说，小程序码的优势如下：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>在样式上更具<strong>辨识度</strong>和<strong>视觉冲击力</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>能够更加清晰地树立小程序的<strong>品牌形象</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>可以帮助开发者<strong>更好地推广小程序</strong>
  <br>
  获取小程序码的 5 个步骤：
  <br>
  <strong>登录小程序管理后台 -&gt; 设置 -&gt; 基本设置 -&gt; 基本信息 -&gt; 小程序码及线下物料下载</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">查看小程序运营数据的两种方式</h2>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>在 “小程序后台” 查看</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>登录小程序管理后台</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>点击侧边栏的 “统计”</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>点击相应的 tab 可以看到相关的数据</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>使用 “小程序数据助手” 查看</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>打开微信</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>搜索“小程序数据助手”</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>查看已发布的小程序相关的数据</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">总结</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>⚫ 微信开发者工具的使用、appID 的获取</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>⚫ app.js、app.json、app.wxss、pages 文件夹</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>⚫ wxml、wxss、json、js</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>⚫ view、text、image</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>⚫ 成员管理、发布小程序、查看运营数</p>
<!-- /wp:paragraph -->]]></description><guid isPermaLink="false">/archives/%E5%B0%8F%E7%A8%8B%E5%BA%8F%EF%BC%88%E5%9F%BA%E7%A1%80%EF%BC%89</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Tue, 21 May 2024 06:53:39 GMT</pubDate></item><item><title><![CDATA[Pinia]]></title><link>https://blog.qiaofugui.cn/archives/pinia</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=Pinia&amp;url=/archives/pinia" width="1" height="1" alt="" style="opacity:0;"><!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">Pinia</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>官网：https://pinia.vuejs.org/zh/</p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>一个全新的用于 Vue 的状态管理库</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">介绍</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>pinia 最初是 一个实验，目的是在2019年11月左右重新设计 Vue 状态管理在 Composite API 上的样子，也就是下一代 Vuex</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>之前的 Vuex 主要服务于 Vue2，选项式 API</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>如果想要在 Vue3 中使用 Vuex，需要使用它的版本4</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>只是一个过渡的选择，还有很大缺陷</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>所以在 Vue3 伴随着组合式 API 诞生之后，也设计了全新的 Vuex：Pinia，也就是 Vuex5</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>提案链接：https://github.com/vuejs/rfcs/pull/271</p>
 <!-- /wp:paragraph -->
 <!-- wp:list -->
 <ul>
  <!-- wp:list-item -->
  <li>Vue2 和 Vue3 都支持</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>处理初始化安装和 SSR 配置之外，两者的 API 都是相同的</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>官方文档注意针对 Vue3 进行说明，必要的时候会提供 Vue2 的注释</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>支持 Vue DevTools</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>跟踪 actions、mutations 的时间线</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>在使用容器的组件中就可以观察到容器本身</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>支持 time-travel 更容易的调试功能</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>在 Vue2 中 Pinia 使用 Vuex 的现有接口，所以不能与 Vuex 一起使用</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>但是针对于 Vue3 中的调试工具支持还不够完美，比如还没有 time-travel 调试功能</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>模块热更新</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>无需重新加载页面即可修改您的容器</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>热更新的时候保持任何现有状态</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>支持使用插件扩展 Pinia 功能</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>相比 Vuex 有更好完美的 TypeScript 支持</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>支持服务端渲染</li>
  <!-- /wp:list-item -->
 </ul>
 <!-- /wp:list -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">核心概念</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Pinia 从使用者角度和之前 Vuex 几乎是一样的</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>Store（如 Pinia）是一个保存状态和业务逻辑的实体，它不绑定到您的组件树。换句话说，<strong>它承载全局 state</strong>。它有点像一个始终存在的组件，每个人都可以读取和写入。它有<strong>三个核心概念</strong></p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>在 Vuex 中有四个核心概念：</p>
 <!-- /wp:paragraph -->
 <!-- wp:list -->
 <ul>
  <!-- wp:list-item -->
  <li>State</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>Getters</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>Mutations</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>Actions</li>
  <!-- /wp:list-item -->
 </ul>
 <!-- /wp:list -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">state：类似组件的 <strong><code>data</code></strong>，用来存储全局状态</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>{
  todos: [
    { id: 1, title: '吃饭', done: false },
    { id: 2, title: '睡觉', done: false },
    { id: 3, title: '打豆豆', done: true }
  ]
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">getters：类似组件的 <strong><code>computed</code></strong>，根据已有的 State 封装派生数据，也具有缓存的特性</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>doneCount() {
  return todos.filter(item =&gt; item.done).length
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">actions：类似组件的 <strong><code>menthods</code></strong>，用来封装业务逻辑，同步异步都可以</h3>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>在 Vuex 中同步操作用 mutations，异步操作用 actions，太麻烦。<strong>Pinia 中没有 mutations</strong></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">基本示例</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>首先创建一个 store：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () =&gt; {
    return { count: 0 }
  },
  // 可以这样写
  // state: () =&gt; ({ count: 0 })

  actions: {
    increment() {
      // 在 Vuex 中需要搞两步：1. 定义 mutations 2. 提交 mutations
      this.count++
    }
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>在组件中使用它：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { useCounterStore } from '@/stores/counter'

export default {
  setuo() {
    const counter = useCounterStore()

    counter.count++
    // with autocompletion
    counter.$patch({ count: counter + 1 })
    // or using an action instead
    counter.increment()
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>甚至可以使用函数（类似于组件 <code>setup()</code>）为更高级的用例定义 Store：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export const useCounterStore = defineStore('count', () =&gt; {
  const count = ref(0)
  function increment() {
    count.value++
  }

  return { count, increment }
})</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>如果仍不熟悉 <code>setup()</code> Composition API，Pania 也支持类似 Vuex 的一组 地图助手。可以以相同的方式定义商店，但随后使用 <code>mapStores()</code>，<code>mapActions()</code>:</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export const useCounterStore = defineStore('count', () =&gt; {
  state: () =&gt; ({ count: 0 }),
  getters: {
    double: (state) =&gt; state.count * 2
  },
  actions: {
    increment() {
      this.count++
    }
  }
})

const useUserStore = defineStore('user', () =&gt; {
  // ...
})

export default {
  computed: {
    // other computed properties
    // ...
    // gives access to this.counterStore and this.userStore
    ...mapStores(useCounterStore, useUserStore),
    // gives read access to this.count and this.double
    ...mapState(useCounterStore, ['count', 'double'])
  },
  methods: {
    // gives access to this.increment()
    ...mapActions(useCounterStore, ['increment'])
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">Pinia vs Vuex</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Pinia 试图尽可能接近 Vuex 的理念。它只在测试 Vuex 下一次迭代的提案，并取得了成功，因为目前有一个针对 Vuex5 的开放式 RFC，其 API 与 Piniia 使用的 API 非常相似。<em>Pinia 的作者（Eduardo）是 Vue.js 核心团队的一员，并积极参与 Router 和 Vuex 等 API 的设计</em>。他对这个项目的意图是重新设计使用全局 Store 的体验，同时保存 Vue 平易近人的哲学。让 Pinia 的 API 与 Vuex 一样接近，因为它不断向前发展，使人们可以轻松地迁移到 Vuex，甚至在未来融合这两个项目（在 Vuex 下）。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">关于版本问题：</h3>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>Vuex 当前最新版本是 4.x</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>VueX4 用于 Vue3</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>Vuex3 用于 Vue2</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>Pinia 当前最新版本是 2.x</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>即支持 Vue2 也支持 Vue3
  <br>
  可以认为就是 Vuex5，因为它的作者是官方的开发人员，并且已被官方接管了</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Pinia API 与 Vuex ≤4 有很大不同，即：</h3>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>没有 <code>mutations</code>，mutations 被认为是非常冗长的。最初带来了 devtools 集成，但这不再是问题</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>不在于模块的嵌套结构。仍可以通过在另一个 store 中导入和使用 store 来隐式嵌套 store，但 Pinia 通过设计提供扁平解构，同时仍然支持 store 之间的交叉组合方式。甚至可以拥有 store 的循环依赖关系</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>更好的 <code>typescript</code> 支持，无需创建自定义的复杂包装器来支持 TypeScript。所有内容都是类型化的，并且 API 的设计方式尽可能地利用 TS 类型推断</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>不再需要注入、导入函数、调用它们，享受自动补全</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>无需动态添加 stores，默认情况下它们都是动态的，您甚至不会注意到，仍可以随时使用 store 来注册它，但因为它是自动的，所以无需担心</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>没有命名空间模块，鉴于 store 的扁平架构，“命名空间” store 是其定义方式所固有的，您可以说所有 stores 都是命名空间的</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">关于名字</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Pinia (发音为 /piːnjʌ/，类似英文中的 “peenya”) 是最接近有效包名 piña (西班牙语中的 pineapple，即“菠萝”) 的词。 菠萝花实际上是一组各自独立的花朵，它们结合在一起，由此形成一个多重的水果。 与 Store 类似，每一个都是独立诞生的，但最终它们都是相互联系的。 它(菠萝)也是一种原产于南美洲的美味热带水果。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">关于作者</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>vuejs 核心团队成员 https://cn.vuejs.org/about/team.html
 <br>
 <img class="wp-image-323" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FPinia01.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">快速入门</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">安装</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>yarn add pinia
# or with npm
npm install pinia</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>如果应用程序使用 Vue2，还需要安装组合式 api 包：<code>@vue/composition-api</code></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">初始化配置</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Vue3:</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const pinia = createPinia()
const app = createApp(App)

app.use(pinia)
app.mount('#app')</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>如果你使用的是 Vue2，你还需要安装一个插件，并在应用的根部注入创建的 pinia：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { createPinia, PiniaVuePlugin } from 'pinia'

Vue.use(PiniaVuePlugin)
const pinia = createPinia()

new Vue({
  el: '#app',
  // 其他配置...
  // ...
  // 请注意，同一个`pinia'实例
  // 可以在同一个页面的多个 Vue 应用中使用。 
  pinia,
})</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>这样才能提供 devtools 的支持。在 Vue 3 中，一些功能仍然不被支持，如 time traveling 和编辑，这是因为 vue-devtools 还没有相关的 API，但 devtools 也有很多针对 Vue 3 的专属功能，而且就开发者的体验来说，Vue 3 整体上要好得多。在 Vue 2 中，Pinia 使用的是 Vuex 的现有接口(因此不能与 Vuex 一起使用)。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">定义和使用 Store</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">使用 Store</h2>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">使用 storeToRefs 解构 Store 数据</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">状态</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">定义状态</h2>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">访问 State</h2>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">重置状态</h2>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">与 Options API 一起使用</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">创建项目</h1>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>yarn create vite</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">安装 Pinia</h1>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>yarn add pinia</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">基本使用</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">1. 创建 pinia 示例并挂载</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// src/main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'

import { createPinia } from 'pinia'
// 1.创建 Pinia 实例
const pinia = createPinia()

const app = createApp(App)

// 2.挂载到 Vue 根实例
app.use(pinia)

app.mount('#app')</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">2. 基本使用</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// src/store/index.ts
import { defineStore } from 'pinia'

// 1.定义并导出容器
// 参数1：容器 ID，必须唯一，将来 Pinia 会把所有的容器挂载到根容器
// 参数2：选项对象
// 返回值：一个函数，调用得到容器实例
export const useMainStore = defineStore('main', {
  /**
   * 类似于组件的 data，用来存储全局状态的
   * 1.必须是函数：是为了在服务端渲染的时候避免交叉请求导致的状态数据污染
   * 2.必须是箭头函数：为了更好的 TS 类型推导
   */
  state: () =&gt; {
    return {
      count: 100,
      name: 'Joe',
      arr: [1, 2, 3]
    }
  },

  /**
   * 类似于组件的 computed，用来封装计算属性，有缓存功能
   */
  getters: {
    // 函数接收一个可选参数： state 状态对象
    count10(state) {
      return state.count + 10
    }
    // 如果在 getters 中使用了 this 则必须手动指定返回值的类型，否则类型推导不出来
    // count10():number {
    //   return this.count + 10
    // }
  },

  /**
   * 类似于组件的 methods，封装业务逻辑，修改 state
   */
  actions: {
    // 不能使用箭头函数定义 actions，箭头函数绑定外部 this
    changeState() {
      // this.count++
      // this.name = 'qiaofugui' + this.count
      // this.arr.push(4)

      this.$patch((state) =&gt; {
        state.count++
        state.name = 'qiaofugui' + state.count
        state.arr.push(4)
      })
    }
  }
})

// 2.使用容器中的 state

// 3. 修改 state

// 4.容器中的 action 使用</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>组件使用</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;template&gt;
  &lt;p&gt;{{ mainStore.count }}&lt;/p&gt;
  &lt;p&gt;{{ mainStore.name }}&lt;/p&gt;
  &lt;p&gt; {{ mainStore.arr }}&lt;/p&gt;
  &lt;p&gt; {{ mainStore.count10 }}&lt;/p&gt;
  &lt;hr&gt;
  &lt;p&gt;{{ count }}&lt;/p&gt;
  &lt;p&gt;{{ name }}&lt;/p&gt;
  &lt;p&gt; {{ arr }}&lt;/p&gt;
  &lt;hr&gt;
  &lt;p&gt;
    &lt;button @click="handelChangeState"&gt;修改数据&lt;/button&gt;
  &lt;/p&gt;
&lt;/template&gt;
&lt;script lang='ts' setup&gt;
import { storeToRefs } from 'pinia'
import { useMainStore } from '../store/index'

const mainStore = useMainStore()
console.log(mainStore)

// 这是有问题的，因为这样拿到的数据不是响应式的
// pinia 其实就是把 state 数据做了 reactive 处理了
// const { count, name, arr } = mainStore
// 解决办法就是使用 storeToRefs 把解构出来的数据做 ref 响应式代理
const { count, name, arr } = storeToRefs(mainStore)

// 修改数据
const handelChangeState = () =&gt; {
  // 方式一：最简单的方式
  // mainStore.count++
  // mainStore.name = 'Joe'

  // 方式二：如果需要修改多个数据，建议使用 $patch 批量更新
  // mainStore.$patch({
  //   count: count.value + 1,
  //   name: 'qiaofugui' + count.value + 1,
  //   arr: [...mainStore.arr, 4]
  // })

  // 方式三：更好的批量更新方式 $patch 一个函数
  // mainStore.$patch(state =&gt; {
  //   state.count++
  //   state.name = 'qiaofugui' + state.count
  //   state.arr.push(4)
  // })

  // 方式四：逻辑比较多的时候封装到 action 处理
  mainStore.changeState()
}
&lt;/script&gt;
&lt;style lang="less" scoped&gt;&lt;/style&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">Pinia 添加到 vue 项目中</h1>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>使用 create-vue 创建空的新项目 <code>npm init vue@latest</code></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>按照官方文档</strong>安装 pinia 到项目 https://pinia.vuejs.org/zh/getting-started.html</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">简单使用</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":322,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fpinia01-2-1024x341.png&amp;size=m" alt="" class="wp-image-322">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">getters</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Pinia 中的 getters 直接使用 <strong><code>computed</code> 函数</strong>进行模拟</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { ref, computed } from 'vue'
// 数据（state）
const count = ref(0)

// getter
const doubleCount = computed(() =&gt; count.value * 2)</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">action 实现异步</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>action 中实现异步和组件中定义数据、方法的风格完全一致</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const API_URL = 'http://127.0.0.1/list'
// 准备数据（state）
const list = ref([])

// 异步 action
const loadList = async () =&gt; {
  count { data: res } = await axios.get(API_URL)
  list.value = res
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">storeToRefs</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在组件中使用 <code>storeToRefs</code> 函数可以辅助保持数据（state + getter）的响应式解构</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { useCounterStore } from '@/stores/counter.js'
import { storeToRefs } from 'pinia'

// 保持数据响应式
const { count, doubleCount } = storeToRefs(counterStore)</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>属性使用 storeToRefs 函数进行解构，方法直接进行解构</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->]]></description><guid isPermaLink="false">/archives/pinia</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Tue, 21 May 2024 06:47:42 GMT</pubDate></item><item><title><![CDATA[React-Route-V6]]></title><link>https://blog.qiaofugui.cn/archives/react-route-v6</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=React-Route-V6&amp;url=/archives/react-route-v6" width="1" height="1" alt="" style="opacity:0;"><!-- wp:paragraph -->
<p><a href="https://blog.qiaofugui.cn/index.html/2024/05/21/react-router-6-%e5%bf%ab%e9%80%9f%e4%b8%8a%e6%89%8b/">React-Router v6快速上手</a></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">一. 简介</h1>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>react-router ：<strong>核心模块</strong>，包含 React 路由大部分的核心功能，包括路由匹配算法和大部分核心组件和钩子。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>react-router-dom：React 应用中用于路由的软件包，包括 react-router 的所有内容，并添加了一些特定于 DOM 的 API，包括但不限于 BrowserRouter、HashRouter 和 Link。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>react-router-native： 用于开发 React Native 应用，包括 react-router 的所有内容，并添加了一些特定于 React Native 的 API，包括但不限于 NativeRouter 和 Link。</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">二. 对比 V5</h1>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>包大小对比
  <br>
  <img class="wp-image-304" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Freact-route-v6.png&amp;size=m" alt=""></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>&lt;Route&gt;</code> 特性变更</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>path：与当前页面对应的 URL 匹配。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>element：新增，用于决定路由匹配时，渲染哪个组件。代替，v5 的 component 和 render。</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><code>&lt;Routes&gt;</code> 代替了 <code>&lt;Switch&gt;</code></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong><code>&lt;Outlet&gt; &lt;/Outlet&gt;</code></strong> 让嵌套路由更简单</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>useNavigate</code> 代替 <code>useHistory</code></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>移除了 <code>&lt;NavLink/&gt;</code> 的 <code>activeClassName</code> 和 <code>activeStyle</code></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>钩子 <code>useRoutes</code> 代替 <code>react-router-config</code></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>https://reactrouter.com/docs/en/v6/upgrading/v5</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">三. 用法详解</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>npm i react-router-dom</code></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">1. 一级路由与多级路由</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;Routes&gt;
    {/*&lt;Route path="/" element={&lt;Film/&gt;}/&gt;*/}
    &lt;Route index element={&lt;Film/&gt;}/&gt; 
    &lt;Route path="/film" element={&lt;Film/&gt;}/&gt;
    &lt;Route path="/cinema" element={&lt;Cinema/&gt;}/&gt;
    &lt;Route path="/center" element={&lt;Center/&gt;}/&gt;
&lt;/Routes&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>index 用于嵌套路由，仅匹配父路径时，设置渲染的组件。</p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>解决当嵌套路由有多个子路由但本身无法确认默认渲染哪个子路由的时候，可以增加 <code>index</code> 属性来指定默认路由。<code>index</code> 路由和其他路由不同的地方是它没有 path 属性，他和父路由共享同一个路径。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">2. 路由重定向</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(1) 官方推荐方案 1: 使用 Navigate 组件替代</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React from 'react'
import { Route, Routes, Navigate } from 'react-router-dom'

import Film from '../views/Film'
import Cinema from '../views/Cinema'
import Center from '../views/Center'

export default function MyRouter () {
  return (
    &lt;div&gt;
      &lt;Routes&gt;
        &lt;Route path="/film" element={&lt;Film /&gt;}&gt;&lt;/Route&gt;
        &lt;Route path="/cinema" element={&lt;Cinema /&gt;}&gt;&lt;/Route&gt;
        &lt;Route path="/center" element={&lt;Center /&gt;}&gt;&lt;/Route&gt;

        &lt;Route path="/" element={&lt;Navigate to='/film' /&gt;}&gt;&lt;/Route&gt;
      &lt;/Routes&gt;
    &lt;/div&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p><code>*</code> 万能匹配，当所有都没匹配到就匹配这个 <code>*</code></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(2) 官方推荐方案 2: 自定义 Redirect 组件</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// components/Redirect.js
import React, { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'

export default function Redirect (props) {

  const navigate = useNavigate()

  useEffect(() =&gt; {
    navigate(props.to, { replace: true })
  }, [])

  return null
}



// 其他组件引用
&lt;Route path="/" element={&lt;Redirect to='/film' /&gt;}&gt;&lt;/Route&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(3) 404 如何实现?</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React from 'react'
import { Route, Routes, Navigate } from 'react-router-dom'

import Redirect from '../components/Redirect'
import NotFound from '../views/NotFound'

export default function MyRouter () {
  return (
    &lt;div&gt;
      &lt;Routes&gt;
        &lt;Route path="/film" element={&lt;Film /&gt;}&gt;&lt;/Route&gt;
        &lt;Route path="/notfound" element={&lt;NotFound /&gt;}&gt;&lt;/Route&gt;

        &lt;Route path="/" element={&lt;Redirect to='/film' /&gt;}&gt;&lt;/Route&gt;
        &lt;Route path="*" element={&lt;Navigate to='/notfound' /&gt;}&gt;&lt;/Route&gt;
      &lt;/Routes&gt;
    &lt;/div&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">3. 嵌套路由</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>&lt;Outlet&gt;&lt;/Outlet&gt;</code></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// router/index.js
import React from 'react'
import { Route, Routes, Navigate } from 'react-router-dom'

import Film from '../views/Film'
import NowPlaying from '../views//films/NowPlaying'
import ComingSoon from '../views/films/ComingSoon'
import Cinema from '../views/Cinema'
import Center from '../views/Center'
import Search from '../views/Search'

import Redirect from '../components/Redirect'
import NotFound from '../views/NotFound'

export default function MyRouter () {
  return (
    &lt;div&gt;
      &lt;Routes&gt;
        &lt;Route path="/film" element={&lt;Film /&gt;}&gt;
          {/* 支持相对路径和绝对路径 */}
          &lt;Route path="" element={&lt;Navigate to='/film/nowplaying' /&gt;}&gt;&lt;/Route&gt;
          &lt;Route path="nowplaying" element={&lt;NowPlaying /&gt;}&gt;&lt;/Route&gt;
          &lt;Route path="comingsoon" element={&lt;ComingSoon /&gt;}&gt;&lt;/Route&gt;
        &lt;/Route&gt;
        &lt;Route path="/cinema" element={&lt;Cinema /&gt;}&gt;&lt;/Route&gt;
        &lt;Route path="/cinema/search" element={&lt;Search /&gt;}&gt;&lt;/Route&gt;
        &lt;Route path="/center" element={&lt;Center /&gt;}&gt;&lt;/Route&gt;
        &lt;Route path="/notfound" element={&lt;NotFound /&gt;}&gt;&lt;/Route&gt;

        &lt;Route path="/" element={&lt;Redirect to='/film' /&gt;}&gt;&lt;/Route&gt;
        &lt;Route path="*" element={&lt;Navigate to='/notfound' /&gt;}&gt;&lt;/Route&gt;
      &lt;/Routes&gt;
    &lt;/div&gt;
  )
}


// views/Film.js
import React from 'react'
import { Outlet } from 'react-router-dom'

export default function Film () {
  return (
    &lt;div&gt;
      Film
      &lt;div
        style={{ height: 200, backgroundColor: 'skyblue' }}&gt;
        轮播
      &lt;/div&gt;

      {/* 路由容器 */}
      &lt;Outlet /&gt;
    &lt;/div&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">4. 声明式导航与编程式导航</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React from 'react'
import { NavLink } from 'react-router-dom'

import './TabBar.css'

export default function TbaBar () {
  return (
    &lt;ul className='tabbar'&gt;
      &lt;li&gt;
        &lt;NavLink to='/film' className={({ isActive }) =&gt; isActive ? 'joe-active' : ''}&gt;Film&lt;/NavLink&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;NavLink to='cinema' className={({ isActive }) =&gt; isActive ? 'joe-active' : ''}&gt;Cinema&lt;/NavLink&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;NavLink to='center' className={({ isActive }) =&gt; isActive ? 'joe-active' : ''}&gt;Center&lt;/NavLink&gt;
      &lt;/li&gt;
    &lt;/ul &gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// url 传参
const navigate = useNavigate()
navigate(`/detail?id=${id}`)

// 获取 url 参数
import { useSearchParams } from 'react-router-dom'
const [searchParams, setSearchParams] = useSearchParams()
// 获取参数
searchParams.get('id')
// 判断参数是否存在
searchParams.has('id')
// 同时页面内也可以用set方法来改变路由
setSearchParams({"id":2})</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// views/NowPlaying.js
import React, { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom';

export default function NowPlaying () {

  const navigate = useNavigate()
  const [list, setList] = useState([]);

  useEffect(() =&gt; {
    fetch('https://m.maizuo.com/gateway?cityId=110100&amp;pageNum=1&amp;pageSize=10&amp;type=1&amp;k=6492296', {
      headers: {
        'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"16717661461034803650494465"}',
        'X-Host': 'mall.film-ticket.film.list'
      }
    }).then(res =&gt; res.json()).then(res =&gt; {
      console.log(res.data.films)
      setList(res.data.films)
    })
  }, []);

  return (
    &lt;div&gt;
      NowPlaying
      &lt;ul&gt;
        {
          list.length === 0 ? '加载中...' :
            list.map(item =&gt; &lt;li key={item.filmId} onClick={() =&gt; {
              navigate(`/detail?id=${item.filmId}`)
            }}&gt;{item.name}&lt;/li&gt;)
        }
      &lt;/ul&gt;
    &lt;/div&gt;
  )
}



// views/Detail.js
import React from 'react'
import { useSearchParams } from 'react-router-dom'

export default function Detail (props) {

  const [searchParams, setSearchParams] = useSearchParams()

  return (
    &lt;div&gt;
      Detail --- {searchParams.get('id')}

      &lt;button onClick={() =&gt; {
        setSearchParams({ id: 1000 })
      }}&gt;猜你喜欢&lt;/button&gt;
    &lt;/div&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">5. 动态路由</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 跳转页面，路由传参
navigate(`/detail/${id}`)

// 配置动态路由
&lt;Route path="/detail/:id" element={&lt;Detail/&gt;}/&gt;

// 获取动态路由参数
const {id} = useParams()</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// views/NowPlaying.js
import React, { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom';

export default function NowPlaying () {

  const navigate = useNavigate()
  const [list, setList] = useState([]);

  useEffect(() =&gt; {
    fetch('https://m.maizuo.com/gateway?cityId=110100&amp;pageNum=1&amp;pageSize=10&amp;type=1&amp;k=6492296', {
      headers: {
        'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"16717661461034803650494465"}',
        'X-Host': 'mall.film-ticket.film.list'
      }
    }).then(res =&gt; res.json()).then(res =&gt; {
      console.log(res.data.films)
      setList(res.data.films)
    })
  }, []);

  return (
    &lt;div&gt;
      NowPlaying
      &lt;ul&gt;
        {
          list.length === 0 ? '加载中...' :
            list.map(item =&gt; &lt;li key={item.filmId} onClick={() =&gt; {
              navigate(`/detail/${item.filmId}`)
            }}&gt;{item.name}&lt;/li&gt;)
        }
      &lt;/ul&gt;
    &lt;/div&gt;
  )
}



// views/Detail.js
import React from 'react'
import { useParams, useNavigate } from 'react-router-dom'

export default function Detail (props) {

  const params = useParams()  
  const navigate = useNavigate()

  return (
    &lt;div&gt;
      Detail --- {params.id}

      &lt;button onClick={() =&gt; {
        navigate(`/detail/${1000}`)
      }}&gt;猜你喜欢&lt;/button&gt;
    &lt;/div&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">6. 路由拦截</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;Route path="/center" element={
  &lt;AuthComponent&gt;
    &lt;Center&gt;&lt;/Center&gt;
  &lt;/AuthComponent&gt;
}/&gt;

function AuthComponent({children}){
  return localStorage.getItem("token") ? children : &lt;Redirect to="/login" /&gt;
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React from 'react'
import { Route, Routes, Navigate } from 'react-router-dom'

import Film from '../views/Film'
import NowPlaying from '../views//films/NowPlaying'
import ComingSoon from '../views/films/ComingSoon'
import Cinema from '../views/Cinema'
import Center from '../views/Center'
import Detail from '../views/Detail'
import Search from '../views/Search'
import Login from '../views/Login'

import Redirect from '../components/Redirect'
import NotFound from '../views/NotFound'

export default function MyRouter () {
  return (
    &lt;div&gt;
      &lt;Routes&gt;

        &lt;Route path="/film" element={&lt;Film /&gt;}&gt;
          {/* 支持相对路径和绝对路径 */}
          &lt;Route path="" element={&lt;Navigate to='/film/nowplaying' /&gt;}&gt;&lt;/Route&gt;
          &lt;Route path="nowplaying" element={&lt;NowPlaying /&gt;}&gt;&lt;/Route&gt;
          &lt;Route path="comingsoon" element={&lt;ComingSoon /&gt;}&gt;&lt;/Route&gt;
        &lt;/Route&gt;
        &lt;Route path="/cinema" element={&lt;Cinema /&gt;}&gt;&lt;/Route&gt;
        &lt;Route path="/cinema/search" element={&lt;Search /&gt;}&gt;&lt;/Route&gt;
        &lt;Route path="/center" element={&lt;AuthComponent&gt;&lt;Center /&gt;&lt;/AuthComponent&gt;}&gt;&lt;/Route&gt;
        &lt;Route path="/detail/:id" element={&lt;Detail /&gt;}&gt;&lt;/Route&gt;
        &lt;Route path="/login" element={&lt;Login /&gt;}&gt;&lt;/Route&gt;
        &lt;Route path="/notfound" element={&lt;NotFound /&gt;}&gt;&lt;/Route&gt;

        &lt;Route path="/" element={&lt;Redirect to='/film' /&gt;}&gt;&lt;/Route&gt;
        &lt;Route path="*" element={&lt;Navigate to='/notfound' /&gt;}&gt;&lt;/Route&gt;
      &lt;/Routes&gt;
    &lt;/div&gt;
  )
}

// 路由拦截组件的封装
function AuthComponent ({ children }) {
  return localStorage.getItem("token") ? children : &lt;Redirect to="/login" /&gt;
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">7. 路由模式</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import {HashRouter} from 'react-router-dom'
import {BrowserRouter} from 'react-router-dom'</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { BrowserRouter } from 'react-router-dom'

import MyRouter from './router';
import TbaBar from './components/TbaBar';

function App () {
  return (
    &lt;BrowserRouter&gt;
      &lt;MyRouter&gt;&lt;/MyRouter&gt;

      &lt;TbaBar&gt;&lt;/TbaBar&gt;
    &lt;/BrowserRouter&gt;
  );
}

export default App;</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>如果是 <code>BrowserRouter</code> 模式后端要做资源处理，如果请求的路径是后端处理不了的，就重新渲染前端的 <code>index.htlm</code> 页面</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">8. <code>withRouter</code> / 类组件跳转方法</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import {
    useLocation,
    useNavigate,
    useParams
  } from "react-router-dom";

function withRouter(Component) {
  function ComponentWithRouterProp(props) {
    let location = useLocation();
    let push = useNavigate();
    let params = useParams();
    return (
      &lt;Component {...props} history={{ location, push, params }} /&gt;
    );
  }
  return ComponentWithRouterProp;
}

export default withRouter</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// components/withRouter.js
import React from 'react'
import { useLocation, useNavigate, useParams } from "react-router-dom";

export default function withRouter (Component) {
  return function (props) {
    let location = useLocation()
    let push = useNavigate()
    let match = useParams()
    return &lt;Component {...props} history={{ location, push, match }} /&gt;
  }
}



// views/films/FilmIem.js
import React, { Component } from 'react'
import withRouter from '../../components/withRouter'

class FilmItem extends Component {
  render () {
    return (
      &lt;li onClick={() =&gt; {
        this.props.history.push(`/detail/${this.props.filmId}`)
      }}&gt;
        {this.props.name}
      &lt;/li&gt;
    )
  }
}

export default withRouter(FilmItem)</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">9. 路由懒加载</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const LazyLoad = (path) =&gt; {
    const Comp = React.lazy(() =&gt; import(`../views/${path}`))
    return (
        &lt;React.Suspense fallback={&lt;&gt;加载中...&lt;/&gt;}&gt;
            &lt;Comp /&gt;
        &lt;/React.Suspense&gt;
    )
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export default function MRouter() {
    return (
        &lt;Routes&gt;
            {/* &lt;Route index element={&lt;Film/&gt;}/&gt; */}
            &lt;Route path="/film" element={LazyLoad("Film")}&gt;
                {/* &lt;Route index  element={&lt;Nowplaying/&gt;}/&gt; */}
                &lt;Route path="" element={&lt;Redirect to="/film/nowplaying"/&gt;}/&gt;
                &lt;Route path="nowplaying" element={LazyLoad("film/Nowplaying")}/&gt;
                &lt;Route path="comingsoon" element={LazyLoad("film/Comingsoon")}/&gt;
            &lt;/Route&gt;
            &lt;Route path="/cinema" element={LazyLoad("Cinema")}/&gt;
            &lt;Route path="/login" element={LazyLoad("Login")}/&gt;
            &lt;Route path="/center" element={&lt;AuthComponent&gt;
                {LazyLoad("Center")}
            &lt;/AuthComponent&gt;}/&gt;
            &lt;Route path="/detail/:id" element={LazyLoad("Detail")}/&gt;
            &lt;Route path="/" element={&lt;Redirect to="/film"/&gt;}/&gt;
            &lt;Route path="*" element={LazyLoad("NotFound")}/&gt;
        &lt;/Routes&gt;
    )
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// router/index.js
import React from 'react'
import { Route, Routes, Navigate } from 'react-router-dom'

import Redirect from '../components/Redirect'

export default function MyRouter () {
  return (
    &lt;div&gt;
      &lt;Routes&gt;
        &lt;Route path="/film" element={lazyLoad('Film')}&gt;
          {/* 支持相对路径和绝对路径 */}
          &lt;Route path="" element={&lt;Navigate to='/film/nowplaying' /&gt;}&gt;&lt;/Route&gt;
          &lt;Route path="nowplaying" element={lazyLoad('films/NowPlaying')}&gt;&lt;/Route&gt;
          &lt;Route path="comingsoon" element={lazyLoad('films/ComingSoon')}&gt;&lt;/Route&gt;
        &lt;/Route&gt;
        &lt;Route path="/cinema" element={lazyLoad('Cinema')}&gt;&lt;/Route&gt;
        &lt;Route path="/cinema/search" element={lazyLoad('Search')}&gt;&lt;/Route&gt;
        &lt;Route path="/center" element={&lt;AuthComponent&gt;{lazyLoad('Center')}&lt;/AuthComponent&gt;}&gt;&lt;/Route&gt;
        {/* 动态路由 */}
        &lt;Route path="/detail/:id" element={lazyLoad('Detail')}&gt;&lt;/Route&gt;
        &lt;Route path="/login" element={lazyLoad('Login')}&gt;&lt;/Route&gt;
        &lt;Route path="/notfound" element={lazyLoad('NotFound')}&gt;&lt;/Route&gt;

        &lt;Route path="/" element={&lt;Redirect to='/film' /&gt;}&gt;&lt;/Route&gt;
        &lt;Route path="*" element={&lt;Navigate to='/notfound' /&gt;}&gt;&lt;/Route&gt;
      &lt;/Routes&gt;
    &lt;/div&gt;
  )
}

// 路由拦截组件的封装
function AuthComponent ({ children }) {
  return localStorage.getItem("token") ? children : &lt;Redirect to="/login" /&gt;
}

// 路由懒加载的封装
function lazyLoad (path) {
  const Com = React.lazy(() =&gt; import(`../views/${path}`))
  return (
    &lt;React.Suspense fallback={&lt;&gt;加载中...&lt;/&gt;}&gt;
      &lt;Com /&gt;
    &lt;/React.Suspense&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">10. <code>useRoutes</code> 钩子配置路由（路由表）</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// router/index.js
import React from 'react'
import { useRoutes, Navigate } from 'react-router-dom'

import Redirect from '../components/Redirect'

export default function MyRouter () {
  const element = useRoutes([
    {
      path: '/film', element: lazyLoad('Film'), children: [
        { path: '', element: &lt;Navigate to='/film/nowplaying' /&gt; },
        { path: 'nowplaying', element: lazyLoad('films/NowPlaying') },
        { path: 'comingsoon', element: lazyLoad('films/ComingSoon') },
      ]
    },
    { path: '/cinema', element: lazyLoad('Cinema') },
    { path: '/center', element: &lt;AuthComponent&gt;{lazyLoad('Center')}&lt;/AuthComponent&gt; },
    { path: '/detail/:id', element: lazyLoad('Detail') },
    { path: '/login', element: lazyLoad('Login') },
    { path: '/notfound', element: lazyLoad('NotFound') },

    { path: '/', element: &lt;Redirect to='/film' /&gt; },
    { path: '*', element: &lt;Navigate to='/notfound' /&gt; },
  ])
  return element
}

// 路由拦截组件的封装
function AuthComponent ({ children }) {
  return localStorage.getItem("token") ? children : &lt;Redirect to="/login" /&gt;
}

// 路由懒加载的封装
function lazyLoad (path) {
  const Com = React.lazy(() =&gt; import(`../views/${path}`))
  return (
    &lt;React.Suspense fallback={&lt;&gt;加载中...&lt;/&gt;}&gt;
      &lt;Com /&gt;
    &lt;/React.Suspense&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export default function MRouter() {
    const element = useRoutes([
        {path:"/film",element:LazyLoad("Film"),children:[
            {
                path:"",
                element:&lt;Redirect to="/film/nowplaying"/&gt;
            },
            {
                path:"nowplaying",
                element:LazyLoad("film/Nowplaying")
            },
            {
                path:"comingsoon",
                element:LazyLoad("film/Comingsoon")
            }
        ]},
        {
            path:"/cinema",element:LazyLoad("Cinema")
        },
        {
            path:"/login",element:LazyLoad("Login")
        },
        {
            path:"/center",element:&lt;AuthComponent&gt;
            {LazyLoad("Center")}
            &lt;/AuthComponent&gt;
        },
        {
            path:"/detail/:id",element:LazyLoad("Detail")
        },
        {
            path:"/",element:&lt;Redirect to="/film"/&gt;
        },
        {
            path:"*",element:LazyLoad("NotFound")
        },
    ])

    return element
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// src/App.js
import { BrowserRouter } from 'react-router-dom'

import MyRouter from './router';

function App () {
  return (
    &lt;BrowserRouter&gt;
      &lt;MyRouter&gt;&lt;/MyRouter&gt;
    &lt;/BrowserRouter&gt;
  );
}

export default App;</code></pre>
<!-- /wp:code -->]]></description><guid isPermaLink="false">/archives/react-route-v6</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Tue, 21 May 2024 06:45:09 GMT</pubDate></item><item><title><![CDATA[React17-全家桶（3）]]></title><link>https://blog.qiaofugui.cn/archives/react17-%E5%85%A8%E5%AE%B6%E6%A1%B6%EF%BC%883%EF%BC%89</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=React17-%E5%85%A8%E5%AE%B6%E6%A1%B6%EF%BC%883%EF%BC%89&amp;url=/archives/react17-%E5%85%A8%E5%AE%B6%E6%A1%B6%EF%BC%883%EF%BC%89" width="1" height="1" alt="" style="opacity:0;"><!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">十五、Mobx</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><a href="https://cn.mobx.js.org">https://cn.mobx.js.org</a></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><code>npm i mobx@5</code></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">1. Mobx 介绍</h2>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>Mobx 是一个功能强大，上手非常容易的状态管理工具</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>Mobx 背后的哲学很简单: 任何源自应用状态的东西都应该自动地获得</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>Mobx 利用 getter 和 setter 来收集组件的数据依赖关系，从而在数据发生变化的时候精确知道哪些组件需要重绘，在界面的规模变大的时候，往往会有很多细粒度更新</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:paragraph -->
<p><strong>（vue类似）</strong></p>
<!-- /wp:paragraph -->
<!-- wp:image {"id":284,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong11-1024x352.png&amp;size=m" alt="" class="wp-image-284">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">2. Mobx 与 redux 的区别</h2>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>Mobx 写法上更偏向于 OOP</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>对一份数据直接进行修改操作，不需要始终返回一个新的数据</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>并非单一 store，可以多 store</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>Redux 默认以 JavaScript 原生对象形式存储数据，而 Mobx 使用<strong>可观察对象</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>优点：</p>
 <!-- /wp:paragraph -->
 <!-- wp:list -->
 <ul>
  <!-- wp:list-item -->
  <li>学习成本小</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>面向对象编程，而且对 TS 友好</li>
  <!-- /wp:list-item -->
 </ul>
 <!-- /wp:list -->
 <!-- wp:paragraph -->
 <p>缺点：</p>
 <!-- /wp:paragraph -->
 <!-- wp:list -->
 <ul>
  <!-- wp:list-item -->
  <li>过于自由：Mobx 提供的约定及模版代码很少，代码编写很自由，如果不做一些约定，比较容易导致团队代码风格不统一</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>相关的中间件很少，逻辑层业务整合是问题</li>
  <!-- /wp:list-item -->
 </ul>
 <!-- /wp:list -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">3. 支持装饰器</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>npm i @babel/core @babel/plugin-proposal-decorators @babel/preset-env</code></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">创建 <code>.babelrc</code> 文件</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>{
  "presets": [
    "@babel/preset-env"
  ],
  "plugins": [
    [
      "@babel/plugin-proposal-decorators",
      {
        "legacy": true
      }
    ]
  ]
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">创建 <code>config-overrides.js</code> 文件</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const path = require('path')
const { override, addDecoratorsLegacy } = require('customize-cra')

function resolve(dir) {
  return path.join(__dirname, dir)
}

const customize = () =&gt; (config, env) =&gt; {
  config.resolve.alias['@'] = resolve('src')
  if (env === 'production') {
    config.externals = {
      'react': 'React',
      'react-dom': 'ReactDOM'
    }
  }
  return config
};

module.exports = override(addDecoratorsLegacy(), customize())</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">安装依赖</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>npm i customize-cra react-app-rewired</code></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">修改 <code>package.json</code> 文件</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>...
"scripts": {
  "start": "react-app-rewired start",
  "build": "react-app-rewired build",
  "test": "react-app-rewired test",
  "eject": "react-app-rewired eject"
},
...</code></pre>
<!-- /wp:code -->
<!-- wp:image {"id":285,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong12.png&amp;size=m" alt="" class="wp-image-285">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">4. Mobx 的使用</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">（1）observable 和 autorun</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { observable, autorun } from 'mobx';

const value = observable.box(0);
const number = observable.box(100);
autorun(() =&gt; {
  console.log(value.get());
});
value.set(1);
value.set(2);
number.set(101);
// 0,1,2。 // autorun 使用到才能被执行
// 只能是同步，异步需要处理

// 观察对象，通过 map
const map = observable.map({ key: "value"});
// map.set("key", "new value");
// map.get("key")

// 观察对象，不通过 map
const map = observable({ key: "value"});
// map.key map.key="xiaoming"

//观察数组
const list = observable([1, 2, 4]);
list[2] = 3;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(2) action，runInAction 和严格模式</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import {observable, action, configure,runInAction} from 'mobx';
configure({enforceActions:'always'})
// 严格模式， 必须写 action,
// 如果是 never，可以不写 action,
// 最好设置 always, 防止任意地方修改值， 降低不确定性。
class Store {
  @observable number = 0;
  @observable name = "Joe";
  @action add = () =&gt; {
    this.number++;
  } // action只能影响正在运行的函数，而无法影响当前函数调用的异步操作
  @action load = async () =&gt; {
    const data = await getData();
    runInAction(() =&gt; {
      this.name = data.name;
    });
  } // runInAction 解决异步问题
}
const newStore = new Store();
newStore.add();

// 如果在组件监听
componentDidMount() {
  this.unsubscribe = autorun(()=&gt;{
    console.log(newStore.number);
  })
}
// 取消监听
componentWillUnmount() {
  this.unsubscribe()
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">5. mobx-react 的使用</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>npm i mobx-react@5</code> or <code>yarn add mobx-react@5</code></p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>react 组件里使用 @observer、observer 函数/装饰器可以用来将 React 组件转变成响应式组件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>可观察的局部组件状态 @observable 装饰器在 React 组件上引入可观察属性。而不需要通过 React 的冗长和强制性的 setState 机制来管理</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import {observer} from "mobx-react"
import {observable} from "mobx"

@observer class Timer extends React.Component {
  @observable secondsPassed = 0

  componentWillMount() {
    setInterval(() =&gt; {
      this.secondsPassed++
    }, 1000)
  } // 如果是严格模式需要加上 @action 和 runInAction

  // 一个新的生命周期钩子函数 componentWillReact
  // 当组件因为它观察的数据发生了改变，它会安排重新渲染，
  // 这个时候 componentWillReact 会被触发

  componentWillReact() {
    console.log("I will re-render, since the todo has changed!");
  }
  render() {
    return (&lt;span&gt;Seconds passed: { this.secondsPassed } &lt;/span&gt; )
  }
}
ReactDOM.render(&lt;Timer /&gt;, document.body)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">（3）Provider 组件</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>它使用了 React 的上下文（context）机制，可以用来向下传递 stores。 要连接到这些 stores，需要传递一个 stores 名称的列表给 inject，这使得 stores 可以作为组件的 props 使用、this.props</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// mobx/store.js
class Store {
  @observable number = 0;
  @action add = () =&gt; {
    this.number++;
  }
}
export default new Store() // 导出 Store 实例


import { inject, observer } from "mobx-react"
  @inject("store")
  @observer // 需要转换为响应式组件
  class Child extends Component{
    render(){
      return (
        &lt;div&gt;
          Child --{this.props.store.number}
        &lt;/div&gt;
      )
    }
  }

  @inject("store")
  class Middle extends Component{
    render(){
      return (
        &lt;div&gt;
          Middle-&lt;button onClick={()=&gt;{
            this.props.store.add();
          }}&gt;test&lt;/button&gt;
          &lt;Child/&gt;
        &lt;/div&gt;
      )
    }
  }

  // 通过 provider 传 store 进去
  &lt;Provider store={store}&gt;
    &lt;Middle/&gt;
  &lt;/Provider&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 函数式组件 
// 方法一
import React from 'react'
import { Observer } from 'mobx-react'
import store from '../mobx/store'

export default function Cinemas (props) {

  return (
    &lt;div&gt;
      &lt;Observer&gt;
        {() =&gt; {
          return (
            store.list.map(item =&gt;
              &lt;dl key={item.cinemaId}&gt;
                &lt;dt&gt;{item.name}&lt;/dt&gt;
                &lt;dd&gt;{item.address}&lt;/dd&gt;
              &lt;/dl&gt;
            )
          )
        }}
      &lt;/Observer&gt;
    &lt;/div&gt;
  )
}



// 方法二
// 使用 mobx 的 observable
import React from 'react'
import { observable } from 'mobx'
import store from '../mobx/store'

function Cinemas (props) {

  return (
    &lt;div&gt;
      {store.list.map(item =&gt;
        &lt;dl key={item.cinemaId}&gt;
          &lt;dt&gt;{item.name}&lt;/dt&gt;
          &lt;dd&gt;{item.address}&lt;/dd&gt;
        &lt;/dl&gt;
      )}
    &lt;/div&gt;
  )
}
export default observable(Cinemas)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">十六、TS</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">1. typescript</h2>
<!-- /wp:heading -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>(<a href="https://zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/">手册 · TypeScript Handbook（中文版） (gitbooks.io)</a>)</p>
 <!-- /wp:paragraph -->
 <!-- wp:list {"ordered":true} -->
 <ol>
  <!-- wp:list-item -->
  <li>TypeScript 的定位是静态类型语言，在写代码阶段就能检查错误，而非运行阶段</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>类型系统是最好的文档，增加了代码的可读性和可维护性</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>有一定的学习成本，需要理解接口（Interfaces）、泛型（Generics）、类（Classes）等</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>ts 最后被编译成 js</li>
  <!-- /wp:list-item -->
 </ol>
 <!-- /wp:list -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">2. 创建 react+ts 项目</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>create-react-app my-app --template typescript</code>
 <br>
 提示以下内容说明 <code>create-react-app</code> 版本低
 <br>
 <img class="wp-image-286" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong13.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">3. 声明</h2>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>可以在当前文件加上 <code>declare const $: any;</code></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>安装 <code>npm i @types/jquery</code> @types 是 npm 的一个分支，用来存放 <code>*.d.ts</code> 文件</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>npm i --save react-router-dom
# 编译器需要通过这个声明文件，进行类型检查工作
npm i --save @types/react-router-dom </code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">4. 变量声明</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// String(原生的构造函数) vs string (ts 中的类型)
var myname:string = "字符"
var mybool:boolean = false
var mynumber:number = 100
var mylist:Array&lt;string&gt; = ["111","222","3333"]
var myname2:string | number | boolean = 100
var myname3:string | number = "Joe"
var mylist2:Array&lt;string| number&gt; = [1,2,"Joe"]
var mylist3:(string| number)[] = [1,2,"Joe"]</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">5. 定义普通函数</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">接口描述形状</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>interface SearchFunc {
  (source: string, subString: string): boolean;
}
// 对于函数类型的类型检查来说，函数的参数名不需要与接口里定义的名字相匹配
let mySearch: SearchFunc;
mySearch = function(src: string, sub: string): boolean {
  let result = src.search(sub);
  return result &gt; -1;
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">传参</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>function Test(list:String[],text?:String,...args:String[]):void{
   console.log(list,text,args)
}

Test(["1111","2222"])
// list:["1111","2222"] text: undefined args: []

Test(["0","1"],"a","b","c")
// list:["0","1"] text: "a" args: ["b","c"]</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">类型断言 <code>as</code></h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>function Test( mytext:string|number ){
console.log((mytext as string).length) // 对
console.log((mytext as any).length) // 对
console.log((mytext as string[]).length) // 错，原声明没有这个类型，无法断言
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">6. 定义普通类</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>interface MyInter {
  name:String, // 必选属性
  readonly country:String, //只读属性
  getName():void // 定义方法
}

class MyObj implements MyInter{
  name="Joe"
  country="China"
  private age = 100 // 私有属性， 不能在接口定义
  getName(){
    // ...
  }
  private getAge(){
    // ...
  } // 私有方法， 不能在接口定义
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">7. 定义类组件</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>interface PropInter {
  name: string | number;
  firstName?: string; // 可选属性
  lastName?: string; // 可选属性
  // [propName: string]: any 任意属性
}
interface StateInter {
  count: number
}

// 根组件 ，第一个参数可以传 any
class HelloClass extends React.Component&lt;PropInter, StateInter&gt; {
  state: State = {
    count: 0,
  }; // setState 时候也才会检查
  static defaultProps = { // 属性默认值
    name: "default name"
    firstName: "",
    lastName: "",
  };
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">8. 定义函数式组件</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 根组件
const App:React.FC = (props)=&gt;{
  console.log(props)
  const [name, setname] = useState&lt;string&gt;("Joe")

  return (
    &lt;div&gt;
      app
    &lt;/div&gt;
  )
}

// 子组件接受属性 -1
interface iprops {
  count:number
}
const Child:React.FC&lt;iprops&gt; = (props)=&gt;{
  return (
    &lt;div&gt;
      child-{props.count}
    &lt;/div&gt;
  )
}

// 子组件接受属性 -2
const Child = (props:iprops)=&gt;{
  return (
    &lt;div&gt;
      child-{props.count}
    &lt;/div&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">useRef</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const mytext = useRef&lt;HTMLInputElement&gt;(null)

&lt;input type="text" ref={mytext}/&gt;

useEffect(() =&gt; {
  console.log(mytext.current &amp;&amp; mytext.current.value)
}, [])</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">useContext</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>interface IContext{
  call:string
}

const GlobalContext = React.createContext&lt;IContext&gt;({
  call:"" // 定义初始值，按照接口规则
})

&lt;GlobalContext.Provider value={{
call:"电话"
}}&gt;
  // ...
&lt;/GlobalContext.Provider&gt;

const {call} = useContext(GlobalContext)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">useReducer</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>interface IPrevState{
  count:number
}
interface IAction{
  type:string,
  payload:any
}

function reducer (prevState:IPrevState,action:IAction){
  // ...
  return prevState
}

const [state,dispatch]= useReducer(reducer,{
  count:1
})

dispatch({
  type:"Action1",
  payload:[]
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">9. 父子通信</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 父组件调用
&lt;Child key={index} item={item} index={index} cb={(index)=&gt;{
  var newlist= [...list]
  newlist.splice(index,1)
  setList(newlist)
}}/&gt;

// 子组件
interface ItemType{
  item:string,
  index:number, // 定义接口
  cb:(param:number)=&gt;void // 定义接口
}

const Child = (props:ItemType)=&gt;{
  let {index,item,cb} = props
  return (
    &lt;div &gt;{item}
      &lt;button onClick={()=&gt;cb(index)}&gt;del-{index}&lt;/button&gt;
    &lt;/div&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">10. 路由</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>npm i react-router-dom@5</code> / <code>npm i --save-dev @types/react-router-dom</code></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">编程式导航</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 使用编程式导航，需要引入接口配置
import { RouteComponentProps } from "react-router-dom";

interface IProps {自己定义的接口}

type HomeProps = IProps &amp; RouteComponentProps; // 两个接口属性都支持

interface IState {}

class Home extends React.Component&lt;HomeProps, IState&gt; {
  private handleSubmit = async () =&gt; {
    // code for API calls
    this.props.history.push("/home");
  };

  public render(): any {
    return &lt;div&gt;Hello&lt;/div&gt;;
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">动态路由</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>interface IParams{
  id:string
}

// RouteComponentProps 是一个泛型接口
class Detail extends Component&lt;RouteComponentProps&lt;IParams&gt;, any&gt;{
  componentDidMount() {
    console.log(this.props.match.params.id)
  }

  render(){
    return (
      &lt;div&gt;
        detail
      &lt;/div&gt;
    )
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">11. redux</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import {createStore} from 'redux'

interface ActionInter{
  type:string,
  payload:any
}
interface IState {
  isShow: boolean
}

const reducer = (prevState: IState = {
  isShow: true
}, action: ActionInter)=&gt;{
  const { type } = action
  const newState = { ...prevState }
  switch (type) {
    case 'show':
      newState.isShow = true
      return newState
    case 'hide':
      newState.isShow = false
      return prevState
    default:
      return prevState
  }
}

const store = createStore(reducer, /* enhancer */)
export default store</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">十七、styled-components</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>npm i styled-components</code> or <code>yarn add styled-components</code>
 <br>
 <em>它是通过 JavaScript 改变 CSS 编写方式的解决方案之一，从根本上解决常规 CSS 编写的一些弊端。通过 JavaScript 来为 CSS 赋能，我们能达到常规 CSS 所不好处理的逻辑复杂、函数方法、复用、避免干扰。样式书写将直接依附在 JSX 上面，HTML、CSS、JS 三者再次内聚。<strong>all in js</strong>的思想</em></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">基本</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component } from 'react'
import styled from 'styled-components'

export default function App () {

  const StyledFooter = styled.footer`
    position:fixed;
      bottom:0;
      width:100%;
      background: pink;
      line-height:30px;

      ul{
        display:flex;
        margin:0;
        padding:0;
        list-style:none;
        justify-content:space-evenly;

        li:hover{
          // PC 测试
          background:skyblue;
        }
      }
  `
  return (
    &lt;div&gt;
      函数组件
      &lt;StyledFooter&gt;
        &lt;ul&gt;
          &lt;li&gt;首页&lt;/li&gt;
          &lt;li&gt;列表&lt;/li&gt;
          &lt;li&gt;我的&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/StyledFooter&gt;
    &lt;/div&gt;
  )
}



export default class App extends Component {

  render () {
    const StyledFooter = styled.footer`
      position:fixed;
      bottom:0;
      width:100%;
      background: pink;
      line-height:30px;

      ul{
        display:flex;
        margin:0;
        padding:0;
        list-style:none;
        justify-content:space-evenly;

        li{
          &amp;:hover {
            // PC 测试
            background:skyblue;
          }
        }
      }
    `
    return (
      &lt;div&gt;
        类组件
        &lt;StyledFooter&gt;
          &lt;ul&gt;
            &lt;li&gt;首页&lt;/li&gt;
            &lt;li&gt;列表&lt;/li&gt;
            &lt;li&gt;我的&lt;/li&gt;
          &lt;/ul&gt;
        &lt;/StyledFooter&gt;
      &lt;/div&gt;
    )
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">透传 props</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component } from 'react'
import styled from 'styled-components'

export default class App extends Component {

  render () {
    const StyledInput = styled.input`
      outline:none;
      border-bottom:1px solid red;
      border-radius:10px;
    `
    return (
      &lt;div&gt;
        &lt;StyledInput type='text' placeholder='输入' /&gt;
      &lt;/div&gt;
    )
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">基于 props 做样式判断</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component } from 'react'
import styled from 'styled-components'

export default class App extends Component {

  render () {
    const StyledDiv = styled.div`
      width:100px;
      height:100px;
      background:${props =&gt; props.bg || 'hotpink'};
    `
    return (
      &lt;div&gt;
        &lt;StyledDiv bg="blue"&gt;&lt;/StyledDiv&gt;
        &lt;StyledDiv&gt;&lt;/StyledDiv&gt;
      &lt;/div&gt;
    )
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">样式化任意组件(一定要写 className )</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component } from 'react'
import styled from 'styled-components'

export default class App extends Component {

  render () {
    const StyledChild = styled(Child)`
      background:pink;
    `
    return (
      &lt;div&gt;
        App
        &lt;StyledChild&gt;&lt;/StyledChild&gt;
      &lt;/div&gt;
    )
  }
}

const Child = (props) =&gt; &lt;div className={props.className}&gt;Child&lt;/div&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>原理：高阶函数/组件</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">扩展样式</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component } from 'react'
import styled from 'styled-components'

export default class App extends Component {

  render () {
    const StyledButton1 = styled.button`
      width:100px;
      height:100px;
      background:skyblue;
    `
    const StyledButton2 = styled(StyledButton1)`
      background:red;
    `
    const StyledButton3 = styled(StyledButton1)`
      background:blue;
    `
    return (
      &lt;div&gt;
        App
        &lt;StyledButton1&gt;按钮1&lt;/StyledButton1&gt;
        &lt;StyledButton2&gt;按钮2&lt;/StyledButton2&gt;
        &lt;StyledButton3&gt;按钮3&lt;/StyledButton3&gt;
      &lt;/div&gt;
    )
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">加动画</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component } from 'react'
import styled, { keyframes } from 'styled-components'

export default class App extends Component {

  render () {
    const rotate360 = keyframes`
      from {
        transform:rotate(0deg);
      }
      to {
        transform:rotate(360deg);
      }
    `
    const StyledDiv = styled.div`
      width:100px;
      height:100px;
      background:red;
      animation: ${rotate360} 3s linear infinite;
    `
    return (
      &lt;div&gt;
        App
        &lt;StyledDiv&gt;&lt;/StyledDiv&gt;
      &lt;/div&gt;
    )
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">十八、单元测试</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">react-test-renderer</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>npm i react-test-renderer</code> or <code>yarn add react-test-renderer</code>
 <br>
 <strong>测试文件要以 <code>.test.js</code> 结尾</strong></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// ./App.js 要测试的文件
import React, { Component } from 'react'

export default class App extends Component {

  state = {
    txt: '',
    list: ['111', '222', '333']
  }

  render () {
    return (
      &lt;div&gt;
        &lt;h1&gt;Joe-TodoList&lt;/h1&gt;
        &lt;input type='text' value={this.state.txt} onChange={(e) =&gt; {
          this.setState({
            txt: e.target.value
          })
        }} /&gt; &lt;button className='add' onClick={() =&gt; {
          this.setState({
            list: [...this.state.list, this.state.txt],
            txt: ''
          })
        }}&gt;add&lt;/button&gt;
        &lt;ul&gt;
          {this.state.list.map((item, index) =&gt; (
            &lt;li key={index}&gt;
              {item}&amp;nbsp;
              &lt;button className='del' onClick={() =&gt; {
                let newList = [...this.state.list]
                newList.splice(index, 1)
                this.setState({
                  list: newList
                })
              }}&gt;del&lt;/button&gt;
            &lt;/li&gt;
          ))}
        &lt;/ul&gt;
      &lt;/div&gt;
    )
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// ./test/react-test.render.test.js 文件
import ShallowRenderer from 'react-test-renderer/shallow'
import ReactTestUtil from 'react-dom/test-utils'

import App from '../App'

describe('react-test-render', function () {
  it('App 的名字是 Joe-TodoList', function () {
    // 渲染到虚拟 DOM
    const render = new ShallowRenderer()
    render.render(&lt;App /&gt;)
    // console.log(render.getRenderOutput().props.children[0].type)

    // 断言这个值是 h1
    expect(render.getRenderOutput().props.children[0].type).toBe('h1')
    expect(render.getRenderOutput().props.children[0].props.children).toBe('Joe-TodoList')
  })

  it('删除功能', function () {
    // 渲染到真实 DOM
    const app = ReactTestUtil.renderIntoDocument(&lt;App /&gt;)
    // 查找 li 标签
    let todoItems = ReactTestUtil.scryRenderedDOMComponentsWithTag(app, 'li')
    console.log(todoItems.length)

    // 获取删除按钮
    let delButton = todoItems[1].querySelector('button')
    // 模拟点击
    ReactTestUtil.Simulate.click(delButton)

    // 获取删除后的 li 标签
    let todoItemsAfterClick = ReactTestUtil.scryRenderedDOMComponentsWithTag(app, 'li')

    // 判断是否删除，长度是否减 1
    expect(todoItems.length - 1).toBe(todoItemsAfterClick.length)
  })

  it('添加功能', function () {
    // 渲染到真实 DOM
    const app = ReactTestUtil.renderIntoDocument(&lt;App /&gt;)
    // 获取 li 标签
    let todoItems = ReactTestUtil.scryRenderedDOMComponentsWithTag(app, 'li')

    // 获取 input输入框 标签
    let addInput = ReactTestUtil.scryRenderedDOMComponentsWithTag(app, 'input')
    // 输入框放入 value
    addInput.value = 'Joe'

    // 获取添加按钮 拿到的是数组
    // let addButton = ReactTestUtil.scryRenderedDOMComponentsWithClass(app, 'add')
    // 拿出一个 返回多个会报错
    let addButton = ReactTestUtil.findRenderedDOMComponentWithClass(app, 'add')

    // 模拟点击
    ReactTestUtil.Simulate.click(addButton)

    // 获取添加后的 li 标签
    let todoItemsAfterClick = ReactTestUtil.scryRenderedDOMComponentsWithTag(app, 'li')

    // 判断是否添加，长度是否加 1
    expect(todoItemsAfterClick.length).toBe(todoItems.length + 1)
  })
})</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p><code>npm run test</code> 执行测试</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">enzyme</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>npm i enzyme</code> or <code>yarn add enzyme</code></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>安装适配器 <code>npm i enzyme-adapter-react-版本</code> or <code>yarn add enzyme-adapter-react-版本</code>
 <br>
 <code>@wojtekmaj/enzyme-adapter-react-17</code> / <code>@cfaester/enzyme-adapter-react-18</code></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// ./test/enzyme.test.js 文件
import Enzyme, { shallow, mount } from 'enzyme' // 需要适配器enzyme-adapter-react-版本
import adapter from '@cfaester/enzyme-adapter-react-18'

import App from '../App'

Enzyme.configure({ adapter: new adapter() })
describe('react-test-render', function () {
  it('App 的名字是 Joe-TodoList', function () {
    // 渲染到虚拟 DOM
    let app = shallow(&lt;App /&gt;)

    // 查找 app 组件的 h1 标签里面的内容是不是 Joe-TodoList
    expect(app.find('h1').text()).toEqual('Joe-TodoList')
  })

  it('删除功能', function () {
    // 渲染到真实 DOM
    let app = mount(&lt;App /&gt;)
    // 获取 app 组件里面所有的 li 标签
    let todoLength = app.find('li').length
    // 查找 app 组件里面的 button 标签类名是 del 的第 0 个，模拟点击
    app.find('button.del').at(0).simulate('click')

    // 期望 app 组件里的 li 标签 删掉了一个
    expect(app.find('li').length).toEqual(todoLength - 1)
  })

  it('添加功能', function () {
    // 渲染到真实 DOM
    let app = mount(&lt;App /&gt;)
    // 获取 app 组件里面所有的 li 标签
    let todoLength = app.find('li').length
    // 获取 app 组件里的 input 标签
    let addInput = app.find('input')
    // 设置 input 标签的 value 值
    addInput.value = 'Joe'
    // 获取
    // 查找 app 组件里面的 .add 类名的标签，模拟点击
    app.find('.add').simulate('click')

    // 期望 app 组件里的 li 标签 增加了一个
    expect(app.find('li').length).toEqual(todoLength + 1)
  })
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">挂载组件</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import Enzyme,{mount} from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17'
// 在使用 Enzyme 前需要先适配 React 对应的版本

Enzyme.configure({ adapter: new Adapter() })

it('挂载拿到状态', () =&gt; {
  const app = mount(&lt;App /&gt;);
  expect(app.state().name).toEqual('Joe');
  expect(app.state().age).toEqual(100);
})

/*
.text()：返回当前组件的文本内容
.html()：返回当前组件的 HTML 代码形式
.props()：返回根组件的所有属性
.prop(key)：返回根组件的指定属性
.state([key])：返回根组件的状态
.setState(nextState)：设置根组件的状态
.setProps(nextProps)：设置根组件的属性
*/</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">测试组件渲染出来的 HTML</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>it('组件渲染出来的 HTML', () =&gt; {
  const app = mount(&lt;App /&gt;);
  expect(app.find('#myid').text()).toEqual('Joe');
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">模拟用户交互</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>it('模拟用户交互', () =&gt; {
  const app = mount(&lt;App /&gt;);
  app.find('#mybtn').simulate('click')
  expect(app.state().name).toEqual('xiaoming');
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">十九、redux-saga</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>redux-saga 它是来处理异步流程的</strong>
 <br>
 <code>npm i redux-saga</code> or <code>yarn add redux-saga</code>
 <br>
 <em>在 saga 中，全局监听器和接收器使用 Generator 函数和 saga 自身的一些辅助函数实现对整个流程的管控</em>
 <br>
 <img class="wp-image-278" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong14.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">代码实现</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":279,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong15.png&amp;size=m" alt="" class="wp-image-279">
</figure>
<!-- /wp:image -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// index.js
import {createStore,applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga/core';
import {reducer} from './reducer'
import mySagas from './saga'
const sagaMiddleware = createSagaMiddleware(); // 创建中间件
const store = createStore(reducer,{list:[]},applyMiddleware(sagaMiddleware))

// 注意运行的时机是在 store 创建好了之后
sagaMiddleware.run(mySagas);

export default store</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// saga.js
import {takeEvery,put} from 'redux-saga/effects'
import {changeList} from './action'
function *mySagas(){
  // 监听 GET_LIST
  // 在每个 `监听 GET_LIST` action 被 dispatch 时调用 getList
  yield takeEvery("GET_LIST", getList);
  // yield takeEvery("DELETE_LIST", deleteList);
}
function *getList(){
  // 异步处理
  let res = yield new Promise(resolve=&gt;{
    setTimeout(()=&gt;{
      resolve(["1111","2222","3333"])
    },2000)
  })
  yield put(changeList(res)) // 发出新的 action
}

export default mySagas</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// action.js
export const changeList = (value)=&gt;{
  return {
    type:"CHANGE_LIST",
    payload:value
  }
}

export const getSaAction = ()=&gt;{
  // GET_LIST 被 saga 监听
  return {
    type:"GET_LIST"
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// reducer.js
export const reducer = (prevState,action)=&gt;{
  let {type,payload} = action;
  switch(type){
    case "CHANGE_LIST":
      let newstate = {...prevState}
      newstate.list = [...newstate.list,...payload]
      return newstate
  default :
    return prevState
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// App.js
class App extends Component {
  componentDidMount() {
    store.subscribe(()=&gt;{
      console.log(store.getState())
    })
  }

  handleClick = ()=&gt;{
    store.dispatch(getSaAction())
  }

  render() {
    return (
      &lt;div &gt;
        &lt;button onClick={this.handleClick}&gt;获取异步&lt;/button&gt;
      &lt;/div&gt;
    )
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">二十、React 补充</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">1. Portal</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Portals 提供了一个最好的在父组件包含的DOM结构层级外的DOM节点渲染组件的方法。</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { createPortal } from 'react-dom'
ReactDOM.createPortal(child,container);</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>第一个参数 child 是可渲染的 react 子项，比如元素，字符串或者片段等。第二个参数 container 是一个 DOM 元素。</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component } from 'react'
import { createPortal } from 'react-dom'

export default class PortalDialog extends Component {
  render () {
    return (
      createPortal(
        &lt;div style={{
          width: '100%',
          height: '100%',
          position: 'fixed',
          top: '0',
          left: '0',
          backgroundColor: 'rgba(0,0,0,.3)',
          zIndex: '99999'
        }}&gt;
          PortalDialog
          &lt;div&gt;loading...&lt;/div&gt;
          {this.props.children}
          &lt;button onClick={this.props.close}&gt;close&lt;/button&gt;
        &lt;/div&gt;
        ,
        document.body
      )
    )
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(1) 用法</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>普通的组件，子组件的元素将挂载到父组件的 DOM 节点中。</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>render() {
  // React 挂载一个 div 节点，并将子元素渲染在节点中
  return (
    &lt;div&gt;
      {this.props.children}
    &lt;/div&gt;
  );
}</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>有时需要将元素渲染到 DOM 中的不同位置上去，这是就用到的 portal 的方法。</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>render(){
  // 此时 React 不再创建 div 节点，而是将子元素渲染到 dom 节点上。domNode，是一个有效的任意位置的 dom 节点。
  return ReactDOM.createPortal(
    this.props.children,
    domNode
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>一个典型的用法就是当父组件的 dom 元素有 overflow:hidden 或者 z-inde 样式，而你又需要显示的子元素超出父元素的盒子。举例来说，如对话框，悬浮框，和小提示。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(2) 在 protal 中的事件冒泡</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>虽然通过 portal 渲染的元素在父组件的盒子之外，但是渲染的 dom 节点仍在 React 的元素树上，在那个 dom 元素上的点击事件仍然能 dom 树中监听到。</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component } from 'react';
import ReactDOM from 'react-dom';

const getDiv = () =&gt; {
  const div = document.createElement('div');
  document.body.appendChild(div);
  return div;
};

const withPortal = (WrappedComponent) =&gt; {
  class AddPortal extends Component {
    constructor(props) {
      super(props);
      this.el = getDiv();
    }
    componentWillUnmount() {
      document.body.removeChild(this.el);
    }
    render(props) {
      return ReactDOM.createPortal(&lt;WrappedComponent {...props} /&gt;, this.el);
    }
  }
  return AddPortal;
};

class Modal extends Component {
  render() {
    return (
      &lt;div&gt;
        &lt;div&gt;amodal content&lt;/div&gt;
        &lt;button type="button"&gt;Click&lt;/button&gt;
      &lt;/div&gt;
    );
  }
}

const PortalModal = withPortal(Modal);

class Page extends Component {
  constructor(props) {
    super(props);
    this.state = { clicks: 0 };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    this.setState(state =&gt; ({
      clicks: state.clicks + 1
    }));
  }
  render() {
    return (
      &lt;div onClick={this.handleClick}&gt;
        &lt;h3&gt;ppppppppp&lt;/h3&gt;
        &lt;h3&gt;num: {this.state.clicks}&lt;/h3&gt;
        &lt;PortalModal /&gt;
      &lt;/div&gt;
    );
  }
}

export default Page;</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">2. Lazy 和 Suspense</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(1) React.lazy 定义</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>React.lazy</code> 函数能让你像渲染常规组件一样处理动态引入（的组件）。
 <br>
 什么意思呢？其实就是懒加载。</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><strong>为什么代码要分割</strong>
  <br>
  当你的程序越来越大，代码量越来越多。一个页面上堆积了很多功能，也许有些功能很可能都用不到，但是一样下载加载到页面上，所以这里面肯定有优化空间。就如图片懒加载的理论。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>实现原理</strong>
  <br>
  当 Webpack 解析到该语法时，它会自动地开始进行代码分割(Code Splitting)，分割成一个文件，当使用到这个文件的时候会这段代码才会被异步加载。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>解决方案</strong>
  <br>
  在 <code>React.lazy</code> 和常用的三方包 react-loadable ，都是使用了这个原理，然后配合 webpack 进行代码打包拆分达到异步加载，这样首屏渲染的速度将大大的提高。
  <br>
  由于 <code>React.lazy</code> 不支持服务端渲染，所以这时候 react-loadable 就是不错的选择。</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component, Suspense } from 'react'

// import NowPlaying from './components/NowPlaying'
// import ComingSoon from './components/ComingSoon'
const NowPlaying = React.lazy(() =&gt; import('./components/NowPlaying'))
const ComingSoon = React.lazy(() =&gt; import('./components/ComingSoon'))

export default class App extends Component {

  state = {
    type: 1
  }

  render () {
    return (
      &lt;div&gt;
        App
        &lt;button onClick={() =&gt; {
          this.setState({
            type: 1
          })
        }}&gt;正在热映&lt;/button&gt;
        &lt;button onClick={() =&gt; {
          this.setState({
            type: 2
          })
        }}&gt;即将上映&lt;/button&gt;

        &lt;Suspense fallback={&lt;div&gt;加载中...&lt;/div&gt;}&gt;
          {
            this.state.type === 1 ?
              &lt;NowPlaying&gt;&lt;/NowPlaying&gt; :
              &lt;ComingSoon&gt;&lt;/ComingSoon&gt;
          }
        &lt;/Suspense&gt;
      &lt;/div&gt;
    )
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(2) 如何使用 React.lazy</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>下面示例代码使用 create-react-app 脚手架搭建：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// OtherComponent.js 文件内容
import React from 'react'

const OtherComponent = ()=&gt;{
  return (
    &lt;div&gt;
      我已加载
    &lt;/div&gt;
  )
}

export default OtherComponent
// App.js 文件内容
import React from 'react';
import './App.css';

// 使用 React.lazy 导入 OtherComponent 组件
const OtherComponent = React.lazy(() =&gt; import('./OtherComponent'));
function App() {
  return (
    &lt;div className="App"&gt;
      &lt;OtherComponent/&gt;
    &lt;/div&gt;
  );
}

export default App;</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>这是最简单的 <code>React.lazy</code>，但是这样页面会报错。这个报错提示我们，在 React 使用了 <code>lazy</code> 之后，会存在一个加载中的空档期，React 不知道在这个空档期中该显示什么内容，所以需要我们指定。接下来就要使用到 <code>Suspense</code>。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">Suspense</h4>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>如果在 App 渲染完成后，包含 <code>OtherComponent</code> 的模块还没有被加载完成，我们可以使用加载指示器为此组件做优雅降级。这里我们使用 <code>Suspense</code> 组件来解决。
 <br>
 这里将 App 组件改一改</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Suspense, Component } from 'react';
import './App.css';

// 使用 React.lazy 导入 OtherComponent 组件
const OtherComponent = React.lazy(() =&gt; import('./OtherComponent'));

export default class App extends Component {
  state = {
    visible: false
  }
  render() {
    return (
      &lt;div className="App"&gt;
        &lt;button onClick={() =&gt; {
          this.setState({ visible: true })
        }}&gt;加载 OtherComponent 组件&lt;/button&gt;
        &lt;Suspense fallback={&lt;div&gt;Loading...&lt;/div&gt;}&gt;
          { this.state.visible ? &lt;OtherComponent /&gt; : null }
        &lt;/Suspense&gt;
      &lt;/div&gt;
    )
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>我们指定了空档期使用 Loading 展示在界面上面，等 <code>OtherComponent</code> 组件异步加载完毕，把 <code>OtherComponent</code> 组件的内容替换掉 Loading 上。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>为了演示把 chrome 网络调到 lower-end mobile，不然看不到 loading 出现。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>可以从上面图片看出，当点击加载的时候，页面的 head 会插入 <code>这段代码，发出一个 get 请求，页面开始显示 loading，去请求 2.chunk.js</code> 文件。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>请求结束返回内容就是 <code>OtherComponent</code> 组件的内容,只是文件名称和文件内容经过 webpack 处理过。</p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p><strong>注意：<code>Suspense</code> 使用的时候，<code>fallback</code> 一定是存在且有内容的， 否则会报错。</strong></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">3. forwordRef</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><em>引用传递（Ref forwading）是一种通过组件向子组件自动传递 引用 ref 的技术。对于应用者的大多数组件来说没什么作用。但是对于有些重复使用的组件，可能有用。例如某些 input 组件，需要控制其 focus，本来是可以使用 ref 来控制，但是因为该 input 已被包裹在组件中，这时就需要使用 Ref forward 来透过组件获得该 input 的引用。可以透传多层</em></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component, forwardRef } from 'react'

export default class App extends Component {
  myRef = React.createRef()

  render () {
    return (
      &lt;div&gt;
        App
        &lt;button onClick={() =&gt; {
          this.myRef.current.focus()
          this.myRef.current.value = ''
        }}&gt;获取焦点&lt;/button&gt;

        &lt;Child ref={this.myRef}&gt;&lt;/Child&gt;
      &lt;/div&gt;
    )
  }
}



const Child = forwardRef((props, ref) =&gt; {
  return (
    &lt;div style={{ backgroundColor: 'skyblue' }}&gt;
      Child
      &lt;input type='text' defaultValue='初始值' ref={ref}&gt;&lt;/input&gt;
    &lt;/div&gt;
  )
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">未使用 forwordRef</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 子组件
class Child extends Component{
  componentDidMount() {
    this.props.callback(this.refs.myinput)
  }
  render(){
    return (
      &lt;div&gt;
        &lt;input type="text" ref="myinput"/&gt;
      &lt;/div&gt;
    )
  }
}

// 父组件
class App extends Component {
  render() {
    return (
      &lt;div&gt;
        &lt;Child callback={(el)=&gt;{
          el.focus()
        }}/&gt;
      &lt;/div&gt;
    )
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">使用 forwardRef</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 子组件
const Child = forwardRef((props,ref)=&gt;{
  return (
    &lt;div&gt;
      &lt;input type="text" ref={ref}/&gt;
    &lt;/div&gt;
  )
})

// 父组件
class App extends Component {
  myref = createRef()
  componentDidMount() {
    this.myref.current.focus()
  }

  render() {
    return (
      &lt;div&gt;
        &lt;Child ref={this.myref}/&gt;
      &lt;/div&gt;
    )
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">4. Functional Component 缓存</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">为什么起 memo 这个名字？</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><em>在计算机领域，记忆化是一种主要用来提升计算机程序速度的优化技术方案。它将开销较大的函数调用的返回结果存储起来，当同样的输入再次发生时，则返回缓存好的数据，以此提升运算效率。</em></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">作用</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><em>组件仅在它的 props 发生改变的时候进行重新渲染。通常来说，在组件树中 React 组件，只要有变化就会走一遍渲染流程。但是 <code>React.memo()</code>，我们可以仅仅让某些组件进行渲染。</em></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">与 PureComponent 区别</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>PureComponent 只能用于 class 组件，memo 用于 functional 组件</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component, memo } from 'react'

export default class App extends Component {
  state = {
    name: 'Joe',
    age: 18
  }

  render () {
    return (
      &lt;div&gt;
        App --- {this.state.name}
        &lt;button onClick={() =&gt; {
          this.setState({
            name: 'qiaofugui'
          })
        }}&gt;Click name&lt;/button&gt;

        &lt;button onClick={() =&gt; {
          this.setState({
            age: 999
          })
        }}&gt;Click age&lt;/button&gt;

        &lt;Child age={this.state.age}&gt;&lt;/Child&gt;
      &lt;/div&gt;
    )
  }
}



const Child = memo((props) =&gt; {
  console.log('Child')
  return (
    &lt;div&gt;Child --- {props.age}&lt;/div&gt;
  )
})</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">用法</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import {memo} from 'react'

const Child = memo(()=&gt;{
  return (
    &lt;div&gt;
      &lt;input type="text" /&gt;
    &lt;/div&gt;
  )
})

// 或者

const Child = ()=&gt;{
  return (
    &lt;div&gt;
      &lt;input type="text" /&gt;
    &lt;/div&gt;
  )
})
const MemoChild = memo(Child)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">二十一、React 扩展</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">1. GraphQL</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">（1）介绍与 hello</h3>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>GraphQL 是 Facebook 开发的一种数据查询语言，并于2015年公开发布。它是 REST API 的替代品</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>GraphQL既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述，使得客户能够准确地获得它需要的数据，而且没有任何冗余，也让 API 更容易地随着时间推进而演进</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>官网：https://graphql.org/</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>中文网：https://graphql.cn/</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">特点</h4>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>请求需要的数据，不多不少 <em>例如：account 中有 name、age、sex、departmen 等。可以取得需要的字段</em></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>获取多个资源，只用一个请求</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>描述所有可能类型的系统。便于维护，根据需求平滑演进，添加或者隐藏字段</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>reslful 一个接口只能返回页资源，graphql 一次可以读取多个资源</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>reslful 用不同的 url 来区分资源，graphql 用类型区分资源</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>query {
  user (id : "1") {
    name
    gender
    employee (first: 20) {
      name
      email
    }
    father {
      telephone
    }
    son {
      school
    }
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:image {"id":280,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong16.png&amp;size=m" alt="" class="wp-image-280">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">（2）参数类型与传递</h3>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>基本类型：<code>String</code> / <code>Int</code> / <code>Float</code> / <code>Boolean</code> 和 ID，可以在 shema 声明的时候直接使用</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>[类型]</code> 代表数组，例如：<code>[Int]</code> 代表整型数组</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>和 js 传递参数一样，小括号内定义形参，但是注意：参数需要定义类型</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>!</code> (叹号)表示参数不能为空</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const express = require('express')
const { buildSchema } = require('graphql')
const graphqlHttp = require('express-graphql')

var Schema = buildSchema(`

  type Account{
    name: String,
    age: Int,
    location: String
  }
  type Film{
    id: Int,
    name: String,
    poster: String,
    price: Int
  }

  type Query{
    hello: String,
    getName: String,
    getAge: Int,
    getAllName: [String],
    getAllAge: [Int],
    getAccountInfo: Account,
    getNowPlayingList: [Film],
    getFilmDetail(id: Int!): Film
  }
`)
// 假数据
var fakeDb = [
  { id: 1, name: 'aaa', poster: 'https://111', price: 100 },
  { id: 2, name: 'bbb', poster: 'https://222', price: 200 },
  { id: 3, name: 'ccc', poster: 'https://333', price: 300 }
]
// 处理器
const root = {
  // 通过数据库查询
  hello: () =&gt; {
    var str = 'hello world'
    return str
  },
  getName: () =&gt; {
    return 'Joe'
  },
  getAge: () =&gt; {
    return 18
  },
  getAllName: () =&gt; {
    return ['aaa', 'bbb', 'ccc']
  },
  getAllAge: () =&gt; {
    return [111, 222, 333]
  },
  getAccountInfo () {
    return { name: 'Joe', age: 18, location: 'chinese' }
  },
  getNowPlayingList () {
    return fakeDb
  },
  getFilmDetail ({ id }) {
    return fakeDb.filter(item =&gt; item.id === id)[0]
  }
}

var app = express()
app.use('/home', function (req, res) {
  res.send('home data')
})
app.use('/list', function (req, res) {
  res.send('list data')
})



app.use('/graphql', graphqlHttp({
  schema: Schema,
  rootValue: root,
  graphiql: true
}))

app.listen(3000)
// http://localhost:3000/graphql?query=</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>查数据</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 查数据
query {
  hello
  getName
  getAge
  getAllName
  getAllAge
  getAccountInfo {
    name
    age
    location
  }
  getNowPlayingList {
    id
    name
    poster
    price
  }
  getFilmDetail(id: 3) {
    id
    name
    poster
    price
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">（3）mutation</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>查询数据用 <code>query</code>，修改数据用 <code>Mutation</code></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const express = require('express')
const { buildSchema } = require('graphql')
const graphqlHttp = require('express-graphql')

var Schema = buildSchema(`

  type Film{
    id: Int,
    name: String,
    poster: String,
    price: Int
  }

  input FilmInput{
    name: String,
    poster: String,
    price: Int
  }

  type Query{
    getNowPlayingList: [Film],
  }

  type Mutation{
    createFilm(input: FilmInput): Film,
    updateFilm(id: Int!, input: FilmInput): Film,
    deleteFilm(id: Int!): Int
  }
`)
// 假数据
var fakeDb = [
  { id: 1, name: 'aaa', poster: 'https://111', price: 100 },
  { id: 2, name: 'bbb', poster: 'https://222', price: 200 },
  { id: 3, name: 'ccc', poster: 'https://333', price: 300 }
]
// 处理器
const root = {
  // 通过数据库查询
  getNowPlayingList () {
    return fakeDb
  },



  createFilm ({ input }) {
    var obj = { ...input, id: fakeDb.length + 1 }
    fakeDb.push(obj)
    return obj
  },
  updateFilm ({ id, input }) {
    var current = null
    fakeDb = fakeDb.map(item =&gt; {
      if (item.id === id) {
        current = { ...item, ...input }
        return { ...item, ...input }
      }
      return item
    })
    return current
  },
  deleteFilm ({ id }) {
    fakeDb = fakeDb.filter(item =&gt; item.id !== id)
    return 1
  }
}

var app = express()
app.use('/home', function (req, res) {
  res.send('home data')
})
app.use('/list', function (req, res) {
  res.send('list data')
})



app.use('/graphql', graphqlHttp({
  schema: Schema,
  rootValue: root,
  graphiql: true
}))

app.listen(3000)
// http://localhost:3000/graphql</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>改数据</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 改数据
mutation {
  createFilm(input:{
    name:"ddd",
    poster:"https://444",
    price:400
  }) {
    id,
    name,
    poster,
    price
  }

  updateFilm(id: 1, input: {
    name: "aaa-修改",
    poster: "https://111-修改",
    price: 111
  }) {
    id,
    name,
    poster,
    price
  }

  deleteFilm(id: 1)
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">（4）结合数据库</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const express = require('express')
const { buildSchema } = require('graphql')
const graphqlHttp = require('express-graphql')

// ----------链接数据库服务----------
var mongoose = require("mongoose")
mongoose.connect("mongodb://localhost:27017/maizuo", { useNewUrlParser: true, useUnifiedTopology: true })

// 限制 数据库这个films（集合表） 只能存三个字段
var FilmModel = mongoose.model("film", new mongoose.Schema({
  name: String,
  poster: String,
  price: Number
}))
// FilmModel.create
// FilmModel.find
// FilmModel.update
// FilmModel.delete
// --------------------------------



var Schema = buildSchema(`

  type Film{
    id: String,
    name: String,
    poster: String,
    price: Int
  }

  input FilmInput{
    name: String,
    poster: String,
    price: Int
  }

  type Query{
    getNowPlayingList: [Film],
  }

  type Mutation{
    createFilm(input: FilmInput): Film,
    updateFilm(id: String!, input: FilmInput): Film,
    deleteFilm(id: String!): Int
  }
`)
// 处理器
const root = {
  // 通过数据库查询
  getNowPlayingList () {
    return FilmModel.find()
  },



  createFilm ({ input }) {
    /*
      1. 创建模型
      2. 操作数据库
    */
    return FilmModel.create({
      ...input
    })
  },
  updateFilm ({ id, input }) {
    return FilmModel.updateOne({
      _id: id
    }, {
      ...input
    }).then(res =&gt; FilmModel.find({ _id: id })).then(res =&gt; res[0])
  },
  deleteFilm ({ id }) {
    return FilmModel.deleteOne({ _id: id }).then(res =&gt; 1)
  }
}

var app = express()
app.use('/home', function (req, res) {
  res.send('home data')
})
app.use('/list', function (req, res) {
  res.send('list data')
})



app.use('/graphql', graphqlHttp({
  schema: Schema,
  rootValue: root,
  graphiql: true
}))

app.listen(3000)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">（5）客户端访问</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":281,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong17.png&amp;size=m" alt="" class="wp-image-281">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">（6） 结合React</h3>
<!-- /wp:heading -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">query</h4>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>npm i react-apollo apollo-boost graphql graphql-tag</code></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component } from 'react'
import { ApolloProvider, Query } from 'react-apollo'
import ApolloClient from 'apollo-boost'
import gql from 'graphql-tag'

const client = new ApolloClient({
  uri: "/graphql"
})
export default class App extends Component {
  render () {
    return (
      &lt;ApolloProvider client={client}&gt;
        &lt;div&gt;
          &lt;JoeQuery&gt;&lt;/JoeQuery&gt;
        &lt;/div&gt;
      &lt;/ApolloProvider&gt;
    )
  }
}



class JoeQuery extends Component {
  query = gql`
        query {
            getNowPlayingList {
            id,
            name,
            price
            }
        }
    `
  render () {
    return &lt;Query query={this.query}&gt;
      {
        ({ loading, data }) =&gt; {
          console.log(loading)
          return loading ? &lt;div&gt;loading....&lt;/div&gt; :
            &lt;div&gt;
              {
                data.getNowPlayingList.map(item =&gt;
                  &lt;div key={item.id}&gt;
                    &lt;div&gt;名字：{item.name}&lt;/div&gt;
                    &lt;div&gt;价格：{item.price}&lt;/div&gt;
                  &lt;/div&gt;
                )
              }
            &lt;/div&gt;
        }
      }
    &lt;/Query&gt;
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>带参数</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component } from 'react'
import { ApolloProvider, Query } from 'react-apollo'
import ApolloClient from 'apollo-boost'
import gql from 'graphql-tag'

const client = new ApolloClient({
  uri: "/graphql"
})
export default class App extends Component {
  render () {
    return (
      &lt;ApolloProvider client={client}&gt;
        &lt;div&gt;
          &lt;JoeQuery&gt;&lt;/JoeQuery&gt;
        &lt;/div&gt;
      &lt;/ApolloProvider&gt;
    )
  }
}



class JoeQuery extends Component {
  query = gql`
        query getNowPlayingList($id:String!){
            getNowPlayingList(id:$id) {
            id,
            name,
            price
            }
        }
    `
  state = {
    id: "61e66f60dd8ae3c99074ac53"
  }
  render () {
    return &lt;div&gt;
      &lt;input type="text" onChange={(evt) =&gt; {
        this.setState({
          id: evt.target.value
        })
      }} /&gt;
      &lt;Query query={this.query} variables={{ id: this.state.id }}&gt;
        {
          ({ loading, data }) =&gt; {
            console.log(loading)
            return loading ? &lt;div&gt;loading....&lt;/div&gt; :
              &lt;div&gt;
                {
                  data.getNowPlayingList.map(item =&gt;
                    &lt;div key={item.id}&gt;
                      &lt;div&gt;名字：{item.name}&lt;/div&gt;
                      &lt;div&gt;价格：{item.price}&lt;/div&gt;
                    &lt;/div&gt;
                  )
                }
              &lt;/div&gt;
          }
        }
      &lt;/Query&gt;
    &lt;/div&gt;
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">mutation</h4>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component } from 'react'
import { ApolloProvider, Mutation } from 'react-apollo'
import ApolloClient from 'apollo-boost'
import gql from 'graphql-tag'

const client = new ApolloClient({
  uri: "/graphql"
})
export default class App extends Component {
  render () {
    return (
      &lt;ApolloProvider client={client}&gt;
        &lt;div&gt;
          &lt;JoeCreate&gt;&lt;/JoeCreate&gt;
        &lt;/div&gt;
      &lt;/ApolloProvider&gt;
    )
  }
}



class JoeCreate extends Component {
  createFilm = gql`
    mutation createFilm($input: FilmInput){
        createFilm(input:$input) {
          id,
          name,
          price
        }
      }
    `
  render () {
    return &lt;div&gt;
      &lt;Mutation mutation={this.createFilm}&gt;
        {
          (createFilm, { data }) =&gt; {
            console.log(data)
            return &lt;div&gt;
              &lt;button onClick={() =&gt; {
                createFilm({
                  variables: {
                    input: {
                      name: "777",
                      poster: "http://777",
                      price: 70
                    }
                  }
                })
              }}&gt;add&lt;/button&gt;
            &lt;/div&gt;
          }
        }
      &lt;/Mutation&gt;
    &lt;/div&gt;
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>更新</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component } from 'react'
import { ApolloProvider, Mutation } from 'react-apollo'
import ApolloClient from 'apollo-boost'
import gql from 'graphql-tag'

const client = new ApolloClient({
  uri: "/graphql"
})
export default class App extends Component {
  render () {
    return (
      &lt;ApolloProvider client={client}&gt;
        &lt;div&gt;
          &lt;JoeUpdate&gt;&lt;/JoeUpdate&gt;
        &lt;/div&gt;
      &lt;/ApolloProvider&gt;
    )
  }
}



class JoeUpdate extends Component {
  createFilm = gql`
    mutation updateFilm($id:String!,$input: FilmInput){
        updateFilm(id:$id,input:$input) {
          id,
          name,
          price
        }
      }
    `
  render () {
    return &lt;div&gt;
      &lt;Mutation mutation={this.createFilm}&gt;
        {
          (updateFilm, { data }) =&gt; {
            console.log(data)
            return &lt;div&gt;
              &lt;button onClick={() =&gt; {
                updateFilm({
                  variables: {
                    id: "61e67c0031bf52b53c9245c7",
                    input: {
                      name: "777-修改",
                      poster: "http://777-修改",
                      price: 700
                    }
                  }
                })
              }}&gt;update&lt;/button&gt;
            &lt;/div&gt;
          }
        }
      &lt;/Mutation&gt;
    &lt;/div&gt;
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>删除</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component } from 'react'
import { ApolloProvider, Mutation } from 'react-apollo'
import ApolloClient from 'apollo-boost'
import gql from 'graphql-tag'

const client = new ApolloClient({
  uri: "/graphql"
})
export default class App extends Component {
  render () {
    return (
      &lt;ApolloProvider client={client}&gt;
        &lt;div&gt;
          &lt;JoeDelete&gt;&lt;/JoeDelete&gt;
        &lt;/div&gt;
      &lt;/ApolloProvider&gt;
    )
  }
}



class JoeDelete extends Component {
  createFilm = gql`
    mutation deleteFilm($id:String!){
        deleteFilm(id:$id)
      }
    `
  render () {
    return &lt;div&gt;
      &lt;Mutation mutation={this.createFilm}&gt;
        {
          (deleteFilm, { data }) =&gt; {
            console.log(data)
            return &lt;div&gt;
              &lt;button onClick={() =&gt; {
                deleteFilm({
                  variables: {
                    id: "61e67c0031bf52b53c9245c7"
                  }
                })
              }}&gt;delete&lt;/button&gt;
            &lt;/div&gt;
          }
        }
      &lt;/Mutation&gt;
    &lt;/div&gt;
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">2. dva</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>npm install dva-cli -g</code>
 <br>
 https://dvajs.com/guide</p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>dva 首先是一个基于 redux 和 redux-saga 的数据流方案，然后为了简化开发体验，dva 还额外内置了 react-router 和 fetch，所以也可以理解为一个轻量级的应用框架</p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p><strong>dva = react-router + redux + redux-saga</strong></p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>dva-cli is deprecated, please use create-umi instead, checkout https://umijs.org/guide.create-umi-app.html for detail.</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">dva 应用的最简洁结构</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import dva from 'dva'
const App = () =&gt; &lt;div&gt;Hello Dva&lt;/div&gt;

// 创建应用
const app = dva()
// 注册视图
app.router(() =&gt; &lt;App /&gt;)
// 启动应用
app.start('#root')</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">数据流图</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":273,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong18-1024x322.png&amp;size=m" alt="" class="wp-image-273">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">3. umi</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><a href="https://umijs.org/">UmiJS - 插件化的企业级前端应用框架</a></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>umi，中文发音为乌米，是一个可插拔的企业级 react 应用框架。umi 以路由为基础的，支持类 next.js 的约定式路由，以及各种进阶的路由功能，并以此进行功能扩展，比如支持路由级的按需加载。umi 在约定式路由的功能层面会更像 nuxt.js 一些。</p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>开箱即用，省去了搭框架的的时间</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">安装脚手架</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在一个空目录里执行</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code># npm
npx @umijs/create-umi-app
npx create-umi@latest

# yarn
yarn create umi

# pnpm
pnpm dlx create-umi@latest</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">目录</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>一个基础的 Umi 项目大致是这样的
 <br>
 <img class="wp-image-274" style="width: 300px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong19.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">路由</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>配置式路由：在 <code>.umirc.ts</code> 文件内 <code>routes</code> 选项配置</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>约定式路由：umi 会根据 pages 目录自动生成路由配置。<strong>需要注释 <code>.umirc.ts</code> 文件内 <code>routes</code> 相关，否则自动配置不生效</strong></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">1. 基础路由</h4>
<!-- /wp:heading -->
<!-- wp:image {"id":275,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong20.png&amp;size=m" alt="" class="wp-image-275">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">2. 重定向</h4>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// pages/index.tsx
import React from 'react'
import { Redirect } from 'umi'

export default function index() {
  return (
    &lt;Redirect to='/film' /&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">3. 嵌套路由</h4>
<!-- /wp:heading -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>有时候不好用，尝试重启一下</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:image {"id":276,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong21.png&amp;size=m" alt="" class="wp-image-276">
</figure>
<!-- /wp:image -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// pages/film/_layout.tsx
import React, { ReactNode } from 'react'
import { IRouteComponentProps, Redirect, useLocation } from 'umi'

interface IProps {
  children: ReactNode
}
type myProps = IProps &amp; RouteComponentProps; // 两个接口属性都支持
export default function Film(props: myProps) {
  if (props.location.pathname === '/film' || props.location.pathname === '/film/') {
    // '/film' 重定向到 '/film/nowplaying'
    return &lt;Redirect to='/film/nowplaying' /&gt;
  }
  return (
    &lt;div&gt;
      Film
      &lt;div style={{ width: '100%', height: '200px', backgroundColor: 'pink' }}&gt;大轮播&lt;/div&gt;
      {props.children}
    &lt;/div&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">4. 动态路由</h4>
<!-- /wp:heading -->
<!-- wp:image {"id":277,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong22.png&amp;size=m" alt="" class="wp-image-277">
</figure>
<!-- /wp:image -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// pages/detail/[id].tsx
import React from 'react'
import { IRouteComponentProps, useParams } from 'umi'

interface IDetailId {
  id: string
}
export default function Detail(props: IRouteComponentProps) {
  const params = useParams&lt;IDetailId&gt;()

  return (
    // &lt;div&gt;Detail --- {props.match.params.id}&lt;/div&gt;
    &lt;div&gt;Detail --- {params.id}&lt;/div&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// pages/film/NowPlaying.tsx
import React, { useEffect, useState } from 'react'
import { IRouteComponentProps, useHistory } from 'umi'

interface IItem {
  filmId: number,
  name: string
}
export default function NowPlaying(props: IRouteComponentProps) {

  const [nowPlayingList, setNowPlayingList] = useState([])
  const history = useHistory()

  useEffect(() =&gt; {
    fetch('https://m.maizuo.com/gateway?cityId=110100&amp;pageNum=1&amp;pageSize=10&amp;type=1&amp;k=3898483', {
      headers: {
        'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"16717661461034803650494465","bc":"110100"}',
        'X-Host': 'mall.film-ticket.film.list'
      }
    }).then(res =&gt; res.json())
      .then(res =&gt; {
        console.log(res.data.films)
        setNowPlayingList(res.data.films)
      })
    return () =&gt; {
    }
  }, [])

  return (
    &lt;div&gt;
      NowPlaying
      &lt;ul&gt;
        {
          nowPlayingList.length === 0
            ? '加载中...' :
            nowPlayingList.map((item: IItem) =&gt; &lt;li key={item.filmId} onClick={() =&gt; {
              // props.history.push(`/detail/${item.filmId}`)
              history.push(`/detail/${item.filmId}`)
            }}&gt;{item.name}&lt;/li&gt;)
        }
      &lt;/ul&gt;
    &lt;/div&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">5. 路由拦截</h4>
<!-- /wp:heading -->
<!-- wp:image {"id":270,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong24.png&amp;size=m" alt="" class="wp-image-270">
</figure>
<!-- /wp:image -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// src/pages/film/Center.tsx
import React from 'react'

function Center() {
  return (
    &lt;div&gt;Center&lt;/div&gt;
  )
}

Center.wrappers = ['@/wrappers/Auth']

export default Center</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// src/wrapper/Auth.tsx
import React, { ReactNode } from 'react'
import { Redirect } from 'umi'

interface IProps {
  children: ReactNode
}
export default (props: IProps) =&gt; {
  const isLogin = localStorage.getItem("token")
  console.log(isLogin);

  if (isLogin) {
    return &lt;div&gt;{props.children}&lt;/div&gt;
  } else {
    return &lt;Redirect to="/login" /&gt;
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">6. hash 模式</h4>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 在.umirc.ts

export default {
  history:{ type: 'hash' }
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 在 .umirc.ts
import { defineConfig } from 'umi';

export default defineConfig({
  nodeModulesTransform: {
    type: 'none',
  },
  history: { type: 'hash' }, // 默认 browser 模式
  // routes: [
  //   { path: '/', component: '@/pages/index' },
  // ],
  fastRefresh: {},
});</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">7. 声明式导航</h4>
<!-- /wp:heading -->
<!-- wp:image {"id":269,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong23.png&amp;size=m" alt="" class="wp-image-269">
</figure>
<!-- /wp:image -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { ReactNode } from 'react'
import { NavLink } from 'umi'

import './index.less'

interface IPRops {
  children: ReactNode
}
export default function IndexLayout(props: IPRops) {
  return (
    &lt;div&gt;
      IndexLayout
      {props.children}

      &lt;ul style={{ display: 'flex', position: 'fixed', left: '0', bottom: '0', justifyContent: 'space-evenly', width: '100%', margin: '0', padding: '0', backgroundColor: 'skyblue', listStyle: 'none', textAlign: 'center' }}&gt;
        &lt;li&gt;
          &lt;NavLink to='/film' activeClassName='active'&gt;Film&lt;/NavLink&gt;
        &lt;/li&gt;
        &lt;li&gt;
          &lt;NavLink to='/cinema' activeClassName='active'&gt;Cinema&lt;/NavLink&gt;
        &lt;/li&gt;
        &lt;li&gt;
          &lt;NavLink to='/center' activeClassName='active'&gt;Center&lt;/NavLink&gt;
        &lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">8. 编程式导航</h4>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { history } from 'umi';

history.push(`/detail/${item}`)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">mock 功能</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><em>umi 里约定 mock 文件夹下的文件或者 page(s) 文件夹下的 _mock 文件即 mock 文件，文件导出接口定义，支持基于 require 动态分析的实时刷新，支持 ES6 语法，以及友好的出错提示</em></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// mock/api.js
export default {
  // 支持值为 Object 和 Array
  'GET /api/users': { users: [1, 2] },

  // GET POST 可省略
  '/api/users/1': { id: 1 },

  // 支持自定义函数，API 参考 express@4
  'POST /api/users/create': (req, res) =&gt; { res.end('OK'); },
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export default {
  'GET /users': { name: 'joe', password: '111' },
  'POST /users/login': (req, res) =&gt; {
    console.log(req.body)
    const { username, userpwd } = req.body
    if (username === 'joe' &amp;&amp; userpwd === '111') {
      res.send({
        ok: 1
      })
    } else {
      res.send({
        ok: 0
      })
    }
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">反向代理</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 在.umirc.ts
proxy: {
  '/ajax': {
    target: 'https://m.maoyan.com',
    // pathRewrite: { '^/api': '' },
    changeOrigin: true
  }
},</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { defineConfig } from 'umi';

export default defineConfig({
  nodeModulesTransform: {
    type: 'none',
  },
  history: { type: 'browser' },
  // routes: [
  //   { path: '/', component: '@/pages/index' },
  // ],
  fastRefresh: {},
  proxy: {
    '/api': {
      target: 'https://i.maoyan.com',
      changeOrigin: true
    }
  }
});</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">antd</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// .umirc.ts
import { defineConfig } from 'umi';

export default defineConfig({
  nodeModulesTransform: {
    type: 'none',
  },
  history: { type: 'browser' },
  // routes: [
  //   { path: '/', component: '@/pages/index' },
  // ],
  fastRefresh: {},
  proxy: {
    '/api': {
      target: 'https://i.maoyan.com',
      changeOrigin: true
    }
  },
  antd: {
    mobile: false
  }
});



// 组件页面中使用
import {Button} from 'antd-mobile'
&lt;Button type="primary"&gt;add&lt;/Button&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">dva 集成</h3>
<!-- /wp:heading -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>默认开启了 redux 调试工具</p>
 <!-- /wp:paragraph -->
 <!-- wp:list -->
 <ul>
  <!-- wp:list-item -->
  <li>按目录约定注册 model，无需手动 app.model</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>文件名即<em>命名空间</em> namespace，可以省去 model 导出的 namespace key</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>无需手写 router.js，交给 umi 处理，支持 model 和 component 的按需加载</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>内置 query-string 处理，无需再手动解码和编码</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>内置 dva-loading 和 dva-immer，其中 dva-immer 需通过配置开启(简化 reducer 编写)</li>
  <!-- /wp:list-item -->
 </ul>
 <!-- /wp:list -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// .umirc.ts
dva:{
  // 自定义配置
}</code></pre>
<!-- /wp:code -->
<!-- wp:image {"id":271,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong25.png&amp;size=m" alt="" class="wp-image-271">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">同步</h4>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// models/joe.js
export default {
  // 命名空间
  namespace:'joe',
  state:{
    isShow:true,
  list:[]
  },

  // 处理 state －－同步
  reducers:{
    // reducer简写， type 类型是 show 的时候自动处理
    show(state,{payload}){
      return {...state,...payload}
    },
    hide(state,{payload}){
      return {...state,...payload}
    }
  },

  // yield 表示后面的方法执行完以后 call 表示调用一个 api 接口
  // put表示一个派发
  effects:{
    *showEffect(payload,{ put }){
      yield put({
        type:'show',
        payload:{
          isShow:true
        }
      })
    },
    *hideEffect(payload,{put}){
      yield put({
        type:'hide',
        payload:{
          isShow:false
        }
      })
    }
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 根组件
import { connect } from 'umi';

function BasicLayout(props) {
  return (
    &lt;div &gt;
      { props.isShow ?... :null }
      {props.children}
    &lt;/div&gt;
  );
}

// state.joe 命名空间
export default connect(state=&gt;state.kerwin)(BasicLayout);</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// detail.js
import { connect, useDispatch } from 'umi';

function Detail(props) {
  const dispatch = useDispatch()
  useEffect(() =&gt; {
    dispatch({
      type:"joe/hideEffect" // 命名空间 joe
    })
    return () =&gt; {
      dispatch({
        type:"joe/showEffect"//命名空间 joe
      })
    };
  }, [])

  return (
    &lt;div&gt;
      Detail
    &lt;/div&gt;
  )
}

export default connect(state=&gt;state.joe)(Detail)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">异步</h4>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// models/joe.js
import {getNowplaying} from '../util/getNowplaying'; // 封装的 fetch 调用接口

export default{
  // ...
  reducers:{
    // ...
    changeList(state,{payload}){
      return {...state,...payload}
    }
  },

  // 异步
  // yield 表示后面的方法执行完以后 call 表示调用一个 api 接口
  effects:{
    // ...
    *getListEffect(payload,{put,call}){
      let res = yield call(getNowplaying,"test-by-joe")
      yield put({
        type:"changeList",
        payload:{
          list:res
        }
      })
    }
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// util/getNowplaying
export async function getNowplaying(value){
  console.log(value) // value 是 call 的第二个参数
  var res = await fetch("/ajax/comingList?ci=65&amp;token=&amp;limit=10&amp;optimus_uuid=43388C403C4911EABDC9998C784A573A4F64A16AA5A34184BADE807E506D749E&amp;optimus_risk_level=71&amp;optimus_code=10").then(res=&gt;res.json())
  return res.coming
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// nowplaying.js
import React,{useEffect} from 'react';
import { connect,useDispatch } from 'umi';

function Nowplaying(props) {
  let {list,loading} = props
  let dispatch = useDispatch()

  useEffect(() =&gt; {
    if(list.length===0){
      dispatch({
        type:"joe/getListEffect" // 命名空间 joe
      })
    }
  }, [list])

  return (
    &lt;div&gt;
      nowplaying--{loading.global?'正在加载数据...':''}
      { 遍历list }
    &lt;/div&gt;
  )
}

export default connect(({joe,loading})=&gt;({
  ...joe,
  loading
}))(Nowplaying)</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// models/cinemaModel.ts
export default {
  namespace: 'cinema',

  state: {
    list: []
  },

  reducers: {
    clearList(prevState: any) {
      return {
        ...prevState,
        list: []
      }
    },
    changeList(prevState: any, action: any) {
      return {
        ...prevState,
        list: action.payload
      }
    }
  },

  effects: {
    *getList(action: any, obj: any): any {
      const { call, put } = obj
      const res = yield call(getListForList, action.payload.cityId)
      console.log(res);

      yield put({
        type: 'changeList',
        payload: res
      })
    }
  }
}



async function getListForList(id: string) {
  let res = await fetch(`https://m.maizuo.com/gateway?cityId=${id}&amp;ticketFlag=1&amp;k=5990609`, {
    headers: {
      'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"16717661461034803650494465"}',
      'X-Host': 'mall.film-ticket.cinema.list'
    }
  }).then(res =&gt; res.json())
  return res.data.cinemas
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// pages/Cinema.tsx
import React, { useEffect } from 'react'
import { DotLoading, NavBar } from 'antd-mobile'
import { SearchOutline } from 'antd-mobile-icons'
import { connect } from 'umi'

function Cinema(props: any) {

  useEffect(() =&gt; {

    if (props.list.length === 0) {
      props.dispatch({
        type: 'cinema/getList',
        payload: {
          cityId: props.cityId
        }
      })
    } else {
      console.log('缓存');
    }

  }, [])

  return (
    &lt;div&gt;
      &lt;NavBar back={
        &lt;div onClick={() =&gt; {
          props.dispatch({
            type: 'cinema/clearList',
          })
          props.history.push('/city')
        }}&gt;
          {props.cityName}
        &lt;/div&gt;
      } backArrow={null} right={
        &lt;div style={{ fontSize: '24px' }}&gt;&lt;SearchOutline /&gt;&lt;/div&gt;
      }&gt;标题
      &lt;/NavBar&gt;
      {
        props.loading &amp;&amp;
        &lt;div style={{ fontSize: 24, textAlign: 'center' }}&gt;
          &lt;DotLoading&gt;&lt;/DotLoading&gt;
        &lt;/div&gt;
      }
      Cinema
      &lt;ul&gt;
        {
          props.list.map((item: any) =&gt;
            &lt;li key={item.cinemaId}&gt;{item.name}&lt;/li&gt;
          )
        }
      &lt;/ul&gt;
    &lt;/div&gt;
  )
}

const mapStateToProps = (state: any) =&gt; {
  console.log(state);
  return {
    loading: state.loading.global,
    cityName: state.city.cityName,
    cityId: state.city.cityId,
    list: state.cinema.list,
  }
}
export default connect(mapStateToProps)(Cinema)</code></pre>
<!-- /wp:code -->]]></description><guid isPermaLink="false">/archives/react17-%E5%85%A8%E5%AE%B6%E6%A1%B6%EF%BC%883%EF%BC%89</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Tue, 21 May 2024 06:41:57 GMT</pubDate></item><item><title><![CDATA[React17-全家桶（2）]]></title><link>https://blog.qiaofugui.cn/archives/react17-%E5%85%A8%E5%AE%B6%E6%A1%B6%EF%BC%882%EF%BC%89</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=React17-%E5%85%A8%E5%AE%B6%E6%A1%B6%EF%BC%882%EF%BC%89&amp;url=/archives/react17-%E5%85%A8%E5%AE%B6%E6%A1%B6%EF%BC%882%EF%BC%89" width="1" height="1" alt="" style="opacity:0;"><!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">八、React 生命周期</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">1. 初始化阶段</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":290,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong04.png&amp;size=m" alt="" class="wp-image-290">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">2. 运行中阶段</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":291,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong05.png&amp;size=m" alt="" class="wp-image-291">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">3. 销毁阶段</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":292,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong06.png&amp;size=m" alt="" class="wp-image-292">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">老生命周期的问题</h2>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><code>componentWillMount</code>，在 ssr 中这个方法将会被多次调用，所以会重复触发多遍，同时在这里如果绑定事件，将无法解绑，导致内存泄漏，变得不够安全高效逐步废弃</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>componentWillReceiveProps</code> 外部组件多次频繁更新传入多次不同的 props，会导致不必要的异步请求</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>componetWillupdate</code>，更新前记录 DOM 状态, 可能会做一些处理，与 <code>componentDidUpdate</code> 相隔时间如果过长，会导致状态不太信</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">新生命周期的提替代</h2>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><code>getDerivedStateFromProps</code> 第一次的初始化组件以及后续的更新过程中（包括自身状态更新以及父传子），返回一个对象作为新的 state，返回 null 则说明不需要在这里更新 state</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 老的生命周期的写法
componentDidMount() {
  if(this.props.value !== undefined){
    this.setState({
      current:this.props.value
    })
  }
}
componentWillReceiveProps(nextProps){
  if(nextProps.value !==undefined){
    this.setState({
      current: nextProps.value
    })
  }
}

// 新的生命周期写法
static getDerivedStateFromProps(nextProps) {
  if(nextProps.value !== undefined){
    return {
      current: nextProps.value
    }
  }
  return null
}</code></pre>
<!-- /wp:code -->
<!-- wp:list {"ordered":true,"start":2} -->
<ol start="2">
 <!-- wp:list-item -->
 <li><code>getSnapshotBeforeUpdate</code> 取代了 <code>componetWillUpdate</code>，触发时间为 update 发生的时候，在 render 之后 dom 渲染之前返回一个值，作为 <code>componentDidUpdate</code> 的第三个参数</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 新的数据不断插入数据前面， 导致我正在看的数据向下走，如何保持可视区依旧是我之前看的数据呢？
getSnapshotBeforeUpdate(){
  return this.refs.wrapper.scrollHeight
}

componentDidUpdate(prevProps, prevState,preHeight) {
  // if(preHeight===200) return;
  this.refs.wrapper.scrollTop += this.refs.wrapper.scrollHeight-preHeight
}

&lt;div style={{height:"200px",overflow:"auto"}}} ref="wrapper"&gt;
  &lt;ul&gt;
    .........
  &lt;/ul&gt;
&lt;/div&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">react 中性能优化的方案</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">1. <code>shouldComponentUpdate</code></h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>控制组件自身或者子组件是否需要更新，尤其在子组件非常多的情况下， 需要进行优化。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">2. <code>PureComponent</code></h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { PureComponent } from 'react'</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p><code>PureComponent</code> 会帮你比较新 props 跟旧的 props，新的 state 和老的 state（值相等，或者对象含有相同的属性、且属性值相等），决定 <code>shouldcomponentUpdate</code> 返回 true 或者 false，从而决定要不要呼叫 render function</p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>注意：如果你的 state 或 props 『永远都会变』，那 <code>PureComponent</code> 并不会比较快，因为 <code>shallowEqual</code> 也需要花时间。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">九、React Hooks</h1>
<!-- /wp:heading -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>使用 hooks 理由</p>
 <!-- /wp:paragraph -->
 <!-- wp:list {"ordered":true} -->
 <ol>
  <!-- wp:list-item -->
  <li>高阶组件为了复用，导致代码层级复杂</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>生命周期的复杂</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>写成 functional 组件，无状态组件，因为需要状态，又改成了 class，成本高</li>
  <!-- /wp:list-item -->
 </ol>
 <!-- /wp:list -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading"><code>useState</code>（保存组件状态）</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const [state, setstate] = useState(initialState)</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading"><code>useEffect</code>（处理副作用）和 <code>useLayoutEffect</code>（同步执行副作用）</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><em>Function Component 不存在生命周期，所以不要把 Class Component 的生命周期概念搬过来试图对号入座。</em></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>useEffect(() =&gt; {
  // effect
  return () =&gt; {
    // cleanup
  };
}, [依赖的状态;空数组,表示不依赖])</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p><strong>不要对 <code>Dependencies</code> 撒谎，如果你明明使用了某个变量，却没有申明在依赖中，你等于向 React 撒了谎，后果就是，当依赖的变量改变时，<code>useEffect</code> 也不会再次执行, eslint 会报警告。</strong></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:paragraph -->
<p><em><code>Preview</code> 页面改造成函数式组件，在路径上从 id=1 切换到 id=2 也会自动重新加载，比 class 组件方便</em></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>let id = props.match.params.myid
useEffect(()=&gt;{
  axios.get(`/articles/${id}`).then(res =&gt; {
    settitle(res.data.title)
    setcontent(res.data.content)
    setcategory(res.data.category)
  })
},[id])</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading"><code>useEffect</code> 和 <code>useLayoutEffect</code> 有什么区别？</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>简单来说就是调用时机不同，<code>useLayoutEffect</code> 和原来 <code>componentDidMount</code> &amp; <code>componentDidUpdate</code> 一致，在 react 完成 DOM 更新后马上同步调用的代码，会阻塞页面渲染。而 <code>useEffect</code> 是会在整个页面渲染完才会调用的代码。</strong></p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>官方建议优先使用 <code>useEffect</code>
  <br>
  However, <strong>we recommend starting with useEffect first</strong> and only trying useLayoutEffect if that causes a problem.</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:paragraph -->
<p>在实际使用时如果想避免<strong>页面抖动</strong>（在 <code>useEffect</code> 里修改 DOM 很有可能出现）的话，可以把需要操作 DOM 的代码放在 <code>useLayoutEffect</code> 里。在这里做点 dom 操作，这些 dom 修改会和 react 做出的更改一起被一次性渲染到屏幕上，只有一次回流、重绘的代价。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading"><code>useCallback</code>（记忆函数）</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><em>防止因为组件重新渲染，导致方法被重新创建 ，起到缓存作用; 只有第二个参数 变化了，才重新声明一次</em></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>var handleClick = useCallback(()=&gt;{
  console.log(name)
},[name])
&lt;button onClick={()=&gt;handleClick()}&gt;hello&lt;/button&gt;

// 只有 name 改变后， 这个函数才会重新声明一次，
// 如果传入空数组， 那么就是第一次创建后就被缓存， 如果 name 后期改变了,拿到的还是老的 name。
// 如果不传第二个参数，每次都会重新声明一次，拿到的就是最新的 name。</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading"><code>useMemo</code>（记忆组件）</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><code>useCallback</code> 的功能完全可以由 <code>useMemo</code> 所取代，如果你想通过使用 <code>useMemo</code> 返回一个记忆函数也是完全可以的。</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>useCallback(fn, inputs) is equivalent to useMemo(() =&gt; fn, inputs).</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>唯一的区别是：<strong><code>useCallback</code> 不会执行第一个参数函数，而是将它返回给你，而 <code>useMemo</code> 会执行第一个函数并且将函数执行结果返回给你。</strong>所以在前面的例子中，可以返回 handleClick 来达到存储函数的目的。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>所以 <code>useCallback</code> 常用记忆事件函数，生成记忆后的事件函数并传递给子组件使用。而 <code>useMemo</code> 更适合经过函数计算得到一个确定的值，比如记忆组件。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading"><code>useRef</code>（保存引用值）</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const myswiper = useRef(null)
&lt;Swiper ref={myswiper}/&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading"><code>useReducer</code> 和 <code>useContext</code>（减少组件层级）</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React from 'react'
var GlobalContext= React.createContext()
// 注意此时的 reduecer 返回值是一个对象 {isShow:false,list:[]}

function App(props){
  let [state,dispatch] = useReducer(reducer,{isShow:true,list:[]})
  return (
    &lt;GlobalContext.Provider value={{dispatch}}&gt;
      &lt;div&gt;
        {state.isShow ? &lt;div&gt;我是选项卡&lt;/div&gt; : null}
        {props.children}
      &lt;/div&gt;
    &lt;/GlobalContext.Provider&gt;
  )
}

function Detail () {
  var { dispatch } = useContext(GlobalContext)
  useEffect(() =&gt; {
    //隐藏
    dispatch({
      type: "Hide",
      payload: false
    })
    return () =&gt; {
      //显示
      dispatch({
        type: "Show",
        payload: true
      })
    }
  }, [])
  return (
    &lt;div&gt;
      detail
    &lt;/div&gt;
  )
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">自定义 hooks</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>当我们想在两个函数之间共享逻辑时，我们会把它提取到第三个函数中。</strong></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>必须以 “use” 开头吗？必须如此。这个约定非常重要。不遵循的话，由于无法判断某个函数是否包含对其内部 Hook的调用，React 将无法自动检查你的 Hook 是否违反了 Hook 的规则。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">十、React 路由 v5</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">1. 什么是路由？</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>路由是根据不同的 url 地址展示不同的内容或页面。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>一个针对 React 而设计的路由解决方案、可以友好的帮你解决 React components 到 URl 之间的同步<strong>映射关系</strong>。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">2. 路由安装</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><a href="https://reactrouter.com/en/main">https://reactrouter.com/en/main</a></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>npm install react-router-dom@5</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">3. 路由使用</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(1) 路由方法导入</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(2) 定义路由以及重定向</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;HashRouter&gt;
  &lt;Switch&gt;
    &lt;Route path="/films" component={Films}/&gt;
    &lt;Route path="/cinemas" component={Cinemas}/&gt;
    &lt;Route path="/center" component={Center}/&gt;
    &lt;Redirect from="/" to="/films" /&gt;
    {/*
      &lt;Redirect from="/" to="/films" exact/&gt;
      &lt;Route path="*" component={NotFound}/&gt; 
    */}
  &lt;/Switch&gt;
&lt;/HashRouter&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>注意：
  <br>
  a. <code>&lt;Redirect from="/" to="/home" /&gt;</code></p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>b. <code>exact</code> 精确匹配（<code>Redirect</code> 即使使用了 <code>exact</code>，外面还要嵌套 <code>Switch</code> 来用）</p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>c. Warning: Hash history cannot PUSH the Same path; a new entry will not be added to the history stack. 这个警告只有在 hash 模式会出现</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(3) 嵌套路由</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;Switch&gt;
  &lt;Route path="/films/nowplaying" component={Nowplaying}/&gt;
  &lt;Route path="/films/comingsoon" component={Comingsoon}/&gt;
  &lt;Redirect from="/films" to="/films/nowplaying"/&gt;
&lt;/Switch&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(4) 路由跳转方式</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>声明式导航</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;NavLink to="/films" activeClassName="active"&gt;films&lt;/NavLink&gt;
&lt;NavLink to="/cinemas" activeClassName="active"&gt;cinemas&lt;/NavLink&gt;
&lt;NavLink to="/center" activeClassName="active"&gt;center&lt;/NavLink&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:list {"ordered":true,"start":2} -->
<ol start="2">
 <!-- wp:list-item -->
 <li>编程式导航</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>this.props.history.push(`/center`)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(5) 路由传参</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 推荐
&lt;Route path="/detail/:id" component={Detail} /&gt;
this.props.history.push('/detail/' + id)
this.props.match.params.id


// (1)
this.props.history.push({ pathname : '/user' ,query : { day: 'Friday'} })
this.props.location.query.day
// (2)
this.props.history.push({ pathname:'/user',state:{day : 'Friday' } })
this.props.location.state.day</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(6) 路由拦截</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;Route path="/center" render={()=&gt;isAuth()?&lt;Center/&gt;:&lt;Login/&gt;}/&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(7) withRouter的应用与原理</h3>
<!-- /wp:heading -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>You can get access to the <code>history</code> object's properties and the closest <code>&lt;Route&gt;</code>'s <code>match</code> via the <code>wi thRouter</code> higher-order component. <code>withRouter</code> will pass updated <code>match</code>, <code>location</code>, and <code>history</code> props to the wrapped component whenever it renders.</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { withRouter } from "react-router-dom";
withRouter(MyComponent);
withRouter(connect(...)(MyComponent))

const WithFilmItem = withRouter(FilmItem)</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">4.项目注意</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(1) 反向代理</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><a href="https://create-react-app.dev/docs/proxying-api-requests-in-development">https://create-react-app.dev/docs/proxying-api-requests-in-development</a></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><code>npm install http-proxy-middleware --save</code></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// src/setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
  app.use(
    '/api',
    createProxyMiddleware({
      target: 'http://localhost:5000',
      changeOrigin: true,
    })
  );
};</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p><strong>setupProxy.js 文件更新完要重启服务器</strong></p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>注意：无需在任何地方导入此文件。启动开发服务器时会自动注册它。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(2) css module</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><a href="https://create-react-app.dev/docs/adding-a-css-modules-stylesheet">https://create-react-app.dev/docs/adding-a-css-modules-stylesheet</a></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><strong>CSS 文件名改成 以 <code>.module.css</code> 结尾 <code>filename.module.css</code></strong></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 导入
import style from 'filename.module.css'
// 使用
&lt;NavLink to="main" activeClassName={style.active}&gt;Main&lt;/NavLink&gt;


全局
:global(.active){

}</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>注意：对标签选择器无效</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">十一、Flux 与 Redux</h1>
<!-- /wp:heading -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>Flux 是一种架构思想，专门解决软件的结构问题。它跟 MVC 架构是同一类东西，但是更加简单和清晰。Flux存在多种实现(至少15种)
  <br>
  https://github.com/voronianski/flux-comparison</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:paragraph -->
<p>Facebook Flux 是用来构建客户端 Web 应用的应用架构。它利用<strong>单向数据流</strong>的方式来组合 React 中的视图组件。它更像一个模式而不是一个正式的框架，开发者不需要太多的新代码就可以快速的上手 Flux。
 <br>
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fp6-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2F4e1943553e144d4797f6654b02e4e7f4~tplv-k3u1fbpfcp-watermark.image%3F&amp;size=m" alt="React17-quanjiatong07.png"></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>Redux 最主要是用作应用状态的管理。简言之，Redux 用一个单独的常量状态树（state 对象）保存这一整个应用的状态，这个对象不能直接被改变。当一些数据变化了，一个新的对象就会被创建（使用 actions 和 reducers），这样就可以进行数据追踪，实现时光旅行。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">1. redux 使用的三大原则</h2>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li><strong>state 以单一对象存储在 store 对象中</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>state 只读（每次都返回有个新的对象）</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><strong>使用纯函数 reducer 执行 state 更新</strong></li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">2. redux 工作流</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":287,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong08.png&amp;size=m" alt="" class="wp-image-287">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">同步</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":267,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FreduxSync-1024x768.gif&amp;size=m" alt="" class="wp-image-267">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">异步</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":658,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2Fredux-async-1-1024x768.gif&amp;size=m" alt="" class="wp-image-658">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">3. 与 react 绑定后使用 redux 实现案例</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// store.js
import { createStore } from 'redux'

const reducer = (state = 'defaultState', data = {}) =&gt; {
  let { type, payload } = data
  switch(type) {
    case 'changetitle':
      return payload
    default:
      return state
  }
}
const store = createStore(reducer)

export default store</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import store from 'store'

store.dispath({
  type: 'changetitle',
  payload: res.data.film.name
})

componentDidMount() {
  store.subscribe(()=&gt;{
    this.setStaet({
      title: store.getState()
    })
  })
}</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { SHOW_TABBAR, HIDE_TABBAR } '../store/type'</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">4. redux 原理解析</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>store 是通过 <code>createStore</code> 创建出来的，所以它的结构：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export const createStore = function(reducer, initialState) {
  ...
  return {
    // dispath 用于 action 的分发，改变 store 里面的 state（currentState = reducer(currentState, action)）
    // 并在内部遍历 subcribe 注册的监听器 subcribe，注册 listener，store 里面 state 发生变化后
    // 执行该 listener getState，取 store 里面的 state
  ...
  }
}

function createStore(reducer) {
  var list = []
  var state = reducer()
  function subscribe(callback) {
    list.push(callback)
  }
  function dispatch(data) {
    state = reducer(state, data)
    for(var i in list) {
        list[i]()
    }
  }
  function getState() {
    return state
  }
  return {
    subscribe,
    dispatch,
    getState
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">5. reducer 扩展</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>如果如果不同的 action 所处理的属性之间没有联系，我们可以把 Reducer 函数拆分。不同的函数负责处理不同属性，最终把它们合并成一个大的 Reducer 即可。</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import {combineReducers} from "redux";
const reducer = combineReducers({
  a: functionA,
  b: functionB,
  c: functionC
})

// 访问：
(state)=&gt;{
  return {
    kerwinstate:state.a (不同的命名空间)
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">6. redux 中间件</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在 redux 里，action 仅仅是携带了数据的普通 js 对象。action creator 返回的值是这个 action 类型的对象。然后通过 <code>store.dispatch()</code> 进行分发。同步的情况下一切都很完美，但是 reducer 无法处理异步的情况。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>那么我们就需要在 action 和 reducer 中间架起一座桥梁来处理异步。这就是 middleware。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(1). 中间件的由来与原理、机制</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>export default function thunkMiddleware({ dispatch, getState }) {
  return next =&gt; action =&gt;
    typeof action === 'function' ? action(dispatch, getState) : next(action);
}</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>这段代码的意思是，中间件这个桥梁接受到的参数 action，如果不是 function 则和过去一样直接执行 next 方法(下一步处理)，相当于中间件没有做任何事。如果 action 是 function，则先执行 action，action 的处理结束之后，再在 action 的内部调用 dispatch。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(2). 常用异步中间件</h3>
<!-- /wp:heading -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">redux-thunk (store.dispatch 参数可以是一个 function)</h4>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// store.js
import thunk from 'redux-thunk';
import {applyMiddleware} from "redux";
const store = createStore(fetchReducer, applyMiddleware(thunk));

const getComingSoon = ()=&gt;{
  //进行异步请求
  return (dispatch,store)=&gt;{

  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":4} -->
<h4 class="wp-block-heading">redux-promise (store.dispatch 参数可以是一个 promise 对象)</h4>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import promiseMiddleware from 'redux-promise';
const store = createStore(fetchReducer, applyMiddleware(thunk,promiseMiddleware));

const getComingSoon = ()=&gt;{
  //进行异步请求
  return axios.get(`****`).then(res=&gt;{
    return {
      type:"cominglist",
      info:res.data.data
    }
  })
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">7. Redux DevTools Extension</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><a href="https://github.com/zalmoxisus/redux-devtools-extension">https://github.com/zalmoxisus/redux-devtools-extension</a></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// redux/store.js
import { createStore, compose} from 'redux'
import reducer from './reducer'

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(reducer, /* preloadedState, */ composeEnhancers())

export default store</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">十二、react-redux</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">1. 介绍</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><a href="https://github.com/reactjs/react-redux">https://github.com/reactjs/react-redux</a></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">2. 容器组件与 UI 组件</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(1) UI组件</h3>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>只负责 UI 的呈现，不带有任何业务逻辑</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>没有状态（即不使用 this.state 这个变量）</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>所有数据都由参数（this.props）提供</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>不使用任何 Redux 的 API</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(2) 容器组件</h3>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>负责管理数据和业务逻辑，不负责 UI 的呈现</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>带有内部状态</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>使用 Redux 的 API</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">3. Provider 与 connect</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">（1）React-Redux 提供 Provider 组件，可以让容器组件拿到 state。</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React from 'react'
import ReactDOM from 'react-dom'

import { Provider } from 'react-redux'
import store from './store'

import App from './App'

const rootElement = document.getElementById('root')
ReactDOM.render(
  &lt;Provider store={store}&gt;
    &lt;App /&gt;
  &lt;/Provider&gt;,
  rootElement
)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">（2）React-Redux 提供 connect 方法，用于从 UI 组件生成容器组件。connect 的意思，就是将这两种组件连起来。</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { connect } from 'react-redux'
import { increment, decrement, reset } from './actionCreators'

// const Counter = ...

const mapStateToProps = (state /*, ownProps*/) =&gt; {
  return {
    counter: state.counter
  }
}
const mapDispatchToProps = { increment, decrement, reset }

// connect(将来给孩子传的属性, 将来给孩子传的回调函数)
export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter)</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">4. HOC 与 context 通信在 react-redux 底层中的应用</h2>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>connect 是 HOC，高阶组件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>Provider 组件，可以让容器组件拿到 state ，使用了 context</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">5. 高阶组件构建与应用</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>HOC 不仅仅是一个方法，确切说应该是一个组件工厂，获取低阶组件，生成高阶组件。</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>代码复用，代码模块化</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>增删改 props</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>渲染劫持</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// Child.js

// 高阶函数
function Control(wrappedComponent) {
  return class MyControl extends React.Component {
    render(){
      if(!this.props.data) {
        return &lt;div&gt;loading...&lt;/div&gt;
      }
      return &lt;wrappedComponent {...props} /&gt;
    }
  }
}

class MyComponent extends React.Component {
  render(){
    return &lt;div&gt;{this.props.data}&lt;/div&gt;
  }
}
export default Control(MyComponent); //高阶组件

// Parent.js
import MyControlComponent from "./Child"
&lt;MyControlComponent data={this.state.value}/&gt;

// 在父级传入 data 是 null 的时候，这一块儿就只会显示 loading...
// 不会显示组件的具体内容，如果 data 不为 null，就显示真实组件信息</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { useEffect } from 'react'

function NotFound (props) {

  useEffect(() =&gt; {
    console.log(props)
    return () =&gt; {
    }
  }, [props])

  return (
    &lt;div&gt;404 NotFound&lt;/div&gt;
  )
}
function joeConnect (cb, obj) {
  var value = cb()
  return (MyComponent) =&gt; {
    return (props) =&gt; {
      return (
        &lt;div style={{ color: 'red' }}&gt;
          &lt;MyComponent {...value} {...props} {...obj}&gt;&lt;/MyComponent&gt;
        &lt;/div&gt;
      )
    }
  }
}

export default joeConnect(() =&gt; {
  return {
    a: 1,
    b: 1
  }
}, {
  aa () { },
  bb () { }
}
)(NotFound)</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">6. Redux 持久化</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import {persistStore, persistReducer} from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';

const persistConfig = {
  key: 'kerwin',
  storage: storage,
  // localStorage: import storage from 'redux-persist/lib/storage'
  // sessionStorage: import storageSession from 'redux-persist/lib/storage/session'
  stateReconciler: autoMergeLevel2
  // 控制在本地存储中，新老状态怎么合并，覆盖？或者合并？
};
// 改造 reducer
const myPersistReducer = persistReducer(persistConfig, reducer)

// 改造 store
export const persistor = persistStore(store)

// 改造根组件
import {persistor} from './Store'
import {PersistGate} from 'redux-persist/lib/integration/react';

&lt;PersistGate loading={null} persistor={persistor}&gt;
  ...
&lt;/PersistGate&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">十三、UI 组件库</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Ant Design 是一个致力于提升『用户』和『设计者』使用体验的设计语言 ；旨在统一中台项目的前端 UI 设计，屏蔽不必要的设计差异和实现成本，解放设计和前端的研发资源； 包含很多设计原则和配套的组件库。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">1. ant-design （PC端）</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><a href="https://ant.design/index-cn">https://ant.design/index-cn</a></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>镜像库，快 https://ant-design.gitee.io/index-cn</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">2. antd-mobile （移动端）</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><a href="https://mobile.ant.design/zh">https://mobile.ant.design/zh</a></p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">十四、Immutable</h1>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>var obj = { /* 一个复杂结构的对象 */ };
doSomething(obj);
// 上面的函数之行完后，此时的 obj 还是最初的那个 obj 吗？

// deepCopy?</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">1. Immutable.js 介绍</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>https://github.com/immutable-js/immutable-js
 <br>
 每次修改一个 Immutable 对象时都会创建一个新的不可变的对象，在新对象上操作并不会影响到原对象的数据。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>这个库的实现是深拷贝还是浅拷贝？</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">2. 深拷贝与浅拷贝的关系</h2>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li><code>var arr = { } ; arr2 = arr</code></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>Object.assign()</code> 只是一级属性复制，比浅拷贝多拷贝了一层而已</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>const obj1 = JSON.parse(JSON.stringify(obj))</code> 数组，对象都好用的方法(缺点: 不能有 <code>undefined</code>)</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:heading -->
<h2 class="wp-block-heading">3. Immutable 优化性能的方式</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Immutable 实现的原理是 Persistent Data Structure（持久化数据结构），也就是使用旧数据创建新数据时，要保证旧数据同时可用且不变。同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗，Immutable 使用了 Structural Sharing（结构共享），即如果对象树中一个节点发生变化，只修改这个节点和受它影响的父节点，其它节点则进行共享。
 <br>
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fp9-juejin.byteimg.com%2Ftos-cn-i-k3u1fbpfcp%2Fda6ad4570bc4404e959a2d618bcef6b1~tplv-k3u1fbpfcp-watermark.image%3F&amp;size=m" alt="React17-quanjiatong11.gif"></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">4. Immutable 中常用类型（Map，List）</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">1. Map</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const { Map } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = map1.set('b', 50);
map1.get('b') + " vs. " + map2.get('b'); // 2 vs. 50</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { Map } from 'immutable'
let a = Map({
  select: 'users',
  filter: Map({ name: 'Cam' })
})
let b = a.set('select', 'people')

a === b // false
a.get('filter') === b.get('filter') // true</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p><strong>挺深：如果上述 select 属性给一个组件用，因为此值改变了，<code>shouldComponentUpdate</code> 应该返回 <code>treu</code>，而 filter 属性给另一个组件用，通过判断并无变化，<code>shouldComponentUpdate</code> 应该返回 <code>false</code>，此组件就避免了重复进行 diff 对比</strong></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">2. List</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const { List } = require('immutable');
const list1 = List([ 1, 2 ]);
const list2 = list1.push(3, 4, 5);
const list3 = list2.unshift(0);
const list4 = list1.concat(list2, list3);
assert.equal(list1.size, 2);
assert.equal(list2.size, 5);
assert.equal(list3.size, 6);
assert.equal(list4.size, 13);
assert.equal(list4.get(0), 1);
// push, set, unshift or splice 都可以直接用，返回一个新的 immutable 对象</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">3. merge, concat</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const { Map, List } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3, d: 4 });
const map2 = Map({ c: 10, a: 20, t: 30 });
const obj = { d: 100, o: 200, g: 300 };
const map3 = map1.merge(map2, obj);
// Map { a: 20, b: 2, c: 10, d: 100, t: 30, o: 200, g: 300 }
const list1 = List([ 1, 2, 3 ]);
const list2 = List([ 4, 5, 6 ]);
const array = [ 7, 8, 9 ];
const list3 = list1.concat(list2, array);
// List [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">4. toJS</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const { Map, List } = require('immutable');
const deep = Map({ a: 1, b: 2, c: List([ 3, 4, 5 ]) });
console.log(deep.toObject()); // { a: 1, b: 2, c: List [ 3, 4, 5 ] }
console.log(deep.toArray()); // [ 1, 2, List [ 3, 4, 5 ] ]
console.log(deep.toJS()); // { a: 1, b: 2, c: [ 3, 4, 5 ] }
JSON.stringify(deep); // '{"a":1,"b":2,"c":[3,4,5]}'</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">5. fromJS</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>const { fromJS } = require('immutable');
const nested = fromJS({ a: { b: { c: [ 3, 4, 5 ] } } });
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ] } } }

const nested2 = nested.mergeDeep({ a: { b: { d: 6 } } });
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 6 } } }

console.log(nested2.getIn([ 'a', 'b', 'd' ])); // 6
// 如果取一级属性 直接通过get方法，如果取多级属性 getIn(["a","b","c"]])

// setIn 设置新的值
const nested3 = nested2.setIn([ 'a', 'b', 'd' ], "kerwin");
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: "kerwin" } } }

// updateIn 回调函数更新
const nested3 = nested2.updateIn([ 'a', 'b', 'd' ], value =&gt; value + 1);
console.log(nested3);
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 7 } } }

const nested4 = nested3.updateIn([ 'a', 'b', 'c' ], list =&gt; list.push(6));
// Map { a: Map { b: Map { c: List [ 3, 4, 5, 6 ], d: 7 } } }</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">5. Immutable + Redux 的开发方式</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// reducer.js
const initialState = fromJS({
  category:"",
  material:""
})
const reducer = (prevstate = initialState,action={})=&gt;{
  let {type,payload} = action
  switch(type){
    case GET_HOME:
      var newstate =prevstate.set("category",fromJS(payload.category))
      var newstate2 =newstate.set("material",fromJS(payload.material))
      return newstate2;
    default:
      return prevstate
  }
}

// home.js
const mapStateToProps = (state)=&gt;{
  return {
    category:state.homeReducer.getIn(["category"]) || Map({}),
    material:state.homeReducer.getIn(["material"]) || Map({})
  }
}

this.props.category.get("相关属性")
this.props.category.toJS() // 或者转成普通对象</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">6. 缺点</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>容易跟原生混淆</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>文档与调试不方便</p>
<!-- /wp:paragraph -->]]></description><guid isPermaLink="false">/archives/react17-%E5%85%A8%E5%AE%B6%E6%A1%B6%EF%BC%882%EF%BC%89</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Tue, 21 May 2024 06:16:25 GMT</pubDate></item><item><title><![CDATA[React17-全家桶（1）]]></title><link>https://blog.qiaofugui.cn/archives/react17-%E5%85%A8%E5%AE%B6%E6%A1%B6%EF%BC%881%EF%BC%89</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=React17-%E5%85%A8%E5%AE%B6%E6%A1%B6%EF%BC%881%EF%BC%89&amp;url=/archives/react17-%E5%85%A8%E5%AE%B6%E6%A1%B6%EF%BC%881%EF%BC%89" width="1" height="1" alt="" style="opacity:0;"><!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">一、React 介绍</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">1. React 起源与发展</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>React 起源于 Facebook 的内部项目，因为该公司对市场上所有 JavaScript MVC 框架，都不满意，就决定自己写一套，用来架设 Instagram 的网站。做出来以后，发现这套东西很好用，就在2013年5月开源了。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">2. React 与传统 MVC 的关系</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>轻量级的视图层库！<em>A JavaScript library for building user interfaces</em></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>React 不是一个完整的 MVC 框架，最多可以认为是 MVC 中的 V（View），甚至 React 并不非常认可 MVC 开发模式；React 构建页面 UI 的库。可以简单地理解为，React 将界面分成了各个独立的小块，每一个块就是组件，这些组件之间可以组合、嵌套，就成了我们的页面。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">3. React 的特性</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":294,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong01.png&amp;size=m" alt="" class="wp-image-294">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">4. 虚拟DOM</h2>
<!-- /wp:heading -->
<!-- wp:image {"id":295,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong02.png&amp;size=m" alt="" class="wp-image-295">
</figure>
<!-- /wp:image -->
<!-- wp:image {"id":289,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FReact17-quanjiatong03.png&amp;size=m" alt="" class="wp-image-289">
</figure>
<!-- /wp:image -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">二、create-react-app</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>全局安装 create-react-app</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>npm install -g create-react-app</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>创建一个项目</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>create-react-app 项目名称</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>如果不想全局安装，可以直接使用 npx</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>npx create-react-app 项目名称</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>这需要等待一段时间，这个过程实际上会安装三个东西</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>react: react 的顶级库</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>react-dom: 因为 react 有很多的运行环境，比如 app 端的 react-native, 我们要在 web 上运行就使用 react-dom</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>react-scripts: 包含运行和打包 react 应用程序的所有脚本及配置</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:paragraph -->
<p>出现下面的界面，表示创建项目成功:</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Success! Created 项目名称 at /dir/项目目录
Inside that directory, you can run several commands:

  npm start
    Starts the development server.

  npm run build
    Bundles the app into static files for production.

  npm test
    Starts the test runner.

  npm run eject
    Removes this tool and copies build dependencies, configuration files
    and scripts into the app directory. If you do this, you can’t go back!

We suggest that you begin by typing:

    cd 项目名称
    npm start

Happy hacking!</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>根据上面的提示，通过 <code>cd 项目名称</code> 命令进入目录并运行 <code>npm start</code> 即可运行项目。
 <br>
 生成项目的目录结构如下：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>├── README.md         使用方法的文档
├── node_modules      所有的依赖安装的目录
├── package-lock.json 锁定安装时的包的版本号,保证团队的依赖能保证一致。
├── package.json
├── public            静态公共目录
└── src               开发用的源代码目录</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p><strong>常见问题：</strong>
  <br>
  npm 安装失败</p>
 <!-- /wp:paragraph -->
 <!-- wp:list -->
 <ul>
  <!-- wp:list-item -->
  <li>切换为 npm 镜像为淘宝镜像</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>使用 yarn，如果本来使用 yarn 还要失败，还得把 yarn 的源切换到国内</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>如果还没有办法解决，请删除 <code>node_modules</code> 及 <code>package-lock.json</code> 然后重新执行 <code>npm install</code> 命令</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>再不能解决就删除 <code>node_modules</code> 及 <code>package-lock.json</code> 的同时清除 npm 缓存 <code>npm cache clean --force</code> 之后再执行 <code>npm install</code> 命令</li>
  <!-- /wp:list-item -->
 </ul>
 <!-- /wp:list -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">三、编写一个 react 应用程序</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>react 开发需要引入多个依赖文件：react.js、react-dom.js，分别又有开发版本和生产版本，create-react-app 里已经帮我们把这些东西都安装好了。把通过 CRA 创建的工程目录下的 src 目录清空，然后在里面重新创建一个 index.js. 写入以下代码：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 从 react 的包当中引入了 React。只要你要写 React.js 组件就必须引入React, 因为 react 里有一种语法叫 JSX，要写JSX，就必须引入 React
import React from 'react'
// ReactDOM 可以帮助我们把 React 组件渲染到页面上去，没有其它的作用了。它是从 react-dom 中引入的，而不是从 react 引入。
import ReactDOM from 'react-dom'

// ReactDOM 里有一个 render 方法，功能就是把组件渲染并且构造 DOM 树，然后插入到页面上某个特定的元素上
ReactDOM.render(
  // 这里就比较奇怪了，它并不是一个字符串，看起来像是纯 HTML 代码写在 JavaScript 代码里面。这并不是合法的JavaScript 代码, “在 JavaScript 写的标签的” 语法叫 JSX-JavaScript XML。
  &lt;h1&gt;欢迎进入 React 的世界&lt;/h1&gt;,
  // 渲染到哪里
  document.getElementById('root')
)</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p><strong>注意：</strong>
  <br>
  <code>&lt;React.StrictMode&gt;</code> 目前有助于：</p>
 <!-- /wp:paragraph -->
 <!-- wp:list {"ordered":true} -->
 <ol>
  <!-- wp:list-item -->
  <li>识别不安全的生命周期</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>关于使用过时字符串 ref API 的警告</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>检测意外的副作用</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>检测过时的 context API</li>
  <!-- /wp:list-item -->
 </ol>
 <!-- /wp:list -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">四、JSX 语法与组件</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">1. JSX 语法</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>JSX 将 HTML 语法直接加入到 JavaScript 代码中，再通过翻译器转换到纯 JavaScript 后由浏览器执行。
 <br>
 在实际开发中，JSX 在产品打包阶段都已经编译成纯 JavaScript，不会带来任何副作用，反而会让代码更加直观并易于维护。 编译过程由Babel 的 JSX 编译器实现。
 <br>
 https://reactjs.org/docs/hello-world.html</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>要明白 JSX 的原理，需要先明白如何用 JavaScript 对象来表现一个 DOM 元素的结构?
 <br>
 看下面的 DOM 结构</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;div class='app' id='appRoot'&gt;
  &lt;h1 class='title'&gt;欢迎进入 React 的世界&lt;/h1&gt;
  &lt;p&gt;
    React.js 是一个帮助你构建页面 UI 的库
  &lt;/p&gt;
&lt;/div&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>上面这个 HTML 所有的信息我们都可以用 JavaScript 对象来表示：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>{
  tag: 'div',
  attrs: { className: 'app', id: 'appRoot'},
  children: [
    {
      tag: 'h1',
      attrs: { className: 'title' },
      children: ['欢迎进入 React 的世界']
    },
    {
      tag: 'p',
      attrs: null,
      children: ['React.js 是一个构建页面 UI 的库']
    }
  ]
}</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>但是用 JavaScript 写起来太长了，结构看起来又不清晰，用 HTML 的方式写起来就方便很多了。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>于是 React.js 就把 JavaScript 的语法扩展了一下，让 JavaScript 语言能够支持这种直接在 JavaScript 代码里面编写类似 HTML 标签结构的语法，这样写起来就方便很多了。编译的过程会把类似 HTML 的 JSX结构转换成 JavaScript 的对象结构。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><strong>下面代码</strong></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React from 'react'
import ReactDOM from 'react-dom'

class App extends React.Component {
  render () {
    return (
      &lt;div className='app' id='appRoot'&gt;
      &lt;h1 className='title'&gt;欢迎进入 React 的世界&lt;/h1&gt;
      &lt;p&gt;
        React.js 是一个构建页面 UI 的库
      &lt;/p&gt;
      &lt;/div&gt;
    )
  }
}

ReactDOM.render(
  &lt;App /&gt;,
  document.getElementById('root')
)</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>编译之后将得到这样的代码：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React from 'react'
import ReactDOM from 'react-dom'

class App extends React.Component {
  render () {
    return (
      React.createElement(
        "div",
        {
          className: 'app',
          id: 'appRoot'
        },
        React.createElement(
          "h1",
          { className: 'title' },
          "欢迎进入React的世界"
        ),
        React.createElement(
          "p",
          null,
          "React.js 是一个构建页面 UI 的库"
        )
      )
    )
  }
}

ReactDOM.render(
  React.createElement(App),
  document.getElementById('root')
)</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p><code>React.createElement</code> 会构建一个 JavaScript 对象来描述你 HTML 结构的信息，包括标签名、属性、还有子元素等, 语法为：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>React.createElement(
  type,
  [props],
  [...children]
)</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>所谓的 JSX 其实就是 JavaScript 对象，所以使用 React 和 JSX 的时候一定要经过编译的过程：</p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>JSX — 使用 react 构造组件，bable 进行编译 —&gt; JavaScript 对象 — ReactDOM.render() —&gt; DOM 元素 —&gt; 插入页面</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">class 组件</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>ES6的加入让JavaScript直接支持使用class来定义一个类，react创建组件的方式就是使用的类的继承，ES6 class 是目前官方推荐的使用方式，它使用了ES6标准语法来构建，看以下代码：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React from 'react'
import ReactDOM from 'react-dom'

class App extends React.Component {
  render () {
    return (
      &lt;h1&gt;欢迎进入 React 的世界&lt;/h1&gt;
    )
  }
}

ReactDOM.render(
  &lt;App /&gt;,
  document.getElementById('root')
)</code></pre>
<!-- /wp:code -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>es6 class 组件其实就是一个构造器,每次使用组件都相当于在实例化组件，像这样：</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React from 'react'
import ReactDOM from 'react-dom'

class App extends React.Component {
  render () {
    return (
      &lt;h1&gt;欢迎进入 {this.props.name} 的世界&lt;/h1&gt;
    )
  }
}

const app = new App({
  name: 'react'
}).render()

ReactDOM.render(
  app,
  document.getElementById('root')
)</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">3. 函数式组件</h2>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React from 'react'
import ReactDOM from 'react-dom'

const App = (props) =&gt; &lt;h1&gt;欢迎进入 React 的世界&lt;/h1&gt;
// or
function App(){
  return &lt;h1&gt;欢迎进入 React 的世界&lt;/h1&gt;
}

ReactDOM.render(
  // React组件的调用方式
  &lt;App /&gt;,
  document.getElementById('root')
)</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>这样一个完整的函数式组件就定义好了。但要注意！注意！注意！<strong>组件名必须大写</strong>，否则报错</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">4. 组件的样式</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">行内样式</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>想给虚拟dom添加行内样式，需要使用表达式传入样式对象的方式来实现：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 注意这里的两个括号，第一个表示我们在要 JSX 里插入 JS 了，第二个是对象的括号
&lt;p style={{color:'red', fontSize:'14px'}}&gt;Hello world&lt;/p&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>行内样式需要写入一个样式对象，而这个样式对象的位置可以放在很多地方，例如 render 函数里、组
 <br>
 件原型上、外链 js 文件中</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">使用 class</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>React 推荐我们使用行内样式，因为 React 觉得每一个组件都是一个独立的整体</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>其实我们大多数情况下还是大量的在为元素添加类名，但是需要注意的是， class 需要写成</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>className （因为毕竟是在写类 js 代码，会收到 js 规则的现在，而 class 是关键字）</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;p className="hello"&gt;Hello world&lt;/p&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p><strong>注意：</strong>
  <br>
  <code>class ==&gt; className</code>, <code>for ==&gt; htmlFor(label)</code></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">5. 事件处理</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">5.1、绑定事件</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>采用 on + 事件名 的方式来绑定一个事件，注意，这里和原生的事件是有区别的，原生的事件全是小写 <code>onclick</code>, React 里的事件是驼峰 <code>onClick</code>，<strong>React 的事件并不是原生事件，而是合成事件</strong>。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">5.2、事件 handler 的写法</h3>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>直接在 render 里写行内的箭头函数（不推荐）</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>在组件内使用箭头函数定义一个方法（推荐）</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>直接在组件内定义一个非箭头函数的方法，然后在 render 里直接使用 <code>onClick={this.handleClick.bind(this)}</code>（不推荐）</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>直接在组件内定义一个非箭头函数的方法，然后在 <code>constructor</code> 里 <code>bind(this)</code>（推荐）</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">5.3、Event 对象</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>和普通浏览器一样，事件 handler 会被自动传入一个 event 对象，这个对象和普通的浏览器 event 对象所包含的方法和属性都基本一致。不同的是 React 中的 event 对象并不是浏览器提供的，而是它自己内部所构建的。它同样具 <code>event.stopPropagation</code>、<code>event.preventDefault</code> 这种常用的方法</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">6. Ref的应用</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">6.1、给标签设置 <code>ref="username"</code></h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>通过这个获取 <code>this.refs.username</code>, ref 可以获取到应用的真实 dom</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">6.2、给组件设置 <code>ref="username"</code></h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>通过这个获取 <code>this.refs.username</code>, ref 可以获取到组件对象</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">6.3、新的写法</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>Ref = React.createRef()

&lt;div ref={this.myRef}&gt;hello&lt;/div&gt;

// 访问 this.myRef.current</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">五、组件的数据挂载方式</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">1. 状态（state）</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>状态就是组件描述某种显示情况的数据，由组件自己设置和更改，也就是说由组件自己维护，使用状态的目的就是为了在不同的状态下使组件的显示不同（自己管理）</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(1) 定义 state</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>第一种方式</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component } from 'react'
import ReactDOM from 'react-dom'

class App extends Component {
  state = {
    name: 'React',
    isLiked: false
  }

  render () {
    return (
      &lt;div&gt;
        &lt;h1&gt;欢迎来到{this.state.name}的世界&lt;/h1&gt;
        &lt;button&gt;
          {
            this.state.isLiked ? '❤取消' : '❤收藏'
          }
        &lt;/button&gt;
      &lt;/div&gt;
    )
  }
}

ReactDOM.render(
  &lt;App/&gt;,
document.getElementById('root')
)</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>另一种方式</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component } from 'react'
import ReactDOM from 'react-dom'

class App extends Component {
  constructor() {
    super()
    this.state = {
      name: 'React',
      isLiked: false
    }
  }

  render () {
    return (
      &lt;div&gt;
        &lt;h1&gt;欢迎来到{this.state.name}的世界&lt;/h1&gt;
        &lt;button&gt;
          {
            this.state.isLiked ? '❤取消' : '❤收藏'
          }
        &lt;/button&gt;
      &lt;/div&gt;
    )
  }
}

ReactDOM.render(
  &lt;App/&gt;,
document.getElementById('root')
)</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p><code>this.state</code> 是纯 js 对象，在 vue 中，data 属性是利用 <code>Object.defineProperty</code> 处理过的，更改 data 的数据的时候会触发数据的 <code>getter</code> 和 <code>setter</code>，但是 React 中没有做这样的处理，如果直接更改的话，react 是无法得知的，所以，需要使用特殊的更改状态的方法 <code>setState</code>。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(2) setState</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>isLiked 存放在实例的 state 对象当中，组件的 render 函数内，会根据组件的 state 的中的 isLiked 不同显示 “取消” 或 “收藏” 内容。下面给 button 加上了点击的事件监听。</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component } from 'react'
import ReactDOM from 'react-dom'

class App extends Component {
  constructor() {
    super()
    this.state = {
      name: 'React',
      isLiked: false
    }
  }

  handleBtnClick = () =&gt; {
    this.setState({
      isLiked: !this.state.isLiked
    })
  }

  render () {
    return (
      &lt;div&gt;
        &lt;h1&gt;欢迎来到{this.state.name}的世界&lt;/h1&gt;
        &lt;button onClick={this.handleBtnClick}&gt;
          {
            this.state.isLiked ? '❤取消' : '❤收藏'
          }
        &lt;/button&gt;
      &lt;/div&gt;
    )
  }
}

ReactDOM.render(
  &lt;App/&gt;,
document.getElementById('root')
)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading"><code>setState</code> 有两个参数</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>第一个参数可以是对象，也可以是方法 return 一个对象，我们把这个参数叫做 updater</p>
<!-- /wp:paragraph -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>参数是对象</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>this.setState({
  isLiked: !this.state.isLiked
})</code></pre>
<!-- /wp:code -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>参数是方法</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>this.setState((prevState, props) =&gt; {
  return {
    isLiked: !prevState.isLiked
  }
})</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>注意的是这个方法接收两个参数，第一个是上一次的 state, 第二个是 props</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading"><code>setState</code> 是异步的</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>所以想要获取到最新的 state，没有办法获取，就有了第二个参数，这是一个可选的回调函数</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>this.setState((prevState, props) =&gt; {
  return {
    isLiked: !prevState.isLiked
  }
}, () =&gt; {
  console.log('回调里的',this.state.isLiked)
})

console.log('setState外部的',this.state.isLiked)</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">2. 属性（props）</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>props 是正常是外部传入的，组件内部也可以通过一些方式来初始化的设置，属性不能被组件自己更改，但是你可以通过父组件主动重新渲染的方式来传入新的 props</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>属性是描述性质、特点的，组件自己不能随意更改。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>之前的组件代码里面有 props 的简单使用，总的来说，在使用一个组件的时候，可以把参数放在标签的属性当中，所有的属性都会作为组件 props 对象的键值。通过箭头函数创建的组件，需要通过函数的参数来接收 props :</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>在组件上通过 <code>key=value</code> 写属性，通过 <code>this.props</code> 获取属性，这样组件的可复用性提高了。</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>注意在传参数时候，如果写成 <code>isShow="true"</code> 那么这是一个字符串 如果写成 <code>isShow={true}</code> 这个是布尔值</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li><code>{...对象}</code> 展开赋值</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>默认属性值</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>*.defaultProps = {
}
  static defaultProps = {
    myname:"默认的myname",
    myshow:true
}</code></pre>
<!-- /wp:code -->
<!-- wp:list {"ordered":true,"start":5} -->
<ol start="5">
 <!-- wp:list-item -->
 <li><code>prop-types</code> 属性验证</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import propTypes from "prop-types";
*.propTypes={
  name:propTypes.string,
  age:propTypes.number
}

static propTypes={
  myname:propTypes.string,
  myshow:propTypes.bool
}</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">3. 属性 vs 状态</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>相似点：都是纯 js 对象，都会触发 render 更新，都具有确定性（状态/属性相同，结果相同）</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><strong>不同点：</strong></p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>属性能从父组件获取，状态不能</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>属性可以由父组件修改，状态不能</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>属性能在内部设置默认值，状态也可以，设置方式不一样</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>属性不在组件内部修改，状态要在组件内部修改</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>属性能设置子组件初始值，状态不可以</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>属性可以修改子组件的值，状态不可以</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>state 的主要作用是用于组件保存、控制、修改自己的可变状态。 state 在组件内部初始化，可以被组件自身修改，而外部不能访问也不能修改。你可以认为 state 是一个局部的、只能被组件自身控制的数据源。 state 中状态可以通过 <code>this.setState</code> 方法进行更新， <code>setState</code> 会导致组件的重新渲染。</p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>props 的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数，组件内部无法控制也无法修改。除非外部组件主动传入新的 props ，否则组件的 props 永远保持不变。</p>
 <!-- /wp:paragraph -->
 <!-- wp:paragraph -->
 <p>没有 state 的组件叫无状态组件（stateless component），设置了 state 的叫做有状态组件（stateful component）。因为状态会带来管理的复杂性，我们尽量多地写无状态组件，尽量少地写有状态的组件。这样会降低代码维护的难度，也会在一定程度上增强组件的可复用性。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">4. 渲染数据</h2>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>条件渲染</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>{
  condition ? '渲染列表的代码' : '空空如也'
}</code></pre>
<!-- /wp:code -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>列表渲染</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 数据
const people = [{
  id: 1,
  name: 'Leo',
  age: 35
}, {
  id: 2,
  name: 'XiaoMing',
  age: 16
}]

// 渲染列表
{
  people.map(person =&gt; {
    return (
      &lt;dl key={person.id}&gt;
        &lt;dt&gt;{person.name}&lt;/dt&gt;
        &lt;dd&gt;age: {person.age}&lt;/dd&gt;
      &lt;/dl&gt;
    )
  })
}</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>React 的高效依赖于所谓的 Virtual-DOM，尽量不碰 DOM。对于列表元素来说会有一个问题：元素可能会在一个列表中改变位置。要实现这个操作，只需要交换一下 DOM 位置就行了，但是 React 并不知道其实我们只是改变了元素的位置，所以它会重新渲染后面两个元素（再执行 Virtual-DOM ），这样会大大增加 DOM 操作。但如果给每个元素加上唯一的标识，React 就可以知道这两个元素只是交换了位置，这个标识就是 key ，这个 key 必须是每个元素唯一的标识。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">dangerouslySetInnerHTML</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>对于富文本创建的内容，后台拿到的数据是这样的：<code>content = "&lt;p&gt;React.js是一个构建UI的库&lt;/p&gt;"</code></p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>处于安全的原因，React 当中所有表达式的内容会被转义，如果直接输入，标签会被当成文本。这时候就需要使用 dangerouslySetHTML 属性，它允许我们动态设置 innerHTML</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React, { Component } from 'react'
import ReactDOM from 'react-dom'

class App extends Component {
  constructor() {
    super()
    this.state = {
      content : "&lt;p&gt;React.js是一个构建UI的库&lt;/p&gt;"
    }
  }

  render () {
    return (
      &lt;div
      // 注意这里是两个下下划线 __html
      dangerouslySetInnerHTML={{__html: this.state.content}}
      /&gt;
    )
  }
}

ReactDOM.render(
  &lt;App/&gt;,
document.getElementById('root')
)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">六、表单中的受控组件与非受控组件</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">1. 非受控组件</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>React 要编写一个非受控组件，可以 使用 ref 来从 DOM 节点中获取表单数据，就是非受控组件。
 <br>
 例如，下面的代码使用非受控组件接受一个表单的值：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>class NameForm extends React.Component {
  constructor(props) {
    super(props);
      this.handleSubmit = this.handleSubmit.bind(this);
      this.input = React.createRef();
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.input.current.value);
    event.preventDefault();
  }

  render() {
    return (
      &lt;form onSubmit={this.handleSubmit}&gt;
        &lt;label&gt;
          Name:
          &lt;input type="text" ref={this.input} /&gt;
        &lt;/label&gt;
        &lt;input type="submit" value="Submit" /&gt;
      &lt;/form&gt;
    );
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>因为非受控组件将真实数据储存在 DOM 节点中，所以在使用非受控组件时，有时候反而更容易同时集成 React 和非 React 代码。如果你不介意代码美观性，并且希望快速编写代码，使用非受控组件往往可以减少你的代码量。否则，你应该使用受控组件。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">默认值</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在 React 渲染生命周期时，表单元素上的 value 将会覆盖 DOM 节点中的值，在非受控组件中，你经常希望 React 能赋予组件一个初始值，但是不去控制后续的更新。 在这种情况下, 你可以指定一个 <code>defaultValue</code> 属性，而不是 value。</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>render() {
  return (
    &lt;form onSubmit={this.handleSubmit}&gt;
      &lt;label&gt;
        Name:
        &lt;input defaultValue="Bob" type="text" ref={this.input} /&gt;
      &lt;/label&gt;
      &lt;input type="submit" value="Submit" /&gt;
    &lt;/form&gt;
  );
}</code></pre>
<!-- /wp:code -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>同样，<code>&lt;input type="checkbox"&gt;</code> 和 <code>&lt;input type="radio"&gt;</code> 支持 <code>defaultChecked</code>， <code>&lt;select&gt;</code> 和 <code>&lt;textarea&gt;</code> 支持 <code>defaultValue</code>。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">2. 受控组件</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在 HTML 中，表单元素（如 <code>&lt;input&gt;</code>、<code>&lt;textarea&gt;</code> 和 <code>&lt;select&gt;</code>）通常自己维护 state，并根据用户输入进行更新。而在 React 中，可变状态（mutable state）通常保存在组件的 state 属性中，并且只能通过使用 <code>setState()</code> 来更新。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>我们可以把两者联合起来使用，使 React 的 state 成为 “唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作，被 React 以这种方式控制取值的表单输入元素就叫做 “受控组件”。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>例如，如果我们想让前一个示例再提交时打印出名称，可以将表单写为受控组件：</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({
      value: event.target.value
    });
  }

  handleSubmit(event) {
    alert('提交的名字：' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      &lt;form onSubmit={this.handleSubmit}&gt;
        &lt;label&gt;
          名字:
          &lt;input type="text" value={this.state.value} onChange={this.handleChange} /&gt;
        &lt;/label&gt;
        &lt;input type=""submit value="提交" /&gt;
      &lt;form/&gt;
    );
  }
}</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>由于在表单元素上设置了 value 属性，因此显示的值将始终为 <code>this.state.value</code>，这使得 React 的 state 成为唯一数据源。由于 <code>handlechange</code> 在每次按键时都会执行并更新 React 的 state，因此显示的值将随着用户输入而更新。</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>对于受控组件来说，输入的值始终由 React 的 state 驱动。你也可以将 value 传递给其他 UI 元素，或者通过其他事件处理函数重置，但这意味着你需要编写更多的代码。</p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>注意: 另一种说法（广义范围的说法），React 组件的数据渲染是否被调用者传递的 props 完全控制，控制则为受控组件，否则非受控组件。</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">七、组件通信的方式</h1>
<!-- /wp:heading -->
<!-- wp:heading -->
<h2 class="wp-block-heading">1. 父子组件通信方式</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(1) 传递数据（父传子）与传递方法（子传父）</h3>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(2) ref 标记 (父组件拿到子组件的引用，从而调用子组件的方法)</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>在父组件中清除子组件的 input 输入框的 value 值。<code>this.refs.form.reset()</code></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">2. 非父子组件通信方式</h2>
<!-- /wp:heading -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(1) 状态提升（中间人模式）</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>React 中的状态提升概括来说，就是将多个组件需要共享的状态提升到它们最近的父组件上，在父组件上改变这个状态然后通过props 分发给子组件。</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(2) 发布订阅模式实现</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 调度中心
const bus = {
  list: [],

  // 订阅
  subscribe (callback) {
    this.list.push(callback)
  },

  // 发布
  publish (text) {
    // 遍历所有的 list，将回调函数执行
    this.list.forEach((callback) =&gt; {
      callback &amp;&amp; callback(text)
    })
  }
}

// 订阅者
bus.subscribe((val) =&gt; {
  console.log(111, val)
})
// 订阅者
bus.subscribe((val) =&gt; {
  console.log(222, val)
})

// 发布者
setTimeout(() =&gt; {
  bus.publish('publish-1')
}, 0)
setTimeout(() =&gt; {
  bus.publish('publish-2')
}, 1000)</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">(3) context 状态树传参</h3>
<!-- /wp:heading -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 1. 先定义全局 context 对象
import React from 'react'
const GlobalContext = React.createContext()
export default GlobalContext</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 2. 根组件引入 GlobalContext，并使用 GlobalContext.Provider（生产者）

// 重新包装根组件 class App {}

&lt;GlobalContext.Provider
  value={{
    name:"Joe",
    age:100,
    content:this.state.content,
    show:this.show.bind(this),
    hide:this.hide.bind(this)
  }}
&gt;

&lt;之前的根组件&gt;&lt;/之前的根组件&gt;
&lt;/GlobalContext.Provider&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:code -->
<pre class="wp-block-code"><code>// 3. 任意组件引入 GlobalContext 并调用 context，使用 GlobalContext.Consumer（消费者）
&lt;GlobalContext.Consumer&gt;
{
  context =&gt; {
    this.myshow = context.show; // 可以在当前组件任意函数触发
    this.myhide = context.hide; // 可以在当前组件任意函数触发
    return (
      &lt;div&gt;
        {context.name}-{context.age}-{context.content}
      &lt;/div&gt;
    )
  }
}
&lt;/GlobalContext.Consumer&gt;</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p><strong>注意：<code>GlobalContext.Consumer</code> 内必须是回调函数，通过 context 方法改变根组件状态</strong></p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>context优缺点：
  <br>
  优点：跨组件访问数据
  <br>
  缺点：react 组件树种某个上级组件 <code>shouldComponetUpdate</code> 返回 false，当 context 更新时，不会引起下级组件更新</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->]]></description><guid isPermaLink="false">/archives/react17-%E5%85%A8%E5%AE%B6%E6%A1%B6%EF%BC%881%EF%BC%89</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Tue, 21 May 2024 06:01:51 GMT</pubDate></item><item><title><![CDATA[TS-在 React 中使用 TypeScript]]></title><link>https://blog.qiaofugui.cn/archives/ts-%E5%9C%A8-react-%E4%B8%AD%E4%BD%BF%E7%94%A8-typescript</link><description><![CDATA[<img src="https://blog.qiaofugui.cn/plugins/feed/assets/telemetry.gif?title=TS-%E5%9C%A8%20React%20%E4%B8%AD%E4%BD%BF%E7%94%A8%20TypeScript&amp;url=/archives/ts-%E5%9C%A8-react-%E4%B8%AD%E4%BD%BF%E7%94%A8-typescript" width="1" height="1" alt="" style="opacity:0;"><!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">使用 CRA 创建支持 TS 的项目</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>React 脚手架工具 create-react-app（简称：CRA）默认支持 TypeScript</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>创建支持 TS 的项目命令：<code>npx create-react-app 项目名称 --template typescript</code></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>当看到以下提示时，表示支持 TS 的项目创建成功：
 <br>
 <img class="wp-image-241" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FTS-react-ts01.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>更多：<a href="https://create-react-app.dev/docs/adding-typescript/">在已有项目中使用 TS</a></p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">相当于非 TS 项目</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>目录结构主要以下三个变化：</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>项目根目录中增加了 <code>tsconfig.json</code> 配置文件：<strong>指定 TS 的编译选项（比如，编译时是否移除注释）</strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>React 组件的文件扩展名变为：<strong><code>*.tsx</code></strong></li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>src 目录中增加了 react-app-env.d.ts <strong>React 项目中默认的类型声明文件</strong></li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:image {"id":242,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FTS-react-ts02.png&amp;size=m" alt="" class="wp-image-242">
</figure>
<!-- /wp:image -->
<!-- wp:heading -->
<h2 class="wp-block-heading">React 项目默认的类型声明文件 <code>react-app-env.d.ts</code></h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><strong>三斜线指令</strong>：指定依赖的其他类型声明文件，types 表示依赖的类型声明文件包的名称
 <br>
 <img class="wp-image-233" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FTS-react-ts03.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>告诉 TS 帮我加载 react-scripts 这个包提供的类型声明</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">react-scripts 的类型声明文件包含了两部分类型：</h3>
<!-- /wp:heading -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>react、react-dom、node 的类型</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>图片、样式等模块的类型，以允许在代码中导入图片、SVG 等文件</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:paragraph -->
<p>TS 会自动加载该 .d.ts 文件，已提供类型声明（通过修改 tsconfig.json 中的 include 配置来验证）</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">TS 配置文件 tscongif.json</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>tsconfig.json 指定：<strong>项目文件和项目编译所需的配置项</strong></p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>TS 的配置项非常多（100+），以 CRA 项目中的配置为例来学习，其他的配置项用到时查<a href="https://www.typescriptlang.org/tsconfig">文档</a>即可</p>
 <!-- /wp:paragraph -->
 <!-- wp:list {"ordered":true} -->
 <ol>
  <!-- wp:list-item -->
  <li>tsconfig.json 文件所在目录为项目根目录（与 package.json 同级）</li>
  <!-- /wp:list-item -->
  <!-- wp:list-item -->
  <li>tsconfig.json 可以自动生成，命令：<code>tsc --init</code></li>
  <!-- /wp:list-item -->
 </ol>
 <!-- /wp:list -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:separator -->
<hr class="wp-block-separator has-alpha-channel-opacity">
<!-- /wp:separator -->
<!-- wp:paragraph -->
<p>除了在 tsconfig.json 文件中使用编译配置外，<strong>还可以通过命令行来使用</strong></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>使用演示：<code>tsc hello.ts --target es6</code></p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>tsc 后<strong>带有输入文件</strong>时（比如，<code>tsc hello.ts</code>），将忽略 tsconfig.json 文件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>tsc 后<strong>不带输入文件</strong>时（比如，<code>tsc</code>），才会启用 tscconfig.json</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p><strong>推荐使用 tsconfig.json 配置文件</strong></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">React 中的常用类型</h1>
<!-- /wp:heading -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>前提声明：基于 class 组件 React+TS 的使用（不是 ReactHooks）</p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:paragraph -->
<p>在不使用 TS 时，可以使用 <code>prop-types</code> 库，为 React 组件提供<a href="https://reactjs.org/docs/typechecking-with-proptypes.html">类型检查</a></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><strong>TS 项目中，推荐使用 TypeScript 实现组件类型校验（代替 PropTypes）</strong></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>不管是 React 还是 Vue，只要是支持 TS 的库，都提供了很多类型，来满足该库对类型的需求</p>
<!-- /wp:paragraph -->
<!-- wp:list {"ordered":true} -->
<ol>
 <!-- wp:list-item -->
 <li>React 项目是通过 <code>@types/react</code>、<code>@types/react-dom</code> 类型声明包，来提供类型的</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>这些包 CRA 已帮我们安装好（react-app-env.d.ts），直接用即可</li>
 <!-- /wp:list-item -->
</ol>
<!-- /wp:list -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p>参考资料：<a href="https://reactjs.org/docs/static-type-checking.html">React文档-静态类型检查</a>、<a href="https://github.com/typescript-cheatsheets/react">React+TS备忘录</a></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:heading -->
<h2 class="wp-block-heading">函数组件的类型</h2>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>组件的类型</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>组件的属性（props）</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>组件属性的默认值（defaultProps）</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>事件绑定和事件对象</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">函数组件的类型以及组件的属性</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":234,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FTS-react-ts04.png&amp;size=m" alt="" class="wp-image-234">
</figure>
<!-- /wp:image -->
<!-- wp:paragraph -->
<p>实际上，还可以直接简化为（完全按照函数在 TS 中的写法）：
 <br>
 <img class="wp-image-235" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FTS-react-ts05.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { FC } from 'react'
import ReactDOM from 'react-dom'

type Props = { name: string; age?: number }

/* const Hello: FC&lt;Props&gt; = ({ name, age }) =&gt; (
  &lt;div&gt;
    你好，我叫：{name}，我 {age} 岁了
  &lt;/div&gt;
) */

// 完全利用 JS（TS）自身的能力来编写组件
const Hello = ({ name, age }: Props) =&gt; (
  &lt;div&gt;
    你好，我叫：{name}，我 {age} 岁了
  &lt;/div&gt;
)

const App = () =&gt; (
  &lt;div&gt;
    &lt;Hello name="Joe" age={18}&gt;&lt;/Hello&gt;
  &lt;/div&gt;
)

ReactDOM.render(&lt;App /&gt;, document.getElementById('root'))</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">函数组件属性的默认值（defaultProps）</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":236,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FTS-react-ts06.png&amp;size=m" alt="" class="wp-image-236">
</figure>
<!-- /wp:image -->
<!-- wp:paragraph -->
<p>实际上，还可以直接简化为（完全按照函数在 TS 中的写法）：
 <br>
 <img class="wp-image-237" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FTS-react-ts07.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import { FC } from 'react'
import ReactDOM from 'react-dom'

type Props = { name: string; age?: number }

/* const Hello: FC&lt;Props&gt; = ({ name, age }) =&gt; (
  &lt;div&gt;
    你好，我叫：{name}，我 {age} 岁了
  &lt;/div&gt;
) 
Hello.defaultProps = {
  age: 18
} */

// 完全利用 JS（TS）自身的能力来编写组件
const Hello = ({ name, age = 18 }: Props) =&gt; (
  &lt;div&gt;
    你好，我叫：{name}，我 {age} 岁了
  &lt;/div&gt;
)

const App = () =&gt; (
  &lt;div&gt;
    &lt;Hello name="Joe"&gt;&lt;/Hello&gt;
  &lt;/div&gt;
)

ReactDOM.render(&lt;App /&gt;, document.getElementById('root'))</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">事件绑定和事件对象</h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><img class="wp-image-229" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FTS-react-ts08.png&amp;size=m" alt="">
 <br>
 再比如，文本框：
 <br>
 <img class="wp-image-230" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FTS-react-ts09.png&amp;size=m" alt=""></p>
<!-- /wp:paragraph -->
<!-- wp:quote -->
<blockquote class="wp-block-quote">
 <!-- wp:paragraph -->
 <p><strong>技巧：在JSX 中写事件处理程序（<code>e =&gt; {}</code>），然后，把鼠标放在 e 上，利用 TS 的类型推论来查看事件对象类型</strong>
  <br>
  <img class="wp-image-231" style="width: 600px;" src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FTS-react-ts10.png&amp;size=m" alt=""></p>
 <!-- /wp:paragraph -->
</blockquote>
<!-- /wp:quote -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React from 'react'
import ReactDOM from 'react-dom'

type Props = { name: string; age?: number }

const Hello = ({ name, age = 18 }: Props) =&gt; {
  const onClick = (e: React.MouseEvent&lt;HTMLButtonElement&gt;) =&gt; {
    console.log('赞', e.currentTarget)
  }
  const onChange = (e: React.ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {
    console.log(e.target.value)
  }

  return (
    &lt;div&gt;
      你好，我叫：{name}，我 {age} 岁了
      &lt;button onClick={onClick}&gt;点赞&lt;/button&gt;
      &lt;input type="text" onChange={onChange} /&gt;
    &lt;/div&gt;
  )
}

const App = () =&gt; (
  &lt;div&gt;
    &lt;Hello name="Joe"&gt;&lt;/Hello&gt;
  &lt;/div&gt;
)

ReactDOM.render(&lt;App /&gt;, document.getElementById('root'))</code></pre>
<!-- /wp:code -->
<!-- wp:heading -->
<h2 class="wp-block-heading">class 类组件的类型</h2>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
 <!-- wp:list-item -->
 <li>组件的类型、属性、事件</li>
 <!-- /wp:list-item -->
 <!-- wp:list-item -->
 <li>组件状态（state）</li>
 <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">class 组件的类型</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":232,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FTS-react-ts11-1024x262.png&amp;size=m" alt="" class="wp-image-232">
</figure>
<!-- /wp:image -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React from 'react'
import ReactDOM from 'react-dom'

type State = { count: number }
type Props = { message?: string }

class C1 extends React.Component {} // 无 props 无 state
class C2 extends React.Component&lt;Props&gt; {} // 有 props 无 state
class C3 extends React.Component&lt;{}, State&gt; {} // 无 props 有 state
class C4 extends React.Component&lt;Props, State&gt; {} // 有 props 有 state

const App = () =&gt; &lt;div&gt;&lt;/div&gt;

ReactDOM.render(&lt;App /&gt;, document.getElementById('root'))</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">class 组件的属性和属性默认值</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":225,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FTS-react-ts12.png&amp;size=m" alt="" class="wp-image-225">
</figure>
<!-- /wp:image -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React from 'react'
import ReactDOM from 'react-dom'

type Props = { name: string; age?: number }

class Hello extends React.Component&lt;Props&gt; {
  // 默认值
  /* static defaultProps: Partial&lt;Props&gt; = {
    age: 18
  } */

  render() {
    // 简化 class 组件的属性默认值
    const { name, age = 18 } = this.props
    return (
      &lt;div&gt;
        你好，我叫：{name}，我 {age} 岁了
      &lt;/div&gt;
    )
  }
}

const App = () =&gt; (
  &lt;div&gt;
    &lt;Hello name="Joe"&gt;&lt;/Hello&gt;
  &lt;/div&gt;
)

ReactDOM.render(&lt;App /&gt;, document.getElementById('root'))</code></pre>
<!-- /wp:code -->
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">class 组件状态（state）和事件</h3>
<!-- /wp:heading -->
<!-- wp:image {"id":226,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full">
 <img src="https://blog.qiaofugui.cn/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.qiaofugui.cn%2Fwp-content%2Fuploads%2F2024%2F05%2FTS-react-ts13.png&amp;size=m" alt="" class="wp-image-226">
</figure>
<!-- /wp:image -->
<!-- wp:code -->
<pre class="wp-block-code"><code>import React from 'react'
import ReactDOM from 'react-dom'

type State = { count: number }

class Counter extends React.Component&lt;{}, State&gt; {
  state: State = {
    count: 0
  }

  onIncrement = () =&gt; {
    this.setState({
      count: this.state.count + 1
    })
  }

  render() {
    return (
      &lt;div&gt;
        &lt;h3&gt;count: {this.state.count}&lt;/h3&gt;
        &lt;button onClick={this.onIncrement}&gt;+1&lt;/button&gt;
      &lt;/div&gt;
    )
  }
}

const App = () =&gt; (
  &lt;div&gt;
    &lt;Counter&gt;&lt;/Counter&gt;
  &lt;/div&gt;
)

ReactDOM.render(&lt;App /&gt;, document.getElementById('root'))</code></pre>
<!-- /wp:code -->]]></description><guid isPermaLink="false">/archives/ts-%E5%9C%A8-react-%E4%B8%AD%E4%BD%BF%E7%94%A8-typescript</guid><dc:creator>qiaofugui</dc:creator><category>笔记</category><pubDate>Tue, 21 May 2024 05:59:53 GMT</pubDate></item></channel></rss>