<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Taro 文档 Blog</title>
        <link>https://docs.taro.zone/blog</link>
        <description>Taro 文档 Blog</description>
        <lastBuildDate>Fri, 16 May 2025 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-cn</language>
        <item>
            <title><![CDATA[Taro on Harmony C-API 版本正式开源]]></title>
            <link>https://docs.taro.zone/blog/2025/05/16/taro-harmony-c-api</link>
            <guid>https://docs.taro.zone/blog/2025/05/16/taro-harmony-c-api</guid>
            <pubDate>Fri, 16 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Taro 是由京东发起并维护的开放式跨端跨框架解决方案，支持以 Web 的开发范式来实现小程序、H5、原生 APP 的跨端统一开发，从 18 年开源至今，在 GitHub 已累计获得 36,000+ Stars。]]></description>
            <content:encoded><![CDATA[<p>Taro 是由京东发起并维护的开放式跨端跨框架解决方案，支持以 Web 的开发范式来实现小程序、H5、原生 APP 的跨端统一开发，从 18 年开源至今，在 GitHub 已累计获得 36,000+ Stars。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="taro-x-纯血鸿蒙">Taro x 纯血鸿蒙<a href="#taro-x-纯血鸿蒙" class="hash-link" aria-label="Taro x 纯血鸿蒙的直接链接" title="Taro x 纯血鸿蒙的直接链接">​</a></h2><p>在过去的一年中，Taro 经历了显著的蜕变，Taro on Harmony 方案完成从 ArkTS 方案到 C-API 方案的升级，成功实现了对纯血鸿蒙的完全适配，扩展了 Taro 的兼容平台家族，实现了对 H5、小程序、RN、原生鸿蒙多端的统一开发。</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-09-26-21-35NaN6T9eOyiqx9Z6.png" alt="Taro X HarmonyOS" class="img_ZOZK"></p><p>去年 9 月，京东 APP 纯血鸿蒙 在鸿蒙应用商城正式上线，APP 中核心购物链路，如首页、搜索、商详、购物车、订单、结算和我京等页面，都是通过 Taro on Harmony C-API 版本进行开发，并且一上线就获得了华为的 S 级应用认证。</p><p><img loading="lazy" src="https://img13.360buyimg.com/img/jfs/t1/235448/38/25755/201048/66d7d844F0baea79b/adfe087ce5348c30.png" alt="JD Harmony" class="img_ZOZK"></p><p>今天，我们正式开源 Taro on Harmony C-API 版本，这次版本的发布，将带来更丰富的样式适配、更高效的渲染性能、更全面的组件支持，让开发者以 Web 范式的方式来开发出媲美原生鸿蒙性能的应用，为鸿蒙应用生态的丰富注入强大的动力。</p><p>对于开发者来说，Taro on Harmony 技术方案为开发者提供了显著优势：显著降低鸿蒙应用开发门槛，让开发者能够运用熟悉的 Web 技术栈快速构建纯血鸿蒙应用。同时，基于鸿蒙 CAPI 构建的高性能渲染管线，在保证开发高效率的同时实现了与原生应用媲美的性能表现。更重要的是，开发者可以充分复用现有研发生态，将存量 Taro 项目快速适配迁移至鸿蒙平台，大幅加速业务在鸿蒙生态的布局与上架进程。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2025-05-16/pic_hd.jpg" alt="JD Harmony APP" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="整体技术架构">整体技术架构<a href="#整体技术架构" class="hash-link" aria-label="整体技术架构的直接链接" title="整体技术架构的直接链接">​</a></h2><p>Taro on Harmony 技术方案支持开发者使用 React DSL 来开发纯血鸿蒙应用，整体架构可以简单分为三层：</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2025-05-16/img3.png" alt="Taro Harmony React" class="img_ZOZK"></p><p>最上层是应用业务代码所在的 ArkVM 层，这一层在 C-API 版本中主要运行业务代码、React 的核心代码以及少量的 Taro 运行时代码。</p><p>中间层是 Taro 的 CSSOM 和 TaroElement 树，负责处理上层 Taro 运行时代码传递下来的指令，比如 TaroElement 节点树创建，绑定关系以及设置属性等操作。</p><p>最下层存放的是 TaroRenderNode 虚拟节点树，这棵节点树和真正的上屏节点树是一一对应的关系，同时在 TaroRenderNode 节点树内会创建对应的 Yoga 节点。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2025-05-16/img4.png" alt="Taro Harmony React DOM" class="img_ZOZK"></p><p>同时 Taro 还基于鸿蒙提供的 VSync 机制设置一套任务处理管线，来处理中间层和下层节点树产生的样式匹配、节点测量、节点布局、样式设置以及节点上屏等任务，来保证任务的时序性和最后上屏渲染结果的正确性。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="重点特性">重点特性<a href="#重点特性" class="hash-link" aria-label="重点特性的直接链接" title="重点特性的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="丰富的能力支持">丰富的能力支持<a href="#丰富的能力支持" class="hash-link" aria-label="丰富的能力支持的直接链接" title="丰富的能力支持的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="常用组件和-api-支持">常用组件和 API 支持<a href="#常用组件和-api-支持" class="hash-link" aria-label="常用组件和 API 支持的直接链接" title="常用组件和 API 支持的直接链接">​</a></h4><p>在 C-API 版本的 Taro on Harmony 中，我们不仅完整支持了 React 18+，另外支持了 View、Text、Image、Video 等近 33 个 Taro 组件，对于常用的 API，如 getSystemInfo、getStorage 等也是在 C-API 版本中得到了完整的支持，而且针对逻辑较为复杂的 API 如：createSelectorQuery 以及 createIntersectionObserver，我们将这些 API 在 C++ 侧进行了重新的实现，大幅提升了他们的执行性能。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="常用样式支持">常用样式支持<a href="#常用样式支持" class="hash-link" aria-label="常用样式支持的直接链接" title="常用样式支持的直接链接">​</a></h4><p>在 C-API 版本中，我们对支持了大部分常见的 CSS 能力：</p><ul><li>支持常见的 CSS 样式和布局，支持 flex、伪类和伪元素</li><li>支持常见的 CSS 定位，绝对定位、fixed 定位</li><li>支持常见的 CSS 选择器和媒体查询</li><li>支持常见的 CSS 单位，比如 vh、vw 以及计算属性 calc</li><li>支持 CSS 变量以及安全区域等预定义变量</li></ul><p>同时，我们参考浏览器 CSSOM 的实现方式，在 C++ 实现了一套 CSSOM 逻辑，里面包含了样式解析、样式匹配、样式合成和应用整个链路的样式处理逻辑。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2025-05-16/img5.png" alt="Taro Harmony CSS" class="img_ZOZK"></p><p>另外，Taro 引入了 Yoga 布局引擎来计算渲染节点的位置和大小，最大程度保证 Taro 构建出来的鸿蒙应用中渲染样式和 W3C 规范的一致性。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2025-05-16/img6.png" alt="Taro Harmony Style" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="媲美原生-arkts-的高性能">媲美原生 ArkTS 的高性能<a href="#媲美原生-arkts-的高性能" class="hash-link" aria-label="媲美原生 ArkTS 的高性能的直接链接" title="媲美原生 ArkTS 的高性能的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="运行时逻辑下沉至-c">运行时逻辑下沉至 C++<a href="#运行时逻辑下沉至-c" class="hash-link" aria-label="运行时逻辑下沉至 C++的直接链接" title="运行时逻辑下沉至 C++的直接链接">​</a></h4><p>在 C-API 的版本中，我们将 ArkVM 层的 Taro 运行时内容削减到极致的薄，将 TaroElement 的大部分内容都下沉到了 C++ 侧，并在 ArkVM 层取消了他们之间父子关系的绑定，极大地提升了 TaroElement 相关逻辑的性能。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2025-05-16/img7.png" alt="Taro Harmony CAPI" class="img_ZOZK"></p><p>另一方面，在 C++ 侧 Taro 会指令式地调用 ArkUI 在 C++ 侧提供的 API，来高效地创建节点、设置属性、绑定事件以及绘制上屏。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="提供长列表组件应对长列表场景">提供长列表组件应对长列表场景<a href="#提供长列表组件应对长列表场景" class="hash-link" aria-label="提供长列表组件应对长列表场景的直接链接" title="提供长列表组件应对长列表场景的直接链接">​</a></h4><p>Taro 还针对长列表场景针对性地提供了长列表类型组件，并对长列表类型组件进行了优化，提供了懒加载、预加载和节点复用等功能，有效地解决大数据量下的性能问题，提高应用的流畅度和用户体验。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2025-05-16/img8.png" alt="Taro Harmony Virtual List" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="支持-c-api-混合原生的渲染模式">支持 C-API 混合原生的渲染模式<a href="#支持-c-api-混合原生的渲染模式" class="hash-link" aria-label="支持 C-API 混合原生的渲染模式的直接链接" title="支持 C-API 混合原生的渲染模式的直接链接">​</a></h3><p>Taro 的组件和 API 是以小程序作为基准来进行设计的，因此在实际的鸿蒙应用开发过程中，会出现部分所需的组件和 API 在 Taro 中不存在的情况，因为针对这种情况，在 C-API 版本中，Taro 提供了原生混合开发的能力，支持将原生页面或者原生组件混合编译到 Taro 鸿蒙项目中，支持 Taro 组件和鸿蒙原生组件在页面上的混合使用。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2025-05-16/img9.png" alt="Taro Harmony CAPI Hybrid" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="使用教程">使用教程<a href="#使用教程" class="hash-link" aria-label="使用教程的直接链接" title="使用教程的直接链接">​</a></h2><h2 class="anchor anchorWithStickyNavbar_mCi1" id="项目开源地址">项目开源地址<a href="#项目开源地址" class="hash-link" aria-label="项目开源地址的直接链接" title="项目开源地址的直接链接">​</a></h2><p>Taro 本地开源地址：<a href="https://github.com/NervJS/taro" target="_blank" rel="noopener noreferrer">https://github.com/NervJS/taro</a></p><p>Taro 鸿蒙 C-API 开源地址：<a href="https://github.com/NervJS/taro-harmony-capi-library" target="_blank" rel="noopener noreferrer">https://github.com/NervJS/taro-harmony-capi-library</a></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="安装和使用">安装和使用<a href="#安装和使用" class="hash-link" aria-label="安装和使用的直接链接" title="安装和使用的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="安装-harmony-插件">安装 harmony 插件<a href="#安装-harmony-插件" class="hash-link" aria-label="安装 harmony 插件的直接链接" title="安装 harmony 插件的直接链接">​</a></h4><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 使用 npm 安装</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i @tarojs/plugin-platform-harmony-cpp</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 使用 pnpm 安装</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> i @tarojs/plugin-platform-harmony-cpp</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="添加插件配置">添加插件配置<a href="#添加插件配置" class="hash-link" aria-label="添加插件配置的直接链接" title="添加插件配置的直接链接">​</a></h4><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">os</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'os'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">path</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'path'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">plugins</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'@tarojs/plugin-platform-harmony-cpp'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">harmony</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 当前仅支持使用 Vite 编译鸿蒙应用</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">compiler</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'vite'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Note: 鸿蒙工程路径，可以参考 [鸿蒙应用创建导读](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/start-with-ets-stage-0000001477980905-V2) 创建</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">projectPath</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> path</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">join</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">os</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">homedir</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'projects/my-business-project'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Taro 项目编译到对应鸿蒙模块名，默认为 entry</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">hapName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'entry'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="编译项目">编译项目<a href="#编译项目" class="hash-link" aria-label="编译项目的直接链接" title="编译项目的直接链接">​</a></h3><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 编译鸿蒙应用</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ taro build --type harmony_cpp</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 编译鸿蒙原生组件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ taro build native-components --type harmony_cpp</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>如果需要编译鸿蒙应用，同时使用编译鸿蒙原生组件，可以在页面配置中添加 &nbsp;<code>entryOption: false</code>&nbsp; 表示该页面是组件，同时可以用过 &nbsp;<code>componentName</code>&nbsp; 指定组件导出名。</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">navigationBarTitleText</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Hello World'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">+</span><span class="token plain">  componentName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'MyComponent'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">+</span><span class="token plain">  entryOption</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="总结与展望">总结与展望<a href="#总结与展望" class="hash-link" aria-label="总结与展望的直接链接" title="总结与展望的直接链接">​</a></h2><p>Taro on Harmony C-API 版本经历了京东鸿蒙 APP 的实践，综合性能、生态以及开发体验来讲，毫无疑问已经成为了开发鸿蒙应用的最佳框架选型之一。</p><p>当下，我们也仍然在不断完善着鸿蒙的适配方案，基于当前的 Taro on Harmony C-API 方案，我们会进行多线程的架构升级以及 React 的 C++ 化，进一步提升 Taro 在鸿蒙端侧的性能，并极大地降低应用的丢帧率，整体进展也已经处于验证和测试阶段。</p><p>也欢迎大家一起参与 Taro on Harmony 的共建，你们的每一个建议，每一次提交，都是推进 Taro 继续往前走最大的动力。</p>]]></content:encoded>
            <category>v4</category>
            <category>harmony</category>
        </item>
        <item>
            <title><![CDATA[Taro on Harmony ：助力业务高效开发纯血鸿蒙应用]]></title>
            <link>https://docs.taro.zone/blog/2025/04/23/taro-on-harmony</link>
            <guid>https://docs.taro.zone/blog/2025/04/23/taro-on-harmony</guid>
            <pubDate>Wed, 23 Apr 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[基于 Taro 打造的京东鸿蒙 APP 已跟随鸿蒙 Next 系统公测，本系列文章将深入解析 Taro 如何实现使用 React 开发高性能鸿蒙应用的技术内幕]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorWithStickyNavbar_mCi1" id="背景">背景<a href="#背景" class="hash-link" aria-label="背景的直接链接" title="背景的直接链接">​</a></h2><p>纯血鸿蒙逐渐成为全球第三大操作系统，业界也掀起了适配鸿蒙原生的浪潮，用户迁移趋势明显，京东作为国民应用，为鸿蒙用户提供完整的购物体验至关重要。</p><p><img loading="lazy" src="https://luckyadam.notion.site/image/attachment%3A3687639d-b6bb-48eb-aa8c-05c5d8bfc76f%3Aimage.png?table=block&amp;id=1de0d09e-99e3-8088-adf0-e48b2bb8c41b&amp;spaceId=9a0c7639-98bb-4652-b1e5-2d1a6b23d718&amp;width=1420&amp;userId=&amp;cache=v2" class="img_ZOZK"></p><p>去年 9 月，京东 APP 纯血鸿蒙 在鸿蒙应用商城正式上线，APP 中核心购物链路，如首页、搜索、商详、购物车、订单、结算和我京等页面，都是通过 Taro on Harmony 方案进行开发，并且一上线就获得了华为的 S 级应用认证。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="taro-介绍">Taro 介绍<a href="#taro-介绍" class="hash-link" aria-label="Taro 介绍的直接链接" title="Taro 介绍的直接链接">​</a></h2><p>Taro 是由京东发起并维护的开放式跨端跨框架解决方案，支持以 Web 的开发范式来实现小程序、H5、原生 APP 的跨端统一开发，从 18 年开源至今，在 GitHub 已累计获得 36,000+ Stars。</p><p><img loading="lazy" src="https://luckyadam.notion.site/image/attachment%3A2eef6133-46f1-4243-9380-b84c58021378%3Aimage.png?table=block&amp;id=1de0d09e-99e3-80e6-ad81-c22e8fcff5e6&amp;spaceId=9a0c7639-98bb-4652-b1e5-2d1a6b23d718&amp;width=1420&amp;userId=&amp;cache=v2" class="img_ZOZK"></p><p>在过去的一年中，Taro 完成了鸿蒙适配方案的开发，Taro on Harmony 方案支持开发者使用 React DSL 来快速开发高性能原生鸿蒙应用，让 Taro 具备了一份代码同时跨鸿蒙、小程序、H5、React Native 多端的能力，可以让开发者以熟悉的方式来开发鸿蒙应用，大幅降低鸿蒙开发门槛，并且存量的 Taro 业务也能快速转成鸿蒙原生应用，可以节约大量研发成本。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="taro-on-harmony-适配方案的演进">Taro on Harmony 适配方案的演进<a href="#taro-on-harmony-适配方案的演进" class="hash-link" aria-label="Taro on Harmony 适配方案的演进的直接链接" title="Taro on Harmony 适配方案的演进的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="基于-arkts-的初始版本">基于 ArkTS 的初始版本<a href="#基于-arkts-的初始版本" class="hash-link" aria-label="基于 ArkTS 的初始版本的直接链接" title="基于 ArkTS 的初始版本的直接链接">​</a></h3><p>Taro 适配鸿蒙方案经过了多个版本的演进迭代，最初版本我们采用的是对接到鸿蒙原生 ArkTS 语言的方式来实现，这和 Taro 适配微信小程序的方案类似，都是通过模拟浏览器 DOM/BOM 环境，然后运行 React 代码构建出虚拟 DOM 树，再将虚拟 DOM 树以递归遍历的方式构建出宿主环境（小程序/鸿蒙）的渲染节点树，从而实现页面渲染。</p><p><img loading="lazy" src="https://luckyadam.notion.site/image/attachment%3A1824337e-2852-4feb-b67c-c2df8684e878%3A2023-12-04-14-58Rxh5858Hz12tATUfWO.png?table=block&amp;id=1de0d09e-99e3-804d-9dc6-c0285dd9d4ee&amp;spaceId=9a0c7639-98bb-4652-b1e5-2d1a6b23d718&amp;width=1420&amp;userId=&amp;cache=v2" class="img_ZOZK"></p><p>但这个方案的缺陷非常明显，那就是在 ArkTS 之上再桥接一层转换，导致性能相比原生存在一定差异，并且这个差异几乎没有办法可以抹平。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="基于-c-api-的全新版本">基于 C API 的全新版本<a href="#基于-c-api-的全新版本" class="hash-link" aria-label="基于 C API 的全新版本的直接链接" title="基于 C API 的全新版本的直接链接">​</a></h3><p>恰逢此时，鸿蒙为了方便三方框架与应用接入鸿蒙生态，在官方开发语言 ArkTS 之外，开放了更底层 C API 能力，提供了 C++ 层的 UI 节点创建、属性设置等能力（类似于 C++ 版本的 DOM），三方框架和应用可以基于 C API 构建高性能的解决方案和应用。</p><p><img loading="lazy" src="https://luckyadam.notion.site/image/attachment%3Ab45a6ed0-7536-4055-a5cd-85ad0f733479%3Aimage.png?table=block&amp;id=1de0d09e-99e3-809a-a1b9-ddf319bf3556&amp;spaceId=9a0c7639-98bb-4652-b1e5-2d1a6b23d718&amp;width=960&amp;userId=&amp;cache=v2" class="img_ZOZK"></p><p>C API 就是 Taro 想要的能力，与 Taro 的架构完美契合，Taro React 构建出来的虚拟 DOM 树可以通过 C API 直接去创建原生 UI 节点，省去了很多流程环节，并且可以将我们大部分的渲染管线处理沉到 C++ 中去，获得飞跃式的性能提升，所以基于 C API 我们首先设计并开发了单线程架构版本。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="单线程架构版本">单线程架构版本<a href="#单线程架构版本" class="hash-link" aria-label="单线程架构版本的直接链接" title="单线程架构版本的直接链接">​</a></h4><p>单线程架构版本的示意图如下，整个渲染过程均在主线程实现，业务代码在 ArkVM 中执行获得虚拟 DOM 树，同时读取样式数据，通过 React Reconciler ，在构建虚拟树时会调用 NAPI 在 C++ 侧构建出对应的 Element Tree，同时也会进行样式数据的解析处理，从而构建出 CSSOM 对象，Element Tree 和 CSSOM 对象会进行匹配从而构建出带有样式的 Render Tree，在这一步也会同时创建 yoga 节点，进行布局计算，最后会再基于布局计算的结果生成 C API 的 ArkNode Tree，从而实现上屏渲染。</p><p><img loading="lazy" src="https://luckyadam.notion.site/image/attachment%3A8b6d3829-3351-4b23-a0b2-cd9e182e14f5%3Aimage.png?table=block&amp;id=1de0d09e-99e3-8029-b026-e1c3831dcf63&amp;spaceId=9a0c7639-98bb-4652-b1e5-2d1a6b23d718&amp;width=1420&amp;userId=&amp;cache=v2" class="img_ZOZK"></p><p>目前京东鸿蒙 APP 中首页、搜推、我京、核心购物流程均是使用 Taro 单线程版本进行开发，性能和稳定性位于第一梯队。</p><p><img loading="lazy" src="https://luckyadam.notion.site/image/attachment%3A3f53d76c-a116-43b7-a6e4-4ad19251cd0f%3A11431743594310_.pic_hd.jpg?table=block&amp;id=1de0d09e-99e3-8081-8a18-fbe100bbc986&amp;spaceId=9a0c7639-98bb-4652-b1e5-2d1a6b23d718&amp;width=1420&amp;userId=&amp;cache=v2" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="多线程架构版本">多线程架构版本<a href="#多线程架构版本" class="hash-link" aria-label="多线程架构版本的直接链接" title="多线程架构版本的直接链接">​</a></h4><p>单线程版本上线之后，我们也发现了单线程架构的不足，① 从业务代码的执行到渲染流程的处理都发生在主线程上，这导致了主线程的过载，使得应用无法及时响应用户的操作，从而引发了用户体验上的卡顿，随着业务逻辑的增加和复杂性的提升，这种卡顿现象会越发明显；② 在单线程架构下，代码都是需要内置到应用包里，这样就无法实现业务代码的动态下发更新。</p><p>所以，为了解决以上问题，进一步提升应用性能，和应对未来更多业务场景需求的可能性，我们实现了多线程架构。</p><p><img loading="lazy" src="https://luckyadam.notion.site/image/attachment%3Af873dd4e-fde0-498f-8f01-61bafef7338c%3Aimage.png?table=block&amp;id=1de0d09e-99e3-804b-bc2c-ec73b07330cb&amp;spaceId=9a0c7639-98bb-4652-b1e5-2d1a6b23d718&amp;width=1420&amp;userId=&amp;cache=v2" class="img_ZOZK"></p><p>相较于单线程架构，多线程架构将整个业务代码执行和渲染的过程拆分到了三个线程，每个线程各司其职，让任务的执行更加合理，不会造成主线程的堵塞，这样的架构主要带来三个好处。</p><p>1.<strong>业务逻辑不再堵塞主流程</strong>，完全剥离到了单独的线程执行，之前容易出现卡顿、甚至 APP Freeze 的页面基本不再有问题，例如商详接入之后滑动页面变得更加流畅；</p><p>2.<strong>动画执行和渲染不再卡顿</strong>，动画的执行交由 Background 线程管控，不再和 JS 线程有冲突问题，动画支持满帧渲染；</p><p>3.<strong>支持动态化的能力</strong>，在性能上可以与原生相媲美，同时还能支持动态下发，支持更新远端 JS 资源来实现 APP 内容的更新，让业务具备快速验证和免发版的能力。</p><p>目前多线程版本正在我们的业务中进行试点接入，很快就会正式上线。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="taro-on-harmony-方案特性">Taro on Harmony 方案特性<a href="#taro-on-harmony-方案特性" class="hash-link" aria-label="Taro on Harmony 方案特性的直接链接" title="Taro on Harmony 方案特性的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="丰富的能力支持">丰富的能力支持<a href="#丰富的能力支持" class="hash-link" aria-label="丰富的能力支持的直接链接" title="丰富的能力支持的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="常用组件和-api-支持">常用组件和 API 支持<a href="#常用组件和-api-支持" class="hash-link" aria-label="常用组件和 API 支持的直接链接" title="常用组件和 API 支持的直接链接">​</a></h4><p>在 C-API 版本的 Taro For Harmony 中，我们不仅完整支持了 React 18+，另外支持了 View、Text、Image、Video 等近 33 个 Taro 组件，对于常用的 API，如 getSystemInfo、getStorage 等也是在 C-API 版本中得到了完整的支持，而且针对逻辑较为复杂的 API 如：createSelectorQuery 以及 createIntersectionObserver，我们将这些 API 在 C++ 侧进行了重新的实现，大幅提升了他们的执行性能。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="常用样式支持">常用样式支持<a href="#常用样式支持" class="hash-link" aria-label="常用样式支持的直接链接" title="常用样式支持的直接链接">​</a></h4><p>在 C-API 版本中，我们对支持了大部分常见的 CSS 能力：</p><ul><li>支持常见的 CSS 样式和布局，支持 flex、伪类和伪元素</li><li>支持常见的 CSS 定位，绝对定位、fixed 定位</li><li>支持常见的 CSS 选择器和媒体查询</li><li>支持常见的 CSS 单位，比如 vh、vw 以及计算属性 calc</li><li>支持 CSS 变量以及安全区域等预定义变量</li></ul><p>同时，我们参考浏览器 CSSOM 的实现方式，在 C++ 实现了一套 CSSOM 逻辑，里面包含了样式解析、样式匹配、样式合成和应用整个链路的样式处理逻辑。</p><p><img loading="lazy" src="https://luckyadam.notion.site/image/attachment%3A98ea6485-da68-4583-9b18-b8f1366e84e5%3Aimage.png?table=block&amp;id=1de0d09e-99e3-800a-8a32-d836783876f1&amp;spaceId=9a0c7639-98bb-4652-b1e5-2d1a6b23d718&amp;width=1170&amp;userId=&amp;cache=v2" class="img_ZOZK"></p><p>另外，Taro 引入了 Yoga 布局引擎来计算渲染节点的位置和大小，最大程度保证 Taro 构建出来的鸿蒙应用中渲染样式和 W3C 规范的一致性。</p><p><img loading="lazy" src="https://luckyadam.notion.site/image/attachment%3Aa740c240-1acc-4021-bf69-3db739ec4391%3Aimage.png?table=block&amp;id=1de0d09e-99e3-8047-8200-cf9f60302c6e&amp;spaceId=9a0c7639-98bb-4652-b1e5-2d1a6b23d718&amp;width=1420&amp;userId=&amp;cache=v2" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="媲美原生-arkts-的高性能">媲美原生 ArkTS 的高性能<a href="#媲美原生-arkts-的高性能" class="hash-link" aria-label="媲美原生 ArkTS 的高性能的直接链接" title="媲美原生 ArkTS 的高性能的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="运行时逻辑下沉至-c">运行时逻辑下沉至 C++<a href="#运行时逻辑下沉至-c" class="hash-link" aria-label="运行时逻辑下沉至 C++的直接链接" title="运行时逻辑下沉至 C++的直接链接">​</a></h4><p>在 C-API 的版本中，我们将 ArkVM 层的 Taro 运行时内容削减到极致的薄，将 TaroElement 的大部分内容都下沉到了 C++ 侧，并在 ArkVM 层取消了他们之间父子关系的绑定，极大地提升了 TaroElement 相关逻辑的性能。</p><p><img loading="lazy" src="https://luckyadam.notion.site/image/attachment%3A6d4739f0-41d3-4177-b293-dafc34817472%3Aimage.png?table=block&amp;id=1de0d09e-99e3-801f-8854-fa40d21f1afb&amp;spaceId=9a0c7639-98bb-4652-b1e5-2d1a6b23d718&amp;width=1420&amp;userId=&amp;cache=v2" class="img_ZOZK"></p><p>另一方面，在 C++ 侧 Taro 会指令式地调用 ArkUI 在 C++ 侧提供的 API，来高效地创建节点、设置属性、绑定事件以及绘制上屏。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="提供长列表组件应对长列表场景">提供长列表组件应对长列表场景<a href="#提供长列表组件应对长列表场景" class="hash-link" aria-label="提供长列表组件应对长列表场景的直接链接" title="提供长列表组件应对长列表场景的直接链接">​</a></h4><p>Taro 还针对长列表场景针对性地提供了长列表类型组件，并对长列表类型组件进行了优化，提供了懒加载、预加载和节点复用等功能，有效地解决大数据量下的性能问题，提高应用的流畅度和用户体验。</p><p><img loading="lazy" src="https://luckyadam.notion.site/image/attachment%3A104a946f-10d1-4403-87a8-f137e2a8e3ab%3Aimage.png?table=block&amp;id=1de0d09e-99e3-80dd-ac4f-d782ad2b53d4&amp;spaceId=9a0c7639-98bb-4652-b1e5-2d1a6b23d718&amp;width=1420&amp;userId=&amp;cache=v2" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="支持-c-api-混合原生的渲染模式">支持 C API 混合原生的渲染模式<a href="#支持-c-api-混合原生的渲染模式" class="hash-link" aria-label="支持 C API 混合原生的渲染模式的直接链接" title="支持 C API 混合原生的渲染模式的直接链接">​</a></h3><p>Taro 的组件和 API 是以小程序作为基准来进行设计的，因此在实际的鸿蒙应用开发过程中，会出现部分所需的组件和 API 在 Taro 中不存在的情况，因为针对这种情况，在 C-API 版本中，Taro 提供了原生混合开发的能力，支持将原生页面或者原生组件混合编译到 Taro 鸿蒙项目中，支持 Taro 组件和鸿蒙原生组件在页面上的混合使用。</p><p><img loading="lazy" src="https://luckyadam.notion.site/image/attachment%3Aca51307a-d51f-460c-b5b8-1c930843f445%3Aimage.png?table=block&amp;id=1de0d09e-99e3-806c-b10f-fbafa2ef8a98&amp;spaceId=9a0c7639-98bb-4652-b1e5-2d1a6b23d718&amp;width=1420&amp;userId=&amp;cache=v2" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="总结">总结<a href="#总结" class="hash-link" aria-label="总结的直接链接" title="总结的直接链接">​</a></h2><p>Taro 鸿蒙方案基于鸿蒙 CAPI 进行构建，实现了将 React DSL 直接对接到 C++ 侧来运行整体渲染管线，从而实现了与原生齐平的渲染性能，同时 Taro 鸿蒙方案是多线程架构的方案，在应用渲染性能、操作响应时延上都在业界做到极致，并且 Taro 鸿蒙方案支持动态更新，是行业首创且经过大规模 APP 应用场景验证的开发框架，综合性能、生态以及开发体验来讲，毫无疑问已经成为了开发鸿蒙应用的最佳框架选型之一。</p><p>目前，我们也仍然在不断完善着鸿蒙的适配方案，目前在渲染性能提升方面我们正在进行 React C++ 化的探索，整体进展也已经处于验证和测试阶段，同时也在进行自研布局引擎的探索，在进一步提升渲染性能的同时，为业务提供更丰富的样式写法支持；此外，在开发效率提升方面，我们也正在进行开发调试工具的探索开发，为业务提供热重载、源码定位、断点调试、元素审查等能力，提升效率和开发体验。</p><p>在近期，我们会将已经在京东鸿蒙 APP 久经考验的单线程架构版本开源出来，为开源社区贡献一份力量，为鸿蒙应用生态的丰富注入强大的动力。</p>]]></content:encoded>
            <category>v4</category>
            <category>harmony</category>
        </item>
        <item>
            <title><![CDATA[Taro 鸿蒙技术内幕系列（五） - 剖析鸿蒙长列表优化实践]]></title>
            <link>https://docs.taro.zone/blog/2024/11/22/harmony-virtual-list</link>
            <guid>https://docs.taro.zone/blog/2024/11/22/harmony-virtual-list</guid>
            <pubDate>Fri, 22 Nov 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[基于 Taro 打造的京东鸿蒙 APP 已跟随鸿蒙 Next 系统公测，本系列文章将深入解析 Taro 如何实现使用 React 开发高性能鸿蒙应用的技术内幕]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://img13.360buyimg.com/img/jfs/t1/148159/17/47762/49649/673ffa2fF63c5cee5/99599074b6ae93ca.png" alt="首图" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="问题背景">问题背景<a href="#问题背景" class="hash-link" aria-label="问题背景的直接链接" title="问题背景的直接链接">​</a></h2><p>长列表类型组件（例如 WaterFlow, List）在应用开发中被广泛使用，特别适合商品列表、订单列表、消息列表等需要无限滚动的场景。这些组件通常包含大量结构相似的子组件，它们的样式和布局相似，但数据内容各异，且数据通常来自网络。</p><p>在典型的 Taro React 应用中 ，正常的列表处理流程如下：</p><ol><li>生成或从远端服务器加载数据。</li><li>框架基于最新数据生成新的虚拟 DOM 树。</li><li>框架使用 diff 算法或其他机制，根据虚拟 DOM 树的变化，触发对应的更新指令。</li><li>运行时捕获框架的更新请求，并实际更新视图。</li></ol><p>然而，如果加载的数据量非常大，可能会引发严重的性能问题，导致视图在一段时间内无法响应用户操作。为了解决这个问题，我们为 Taro 长列表类型组件提供了懒加载、预加载和组件复用等功能，专门针对长列表类型组件进行了优化，有效地解决大数据量下的性能问题，提高应用的流畅度和用户体验。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro-虚拟列表">Taro 虚拟列表<a href="#taro-虚拟列表" class="hash-link" aria-label="Taro 虚拟列表的直接链接" title="Taro 虚拟列表的直接链接">​</a></h3><p>目前 Taro 在其他端侧已支持<a href="https://docs.taro.zone/docs/next/virtual-list/" target="_blank" rel="noopener noreferrer">虚拟列表</a>，并在实践中证明虚拟列表可以极大程度提升列表渲染性能。出于性能考虑，Taro 通过 CAPI 对接鸿蒙 ArkUI，将更多的运行时逻辑如组件、动效、测算和布局等逻辑下沉到了 C++ 层。虚拟列表也需要在 C++层重新设计实现，在只渲染当前可视区域视图的基础上，我们还加入了组件预加载，数据预请求和组件复用等高阶功能，有效地解决大数据量下的性能问题，提高应用的流畅度和用户体验，并大幅度降低了应用内存占用。以下是 Taro 虚拟列表渲染流程：</p><p><img loading="lazy" src="https://img20.360buyimg.com/img/jfs/t1/247467/7/23875/28060/673b1b6cFdf12e18e/226b35e0f280f63c.png" alt="虚拟列表" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="优化方案">优化方案<a href="#优化方案" class="hash-link" aria-label="优化方案的直接链接" title="优化方案的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="nodeadapter-介绍">NodeAdapter 介绍<a href="#nodeadapter-介绍" class="hash-link" aria-label="NodeAdapter 介绍的直接链接" title="NodeAdapter 介绍的直接链接">​</a></h4><p>当前 ArkTS 滚动类容器使用 LazyForEach 时，框架会根据滚动容器可视区域按需创建组件，但是鸿蒙 CAPI 接口不支持 LazyForEach 功能。作为 ArkTS 侧 LazyForEach 的替代方案，开发者可以使用 NodeAdapter 对象在 C++ 侧实现类似的效果。NodeAdapter 已支持 List、Grid、Swiper 和 WaterFlow 四种主要的长列表组件。</p><p>本文将以 WaterFlow 组件为例，详细介绍如何利用 NodeAdapter 对象和 CAPI 提供的能力来实现长列表组件的懒加载、预加载和组件复用。</p><p>NodeAdapter 通过特定的事件机制时需要进行相关的处理。以下是 NodeAdapter 的典型工作流程：</p><p><img loading="lazy" src="https://img13.360buyimg.com/img/jfs/t1/214063/21/46994/43111/67330f8cF5dd5b1d3/0c3b04cea1c4a205.png" alt="NodeAdapter" class="img_ZOZK"></p><p>通过这种方式，开发者可以更好地控制长列表组件的行为，从而显著提升其性能。</p><p>NodeAdapter 的创建和挂载如下：</p><ul><li>创建 NodeAdapter 对象并挂载回调处理函数</li></ul><div class="language-cpp codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-cpp codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">RenderNode</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token base-clause keyword" style="color:#00009f">public</span><span class="token base-clause"> std</span><span class="token base-clause double-colon punctuation" style="color:#393A34">::</span><span class="token base-clause class-name">enable_shared_from_this</span><span class="token base-clause operator" style="color:#393A34">&lt;</span><span class="token base-clause class-name">RenderNode</span><span class="token base-clause operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">public</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">RenderNode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">string str</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">str_</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">str</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">node_</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">nullptr</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">child_</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">nullptr</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">reuseId_</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">string str_</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">shared_ptr</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">ArkUIBaseNode</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> node_</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">string reuseId_</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">shared_ptr</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">ArkUIBaseNode</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> child_</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">ArkUIWaterFlowAdapter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">handle_</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">OH_ArkUI_NodeAdapter_Create</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 初始化懒加载数据。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">int32_t</span><span class="token plain"> i </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> i </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">50</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> i</span><span class="token operator" style="color:#393A34">++</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> renderNode </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token generic-function function" style="color:#d73a49">make_shared</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">RenderNode</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token function" style="color:#d73a49">to_string</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">i</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        renderNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">reuseId_ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"reuse"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        data_</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">emplace_back</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">renderNode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 设置懒加载数据。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">OH_ArkUI_NodeAdapter_SetTotalNodeCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">handle_</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> data_</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">size</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 设置懒加载回调事件。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">OH_ArkUI_NodeAdapter_RegisterEventReceiver</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">handle_</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> OnStaticAdapterEvent</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ul><li>将 NodeAdapter 绑定到对应的容器组件上</li></ul><div class="language-cpp codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-cpp codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 引入懒加载模块。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">SetLazyAdapter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">shared_ptr</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">ArkUIWaterFlowAdapter</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">adapter</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">assert</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">handle_</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ArkUI_AttributeItem item</span><span class="token punctuation" style="color:#393A34">{</span><span class="token keyword" style="color:#00009f">nullptr</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">nullptr</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> adapter</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">GetHandle</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    nativeModule_</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">setAttribute</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">handle_</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> NODE_WATER_FLOW_NODE_ADAPTER</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">item</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ul><li>回调处理函数定义</li></ul><div class="language-cpp codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-cpp codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">OnAdapterEvent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ArkUI_NodeAdapterEvent </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> type </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">OH_ArkUI_NodeAdapterEvent_GetType</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> index </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">OH_ArkUI_NodeAdapterEvent_GetItemIndex</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">switch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">type</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> NODE_ADAPTER_EVENT_ON_GET_NODE_ID</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">OnNewItemIdCreated</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">break</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> NODE_ADAPTER_EVENT_ON_ADD_NODE_TO_ADAPTER</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">OnNewItemAttached</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">break</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">OnItemDetached</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">break</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">default</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">break</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="懒加载">懒加载<a href="#懒加载" class="hash-link" aria-label="懒加载的直接链接" title="懒加载的直接链接">​</a></h4><p>懒加载特性会根据容器组件能够容纳显示的组件数量按需加载数据，创建对应的 Item 组件，并挂载在容器组件树根组件上。整体渲染流程如下图所示(备注:图片来自 HarmonyOS NEXT 官网)：</p><p><img loading="lazy" src="https://img13.360buyimg.com/img/jfs/t1/207202/22/32362/17737/66f23379F2bc52482/b6ad124768016e4d.png" alt="渲染流程" class="img_ZOZK"></p><p>懒加载创建组件示例代码：</p><ul><li>创建组件唯一 ID：</li></ul><div class="language-cpp codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-cpp codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 分配ID给需要显示的Item，用于ReloadAllItems场景的元素diff。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">OnNewItemIdCreated</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ArkUI_NodeAdapterEvent </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> index </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">OH_ArkUI_NodeAdapterEvent_GetItemIndex</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">static</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">hash</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">string</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> hashId </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token generic-function function" style="color:#d73a49">hash</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">std</span><span class="token generic-function generic class-name double-colon punctuation" style="color:#393A34">::</span><span class="token generic-function generic class-name">string</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> id </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">hashId</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token function" style="color:#d73a49">to_string</span><span class="token punctuation" style="color:#393A34">(</span><span class="token generic-function function" style="color:#d73a49">reinterpret_cast</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">std</span><span class="token generic-function generic class-name double-colon punctuation" style="color:#393A34">::</span><span class="token generic-function generic class-name">uintptr_t</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data_</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">index</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">OH_ArkUI_NodeAdapterEvent_SetNodeId</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ul><li>处理组件上屏事件：</li></ul><div class="language-cpp codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-cpp codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 需要新的Item显示在可见区域。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">OnNewItemAttached</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ArkUI_NodeAdapterEvent </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">uint32_t</span><span class="token plain"> index </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">OH_ArkUI_NodeAdapterEvent_GetItemIndex</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">index </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> data_</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">size</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> newNode </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> data_</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">index</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// RecycleManager(newNode);</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 创建新的组件元素。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> flowItem </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token generic-function function" style="color:#d73a49">make_shared</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">ArkUIFlowItemNode</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> textNode </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token generic-function function" style="color:#d73a49">make_shared</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">ArkUITextNode</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    textNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">SetTextContent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">str_</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    textNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">SetFontSize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    textNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">SetPercentWidth</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0.5</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    textNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">SetHeight</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    textNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">SetTextAlign</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ARKUI_TEXT_ALIGNMENT_CENTER</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">uint32_t</span><span class="token plain"> color </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0xFFFF0000</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    textNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">SetBackgroundColor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">color</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    flowItem</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">AddChild</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">textNode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">node_ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> flowItem</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">child_ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> textNode</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">node_</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    items_</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">insert_or_assign</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">node_</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">GetHandle</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> newNode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 设置需要展示的组件元素。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">OH_ArkUI_NodeAdapterEvent_SetItem</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">node_</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">GetHandle</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="预加载">预加载<a href="#预加载" class="hash-link" aria-label="预加载的直接链接" title="预加载的直接链接">​</a></h3><p>尽管按需加载列表项可以提升长列表的性能，但在用户快速滑动，特别是数据中包含大量图片和视频的情况下，可能会出现渲染不及时导致的白块现象，影响用户体验。</p><p>为了解决该问题，NodeAdapter 支持设置 cachedCount 属性来指定缓存数量，当设置 cachedCount 后，除了容器类组件内显示的 Item 外，还将预先创建 cachedCount 条数据作为缓存。这样可以有效避免白块现象，确保用户在滑动过程中有流畅的视觉体验。</p><p>一般而言，<strong>缓存的 cachedCount 为一屏显示数据的一半的时候，效果较好。</strong></p><p>整体渲染流程如下图所示(备注:图片来自 HarmonyOS NEXT 官网)：</p><p><img loading="lazy" src="https://img13.360buyimg.com/img/jfs/t1/124119/7/48766/62271/66f2337cF99412cfe/67e9b88733775837.png" alt="渲染流程" class="img_ZOZK"></p><ul><li>cachedCount 的使用方法如下所示：</li></ul><div class="language-cpp codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-cpp codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">setCachedCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">int32_tcount</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ArkUI_NumberValue value</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">i32 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> count</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    item </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">value</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    nativeModule_</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">setAttribute</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">handle_</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> NODE_WATER_FLOW_CACHED_COUNT</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">item</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="数据预请求">数据预请求<a href="#数据预请求" class="hash-link" aria-label="数据预请求的直接链接" title="数据预请求的直接链接">​</a></h4><p>尽管长列表组件支持无限滚动，但在实际业务场景中，上层业务通常会采用分页方式来加载数据。以京东 App 首页为例，一屏可以显示 6 个商品，一次网络请求加载 20 条数据。如果在滑动到底部时才发起请求加载新数据，可能会出现明显的加载停顿，尤其是在网络时延较高的情况下，这种停顿会更加突出。</p><p>为了解决该问题，我们可以调整请求新数据的时机。具体来说，当还未上屏的数据数量低于某个阈值时，就提前发起请求来加载新数据。这样可以预先准备好下一批数据，避免在滑动过程出现加载停顿。</p><p>数据预请求示例代码：</p><ul><li>注册 NODE_WATER_FLOW_ON_SCROLL_INDEX 事件，获取当前容器显示的起始位置/终止索引</li></ul><div class="language-cpp codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-cpp codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">RegisterOnScrollIndex</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    nativeModule_</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">registerNodeEvent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">handle_</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> NODE_WATER_FLOW_ON_SCROLL_INDEX</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">nullptr</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ul><li>请求加载新数据</li></ul><div class="language-cpp codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-cpp codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> callback </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ArkUI_NodeEvent</span><span class="token operator" style="color:#393A34">*</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> handle </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">OH_ArkUI_NodeEvent_GetNodeHandle</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> eventType </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">OH_ArkUI_NodeEvent_GetEventType</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> target </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">OH_ArkUI_NodeEvent_GetTargetId</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> componentEvent </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">OH_ArkUI_NodeEvent_GetNodeComponentEvent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">eventType </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> NODE_WATER_FLOW_ON_SCROLL_INDEX</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">int</span><span class="token plain"> start </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> componentEvent</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">i32</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">int</span><span class="token plain"> end </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> componentEvent</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">i32</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">end </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> waterFlow</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">adapter_</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">GetDataSize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">int</span><span class="token plain"> size </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> waterFlow</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">UpdateSectionSize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">20</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">int</span><span class="token plain"> i </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> i </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">20</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> i</span><span class="token operator" style="color:#393A34">++</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                waterFlow</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">adapter_</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">InsertItem</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">size </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> i</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token function" style="color:#d73a49">to_string</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">size</span><span class="token operator" style="color:#393A34">+</span><span class="token plain">i</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>下面是使用懒加载、预加载和数据预请求的前后对比（左边为使用后，右边为使用前）：</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="组件复用">组件复用<a href="#组件复用" class="hash-link" aria-label="组件复用的直接链接" title="组件复用的直接链接">​</a></h4><p>对于离开可视区域的组件，NodeAdapter 不会主动释放它们，而是通过 NODE_ADAPTER_EVENT_ON_REMOVE_NODE_FROM_ADAPTER 回调通知开发者。这样可以让开发者根据实际情况决定是释放组件对象，还是将其放入复用缓存池以便后续使用。</p><p>Taro 采用了组件复用的策略，<strong>当组件离开可视区域时，并不会立即释放其占用的资源，而是将其放入复用缓存池中。</strong></p><p>当用户滚动到其他位置，需要渲染新的子组件时，Taro 会先检查缓存池中是否有结构相似的组件。如果找到了相似的组件，就直接从缓存池中取出该组件，并更新其属性和事件。</p><p>这种做法可以有效降低应用的内存占用，减少频繁创建和销毁组件带来的性能开销，提升应用的整体性能和用户体验。</p><p>原理如下图所示：</p><p><img loading="lazy" src="https://img14.360buyimg.com/img/jfs/t1/101938/1/51422/52628/66f2328dFed641532/809db22518d091df.png" alt="组件复用" class="img_ZOZK"></p><p>识别子组件相似性对于组件复用至关重要。由于上层业务开发者对 Item 的结构和子组件的相似性有着全面的了解, 为了更准确地识别可复用的子组件, Taro 框架引入了一个<strong>新的属性 reuseId</strong>，表示以当前组件为根组件的子组件树可以被复用。开发者可以根据业务场景和 Item 结构特点，灵活地标记出具有相似性的子组件。</p><p>当列表中存在多个具有相同 reuseId 的子组件树时, 框架会自动识别并复用这些子组件, 而不是重新创建和渲染它们。</p><p><img loading="lazy" src="https://img30.360buyimg.com/img/jfs/t1/103040/30/52968/367786/66f25fd0F0b7dd064/1a34cfc6a3cb4436.png" alt="首页组件复用" class="img_ZOZK"></p><p>组件复用是 Taro 优化性能的重要策略，其流程如下：</p><ol><li><p>从缓存池中获取与当前组件 reuseId 相匹配的子组件。</p></li><li><p>对获取到的组件进行类型比较(diff)：</p><ul><li>如果组件类型相同，则认为可以直接复用该组件，进入下一步；</li><li>如果组件类型不同，则销毁该组件及其所有子组件，并创建对应的新组件后挂载，结束复用流程。</li></ul></li><li><p>对于类型相同的组件，进一步比较保其属性：</p><ul><li>通过 diff 算法识别出属性的差异部分；</li><li>将差异部分的属性更新到复用的组件上，保证组件的属性与当前组件状态一致。</li></ul></li><li><p>更新组件的事件绑定，确保事件处理函数与当前组件的事件处理逻辑相匹配。</p></li><li><p>递归地对该组件的子组件重复步骤 2 到步骤 4，实现对整个子树的深度复用。</p></li></ol><ul><li>组件复用示例代码：</li></ul><div class="language-cpp codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-cpp codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 管理NodeAdapter生成的元素。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">unordered_map</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">ArkUI_NodeHandle</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">shared_ptr</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">RenderNode</span><span class="token operator" style="color:#393A34">&gt;&gt;</span><span class="token plain"> items_</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 管理回收复用组件池，key为 reuseId</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">unordered_map</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">string</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">unordered_set</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">shared_ptr</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">RenderNode</span><span class="token operator" style="color:#393A34">&gt;&gt;</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> cachedItems_</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ul><li>组件回收逻辑：</li></ul><div class="language-cpp codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-cpp codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Item从可见区域移除。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">OnItemDetached</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ArkUI_NodeAdapterEvent </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> item </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">OH_ArkUI_NodeAdapterEvent_GetRemovedNode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 放置到缓存池中进行回收复用。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> it </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> items_</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">find</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">item</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">it </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> items_</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">end</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> renderNode </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> it</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">second</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">renderNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">reuseId_</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">empty</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            cachedItems_</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">renderNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">reuseId_</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">emplace</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">renderNode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ul><li>组件上屏逻辑：</li></ul><div class="language-cpp codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-cpp codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">bool</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">isInitilaizedNode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">shared_ptr</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">RenderNode</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> newNode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">node_</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> reuseId </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">reuseId_</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> it </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cachedItems_</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">find</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">reuseId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">it </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> cachedItems_</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">end</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> setIt </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> it</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">second</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">find</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">newNode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">setIt </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> it</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">second</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">end</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                it</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">second</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">erase</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">setIt</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">initRenderNode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">shared_ptr</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">RenderNode</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> newNode</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">shared_ptr</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">RenderNode</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> reuseNode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">node_ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> reuseNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">node_</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">child_ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> reuseNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">child_</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> newText </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token generic-function function" style="color:#d73a49">dynamic_pointer_cast</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">ArkUITextNode</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">child_</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 处理子组件，属性 diff 和更新</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">newText </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">str_ </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> reuseNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">str_</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        newText</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">SetTextContent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">str_</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    reuseNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">node_ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">nullptr</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    reuseNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">child_ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">nullptr</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">RecycleManager</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">shared_ptr</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">RenderNode</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> newNode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">isInitilaizedNode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">newNode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> it </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cachedItems_</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">find</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">reuseId_</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">it </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> cachedItems_</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">end</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!</span><span class="token plain">it</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">second</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">empty</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// 组件复用</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> reuseIt </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> it</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">second</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">begin</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> reuseNode </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">reuseIt</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">initRenderNode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">newNode</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> reuseNode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        it</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">second</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">erase</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">reuseIt</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// 创建新的元素。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> flowItem </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token generic-function function" style="color:#d73a49">make_shared</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">ArkUIFlowItemNode</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> textNode </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> std</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token generic-function function" style="color:#d73a49">make_shared</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">ArkUITextNode</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        textNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">SetTextContent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">str_</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        textNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">SetFontSize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        textNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">SetPercentWidth</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0.5</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        textNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">SetHeight</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        textNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">SetTextAlign</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ARKUI_TEXT_ALIGNMENT_CENTER</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">uint32_t</span><span class="token plain"> color </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0xFFFF0000</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        textNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">SetBackgroundColor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">color</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        flowItem</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">AddChild</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">textNode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">node_ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> flowItem</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">child_ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> textNode</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 需要新的Item显示在可见区域。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">OnNewItemAttached</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ArkUI_NodeAdapterEvent </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">uint32_t</span><span class="token plain"> index </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">OH_ArkUI_NodeAdapterEvent_GetItemIndex</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">index </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> data_</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">size</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">auto</span><span class="token plain"> newNode </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> data_</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">index</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">RecycleManager</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">newNode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">node_</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    items_</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">insert_or_assign</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">node_</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">GetHandle</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> newNode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 设置需要展示的元素。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">OH_ArkUI_NodeAdapterEvent_SetItem</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> newNode</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">node_</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">GetHandle</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="总结">总结<a href="#总结" class="hash-link" aria-label="总结的直接链接" title="总结的直接链接">​</a></h2><p>本文重点阐述了一种针对长列表类型组件的优化方案，利用 CAPI 提供的能力，结合组件复用方案，有效地提升了页面性能。文中还附带了 CAPI 实现 demo 以供参考。目前这一方案已经在京东 App 的多个核心页面得到应用，包括首页、搜索结果页和购物车等，实践表明页面的<strong>加载和更新速度平均提升了 38%，组件复用降低京东 App 首页 54% 的内存占用</strong>。该方案在实际项目中的成功应用，为 CAPI 实现长列表组件，及长列表组件性能优化提供了有益的参考和借鉴。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="下期预告">下期预告<a href="#下期预告" class="hash-link" aria-label="下期预告的直接链接" title="下期预告的直接链接">​</a></h3><p><img loading="lazy" src="https://img30.360buyimg.com/img/jfs/t1/244935/34/24667/267488/673ffa36F6da4f1ed/c22723a2134f55ab.png" alt="尾图" class="img_ZOZK"></p>]]></content:encoded>
            <category>v4</category>
            <category>harmony</category>
        </item>
        <item>
            <title><![CDATA[Taro 鸿蒙技术内幕系列（四）：JDImage 自研鸿蒙图片库]]></title>
            <link>https://docs.taro.zone/blog/2024/11/14/harmony-image</link>
            <guid>https://docs.taro.zone/blog/2024/11/14/harmony-image</guid>
            <pubDate>Thu, 14 Nov 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[基于 Taro 打造的京东鸿蒙 APP 已跟随鸿蒙 Next 系统公测，本系列文章将深入解析 Taro 如何实现使用 React 开发高性能鸿蒙应用的技术内幕]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-09-26-21-35NaN6T9eOyiqx9Z6.png" class="img_ZOZK"></p><blockquote><p>基于 Taro 打造的京东鸿蒙 APP 已跟随鸿蒙 Next 系统公测，本系列文章将深入解析 Taro 如何实现使用 React 开发高性能鸿蒙应用的技术内幕</p></blockquote><h2 class="anchor anchorWithStickyNavbar_mCi1" id="背景">背景<a href="#背景" class="hash-link" aria-label="背景的直接链接" title="背景的直接链接">​</a></h2><p>2024 年初，京东正式启动了鸿蒙 APP 的开发工作。由于电商 APP 大量依赖图片来展示商品信息，对图片库的性能和加载体验要求极高，因此图片库被作为核心基础能力提前纳入京东鸿蒙首期基础建设计划。本文将详细介绍京东自研鸿蒙图片库的开发过程及其技术原理。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="技术实现">技术实现<a href="#技术实现" class="hash-link" aria-label="技术实现的直接链接" title="技术实现的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="前期调研">前期调研<a href="#前期调研" class="hash-link" aria-label="前期调研的直接链接" title="前期调研的直接链接">​</a></h4><p>经过前期的调研，我们发现<code>HarmonyOS</code>平台的网络图片加载主要依赖以下两种实现方式：</p><h6 class="anchor anchorWithStickyNavbar_mCi1" id="系统---image-组件">系统 - Image 组件<a href="#系统---image-组件" class="hash-link" aria-label="系统 - Image 组件的直接链接" title="系统 - Image 组件的直接链接">​</a></h6><p>仅仅提供基础的图片组件用于图片加载，但功能、图片格式支持和扩展性都较弱，无法实现更丰富的图片库能力。存在的一些问题：</p><ul><li>性能一般，尤其在同时加载多个图片时较慢</li><li>不支持 AVIF 图片格式并且无法扩展</li><li>无法搭建完善的质量监控体系</li><li>无法控制和扩展图片下载、解码、缓存流程，无法加入更多的性能优化手段和扩展定制</li></ul><h5 class="anchor anchorWithStickyNavbar_mCi1" id="第三方库---imageknife">第三方库 - ImageKnife<a href="#第三方库---imageknife" class="hash-link" aria-label="第三方库 - ImageKnife的直接链接" title="第三方库 - ImageKnife的直接链接">​</a></h5><p>社区参考 Android 图片库<code>Glide</code>的<code>HarmonyOS</code>版本实现，使用<code>ArkTS</code>开发。虽然能力比系统<code>Image</code>组件完善，但在性能、稳定性和扩展性都无法满足诉求。存在的一些问题：</p><ul><li>性能一般</li><li>代码质量一般，存在一些 bug 和 Crash</li><li>整体架构、扩展性设计不够，无法加入更多的性能优化手段和扩展定制</li></ul><p>由于系统<code>Image</code>组件和<code>ImageKnife</code>开源库无法满足诉求，我们决定自主研发鸿蒙图片库。此外，随着<code>HarmonyOS</code>系统的引入，客户端在原有的<code>iOS</code>和<code>Android</code>基础设施上新增了一个平台，增加了维护成本。现有 iOS/Android 图片库也存在双端部分能力不一致的情况。集团内部期望未来的客户端基础设施能够支持跨端复用，以提高开发效率和一致性。</p><p>因此,团队选择基于<code>C++</code>为核心进行图片库开发，这种实现方式有以下优势：</p><ul><li>跨端复用：将更多的公共模块下沉到<code>C++</code>层以实现跨端复用，未来图片库可以扩展到<code>iOS</code>和<code>Android</code>平台。</li><li>适配 Taro：<code>Taro Harmony</code>框架基于鸿蒙<code>CAPI</code>进行开发，需要依赖<code>CAPI</code>的图片组件，可以避免<code>ArkTS</code>和<code>C++</code>之间频繁通信带来的性能损耗。</li><li>高性能：<code>C++</code>在运行时性能上优于<code>ArkTS</code>。</li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="jdimage-能力">JDImage 能力<a href="#jdimage-能力" class="hash-link" aria-label="JDImage 能力的直接链接" title="JDImage 能力的直接链接">​</a></h3><p><img loading="lazy" src="https://img14.360buyimg.com/img/s1500x0_jfs/t1/211373/11/47107/74028/6735b559F4b6199c8/7b893857d765533c.png" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="架构设计">架构设计<a href="#架构设计" class="hash-link" aria-label="架构设计的直接链接" title="架构设计的直接链接">​</a></h3><p>由于图片库使用<code>C++</code>进行跨端开发，面临不同平台之间的兼容性挑战，部分功能需要依赖于端侧的特定实现。为了应对这些复杂性，需要提供了一定的抽象能力，使得在支持不同平台时能够灵活调整和替换模块具体实现。在设计过程中也参考了<code>iOS</code>/<code>Android</code>业界成熟的开源库，如<code>SDWebImage</code>、<code>Glide</code>和<code>Fresco</code>。</p><p>我们选择采用<code>模块化</code>和<code>架构分层</code>的思想来设计图片库的整体架构。<code>模块化</code>将图片库拆分为多个独立模块，不仅增强了系统的灵活性和可替换性，还使我们能够在不同的客户端环境和图片加载场景下进行灵活调整和扩展。<code>架构分层</code>将图片库划分为<code>Core</code>层和<code>客户端</code>层，<code>Core</code>层使用<code>C++</code>进行开发支持跨端复用，<code>客户端</code>层用于特定模块的平台差异化实现。这种设计方法有效地解决了跨端开发的复杂性问题，提高了系统的可维护性和稳定性。</p><p><img loading="lazy" src="https://img12.360buyimg.com/img/s1500x0_jfs/t1/156196/18/48708/97179/6716635eF6ae28be0/f1ea426d1b369f28.png" alt="图片库架构图" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="核心模块介绍">核心模块介绍<a href="#核心模块介绍" class="hash-link" aria-label="核心模块介绍的直接链接" title="核心模块介绍的直接链接">​</a></h4><p>图片库主要分为<code>图片缓存</code>、<code>解码器</code>、<code>图片源拉取</code>、<code>性能监控</code>、<code>图片组件</code>这几个核心功能模块，模块之间不直接依赖可以被单独调用执行。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="图片缓存">图片缓存<a href="#图片缓存" class="hash-link" aria-label="图片缓存的直接链接" title="图片缓存的直接链接">​</a></h5><ul><li>内存缓存：用于保存已解码后的位图对象，避免每次图片加载都要进行图片解码，从而提高加载性能。我们采用<code>LRU</code>算法实现来高效利用内存，同时提供了最大内存缓存大小和设备内存紧张时回收一部分图片缓存的策略，以动态调整内存消耗。</li><li>磁盘缓存：用于保存下载后的网络图片在 APP 沙盒中，降低网络消耗并提高加载性能。提供了基础的图片读取、保存、删除、缓存大小读取等能力，同时支持多线程并行执行，实现图片磁盘缓存的高性能读写能力。</li></ul><h5 class="anchor anchorWithStickyNavbar_mCi1" id="图片数据源拉取">图片数据源拉取<a href="#图片数据源拉取" class="hash-link" aria-label="图片数据源拉取的直接链接" title="图片数据源拉取的直接链接">​</a></h5><p>图片数据源拉取模块负责图片数据源读取能力。我们在<code>HarmonyOS</code>平台上提供了多个数据源实现，处理不同格式的图片数据源拉取任务：</p><ul><li>HTTP：提供<code>HTTP</code>链接的网络图片下载能力。底层使用<code>Cornet</code>进行实现，实现高性能的多请求并行网络下载能力，同时方便未来实现跨端复用。</li><li>Base64：提供<code>Base64</code>格式的图片格式读取能力。</li></ul><h5 class="anchor anchorWithStickyNavbar_mCi1" id="解码器">解码器<a href="#解码器" class="hash-link" aria-label="解码器的直接链接" title="解码器的直接链接">​</a></h5><p>图片解码器模块负责将编码后的图片格式解码为操作系统的图片格式用于显示。我们在<code>HarmonyOS</code>平台上提供了多个解码器实现处理不同格式的图片解码任务，可以支持多线程并行执行提高解码性能。</p><ul><li>系统解码器：利用<code>HarmonyOS</code>系统提供的图片解码器可以支持<code>PNG</code>、<code>JPG</code>、<code>GIF</code>、<code>WebP</code>、<code>SVG</code>等常用图片的高性能硬件解码能力。</li><li>AVIF 解码器：由于京东 APP 大量使用<code>AVIF</code>图片格式，通过集成<code>libavif</code>库支持<code>AVIF</code>图片的解码能力。</li></ul><h5 class="anchor anchorWithStickyNavbar_mCi1" id="性能监控">性能监控<a href="#性能监控" class="hash-link" aria-label="性能监控的直接链接" title="性能监控的直接链接">​</a></h5><ul><li>加载性能监控：统计图片加载过程的各阶段耗时数据、缓存命中率、图片格式、资源消耗等信息，用来搭建图片加载性能数据平台和性能优化参考指标。</li><li>异常监控：捕获图片加载过程中的各种错误异常，用来搭建图片加载线上异常告警和根据场景进行异常重试。</li></ul><h5 class="anchor anchorWithStickyNavbar_mCi1" id="图片组件">图片组件<a href="#图片组件" class="hash-link" aria-label="图片组件的直接链接" title="图片组件的直接链接">​</a></h5><p>图片组件基于系统<code>Image</code>组件进行封装，通过<code>src</code>属性传入解码后的<code>PixelMap</code>进行渲染。同时提供了一些额外的加载属性用于提升加载性能，例如添加<code>lazy</code>属性支持图片懒加载、<code>sourceSize</code>属性指定图片显示尺寸、 <code>priority</code>属性设置加载优先级。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="性能优化">性能优化<a href="#性能优化" class="hash-link" aria-label="性能优化的直接链接" title="性能优化的直接链接">​</a></h4><h5 class="anchor anchorWithStickyNavbar_mCi1" id="图片加载流水线">图片加载流水线<a href="#图片加载流水线" class="hash-link" aria-label="图片加载流水线的直接链接" title="图片加载流水线的直接链接">​</a></h5><p><img loading="lazy" src="https://img11.360buyimg.com/img/s1500x0_jfs/t1/164538/21/52510/82513/6735a1cbF89064750/e6349d83c4379355.png" alt="图片加载流水线" class="img_ZOZK"><br>
<!-- -->图片加载流水线借鉴了<code>Fresco</code>的流水线设计。采用流水线方式可以灵活调整流水线的执行流程，可以通过搭配不同的流水线子任务以满足不同类型的图片加载任务和不同客户端的差异化诉求。同时也添加了很多优化策略提升优化图片加载流水线的执行性能。<code>图片加载流水线</code>主要包含以下这些能力：</p><ul><li>调度执行顺序：管理图片加载的流水线子任务执行顺序，确保各个步骤按照预期的顺序进行执行。</li><li>调用功能模块：灵活调用不同的功能模块，如解码、缓存、网络请求等，根据具体需求动态调整，以适应不同的图片加载任务。</li><li>线程调度：对流水线子任务进行执行线程调度，确保非主线程任务在子线程中并行执行，主线程任务在主线程中执行。使用<code>HarmonyOS</code>提供的<code>FFRT</code>框架进行高性能线程调度。</li><li>重复任务聚合：识别重复的加载任务，避免非必要的重复请求，提高加载性能。</li><li>加载监控：统计加载过程性能数据和错误异常。同时加载数据也可以用于回溯分析异常问题。</li><li>取消/重试机制：实现图片加载的取消和重试逻辑。提前取消可以降低非必要的图片加载请求，重试机制可以尽可能降低线上显示异常。</li></ul><h5 class="anchor anchorWithStickyNavbar_mCi1" id="面向京东图片服务器设计">面向京东图片服务器设计<a href="#面向京东图片服务器设计" class="hash-link" aria-label="面向京东图片服务器设计的直接链接" title="面向京东图片服务器设计的直接链接">​</a></h5><p>京东<code>图片服务器</code>提供了多种处理功能，例如图片<code>格式转换</code>、<code>图片降质</code>、<code>图片缩放</code>、<code>图片圆角</code>、<code>灰度图</code>等。这些功能通过在图片<code>URL</code>中添加特定图片处理参数实现，图片服务器会根据参数设置提前将图片处理完成并保存到<code>CDN</code>服务器。</p><p>我们可以通过在流水线预处理阶段，针对京东域名图片自动追加特定的图片处理参数降低网络流量传输消耗，例如根据视图实际显示大小添加缩放参数，添加降质参数，使用<code>.avif</code>格式图片降低网络传输大小。同时在磁盘缓存模块实现中，我们也通过实现优化策略让部分相同的图片可以进行磁盘缓存复用避免重复下载。</p><p><img loading="lazy" src="https://img14.360buyimg.com/img/s1500x0_jfs/t1/195443/1/49323/41899/67175036F3cad4599/0966c4efebe69ce4.png" alt="URL预处理" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="质量保障">质量保障<a href="#质量保障" class="hash-link" aria-label="质量保障的直接链接" title="质量保障的直接链接">​</a></h4><p>作为一个新上线的基础组件，保证业务稳定性至关重要。为了快速发现并解决问题，图片库中实现了线上监控和异常恢复机制。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="线上监控">线上监控<a href="#线上监控" class="hash-link" aria-label="线上监控的直接链接" title="线上监控的直接链接">​</a></h5><p>####### 异常监控</p><p>如前文所述，图片加载流水线涉及多个子任务，加载异常可能出现在任何一个环节。为了精准定位问题，图片库为每个子任务定制了不同的异常对象，每个异常对象包含独特的错误码，并会根据严重程度将其分为异常和警告两个级别。当加载流程结束时会根据上报策略进行异常上报。</p><h6 class="anchor anchorWithStickyNavbar_mCi1" id="性能监控-1">性能监控<a href="#性能监控-1" class="hash-link" aria-label="性能监控的直接链接" title="性能监控的直接链接">​</a></h6><p>除了加载成功率，加载速度也是图片库的核心指标，为了能正确还原加载流程，图片库按照配置策略记录每个子任务开始和结束时间，一方面用于统计各阶段的加载耗时，另一方面还作为异常定位时的重要辅助信息。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="异常恢复">异常恢复<a href="#异常恢复" class="hash-link" aria-label="异常恢复的直接链接" title="异常恢复的直接链接">​</a></h5><p>除了从监控发现问题，为了将异常影响降至最低，图片库也实现了流水线失败重试、组件异常降级及线上配置降级三种异常恢复机制。</p><h6 class="anchor anchorWithStickyNavbar_mCi1" id="流水线失败重试">流水线失败重试<a href="#流水线失败重试" class="hash-link" aria-label="流水线失败重试的直接链接" title="流水线失败重试的直接链接">​</a></h6><p>在图片库任务预处理阶段，会基于配置策略对图片链接进行处理，如统一图片域名，图片格式转换等等。由于图片链接拼接规则复杂，无法保证所有链接进行转化处理后仍能正常加载，因此图片库流水线中增加了重试机制，会根据错误类型使用不同的重试策略。</p><h6 class="anchor anchorWithStickyNavbar_mCi1" id="组件异常降级">组件异常降级<a href="#组件异常降级" class="hash-link" aria-label="组件异常降级的直接链接" title="组件异常降级的直接链接">​</a></h6><p>对于经过重试仍然失败的任务，图片库组件侧会尝试直接使用系统<code>Image</code>组件进行兜底渲染，以防止出现图片库特定格式兼容性的问题。<br>
<!-- -->此外，因为图片库流水线加载完成不意味着图片一定能正常渲染，图片的最终渲染成功与否取决于最终图片是否能在图片组件完成渲染上屏。因此，针对流水线成功但组件渲染失败的情况，图片库也会尝试降级到系统<code>Image</code>组件加载原始链接。<br>
<!-- -->如果降级后仍然失败，则会按照业务配置顺序加载异常兜底图。</p><h6 class="anchor anchorWithStickyNavbar_mCi1" id="线上配置降级">线上配置降级<a href="#线上配置降级" class="hash-link" aria-label="线上配置降级的直接链接" title="线上配置降级的直接链接">​</a></h6><p>由于线上环境复杂，可能会出现仅发生在特定用户、系统版本、APP 版本的异常，图片库也基于移动配置 SDK 支持了针对特定用户、机型、版本强制降级的能力。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="加载对比">加载对比<a href="#加载对比" class="hash-link" aria-label="加载对比的直接链接" title="加载对比的直接链接">​</a></h4><p><img loading="lazy" src="https://storage.360buyimg.com/jxfe/ppms/c/202410/22/b377d9f5-e767-4c14-97a6-ee0ba489c649.gif" alt="加载对比图" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="未来规划">未来规划<a href="#未来规划" class="hash-link" aria-label="未来规划的直接链接" title="未来规划的直接链接">​</a></h3><p>我们也梳理了未来可能尝试的一些图片库优化方向：</p><ul><li>网络优化：弱网优化、HTTPDNS 等策略优化图片网络下载性能。</li><li>性能优化：提高磁盘缓存/内存缓存复用率、使用堆内存池减少堆内存分配、优化 GIF 播放、低端机优化等。</li><li>适配京东图片服务器能力：利用服务器图片处理能力，如使用不同的缩放尺寸参数、清晰度、图片格式、图片域名等参数提高加载性能。</li><li>能力完善：提供更多图片加载参数、支持更多图片类型等能力满足不同业务的加载诉求。</li><li>更多平台支持：扩展到<code>iOS</code>、<code>Android</code>等更多平台。</li><li>提升图片效果：使用不同的插值方式、超分技术、HDR 等能力提高图片显示效果。</li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="总结">总结<a href="#总结" class="hash-link" aria-label="总结的直接链接" title="总结的直接链接">​</a></h2><p>京东自研的鸿蒙图片库通过模块化设计和流水线调度策略，成功解决了跨端开发的复杂性问题，同时提高了系统的可维护性和稳定性。通过实现多重异常容错机制，保证了业务的稳定性。该图片库不仅满足了京东 APP 在鸿蒙系统上的高性能图片加载需求，还为未来的跨平台开发奠定了基础。</p><p>目前，图片库已通过<code>Taro</code>集成到<code>京东</code>鸿蒙 APP 中上线使用，在图片加载体验和稳定性上有不错的表现。未来，我们将继续优化图片库的性能，扩展功能，并探索在更多平台上的应用可能。同时，我们也会持续关注业界最新的图片处理技术，不断改进我们的解决方案，为用户提供更好的图片加载体验。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="系列往期精选">系列往期精选<a href="#系列往期精选" class="hash-link" aria-label="系列往期精选的直接链接" title="系列往期精选的直接链接">​</a></h3><ul><li><a href="/blog/2024/09/11/harmony-react-on-arkts"><strong>京东鸿蒙上线前瞻——使用 Taro 打造高性能原生应用</strong></a></li><li><a href="/blog/2024/09/29/harmony-react-on-arkts"><strong>Taro 鸿蒙技术内幕系列（一）：如何将 React 代码跑在 ArkUI 上</strong></a></li><li><a href="/blog/2024/10/16/harmony-w3c-css"><strong>Taro 鸿蒙技术内幕系列（二）：如何让 W3C 标准的 CSS 跑在鸿蒙上</strong></a></li><li><a href="/blog/2024/11/01/harmony-native-events"><strong>Taro 鸿蒙技术内幕系列（三） - 多语言场景下的通用事件系统设计</strong></a></li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="下期预告">下期预告<a href="#下期预告" class="hash-link" aria-label="下期预告的直接链接" title="下期预告的直接链接">​</a></h3><p><img loading="lazy" src="https://img13.360buyimg.com/img/s800x0_jfs/t1/121427/40/52661/58146/6735bde4F3f037868/f6ecc76a38499abf.png" alt="下期预告" class="img_ZOZK"></p>]]></content:encoded>
            <category>v4</category>
            <category>harmony</category>
        </item>
        <item>
            <title><![CDATA[Taro 鸿蒙技术内幕系列（三） - 多语言场景下的通用事件系统设计]]></title>
            <link>https://docs.taro.zone/blog/2024/11/01/harmony-native-events</link>
            <guid>https://docs.taro.zone/blog/2024/11/01/harmony-native-events</guid>
            <pubDate>Fri, 01 Nov 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[基于 Taro 打造的京东鸿蒙 APP 已跟随鸿蒙 Next 系统公测，本系列文章将深入解析 Taro 如何实现使用 React 开发高性能鸿蒙应用的技术内幕]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-09-26-21-35NaN6T9eOyiqx9Z6.png" alt="title_logo" class="img_ZOZK"></p><blockquote><p>基于 Taro 打造的京东鸿蒙 APP 已跟随鸿蒙 Next 系统公测，本系列文章将深入解析 Taro 如何实现使用 React 开发高性能鸿蒙应用的技术内幕</p></blockquote><h2 class="anchor anchorWithStickyNavbar_mCi1" id="背景">背景<a href="#背景" class="hash-link" aria-label="背景的直接链接" title="背景的直接链接">​</a></h2><p>在鸿蒙生态系统中，虽然原生应用通常基于 ArkTS 实现，但在实际研发过程中发现，使用 C++ 可以显著提升应用框架和业务的性能表现。随着鸿蒙系统的不断迭代升级，不同语言环境间的协作已成为不可或缺的开发范式，共同构建了更丰富的研发生态。</p><p>Taro 通过接入鸿蒙端的 <code>C-API</code> 相关能力，将组件、样式布局等运行时逻辑下沉到 C++ 层，从而极大地提升了页面的渲染性能。</p><p>在这样的背景下，构建一套在 C++、ArkTS 等不同语言环境之间高效通信的事件系统，成为了一个极具价值，对于 Taro 来说也是必修的课题。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="多语言环境的事件处理机制">多语言环境的事件处理机制<a href="#多语言环境的事件处理机制" class="hash-link" aria-label="多语言环境的事件处理机制的直接链接" title="多语言环境的事件处理机制的直接链接">​</a></h2><p>在 Harmony 端的适配过程中，事件系统扮演着双重角色：不仅驱动应用、页面和各模块组件的生命周期，还因为 ArkTS 和业务代码（JS）之间存在人为设定的界限，需要事件作为桥梁，以便 JS 能够调用 ArkTS 的原生能力。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="跨语言环境事件驱动架构的设计考量">跨语言环境事件驱动架构的设计考量<a href="#跨语言环境事件驱动架构的设计考量" class="hash-link" aria-label="跨语言环境事件驱动架构的设计考量的直接链接" title="跨语言环境事件驱动架构的设计考量的直接链接">​</a></h3><p>在设计跨语言环境的事件驱动架构时，需要同时考虑 ArkTS、JS 和 C++ 等多个语言环境的限制和运行时差异。如何实现事件在这些环境之间的有序传递，以驱动页面和组件的生命周期，是事件系统设计的重要考量。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2024-11-01-Taro-harmony-event/1_mindmap.png" alt="mindmap" class="img_ZOZK"></p><p>通过 C++ 实现事件的底层逻辑，构建一个高效的事件管理系统，可以有效避免冗余接口的设计。同时，与鸿蒙的 <code>C-API</code> 支持的事件系统对接，将各类事件分发到不同语言环境，确保跨语言环境的事件分发与处理的有序性、高效性。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2024-11-01-Taro-harmony-event/2_roadmap.png" alt="roadmap" class="img_ZOZK"></p><p>回顾 Taro 开始适配鸿蒙至今，事件系统也随之经历了从简单到完善的演进历程。从最初在 ArkTS 方案中的基础实现，到随着 Taro for Harmony 方案迭代发展，事件系统的设计也面临 ArkTS 带来的一些限制。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="在-arkts-语言环境中事件架构的局限性">在 ArkTS 语言环境中事件架构的局限性<a href="#在-arkts-语言环境中事件架构的局限性" class="hash-link" aria-label="在 ArkTS 语言环境中事件架构的局限性的直接链接" title="在 ArkTS 语言环境中事件架构的局限性的直接链接">​</a></h3><p>基于 ArkTS 语言环境实现的事件架构，在性能方面存在较大局限性。特别是在事件冒泡过程中，性能较差的语法，和回调逻辑可能会导致性能严重劣化，甚至阻塞主线程。这不仅会影响应用的响应速度，更有甚者可能对整体用户体验产生负面影响。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2024-11-01-Taro-harmony-event/3_single_thread.png" alt="single_thread" class="img_ZOZK"></p><p>为了解决这些问题，提升性能以保证用户体验成为关键目标。通过将事件处理逻辑下沉到 C++ 层，并置于后台线程执行等优化手段。能够有效提高代码执行效率，同时避免逻辑阻塞主线程导致的延迟响应，以提升应用的流畅性，提供更佳的用户体验。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2024-11-01-Taro-harmony-event/4_mutli_thread.png" alt="mutli_thread" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="构建多语言环境下的事件系统">构建多语言环境下的事件系统<a href="#构建多语言环境下的事件系统" class="hash-link" aria-label="构建多语言环境下的事件系统的直接链接" title="构建多语言环境下的事件系统的直接链接">​</a></h2><p>在构建多语言环境下的事件系统时，首要考虑各种类型的事件，比如：鸿蒙提供的组件<a href="https://developer.huawei.com/consumer/cn/doc/harmonyos-references/5_1_u901a_u7528_u4e8b_u4ef6-V5" target="_blank" rel="noopener noreferrer">通用事件</a>、<a href="https://developer.huawei.com/consumer/cn/doc/harmonyos-references/5_3_u624b_u52bf_u5904_u7406-V5" target="_blank" rel="noopener noreferrer">手势</a>等。事件系统需要有效地管理这些不同的事件来源，并根据框架和用户的监听行为有序进行事件的分发。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2024-11-01-Taro-harmony-event/5_events.png" alt="events" class="img_ZOZK"></p><p>在这些事件类型中，大致可以分为普通事件和节点事件两类。前者涵盖系统层面和应用、组件等生命周期的变化，通常由系统或应用状态的改变触发，主要由事件中心（eventCenter）来处理；节点事件则与 <code>DOM Tree</code> 紧密相关，这些事件通常需要快速响应，以确保用户界面的流畅性和交互的即时性。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="事件中心eventcenter的实现">事件中心（eventCenter）的实现<a href="#事件中心eventcenter的实现" class="hash-link" aria-label="事件中心（eventCenter）的实现的直接链接" title="事件中心（eventCenter）的实现的直接链接">​</a></h3><p>作为 Taro 运行时中的基础模块，事件中心专注于处理系统事件和生命周期。它允许框架和应用开发者在后台线程注册事件队列，并异步分发事件，从而有效减轻主线程的负担。事件中心能够快速响应各种事件，同时具备健壮的错误处理机制，帮助开发者快速定位和解决事件回调中的问题，从而提升开发效率和系统稳定性。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="事件监听与分发">事件监听与分发<a href="#事件监听与分发" class="hash-link" aria-label="事件监听与分发的直接链接" title="事件监听与分发的直接链接">​</a></h4><p>开发者可以在 C++ 和 ArkTS 等多种语言环境中创建事件监听器，并将相应的回调函数添加到事件队列中。这一机制允许开发者在不同的编程语言中灵活地定义和处理事件响应逻辑。</p><p>当事件触发时，会根据不同语言环境的运行时差异，将事件参数转换为对应的格式。这种参数转换确保了各语言环境能够正确理解并处理事件及包含的数据，无论是简单的数据类型还是复杂的对象结构，都能在不同语言之间无缝传递。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2024-11-01-Taro-harmony-event/6_event_center.png" alt="event_center" class="img_ZOZK"></p><p>事件队列会根据监听器的类型，按照预定义的顺序，将事件分发到相应的语言环境中。这样一来，每个监听器都能在其所属的环境中高效地执行对应的回调函数。通过这种方式，不仅可以实现了跨语言的事件处理，优化事件的分发效率，并确保应用在响应用户交互时保持高性能和高稳定性。</p><blockquote><p>需要注意的是，受限于底层限制，在 ArkTS 环境中注册的事件需要回到主线程执行，同时在鸿蒙端不支持 Symbol 类型的事件。</p></blockquote><h3 class="anchor anchorWithStickyNavbar_mCi1" id="节点事件处理domevent">节点事件处理（domEvent）<a href="#节点事件处理domevent" class="hash-link" aria-label="节点事件处理（domEvent）的直接链接" title="节点事件处理（domEvent）的直接链接">​</a></h3><p>在 HTML 中，节点事件处理流程会如下图所示，事件从根节点开始向下传播至目标节点，触发后再从目标节点顺着节点树向上冒泡。在鸿蒙端实现中，Taro 基于这一事件传播流程，为开发者提供一致的事件处理机制。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="事件类型">事件类型<a href="#事件类型" class="hash-link" aria-label="事件类型的直接链接" title="事件类型的直接链接">​</a></h4><p>在 Taro 框架中，节点主要处理三种类型的事件：鸿蒙事件、鸿蒙手势事件和自定义事件。这些事件都是从 &nbsp;<code>TaroElement</code>&nbsp; 上进行监听和触发的。根据事件的类型不同，节点会从相应的事件源设置 &nbsp;<code>Receiver</code>&nbsp;（事件接收器）来进行监听并处理回调逻辑。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2024-11-01-Taro-harmony-event/7_event_dom.png" alt="event_dom" class="img_ZOZK"></p><p>鸿蒙事件和鸿蒙手势事件分别通过 &nbsp;<code>RenderNode</code>&nbsp; 注册到 &nbsp;<code>Receiver</code>，确保事件能够正确地传递和触发。而自定义事件则根据节点实现或用户自行触发，以满足各种不同类型的交互响应。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="事件传播">事件传播<a href="#事件传播" class="hash-link" aria-label="事件传播的直接链接" title="事件传播的直接链接">​</a></h4><p>当 &nbsp;<code>TaroElement</code>&nbsp; 上的事件被触发后，事件会沿着节点树向上传播。每个节点依次接收到事件，并执行相应的回调。执行完回调后，会检查开发者是否阻止冒泡，以决定是否继续向上传播。事件从目标节点开始，逐级往上直到根节点或者冒泡被阻止。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2024-11-01-Taro-harmony-event/8_html_event.png" alt="html_event" class="img_ZOZK">
这允许开发者在事件传播过程中，通过任意节点处理或拦截事件来调整业务逻辑实现，以更灵活的方式在特定节点上执行逻辑，或通过阻止冒泡避免对上层节点的影响。这样的设计对于前端开发者来说，更加熟悉、直观。</p><blockquote><p>鸿蒙系统的底层节点事件也有自己的传播逻辑，但由于其机制与 <code>ArkNode</code> 节点树差异，为避免其事件干扰，需要阻止其冒泡行为并接管其传播流程，以确保事件传播与节点树正确关联。</p></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="事件回调">事件回调<a href="#事件回调" class="hash-link" aria-label="事件回调的直接链接" title="事件回调的直接链接">​</a></h4><p>由于节点事件也需要回调 JS 环境中执行，根据事件类型的不同，按照 Web 标准将相应的节点、值和方法如 <code>target</code>、<code>stopPropagation</code>、<code>value</code> 等等挂载到事件对象上。通过执行当前回调的序列化方法，确保事件在不同语言环境传递时，可以保证其回调对象能力一致、参数完整。</p><p>在 C++ 中，许多组件依赖于事件机制来实现功能。例如，通过鸿蒙事件更新组件属性，还有各个组件节点间的事件传递等。这些组件利用事件机制来确保数据变化能够及时反映，并且用户交互能够顺利传递到系统的各个部分。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="总结与展望">总结与展望<a href="#总结与展望" class="hash-link" aria-label="总结与展望的直接链接" title="总结与展望的直接链接">​</a></h2><p>在多语言环境中，确保事件在不同语言环境传递时的一致性尤为重要，各个模块以及应用内不同页面或组件通过事件解耦驱动来提升可维护性。当前的解决方案有效提升了系统的响应速度和模块间的协作能力。</p><p>当下方案实现中仍然存在一些问题，比如早期通过事件绕过 ArkTS 与 JS 之间相互调用限制等场景，可以通过 TurboModule 来提供更加直接的调用方案。</p><p>未来，在 Taro for Harmony 场景下，各语言模块的协同将进一步增强。基于事件系统的设计，可以有效地解耦模块间逻辑，实现更灵活的组合。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="拓展阅读"><strong>拓展阅读</strong><a href="#拓展阅读" class="hash-link" aria-label="拓展阅读的直接链接" title="拓展阅读的直接链接">​</a></h3><ul><li><a href="/blog/2024/09/11/harmony-react-on-arkts"><strong>京东鸿蒙上线前瞻——使用 Taro 打造高性能原生应用</strong></a></li><li><a href="/blog/2024/09/29/harmony-react-on-arkts"><strong>Taro 鸿蒙技术内幕系列（一）：如何将 React 代码跑在 ArkUI 上</strong></a></li><li><a href="/blog/2024/10/16/harmony-w3c-css"><strong>Taro 鸿蒙技术内幕系列（二）：如何让 W3C 标准的 CSS 跑在鸿蒙上</strong></a></li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="下期预告"><strong>下期预告</strong><a href="#下期预告" class="hash-link" aria-label="下期预告的直接链接" title="下期预告的直接链接">​</a></h3><p><a href="http://xingyun.jd.com/shendeng/article/detail/38690" target="_blank" rel="noopener noreferrer"><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2024-11-01-Taro-harmony-event/next.png" alt="next" class="img_ZOZK"></a></p>]]></content:encoded>
            <category>v4</category>
            <category>harmony</category>
        </item>
        <item>
            <title><![CDATA[Taro 鸿蒙技术内幕系列（二）：如何让 W3C 标准的 CSS跑在鸿蒙上]]></title>
            <link>https://docs.taro.zone/blog/2024/10/16/harmony-w3c-css</link>
            <guid>https://docs.taro.zone/blog/2024/10/16/harmony-w3c-css</guid>
            <pubDate>Wed, 16 Oct 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[基于 Taro 打造的京东鸿蒙 APP 已跟随鸿蒙 Next 系统公测，本系列文章将深入解析 Taro 如何实现使用 React 开发高性能鸿蒙应用的技术内幕]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-09-26-21-35NaN6T9eOyiqx9Z6.png" class="img_ZOZK"></p><blockquote><p>基于 Taro 打造的京东鸿蒙 APP 已跟随鸿蒙 Next 系统公测，本系列文章将深入解析 Taro 如何实现使用 React 开发高性能鸿蒙应用的技术内幕</p></blockquote><h2 class="anchor anchorWithStickyNavbar_mCi1" id="背景">背景<a href="#背景" class="hash-link" aria-label="背景的直接链接" title="背景的直接链接">​</a></h2><p>HarmonyOS 采用自研的 ArkUI 框架作为原生 UI 开发方案，这套方案有完善的布局系统和样式控制，但是他的标准与 W3C 的 CSS 标准存在不一致性。这意味着，如果 Taro 直接使用 HarmonyOS 提供的样式系统，开发者在使用 Taro 开发时会遇到非常多的样式兼容性问题，写出来的代码也会失去跨平台兼容的能力，与 Taro 多端统一开发的定位不符。如何抹平 ArkUI 标准和 W3C 的 CSS 标准之间的差异成了一个重中之重的任务。</p><p>本文将介绍 Taro 处理 CSS 的全流程，剖析将不同的 CSS 样式转换为 ArkUI 样式遇到的问题和对应的解决方案。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="css-样式和-arkui-capi-样式的差异和抹平">CSS 样式和 ArkUI CAPI 样式的差异和抹平<a href="#css-样式和-arkui-capi-样式的差异和抹平" class="hash-link" aria-label="CSS 样式和 ArkUI CAPI 样式的差异和抹平的直接链接" title="CSS 样式和 ArkUI CAPI 样式的差异和抹平的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1样式书写方式不一致">1、样式书写方式不一致<a href="#1样式书写方式不一致" class="hash-link" aria-label="1、样式书写方式不一致的直接链接" title="1、样式书写方式不一致的直接链接">​</a></h3><p>以几个我们日常会使用到的属性为例，下面的分别是 CSS 的写法和 ArkUI CAPI 的写法。</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-10-16-15-03qlrpcJHjnf8UwTx.png" class="img_ZOZK"></p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-10-16-15-036HAOAmekXNOAY16R.png" class="img_ZOZK"></p><p>对比可以看出，CSS 样式和鸿蒙样式在单位系统和数据表示方式上存在显著差异。CSS 提供多样化的尺寸和颜色单位，而 ArkUI 的 CAPI 接口采用更统一的表示方式。</p><p>ArkUI 的 CAPI 接口将所有尺寸统一为 vp 单位，颜色采用 0xAARRGGBB 格式的 uint32 类型，对于渐变和 transform 等复杂样式属性，更是需要转换为颜色停止点和角度值列表和矩阵运算，这样的接口简洁但需要调用者根据具体场景完成必要的单位转换。</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-10-16-15-03m3DrlNt9ZWM9r33.png" class="img_ZOZK"></p><p>一个页面通常会包含非常多的样式规则，如果所有的单位转换都放在运行时候完成，必定会造成明显的性能问题。因此，我们选择提前完成部分转换。HTML 节点的样式主要来源于 CSS 和 Style 属性。CSS 样式通常是静态的，可以在编译阶段进行转换。为此，我们基于 lightningCSS 开发了一个 Rust 插件。该插件通过遍历项目 CSS 的抽象语法树（AST），将其转换为 ArkUI 的 CAPI 接口可直接使用的数据结构。</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-10-16-15-03Kx96fONRVSUre9b.png" class="img_ZOZK"></p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-10-16-17-49CfGeE7ci5xmvdWB.png" class="img_ZOZK"></p><p>而对于 Style 属性，其内容在运行时才能确定，因此必须在运行时进行转换。在 React 的语法中，Style 可能以字符串形式呈现，也可能是 CSS 属性名和属性值的键值对。为了有效解析 Style，我们针对各种类型的 CSS 语法写了一系列小型的 CSS 语法解析逻辑。这些逻辑能够从各种不同格式的字符串中准确匹配出属性值并进行转换。</p><p>虽然这种方法需要在运行时进行语法分析，但考虑到 Style 属性通常只包含有限的样式，加上 C++语言的高效执行特性，这种实时转换对性能的影响可以忽略不计。更为重要的是，这些运行时的 CSS 语法解析逻辑可以为后面 Taro 支持 CSS 变量提供能力支持。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2布局存在差异">2、布局存在差异<a href="#2布局存在差异" class="hash-link" aria-label="2、布局存在差异的直接链接" title="2、布局存在差异的直接链接">​</a></h3><p>除了书写方式的差异，ArkUI 有很多布局属性的行为在细节上也和 W3C 的布局属性存在着不小的差异，比如鸿蒙的绝对定位相对父级定位，Web 的绝对定位相对最近的已定位祖先元素定位，并且鸿蒙的定位不支持 right 和 bottom（早期）。</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-10-16-15-03NUl0N103qEzwjsb.png" class="img_ZOZK"></p><p>web 可以通过 margin 的 auto 实现居中，鸿蒙能通过 flex 实现居中。</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-10-16-15-03NYxqcvPBzFwq9or.png" class="img_ZOZK"></p><p>同时， ArkUI 的 CAPI 接口缺少一些 Web 常用功能，如 calc() 计算和百分比设置支持。为消除这些差异，我们选择采用 Yoga 作为布局引擎，而非使用鸿蒙原生提供的布局。Yoga 是 Facebook 开发的开源跨平台布局引擎，实现了基于 Web 标准的 FlexBox 布局算法。使用 Yoga 可以很容易地实现对大部分 CSS 布局属性的支持，让两端的差异缩小 。</p><p>在具体实现中，我们需要在构建 Taro 节点树的同时，构建结构一致的 Yoga 节点树。</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-10-16-15-03qwUThCxxrnAvGeD.png" class="img_ZOZK"></p><p>然后把原本直接设置到鸿蒙节点上的样式属性（如宽高、margin、padding、display 和 position）设置到 Yoga 节点上。经 Yoga 计算后，我们再从 Yoga 节点上读取计算后的 width、height、x 和 y 值设置到鸿蒙节点上，从而实现鸿蒙端和 web 端的布局一致性。</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-10-16-15-0399999U0JRKVdRoY.png" class="img_ZOZK"></p><p>通过使用 Yoga 作为布局引擎，我们不仅解决了鸿蒙系统与 Web 布局之间的差异，还提高了跨平台一致性。这种方法使开发者可以使用熟悉的 Web 布局概念，同时确保在鸿蒙平台上获得预期的布局效果。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="样式的工作流程">样式的工作流程<a href="#样式的工作流程" class="hash-link" aria-label="样式的工作流程的直接链接" title="样式的工作流程的直接链接">​</a></h2><p>介绍完 Taro 适配 ArkUI 的 CAPI 样式过程中遇到的问题和对应的解决策略之后，我们就可以来看看基于这些策略，鸿蒙样式的整个工作流程是怎么样的。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="样式初始化">样式初始化<a href="#样式初始化" class="hash-link" aria-label="样式初始化的直接链接" title="样式初始化的直接链接">​</a></h3><p>首先，项目启动后，编译器处理后的样式文件将第一个被加载到运行时环境。样式处理逻辑会根据各个选择器（selector）生成相应的样式规则（StyleRule），即 CSS 属性的键值对集合。</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-10-16-15-03KY8OrbieO3znaFI.png" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="根据-classname-匹配-stylerule">根据 ClassName 匹配 StyleRule<a href="#根据-classname-匹配-stylerule" class="hash-link" aria-label="根据 ClassName 匹配 StyleRule的直接链接" title="根据 ClassName 匹配 StyleRule的直接链接">​</a></h3><p>React 在构建每个节点的同时，会通过 Reconciler 把 React 节点的 ClassName 和 Style 设置到相应的 Taro 节点上，这个时候我们就开始进入节点的样式匹配环节。样式处理会执行以下步骤：首先，从 CSSStylesheet 这个样式集合里识别出与 className 相关的所有 StyleRule；然后，根据选择器的优先级合并这些 StyleRule；最后，将合并结果与由 Style 生成的 StyleRule 合并，从而得出最终的样式配置。</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-10-16-15-03ffn9Fza6W9mNg7m.png" class="img_ZOZK"></p><p>这里顺带提一下，CSS 除了可以书写样式之外，还可以书写伪元素和关键帧动画，这两者在都没办法直接设置到鸿蒙的样式里，在处理某个节点时，如果匹配到这个节点的样式里包含伪元素，就会把这个伪元素转换成 一次 insertBefore api 的调用，用这个新 insert 进去的子元素来承载伪元素的 StyleRule，举一个例子，下面的 F 节点的 CSS 样式里带有一个 ::after 的伪类，那么当 F 节点匹配到这个样式的时候，就会被插入一个子节点用来承载 ::after 对应的样式。</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-10-16-15-033Djdj7rBjEv16jlI.png" class="img_ZOZK"></p><p>而对于匹配到关键帧的动画，会把动画对应的元素，动画播放的次数、播放的方向、播放的缓动函数收集起来放到另外的线程，由这个线程算出元素每一帧对应的属性值，并在元素当前帧的 StyleRule 设置完之后，设置到节点上，保证动画的优先级一定是最高的。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="样式的应用">样式的应用<a href="#样式的应用" class="hash-link" aria-label="样式的应用的直接链接" title="样式的应用的直接链接">​</a></h3><p>确定了节点对应的样式表后，我们就到了把样式应用到节点上这个环节了。这个环节我们会调用节点的 SetStyle 方法，遍历 StyleRule 中的所有样式。对于布局相关的属性（如 display、position、float、flex、width、height、margin、padding），如需更新，会被设置到节点对应的 Yoga 节点上，同时为节点本身添加 layout_dirty 标记。接着，判断是否有绘制相关的属性需要更新，如果有，则设置到节点对象的临时属性上，并为节点添加 draw_dirty 标记。这些被标记的节点并不会立刻被处理，而是会被纳入下一帧的样式处理队列中，这样能避免同一帧多次设置同一个结点的相同属性，确保样式更新的高效性，同时也能保证布局属性和绘制属性设置到鸿蒙节点时的前后时序。</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-10-16-15-03yhNRtVtNdTewURo.png" class="img_ZOZK"></p><p>在标记完所有需要更新的节点后，下一帧的样式处理流程就会对这些节点进行处理。首先，系统会调用 Yoga 的 calcYGLayout 函数，让 Yoga 从根节点开始对所有的 Yoga 节点进行测算。在此过程中，布局信息发生改变的 Yoga 节点会被打上 has_new_layout 的标记，节点上的信息也会被更新。</p><p>我们用一个例子来说明 Yoga 如何判断布局变化的影响范围：假设节点 E 的宽度改变，这可能影响到依赖父元素宽度的子元素以及由子元素撑开宽度的父元素。计算后，系统可能会更新 A、B、C、E、F、H 等节点。</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-10-16-15-0310QvORMoGqrFB7pD.png" class="img_ZOZK"></p><p>测算完成后，我们遍历 Yoga 节点树，找出标记为 has_new_layout 的节点，并将其 width、height、x、y 值更新到对应的鸿蒙节点上。这样，所有节点的布局信息就更新完毕了。</p><p>布局更新完成后，我们再把前一帧中添加到样式处理队列的节点拿出来。将存储在节点临时对象中的绘制属性转移到鸿蒙节点上。在这个环节里大多数绘制属性可以直接设置，少量依赖节点布局信息的属性（如百分比形式的 background-size）也可以利用新计算出的布局信息来准确确定这些属性的值。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="样式的更新">样式的更新<a href="#样式的更新" class="hash-link" aria-label="样式的更新的直接链接" title="样式的更新的直接链接">​</a></h3><p>了解了初始化状态的样式工作流程后，我们再回过头来看一下样式更新部分的逻辑，在这一块逻辑里，样式的匹配和应用与前面的流程没有任何区别，所以只是简单介绍一下一个节点的样式是怎么被更新的。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="style-更新">Style 更新<a href="#style-更新" class="hash-link" aria-label="Style 更新的直接链接" title="Style 更新的直接链接">​</a></h4><p>Style 的更新是相对比较好处理的一部分，因为 Style 的影响范围只在节点的本身。当元素的 Style 更新时，我们只需要重新生成对应的 inline_style<!-- -->_<!-- -->，然后将其与通过 className 生成的样式进行合并应用即可。这个过程相对简单直接，因为不需要考虑对其他元素的影响。通过这种方式，我们可以确保元素的样式得到准确更新，同时保持整体样式系统的一致性和效率。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="classname-更新">ClassName 更新<a href="#classname-更新" class="hash-link" aria-label="ClassName 更新的直接链接" title="ClassName 更新的直接链接">​</a></h4><p>当元素的 ClassName 更新时，我们需要执行以下步骤来确保样式正确应用：</p><p>1.识别包含新 ClassName 的所有选择器规则。</p><p>2.根据 ClassName 在规则中的位置，确定需要重新进行样式匹配的元素：</p><ul><li>目标元素选择器：更新当前节点</li><li>直接后代选择器：更新直接子节点</li><li>后代选择器：更新所有子孙节点</li></ul><p>这些规则适用于 className 的增加、删除、修改和查询操作。对于 className 的修改，我们将其视为先删除旧 className 再添加新 className，并执行两次规则匹配。</p><p>举一个例子，当样式规则和元素结构如下时：</p><div class="language-test codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-test codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">.E .G {}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">.E .H {}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">.I &gt; .J {}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">.I {}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-10-16-15-03MLVnKT109Lq9YZEQ.png" class="img_ZOZK"></p><p>为蓝色的节点添加 className I，为红色的节点添加 className E，那么需要要被更新的节点就有 F G H I J</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-10-16-15-03oO6tk8XfFlPmRrb.png" class="img_ZOZK"></p><p>在实际应用中，我们还需考虑性能问题。对于大型应用或复杂的元素结构，频繁的样式重计算可能会影响性能。因此，我们采取了一种优化策略：找出需要更新的节点后，不会立即进行样式重匹配，而是将这些节点标记为"脏"并放入更新队列中。然后，我们在下一帧统一完成所有样式重匹配的工作。这种方法可以有效减少重复计算，提高整体性能。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="总结">总结<a href="#总结" class="hash-link" aria-label="总结的直接链接" title="总结的直接链接">​</a></h2><p>通过本文，我们详细阐述了 Taro 在处理 CSS 样式与鸿蒙系统 ArkUI 框架之间差异的全流程。我们探讨了样式书写方式的不一致性、样式匹配和应用的复杂过程，以及样式更新时的处理策略。这些功能和特性使得 Taro 能够在保持跨平台兼容性的同时，实现 CSS 样式到鸿蒙系统的有效转换。</p><p>作为开发者，我们深知这个过程中面临的挑战，但也为最终取得的成果感到自豪。通过这种方法，我们为开发者提供了一个统一且强大的多端开发解决方案，使他们能够更加高效地开发跨平台应用。</p><p>我们相信，随着技术的不断进步，未来还会出现更多的优化空间。我们将继续致力于改进 Taro 的性能和兼容性，为开发者提供更好的开发体验。同时，我们也欢迎社区的反馈和贡献，共同推动 Taro 在多端开发领域的发展。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="系列往期精选">系列往期精选<a href="#系列往期精选" class="hash-link" aria-label="系列往期精选的直接链接" title="系列往期精选的直接链接">​</a></h3><p><a href="/blog/2024/09/11/harmony-react-on-arkts">《京东鸿蒙上线前瞻——使用 Taro 打造高性能原生应用》</a></p><p><a href="/blog/2024/09/29/harmony-react-on-arkts">Taro 鸿蒙技术内幕系列（一）：如何将 React 代码跑在 ArkUI 上文章</a></p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-10-16-15-030TfLhjzRf916dOEY.png" class="img_ZOZK"></p><p><a href="/blog/2024/11/01/harmony-native-events">《Taro 鸿蒙技术内幕系列（三） - 多语言场景下的通用事件系统设计》</a></p>]]></content:encoded>
            <category>v4</category>
            <category>harmony</category>
        </item>
        <item>
            <title><![CDATA[Taro 鸿蒙技术内幕系列（一）：如何将 React 代码跑在 ArkUI 上文章]]></title>
            <link>https://docs.taro.zone/blog/2024/09/29/harmony-react-on-arkts</link>
            <guid>https://docs.taro.zone/blog/2024/09/29/harmony-react-on-arkts</guid>
            <pubDate>Sun, 29 Sep 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[基于 Taro 打造的京东鸿蒙 APP 已跟随鸿蒙 Next 系统公测，本系列文章将深入解析 Taro 如何实现使用 React 开发高性能鸿蒙应用的技术内幕]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-09-26-21-35NaN6T9eOyiqx9Z6.png" class="img_ZOZK"></p><blockquote><p>基于 Taro 打造的京东鸿蒙 APP 已跟随鸿蒙 Next 系统公测，本系列文章将深入解析 Taro 如何实现使用 React 开发高性能鸿蒙应用的技术内幕</p></blockquote><h2 class="anchor anchorWithStickyNavbar_mCi1" id="背景">背景<a href="#背景" class="hash-link" aria-label="背景的直接链接" title="背景的直接链接">​</a></h2><p>随着鸿蒙操作系统的快速发展，开发者们期待将现有跨平台应用迁移到鸿蒙平台。Taro 作为一个流行的跨平台开发框架，其支持鸿蒙系统的可能性引起了广泛关注。</p><p>然而，鸿蒙系统采用全新的 ArkUI 框架作为原生 UI 开发方案，与 Taro 原本支持的平台存在显著差异。将 Taro 的 React 开发模式与 ArkUI 的声明式 UI 开发范式进行有效对接成为了一个技术难题。</p><p>本文将探讨 Taro 框架如何通过创新方案实现 React 代码在 ArkUI 上的运行。我们将解析 Taro 的运行时原理，剖析其如何将 React 组件转换为 ArkUI 可识别的结构，以及相关技术挑战和解决方案。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro-运行时原理介绍">Taro 运行时原理介绍<a href="#taro-运行时原理介绍" class="hash-link" aria-label="Taro 运行时原理介绍的直接链接" title="Taro 运行时原理介绍的直接链接">​</a></h3><p>为了理解 Taro 适配 ArkUI 的核心机制，我们首先需要深入了解 Taro 的运行时原理。Taro 通过巧妙的设计，将 React 代码转换为各平台可执行的形式，其中包括对鸿蒙平台的适配。下面将详细介绍 Taro 是如何将 React 代码转换为 ArkUI 可执行的形式，以及节点转换的流程细节。</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-09-26-21-35o26cuJ48IrXps9Pxy.png" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-从-react-到-taro">1. 从 React 到 Taro<a href="#1-从-react-到-taro" class="hash-link" aria-label="1. 从 React 到 Taro的直接链接" title="1. 从 React 到 Taro的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="react-跨平台的秘诀">React 跨平台的秘诀<a href="#react-跨平台的秘诀" class="hash-link" aria-label="React 跨平台的秘诀的直接链接" title="React 跨平台的秘诀的直接链接">​</a></h4><p>在 Taro 的运行时中，首先执行的是开发者编写的 React 业务代码。这些代码定义了业务应用的结构、逻辑和状态管理。那么既然要对接 React，那肯定先得了解它的核心架构，React 是怎么运作的：</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-09-26-21-35zjLOZWNpLyg9WIv.png" class="img_ZOZK"></p><p>了解了 React 的基本架构后，我们可以清晰地看到，<code>**Renderer**</code> 作为渲染器，负责将 React 的虚拟节点操作最终映射到相应的平台上。例如，<code>react-dom</code>将这些操作对接到浏览器上，而<code>react-native</code>则将其对接到 iOS 或 Android 平台。这种设计使得 React 能够适配不同的运行环境。</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-09-26-21-35vNxJHNqGiGD7EiB.png" class="img_ZOZK"></p><p>正是基于这种思路，Taro 团队设计了 <strong>Taro Renderer</strong>。这个渲染器充当了 React 与 Taro 虚拟节点树之间的桥梁，使得 React 的操作可以被转换为 Taro 的中间表示。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="通过实现-taro-renderer-生成-taro-虚拟节点树">通过实现 Taro Renderer 生成 Taro 虚拟节点树<a href="#通过实现-taro-renderer-生成-taro-虚拟节点树" class="hash-link" aria-label="通过实现 Taro Renderer 生成 Taro 虚拟节点树的直接链接" title="通过实现 Taro Renderer 生成 Taro 虚拟节点树的直接链接">​</a></h4><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-09-26-21-35NBq0KTsHmbCx9Cz.png" class="img_ZOZK"></p><p>HostConfig 接口实现</p><p>要实现 Taro 的 Renderer，我们需要实现 <code>React Reconciler</code> 所需的 <code>hostConfig</code> 接口。这个接口定义了一系列方法，用于创建、更新和管理渲染目标平台的元素。以下是一些关键的 hostConfig 方法：</p><ul><li><strong>createElement：</strong>创建 ArkUI 对应的元素。</li><li><strong>createTextInstance</strong>：创建文本节点。</li><li><strong>appendChild：</strong>将子元素添加到父元素。</li><li><strong>removeChild：</strong>从父元素中移除子元素。</li><li><strong>insertBefore：</strong>在指定位置插入元素。</li><li><strong>commitUpdate：</strong>更新元素属性。</li></ul><p>通过实现这些方法，<strong>Taro Rendere</strong>r 能够将 <strong>React 的操作转换为 Taro 虚拟节点树的相应操作</strong>。这个虚拟节点树是 Taro 实现跨平台的核心，它为不同平台的渲染提供了统一的中间表示。</p><div class="language-ts codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-ts codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 部分HostConfig接口实现的代码</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> hostConfig</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> HostConfig </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 创建Taro虚拟节点</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">createInstance</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">type</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> props</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Props</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _rootContainerInstance</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _hostContext</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> internalInstanceHandle</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Fiber</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> element</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> TaroElement </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> TaroNativeModule</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">createTaroNode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">type</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">precacheFiberNode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">internalInstanceHandle</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> element</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">updateFiberProps</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">element</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> props</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> element</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 更新属性</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">commitUpdate</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dom</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> updatePayload</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> oldProps</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> newProps</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">updatePropsByPayload</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dom</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> oldProps</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> updatePayload</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">updateFiberProps</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dom</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> newProps</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 插入节点</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">insertBefore</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">parent</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> TaroElement</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> child</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> TaroElement</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> refChild</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> TaroElement</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      parent</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">insertBefore</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">child</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> refChild</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 移除节点</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">removeChild</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">parent</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> TaroElement</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> child TaroElement</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      parent</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">removeChild</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">child</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-从-taro-到-arkui">2. 从 Taro 到 ArkUI<a href="#2-从-taro-到-arkui" class="hash-link" aria-label="2. 从 Taro 到 ArkUI的直接链接" title="2. 从 Taro 到 ArkUI的直接链接">​</a></h3><p>在将 Taro 虚拟节点树转换为 ArkUI 的过程中，我们需要进行几个关键步骤：</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-09-26-21-35NrS7935hv26Lf9xJ35.png" class="img_ZOZK"></p><p>Taro Element 转换 ArkUI 过程</p><p>首先，我们需要在 ArkUI 层面<strong>实现一套与 Taro 组件对应的组件库</strong>。这个步骤至关重要，因为它建立了 Taro 组件和 ArkUI 组件之间的映射关系。例如，我们需要为 Taro 的 <code>View</code>、<code>Text</code>、<code>Image</code> 等基础组件创建对应的 ArkUI 组件。这样，当我们遍历 Taro 虚拟节点树时，就能找到每个节点在 ArkUI 中的对应实现。</p><p>在节点映射的过程中，我们注意到 Taro 虚拟节点树与实际 ArkUI 视图结构存在差异。这些差异主要体现在以下几个方面：</p><ul><li><strong>复合组件结构：</strong>某些 Taro 组件在 ArkUI 中可能需要多个组件配合实现。例如，ScrollView 组件在 ArkUI 中可能需要一个 Scroll 节点搭配一个 Stack 来实现完整功能。</li><li><strong>层级位置调整：</strong>一些特殊定位的节点（如 Fixed 定位的元素）在最终渲染时的位置可能与其在虚拟节点树中的层级不一致。这需要在生成渲染树时进行特殊处理。</li><li><strong>平台特定组件：</strong>某些 Taro 组件可能需要使用 ArkUI 特有的组件或布局方式来实现，这要求我们在转换过程中进行适当的调整和映射。</li></ul><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-09-26-21-35PhZHoHOEsU9f6Zg.png" class="img_ZOZK"></p><p>因此，在生成渲染树时，我们需要一个更复杂的转换过程，不仅要考虑简单的一对一映射，还要处理这些结构性的差异，确保最终生成的 ArkUI 组件树能够正确反映预期的视图结构和布局。<strong>因此，在 Taro &gt; ArkUI 的节点对接中，我们需要维护一棵 Render Tree，用于做中间的桥梁。</strong></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="1-根据组件类型-创建-taro-element">1. 根据组件类型 创建 Taro Element<a href="#1-根据组件类型-创建-taro-element" class="hash-link" aria-label="1. 根据组件类型 创建 Taro Element的直接链接" title="1. 根据组件类型 创建 Taro Element的直接链接">​</a></h4><p>在创建 Taro Element 的过程中，我们根据组件的类型来实例化相应的 Taro 元素。这一步骤是将 React 组件转换为 Taro 内部表示的关键。</p><div class="language-ts codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-ts codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 根据组件类型创建对应的Taro节点</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">std</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token plain">shared_ptr</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">TaroElement</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> TaroDocument</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token function" style="color:#d73a49">CreateElement</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">napi_value </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">node</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// 获取组件类型</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token constant" style="color:#36acaa">TAG_NAME</span><span class="token plain"> tag_name_ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> TaroDOM</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token plain">TaroElement</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token function" style="color:#d73a49">GetTagName</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">node</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 根据组件类型，创建对应的实例</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    std</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token plain">shared_ptr</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">TaroDOM</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token plain">TaroElement</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> item</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">switch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tag_name_</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">TAG_NAME</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token constant" style="color:#36acaa">SCROLL_VIEW</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            item </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> std</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token generic-function function" style="color:#d73a49">make_shared</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">TaroDOM</span><span class="token generic-function generic class-name operator" style="color:#393A34">:</span><span class="token generic-function generic class-name operator" style="color:#393A34">:</span><span class="token generic-function generic class-name">TaroScrollView</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">node</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">break</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">TAG_NAME</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token constant" style="color:#36acaa">IMAGE</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            item </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> std</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token generic-function function" style="color:#d73a49">make_shared</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">TaroDOM</span><span class="token generic-function generic class-name operator" style="color:#393A34">:</span><span class="token generic-function generic class-name operator" style="color:#393A34">:</span><span class="token generic-function generic class-name">TaroImage</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">node</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">break</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">TAG_NAME</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token constant" style="color:#36acaa">SPAN</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">TAG_NAME</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token constant" style="color:#36acaa">TEXT</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            item </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> std</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token generic-function function" style="color:#d73a49">make_shared</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">TaroDOM</span><span class="token generic-function generic class-name operator" style="color:#393A34">:</span><span class="token generic-function generic class-name operator" style="color:#393A34">:</span><span class="token generic-function generic class-name">TaroText</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">node</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">break</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">TAG_NAME</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token constant" style="color:#36acaa">SWIPER</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            item </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> std</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token generic-function function" style="color:#d73a49">make_shared</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">TaroDOM</span><span class="token generic-function generic class-name operator" style="color:#393A34">:</span><span class="token generic-function generic class-name operator" style="color:#393A34">:</span><span class="token generic-function generic class-name">TaroSwiper</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">node</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">break</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> item</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="2-taro-element-创建-taro-rendernode">2. Taro Element 创建 Taro RenderNode<a href="#2-taro-element-创建-taro-rendernode" class="hash-link" aria-label="2. Taro Element 创建 Taro RenderNode的直接链接" title="2. Taro Element 创建 Taro RenderNode的直接链接">​</a></h4><p>在创建完 Taro Element 之后，下一步是将其转换为 Taro RenderNode。这个过程是将 Taro 的内部表示进一步转化为更接近 ArkUI 结构的渲染节点。</p><div class="language-ts codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-ts codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 创建 Taro RenderNode</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> TaroSwiper</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token function" style="color:#d73a49">Build</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">is_init_</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// create render node</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        TaroElementRef element </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> std</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token generic-function function" style="color:#d73a49">static_pointer_cast</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">TaroElement</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">shared_from_this</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        auto render_swiper </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> std</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token generic-function function" style="color:#d73a49">make_shared</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">TaroSwiperNode</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">element</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        render_swiper</span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token function" style="color:#d73a49">Build</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="3-taro-rendernode-创建-arkui-node">3. Taro RenderNode 创建 ArkUI Node<a href="#3-taro-rendernode-创建-arkui-node" class="hash-link" aria-label="3. Taro RenderNode 创建 ArkUI Node的直接链接" title="3. Taro RenderNode 创建 ArkUI Node的直接链接">​</a></h4><p>最后一步是将 Taro RenderNode 转换为实际的 ArkUI 节点。这个过程涉及到直接与 ArkUI 的底层 API 交互，创建和配置 ArkUI 的原生节点。实现了从 Taro 的渲染节点到 ArkUI 实际可渲染节点的最终转换。</p><div class="language-ts codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-ts codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 创建 ArkUI Node</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> TaroSwiperNode</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token function" style="color:#d73a49">Build</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    NativeNodeApi </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">nativeNodeApi </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> NativeNodeApi</span><span class="token operator" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">:</span><span class="token function" style="color:#d73a49">getInstance</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 创建一个Swiper的ArkUI节点</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">SetArkUINodeHandle</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">nativeNodeApi</span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token function" style="color:#d73a49">createNode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token constant" style="color:#36acaa">ARKUI_NODE_SWIPER</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>通过这三个步骤，我们在 C++ 层面成功实现了 React 组件结构到 ArkUI 原生组件结构的映射。这一过程使 Taro 应用能够在鸿蒙系统上准确地渲染和运行，为跨平台开发提供了有力支持。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="总结">总结<a href="#总结" class="hash-link" aria-label="总结的直接链接" title="总结的直接链接">​</a></h2><p>最后总结下，本文探讨了 Taro 框架如何将 React 代码成功运行在鸿蒙系统的 ArkUI 上。这个过程主要分为两个关键部分：</p><p><img loading="lazy" src="https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-09-26-21-35jfyTMtdtlxvYQ69.png" class="img_ZOZK"></p><p>React &gt; ArkUI 架构图</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-taro-对接-react">1. Taro 对接 React<a href="#1-taro-对接-react" class="hash-link" aria-label="1. Taro 对接 React的直接链接" title="1. Taro 对接 React的直接链接">​</a></h3><p>Taro 通过实现自定义的 Renderer 来对接 React。这个 Renderer 包含了一系列方法，如 createInstance、commitUpdate 等，用于将 React 的操作转换为 Taro 虚拟节点树的操作。这个虚拟节点树是 Taro 实现跨平台的核心，为不同平台的渲染提供了统一的中间表示。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-taro-对接-arkui">2. Taro 对接 ArkUI<a href="#2-taro-对接-arkui" class="hash-link" aria-label="2. Taro 对接 ArkUI的直接链接" title="2. Taro 对接 ArkUI的直接链接">​</a></h3><p>Taro 通过自定义 Renderer 将 React 操作转换为虚拟节点树，然后通过三步转换过程将其映射到 ArkUI 结构。这个过程涉及 Taro Element、Taro RenderNode 和 ArkUI Node 这三棵树的维护，主要通过这三个流程步骤实现：</p><p>1.<strong>创建 Taro Element：</strong>这一步将 React 组件转换为 Taro 内部表示。</p><p>2.<strong>创建 Taro RenderNode：</strong>将 Taro 的内部表示进一步转化为更接近 ArkUI 层级结构的渲染节点。</p><p>3.<strong>创建 ArkUI Node：</strong>最后一步是将 Taro RenderNode 转换为实际的 ArkUI 节点，直接与 ArkUI 的底层 API 交互。</p><p>通过这种方式，Taro 成功地将 React 组件结构映射到 ArkUI 原生组件结构，使得 Taro 应用能够在鸿蒙系统上准确地渲染和运行，同时也为跨平台开发提供了有力支持。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="系列往期精选">系列往期精选<a href="#系列往期精选" class="hash-link" aria-label="系列往期精选的直接链接" title="系列往期精选的直接链接">​</a></h2><p><a href="/blog/2024/09/11/harmony-react-on-arkts">《京东鸿蒙上线前瞻——使用 Taro 打造高性能原生应用》</a></p>]]></content:encoded>
            <category>v4</category>
            <category>harmony</category>
        </item>
        <item>
            <title><![CDATA[京东鸿蒙上线前瞻——使用 Taro 打造高性能原生应用]]></title>
            <link>https://docs.taro.zone/blog/2024/09/11/harmony-high-performance</link>
            <guid>https://docs.taro.zone/blog/2024/09/11/harmony-high-performance</guid>
            <pubDate>Wed, 11 Sep 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[2024 年 1 月，京东正式启动鸿蒙原生应用开发，基于 HarmonyOS NEXT 的全场景、原生智能、原生安全等优势特性，为消费者打造更流畅、更智能、更安全的购物体验。]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorWithStickyNavbar_mCi1" id="背景">背景<a href="#背景" class="hash-link" aria-label="背景的直接链接" title="背景的直接链接">​</a></h2><p>2024 年 1 月，京东正式启动鸿蒙原生应用开发，基于 HarmonyOS NEXT 的全场景、原生智能、原生安全等优势特性，为消费者打造更流畅、更智能、更安全的购物体验。同年 6 月，京东鸿蒙原生应用尝鲜版上架华为应用市场，计划 9 月完成正式版的上架。</p><p><img loading="lazy" src="https://img13.360buyimg.com/img/jfs/t1/235448/38/25755/201048/66d7d844F0baea79b/adfe087ce5348c30.png" alt="配图2.png" class="img_ZOZK"></p><p>早在 2020 年，京东与华为就签署了战略合作协议，不断加大技术投入探索 HarmonyOS 的创新特性。作为华为鸿蒙生态的首批头部合作伙伴，在适配鸿蒙操作系统的过程中，京东与华为一直保持着密切的技术沟通与共创，双方共同攻坚行业适配难点，并推动多端统一开发解决方案 Taro 在业界率先实现对鸿蒙 ArkUI 的原生开发支持。</p><p>本文将阐述京东鸿蒙原生应用在开发时所采用的技术方案、技术特点、性能表现以及未来的优化计划。<strong>通过介绍选择 Taro 作为京东鸿蒙原生应用的开发框架的原因，分析 Taro 在支持 Web 范式开发、快速迁移存量项目、渲染性能优化、高阶功能支持以及混合开发模式等方面的优势。</strong></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="技术方案">技术方案<a href="#技术方案" class="hash-link" aria-label="技术方案的直接链接" title="技术方案的直接链接">​</a></h2><p>京东在开发鸿蒙原生应用的过程中，需要考虑如何在有限的时间内高效完成项目，同时兼顾应用的性能与用户体验。为了达成这一目标，选择合适的技术方案至关重要。</p><p>在技术选型方面，开发一个鸿蒙原生应用，一般会有两种选择：</p><ul><li>使用原生 ArkTS 进行鸿蒙开发</li><li>使用跨端框架进行鸿蒙开发</li></ul><p>使用原生 ArkTS 进行鸿蒙开发，面临着<strong>开发周期冗长、维护多端多套应用代码成本高昂的挑战</strong>。在交付时间紧、任务重的情况下，京东果断选择跨端框架来开发鸿蒙原生应用，以期在有限的时间内高效完成项目。</p><p>作为在业界具备代表性的开源跨端框架之一，Taro 是由京东凹凸实验室团队开发的一款开放式跨端跨框架解决方案，它支持开发者使用一套代码，实现在 H5、小程序以及鸿蒙等多个平台上的运行。</p><p>通过 Taro 提供的编译能力，开发者可以将整个 Taro 项目轻松地转换为一个独立的鸿蒙应用，无需额外的开发工作。</p><p><img loading="lazy" src="https://img10.360buyimg.com/img/jfs/t1/92427/11/48326/91373/66d7d84aFb8575cd7/905a1ce0931eba57.png" alt="image.png" class="img_ZOZK"></p><p>另外，Taro 也支持<strong>将项目里的部分页面以模块化的形式打包进原生的鸿蒙应用中</strong>，京东鸿蒙原生应用便是使用这种模式进行开发的。</p><p>京东鸿蒙原生应用的基础基建能力如路由、定位、权限等能力由京东零售 mpass 团队来提供，而原生页面的渲染以及与基建能力的桥接则由 Taro 来负责，业务方只需要将写好的 Taro 项目通过执行相应的命令，就可以将项目以模块的形式一键打包到鸿蒙应用中，最终在应用内渲染出对应的原生页面，整个过程简单高效。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="技术特点">技术特点<a href="#技术特点" class="hash-link" aria-label="技术特点的直接链接" title="技术特点的直接链接">​</a></h2><p>Taro 作为一款开放式跨端跨框架解决方案，在支持开发者一套代码多端运行的同时，也为开发鸿蒙原生应用提供了诸多便利。在权衡多方因素后，我们最终选择了 Taro 作为开发鸿蒙原生应用的技术方案，总的来说，使用 Taro 来开发鸿蒙原生应用会有下面几点优势：</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="支持开发者使用-web-范式来开发鸿蒙原生应用">支持开发者使用 Web 范式来开发鸿蒙原生应用<a href="#支持开发者使用-web-范式来开发鸿蒙原生应用" class="hash-link" aria-label="支持开发者使用 Web 范式来开发鸿蒙原生应用的直接链接" title="支持开发者使用 Web 范式来开发鸿蒙原生应用的直接链接">​</a></h3><p>与鸿蒙原生开发相比，使用 Taro 进行开发的最大优点在于 Taro 支持开发者使用前端 Web 范式来开发鸿蒙原生应用，基于这一特点，我们<strong>对大部分 CSS 能力进行了适配</strong>：</p><ul><li>支持常见的 CSS 样式和布局，支持 flex、伪类和伪元素</li><li>支持常见的 CSS 定位，绝对定位、fixed 定位</li><li>支持常见的 CSS 选择器和媒体查询</li><li>支持常见的 CSS 单位，比如 vh、vw 以及计算属性 calc</li><li>支持 CSS 变量以及安全区域等预定义变量</li></ul><p>在编译流程上，我们<strong>采用了 Rust 编写的 LightningCSS，极大地提升了 CSS 文件的编译和解析速度</strong>。</p><p><img loading="lazy" src="https://img14.360buyimg.com/img/jfs/t1/8076/32/27814/38590/66d7d83dF04a597fa/bcfcd608361e3006.png" alt="image.png" class="img_ZOZK"></p><p>（图片来自 LightningCSS 官网）</p><p>在运行时上，我们<strong>参考了 WebKit 浏览器内核的处理流程</strong>，对于 CSS 规则的匹配和标脏进行了架构上的升级，大幅提升了 CSS 应用和更新的性能。</p><p><img loading="lazy" src="https://img13.360buyimg.com/img/jfs/t1/171695/16/47089/37772/66d7d83dFa03594a6/3d5e2ae2082a3236.png" alt="image.png" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="支持存量-taro-项目的快速迁移">支持存量 Taro 项目的快速迁移<a href="#支持存量-taro-项目的快速迁移" class="hash-link" aria-label="支持存量 Taro 项目的快速迁移的直接链接" title="支持存量 Taro 项目的快速迁移的直接链接">​</a></h3><p>将现有业务适配到一个全新的端侧平台，无疑需要投入大量的人力物力。而 Taro 框架的主要优势，正是能够有效解决这种跨端场景下的项目迁移难题。通过 Taro，我们可以以极低的成本，在保证高度还原和高性能的前提下，快速地将现有的 Taro 项目迁移到鸿蒙系统上。</p><p><img loading="lazy" src="https://img12.360buyimg.com/img/jfs/t1/106273/17/48258/324239/66d7d847Ff0743b4f/29415a5d30112870.png" alt="image.png" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="渲染性能比肩原生开发">渲染性能比肩原生开发<a href="#渲染性能比肩原生开发" class="hash-link" aria-label="渲染性能比肩原生开发的直接链接" title="渲染性能比肩原生开发的直接链接">​</a></h3><p>在 Taro 转换鸿蒙原生页面的技术实现上，我们摒弃了之前<a href="http://sd.jd.com/article/25595?shareId=4260&amp;isHideShareButton=1" target="_blank" rel="noopener noreferrer">使用 ArkTS 原生组件递归渲染节点树的方案</a>，<strong>将更多的运行时逻辑如组件、动效、测算和布局等逻辑下沉到了 C++ 层</strong>，极大地提升了页面的渲染性能。</p><p>另外，我们对于 Taro 项目中 CSS 样式的处理架构进行了一次整体的重构和升级，并引入布局引擎 Yoga，将页面的测量和布局放在 Taro 侧进行实现，基于这些优化，实现一套高效的渲染任务管线，使得 Taro 开发的鸿蒙页面在性能上足以和鸿蒙 ArkTS 原生页面比肩。</p><p><img loading="lazy" src="https://img13.360buyimg.com/img/jfs/t1/165604/37/47102/104631/66d7d841Fef201e37/43f11bccf8955309.png" alt="image.png" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="支持虚拟列表和节点复用等高阶功能">支持虚拟列表和节点复用等高阶功能<a href="#支持虚拟列表和节点复用等高阶功能" class="hash-link" aria-label="支持虚拟列表和节点复用等高阶功能的直接链接" title="支持虚拟列表和节点复用等高阶功能的直接链接">​</a></h3><p>长列表渲染是应用开发普遍会遇到的场景，在商品列表、订单列表、消息列表等需要无限滚动的组件和页面中广泛存在，这些场景如果不进行特殊的处理，只是单纯对数据进行渲染和更新，在数据量非常大的情况下，可能会引发严重的性能问题，导致视图在一段时间内无法响应用户操作。</p><p>在这个背景下，<strong>Taro 在鸿蒙端提供了长列表类型组件（WaterFlow &amp; List）</strong>，并对长列表类型组件进行了优化，提供了懒加载、预加载和节点复用等功能，有效地解决大数据量下的性能问题，提高应用的流畅度和用户体验。</p><p><img loading="lazy" src="https://img12.360buyimg.com/img/jfs/t1/77051/16/27856/17737/66d7d83bF4dccbb30/283c52c3099c79c5.png" alt="image.png" class="img_ZOZK"></p><p>（图片来自 HarmonyOS 官网）</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="支持原生混合开发等多种开发模式">支持原生混合开发等多种开发模式<a href="#支持原生混合开发等多种开发模式" class="hash-link" aria-label="支持原生混合开发等多种开发模式的直接链接" title="支持原生混合开发等多种开发模式的直接链接">​</a></h3><p>Taro 的组件和 API 是以小程序作为基准来进行设计的，因此在实际的鸿蒙应用开发过程中，会出现所需的组件和 API 在 Taro 中不存在的情况，因为针对这种情况，Taro 提供了原生混合开发的能力，支持将原生页面或者原生组件混合编译到 Taro 鸿蒙项目中，<strong>支持 Taro 组件和鸿蒙原生组件在页面上的混合使用</strong>。</p><p><img loading="lazy" src="https://img20.360buyimg.com/img/jfs/t1/778/17/23935/61908/66d7d843F06d0a9b1/b23cef996a033f1f.png" alt="image.png" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="性能表现">性能表现<a href="#性能表现" class="hash-link" aria-label="性能表现的直接链接" title="性能表现的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="京东鸿蒙原生应用性能数据">京东鸿蒙原生应用性能数据<a href="#京东鸿蒙原生应用性能数据" class="hash-link" aria-label="京东鸿蒙原生应用性能数据的直接链接" title="京东鸿蒙原生应用性能数据的直接链接">​</a></h3><p>经过对 Taro 的屡次优化和打磨，使得京东鸿蒙原生应用取得了优秀的性能表现，最终首页的渲染耗时 <strong>1062ms</strong>，相比于之前的 ArkTS 版本，性能提升了 <strong>23.9%</strong>；商详的渲染耗时 <strong>560 ms</strong>，相比于之前的 ArkTS 版本，性能提升 <strong>74.2%</strong>。</p><p>值得注意的是商详页性能提升显著，经过分析发现商详楼层众多，CSS 样式也复杂多样，因此在 ArkTS 版本中，在 CSS 的解析和属性应用阶段占用了过多的时间，在 CAPI 版本进行了 CSSOM 模块的架构升级后，带来了明显的性能提升。</p><p><img loading="lazy" src="https://img10.360buyimg.com/img/jfs/t1/82615/40/28260/13512/66d80b4eFa86acffa/d45e6b2614a8b4a7.png" alt="iShot_2024-09-03_22.57.29.png" class="img_ZOZK"></p><p>基于 Taro 开发的页面，在华为性能工厂的专业测试下，大部分都以优异的成绩通过了性能验收，充分证明了 Taro 在鸿蒙端的高性能表现。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="总结和未来展望">总结和未来展望<a href="#总结和未来展望" class="hash-link" aria-label="总结和未来展望的直接链接" title="总结和未来展望的直接链接">​</a></h2><p>Taro 目前已经成为一个全业务域的跨端开发解决方案，实现 Web 类（如小程序、Hybrid）和原生类（iOS、Android、鸿蒙）的一体化开发，在高性能的鸿蒙适配方案的加持下，业务能快速拓展到新兴的鸿蒙系统中去，可以极大满足业务集约化开发的需求。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="未来计划">未来计划<a href="#未来计划" class="hash-link" aria-label="未来计划的直接链接" title="未来计划的直接链接">​</a></h3><p>后续，Taro 还会持续在性能上进行优化，以更好地适配鸿蒙系统：</p><ul><li><strong>将开发者的 JS 业务代码和应用框架层的 JS 代码与主线程的 UI 渲染逻辑分离</strong>，另起一条 JavaScript 线程，执行这些 JS 代码，避免上层业务逻辑堵塞主线程运行，防止页面出现卡顿、丢帧的现象。</li></ul><p><img loading="lazy" src="https://img30.360buyimg.com/img/jfs/t1/234809/21/25643/54173/66d7d845F3c696808/b048628d4306411f.png" alt="image.png" class="img_ZOZK"></p><ul><li><strong>实现视图节点拍平</strong>，将不影响布局的视图节点进行整合，减少实际绘制上屏的页面组件节点数量，提升页面的渲染性能。</li></ul><p><img loading="lazy" src="https://img20.360buyimg.com/img/jfs/t1/172158/1/46156/23746/66d7d843F8b63c336/046acd503fcc9253.png" alt="image.png" class="img_ZOZK"></p><p>（图片来自 React Native 官网）</p><ul><li><strong>实现原生性能级别的动态更新能力</strong>，支持开发者在不重新编译和发布应用的情况下，动态更新应用中的页面和功能。</li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="总结">总结<a href="#总结" class="hash-link" aria-label="总结的直接链接" title="总结的直接链接">​</a></h3><p>京东鸿蒙原生应用是 Taro 打响在鸿蒙端侧适配的第一枪，证明了 Taro 方案适配鸿蒙原生应用的可行性。这标志着 Taro 在多端统一开发上的新突破，意味着 Taro 将为更多的企业和开发者提供优秀的跨端解决方案，使开发者能够以更高的效率开发出适配鸿蒙系统的高性能应用。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="京东鸿蒙原生应用体验视频">京东鸿蒙原生应用体验视频<a href="#京东鸿蒙原生应用体验视频" class="hash-link" aria-label="京东鸿蒙原生应用体验视频的直接链接" title="京东鸿蒙原生应用体验视频的直接链接">​</a></h3><video src="https://ling-cdn.s3-cache-accelerate.cn-north-1.jdcloud-oss.com/ling-material-video/1418233744795033602/52f17f47e1d1a49c7b4878136041dfe8.m4v" width="100%" height="auto" controls=""></video>]]></content:encoded>
            <category>v4</category>
            <category>harmony</category>
        </item>
        <item>
            <title><![CDATA[使用 harmony-hybrid 平台插件开发鸿蒙应用]]></title>
            <link>https://docs.taro.zone/blog/2024/01/18/harmony-hybrid</link>
            <guid>https://docs.taro.zone/blog/2024/01/18/harmony-hybrid</guid>
            <pubDate>Thu, 18 Jan 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[基于 web 的混合开发模式 -- harmony-hybrid 平台发布]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorWithStickyNavbar_mCi1" id="一背景">一、背景<a href="#一背景" class="hash-link" aria-label="一、背景的直接链接" title="一、背景的直接链接">​</a></h2><p>HarmonyOS NEXT Developer Preview 版本发布在即，越来越多的厂商加快产品鸿蒙化进程。如何把已有产品快速迁移至鸿蒙原生是大部分厂商亟待解决的问题之一。在此背景下， Taro <code>harmony-hybrid</code> 平台插件提供基于 Web 开发鸿蒙原生应用的迁移方案。该插件继承 H5 平台，并注入部分鸿蒙原生能力接口，扩展了 Web 应用操作设备的能力，助力厂商的产品快速鸿蒙化。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="二harmony-hybrid-平台">二、harmony-hybrid 平台<a href="#二harmony-hybrid-平台" class="hash-link" aria-label="二、harmony-hybrid 平台的直接链接" title="二、harmony-hybrid 平台的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-简介">1. 简介<a href="#1-简介" class="hash-link" aria-label="1. 简介的直接链接" title="1. 简介的直接链接">​</a></h3><p>Taro <code>harmony-hybrid</code> 平台插件和鸿蒙三方模块 <a href="https://ohpm.openharmony.cn/#/cn/detail/@hybrid%2Fweb-container" target="_blank" rel="noopener noreferrer">@hybrid/web-container</a> 是上述方案的两大组成部分。该方案创建的鸿蒙应用的打包部署形态如下图所示：</p><p><img loading="lazy" src="https://img14.360buyimg.com/ling/jfs/t1/244964/1/2934/36138/65a88701Fc20a552c/7d44450501db4b57.png" alt="运行框架" class="img_ZOZK"></p><p>编译 Taro 工程生成 harmony-hybrid 平台的 Web 资源，由 Web 服务器加载运行；鸿蒙应用使用 TaroWebContainer 组件加载网站资源，借助该组件提供的 Taro API 实现来达到业务访问原生与使用硬件的能力。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-基于-harmony-hybrid-开发鸿蒙应用">2. 基于 harmony-hybrid 开发鸿蒙应用<a href="#2-基于-harmony-hybrid-开发鸿蒙应用" class="hash-link" aria-label="2. 基于 harmony-hybrid 开发鸿蒙应用的直接链接" title="2. 基于 harmony-hybrid 开发鸿蒙应用的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="编译-taro-工程生成-harmony-hybrid-应用资源">编译 Taro 工程生成 harmony-hybrid 应用资源<a href="#编译-taro-工程生成-harmony-hybrid-应用资源" class="hash-link" aria-label="编译 Taro 工程生成 harmony-hybrid 应用资源的直接链接" title="编译 Taro 工程生成 harmony-hybrid 应用资源的直接链接">​</a></h4><p>通过 Taro CLI 工具基于 <code>默认模板</code> 创建 Taro 工程，使用如下编译命令生成 harmony-hybrid 平台的 Web 资源：</p><div class="language-shell codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-shell codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># yarn</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">yarn</span><span class="token plain"> build:harmony-hybrid</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># npm script</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run build:harmony-hybrid</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># pnpm script</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> build:harmony-hybrid</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>生成的 Web 资源使用 Web 服务器加载运行或者内置于鸿蒙应用的 <code>resources/rawfile</code> 目录下。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="创建鸿蒙应用">创建鸿蒙应用<a href="#创建鸿蒙应用" class="hash-link" aria-label="创建鸿蒙应用的直接链接" title="创建鸿蒙应用的直接链接">​</a></h4><p><code>@hybrid/web-container</code> 三方模块提供自定义鸿蒙组件 <code>TaroWebContainer</code>，为运行其中的 harmony-hybrid 应用提供运行时访问系统设备的能力。</p><p>在鸿蒙应用中使用 <code>TaroWebContainer</code> 组件的示例代码如下：</p><ul><li>entry/oh-package.json5 配置</li></ul><div class="language-json codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-json codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token property" style="color:#36acaa">"dependencies"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"@hybrid/web-container"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"1.0.0"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ul><li>页面中使用 <code>TaroWebContainer</code> 组件：</li></ul><div class="language-ts codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-ts codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> TaroWebContainer</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> HostPageState</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> TaroWebController </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@hybrid/web-container'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token decorator at operator" style="color:#393A34">@</span><span class="token decorator function" style="color:#d73a49">Entry</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token decorator at operator" style="color:#393A34">@</span><span class="token decorator function" style="color:#d73a49">Component</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">struct HarmonyHybridSample </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token decorator at operator" style="color:#393A34">@</span><span class="token decorator function" style="color:#d73a49">State</span><span class="token plain"> pageState</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> HostPageState </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> HostPageState</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">PageInit</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token decorator at operator" style="color:#393A34">@</span><span class="token decorator function" style="color:#d73a49">State</span><span class="token plain"> taroWebController</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> TaroWebController </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">TaroWebController</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">build</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">Column</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">TaroWebContainer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        webUrl</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'https://域名/index.html'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        webUrlPrefix</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'https://域名/'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        taroWebController</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">taroWebController</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        pageState</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pageState</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">width</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'100%'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">height</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'100%'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>具体的使用方式可以参考 <code>@hybrid/web-container</code> 的 <code>README.md</code> 文档。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="环境要求">环境要求<a href="#环境要求" class="hash-link" aria-label="环境要求的直接链接" title="环境要求的直接链接">​</a></h4><ul><li>Taro: 3.6.24 以上版本</li><li>鸿蒙集成开发环境: <code>DevEco Studio NEXT Developer Preview1</code> 及以上版本</li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="三转换补充说明">三、转换补充说明<a href="#三转换补充说明" class="hash-link" aria-label="三、转换补充说明的直接链接" title="三、转换补充说明的直接链接">​</a></h2><p>对于具备小程序版本的产品，可以使用 <a href="https://docs.taro.zone/docs/taroize/" target="_blank" rel="noopener noreferrer">@tarojs/cli-convertor</a> 命令行工具转化成 Taro 项目，再编译成 <code>harmony-hybrid</code> 来加快适配流程。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="最后">最后<a href="#最后" class="hash-link" aria-label="最后的直接链接" title="最后的直接链接">​</a></h2><p>后续我们会继续完善对 <code>harmony-hybrid</code> 平台的适配工作，并根据最新鸿蒙系统能力来补齐和完善 <code>harmony-hybrid</code> 平台能力，同时也希望广大的开发者和参与到 Taro 开源生态共建的同学们，能够一起参与<code>harmony-hybrid</code> 平台的构建和完善，助力鸿蒙生态和 Taro 开源生态的发展。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="相关阅读">相关阅读<a href="#相关阅读" class="hash-link" aria-label="相关阅读的直接链接" title="相关阅读的直接链接">​</a></h3><ul><li><p><a href="https://developer.harmonyos.com/cn/develop/deveco-studio" target="_blank" rel="noopener noreferrer">《华为开发者工具 DevEco Studio》</a></p></li><li><p><a href="https://developer.harmonyos.com/cn/documentation" target="_blank" rel="noopener noreferrer">《鸿蒙开发文档》</a></p></li></ul>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[多编译内核生态下的极速研发体验]]></title>
            <link>https://docs.taro.zone/blog/2023/03/29/D2_17</link>
            <guid>https://docs.taro.zone/blog/2023/03/29/D2_17</guid>
            <pubDate>Wed, 29 Mar 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[本文整理自第十七届 D2 多终端技术大会《卓越工程专题：多编译内核生态下的极速研发体验》分享内容，项目工程化中研发体验是不可分割的一环，通过多编译内核生态来应对多终端研发场景下的研发体验与效率提升需求。]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorWithStickyNavbar_mCi1" id="一背景">一、背景<a href="#一背景" class="hash-link" aria-label="一、背景的直接链接" title="一、背景的直接链接">​</a></h2><p>跨端是前端研发过程中经久不衰的话题，为应对与不同场景的跨端需求也会有很多不一样的跨端解决方案诞生。为支持各端的研发诉求，我们为跨端解决方案提供了多编译内核，并以此逐步拓展各编译内核的特性，为开发者的研发体验提速提供更多可能性，这也就是所谓<strong>多编译内核生态</strong>。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="多编译内核生态">多编译内核生态<a href="#多编译内核生态" class="hash-link" aria-label="多编译内核生态的直接链接" title="多编译内核生态的直接链接">​</a></h3><p>不论是在各种前端工程化或是跨端解决方案中，<code>Webpack</code> 的出现频率高是毋庸置疑的，同样在 Taro 中也是如此，基于 <code>Webpack</code> 实现的编译内核，就能够为包括各类小程序在内的终端项目提供很好的研发体验，达成一整套开端解决方案来实现所谓「一套代码，多端运行」的能力特性。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/0.png" alt="Untitled" class="img_ZOZK"></p><p>但实际上在跨端方案不断迭代的过程中，在个别端生态中可能遇到各种问题和瓶颈也逐渐凸显，比如说我们基于 <code>React Native</code> 实现的 App 端就是如此。</p><p>基于 <code>Webpack</code> 实现输出 RN 项目的能力，并借助于 <code>Metro</code> 完成 RN App 的打包和调试等等工作，会是十分便捷的方案，但在实际项目中却依旧会遇到诸如框架层面不能与 Metro 直接交互等等很多问题，维护起来也有诸多不便。</p><p>这也就是为什么我们需要在框架内提供 <code>Metro</code> 编译内核，来统一 RN 端的打包工具，为 <code>React Native</code> 生态提供更好的兼容性。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/1.png" alt="Untitled" class="img_ZOZK"></p><p>相较于 <code>Webpack</code>，使用 <code>Vite</code> 编译项目在 Web 端会有很多优势，在社区内一直以来也有很高的呼声，不在少数的开发者希望能够在跨端解决方案中支持使用 Vite 编译项目。那么如果能够基于 Vite 实现与 <code>Webpack</code> 编译内核基本一致的能力，来完成多终端项目的编译工作，实际体验又会如何呢？</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/2.png" alt="Untitled" class="img_ZOZK"></p><p>正是由于许多诸如此类的因素存在，也让我们不断思考解决的方案，最终在面向多终端的研发场景中，继跨端、跨框架之后，有了新的目标——即支持多编译内核的研发生态。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="面向多终端研发">面向多终端研发<a href="#面向多终端研发" class="hash-link" aria-label="面向多终端研发的直接链接" title="面向多终端研发的直接链接">​</a></h3><p>在面向多终端研发的场景中，通过借助 <code>Webpack</code>、<code>Vite</code>、<code>Metro</code> 这类编译工具，可以实现包括 <strong>Web</strong>、<strong>App</strong>、<strong>小程序</strong>、<strong>鸿蒙</strong>等多终端的研发场景，开发者也能根据项目按需选择，以期获得更佳的研发体验。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/3.png" alt="Untitled" class="img_ZOZK"></p><p>尽管我们主动选择在编译环节使用多编译内核，但这样的架构却并不只有优势，同时还要不少问题亟待解决。</p><p>譬如需要通过提供一致的生命周期支持，抹平各个插件对支持不同编译内核时的差异；框架配置也不会由于编译内核切换，导致额外的学习成本……编译内核的切换能够在研发和生态等各层面都能平滑无感，这也是我们正努力去实现的。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/4.png" alt="Untitled" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="二极速构建">二、极速构建<a href="#二极速构建" class="hash-link" aria-label="二、极速构建的直接链接" title="二、极速构建的直接链接">​</a></h2><p>多编译内核或许是一个很好的架构方案，能够支撑各研发场景的特性实现，并给开发者更多选择的自由，但作为跨端解决方案，却不能局限于此。在面向多终端的研发场景中，多编译内核的研发生态下，想要为开发者提供极致的，极速研发体验又该怎样去做呢？</p><p>以各个编译内核及其生态为出发点，在框架内为不同研发场景提供极速构建的能力，结合框架架构内的插件系统，最终能够基于多编译内核方案得到全新的架构。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/5.png" alt="Untitled" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="webpack-编译内核">Webpack 编译内核<a href="#webpack-编译内核" class="hash-link" aria-label="Webpack 编译内核的直接链接" title="Webpack 编译内核的直接链接">​</a></h3><p>在这些编译工具中，相信大家对于 <code>Wepback</code> 都不陌生，将开发者的代码输入 <code>Wepback</code> 内核编译就能够得到 Web 或小程序等等终端应用。</p><p>在 <code>Webpack</code> 编译内核中，我们通过 <code>combination</code> 为多端编译提供项目配置处理，不同模块的加载规则等通用能力；小程序端还有插件处理小程序模块和原生模块加载等特性；web 端也需要插件根据打包模式来处理应用和页面入口等等。结合框架的插件机制，开发者也能介入编译流程中，为项目提供自定义拓展，满足个性化的需求。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/6.png" alt="Untitled" class="img_ZOZK"></p><p>想要在 <code>Webpack</code> 内核中达成更好的构建体验，除了自定义插件还有更多方案可以选择，比如 <code>Webpack5</code> 的持久化缓存就是其中之一，考虑到编译安全相比于速度更为重要，该特性会默认关闭。当然在绝大多数情况下，持久化缓存都能够以较稳定的方式帮助开发者提升研发体验，如果当项目研发过程中受困于编译速度时这会是个不错的选择。</p><p>借助于 <code>ESBuild</code> 等不同工具，我们提供了各可配置模块供开发者选择，依照需求选择更适合项目的工具，依赖预编译也是为此设计的重要特性之一，通过提前编译依赖可以在实际项目中极大提升我们的研发效率，在社区内也广受好评。</p><p><strong>依赖预编译</strong>，也就是 <code>PreBundle</code> 特性通过 <code>ESBuild</code> 扫描项目依赖并提前编译，借助于 <code>Webpack</code> 的模块联邦能力分别构建 <strong>Host</strong> 和 <strong>Remote</strong> 应用，就能实现这一有意思的特性，帮助开发者提升多终端研发的编译体验。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/7.png" alt="Untitled" class="img_ZOZK"></p><p>原理其实不复杂，但对应不同端实际执行的操作会有一定不同，在小程序端包括了以下几个步骤。</p><p>首先需要扫描项目内的依赖，通过 <code>ESBuild</code> 能够很好的识别项目内使用到的 JS 模块，但是也有不少细节需要注意并处理的，比如识别 <code>Vue</code> 中 <code>setup</code> 语法，避免误判导致遗漏需要预编译的模块；同时其它终端所需的依赖也需避免误判被编译，导入终端不支持的依赖等等。</p><p>得到项目所需的依赖后，再次通过 <code>ESBuild</code> 将其打包就能够得到项目所需的 Bundle，但在这里我们还需要需解析对应依赖的入口模式，同时借助于自定义的 <code>SWC</code> 插件优化代码编译能力，将基于 <code>CJS</code> 模式输出的依赖转换成 <code>ESM</code> 后的编译结果进行兼容。</p><p>想要在 ESBuild 输出结果中保留原始路径，将会使最终结果难以预测，所以我们需要将其扁平化处理，仅通过虚拟模块保留原始路径。最后对编译内容进行缓存，避免多次编译造成性能损耗。</p><p>紧接着，我们需要借助 <code>Webpack</code> 通过 MF 插件打包出 <strong>Remote</strong> 应用，在这个过程中同时需要传入编译所需的各种插件，比如说 <code>ProviderPlugin</code> 用于提供小程序依赖的运行时环境，并缓存编译结果。<strong>Host</strong> 应用必要的配置将会同时生成，并将 <strong>Host</strong> 应用及其资产注入到小程序目录，供最终的项目运行使用。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/8.png" alt="Untitled" class="img_ZOZK"></p><p>与小程序端相比，Web 端有很多步骤非常相似，比如前置的依赖扫描与打包等等，尽管具体配置会存在一定差异，比如 Web 端用于 API Shaking 的自定义 <code>Babel</code> 插件就需要通过 <code>SWC</code> 插件重新实现避免基于 Taro 的组件库在 <code>PreBundle</code> 特性中无法得到支持。同时由于小程序和 Web 端本身的差异，后续的步骤中也会有较大差异，比如需要为 <strong>Remote</strong> 应用配置代理。</p><p>虽然 Web 端编译 <strong>Remote</strong> 应用与小程序一样，都需要通过 <code>ContainerPlugin</code> 创建额外的容器入口，但却并不需要借助 <code>ProviderPlugin</code> 注入 runtime 等等依赖，同时也无需将远程依赖修改为同步模式等等操作。</p><p>在配置 <strong>Host</strong> 应用插件时，需要通过 <code>ContainerReferencePlugin</code> 将 <strong>Remote</strong> 提供的所有依赖修改为远程模块，并通过特定的引用添加作为外部资源的容器，允许它们加载远程模块，同时为公共的运行时注入必须的 <code>Webpack</code> 工具函数。最后在使用前，我们还需要为 <strong>Remote</strong> 应用设置代理，供 <strong>Host</strong> 应用加载应用这些依赖。</p><p>实际上，当我们在 <strong>Host</strong> 应用中直接使用未经加载的 <strong>Remote</strong> 依赖时，会导致应用报错。在业内一些类似的方案中，往往会需要通过 <code>Babel</code> 修改引用依赖相关的代码逻辑，但实际上如果在应用入口嵌套一层动态加载，<code>Webpack</code> 就会预加载这些 <strong>Remote</strong> 依赖避免相关问题出现，这也是在官方 MF 示例中推荐的方法。而在该特性方案中，为支持包括外出使用、多页应用构建等特性，我们选择基于 <code>virtual-module</code> 实现这一操作，最大程度借助于 <code>Webpack</code> 自有的能力与生态。</p><p>在通过 <code>NutUI</code> 的示例项目测试编译预编译时，可以发现通过 <code>PreBundle</code> 特性能够得到不错的优化，在社区开发者的体验当中，我们也收到了类似的反馈，小程序编译速度约为使用前的 <strong>20%</strong>，Web 应用也将耗时降低至原方案的约 <strong>30%</strong>。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/9.png" alt="Untitled" class="img_ZOZK"></p><p>这样的性能提升结果让人十分满意，但在跨端环境下使用模块联邦能力，特别是在小程序环境中使用真的如此容易？受限于小程序本身，借助于网络请求来实现该特性会极大程度的降低小程序的使用体验，牺牲用户体验不应该是一个成熟框架应该做的。</p><p>模块联邦特性实际是基于 <code>ModuleFederationPlugin</code> 封装提供给开发者使用，如果我们基于它内置的两个插件实现逻辑调整能否优化这一实现呢？</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/10.png" alt="Untitled" class="img_ZOZK"></p><p>在这两个内置插件中，<code>ContainerPlugin</code> 在构建小程序端使用的 Remote 应用时，需要在插件内为小程序创建额外的容器入口，并提供模块自动注册相关的逻辑，通过劫持 <code>ContainerEntryDependency</code> 将依赖异步逻辑改为同步；与此同时还需要收集使用到的 <code>Webpack</code> 工具函数，并删除冗余的入口、runtime 等 chunk。</p><p>而在 <code>ContainerReferencePlugin</code> 中，需要将预编译依赖设置为远程模块，并注入收集到的 <code>Webpack</code> 工具函数，修改 <code>remote</code> 模块中的 <code>id</code> 映射对象，并插入自动注册模块相关的逻辑，在 <code>app.js</code> 中通过 <code>require</code> 依赖所有预编译生成的 <code>chunk</code> 和 <code>remoteEntry</code>；同时需要删除冗余的文件，比如已经无用的远程模加载模块，减少不必要的代码输出。</p><p>通过基于提供 <code>ContainerPlugin</code>、<code>ContainerReferencePlugin</code> 的魔改插件 <code>TaroModuleFederationPlugin</code> 供框架使用，就能在小程序中不损失用户体验的同时使用这一特性。开发者也可以自行配置使用，不过如果你的项目正在使用跨端框架，直接使用 <code>PreBundle</code> 特性会是个更好的选择。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/11.png" alt="Untitled" class="img_ZOZK"></p><p>当然不论是 <code>TaroModuleFederationPlugin</code> 还是 <code>PreBundle</code> 特性，都可以在第三方的项目中配置使用，以 <code>PreBundle</code> 为例，通过 <code>Chain</code> 传入 <code>Webpack</code> 配置，并执行实例上的 <code>run</code> 方法，我们就能得到最终所需的 <code>Webpack</code> 项目配置文件，并启动项目。</p><p>目前也有不少外部项目都通过接入该特性，来获得更佳的研发体验。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="vite-编译内核">Vite 编译内核<a href="#vite-编译内核" class="hash-link" aria-label="Vite 编译内核的直接链接" title="Vite 编译内核的直接链接">​</a></h3><p>实现 <code>Vite</code> 编译内核，可以说是在追赶潮流，也是当下社区开发者们的呼声，但更多也是希望能够通过支持 <code>Vite</code> 编译提供更多元的研发场景，让开发者可以根据实际需求，自由选择项目在对应场景下所需的编译内核。</p><p>相较于传统的编译模式，启动项目时需优先扫描整个项目依赖和代码，才能在应用完成构建后提供服务；<code>Vite</code> 通过使用 <code>ESBuild</code> 构建依赖，并在浏览器请求源码时，将对应模块转换为原生 ESM 按需提供源码，研发速度要快很多。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/12.png" alt="Untitled" class="img_ZOZK"></p><p>通过 <code>Vite</code> 我们可以给 <code>Web</code> 端提供更好的研发体验，当然还需要兼容各种小程序的特性，比如说路由、TabBar 等等，还有各类 API 和跨端组件库，也需要兼容性配置。</p><p>同样，在 <code>Webpack</code> 编译内核中已经支持的各种能力和特性也不能抛下，包括 <code>React</code>、<code>Preact</code> 等前端 UI 框架的支持，多路由模式等等，也需要在 <code>Vite</code> 编译内核中得到支持。</p><p>相比于在 Web 端能够得到不小的提升，在适配小程序场景中的实际收益相对就要低上不少，由于缺少持久化缓存，在编译大型项目时的实际体验与 <code>Webpack</code> 相比并无明显改善，但是通过配置跳过更新的页面也能够给需要的项目提供不错的研发体验。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/13.png" alt="Untitled" class="img_ZOZK"></p><p>同样由于小程序自身的限制，<code>Vite</code> 并不能以开发模式来完成小程序的编译，我们在生产模式下提供应用和各页面入口的编译，写入到对应的小程序目录中，并以虚拟模块的形式更新页面，配合适配小程序端的 <code>Vite</code> 插件就可以完成小程序端编译工作。</p><p>总结来说，在 <code>Vite</code> 编译核心内，我们通过为小程序和 Web 端提供 config、entry、style 插件来适配各端的能力，当然小程序还需要 page 插件来为页面提供额外的处理。结合框架自身的插件机制，我们就能让 <code>Vite</code> 在适配跨端解决方案的同时，与其它编译内核形成一个互补选项，供开发者使用。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/14.png" alt="Untitled" class="img_ZOZK"></p><p>在整个编译系统中，除了我们提供的 <code>Vite</code> 编译内核，CLI 还有配置工具也需要为此同步做出调整，结合内核提供的代码和样式编译能力，就能为绝大多数的编译场景提供支持。同时一些工作我们也在社区内逐步去推进完成，比如小程序的分包能力，还有 Web 端的多路由应用模式等等，都会在之后的版本里一一补齐。</p><p>当然在使用 <code>Vite</code> 内核还是可能遇到一些问题，我们在后续也会尝试去针对性优化，比如打包输出成 CJS 模式，在配置 <code>splitChunk</code> 后可能会导致循环依赖，在小程序中报错；另一个就是刚刚有提到的编译缓存，在小程序中二次编译速度可能不及基于其它打包工具的编译内核。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="metro-编译内核">Metro 编译内核<a href="#metro-编译内核" class="hash-link" aria-label="Metro 编译内核的直接链接" title="Metro 编译内核的直接链接">​</a></h3><p>从 <code>Webpack</code> 输出 RN 项目，到改用 <code>Metro</code> 是一个很自然的选择，借助于 <code>Metro</code> 我们也能将适配 <code>React Native</code> 的很多操作，从编译时转向到运行时，为开发者提供更好的研发体验。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/15.png" alt="Untitled" class="img_ZOZK"></p><p>从 CLI 读取项目配置，到 <code>Metro</code> 内核完成编译，并启动调试服务，<code>Metro</code> 都能比 <code>Webpack</code> 更好的兼容 <code>React Native</code> 已有的生态环境。在框架层面需要做的，仅是为开发者提供合并定义 <code>Metro</code> 配置的能力，并让 <code>Metro</code> 可以理解我们在适配其它端时对 <code>Webpack</code> 的操作，并在一定程度上复刻。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/16.png" alt="Untitled" class="img_ZOZK"></p><p>基于 <code>@tarojs/rn-transformer</code> 处理 RN 的入口文件，在编译时注入页面的包装方法等，并将入口的全局样式注入到页面中；同时对运行时进行一定程度的修改，比如基 <code>React Navigation</code> 对路由进行封装，提供动态创建导航的方法，并且封装导航相应的 API 内容；在 runtime 中对应用和页面配置进行处理等等。</p><p>相比于过往方案，<code>Metro</code> 能够提升编译速度，提供更好的 SourceMap 支持，提升研发效率和体验；同时规避由于 <code>Webpack</code> 提供的多 entry 模式导致的问题，并降低包体大小等等；也与 <code>React Native</code> 默认配置保持一致，支持更灵活的合并方式。</p><p>在 App 端，我们通过 <code>Metro</code> 在 <code>Transformer</code> 和 <code>Runtime</code> 两个层面对项目进行修改，让方案可以在支持多端的同时，更加顺滑的支持使用 RN 完成 App 端的研发工作。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/17.png" alt="Untitled" class="img_ZOZK"></p><p>那如果希望在框架之外，使用这些开发好的 RN 能力，也有比较简单的方法快速接入，通过 <code>@tarojs/rn-supporter</code> 就能提供编译、运行时配置，和资源样式解析方案的支持。</p><p>具体使用如下图代码所示，需要注意的是，在该外部使用的方案中，路由相关的逻辑需要自行处理。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/18.png" alt="Untitled" class="img_ZOZK"></p><p>总结来说，在我们的编译系统方案中，通过 CLI 和插件系统结合多个编译内核来为开发者提供跨端解决方案。</p><p>在编译内核之外，插件系统也能够很好的帮助框架为开发者根据编译平台，提供解决方案。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/19.png" alt="Untitled" class="img_ZOZK"></p><p>通过 CLI 完成编译工作时，我们也可以通过加载不同平台插件的方式，来完成编译时拓展和运行时能力的注入，提供最终的编译结果。</p><p>在插件系统中，我们从 CLI、编译过程和编译平台三个方向提供能力的拓展，结合各个生命周期，比如：<code>onBuildStart</code>、<code>onCompilerMake</code>、<code>modifyWebpackChain</code> 等等，各个编译内核都能为不同端编译提供更加个性化的定制服务。借助于此，我们可以提供 <code>React</code>、<code>Vue</code>、<code>Flutter</code> 等等不同 DSL 的插件；也能为小程序、鸿蒙等多端提供端平台插件，辅助完成编译。</p><p>在这样的架构下，开发者也能根据我们提供的生命周期，横向或纵向拓展编译能力，为各个终端拓展编译能力支持，比如说飞书、钉钉、企微等等小程序还有鸿蒙端平台插件，都是 Taro 技术委员会或者社区开发者通过这样的方式提供的。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/20.png" alt="Untitled" class="img_ZOZK"></p><p>同时在其它方面，为提升构建体验也有不少小优化，比如 <code>SWC</code> 虽然与 <code>Babel</code> 实际上还存在差异，但在很多特殊的应用场景下已经足够使用，比如在依赖预编译中提供代码编译，以及应用与页面配置读取等等，都有不少提升。</p><p><code>ESBuild</code> 也是如此，除了 <code>PreBundle</code> 特性中的使用，我们还在压缩代码和样式中提供了相关的配置，不过相比于 <code>terser</code> 和 <code>csso</code>，还存在压缩效果等问题，加之对其稳定性的考量，我们并没有将其配置为默认选项，可以由开发者自行选择。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="三总结与展望">三、总结与展望<a href="#三总结与展望" class="hash-link" aria-label="三、总结与展望的直接链接" title="三、总结与展望的直接链接">​</a></h2><p>跨端解决方案结合多编译内核生态，我们能够在开放式的跨端跨框架解决方案中，为开发者提供更高的自由度和研发体验。</p><p>通过 <code>React</code>、<code>Vue</code>、<code>Svelte</code> 等 DSL 插件，结合 Web 端渲染规范，小程序的标准 API 和组件库等等，我们能够为不同终端提供可靠的跨端能力，同时在 <code>Webpack</code>、<code>Vite</code>、<code>Metro</code> 等多个编译内核的帮助下，我们能够提供更加高效的研发体验，为日常的开发工作提供更多的便利。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/21.png" alt="Untitled" class="img_ZOZK"></p><p>对于框架本身来说，在未来我们也会提供更多的前端 UI 框架或编译工具的支持，比如 Solid 等等。</p><p>性能方面，也希望为小程序提供更好的热重载、预渲染等能力，提升多端研发体验，提供适用于更多场景的多端性能优化方案。</p><p>同时根据场景提示性能优化方案，以及性能指标测评工具，为项目优化提供指引。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/22.png" alt="Untitled" class="img_ZOZK"></p><p>对于 <code>Flutter</code> 编译内核的支持也在日程中，在社区内持续推进；通过 <code>Web Assembly</code> 对小程序的运行时进行优化等等，也都在 Taro 未来的路线规划当中，敬请期待。</p><p>同时，我们还预期提供框架的测试方案，通过 <code>TestRenderer</code> 为跨端方案的测试提供更好的体验。</p><p><img loading="lazy" src="https://storage.jd.com/aotu-team/zakary-blog/2023-03-29-Taro-D2_17/23.png" alt="Untitled" class="img_ZOZK"></p><p>最后关于 Taro 开源工作，目前已经由 Taro 技术委员会主导，在 Taro 生态<strong>超过 700 位</strong>贡献者中，有 <strong>50 余</strong>位行业内各个公司的同学长期参与 <code>Taro TOC</code> 的会议分享，所有贡献者们都为推进 Taro 跨端解决方案不断迭代作出了诸多努力和贡献，正因如此才能够让我们在多编译内核生态下，获得更加极致的研发体验。</p><p>正如技术委员会成员 <code>@xueshuai</code> 在投稿 v3.6 版本代号时说道，“「Reach」代表着不屈和希望，给开发者带来更好的用户体验，是全体开发者的坚守“，希望社区的积极氛围可以引导 Taro 的生态更加美好。</p>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[Taro v3.6 Reach 正式发布]]></title>
            <link>https://docs.taro.zone/blog/2023/02/01/Taro-3.6</link>
            <guid>https://docs.taro.zone/blog/2023/02/01/Taro-3.6</guid>
            <pubDate>Wed, 01 Feb 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Taro v3.6 Reach 正式版本发布 🚀]]></description>
            <content:encoded><![CDATA[<p>两个月前，我们发布了 Taro v3.6 的 canary 版本，在技术委员会和社区范围内提供跨端路由库、跨框架组件等主要能力和重要修复的测试，并发起社区投票正式确定了当前版本的代号 —— Reach。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2022-11-18-Taro-3.6-canary/taro-3.6.jpg" alt="taro-3.6.jpg" class="img_ZOZK"></p><p>日前 Taro v3.6 正式版本已经发布，下文将围绕 3.6 版本内的跨端、平台能力支持等多个方面展开，快速了解在 v3.6 中各个重要特性。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="一跨端能力支持">一、跨端能力支持<a href="#一跨端能力支持" class="hash-link" aria-label="一、跨端能力支持的直接链接" title="一、跨端能力支持的直接链接">​</a></h2><p>支持各类跨端能力，抹平多端研发之间的体验差异，是 Taro 一直以来尝试去实现的，基于 Taro 3 适配多端前端 UI 框架的逻辑，通过在小程序端模拟实现框架所需的 BOM / DOM API 就能达成对于各类跨端能力的适配。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-支持路由库">1. 支持路由库<a href="#1-支持路由库" class="hash-link" aria-label="1. 支持路由库的直接链接" title="1. 支持路由库的直接链接">​</a></h3><p>在 Web BOM 中，History &amp; Location 对象是重要组成部分，它们是实现前端路由的关键。Taro 为支持前端路由库的使用，在运行时中引入了 <code>histroy</code> <code>location</code> 对象的实现，同时尽可能与 Web 端规范对齐。通过在 <code>window</code> 对象上访问到 <code>history</code> 和 <code>location</code> 对象，并支持监听 <code>hashchange</code> 和 <code>popstate</code> 事件，为跨端使用路由库提供基础。</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 统称: 页面路由状态</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token dom variable" style="color:#36acaa">window</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">history</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token dom variable" style="color:#36acaa">window</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">location</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 支持监听事件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token dom variable" style="color:#36acaa">window</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addEventListener</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'hashchange'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token dom variable" style="color:#36acaa">window</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addEventListener</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'popstate'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>小程序天然支持多页面(<code>pages</code> 数组配置)，因此 Taro 并非以整个应用为一个路由系统，而是顺应小程序规范以页面维度进行路由管理。每当切换页面时，会将当前页面的页面路由状态缓存。跳转至新页面后会重新创建页面路由状态，并挂载在 <code>window</code> 对象上。当返回上一级页面时，会将上一级页面的页面路由状态重新挂载到 <code>window</code> 对象中。</p><p>至此，可以在小程序中使用成熟的前端路由库了，包括 <code>react-router</code> 和 <code>vue-router</code>。在路由库中，诸如 <code>&lt;Link&gt;</code> 组件内部会动态生成 <code>&lt;a&gt;</code> 标签，因此需要引入 <a href="https://nervjs.github.io/taro-docs/docs/use-h5" target="_blank" rel="noopener noreferrer"><code>@tarojs/plugin-html</code></a> 插件以支持在 Taro 中使用 <code>html</code> 标签开发组件。</p><div class="language-json codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-json codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"plugins"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"@tarojs/plugin-html"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在 Taro 编译过程中，当 DOM 序列化数据的 nn 字段为 HTML 标签时，标签会映射为对应的小程序组件名称。由于无法提前预知动态标签，因此需要应用显式告知可能会使用到的动态标签。例如在应用中塞入一个无样式的标签名即可：</p><div class="language-html codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-html codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">a</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">a</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>更多细节可以查看 <a href="https://nervjs.github.io/taro-docs/docs/router-extend" target="_blank" rel="noopener noreferrer">官方文档</a>，也可以查看官方提供的 DEMO 获知更多用法。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-支持网络请求库">2. 支持网络请求库<a href="#2-支持网络请求库" class="hash-link" aria-label="2. 支持网络请求库的直接链接" title="2. 支持网络请求库的直接链接">​</a></h3><p>与适配各路由库类似，通过对运行时补充就能支持绝大多数的网络请求库，所有请求库在底层都是通过使用 <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest" target="_blank" rel="noopener noreferrer">XMLHttpRequest</a> 或 <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API" target="_blank" rel="noopener noreferrer">Fetch</a> 提供能力支持的，而请求库大多都兼容 <code>XMLHttpRequest</code> 对象，也即是在提供 <code>XMLHttpRequest</code> 对象实现的基础上，就能支持绝大多数的第三方库。</p><p>支持这些成熟的网络请求库（例如 <a href="https://axios-http.com/" target="_blank" rel="noopener noreferrer">axios</a> 等）就能为开发者在跨端研发场景下，提供更好的研发体验。通过引入 @tarojs/plugin-http 插件，为小程序环境提供网络请求库所需的运行时环境支持。</p><div class="language-json codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-json codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"plugins"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"@tarojs/plugin-http"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p>注意：当前 @tarojs/runtime 在小程序环境中缺少<code>Blob</code>、<code>FormData</code>、<code>File</code>对象，这在网络请求库的文件上传特性中是必须的，故暂不支持。</p></blockquote><p>在支持网络请求库的同时，考虑到部分用户，特别是 web 转小程序的项目中依赖 <code>cookie</code> 实现鉴权，@tarojs/plugin-http 插件模拟实现了 <code>document.cookie</code> api 以及通过 http 响应头 <code>Set-Cookie</code> 来设置客户端 <code>cookie</code> 值 ，行为和 web 中保持一致。 此功能默认设置为关闭，需要的可通过 <code>enableCookie</code> 配置开启。</p><table><thead><tr><th>参数名</th><th>类型</th><th>默认值</th><th>说明</th></tr></thead><tbody><tr><td>enableCookie</td><td>Boolean</td><td>false</td><td>支持 document.cookie 、 通过后端返回 Set-Cookie 响应头来设置 cookie</td></tr><tr><td>disabledFormData</td><td>Boolean</td><td>true</td><td>是否禁用 FormData 全局对象</td></tr><tr><td>disabledBlob</td><td>Boolean</td><td>true</td><td>是否禁用 Blob 全局对象</td></tr></tbody></table><p>在 @tarojs/plugin-http 插件中，以 <code>axios</code> 作为基准库完成测试，如果在使用其他请求库时遇到适配问题，可在社区或通过 <a href="https://github.com/NervJS/taro/issues" target="_blank" rel="noopener noreferrer">issues</a> 反馈相关信息。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="二平台能力支持">二、平台能力支持<a href="#二平台能力支持" class="hash-link" aria-label="二、平台能力支持的直接链接" title="二、平台能力支持的直接链接">​</a></h2><p>拓展更多端平台，适配支持各端能力与特性，是跨端解决方案不断发展的重要组成部分之一。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-支持鸿蒙端平台插件">1. 支持鸿蒙端平台插件<a href="#1-支持鸿蒙端平台插件" class="hash-link" aria-label="1. 支持鸿蒙端平台插件的直接链接" title="1. 支持鸿蒙端平台插件的直接链接">​</a></h3><p>在 Taro 与 OpenHarmony 建立官方合作关系，并受邀成立 <a href="https://gitee.com/openharmony-sig/taro" target="_blank" rel="noopener noreferrer">CrossPlatformUI Sig</a>（跨平台前端框架兴趣小组）后，让 Taro 支持支配鸿蒙就一直在议程上，鸿蒙的方舟开发框架提供类 Web 范式编程，支持使用 JS 开发 UI 层，其语法与小程序相接近，可以沿用 Taro 现有的架构适配鸿蒙。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2022-11-18-Taro-3.6-canary/taro-harmony.png" alt="taro-harmony" class="img_ZOZK"></p><p>持续关注 Taro 的开发者可能还记得，在 v3.5-canary 版本时，我们曾推出支持 Taro 应用适配到鸿蒙平台的插件，但最终没有合入 v3.5 版本主干并顺势推出该能力。</p><p>在 <code>@tarojs/plugin-platform-harmony</code> 端平台插件经过一段时间的打磨，相关能力与特性也在社区推进下持续优化，框架编译的项目在鸿蒙开发板上得到进一步验证，同时在 Taro v3.5 新增的 <code>@tarojs/webpack5-runner</code> 编译内核也能够为鸿蒙项目编译提供支持，终于我们在 v3.6 中再次为社区开发者提供了适配鸿蒙的端平台插件。</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">// config/index.js</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">config = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // 配置使用插件</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  plugins: ['@tarojs/plugin-platform-harmony'],</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  // harmony 相关配置</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  harmony: {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    // 【必填】鸿蒙应用的绝对路径</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    projectPath: path.resolve(process.cwd(), '../MyApplication'),</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    // 【可选】HAP 的名称，默认为 'entry'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    hapName: 'entry',</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    // 【可选】JS FA 的名称，默认为 'default'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    jsFAName: 'default'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>具体使用方法可查看<a href="https://nervjs.github.io/taro-docs/docs/harmony" target="_blank" rel="noopener noreferrer">官方文档</a>，需要注意鸿蒙插件不在 Taro 项目内维护，所以并不会每次发布同版本号版本，直接使用 minor 与 Taro 版本号相同的版本即可。</p><p>特别感谢以下同学为鸿蒙适配作出的贡献：</p><p><a href="https://github.com/AdvancedCat" target="_blank" rel="noopener noreferrer">@AdvancedCat</a>、<a href="https://github.com/jiaozitang" target="_blank" rel="noopener noreferrer">@jiaozitang</a>、<a href="https://github.com/huozhongyi123" target="_blank" rel="noopener noreferrer">@huozhongyi123</a>、<a href="https://github.com/troy-sxj" target="_blank" rel="noopener noreferrer">@troy-sxj</a>、<a href="https://github.com/JSZabc" target="_blank" rel="noopener noreferrer">@JSZabc</a>、<a href="https://github.com/crazyonebyone" target="_blank" rel="noopener noreferrer">@crazyonebyone</a>、<a href="https://github.com/evernoteHW" target="_blank" rel="noopener noreferrer">@evernoteHW</a>、<a href="https://github.com/soulhat" target="_blank" rel="noopener noreferrer">@soulhat</a>、<a href="https://github.com/xueshuai" target="_blank" rel="noopener noreferrer">@xueshuai</a>、<a href="https://github.com/LuMeiling" target="_blank" rel="noopener noreferrer">@LuMeiling</a></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-react-native-能力">2. React Native 能力<a href="#2-react-native-能力" class="hash-link" aria-label="2. React Native 能力的直接链接" title="2. React Native 能力的直接链接">​</a></h3><p>为了让整体开发体验跟 RN 更加一致，减少开发者的理解成本。我们对 @tarojs/rn-runner 的代码进行了重构。将 Taro RN 需要的所有编译逻辑，都封装到了 metro 配置中，与 RN 项目集成会更加灵活。</p><p>新版本在项目根目录下会创建入口文件 index.js 和配置文件 metro.config.js。如项目本身有这两个文件，则不会生成，需要参考<a href="https://github.com/NervJS/taro-project-templates/tree/v3.6/react-native" target="_blank" rel="noopener noreferrer">模板</a>进行添加或合并。另外 Taro RN 的相关配置，集中在 resolver 和 transformer 中，可根据相关源码自行覆盖调整。</p><p><a href="https://reactnative.dev/blog/2022/09/05/version-070" target="_blank" rel="noopener noreferrer">React Native 0.70 版本已于 2022-9-5 正式发布</a>，在 0.70 版本中 Hermes 已成为默认的 JS 引擎，</p><p>v3.6 版本将与 RN 默认配置保持一致，如不需要可自行关闭。Hermes 也带来了 RN 性能的较大提升，特别是启动场景，详细内容参考<a href="https://reactnative.dev/blog/2022/07/08/hermes-as-the-default" target="_blank" rel="noopener noreferrer">官方文章</a>。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2023-02-01-Taro-3.6/hermes.png" alt="Hermes" class="img_ZOZK"></p><p>Taro 将与 RN 社区保持同步，将默认的 RN 版本设置为 0.70。相关依赖也已同步至最新版本，仍然可使用 <code>yarn upgradePeerdeps</code> 进行更新。<code>@react-native-community/clipboard</code> 及 <code>@react-native-community/cameraroll</code> 已被弃用，旧版本升级后需要删除。</p><blockquote><p>注意：升级后将不再支持 iOS 12，详细内容请参考 <a href="https://github.com/NervJS/taro/discussions/12468" target="_blank" rel="noopener noreferrer">discussions</a>。同时 <a href="https://github.com/wuba/taro-playground" target="_blank" rel="noopener noreferrer">Taro Playground</a> 作为 RN 端的调试工具及跨端 Demo 同步更新至 v3.6。</p></blockquote><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-web-端能力">3. Web 端能力<a href="#3-web-端能力" class="hash-link" aria-label="3. Web 端能力的直接链接" title="3. Web 端能力的直接链接">​</a></h3><p>通过在社区中收集的相关问题反馈，Taro Web 各类特性也一直在不断推进，让开发者在多端研发的体验能够尽可能达成一致。面对各类自定义 Web 端能力的需求，虽然有很多方案可以提供组件、API 等能力的补充，但类似小程序端平台插件这样的能力在 Web 端中并没有得到支持。</p><p>在 3.6 版本中，我们将 Web 端各类定制化的能力从 runner 中转移到 <code>@tarojs/plugin-platform-h5</code> 插件中提供，譬如通过配置 <code>useHtmlComponents</code> 模式替换使用的组件库；注册 Web Components 组件库，配置各前端 UI 框架组件适配器；移除不必要的 API 等等特性。</p><div class="language-tsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-tsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name constant" style="color:#36acaa">H5</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">TaroPlatformWeb</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">registerPlatform</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'h5'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    useConfigName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'h5'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">fn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> config </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> program </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name constant" style="color:#36acaa">H5</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> config</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> program</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">start</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>和小程序端一样，借助于插件或 <code>TaroPlatformWeb</code> 基类，开发者可以很容易横向或纵向拓展 Web 端的各项能力，详情可<a href="https://nervjs.github.io/taro-docs/docs/next/platform-plugin/how#web-%E7%AB%AF%E5%B9%B3%E5%8F%B0%E6%8F%92%E4%BB%B6" target="_blank" rel="noopener noreferrer">参考文档</a>。</p><p>Web 端也一直在补充各类开发者常用的组件与 API 抹平与小程序端的差异。在 v3.6 版本中新增生命周期、WXML 相关的 API 支持若干，例如：<code>createIntersectionObserver</code> 、<code>createMediaQueryObserver</code> 等，同时新增 <code>movable-area</code>、<code>movable-view</code> 等组件支持。</p><p>在社区开发者交流时，我们也发现了部分研发场景下需要监听各 API、组件不支持事件，相比于支持 canIUse 方法在跨端转换场景中能够更有效定位问题，所以通过支持 <code>__taroNotSupport</code> 事件满足相关需求，可以参考以下示例使用。</p><div class="language-tsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-tsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">interface</span><span class="token plain"> </span><span class="token class-name">IOption</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 不支持的组件或 API 名称</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'method'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'component'</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 'method': API; 'component': 组件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  category</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'permanently'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'temporarily'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'weixin_corp'</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 'weixin_corp': 仅在微信公众号 JS-SDK 环境下支持</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  args</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">any</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// API 传入参数</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  instance</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">unknown</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 组件实例</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token maybe-class-name">Taro</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">eventCenter</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'__taroNotSupport'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">option</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">IOption</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">warn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'调用不支持的 API 或组件'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> option</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="三跨框架组件库">三、跨框架组件库<a href="#三跨框架组件库" class="hash-link" aria-label="三、跨框架组件库的直接链接" title="三、跨框架组件库的直接链接">​</a></h2><p>借助于 stencil，Taro 3 得以通过 Web Components 实现一套跨框架组件库，通过适配器将 Taro 的组件库提供给各个前端 UI 框架使用，开发者也可以基于这些封装上层组件，提供更多有趣的能力。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-web-端适配器">1. Web 端适配器<a href="#1-web-端适配器" class="hash-link" aria-label="1. Web 端适配器的直接链接" title="1. Web 端适配器的直接链接">​</a></h3><p>出于降低开发者维护门槛，与 stencil 组件库打包流程更好兼容等多方面考虑，在 3.6 版本中我们在升级 stencil 依赖版本的同时，通过官方提供的 &nbsp;<a href="https://github.com/ionic-team/stencil-ds-plugins/blob/master/README.md" target="_blank" rel="noopener noreferrer">ds-output-target</a> 工具替换了原有的自定义适配器。</p><p>该版本适配器更好的抹平各个框架组件使用差异，补齐过往版本迭代过程中部分特性兼容性的缺失问题，为开发者提供更好的体验。在 3.6 中依旧保留过往版本各框架适配器，可以参考以下示例通过配置别名替换组件库适配器（不建议使用，后续不会维护旧版适配器，可能无法得到新的组件或特性支持）。</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// config/index.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">h5</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">webpackChain</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">chain</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      chain</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">resolve</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">alias</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// 当前版本适配层地址 @tarojs/components/dist/[framework]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">'@tarojs/components/dist/react'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// 旧版本适配层地址 @tarojs/components/dist/[framework]/component-lib</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">'@tarojs/components/dist/react/component-lib'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-虚拟列表">2. 虚拟列表<a href="#2-虚拟列表" class="hash-link" aria-label="2. 虚拟列表的直接链接" title="2. 虚拟列表的直接链接">​</a></h3><p>作为从 Taro 3 开始支持的上层组件，虚拟列表应当是很多开发者都熟悉的特性，在过往版本中也有过数次升级，支持包括 unlimitedSize、relative 定位模式等特性，在 v3.6 版本中我们再次对虚拟列表做出了调整，将其从 <code>@tarojs/components</code> 包中抽离到 @tarojs/components-advanced 中维护（4.x 版本之前开发者依旧可以通过 @tarojs/components/virtual-list 引用虚拟列表组件），也欢迎大家一同参与上层组件库的维护与共建，沉淀更多跨端可用的能力。</p><p><img loading="lazy" src="https://img20.360buyimg.com/ling/jfs/t1/125645/6/13305/50138/5f6aaaa4E2f20eba7/d70a2d2da2d68de1.jpg" alt="https://img20.360buyimg.com/ling/jfs/t1/125645/6/13305/50138/5f6aaaa4E2f20eba7/d70a2d2da2d68de1.jpg" class="img_ZOZK"></p><p>在新版本中，虚拟列表支持在选择 preact、vue3 框架构建的项目中使用，同时在使用各个前端 UI 框架的项目中都支持使用选择 <code>absolute</code><strong>*<!-- -->*<!-- -->*<!-- -->*</strong>、<strong>*<!-- -->*<!-- -->*<!-- -->*</strong><code>relative</code> <strong>*<!-- -->*</strong>*<!-- -->*<!-- -->*<!-- -->*<strong>*<!-- -->*</strong>不同定位方式，unlimitedSize 模式与传入 itemSize 函数等特性也得到支持。</p><p>以 Vue 为例，我们需要在入口文件声明使用：</p><div class="language-tsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-tsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// app.js 入口文件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Vue</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'vue'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">registerVirtualList</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/components-advanced/dist/components/virtual-list'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Note: 使用以下路径导出插件可以在 vue 中获得更好的类型支持</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// import registerVirtualList from '@tarojs/components-advanced/dist/components/virtual-list/vue'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token maybe-class-name">Vue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">use</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">registerVirtualList</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>一个最简单的长列表组件会像这样，<code>virtual-list</code>&nbsp; 的 5 个属性都是必填项：</p><div class="language-tsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-tsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">!</span><span class="token plain"> –– row</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">vue</span><span class="token plain"> 单项组件 ––</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">template</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">view</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">:id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">id</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">:class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">index % 2 ? </span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag attr-value" style="color:#e3116c">ListItemOdd</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag attr-value" style="color:#e3116c"> : </span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag attr-value" style="color:#e3116c">ListItemEven</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"> Row </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> index </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"> : </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> data</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">index</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"> </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">template</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  export default {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    props: ['id', 'index', 'data'],</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">!</span><span class="token plain"> –– page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">vue</span><span class="token plain"> 页面组件 ––</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">template</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">virtual-list</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">wclass</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">List</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">:height</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">500</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">:item-data</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">list</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">:item-count</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">list.length</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">:item-size</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">100</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">:item</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">Row</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">width</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">100%</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">  </span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    &lt;! –– Vue 中支持列表首尾使用的插槽，对应 React 中的 renderTop、renderBottom 参数 ––&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">template</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name namespace" style="color:#00a4db;opacity:0.7">v-slot:</span><span class="token tag attr-name" style="color:#00a4db">top</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">top</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">template</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">template</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name namespace" style="color:#00a4db;opacity:0.7">v-slot:</span><span class="token tag attr-name" style="color:#00a4db">bottom</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">bottom</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">template</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">virtual-list</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">template</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  import { markRaw } from 'vue'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  import Row from './row.vue'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  function buildData(offset = 0) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    return Array(100)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      .fill(0)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      .map((_, i) =&gt; i + offset)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  export default {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    data() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      return {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        Row: markRaw(Row),</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        list: buildData(0),</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p>需要注意的是，为抹平多框架参数差异便于维护，旧版本中部分命名会统一修改，比如在 React 版本中通过 children 传入的子节点组件改为 item；Vue 中的 wclass、wstyle 这类写法也不再支持。</p></blockquote><p>在新版本中，根据需求和研发场景合理设置 <code>itemSize</code>、<code>overscanCount</code>、<code>placeholderCount</code> 等参数优化长列表，可以获得比旧版本更加顺滑的体验，更多详情可以参考<a href="https://nervjs.github.io/taro-docs/docs/next/virtual-list#vue" target="_blank" rel="noopener noreferrer">官方文档</a>。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="四研发生态">四、研发生态<a href="#四研发生态" class="hash-link" aria-label="四、研发生态的直接链接" title="四、研发生态的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-小程序持续集成-ci">1. 小程序持续集成 CI<a href="#1-小程序持续集成-ci" class="hash-link" aria-label="1. 小程序持续集成 CI的直接链接" title="1. 小程序持续集成 CI的直接链接">​</a></h3><p>去年 Taro 提供小程序持续集成插件 <code>@tarojs/plugin-mini-ci</code> ，帮助开发者提供更好的研发体验，经过一年的项目沉淀和反馈，在本次版本重构了去年的所有代码，并提供了更优秀体验和灵活的配置。</p><ul><li>本次新增特性支持独立的 <code>open</code>、<code>preview</code>、<code>upload</code> 命令，操作自定义目录适用于将 taro 作为项目一部分（混合开发）的开发场景；</li><li>同步更新各个小程序端的 CI API 变更（阿里系、抖音小程序变化最大）</li><li>新增钉钉小程序 CI 集成；</li><li>新增京东小程序 CI 集成;</li><li>统一所有平台 CI 构建后的输出数据，并将数据传递给新增的<code>onPreviewComplete</code>、<code>onUploadComplete</code>两个钩子用户可以编写新的插件，基于这个钩子实现 飞书、钉钉 的 CI 推送功能等等。</li></ul><p>以下为当前各平台支持的功能情况表：</p><table><thead><tr><th>平台/功能</th><th>自动打开 IDE</th><th>输出预览二维码</th><th>输出体验二维码</th></tr></thead><tbody><tr><td>weapp</td><td>✅</td><td>✅</td><td>✅</td></tr><tr><td>qywx</td><td>✅</td><td>✅</td><td>✅</td></tr><tr><td>alipay</td><td>✅</td><td>✅</td><td>✅</td></tr><tr><td>dd</td><td>✅</td><td>✅</td><td>❌</td></tr><tr><td>swan</td><td>✅</td><td>✅</td><td>✅</td></tr><tr><td>jd</td><td>✅</td><td>✅</td><td>✅</td></tr></tbody></table><p>ps： 该插件在 taro3.6.0 版本以下亦可支持单独使用，插件版本号无需保持与其他包同步。</p><p>更多详情可以参考<a href="https://nervjs.github.io/taro-docs/docs/plugin-mini-ci" target="_blank" rel="noopener noreferrer">官方文档</a>，同时要特别感谢 <a href="https://github.com/bigmeow" target="_blank" rel="noopener noreferrer">@bigMeow</a> 为 CI 自动化脚本做出的贡献～</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-postcss-版本升级">2. PostCSS 版本升级<a href="#2-postcss-版本升级" class="hash-link" aria-label="2. PostCSS 版本升级的直接链接" title="2. PostCSS 版本升级的直接链接">​</a></h3><p>在 Taro 项目持续迭代的过程中，部分依赖稳定没有实时跟进各个社区内的特性与优化，并升级相关依赖，PostCSS 就是其中之一。如果开发者想要通过新版本的特性来优化构建流程与最终产物，相对会很困难且可能会存在一定问题阻塞。</p><p>为此在 3.6 canary 通过梳理项目内相关插件与依赖，对 PostcCSS 版本进行梳理并升级，升级后版本为 v8.4.18。本次升级主要包含以下内容：</p><ol><li>对 Taro 内部的 PostCSS 插件使用 PostCSS 8 版本 API 进行改写，降低代码量同时减少插件对 CSS 扫描次数进而提高构建速度；</li><li>使用 peerDependencies 管理 postCSS 依赖，降低用户的 node_modules 体积和复杂度；</li><li>对 Taro 全量模板的 PostCSS 版本同步进行更新，方便开发者对新特性的使用。</li></ol><p>特别感谢 <a href="https://github.com/xueshuai" target="_blank" rel="noopener noreferrer">@xueshuai</a> 为相关工作做出的贡献，希望开发者可以因此获得更好的研发体验。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-类型与文档自动同步">3. 类型与文档自动同步<a href="#3-类型与文档自动同步" class="hash-link" aria-label="3. 类型与文档自动同步的直接链接" title="3. 类型与文档自动同步的直接链接">​</a></h3><p>快速同步各平台支持的类型，一直以来都是十分头疼的问题之一，想要实时跟进各个平台以提升用户体验十分困难，更多时候我们都通过开发者提交的 PR 或 issue 对这些平台的类型更新维护。如果能够根据各个小程序平台官方提供的文档自动化生成类型，这对于框架维护和开发者来说会是一个很棒的体验。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2023-02-01-Taro-3.6/canIUse.png" alt="CanIUse" class="img_ZOZK"></p><p>通过在 Taro 社区中的积极<a href="https://github.com/NervJS/taro/discussions/11740" target="_blank" rel="noopener noreferrer">探讨</a>和论证，我们引入了自动同步各小程序平台组件类型的脚本机制，并通过与 GitHub CI 让机器人为 Taro 仓库提交类型更新 PR。组件类型的自动化同时让 Taro 的文档在类型更新时，同步这些平台组件的变更，为开发者提供更好的文档索引能力。同时我们也在文档提供了 <a href="https://nervjs.github.io/taro-docs/canIUse/" target="_blank" rel="noopener noreferrer">canIUse</a> 页面，供开发者快速检索组件、特性在各个平台支持的程度。</p><p>希望后续也能够通过和各平台合作的方式优化该能力，为开发者提供更好的体验。在这里特别感谢 <a href="https://github.com/robinv8" target="_blank" rel="noopener noreferrer">@rebinv8</a> 为组件类型自动化脚本做出的贡献～</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="4-taro-playground-同步升级">4. Taro Playground 同步升级<a href="#4-taro-playground-同步升级" class="hash-link" aria-label="4. Taro Playground 同步升级的直接链接" title="4. Taro Playground 同步升级的直接链接">​</a></h3><p>作为 RN 端的调试工具及跨端 Demo，<a href="https://github.com/wuba/taro-playground" target="_blank" rel="noopener noreferrer">Taro Playground</a> 同步更新 v3.6 更新。此次更新无法保证向下兼容，使用旧版本 Taro 的开发者，如需调试 Android，可在 <a href="https://github.com/wuba/taro-playground/releases" target="_blank" rel="noopener noreferrer">releases</a> 中下载旧包进行调试。在 App Store 中，我们只上架最新版本，需要旧版本的开发者请不要开启应用自动更新。如不慎升级，需自行打包编译，或联系我们加入测试组。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="五升级指南">五、升级指南<a href="#五升级指南" class="hash-link" aria-label="五、升级指南的直接链接" title="五、升级指南的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-创建-v36-版本项目">1. 创建 <strong>v3.6 版本项目：</strong><a href="#1-创建-v36-版本项目" class="hash-link" aria-label="1-创建-v36-版本项目的直接链接" title="1-创建-v36-版本项目的直接链接">​</a></h3><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 安装 v3.6.0 的 CLI 工具</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i -g @tarojs/cli@latest</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 创建项目</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">taro init taro_project</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 也可以跳过安装 CLI 工具使用 npx 创建项目</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">npx @tarojs/cli@latest init taro_project</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-已有项目升级到最新版本"><strong>2. 已有项目升级到最新版本：</strong><a href="#2-已有项目升级到最新版本" class="hash-link" aria-label="2-已有项目升级到最新版本的直接链接" title="2-已有项目升级到最新版本的直接链接">​</a></h3><ol><li>将 package.json 文件中 Taro 相关依赖修改为 <code>3.6.0</code> 版本；</li><li>Taro v3.6 将 web 端插件化，需要新增依赖 <code>@tarojs/plugin-platform-h5</code>，同时 <code>@tarojs/router</code>、<code>@tarojs/taro-h5</code> 不再需要额外声明依赖，可自行移除；</li><li>重新安装依赖，如果安装失败或打开项目失败，可以删除 <code>node_modules</code>、<code>yarn.lock</code>、<code>package-lock.json</code> 后重新安装依赖重新尝试。</li></ol><h2 class="anchor anchorWithStickyNavbar_mCi1" id="最后">最后<a href="#最后" class="hash-link" aria-label="最后的直接链接" title="最后的直接链接">​</a></h2><p>自 Taro 引入<a href="https://nervjs.github.io/taro-docs/docs/team/#%E6%8A%80%E6%9C%AF%E5%A7%94%E5%91%98%E4%BC%9A" target="_blank" rel="noopener noreferrer">技术委员会</a>等机制后，与社区互动愈加频繁，Taro 也得益于诸多<a href="https://nervjs.github.io/taro-docs/docs/team/role-committer" target="_blank" rel="noopener noreferrer">贡献者</a>的帮助有能力去实现更多有意思的特性，给开发者带来更好的用户体验，这也是 Taro v3.6 在代号「Reach」中的期待。感谢各位参与到 Taro 开源生态共建的同学们，所有的努力都让 Taro 的生态更加美好，为建立更加完善、更加可持续的 Taro 开源生态，贡献力量！</p>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[Taro v3.6 代号为「Reach」，已发布 canary 版本]]></title>
            <link>https://docs.taro.zone/blog/2022/11/18/Taro-3.6-canary</link>
            <guid>https://docs.taro.zone/blog/2022/11/18/Taro-3.6-canary</guid>
            <pubDate>Fri, 18 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Taro v3.6 代号为「Reach」，已发布 canary 版本]]></description>
            <content:encoded><![CDATA[<p>近期我们确定了 v3.6 版本的代号「Reach」，并发布了 v3.6-canary 版本，多个新特性在该版本内开放给社区各位开发者体验，欢迎大家试用并在社区中反馈相关问题。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2022-11-18-Taro-3.6-canary/taro-3.6.jpg" alt="taro-3.6.jpg" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="一支持路由库">一、支持路由库<a href="#一支持路由库" class="hash-link" aria-label="一、支持路由库的直接链接" title="一、支持路由库的直接链接">​</a></h2><p>Taro 3 适配前端 UI 框架的方式更接近于前端的本质，通过在小程序端模拟实现框架所需的 BOM/DOM API 来达成，对于适配各个路由库也是同样的思路。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-运行时引入-history--location-对象">1. 运行时引入 History &amp; Location 对象<a href="#1-运行时引入-history--location-对象" class="hash-link" aria-label="1. 运行时引入 History &amp; Location 对象的直接链接" title="1. 运行时引入 History &amp; Location 对象的直接链接">​</a></h3><p>在 Web BOM 中，History &amp; Location 对象是重要组成部分，它们是实现前端路由的关键。Taro 为了支持前端路由库的使用，在运行时中引入了 <code>histroy</code> <code>location</code> 对象的实现，且尽可能与 Web 端规范对齐，你可以在 <code>window</code> 对象上访问到 <code>history</code> 和 <code>location</code> 对象。同时，也支持监听 <code>hashchange</code> 和 <code>popstate</code> 事件，这为使用路由库提供技术基础。</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 统称: 页面路由状态</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token dom variable" style="color:#36acaa">window</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">history</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token dom variable" style="color:#36acaa">window</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">location</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 支持监听事件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token dom variable" style="color:#36acaa">window</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addEventListener</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'hashchange'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token dom variable" style="color:#36acaa">window</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addEventListener</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'popstate'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>小程序天然支持多页面(<code>pages</code> 数组配置)，因此 Taro 并非以整个应用为一个路由系统，而是顺应小程序规范以页面维度进行路由管理。每当切换页面时，会将当前页面的页面路由状态缓存。跳转至新页面后会重新创建页面路由状态，并挂载在 <code>window</code> 对象上。当返回上一级页面时，会将上一级页面的页面路由状态重新挂载到 <code>window</code> 对象中。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-使用路由库">2. 使用路由库<a href="#2-使用路由库" class="hash-link" aria-label="2. 使用路由库的直接链接" title="2. 使用路由库的直接链接">​</a></h3><p>至此，可以在小程序中使用成熟的前端路由库了，包括 <code>react-router</code> 和 <code>vue-router</code>。在路由库中，诸如 <code>&lt;Link&gt;</code> 组件内部会动态生成 <code>&lt;a&gt;</code> 标签，因此需要引入 <code>[@tarojs/plugin-html](https://docs.taro.zone/docs/use-h5)</code> 插件以支持在 Taro 中使用 <code>html</code> 标签开发组件。</p><div class="language-json codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-json codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"plugins"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"@tarojs/plugin-html"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在 Taro 编译过程中，当 DOM 序列化数据的 nn 字段为 HTML 标签时，标签会映射为对应的小程序组件名称。由于无法提前预知动态标签，因此需要应用显式告知可能会使用到的动态标签。例如在应用中塞入一个无样式的标签名即可：</p><div class="language-html codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-html codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">a</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">a</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>更多细节可以查看 <a href="https://docs.taro.zone/docs/router-extend" target="_blank" rel="noopener noreferrer">官方文档</a>，也可以查看官方提供的 DEMO 获知更多用法。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="二跨框架组件库">二、跨框架组件库<a href="#二跨框架组件库" class="hash-link" aria-label="二、跨框架组件库的直接链接" title="二、跨框架组件库的直接链接">​</a></h2><p>借助于 stencil，Taro 3 得以通过 Web Components 实现一套跨框架组件库，不过由于其 2.14+ 版本会使用一些 webpack4 不兼容的语法特性，在 3.6-canary 之前的组件库将 stencil 版本限制在 2.13+ 版本内，在 3.6-canary 版本中也针对该问题进行了修复，同时借助 stencil 的新特性优化诸多组件库在 props 与事件的遗留问题。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-web-端框架适配器">1. Web 端框架适配器<a href="#1-web-端框架适配器" class="hash-link" aria-label="1. Web 端框架适配器的直接链接" title="1. Web 端框架适配器的直接链接">​</a></h3><p>Web Components 虽然可以在各个前端 UI 框架中使用，但是依旧会有很多兼容性问题，这些可以通过 <a href="https://custom-elements-everywhere.com/" target="_blank" rel="noopener noreferrer">Custom Elements Everywhere</a>&nbsp; 的一系列测试用例有所了解。所以在 Taro 3 发布之初，Web 端跨框架组件库还通过提供不同前端 UI 框架的自定义适配器，让不同框架使用 taro 提供的跨框架组件。</p><p>尽管这套适配层方案能够很好的兼容 react 框架，但对于组件库的维护者来说会额外的心智负担，比如新增组件时需要同步更新适配器；这个问题在 vue 中则更为明显，不仅 props 需要额外的配置，表单类组件也需要对事件进行标注等。这些问题不仅提升了新同学介入组件库维护的门槛，也使得维护者不能专注于组件库的能力优化。</p><p>解决问题的方法当然也很简单，那就是通过脚本在组件库编译时针对不同 UI 框架自动化导出对应组件，社区内也有足够多方案可供选择。在这里我们使用官方提供的 &nbsp;<a href="https://github.com/ionic-team/stencil-ds-plugins/blob/master/README.md" target="_blank" rel="noopener noreferrer">ds-output-target</a> 工具并在一定程度上调整输出结果，并同步过往适配器中 Taro 做出的体验优化。</p><p>在 3.6 版本中，新的适配层不仅对齐各个框架组件使用体验，同时也补齐过往适配器在迭代过程中部分特性兼容性的缺失问题。如果依旧希望使用 3.6 以前版本适配器，也可以参考以下示例，通过配置别名替换组件库。</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// config/index.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">h5</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">webpackChain</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">chain</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      chain</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">resolve</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">alias</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// 当前版本适配层地址 @tarojs/components/dist/[framework]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">'@tarojs/components/dist/react'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// 旧版本适配层地址 @tarojs/components/dist/[framework]/component-lib</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">'@tarojs/components/dist/react/component-lib'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-类型自动生成">2. 类型自动生成<a href="#2-类型自动生成" class="hash-link" aria-label="2. 类型自动生成的直接链接" title="2. 类型自动生成的直接链接">​</a></h3><p>如何快速同步各小程序平台当前支持的类型，对于 Taro 来说一直是个很让人头疼的问题，实时跟进各个平台的每一次更新很难做到，更多时候我们都通过开发者提交的 PR 或 issue 对这些平台的类型更新。如果能够根据各个小程序平台官方提供的文档自动化生成组件类型，这对于开发者来说会是一个很棒的体验。</p><p>通过在 Taro 社区中的积极<a href="https://github.com/NervJS/taro/discussions/11740" target="_blank" rel="noopener noreferrer">探讨</a>和论证，我们引入了自动同步各小程序平台组件类型的脚本，并通过与 GitHub CI 让机器人为 Taro 仓库提交类型更新 PR。组件类型的自动化同时让 Taro 的文档在类型更新时，同步这些平台组件的变更，为开发者提供更好的文档索引能力。</p><p>在这里特别感谢 <a href="https://github.com/robinv8" target="_blank" rel="noopener noreferrer">@rebinv8</a> 为组件类型自动化脚本做出的贡献～</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="三支持适配鸿蒙">三、<strong>支持适配鸿蒙</strong><a href="#三支持适配鸿蒙" class="hash-link" aria-label="三支持适配鸿蒙的直接链接" title="三支持适配鸿蒙的直接链接">​</a></h2><p>在 Taro 与 OpenHarmony 建立官方合作关系，并受邀成立 <a href="https://gitee.com/openharmony-sig/taro" target="_blank" rel="noopener noreferrer">CrossPlatformUI Sig</a>（跨平台前端框架兴趣小组）后，让 Taro 支持适配鸿蒙就一直在议程上，鸿蒙的方舟开发框架提供类 Web 范式编程，支持使用 JS 开发 UI 层，其语法与小程序相接近，可以沿用 Taro 现有的架构适配鸿蒙。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2022-11-18-Taro-3.6-canary/taro-harmony.png" alt="taro-harmony" class="img_ZOZK"></p><p>持续关注 Taro 的开发者可能还记得，在 v3.5-canary 版本时，我们曾推出支持 Taro 应用适配到鸿蒙平台的插件，但最终没有合入 v3.5 版本主干并顺势推出该能力。在 <code>@tarojs/plugin-platform-harmony</code> 端平台插件经过一段时间的打磨，相关能力与特性也在社区推进下持续优化，框架编译的项目在鸿蒙开发板上得到进一步验证，同时在 Taro v3.5 新增的 <code>@tarojs/webpack5-runner</code> 编译内核在 canary 版本中也能够为鸿蒙项目编译提供支持，终于我们在 v3.6-canary 中再次为社区开发者提供了适配鸿蒙的端平台插件。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="使用方法">使用方法<a href="#使用方法" class="hash-link" aria-label="使用方法的直接链接" title="使用方法的直接链接">​</a></h3><ol><li><p>在项目中安装鸿蒙端平台插件</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> -D @tarojs/plugin-platform-harmony@canary</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p>需要注意鸿蒙插件不在 Taro 项目内维护，所以并不会每次发布同版本号版本，直接使用 minor 与 Taro 版本号相同的版本即可。</p></blockquote></li><li><p>在 <code>config/index.js</code> 中增加编译配置</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// config/index.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 配置使用插件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">plugins</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'@tarojs/plugin-platform-harmony'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">mini</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 如果使用开发者工具的预览器（previewer）进行预览的话，需要使用 development 版本的 react-reconciler。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 因为 previewer 对长串的压缩文本解析有问题。（真机/远程真机没有此问题）</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">debugReact</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 如果需要真机断点调试，需要关闭 sourcemap 的生成</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">enableSourceMap</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// harmony 相关配置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">harmony</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 【必填】鸿蒙应用的绝对路径</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">projectPath</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> path</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">resolve</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">cwd</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'../MyApplication'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 【可选】HAP 的名称，默认为 'entry'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">hapName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'entry'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 【可选】JS FA 的名称，默认为 'default'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">jsFAName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'default'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>准备鸿蒙运行环境</p><blockquote><p>开发鸿蒙软件需要用到 HUAWEI DevEco Studio，它提供了模板创建、开发、编译、调试、发布等服务。</p></blockquote><p>鸿蒙运行环境主要包括以下内容</p><p>（1） 注册开发者账号</p><p>（2）下载 DevEco Studio 安装包</p><p>（3）启动 DevEco Studio，根据工具引导下载 HarmonyOS SDK</p><p>（4）新建 MyApplication JS 项目</p><p>（5）使用预览器或真机查看应用效果</p><p>参考资料：《<a href="https://juejin.cn/post/6972109475347955749" target="_blank" rel="noopener noreferrer">初窥鸿蒙</a>》、《<a href="https://developer.harmonyos.com/cn/develop/deveco-studio" target="_blank" rel="noopener noreferrer">华为开发者工具</a>》、《<a href="https://developer.harmonyos.com/cn/documentation" target="_blank" rel="noopener noreferrer">鸿蒙开发文档</a>》</p></li><li><p>项目运行</p><p>Taro 编译鸿蒙项目命令</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">$ taro build —-type harmony —-watch</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2022-11-18-Taro-3.6-canary/taro-harmony-example.png" alt="taro-harmony-example" class="img_ZOZK"></p><blockquote><p>testHarmony 为您通过 DevEco Studio 创建的 JS 项目。</p></blockquote></li></ol><p>特别感谢以下同学为鸿蒙适配作出的贡献：</p><p><a href="https://github.com/AdvancedCat" target="_blank" rel="noopener noreferrer">@AdvancedCat</a>、<a href="https://github.com/jiaozitang" target="_blank" rel="noopener noreferrer">@jiaozitang</a>、<a href="https://github.com/huozhongyi123" target="_blank" rel="noopener noreferrer">@huozhongyi123</a>、<a href="https://github.com/troy-sxj" target="_blank" rel="noopener noreferrer">@troy-sxj</a>、<a href="https://github.com/JSZabc" target="_blank" rel="noopener noreferrer">@JSZabc</a>、<a href="https://github.com/crazyonebyone" target="_blank" rel="noopener noreferrer">@crazyonebyone</a>、<a href="https://github.com/evernoteHW" target="_blank" rel="noopener noreferrer">@evernoteHW</a>、<a href="https://github.com/soulhat" target="_blank" rel="noopener noreferrer">@soulhat</a>、<a href="https://github.com/xueshuai" target="_blank" rel="noopener noreferrer">@xueshuai</a>、<a href="https://github.com/LuMeiling" target="_blank" rel="noopener noreferrer">@LuMeiling</a></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="四rn">四、RN<a href="#四rn" class="hash-link" aria-label="四、RN的直接链接" title="四、RN的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-react-native-070-版本支持">1. React Native 0.70 版本支持<a href="#1-react-native-070-版本支持" class="hash-link" aria-label="1. React Native 0.70 版本支持的直接链接" title="1. React Native 0.70 版本支持的直接链接">​</a></h3><p><a href="https://reactnative.dev/blog/2022/09/05/version-070" target="_blank" rel="noopener noreferrer">React Native 0.70 版本已于 2022-9-5 正式发布</a>。在 0.70 版本中 Hermes 已成为默认的 JS 引擎，我们将与 RN 默认配置保持一致，如不需要可自行关闭。Hermes 也带来了 RN 性能的较大提升，特别是启动场景，详细内容参考<a href="https://reactnative.dev/blog/2022/07/08/hermes-as-the-default" target="_blank" rel="noopener noreferrer">官方文章</a>。
Taro 将与 RN 社区保持同步，将默认的 RN 版本设置为 0.70。相关依赖也已同步至最新版本，仍然可使用 <code>yarn upgradePeerdeps</code> 进行更新。<code>@react-native-community/clipboard</code> 及 <code>@react-native-community/cameraroll</code> 已被弃用，更新后可删除。</p><p>注意：升级后将不再支持 iOS 12，详细内容请参考 <a href="https://github.com/NervJS/taro/discussions/12468" target="_blank" rel="noopener noreferrer">discussions</a>。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-tarojsrn-runner-代码重构">2. @tarojs/rn-runner 代码重构<a href="#2-tarojsrn-runner-代码重构" class="hash-link" aria-label="2. @tarojs/rn-runner 代码重构的直接链接" title="2. @tarojs/rn-runner 代码重构的直接链接">​</a></h3><p>之前的版本中，为了让 Taro 代码能够运行在 RN 平台上，我们对 Metro 的编译过程做了较多的定制，并且封装了入口文件以及 metro 的相关配置。这样做导致了多个问题：</p><ol><li>打包只能通过 <code>yarn build:rn</code> 等方式进行，而无法使用 <code>react-native bundle</code> 进行，存在学习成本。</li><li>RN 的编译流程中，需要过滤掉 RN 原有的 bundle 打包过程，替换成 Taro 的打包。这一步在开发者环境搭建过程中，常常出现问题。</li><li>无法对入口文件进行处理，比如加入一些全局逻辑。</li></ol><p>为了让整体开发体验跟 RN 更加一致，减少开发者的理解成本。我们对 @tarojs/rn-runner 的代码进行了重构。将 Taro RN 需要的所有编译逻辑，都封装到了 metro 配置中。新版本在项目根目录下会创建入口文件 index.js 和配置文件 metro.config.js。如项目本身有这两个文件，则不会生成，需要参考<a href="https://github.com/NervJS/taro-project-templates/tree/v3.6/react-native" target="_blank" rel="noopener noreferrer">模板</a>进行添加或合并。另外 Taro RN 的相关配置，集中在 resolver 和 transformer 中，如需覆盖，建议先看一下相关源码。</p><p>这样做之后，Taro RN 的打包过程就与 RN 本身无太多区别，与 RN 项目集成会更加灵活。<code>react-native</code> 命令行的使用，请参考<a href="https://github.com/react-native-community/cli#documentation" target="_blank" rel="noopener noreferrer">官方文档</a>， <code>yarn build:rn</code> 等命令仍然保留。使用 <code>react-native</code> 命令行无法自动打印二维码，请输入 <code>q</code> 进行打印。ip 更换等场景，也可以输入 <code>q</code> 重新打印二维码。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-调试工具-taro-playground-升级至-taro-36-版本及-react-native-070">3. 调试工具 Taro Playground 升级至 Taro 3.6 版本及 React Native 0.70<a href="#3-调试工具-taro-playground-升级至-taro-36-版本及-react-native-070" class="hash-link" aria-label="3. 调试工具 Taro Playground 升级至 Taro 3.6 版本及 React Native 0.70的直接链接" title="3. 调试工具 Taro Playground 升级至 Taro 3.6 版本及 React Native 0.70的直接链接">​</a></h3><p><a href="https://github.com/wuba/taro-playground" target="_blank" rel="noopener noreferrer">Taro Playground</a> 作为 Taro RN 端的调试工具及跨端 Demo，进行了同步更新。此次更新无法保证向下兼容，使用旧版本 Taro 的开发者，如需调试 Android，可在 <a href="https://github.com/wuba/taro-playground/releases" target="_blank" rel="noopener noreferrer">releases</a> 中下载旧包进行调试。在 App Store 中，我们只上架最新版本，需要旧版本的开发者请不要开启应用自动更新。如不慎升级，需自行打包编译，或联系我们加入测试组。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="五研发生态">五、研发生态<a href="#五研发生态" class="hash-link" aria-label="五、研发生态的直接链接" title="五、研发生态的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-小程序持续集成-ci">1. 小程序持续集成 CI<a href="#1-小程序持续集成-ci" class="hash-link" aria-label="1. 小程序持续集成 CI的直接链接" title="1. 小程序持续集成 CI的直接链接">​</a></h3><p>去年 Taro 提供小程序持续集成插件 <code>@tarojs/plugin-mini-ci</code> ，帮助开发者提供更好的研发体验，经过一年的项目沉淀和反馈，在本次版本更新中提供了更优秀的体验。</p><p>本次新增特性支持独立的 <code>open</code>、<code>preview</code>、<code>upload</code> 命令，操作自定义目录适用于将 taro 作为项目一部分（混合开发）的开发场景；同时再次同步各个小程序端的 CI 版本变更，并支持应用到钉钉小程序上；统一所有平台 CI 构建后的输出数据，并将数据传递给新增的<code>onPreviewComplete</code>、<code>onUploadComplete</code>两个钩子用户可以编写新的插件，基于这个钩子实现 飞书、钉钉 的 CI 推送功能等等。</p><p>更多详情可以参考<a href="/docs/plugin-mini-ci">官方文档</a>，同时要特别感谢 <a href="https://github.com/bigmeow" target="_blank" rel="noopener noreferrer">@bigMeow</a> 为组件类型自动化脚本做出的贡献～</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-postcss-版本升级">2. PostCSS 版本升级<a href="#2-postcss-版本升级" class="hash-link" aria-label="2. PostCSS 版本升级的直接链接" title="2. PostCSS 版本升级的直接链接">​</a></h3><p>在 Taro 项目持续迭代的过程中，部分依赖稳定没有实时跟进各个社区内的特性与优化，并升级相关依赖，PostCSS 就是其中之一。如果开发者想要通过新版本的特性来优化构建流程与最终产物，相对会很困难且可能会存在一定问题阻塞。</p><p>为此在 3.6 canary 通过梳理项目内相关插件与依赖，对 PostcCSS 版本进行梳理并升级，升级后版本为 v8.4.18。本次升级主要包含以下内容：</p><ol><li>对 Taro 内部的 PostCSS 插件使用 PostCSS 8 版本 API 进行改写，降低代码量同时减少插件对 CSS 扫描次数进而提高构建速度；</li><li>使用 peerDependencies 管理 postCSS 依赖，降低用户的 node_modules 体积和复杂度；</li><li>对 Taro 全量模板的 PostCSS 版本同步进行更新，方便开发者对新特性的使用。</li></ol><p>特别感谢 <a href="https://github.com/xueshuai" target="_blank" rel="noopener noreferrer">@xueshuai</a> 为相关工作做出的贡献，希望开发者可以因此获得更好的研发体验。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="六升级指南">六、升级指南<a href="#六升级指南" class="hash-link" aria-label="六、升级指南的直接链接" title="六、升级指南的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-创建-canary-版本项目">1. 创建 <strong>canary 版本项目：</strong><a href="#1-创建-canary-版本项目" class="hash-link" aria-label="1-创建-canary-版本项目的直接链接" title="1-创建-canary-版本项目的直接链接">​</a></h3><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 安装 **v3.6.0-canary 的 CLI 工具**</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i -g @tarojs/cli@**canary</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 创建 canary 版本项目</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">taro init taro_canary_project</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 也可以跳过安装 CLI 工具使用 npx 创建 canary 版本项目</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">npx** @tarojs/cli@**canary init taro_canary_project**</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-已有项目升级到-canary-版本">2. 已有项目升级到 canary 版本：<a href="#2-已有项目升级到-canary-版本" class="hash-link" aria-label="2. 已有项目升级到 canary 版本：的直接链接" title="2. 已有项目升级到 canary 版本：的直接链接">​</a></h3><ol><li>将 package.json 文件中 Taro 相关依赖的版本修改为 <code>3.6.0@canary</code></li><li>重新安装依赖，如果安装失败或打开项目失败，可以删除 node_modules、yarn.lock、package-lock.json 后重新安装依赖重新尝试。</li></ol><h2 class="anchor anchorWithStickyNavbar_mCi1" id="最后">最后<a href="#最后" class="hash-link" aria-label="最后的直接链接" title="最后的直接链接">​</a></h2><p>Taro v3.6 代号「Reach」，致远星代表着不屈和希望，希望该版本能够给开发者带来更好的用户体验，感谢各位参与到 Taro 开源生态共建的同学们，所有的努力都让 Taro 的生态更加美好，为建立更加完善、更加可持续的 Taro 开源生态，贡献力量！</p>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[Taro v3.5 正式发布：开发体验提升]]></title>
            <link>https://docs.taro.zone/blog/2022/07/26/Taro-3.5</link>
            <guid>https://docs.taro.zone/blog/2022/07/26/Taro-3.5</guid>
            <pubDate>Tue, 26 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Taro v3.5 正式版本发布 🚀]]></description>
            <content:encoded><![CDATA[<p>距离 Taro 3.5 的 Beta 版本发布已有两个月的时间，期间我们在不断地对基于 Webpack5 的编译系统、基于 Next.js 的 SSR 等功能进行打磨的同时，新增了对 pnpm 的支持等新功能。此外 Taro 社区也有很多同学参与共建，如 Taro 合作者 <a href="https://github.com/biorz" target="_blank" rel="noopener noreferrer">@biorz</a> 为 ReactNative 侧贡献了重要特性：支持把 Taro 组件编译为 RN 组件。</p><p>日前 Taro v3.5 已正式发布，下文将介绍关于 3.5 的主要特性与重要修复，以及后续的版本规划。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="一编译提速">一、编译提速<a href="#一编译提速" class="hash-link" aria-label="一、编译提速的直接链接" title="一、编译提速的直接链接">​</a></h2><p>Taro v3.5 基于 Webpack5 构建了新的编译系统，利用持久化缓存、依赖预编译、SWC 等方法与工具，大幅降低了编译所需耗时。开发者可以自由选择切换使用 Webpack5 进行编译，也可以继续保持使用 Webpack4，另外在 v3.6 中 Taro 还将支持使用 Vite 进行编译。</p><blockquote><p>依赖预编译可以预先把项目的第三方依赖打包为一个模块联邦 remote 应用，再次编译时 Webpack 无需再对这些依赖进行编译，从而提升编译速度。关于 Webpack5 编译系统的实现细节，请浏览 《Taro v3.5 beta 编译提速》</p></blockquote><p>v3.5 Beta 发布后，我们补全了 H5 端的依赖预编译功能，并且把依赖预编译作为一个通用能力提取了出来。可供 Taro 以外的使用 Webpack5 的 H5 项目使用，通过 <code>@tarojs/webpack5-prebundle</code> 进行编译提速，具体使用方法可参考<a href="/docs/next/prebundle#%E7%AC%AC%E4%B8%89%E6%96%B9%E6%8E%A5%E5%85%A5">相关文档</a>。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="提速效果">提速效果<a href="#提速效果" class="hash-link" aria-label="提速效果的直接链接" title="提速效果的直接链接">​</a></h3><p>以 <a href="https://github.com/jdf2e/nutui/tree/next/src/sites/mobile-taro/vue" target="_blank" rel="noopener noreferrer">NutUI 组件示例库</a> 为例，小程序、H5 端的编译速度测试结果如下：</p><p><strong>小程序：</strong></p><p><img loading="lazy" src="https://storage.360buyimg.com/cjj-pub-images/mini-speed.jpeg" alt="GroupBar-20220725.jpeg" class="img_ZOZK"></p><p><strong>H5：</strong></p><p><img loading="lazy" src="https://storage.360buyimg.com/cjj-pub-images/web-speed.jpeg" alt="GroupBar-20220725 (1).jpeg" class="img_ZOZK"></p><p>使用方法</p><p>简单修改 Taro 的编译配置即可切换使用 Webpack4 或 Webpack5 进行编译：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">/** config/index.js */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 自定义编译工具，可选 'Webpack4' 或 'Webpack5'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">compiler</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'webpack4'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'webpack5'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="二rn">二、RN<a href="#二rn" class="hash-link" aria-label="二、RN的直接链接" title="二、RN的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-react-native-068-版本支持">1. React Native 0.68 版本支持<a href="#1-react-native-068-版本支持" class="hash-link" aria-label="1. React Native 0.68 版本支持的直接链接" title="1. React Native 0.68 版本支持的直接链接">​</a></h3><p><a href="https://reactnative.dev/blog/2022/03/30/version-068" target="_blank" rel="noopener noreferrer">React Native 0.68 版本已于 2022-3-30 正式发布</a>。0.68 是首个可选接入 New Architecture 的版本，新架构有望为 RN 带来性能和体验上的飞跃。Taro 默认选择的 RN 版本，正式切换到了 0.68，开发者通过 <code>taro init</code> 选择 <code>react-native</code> 模板即可。</p><p>另外 0.69 版本的适配，也在进展中。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-rn-相关依赖库由-unimodules-升级至-expo">2. RN <strong>相关依赖库由 unimodules 升级至 expo</strong><a href="#2-rn-相关依赖库由-unimodules-升级至-expo" class="hash-link" aria-label="2-rn-相关依赖库由-unimodules-升级至-expo的直接链接" title="2-rn-相关依赖库由-unimodules-升级至-expo的直接链接">​</a></h3><p>Expo 是 React Native 生态中的重要角色，提供了非常多优秀的模块，在 Taro 中有较为广泛的使用，如 expo-av、expo-camera 等，将来我们还会持续接入新的模块。Expo 的模块系统，由 unimodules 变更为 expo 已有一段时日，其架构变更原因可参考文章：&nbsp;<a href="https://blog.expo.dev/whats-new-in-expo-modules-infrastructure-7a7cdda81ebc" target="_blank" rel="noopener noreferrer">What’s new in Expo modules infrastructure</a>。</p><p>Taro v3.5 及以后将使用新的模块系统，后续壳工程不再包含 unimodules 版本。旧版本升级可参考此 <a href="https://github.com/NervJS/taro-native-shell/pull/72" target="_blank" rel="noopener noreferrer">PR</a>，升级过程较为繁琐，建议重新 init 一个仓库，再将业务改动同步。升级为 expo 后，不再支持 iOS 11，详细内容请参考 <a href="https://github.com/NervJS/taro/discussions/11565" target="_blank" rel="noopener noreferrer">discussions</a>。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-支持把-taro-组件编译为-react-native-组件">3. <strong>支持把 Taro 组件编译为 React Native 组件</strong><a href="#3-支持把-taro-组件编译为-react-native-组件" class="hash-link" aria-label="3-支持把-taro-组件编译为-react-native-组件的直接链接" title="3-支持把-taro-组件编译为-react-native-组件的直接链接">​</a></h3><p>如果您想在现存的 React Native 项目中(非 Taro RN )，复用您的 Taro 组件，那么这个新功能或许适合您。</p><p>您可以使用以下命令编译组件，编译后的组件产物可以直接在 React Native 项目中使用。 详细内容请参考 <a href="https://github.com/NervJS/taro/discussions/11860" target="_blank" rel="noopener noreferrer">discussions</a>。</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">taro build native-components --type rn</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="4-编译打包方案优化">4. 编译打包方案优化<a href="#4-编译打包方案优化" class="hash-link" aria-label="4. 编译打包方案优化的直接链接" title="4. 编译打包方案优化的直接链接">​</a></h3><p>Android 的打包过程，从调用 gradlew 改为使用 &nbsp;<a href="https://docs.fastlane.tools/" target="_blank" rel="noopener noreferrer">fastlane</a>，将打包过程配置化，尽可能地减少对 RN 初始化后原生代码的入侵。相关配置位于 &nbsp;<code>android/fastlane</code>，目前仅做了基础配置，开发者可进一步自定义。</p><p>刚接触 Taro 开发 APP 的开发者，经常在开发环境的配置上，消耗大量时间。再次建议大家先学习利用 GitHub Action 进行打包编译，相关代码位于 <code>.github</code> 目录中。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="5-调试工具-taro-playground-升级至-taro-35-版本及-react-native-068">5. 调试工具 Taro Playground 升级至 Taro 3.5 版本及 React Native 0.68<a href="#5-调试工具-taro-playground-升级至-taro-35-版本及-react-native-068" class="hash-link" aria-label="5. 调试工具 Taro Playground 升级至 Taro 3.5 版本及 React Native 0.68的直接链接" title="5. 调试工具 Taro Playground 升级至 Taro 3.5 版本及 React Native 0.68的直接链接">​</a></h3><p><a href="https://github.com/wuba/taro-playground" target="_blank" rel="noopener noreferrer">Taro Playground</a> 作为 Taro RN 端的调试工具及跨端 Demo，进行了同步更新。此次更新无法保证向下兼容，使用旧版本 Taro 的开发者，如需调试 Android，可在 <a href="https://github.com/wuba/taro-playground/releases" target="_blank" rel="noopener noreferrer">releases</a> 中下载旧包进行调试。在 App Store 中，我们只上架最新版本，需要旧版本的开发者请不要开启应用自动更新。如不慎升级，需自行打包编译，或联系我们加入测试组。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="6-壳工程代码整理">6. 壳工程代码整理<a href="#6-壳工程代码整理" class="hash-link" aria-label="6. 壳工程代码整理的直接链接" title="6. 壳工程代码整理的直接链接">​</a></h3><p>对于 0.68 版本的<a href="https://github.com/NervJS/taro-native-shell/tree/0.68.0" target="_blank" rel="noopener noreferrer">壳工程</a>，我们进行了代码的重新整理。将初始化 RN、安装 expo、兼容 Taro、安装依赖、添加 Github Action，每一个步骤一一列出，方便开发者在定制壳工程时进行参考。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="三pnpm">三、pnpm<a href="#三pnpm" class="hash-link" aria-label="三、pnpm的直接链接" title="三、pnpm的直接链接">​</a></h2><p>众所周知，pnpm 在当下被誉为“最先进的包管理工具”，它是由 npm/yarn 衍生而来，解决了 npm/yarn 内部潜在的 bug，极大的优化了性能，扩展了使用场景。在社区内很高的呼声下，Taro 也在提供了这一热门的包管理工具选项。</p><p>在 Taro v3.5 版本以后，在脚手架内置包管理工具不再自动识别本地环境内安装的包管理工具，而是需要开发者自行选择需要的包管理工具，供开发者更方便使用和操作。</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">? 请选择包管理工具 </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Use arrow keys</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">❯ </span><span class="token function" style="color:#d73a49">yarn</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  cnpm</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>如果是在较旧的 Taro 项目中，想使用 pnpm 管理工具，由于<strong>幽灵依赖</strong>的存在，开发者需要在项目中手动安装并升级依赖来修复该问题，具体操作可参考文中<code>升级指南</code>第 5 项。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="四其它特性">四、其它特性<a href="#四其它特性" class="hash-link" aria-label="四、其它特性的直接链接" title="四、其它特性的直接链接">​</a></h2><p>除了以上新特性外，v3.5 还包括很多重要的更新：</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-适配-react-18">1. 适配 React 18<a href="#1-适配-react-18" class="hash-link" aria-label="1. 适配 React 18的直接链接" title="1. 适配 React 18的直接链接">​</a></h3><p>从 Taro v3.5 开始，Taro 将默认使用 React 18 版本。你可以在 Taro 使用 React18 中激动人心的新特性了。从新建项目开始探索吧：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># @tarojs/cli 升级到 v3.5</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ taro init myProject</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 选择「react」框架</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>需要注意的是，受小程序环境限制，诸如新 Suspense 特性将不能在小程序中使用，详情请<a href="/docs/react-18">浏览文档</a>。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-h5-支持服务端渲染">2. H5 支持服务端渲染<a href="#2-h5-支持服务端渲染" class="hash-link" aria-label="2. H5 支持服务端渲染的直接链接" title="2. H5 支持服务端渲染的直接链接">​</a></h3><p>通过 <a href="https://github.com/SyMind/tarojs-plugin-platform-nextjs" target="_blank" rel="noopener noreferrer">tarojs-plugin-platform-nextjs</a> 插件配置，我们可以将 Taro 和 nextjs 社区生态打通，让 Taro H5 支持 Pre-rendering（预渲染）、SSR（服务端渲染）和 ISR（增量静态生成）各种特性，提升页面首屏渲染速度 🚀，也利于 SEO 优化 🔍。</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> tarojs-plugin-platform-nextjs next</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在 Taro 项目的 &nbsp;<code>config/index.js</code> 中添加插件。</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">plugins</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'tarojs-plugin-platform-nextjs'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>启动项目。</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">npx taro build --type nextjs --watch</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>插件来自社区大佬 <a href="https://github.com/SyMind" target="_blank" rel="noopener noreferrer">@SyMind</a> 贡献，详细用法可以参考<a href="https://github.com/SyMind/tarojs-plugin-platform-nextjs" target="_blank" rel="noopener noreferrer">插件文档</a>。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-h5-支持多页应用模式">3. H5 支持多页应用模式<a href="#3-h5-支持多页应用模式" class="hash-link" aria-label="3. H5 支持多页应用模式的直接链接" title="3. H5 支持多页应用模式的直接链接">​</a></h3><p>H5 端的多页应用模式是社区呼声最高的若干特性之一，在新版本中将得到支持，详细用法及注意事项请<a href="/docs/next/config-detail#h5routermode">参考文档</a>。</p><p>配置开启多页应用模式：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">/** config/index.js */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">h5</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">router</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">mode</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'multi'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="4-补全对小程序生命周期方法的支持">4. 补全对小程序生命周期方法的支持<a href="#4-补全对小程序生命周期方法的支持" class="hash-link" aria-label="4. 补全对小程序生命周期方法的支持的直接链接" title="4. 补全对小程序生命周期方法的支持的直接链接">​</a></h3><p>过去 Taro 对于小程序常用的生命周期方法支持得不够完整，新版本中将补全对应的方法与钩子。</p><p><strong>新增 App 生命周期：</strong></p><ul><li>onError（React &amp; Vue3）</li></ul><p><strong>新增钩子：</strong></p><ul><li>useLaunch（React）</li><li>useError（React）</li><li>usePageNotFound（React）</li><li>useLoad（React &amp; Vue3）</li><li>useUnload（React &amp; Vue3）</li><li>useSaveExitState（React &amp; Vue3）</li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="5-小程序内部实现优化">5. 小程序内部实现优化<a href="#5-小程序内部实现优化" class="hash-link" aria-label="5. 小程序内部实现优化的直接链接" title="5. 小程序内部实现优化的直接链接">​</a></h3><p>对小程序的内部实现进行优化，减少约 50k 包体积，同时降低内存占用，减少 setData 发送的数据量。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="五升级指南">五、升级指南<a href="#五升级指南" class="hash-link" aria-label="五、升级指南的直接链接" title="五、升级指南的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-升级-taro-cli">1. 升级 Taro CLI<a href="#1-升级-taro-cli" class="hash-link" aria-label="1. 升级 Taro CLI的直接链接" title="1. 升级 Taro CLI的直接链接">​</a></h3><p>升级全局的 Taro CLI：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i -g @tarojs/cli</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>或升级本地的 Taro CLI 工具：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i @tarojs/cli</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-更新项目依赖">2. 更新项目依赖<a href="#2-更新项目依赖" class="hash-link" aria-label="2. 更新项目依赖的直接链接" title="2. 更新项目依赖的直接链接">​</a></h3><blockquote><p>如果依赖安装失败或安装成功却运行报错，可以尝试删除 node_modules、yarn.lock、package-lock.json、pnpm-lock.yaml 后重新安装依赖。</p></blockquote><h3 class="anchor anchorWithStickyNavbar_mCi1" id="21-更新项目内的-taro-相关依赖">2.1 更新项目内的 Taro 相关依赖<a href="#21-更新项目内的-taro-相关依赖" class="hash-link" aria-label="2.1 更新项目内的 Taro 相关依赖的直接链接" title="2.1 更新项目内的 Taro 相关依赖的直接链接">​</a></h3><p>把 <code>package.json</code> 中 Taro 相关依赖的版本修改为 <code>3.5.0</code> 后重新安装依赖。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="22-使用-react-的项目">2.2 使用 React 的项目<a href="#22-使用-react-的项目" class="hash-link" aria-label="2.2 使用 React 的项目的直接链接" title="2.2 使用 React 的项目的直接链接">​</a></h3><ul><li><strong>【Breaking】</strong>使用 React 的项目需要额外安装 <code>@pmmmwh/react-refresh-webpack-plugin</code> 和 <code>react-refresh</code>：</li></ul><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i @pmmmwh/react-refresh-webpack-plugin react-refresh --save-dev</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="23-使用-preact-的项目">2.3 使用 Preact 的项目<a href="#23-使用-preact-的项目" class="hash-link" aria-label="2.3 使用 Preact 的项目的直接链接" title="2.3 使用 Preact 的项目的直接链接">​</a></h3><ul><li><strong>【Breaking】</strong>使用 Preact 的项目需要额外安装 <code>@prefresh/webpack</code> 和 <code>@prefresh/babel-plugin</code>：</li></ul><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i @prefresh/webpack @prefresh/babel-plugin --save-dev</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="24-使用-vue2-的项目">2.4 使用 Vue2 的项目<a href="#24-使用-vue2-的项目" class="hash-link" aria-label="2.4 使用 Vue2 的项目的直接链接" title="2.4 使用 Vue2 的项目的直接链接">​</a></h3><ul><li><strong>【Breaking】</strong>使用 Vue2 的项目需要额外安装 <code>@vue/babel-preset-jsx</code>：</li></ul><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i @vue/babel-preset-jsx --save-dev</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="25-使用-vue3-的项目">2.5 使用 Vue3 的项目<a href="#25-使用-vue3-的项目" class="hash-link" aria-label="2.5 使用 Vue3 的项目的直接链接" title="2.5 使用 Vue3 的项目的直接链接">​</a></h3><ul><li><strong>【Breaking】</strong>使用 Vue3 的项目需要额外安装 <code>@vue/babel-plugin-jsx</code>：</li></ul><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i @vue/babel-plugin-jsx --save-dev</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-使用-webpack5">3. 使用 Webpack5<a href="#3-使用-webpack5" class="hash-link" aria-label="3. 使用 Webpack5的直接链接" title="3. 使用 Webpack5的直接链接">​</a></h3><p>开发者需要先卸载 <code>@tarojs/mini-runner</code> 和 <code>@tarojs/webpack-runner</code>：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> uninstall @tarojs/mini-runner @tarojs/webpack-runner</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>然后安装 <code>@tarojs/webpack5-runner</code>：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> @tarojs/webpack5-runner</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>最后修改 Taro 编译配置即可：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">/** config/index.js */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">compiler</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'webpack5'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="4-使用-react-18">4. 使用 React 18<a href="#4-使用-react-18" class="hash-link" aria-label="4. 使用 React 18的直接链接" title="4. 使用 React 18的直接链接">​</a></h3><p>需要更新 <code>react</code>、<code>react-dom</code>、<code>@types/react</code> 的版本：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i react react-dom</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i @types/react --save-dev</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="5-使用-pnpm">5. 使用 pnpm<a href="#5-使用-pnpm" class="hash-link" aria-label="5. 使用 pnpm的直接链接" title="5. 使用 pnpm的直接链接">​</a></h3><p>因为 pnpm 不允许<strong>幽灵依赖</strong>的存在，因此开发者需要在项目中手动安装下列依赖：</p><p><code>dependencies</code> 补充依赖：</p><div class="language-json codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-json codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token property" style="color:#36acaa">"@tarojs/helper"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3.5.0"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token property" style="color:#36acaa">"@tarojs/plugin-platform-weapp"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3.5.0"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token property" style="color:#36acaa">"@tarojs/plugin-platform-alipay"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3.5.0"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token property" style="color:#36acaa">"@tarojs/plugin-platform-tt"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3.5.0"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token property" style="color:#36acaa">"@tarojs/plugin-platform-swan"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3.5.0"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token property" style="color:#36acaa">"@tarojs/plugin-platform-jd"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3.5.0"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token property" style="color:#36acaa">"@tarojs/plugin-platform-qq"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3.5.0"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token property" style="color:#36acaa">"@tarojs/router"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3.5.0"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token property" style="color:#36acaa">"@tarojs/shared"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3.5.0"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token property" style="color:#36acaa">"@tarojs/taro-h5"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3.5.0"</span><span class="token punctuation" style="color:#393A34">,</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><code>devDependencies</code> 补充依赖：</p><p>安装项目对应的 Webpack 版本，如 Webpack5：</p><div class="language-json codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-json codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token property" style="color:#36acaa">"webpack"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"^5.73.0"</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="六最后">六、最后<a href="#六最后" class="hash-link" aria-label="六、最后的直接链接" title="六、最后的直接链接">​</a></h2><p>下半年 Taro 团队的核心将围绕以下各方向展开：</p><ul><li>支持使用 Vite 进行编译（预计 Q3 推出 alpha 版本）</li><li>小程序方面将持续对性能进行优化、支持更多的 React/Vue 特性（如 Portal）和生态库（如 React/Vue Router）。</li><li>H5 方面将输出适配 Vue3 的 SSR 方案。</li><li>RN 方面将深入探索高阶功能，如地图、动画、2D 及 3D 图形方案，并推出跨端可视化库，提升 Taro 跨端能力。</li><li>此外还会探索对于 Flutter 的适配。</li></ul><p>最后的最后，衷心感谢参与社区共建与交流的各位同学！上半年我们制定了开发者贡献制度，建立起规范的项目分工与有效的荣誉激励机制。未来 Taro 将积极探寻更中立与开放的开源治理机制，欢迎各位开发者参与到 Taro 社区的建设中~</p>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[Taro v3.5 beta 编译提速，支持 Webpack5、React 18...]]></title>
            <link>https://docs.taro.zone/blog/2022/05/19/Taro-3.5-beta</link>
            <guid>https://docs.taro.zone/blog/2022/05/19/Taro-3.5-beta</guid>
            <pubDate>Thu, 19 May 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Taro v3.5 beta 版本发布 🚀]]></description>
            <content:encoded><![CDATA[<p>编译速度一直是困扰开发者的头等问题，现阶段大型 Taro 项目即使在增加了 <code>cache-loader</code>、<code>thread-loader</code> 等优化手段后，编译耗时仍高居不下。因此在 v3.5 版本中 Taro 重点对编译系统进行了重构，引入对 Webpack5 的支持，改善小程序 &amp; H5 编译时的性能与体验。（除此之外，Taro 也正在落地对于 Vite 的支持，届时开发者将可以自由地选择编译工具。）</p><p>同时，Taro v3.5 还带来了兼容 React 18、H5 MPA 等新特性，欢迎各位同学升级试用~</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="一编译提速">一、编译提速<a href="#一编译提速" class="hash-link" aria-label="一、编译提速的直接链接" title="一、编译提速的直接链接">​</a></h2><p>为了改善编译性能，Taro 主要做了以下事情：</p><ul><li>支持 Webpack5</li><li>基于模块联邦的依赖预编译</li><li>支持使用 <code>esbuild</code> 压缩 JS，使用 <code>esbuild</code> 或 <code>@parcel/css</code> 压缩 CSS</li><li>使用 <code>@swc/register</code> 代替 <code>@babel/register</code></li></ul><p>接下来将简单聊聊 Webpack5 与依赖预编译，关于编译提速的完整实现细节请参阅 <a href="https://github.com/NervJS/taro/discussions/11533" target="_blank" rel="noopener noreferrer">RFC 文档</a>。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-webpack5">1. Webpack5<a href="#1-webpack5" class="hash-link" aria-label="1. Webpack5的直接链接" title="1. Webpack5的直接链接">​</a></h3><p>Webpack5 发布已有两年时间，功能足够稳定，同时其持久化缓存、模块联邦、更优的 Tree Shaking 等特性都为项目的编译流程提供了更好的解决方案。</p><p>其中的<strong>持久化缓存功能</strong>是最重要的特性之一，能极大提升再次编译时的速度。但同时也引入了如何使缓存失效的问题。</p><p>Taro 遵循 Webpack <strong>“编译安全比编译速度重要”</strong> 的理念，默认不开启持久化缓存。当开发者设计好缓存策略后，强烈建议开启持久化缓存。详细配置请参考 <a href="/docs/next/config-detail#cache">mini.cache</a>。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-依赖预编译">2. 依赖预编译<a href="#2-依赖预编译" class="hash-link" aria-label="2. 依赖预编译的直接链接" title="2. 依赖预编译的直接链接">​</a></h3><p>Webpack5 另一个重要的特性要数<strong>模块联邦（Module Federation）</strong>。受 <a href="https://umijs.org/zh-CN/docs/mfsu" target="_blank" rel="noopener noreferrer">UmiJS mfsu</a> 特性的启发，可以预先把项目的 node_modules 依赖打包为一个模块联邦 remote 应用，再次编译时 Webpack 则无需再对依赖进行编译，从而提升编译速度。</p><p>依赖预编译可以分为三步：</p><ol><li>收集依赖</li><li>打包依赖</li><li>打包 Module Federation Remote 应用</li></ol><p>Taro 参考 <a href="https://cn.vitejs.dev/" target="_blank" rel="noopener noreferrer">Vite</a> 使用了 esbuild 收集用户使用到的第三方依赖，并分别进行打包。打包后的模块会作为 Webpack 的 entry，最终打包为模块联邦 Remote 应用，供主应用（Host）消费。实现细节请参考 <a href="https://github.com/NervJS/taro/discussions/11533" target="_blank" rel="noopener noreferrer">RFC 文档</a>。</p><p>Taro 会在小程序环境的 dev 模式下默认开启依赖预编译功能。首次编译时，因为使用了 esbuild 打包第三方依赖，所以会比普通编译稍快。二次编译时，Taro 能直接复用 Remote App，Webpack 只需编译业务代码，因此根据不同项目会有不同的编译提速效果。</p><p>依赖预编译的流程图：</p><p><img loading="lazy" src="https://storage.360buyimg.com/cjj-pub-images/prebundle.png" alt="https://storage.360buyimg.com/cjj-pub-images/prebundle.png" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-提速效果">3. 提速效果<a href="#3-提速效果" class="hash-link" aria-label="3. 提速效果的直接链接" title="3. 提速效果的直接链接">​</a></h3><p>以 <a href="https://github.com/jdf2e/nutui/tree/next/src/sites/mobile-taro/vue" target="_blank" rel="noopener noreferrer">NutUI 组件示例库</a>为例，分别测试 dev 与 prod 环境下编译微信小程序的编译提速效果：</p><p><img loading="lazy" src="https://storage.jd.com/cjj-pub-images/compile-speed_dev.png" alt="https://storage.jd.com/cjj-pub-images/compile-speed_dev.png" class="img_ZOZK"></p><p><img loading="lazy" src="https://storage.jd.com/cjj-pub-images/compile-speed_prod.png" alt="https://storage.jd.com/cjj-pub-images/compile-speed_prod.png" class="img_ZOZK"></p><p>可以看出：</p><ol><li>在 dev 环境下因为 Taro 默认开启了依赖预编译，因此 Webpack5 首次编译速度比 Webpack4 稍快。而 prod 环境没有默认开启依赖预编译，因此两者速度相当（而且 Webpack5 需要写入缓存，可能会比 Webpack4 稍慢）。</li><li>无论是 dev 还是 prod 环境，在完全命中缓存的最优情况下，Webpack5 的编译速度都能得到极大提升。即使是修改源码导致了部分缓存失效时，编译速度仍然比首次编译快得多。</li></ol><h3 class="anchor anchorWithStickyNavbar_mCi1" id="4-使用">4. 使用<a href="#4-使用" class="hash-link" aria-label="4. 使用的直接链接" title="4. 使用的直接链接">​</a></h3><blockquote><p>旧项目升级后需要安装 Webpack5 的相关依赖才能正常编译，详情请参考下文的【升级指南】部分。</p></blockquote><p>简单修改 Taro 的编译配置即可开启 Webpack5、持久化缓存、依赖预编译等功能：</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">/** config/index.js */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 自定义编译工具，可选 'Webpack4' 或 'Webpack5'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">compiler</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'webpack5'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 依赖预编译配置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">prebundle</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">enable</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 持久化缓存配置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">cache</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">enable</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="二兼容-react18">二、兼容 React18<a href="#二兼容-react18" class="hash-link" aria-label="二、兼容 React18的直接链接" title="二、兼容 React18的直接链接">​</a></h2><p>React 官方正式发布了 react v18 版本，带来了 Automatic Batching、Transitions 和 Concurrent 等诸多新特性，提升了 React 性能。Taro 也在第一时间完成了对 React18 的兼容。</p><p>React 目前存在两种工作模式：<code>legacy</code> 和 <code>concurrent</code> 。在 concurrent 模式下，会使用 New Client API 来渲染组件。</p><blockquote><p>不要忘记将项目的 react 版本升级到 v18 哦</p></blockquote><p>详细内容请参考 <a href="https://docs.taro.zone/docs/react-18" target="_blank" rel="noopener noreferrer">React 18</a></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="三支持服务端渲染ssr">三、支持服务端渲染（SSR）<a href="#三支持服务端渲染ssr" class="hash-link" aria-label="三、支持服务端渲染（SSR）的直接链接" title="三、支持服务端渲染（SSR）的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-动机">1. 动机<a href="#1-动机" class="hash-link" aria-label="1. 动机的直接链接" title="1. 动机的直接链接">​</a></h3><p>与 SPA（单页应用程序，Single-Page Application）相比，服务器端渲染（SSR）能带来带来<strong>更快的首屏渲染速度</strong>和<strong>更好的 SEO</strong>，因此 SSR 功能是大家期望 Taro 支持的特性之一。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-实现原理">2. 实现原理<a href="#2-实现原理" class="hash-link" aria-label="2. 实现原理的直接链接" title="2. 实现原理的直接链接">​</a></h3><p>Taro 在 3.1 版本提出了<strong>开放式架构</strong>的思想，让开发者可以通过编写插件来让 Taro 支持一个新的平台。</p><p>通过 Taro 插件，将 React 生态中知名的 Next.js 框架作为 Taro 的一个新的目标平台，以此让 Taro 能够支持服务端渲染（SSR）。</p><p>目前这个插件项目的地址位于：<a href="https://github.com/SyMind/tarojs-plugin-platform-nextjs" target="_blank" rel="noopener noreferrer">SyMind/tarojs-plugin-platform-nextjs</a></p><blockquote><p><strong>⚠️&nbsp; 插件目前处于早期建设中，不建议用于生产环境！</strong></p></blockquote><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-安装与使用">3. 安装与使用<a href="#3-安装与使用" class="hash-link" aria-label="3. 安装与使用的直接链接" title="3. 安装与使用的直接链接">​</a></h3><p>安装插件和 Next.js 框架。</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 使用 npm 安装插件与 next.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> tarojs-plugin-platform-nextjs next</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 使用 pnpm 安装插件与 next.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">pnpm</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> tarojs-plugin-platform-nextjs next</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在 Taro 项目的<a href="/docs/config">编译配置</a>中添加本插件。</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">const config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  plugins: </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">'tarojs-plugin-platform-nextjs'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>开始尝试它吧！</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">npx taro build --type nextjs --watch</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="四支持多页应用mpa">四、支持多页应用（MPA）<a href="#四支持多页应用mpa" class="hash-link" aria-label="四、支持多页应用（MPA）的直接链接" title="四、支持多页应用（MPA）的直接链接">​</a></h2><p>很多同学通过阅读 Taro 的源码发现，Taro 曾在 1.x 支持过多页面应用，通过配置 <code>multi</code> 模式就可以开启该特性，但是由于并没有很好支持，也没有相应的业务场景测试，最终并没有在文档中呈现。</p><p>在 2.x 发布时，由于各种原因我们回滚了该特性，尽管开发者们依旧可以通过插件或者在项目内自定义 webpack 配置实现类似的需求，不过这依旧会在一些细节上难以处理得尽善尽美。比如需要额外配置文件拆分规则避免冗余的代码，对于 MPA 也并不需要<code>taro-router</code>提供对于路由相关的事件方法的支持……</p><p>MPA 是不少社区开发者所追求的重要特性之一，为此我们改写了 <code>taro-loader</code> 和 <code>taro-router</code> 以适应该模式的个性化需求，应用该模式也只需要如下配置：</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">h5</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">router</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">mode</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'multi'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>需要注意的是，有很多小程序事件和方法都是基于 SPA 模式设计使用的，在 MPA 模式并不适用，所以会存在一些问题，比如由于多页面导致 TabBar 会重复加载，App 级的生命周期会重复触发，不支持路由动画，生产环也需要额外配置路由映射等等，开启该模式前需要认真考量适用场景。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="五rn-相关依赖库由-unimodules-升级至-expo">五、RN <strong>相关依赖库由 unimodules 升级至 expo</strong><a href="#五rn-相关依赖库由-unimodules-升级至-expo" class="hash-link" aria-label="五rn-相关依赖库由-unimodules-升级至-expo的直接链接" title="五rn-相关依赖库由-unimodules-升级至-expo的直接链接">​</a></h2><p>Expo 是 React Native 生态中的重要角色，提供了非常多优秀的模块，在 Taro 中有较为广泛的使用，如 expo-av、expo-camera 等，将来我们还会持续接入新的模块。Expo 的模块系统，由 unimodules 变更为 expo 已有一段时日，其架构变更原因可参考文章：&nbsp;<a href="https://blog.expo.dev/whats-new-in-expo-modules-infrastructure-7a7cdda81ebc" target="_blank" rel="noopener noreferrer">What’s new in Expo modules infrastructure</a>。</p><p>Taro v3.5 及以后将使用新的模块系统，可以通过 taro init 选择 react-native 模板体验。如果你使用的是 Taro 壳工程，可切换到 <a href="https://github.com/NervJS/taro-native-shell/tree/0.67.0-expo" target="_blank" rel="noopener noreferrer">0.67.0-expo</a> 分支体验。</p><p>新老版本的 Taro 及壳工程之间混用的话，将存在不兼容情况，主要原因是存在多个版本原生依赖导致，可通过 resolution 进行版本锁定解决，相应版本参考<a href="https://github.com/NervJS/taro/discussions/11565#discussioncomment-2552958" target="_blank" rel="noopener noreferrer">此处</a>。</p><p>后续壳的工程将不再包含 unimodules 版本。旧版本升级可参考此<a href="https://github.com/NervJS/taro-native-shell/pull/72" target="_blank" rel="noopener noreferrer">PR</a>。</p><p>注意：升级为 expo 将不再支持 iOS 11，详细内容请参考 <a href="https://github.com/NervJS/taro/discussions/11565" target="_blank" rel="noopener noreferrer">discussions</a>。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="六升级指南">六、升级指南<a href="#六升级指南" class="hash-link" aria-label="六、升级指南的直接链接" title="六、升级指南的直接链接">​</a></h2><p>1<strong>. 安装 v3.5.0-beta 的 CLI 工具：</strong></p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i -g @tarojs/cli@beta</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><strong>2. 更新项目依赖</strong></p><blockquote><p>如果安装失败或打开项目失败，可以删除 node_modules、yarn.lock、package-lock.json 后重新安装依赖再尝试。</p></blockquote><p>2.1 把 package.json 文件中 Taro 相关依赖的版本修改为 <code>3.5.0@beta</code></p><p>2.2 Breakings</p><ul><li><p>Vue2 项目需要添加 devDenpendencies：<code>"@vue/babel-preset-jsx": "^1.2.4”</code></p></li><li><p>Vue3 项目需要添加 devDenpendencies：<code>"@vue/babel-plugin-jsx": "^1.0.6"</code></p></li><li><p>React 项目需要添加 devDenpendencies：</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token string-property property" style="color:#36acaa">"@pmmmwh/react-refresh-webpack-plugin"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"0.5.4"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token string-property property" style="color:#36acaa">"react-refresh"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"0.11.0"</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>Preact 项目需要添加 devDenpendencies：</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">```jsx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">"@prefresh/webpack": "^3.2.3",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">"@prefresh/babel-plugin": "^0.4.1"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">```</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>2.3 重新安装依赖</p></li></ul><p><strong>3. 使用 Webpack5</strong></p><p>新项目在创建项目时，选择编译工具为 <code>"webpack5"</code> 即可。</p><p>旧项目升级后需要更新依赖：</p><ul><li>建议首先删除 <code>node_modules</code>、<code>yarn.lock</code>、<code>package-lock.json</code>。</li><li><code>package.json</code> 中删除 <code>@tarojs/mini-runner</code> 和 <code>@tarojs/webpack-runner</code> 依赖，添加 <code>@tarojs/webpack5-runner</code> 依赖。</li><li>重新安装依赖。</li><li>Taro 编译配置添加 <code>compiler: 'webpack5'</code>，最后开启编译。</li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="最后">最后<a href="#最后" class="hash-link" aria-label="最后的直接链接" title="最后的直接链接">​</a></h2><p>接下来我们会继续对 <code>v3.5</code> 版本进行迭代，包括实现 H5 的依赖预编译等。而在 <code>v3.6</code> 版本则会落地对 Vite 的支持，同时优化运行时的性能。</p><p>最后的最后，衷心各位感谢参与 Taro 开源共建的同学！为了建立更加完善、更加可持续的 Taro 开源生态，突出贡献者价值，Taro 推出了<a href="/blog/2022/03/29/Taro-community">更清晰的参与机制和荣誉激励机制</a>，欢迎更多的同学参与进来~</p>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[成为 Taro 社区的一员 - 贡献者晋级指南]]></title>
            <link>https://docs.taro.zone/blog/2022/03/29/Taro-community</link>
            <guid>https://docs.taro.zone/blog/2022/03/29/Taro-community</guid>
            <pubDate>Tue, 29 Mar 2022 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>为了建立更加完善、更加可持续的 Taro 开源生态，突出贡献者价值，我们参照成熟开源社区运行机制制定了《 Taro 贡献者晋级制度》，为热爱和喜欢 Taro 技术的开发者和贡献者提供更清晰的参与机制和荣誉激励机制。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="晋升角色">晋升角色<a href="#晋升角色" class="hash-link" aria-label="晋升角色的直接链接" title="晋升角色的直接链接">​</a></h2><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2022-03-29-Taro-community/RoadMap.png" alt="晋升角色路线图" class="img_ZOZK"></p><p>如图包含 4 个晋升角色：个人贡献者&amp;生态个人贡献者、助手、合作者&amp;生态合作者、技术委员会委员，晋升机制通过提名+投票的方式进行共识决策，晋升路径如下：</p><ul><li>个人贡献者 → 助手 → 合作者 → 技术委员会委员</li><li>个人贡献者 → 合作者 → 技术委员会委员</li><li>生态个人贡献者 → 生态合作者 → 技术委员会委员</li></ul><p>对于一年内不活跃者，会进行自动进行降级，贡献突出者可申请荣休。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="个人贡献者生态个人贡献者individual-committer">个人贡献者&amp;生态个人贡献者（Individual Committer）<a href="#个人贡献者生态个人贡献者individual-committer" class="hash-link" aria-label="个人贡献者&amp;生态个人贡献者（Individual Committer）的直接链接" title="个人贡献者&amp;生态个人贡献者（Individual Committer）的直接链接">​</a></h3><p>任何同意《 Taro 行为准则》的个人开发者，都可以基于<a href="https://github.com/NervJS/taro/blob/next/CONTRIBUTING.md" target="_blank" rel="noopener noreferrer">《贡献者指南》</a>进行 pull request 提交， bug 反馈 &amp; 修复、新特性提议或 PR 均可，会在 <a href="/docs/team/">Taro 官方文档</a>中列为个人贡献者。当贡献者的有价值工作被其他合作者注意到后，可以被提名为合作者。</p><p>任何基于或围绕 Taro 生态进行的工具、插件、培训、教程等个人都会在 <a href="/docs/team/">Taro 官方文档</a>中列为生态个人贡献者。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="助手triage">助手（Triage）<a href="#助手triage" class="hash-link" aria-label="助手（Triage）的直接链接" title="助手（Triage）的直接链接">​</a></h3><p>负责 <a href="https://github.com/NervJS/taro" target="_blank" rel="noopener noreferrer">NervJS/taro</a>、<a href="https://github.com/NervJS/taro-ui" target="_blank" rel="noopener noreferrer">NervJS/taro-ui</a> 仓库新 issues 的维护，负责给 issues 或 pull requests 打标签，以及负责评论、关闭和重新开启 issue 或 pull request，负责将 bug 或 feature 分流给具体工作组。</p><ul><li><p>目的</p><p>旨在减少 issue 列表，保持 issue 及时跟踪，促进新人参与及贡献 pull request。</p></li><li><p>权益</p><p>Github NervJS 组 Member 权限，相关项目 Triage 权限，可以管理 issues 和 pull requests（没有写权限）。</p></li><li><p>申请方法</p><p>对 Taro 项目有全面了解和深度开发经验的任何人，可以在 NervJS/taro README.md 中提交一个 pull request，说明申请成为助手的动机并同意本项目的行为守则，经 2 名合作者同意即可通过。</p><p>申请 pull request 参考模版如下：</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2022-03-29-Taro-community/PR-Triage.png" alt="PR 模板" class="img_ZOZK"></p></li><li><p>退出机制</p><p>对 6 个月不活跃的小助手进行定期移除。</p></li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="合作者--生态合作者collaborator">合作者 &amp; 生态合作者（Collaborator）<a href="#合作者--生态合作者collaborator" class="hash-link" aria-label="合作者 &amp; 生态合作者（Collaborator）的直接链接" title="合作者 &amp; 生态合作者（Collaborator）的直接链接">​</a></h3><p>负责维护 <a href="https://github.com/NervJS/taro" target="_blank" rel="noopener noreferrer">NervJS/taro</a>、<a href="https://github.com/NervJS/taro-ui" target="_blank" rel="noopener noreferrer">NervJS/taro-ui</a> 仓库，帮助用户和初级贡献者，参加具体工作组为当前项目贡献代码和文档，评审和评论 issues 和 pull requests。</p><ul><li><p>目的</p><p>旨在不断丰富 Taro 特性、性能、安全等。</p></li><li><p>权益</p><p>Github NervJS 组 Member 权限，Github Write 权限，可以提交 commit 到 <a href="https://github.com/NervJS/taro" target="_blank" rel="noopener noreferrer">NervJS/taro</a> 仓库，可以配置持续集成任务，负责 pull request 评审及合并，1 个 PR 合并需至少 2 名合作者或 1 名技术委员会成员同意即可进入观察期，观察期 3 个月即可正式成为合作者。</p></li><li><p>申请方法</p><p>合作者提名有突出贡献的个人贡献者，通过投票机制决定是否可以成为合作者。一名合格的合作者需具备：技术精进，业务精湛；沟通无障碍，至少读写无阻碍；人品优良，能钻研，不轻易半途而废；态度谦逊，能接受他人意见；Owner 心态，积极主动。</p><p>申请 pull request 参考模版如下：</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2022-03-29-Taro-community/PR-Collaborator.png" alt="PR 模板" class="img_ZOZK"></p></li><li><p>退出机制</p><p>对不活跃的合作者，技术委员会有权进行移除或设置为荣休状态，荣休成员可以重新向技术委员会申请为活跃状态。如果一个合作者超过 6 个月无任何贡献，会自动设置成荣休状态。</p></li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="技术委员会成员technical-steering-committee">技术委员会成员（Technical Steering Committee）<a href="#技术委员会成员technical-steering-committee" class="hash-link" aria-label="技术委员会成员（Technical Steering Committee）的直接链接" title="技术委员会成员（Technical Steering Committee）的直接链接">​</a></h3><p>负责技术方向、项目管理、项目发布、贡献政策、仓库托管、行为准则、维护合作者列表，定期参加 TSC 活动，主席（主持人）会在线上主持活动，并做好活动记录并公布。</p><ul><li><p>目的</p><p>解决难以达成共识的技术难题、新方向等。</p></li><li><p>权益</p><p>Github NervJS 组 Owner 权限。</p></li><li><p>申请方法</p><p>新增 TSC 成员需要由其他 TSC 成员提名并讨论投票。</p><p>申请 pull request 参考模版如下：</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2022-03-29-Taro-community/PR-Committee.png" alt="PR 模板" class="img_ZOZK"></p></li><li><p>退出机制</p><p>在一季度内，缺席 75%的活动，且未参与任何一次投票，自动除名。成员可提出暂时”荣休“。</p></li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="运行机制">运行机制<a href="#运行机制" class="hash-link" aria-label="运行机制的直接链接" title="运行机制的直接链接">​</a></h2><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2022-03-29-Taro-community/structure.png" alt="技术委员会组成" class="img_ZOZK"></p><p>如图运行机制包含技术委员会以及下设的 5 个团队（Core 团队、Plugins 团队、Platform 团队、<strong>创新团队</strong>、社区团队）。技术委员会由技术委员会委员组成，负责技术方向、项目管理、贡献政策、仓库托管、行为准则、维护合作者列表等，技术委员会主席负责定期组织会议。工作组由合作者成员组成，每个方向有一个 Owner，负责相关工作组的开发进展。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="团队">团队<a href="#团队" class="hash-link" aria-label="团队的直接链接" title="团队的直接链接">​</a></h3><ul><li><p>Core 团队</p><ul><li><p>Cli 工作组</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">主要负责 Taro 命令行工具的开发和维护工作。</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>Compile 工作组</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">负责维护、优化小程序和 H5 的编译系统。</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>Runtime 工作组</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">负责维护小程序运行时系统。</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li></ul></li><li><p><strong>Plugin</strong> 团队</p><p>负责维护各 Taro 插件，包括端平台插件，React、Vue DevTools 等。</p><ul><li><p>端平台插件工作组</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">负责维护各端平台插件，包括对微信、支付宝、百度、字节跳动、QQ、京东、企业微信、飞书、快手、钉钉、小红书等厂商小程序的适配等。</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>混合开发组</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">负责维护 Taro 与原生小程序的相互调用功能、Taro 开发原生插件等。</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li></ul></li><li><p>Platform 团队</p><p>负责 App、Web、Open Harmony 等跨平台开发。</p><ul><li><p>H5 工作组</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">负责维护 H5 的各模块，包括路由、组件库、API 库等。</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>React Native 工作组</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">负责React Native适配核心、组件库、API 库等部分的开发。</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>Open Harmony 工作组</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">负责鸿蒙适配核心、组件库、API 库等部分的开发。</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>快应用工作组</p></li></ul></li><li><p><strong>创新团队</strong></p><p>Taro 创新特新、新方向探索，如 wasm、rust、vite、flutter、electron 等。</p><ul><li><p>UI 框架兴趣组</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">TaroUI、NutUI 等 UI 库和其他类型生态工具的研发与管理。</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li></ul></li><li><p>社区团队</p><p>负责 Taro 生态与运营，和 Taro 社区的运营推广工作。</p></li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="技术委员会双周会">技术委员会双周会<a href="#技术委员会双周会" class="hash-link" aria-label="技术委员会双周会的直接链接" title="技术委员会双周会的直接链接">​</a></h3><ul><li>时间：每双周周四前，在 TSC issue 中预告下次会议的内容和日期。</li><li>议题：来自 Taro 下各项目中标注了 tsc-agenda 标签的事宜。会议结束后提交会议纪要 pull request。每次会议可邀请非委会参加，但无投票权。</li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="基于共识决策的投票机制">基于共识决策的投票机制<a href="#基于共识决策的投票机制" class="hash-link" aria-label="基于共识决策的投票机制的直接链接" title="基于共识决策的投票机制的直接链接">​</a></h3><p>各个晋升投票环节，基于共识决策原则，原则上达成多数一致。</p><ul><li>待投票的议题需要在会议前周知各成员，给予成员足够调研思考时间</li><li>议题在即将达成一致时，在结题前必须询问“有人反对吗？”，以周知最后反对的机会</li><li>议题无法达成一致时，可以投票多数支持是否延期到下一个会议，否则必须继续讨论</li><li>议题满足“多数胜利”后即可通过，成员可以弃票</li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="引导--培训机制">引导 / 培训机制<a href="#引导--培训机制" class="hash-link" aria-label="引导 / 培训机制的直接链接" title="引导 / 培训机制的直接链接">​</a></h3><p>助手、合作者和技术委员会成员每个阶段，均提供相应的引导和培训，让新晋升者可以快速开展工作。</p>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[速来围观，你最期待的 Taro 特性正在发布]]></title>
            <link>https://docs.taro.zone/blog/2022/03/24/Taro-feature</link>
            <guid>https://docs.taro.zone/blog/2022/03/24/Taro-feature</guid>
            <pubDate>Thu, 24 Mar 2022 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>关注 Taro 近期动态的朋友们可能都已经注意到，社区推送了<a href="https://wj.qq.com/s2/9739035/9abd/" target="_blank" rel="noopener noreferrer">一份问卷</a>，向各位开发者征集对于 Taro 尚在调研中，或未发布各类新特性的建议，短时间内就收获非常多反馈，远超预期。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="框架特性">框架特性<a href="#框架特性" class="hash-link" aria-label="框架特性的直接链接" title="框架特性的直接链接">​</a></h2><p>依托于开放式跨端跨框架架构，Taro 框架和生态都聚集了大量特性供开发者使用，不论是使用 Vue、Preact 开发多端应用，还是各种拓展插件都是基于这样的架构开发的特性。目前我们准备了很多新特性的研发方向，依旧打算通过提交 <a href="https://github.com/NervJS/taro-rfcs" target="_blank" rel="noopener noreferrer">RFC</a> 提案来达成社区共识，希望这些特性是开发者在多端项目中真正需要的。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2022-03-24-Taro-feature/taro-feature.jpeg" alt="taro-feature.jpeg" class="img_ZOZK"></p><p>从投票结果来看，大家对于 Vite 和 Flutter 有很高的期待，同时对于 pnpm 的支持也有很高的需求。同时也有很多开发者提到的了更多的需求，比方说支持 WebComponent API、小程序 SSR 方案，Webpack5、Windi、Tailwind，甚至是 Angular 等等，这些特性有的已经在最后的验证阶段，有的则还在调研当中，会在合适的时候发布到社区中，可以耐心等待。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="web-端">Web 端<a href="#web-端" class="hash-link" aria-label="Web 端的直接链接" title="Web 端的直接链接">​</a></h3><p>作为 Taro 最早支持的非小程序端，Taro H5 一直在稳步的迭代当中，在同步小程序能力的同时，也在不断补充 Web 端生态的能力，类似路由动画、多页面应用等等，都在不断迭代的过程中一一补齐，其他的特性比如 SSR、云开发等方案，或者备受期待的自定义 TabBar 也都会在后续的更新过程中不断补充。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2022-03-24-Taro-feature/taro-web.jpeg" alt="taro-web.jpeg" class="img_ZOZK"></p><p>当然对于很多 Taro H5 中未能同步的组件、API 等等，也将逐一对齐，但短期内完成也并不现实，因此我们会依据社区反馈的需求按照优先级提供相应的能力，比方说 CoverView、CoverImage 等等特性已经上线，剩下的诸如 MovableArea、MovableView 、PickerView、支付能力等等也是在排期中，有的则是已经有 PR 等待合并……同时也欢迎大家参与相关能力的共建，提升 Web 端能力的边界和开发体验～</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="app-端">App 端<a href="#app-端" class="hash-link" aria-label="App 端的直接链接" title="App 端的直接链接">​</a></h3><p>App 端当前使用的是 RN 方案，问卷提出的特性也是基于这一方案考量的，目前 RN 和 H5、Open Harmony、快应用都是由 Taro 技术委员会 Hybrid 工作组负责，Taro RN 主要由该工作组的 Owner <a href="https://avatars1.githubusercontent.com/u/1876158?s=88&amp;u=0305430cab9f7a516c720e7fc7f8680df1c835b9&amp;v=4" target="_blank" rel="noopener noreferrer">zhiqingchen</a> ，其与 58 同城的小伙伴主要负责相关能力的架构与研发工作。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2022-03-24-Taro-feature/taro-rn.jpeg" alt="taro-rn.jpeg" class="img_ZOZK"></p><p>Taro RN 目前也在高速迭代的过程中，每日相关的 Issue 和 PR 都十分活跃，社区提交的需求在评估过后，也能快速相应。目前提出的这些特性，在完成相关的评审之后，也会按照优先级逐一在后续版本中呈现。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="小程序">小程序<a href="#小程序" class="hash-link" aria-label="小程序的直接链接" title="小程序的直接链接">​</a></h2><p>对于小程序生态的持续性探索，一直以来都是我们前行的方向，自 Taro 诞生至今从未停止过相关的探索，当前的跨端方案，各种混合开发的能力等等都是在这条路上寻觅所得。时至 2022 年，在这条路上依旧未能穷尽，也有很多开发者们翘首以盼的新特性正在努力研发的过程中。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="探索新特性">探索新特性<a href="#探索新特性" class="hash-link" aria-label="探索新特性的直接链接" title="探索新特性的直接链接">​</a></h3><p>最受开发者们期待的是预渲染骨架屏方案；其次则是 Web Component 和 History 这些打通 Web 端能力的特性；同样也有不在少数的开发者希望能够支持小程序的 wxs 语法……</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2022-03-24-Taro-feature/taro-mini.jpeg" alt="taro-mini.jpeg" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="混合开发能力">混合开发能力<a href="#混合开发能力" class="hash-link" aria-label="混合开发能力的直接链接" title="混合开发能力的直接链接">​</a></h3><p>使用混合开发能力来拓展能力边界，丰富小程序生态逐渐成为很多开发者的选择。Taro 也在多端生态融合中，有很多努力与尝试，比方说 taro-convert 和 taro-blended 等等都在尝试打破多端生态的边界，但是在混合开发过程中，依旧有不少有待完善。</p><p>很多开发者朋友对于 Vue 生态有着很高的需求，不论是微信、支付宝小程序插件，还是开发独立分包，都有超过五成的投票，在之后的研发中，我们也会上调相关特性的优先级，敬请期待～</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2022-03-24-Taro-feature/taro-mini-plugin.jpeg" alt="taro-mini-plugin.jpeg" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="开发中最常用的-taro-版本是">开发中最常用的 Taro 版本是？<a href="#开发中最常用的-taro-版本是" class="hash-link" aria-label="开发中最常用的 Taro 版本是？的直接链接" title="开发中最常用的 Taro 版本是？的直接链接">​</a></h2><p>从 Taro 正式开源算起，按照版本号可以分成四类，目前最受欢迎的是 Taro 3.x，也就是 20 年团队提出的开放式跨端跨框架方案，有超过 90 % 的开发者正在基于这个版本开发项目，其中超过五成开发者正在使用最新的 3.4 版本。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2022-03-24-Taro-feature/taro-version.jpeg" alt="taro-version.jpeg" class="img_ZOZK"></p><p>Taro 3 方案受到社区的广泛接受这件事，让团队内备受鼓舞，对于方案能力优化与边界拓展也一直是我们下苦功的地方。当然也有不少开发者在问卷中表示自己还有一些项目正在依赖 Taro 的历史版本，也不必太过担心，尽管 Taro 团队已经公告过暂停维护相关版本，但是对于一些重大的问题，依旧会发新版本修复。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="渐进式文档与教程">渐进式文档与教程<a href="#渐进式文档与教程" class="hash-link" aria-label="渐进式文档与教程的直接链接" title="渐进式文档与教程的直接链接">​</a></h2><blockquote><p>文档 &amp; 视频 &amp; 直播</p></blockquote><p>社区化运营是 Taro 很长时间以来追求的目标，Taro RFC、Task List 还有贡献者等机制都是基于这一诉求逐步设计完善的设计的。为了能够达成这一目的，在各类技术文章和渐进式文档等等也有内容持续性输出。这次调研的四个方向也是我们一直都有文章输出的领域，在问卷中也有很多朋友提到了最佳实践、体积优化等等相关的内容也会整理成文章或教程输出，大家如果有更多想法也可以提出，和 Taro 相关的文章也可以在 <strong>Taro 社区</strong>中投稿，丰富社区的动态资讯。</p><p>2022 年，自 Taro 开源以来已经快足足四年，直播课程也是我们一直想做但没有足够人力去完成的内容，今年我们会考虑在重大版本或其他合适的时机去实现这件事情，侧重的主题还会再斟酌思量，如果感兴趣可以继续关注这方面的动态，或者在评论区说说你们的看法。</p><p><img loading="lazy" src="https://storage.360buyimg.com/aotu-team/zakary-blog/2022-03-24-Taro-feature/taro-edu.jpeg" alt="taro-edu.jpeg" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="写在最后">写在最后<a href="#写在最后" class="hash-link" aria-label="写在最后的直接链接" title="写在最后的直接链接">​</a></h2><p>在问卷有一道开放题中，我们询问了大家对于 Taro 的发展有什么看法和意见，相当多朋友都在这一题中留言，其中不乏见解深刻的长文解读。正是这些来自各位开发者朋友们的看法，让我们对于社区有了更深刻的认知，同时也意识到了对于目前的 Taro 来说，在很多方面都有足够的提升空间，这也让我们确信目前社区前行的方向是对的，正有足够多的同伴和我们携手共进。</p><p>有很多朋友都提到了 Taro 对于 Vue 生态支持力度的不足，希望在使用 Vue 时能获得更好的开发体验；我们同样注意到，很多朋友希望文档可以更加完善，对于框架原理和各个版本功能有更全面的指引，对于贡献者和开发者都有更好的体验；生态不够健壮，缺少强有力的研发工具辅助开发，包括可靠的 UI 库等等……这些问题中，有很多都是我们正在努力探索并解决的，有些做得不够的地方也希望大家可以参与进来，共同为社区生态建设出一份力。</p><p>当然也有些许感动，很多小伙伴送出了大量的祝福和鼓励，很感谢大家对于 Taro 社区的支持，也正是因此，还有大量贡献者为 Taro 开源做出的努力，使得我们可以走到今天。</p><p>今后，Taro 也会以更加坚定的姿态向前迈进，大家有任何意见或问题都可以在评论区留下自己的看法，每一条反馈我们都会仔细阅读，希望可以和大家一同见证 Taro 社区的繁荣。</p>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[Taro 正式发布 3.4 版本 —— 全面支持 Preact 和 Vue3.2]]></title>
            <link>https://docs.taro.zone/blog/2022-01-20-Taro-3.4</link>
            <guid>https://docs.taro.zone/blog/2022-01-20-Taro-3.4</guid>
            <pubDate>Thu, 20 Jan 2022 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>距 Taro v3.4 beta 版本的发布已有一段时间，期间我们完善了对 Preact 和 Vue3 的支持，加入了一些有趣的特性，更是对 H5 作了大幅度的优化与调整，并于近期发布了 v3.4 的正式版本。</p><blockquote><p>上月我们还推出了支持开发鸿蒙应用的 v3.5.0 canary 版本，欢迎各位同学关注~</p></blockquote><h2 class="anchor anchorWithStickyNavbar_mCi1" id="一支持使用-preact"><strong>一、支持使用 Preact</strong><a href="#一支持使用-preact" class="hash-link" aria-label="一支持使用-preact的直接链接" title="一支持使用-preact的直接链接">​</a></h2><p>开发小程序应用时我们经常会受到包体积的掣肘，大型应用常常为了“尺土寸金”的包体积开展瘦身行动。在此背景下 React 将近 100k 的体积则显得有点过于奢侈。因此 Taro v3.4 实现了对 Preact 的支持，仅需少量配置即可从 React 切换到 Preact，有效地降低了包体积。</p><blockquote><p><a href="https://preactjs.com/" target="_blank" rel="noopener noreferrer">Preact</a> 是一款体积超小的类 React 框架，提供和 React 几乎一致的 API，兼容 React 生态，而体积只有 5k 左右。</p></blockquote><p>适配思路与具体用法请参阅 <a href="/blog/2021-11-24-Taro-3.4-beta#%E6%94%AF%E6%8C%81%E4%BD%BF%E7%94%A8-preact">《Taro v3.4 发布 beta 版本 —— 支持使用 Preact 进行开发》</a></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="二更好地支持-vue-32"><strong>二、更好地支持 Vue 3.2</strong><a href="#二更好地支持-vue-32" class="hash-link" aria-label="二更好地支持-vue-32的直接链接" title="二更好地支持-vue-32的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-支持-composition-api-版本的小程序生命周期钩子"><strong>1. 支持 Composition API 版本的小程序生命周期钩子</strong><a href="#1-支持-composition-api-版本的小程序生命周期钩子" class="hash-link" aria-label="1-支持-composition-api-版本的小程序生命周期钩子的直接链接" title="1-支持-composition-api-版本的小程序生命周期钩子的直接链接">​</a></h3><blockquote><p><a href="/docs/next/composition-api">文档地址</a></p></blockquote><p>Vue 3.2 正式推出了 <a href="https://v3.vuejs.org/api/sfc-script-setup.html#basic-syntax" target="_blank" rel="noopener noreferrer">script setup</a> 语法，过去 Taro 的 Options 式小程序生命周期钩子难以配合 script setup 语法进行使用。因此 Taro v3.4 提供了 Composition API 版本的小程序生命周期钩子，方便开发者配合 setup 语法组织和复用代码逻辑。</p><p>例子：</p><div class="language-html codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-html codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">script</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">setup</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">  </span><span class="token script language-javascript keyword module" style="color:#00009f">import</span><span class="token script language-javascript"> </span><span class="token script language-javascript imports punctuation" style="color:#393A34">{</span><span class="token script language-javascript imports"> useDidShow </span><span class="token script language-javascript imports punctuation" style="color:#393A34">}</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword module" style="color:#00009f">from</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'@tarojs/taro'</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">  </span><span class="token script language-javascript function" style="color:#d73a49">useDidShow</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token script language-javascript"> </span><span class="token script language-javascript console class-name">console</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">log</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'onShow'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-支持-style-v-bind-语法"><strong>2. 支持 <code>&lt;style&gt; v-bind</code> 语法</strong><a href="#2-支持-style-v-bind-语法" class="hash-link" aria-label="2-支持-style-v-bind-语法的直接链接" title="2-支持-style-v-bind-语法的直接链接">​</a></h3><p>Vue 3.2 的 <code>&lt;style&gt; v-bind</code> 语法让我们可以对样式进行数据绑定。它的实现使用了 DOM 的 MutationObserver API，但之前 Taro DOM 没有模拟实现此 API，因此使用 <code>&lt;style&gt; v-bind</code> 时会报错。</p><p>感谢 <a href="https://github.com/b2nil" target="_blank" rel="noopener noreferrer">@b2nil</a> 同学，参照 <a href="https://github.com/ampproject/worker-dom" target="_blank" rel="noopener noreferrer">worker-dom</a> 为 Taro DOM 实现了 <code>MutationObserver</code> API，让我们可以使用 <code>&lt;style&gt; v-bind</code> 语法。</p><blockquote><p>Taro DOM 只针对 Vue3 暴露了 <code>MutationObserver</code> API，使用 React 或 Vue2 的同学不需要担心会增大代码体积。</p></blockquote><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-暴露-vueloader-的配置"><strong>3. 暴露 VueLoader 的配置</strong><a href="#3-暴露-vueloader-的配置" class="hash-link" aria-label="3-暴露-vueloader-的配置的直接链接" title="3-暴露-vueloader-的配置的直接链接">​</a></h3><blockquote><p><a href="/docs/next/vue3#compileroptions">文档地址</a></p></blockquote><p>开发者有时需要修改 VueLoader 的配置，例如使用小程序原生组件时需要配置 <code>compilerOptions.isCustomElement</code>。以往开发者只能通过 <code>WebpackChain</code> 去修改，Taro v3.4 暴露了 VueLoader 的配置，让开发者可以更方便地进行修改。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="三h5"><strong>三、H5</strong><a href="#三h5" class="hash-link" aria-label="三h5的直接链接" title="三h5的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-自定义多路由配置"><strong>1. 自定义多路由配置</strong><a href="#1-自定义多路由配置" class="hash-link" aria-label="1-自定义多路由配置的直接链接" title="1-自定义多路由配置的直接链接">​</a></h3><p>Taro-H5 过去并不支持多路由访问同一个页面实例的操作，即便通过自定义路由配置也并不能在多个路由中访问同一个页面。</p><p>因此 Taro-H5 提供了自定义多路由配置的参数，供开发者根据需求自行配置。</p><blockquote><p><a href="/docs/next/config-detail#h5routercustomroutes">文档地址</a></p></blockquote><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">h5</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">router</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">customRoutes</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// "页面路径": "自定义路由"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string-property property" style="color:#36acaa">'/pages/index/index'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/index'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string-property property" style="color:#36acaa">'/pages/detail/index'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'/detail'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 可以通过数组为页面配置多个自定义路由</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-路由动画-by-shaogongbra"><strong>2. 路由动画 by <a href="https://github.com/ShaoGongBra" target="_blank" rel="noopener noreferrer">@ShaoGongBra</a></strong><a href="#2-路由动画-by-shaogongbra" class="hash-link" aria-label="2-路由动画-by-shaogongbra的直接链接" title="2-路由动画-by-shaogongbra的直接链接">​</a></h3><p>Taro-H5 支持了路由动画，开发者可以通过配置 <code>app.config.js</code> 来控制页面的动画效果，也可以通过覆盖 CSS 样式来调整动画。当然一些场景下，比如页面需要使用 <code>position: fixed;</code> 会因为 <code>translate3d</code> 影响实际效果，可以将动画禁用。</p><blockquote><p><a href="/docs/next/app-config#animation">文档地址</a></p></blockquote><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">animation</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 动画切换时间，单位毫秒</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">duration</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">300</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 动画切换时间，单位毫秒</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">delay</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">50</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-dynamic-import-node"><strong>3. dynamic-import-node</strong><a href="#3-dynamic-import-node" class="hash-link" aria-label="3-dynamic-import-node的直接链接" title="3-dynamic-import-node的直接链接">​</a></h3><p>Taro-H5 打包时会将页面和组件拆分成独立的文件按需加载，但这么做会导致没有用到的页面和组件依旧会被打包，导致编译体积变大，在一些特殊场景中（比如 PWA 等需要严格限制包体大小时）会因此受到不小的困扰。</p><p>所以我们通过 babel 插件提供了移除懒加载的方法：</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">presets</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string" style="color:#e3116c">'taro'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">framework</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'react'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">hot</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string-property property" style="color:#36acaa">'dynamic-import-node'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="四新增-defineappconfig-与-definepageconfig-编译宏"><strong>四、新增 defineAppConfig 与 definePageConfig 编译宏</strong><a href="#四新增-defineappconfig-与-definepageconfig-编译宏" class="hash-link" aria-label="四新增-defineappconfig-与-definepageconfig-编译宏的直接链接" title="四新增-defineappconfig-与-definepageconfig-编译宏的直接链接">​</a></h2><blockquote><p>再次感谢 <a href="https://github.com/b2nil" target="_blank" rel="noopener noreferrer">@b2nil</a> 同学为 Taro 新增了此特性。 文档地址：<a href="/docs/next/app-config#defineappconfig-%E5%AE%8F%E5%87%BD%E6%95%B0">defineAppConfig</a>、<a href="/docs/next/page-config#definepageconfig-%E5%AE%8F%E5%87%BD%E6%95%B0">definePageConfig</a></p></blockquote><h3 class="anchor anchorWithStickyNavbar_mCi1" id="defineappconfig"><strong>defineAppConfig</strong><a href="#defineappconfig" class="hash-link" aria-label="defineappconfig的直接链接" title="defineappconfig的直接链接">​</a></h3><p>开发者可以使用 <code>defineAppConfig</code> 包裹 App 配置对象，以获得全局配置的<strong>类型提示</strong>和<strong>自动补全</strong>，如：</p><div class="language-tsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-tsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// app.config.ts</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">defineAppConfig</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  pages</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'pages/index/index'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token dom variable" style="color:#36acaa">window</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    navigationBarTitleText</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'WeChat'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="definepageconfig"><strong>definePageConfig</strong><a href="#definepageconfig" class="hash-link" aria-label="definepageconfig的直接链接" title="definepageconfig的直接链接">​</a></h3><p>使用 <code>definePageConfig</code> 包裹 Page 配置对象，同样可以获得页面配置的<strong>类型提示</strong>和<strong>自动补全</strong>，如：</p><div class="language-tsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-tsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// page.config.ts</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">definePageConfig</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  navigationBarTitleText</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'首页'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>除此之外，<strong>开发者可以不提供页面的配置文件，直接在页面逻辑文件中使用 <code>definePageConfig</code> 定义页面配置</strong>。</p><p>如在 React 中：</p><div class="language-tsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-tsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// page.tsx</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">definePageConfig</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  navigationBarTitleText</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'首页'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Index</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在 Vue 中：</p><div class="language-html codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-html codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">  </span><span class="token script language-javascript function" style="color:#d73a49">definePageConfig</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">    </span><span class="token script language-javascript literal-property property" style="color:#36acaa">navigationBarTitleText</span><span class="token script language-javascript operator" style="color:#393A34">:</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'首页'</span><span class="token script language-javascript punctuation" style="color:#393A34">,</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">  </span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">  </span><span class="token script language-javascript keyword module" style="color:#00009f">export</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword module" style="color:#00009f">default</span><span class="token script language-javascript"> </span><span class="token script language-javascript punctuation" style="color:#393A34">{</span><span class="token script language-javascript punctuation" style="color:#393A34">}</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="五其它重要特性与优化"><strong>五、其它重要特性与优化</strong><a href="#五其它重要特性与优化" class="hash-link" aria-label="五其它重要特性与优化的直接链接" title="五其它重要特性与优化的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="性能"><strong>性能</strong><a href="#性能" class="hash-link" aria-label="性能的直接链接" title="性能的直接链接">​</a></h3><ul><li>修复 <code>eventSource</code> 导致的内存泄漏的问题，相关 <a href="https://github.com/NervJS/taro/commit/41c7cef9b0832306e096121b84a26947b896416e" target="_blank" rel="noopener noreferrer">commit</a>。</li><li>修复 <code>CustomWrapper</code> 嵌套使用后失效的问题，感谢 <a href="https://github.com/CS-Tao" target="_blank" rel="noopener noreferrer">@CS-Tao</a> 的贡献。</li><li>运行时体积优化，相比 Taro v3.3 版本大约减少了 <strong>30k</strong> 空间。</li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="特性"><strong>特性</strong><a href="#特性" class="hash-link" aria-label="特性的直接链接" title="特性的直接链接">​</a></h3><ul><li>支持微信小程序开发者工具的<strong>热重载</strong>功能，<a href="/docs/next/mini-troubleshooting#%E7%83%AD%E9%87%8D%E8%BD%BD">文档地址</a>。</li><li>支持支付宝小程序 <strong>2.0 构建</strong>。</li><li>H5 端支持配置渲染页面的容器 id，<a href="/docs/next/app-config#appid">文档地址</a></li><li>H5 端路由规则调整，Query 参数不再添加到 <code>pageId</code> 中，同时 <code>TabBar</code> 页面不会重新创建重复节点。</li><li>H5 端支持 <code>entryPagePath</code> 参数，by <a href="https://github.com/liuchuzhang" target="_blank" rel="noopener noreferrer">@liuchuzhang</a></li><li>H5 端支持 <code>CoverView</code> &amp; <code>CoverImage</code> 组件，by <a href="https://github.com/jiaozitang" target="_blank" rel="noopener noreferrer">@jiaozitang</a></li><li>H5 端支持 <code>NodesRef.context</code> &amp; <code>NodesRef.node</code> 方法</li><li>H5 端支持通过 <code>useResize</code> 方法监听 <code>resize</code> 事件</li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="修复"><strong>修复</strong><a href="#修复" class="hash-link" aria-label="修复的直接链接" title="修复的直接链接">​</a></h3><ul><li>修复预渲染失败的问题。</li><li>修复多个页面使用同一个组件时，因为组件定义了 <code>id</code> 而导致事件触发失效的问题。</li><li>修复 H5 端多页面滚动事件偶发性触发错误问题。</li><li>修复 3.x 中 H5 端 API 失效的 Shaking 能力。</li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="六breaking-changes"><strong>六、Breaking Changes</strong><a href="#六breaking-changes" class="hash-link" aria-label="六breaking-changes的直接链接" title="六breaking-changes的直接链接">​</a></h2><ul><li>旧项目升级到 Taro v3.4 需要安装对应的<strong>框架适配插件</strong>，详情浏览升级指南。</li><li>百度小程序使用 <code>onInit</code> 代替 <code>onLoad</code> 生命周期，以优化首次启动时间。</li><li>H5 端调整了 showModal 返回的 errMsg 参数，和小程序接口对齐，如果项目内针对这个差异做过适配，可以在升级后移除。 <a href="https://github.com/NervJS/taro/issues/11040" target="_blank" rel="noopener noreferrer">#11040</a></li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="升级指南"><strong>升级指南</strong><a href="#升级指南" class="hash-link" aria-label="升级指南的直接链接" title="升级指南的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-把-taro-cli-更新到-v340"><strong>1. 把 Taro CLI 更新到 <code>v3.4.0</code>：</strong><a href="#1-把-taro-cli-更新到-v340" class="hash-link" aria-label="1-把-taro-cli-更新到-v340的直接链接" title="1-把-taro-cli-更新到-v340的直接链接">​</a></h3><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i -g @tarojs/cli</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-更新项目依赖"><strong>2. 更新项目依赖</strong><a href="#2-更新项目依赖" class="hash-link" aria-label="2-更新项目依赖的直接链接" title="2-更新项目依赖的直接链接">​</a></h3><blockquote><p>如果安装失败或打开项目失败，可以删除 <strong>node_modules</strong>、<strong>yarn.lock</strong>、<strong>package-lock.json</strong> 后重新安装依赖再尝试。</p></blockquote><p>修改 <code>package.json</code> 文件中 Taro 相关依赖的版本修改为 <code>3.4.0</code>，再重新安装依赖。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3breaking-changes安装对应的框架适配插件"><strong>3.【Breaking Changes】安装对应的框架适配插件</strong><a href="#3breaking-changes安装对应的框架适配插件" class="hash-link" aria-label="3breaking-changes安装对应的框架适配插件的直接链接" title="3breaking-changes安装对应的框架适配插件的直接链接">​</a></h3><p>因为 Taro v3.4 把各前端框架的适配逻辑拆分到对应的插件中，因此旧项目升级时需要安装对应框架的适配插件：</p><ul><li>使用 React，请安装 <code>@tarojs/plugin-framework-react</code></li><li>使用 Vue2，请安装 <code>@tarojs/plugin-framework-vue2</code></li><li>使用 Vue3，请安装 <code>@tarojs/plugin-framework-vue3</code></li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="最后"><strong>最后</strong><a href="#最后" class="hash-link" aria-label="最后的直接链接" title="最后的直接链接">​</a></h2><p>接下来 Taro v3.6 的工作重心将会放在小程序性能优化、编译系统升级（如升级 Webpack5）和优化 H5 能力（如输出 SSR 方案、优化路由系统等）上。</p><p>Taro 迭代的另一条主线是对 <strong>鸿蒙应用 &amp;&amp; OpenHarmony</strong> 的适配，Taro 与 OpenHarmony 团队成立了<a href="https://gitee.com/NervJS/community/blob/master/sig/sig-crossplatformui/sig_crossplatformui_cn.md" target="_blank" rel="noopener noreferrer">跨平台 UI 兴趣组</a>，将联合社区共同展开适配工作。目前第一阶段的开发工作已完成，并发布了 Taro v3.5-canary 版本。</p><p>相关咨询：</p><ul><li><a href="https://github.com/NervJS/taro/discussions/categories/%E9%B8%BF%E8%92%99-openharmony-%E9%80%82%E9%85%8D%E5%B0%8F%E7%BB%84" target="_blank" rel="noopener noreferrer">鸿蒙 &amp;&amp; OpenHarmony 适配小组</a></li><li><a href="https://github.com/NervJS/taro/projects/2" target="_blank" rel="noopener noreferrer">适配进度</a></li></ul><p>鸿蒙 &amp; OpenHarmony 交流群：</p><p><img loading="lazy" src="http://storage.360buyimg.com/taro-jd-com/static/contact_taro_harmony_qr.png" alt="Taro X Harmony 交流群" class="img_ZOZK"></p><p>最后，衷心感谢参与了 Taro 开源共建的各位同学，也欢迎更多的同学参与进来！</p>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[如何参与大型开源项目-Taro 共建]]></title>
            <link>https://docs.taro.zone/blog/2022-01-19-how-to-join-Taro</link>
            <guid>https://docs.taro.zone/blog/2022-01-19-how-to-join-Taro</guid>
            <pubDate>Wed, 19 Jan 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[本文讲述了参与大型开源项目-Taro 的流程，其中以为 Taro 解决 issue 的视角，介绍了从认领 issue、解决 issue、贡献代码的完整过程。]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorWithStickyNavbar_mCi1" id="一背景">一、背景<a href="#一背景" class="hash-link" aria-label="一、背景的直接链接" title="一、背景的直接链接">​</a></h2><p>在开发的工作中，我们都引用过大量的社区优秀的开源项目，但怎么才能更好的了解这些开源项目呢，答案是 <strong>Join it</strong>。</p><p>参与开源项目，能够帮助我们拓宽对研发项目的认识，更好的理解开源项目的原理，以及提高个人影响力、竞争力。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="二选择项目">二、选择项目<a href="#二选择项目" class="hash-link" aria-label="二、选择项目的直接链接" title="二、选择项目的直接链接">​</a></h2><p>人对于不熟悉的东西总是觉得高深莫测的，没有参与开源项目的经验的时候，会对参与开源项目不知道从何下手。</p><p>其实不然，在我们开发日常需求时就可以参与到开源项目中来，开发时用到的技术栈，就是我们最值得入手的开源项目了。</p><p>像我自己日常有开发微信小程序、京东小程序等跨平台的需求，这类型需求主要技术栈是 <a href="https://github.com/NervJS/taro" target="_blank" rel="noopener noreferrer">Taro</a>，<a href="https://github.com/NervJS/taro" target="_blank" rel="noopener noreferrer">Taro</a> 本身就是 github star 近 30 k 的优秀开源项目了，那么我就可以从 Taro 着手，参与到开源项目的建设工作中。</p><p><img loading="lazy" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8a28ef429b4047ea838208d14143f1f5~tplv-k3u1fbpfcp-watermark.image?" alt="image.png" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="三快速开始">三、快速开始<a href="#三快速开始" class="hash-link" aria-label="三、快速开始的直接链接" title="三、快速开始的直接链接">​</a></h2><p>首先要了解、遵守开源项目的贡献规范，一般可以在官网找到贡献规范文档，如 <a href="/docs/CONTRIBUTING">Taro 贡献指南</a>。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-确定贡献形式">1. 确定贡献形式<a href="#1-确定贡献形式" class="hash-link" aria-label="1. 确定贡献形式的直接链接" title="1. 确定贡献形式的直接链接">​</a></h3><p>贡献形式多种多样，下面我以 <strong>“提交代码”</strong> 类型快速开始贡献流程。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b01d363357f24181835ac08d43196231~tplv-k3u1fbpfcp-zoom-1.image" alt="s17110101222022" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-找到感兴趣的-issue">2. 找到感兴趣的 issue<a href="#2-找到感兴趣的-issue" class="hash-link" aria-label="2. 找到感兴趣的 issue的直接链接" title="2. 找到感兴趣的 issue的直接链接">​</a></h3><blockquote><p>Taro 官网：issue 中会列出所有被标记为 good first issue 或 Helper Wanted 的 Issues，并且会被分为 Easy、 Medium、 Hard 三种难易程度。</p></blockquote><p>通过 issue 标签筛选，选择自己感兴趣的 issue，可以先从 <strong>"good first issue"</strong> 或 <strong>"Easy"</strong> 的开始：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/70b054166ff542e7be25584fde5f6de8~tplv-k3u1fbpfcp-zoom-1.image" alt="s17454801222022" class="img_ZOZK"></p><p>在 Issue 中查看有没有你感兴趣的而且可以被分配（无其他重复 Issue 且未被分配）的 Issue， Assignees 至自己。如无 Assignees 权限，可以通过 <a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests" target="_blank" rel="noopener noreferrer">Draft Pull Requests</a> 占位或在评论区中 @管理员使其分配任务。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0f1b088680d34783ac2712106b77cf4e~tplv-k3u1fbpfcp-zoom-1.image" alt="s17463701222022" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-fork--clone">3. fork &amp; clone<a href="#3-fork--clone" class="hash-link" aria-label="3. fork &amp; clone的直接链接" title="3. fork &amp; clone的直接链接">​</a></h3><p>fork Taro 源码至自己仓库：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7d23dd5050964d1ea3aa0b2f75c1d0c9~tplv-k3u1fbpfcp-zoom-1.image" alt="s17280901222022" class="img_ZOZK"></p><p>clone 个人仓库的 Taro 源码至本地：</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">git clone https://github.com/jiaozitang/taro</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="4-本地开发环境">4. 本地开发环境<a href="#4-本地开发环境" class="hash-link" aria-label="4. 本地开发环境的直接链接" title="4. 本地开发环境的直接链接">​</a></h3><p>在 Taro 源码项目中安装依赖并编译：</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain"># 安装依赖</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ yarn</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 编译</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ yarn build</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>查看该 issue 涉及哪些 package，为这些 package 设置 yarn link，并在本地编译，使得调试项目能够 link 到开发中的源码：</p><blockquote><p>Taro package 说明见文档：<a href="/docs/codebase-overview">Taro 仓库概览</a></p></blockquote><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain"># yarn link</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ cd packages/taro-components</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ yarn link</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 本地编译</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ yarn dev</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>新建 Taro 项目用于调试 Taro 源码：</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain"># 使用 npm 安装 CLI</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ npm install -g @tarojs/cli</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 初始化项目</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ taro init myApp</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># yarn link</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ yarn link "@tarojs/components"</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="5-开始开发">5. 开始开发<a href="#5-开始开发" class="hash-link" aria-label="5. 开始开发的直接链接" title="5. 开始开发的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="51-功能开发">5.1 功能开发<a href="#51-功能开发" class="hash-link" aria-label="5.1 功能开发的直接链接" title="5.1 功能开发的直接链接">​</a></h4><p>通过以下步骤解决上述 <strong><a href="https://github.com/NervJS/taro/issues/8003" target="_blank" rel="noopener noreferrer">“textarea 组件 onLineChange 时间调用无效”</a></strong> issue：</p><blockquote><p>源码路径：packages/taro-components/src/components/textarea/textarea.tsx</p></blockquote><p>onLineChange 事件：</p><ul><li>新增 onLineChange 事件</li><li>监听输入事件，输入时通过行高计算行数</li><li>行数改变时触发 onLineChange</li></ul><p>auto-height 属性：</p><ul><li>新增 auto-height 属性</li><li>新增 auto-height 样式</li></ul><p>具体代码见：<a href="https://github.com/NervJS/taro/pull/10681/files" target="_blank" rel="noopener noreferrer">https://github.com/NervJS/taro/pull/10681/files</a></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="52-更新测试用例">5.2 更新测试用例<a href="#52-更新测试用例" class="hash-link" aria-label="5.2 更新测试用例的直接链接" title="5.2 更新测试用例的直接链接">​</a></h4><p>在测试用例中添加对 onLineChange 事件、aotu-height 属性的测试。</p><blockquote><p>源码路径：packages/taro-components/<!-- -->_<!-- -->_<!-- -->tests<!-- -->_<!-- -->_<!-- -->/textarea.spec.js</p></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="53-更新文档">5.3 更新文档<a href="#53-更新文档" class="hash-link" aria-label="5.3 更新文档的直接链接" title="5.3 更新文档的直接链接">​</a></h4><p>在 README 中更新对 onLineChange、auto-height 的描述。</p><blockquote><p>源码地址：packages/taro-components/src/components/textarea/index.md</p></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="54-自测">5.4 自测<a href="#54-自测" class="hash-link" aria-label="5.4 自测的直接链接" title="5.4 自测的直接链接">​</a></h4><p>在本地测试项目 myApp 中，自测 onLineChange 事件、auto-height 属性功能是否正常，自测通过后，在 Taro 源码项目中跑自动化测试。</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain"># 自动化测试</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ npm run test</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="6-提交-pull-request">6. 提交 pull request<a href="#6-提交-pull-request" class="hash-link" aria-label="6. 提交 pull request的直接链接" title="6. 提交 pull request的直接链接">​</a></h3><p>测试通过后，在 github 中提交 pull requrst。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/711361ce7a3b4e8bb21920c717693e6e~tplv-k3u1fbpfcp-zoom-1.image" alt="s18260601222022" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="7-code-review-流程">7. code review 流程<a href="#7-code-review-流程" class="hash-link" aria-label="7. code review 流程的直接链接" title="7. code review 流程的直接链接">​</a></h3><p>提交 pull request 后等待社区 code review，并及时跟进 code review 反馈进行修改。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3ddab0a662824ad78e63b9ee2232d7c3~tplv-k3u1fbpfcp-zoom-1.image" alt="s09142901242022" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="四总结">四、总结<a href="#四总结" class="hash-link" aria-label="四、总结的直接链接" title="四、总结的直接链接">​</a></h2><p>本文讲述了参与大型开源项目-Taro 的流程，其中以为 Taro 解决 issue 的视角，介绍了从认领 issue、解决 issue、贡献代码的完整过程。</p><p>在这个过程中，我们可以了解到如何参与开源项目并帮助开源项目解决问题，在开发工作中遇到开源项目的问题时，就可以愉快的参与进来了，不用因为一个小问题耽搁项目进度。</p><p>星星之火，可以燎原，在越来越多的开发者的参与下，开源社区的发展未来可期。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="参考资料">参考资料<a href="#参考资料" class="hash-link" aria-label="参考资料的直接链接" title="参考资料的直接链接">​</a></h2><ul><li><a href="https://juejin.cn/post/6844903918749614087" target="_blank" rel="noopener noreferrer">如何参与一个顶级开源项目</a></li></ul>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[Taro v3.5 发布 canary 版本 —— 支持适配 鸿蒙]]></title>
            <link>https://docs.taro.zone/blog/2021-12-08-Taro-3.5-canary</link>
            <guid>https://docs.taro.zone/blog/2021-12-08-Taro-3.5-canary</guid>
            <pubDate>Wed, 08 Dec 2021 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2 class="anchor anchorWithStickyNavbar_mCi1" id="一背景">一、背景<a href="#一背景" class="hash-link" aria-label="一、背景的直接链接" title="一、背景的直接链接">​</a></h2><p>鸿蒙作为华为自研开发的一款可以实现万物互联的操作系统，一经推出就受到了很大的关注，被国人寄予了厚望。而鸿蒙也没让人失望，今年 Harmony2.0 正式推出供用户进行升级之后，在短短的三个月内实现了 1.2 亿的装机量，并且在前不久的华为开发者大会上，华为宣布 Harmony2.0 的装机量已经突破了 1.5 亿。</p><p>众多应用厂商都逐步推出了适配的鸿蒙应用，Taro 作为一个开放式的 跨端跨框架 解决方案，不少开发者期待将小程序的能力移植到鸿蒙 OS 上，可以使用 Taro 开发鸿蒙 &amp;&amp; OpenHarmony 应用。</p><p>鸿蒙的方舟开发框架提供类 Web 范式编程，支持使用 JS 开发 UI 层，其语法与小程序相接近。经过前期调研，可以沿用 Taro 现有的架构适配鸿蒙</p><p>今年 6 月份我们新建了支持鸿蒙的提案，希望能达成三大目标：</p><ul><li>开发者可以使用 Taro 开发鸿蒙应用。</li><li>开发者可以把现有的 Taro 应用适配到鸿蒙平台。</li><li>开发者可以使用 Taro 的反向转换工具，把原生开发的小程序转换为 Taro 应用，再适配到鸿蒙平台。</li></ul><p>目前 Taro 和 OpenHarmony 建立了官方合作关系，受邀并成立了<a href="https://gitee.com/tangjiao11/community/blob/master/sig/sig-crossplatformui/sig_crossplatformui_cn.md" target="_blank" rel="noopener noreferrer">跨平台前端框架兴趣小组（SIG-CROSS-PLATFORM-UI）</a>，同时 Taro 与华为鸿蒙 &amp;&amp; OpenHarmony 保持着内部沟通与分享，Taro 拥有的海量开发者和优秀案例，能有效补充华为鸿蒙 &amp;&amp; OpenHarmony 生态。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="二实现细节">二、实现细节<a href="#二实现细节" class="hash-link" aria-label="二、实现细节的直接链接" title="二、实现细节的直接链接">​</a></h2><p>鸿蒙的 JS UI 语法与小程序类似，但毕竟两者底层原理不一样，不可避免地存在许多差异。接下来将简单介绍鸿蒙与小程序的主要差异，和 Taro 又是如何处理这些差异的。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-鸿蒙与小程序的异同">1. 鸿蒙与小程序的异同<a href="#1-鸿蒙与小程序的异同" class="hash-link" aria-label="1. 鸿蒙与小程序的异同的直接链接" title="1. 鸿蒙与小程序的异同的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="11-项目组织">1.1 项目组织<a href="#11-项目组织" class="hash-link" aria-label="1.1 项目组织的直接链接" title="1.1 项目组织的直接链接">​</a></h4><p>鸿蒙的项目组织和小程序类似，有入口文件 <code>app.js</code> 、页面、自定义组件。</p><p>其中页面、自定义组件均由三类文件组成：</p><ul><li><code>.hml</code> 用来描述布局结构。与小程序的模板文件相比，语法、支持的能力有少许区别。</li><li><code>.css</code> 用来描述页面样式。</li><li><code>.js</code> 用于处理页面和用户的交互，默认支持 ES6 语法。</li></ul><h4 class="anchor anchorWithStickyNavbar_mCi1" id="12-配置文件">1.2 配置文件<a href="#12-配置文件" class="hash-link" aria-label="1.2 配置文件的直接链接" title="1.2 配置文件的直接链接">​</a></h4><p>和小程序规定的入口文件、页面文件、自定义组件各自对应一份配置文件不一样，鸿蒙 JS UI 的配置文件只有一份。</p><p>鸿蒙的<strong>路由</strong>和小程序一样是配置式的，需要在 JS UI 的配置文件中进行配置。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="13-样式">1.3 样式<a href="#13-样式" class="hash-link" aria-label="1.3 样式的直接链接" title="1.3 样式的直接链接">​</a></h4><p>CSS 方面，鸿蒙和 RN 一样有着诸多限制。例如不支持盒子模型、各组件只支持部分 CSS 属性等。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="14-组件--api">1.4 组件 &amp; API<a href="#14-组件--api" class="hash-link" aria-label="1.4 组件 &amp; API的直接链接" title="1.4 组件 &amp; API的直接链接">​</a></h4><p>鸿蒙提供了一系列功能丰富的组件，与小程序的组件相比，命名、功能上略有差别。</p><p>API 也是一样的，两者的 API 集合有部分交集，用法、功能上有差别。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-兼容细节">2. 兼容细节<a href="#2-兼容细节" class="hash-link" aria-label="2. 兼容细节的直接链接" title="2. 兼容细节的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="21-taro-可以解决什么">2.1 Taro 可以解决什么？<a href="#21-taro-可以解决什么" class="hash-link" aria-label="2.1 Taro 可以解决什么？的直接链接" title="2.1 Taro 可以解决什么？的直接链接">​</a></h4><p>Taro 适配鸿蒙致力于尽可能地抹平差异，但作为一个框架，注定有它能够解决和不能解决的问题。</p><p>语法差异可以通过编写运行时框架去处理；使用鸿蒙的组件、API 去尽可能地实现微信小程序规范的组件和 API，以抹平两者之间的使用差别。</p><p>而 CSS 的差异、组件和 API 能力上的差异等依赖着鸿蒙底层实现，Taro 是无法解决的。</p><p><img loading="lazy" src="https://img11.360buyimg.com/imagetools/jfs/t1/207457/5/11670/466511/61b05b41E43730965/de2d1029083cc60d.png" alt="适配方案" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="22-鸿蒙插件">2.2 鸿蒙插件<a href="#22-鸿蒙插件" class="hash-link" aria-label="2.2 鸿蒙插件的直接链接" title="2.2 鸿蒙插件的直接链接">​</a></h4><p>Taro 在鸿蒙方面的兼容工作主要由 <code>@tarojs/plugin-platform-harmony</code> 插件来完成，开发者引入该插件即可编译为鸿蒙应用。它主要做了这些适配工作：</p><p><strong>a) 模板</strong></p><p>熟悉 Taro 的同学都应该清楚，Taro 在小程序端利用 <code>&lt;template&gt;</code> 标签的递归来渲染页面动态的 DOM 树。而鸿蒙中并没有 <code>&lt;template&gt;</code> ，因此我们使用自定义组件进行递归。</p><p><strong>b) 运行时</strong></p><p>运行时主要在鸿蒙端兼容了小程序的生命周期和数据更新方法 <code>setData</code> 。</p><p><strong>c) 组件 &amp; API</strong></p><p>我们使用鸿蒙的原生语法封装了符合微信小程序规范的组件库和 API 库。在兼容微信小程序的属性的同时，也保留了鸿蒙独有的支持属性。</p><p>目前共适配了 29 个组件，16 类 API。组件示例库可参考：<a href="https://github.com/NervJS/taro-components-sample/tree/harmony" target="_blank" rel="noopener noreferrer">taro-components-sample</a></p><p><img loading="lazy" src="https://img14.360buyimg.com/imagetools/jfs/t1/137976/7/22742/2461697/61b17384Ebbb815c0/4949c696d9a49283.jpg" alt="组件示例图" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-架构图">3. 架构图<a href="#3-架构图" class="hash-link" aria-label="3. 架构图的直接链接" title="3. 架构图的直接链接">​</a></h3><p><img loading="lazy" src="https://img14.360buyimg.com/imagetools/jfs/t1/215443/28/7034/1135729/61b05b42E21c6230a/5e8fc76941f98920.png" alt="架构图" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="三使用方法">三、使用方法<a href="#三使用方法" class="hash-link" aria-label="三、使用方法的直接链接" title="三、使用方法的直接链接">​</a></h2><p>如您是新项目， 升级 Taro 选择鸿蒙模板即可；</p><p>旧项目需要按照如下方法进行手动配置：</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-把-taro-升级到-v350-canary0-版本">1. 把 Taro 升级到 v3.5.0-canary.0 版本<a href="#1-把-taro-升级到-v350-canary0-版本" class="hash-link" aria-label="1. 把 Taro 升级到 v3.5.0-canary.0 版本的直接链接" title="1. 把 Taro 升级到 v3.5.0-canary.0 版本的直接链接">​</a></h3><p>首先需要安装 <code>v3.5.0-canary.0</code> 的 CLI 工具</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">npm i </span><span class="token operator" style="color:#393A34">-</span><span class="token plain">g @tarojs</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">cli@canary</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>然后更新项目本地的 Taro 相关依赖：把 &nbsp;<code>package.json</code>&nbsp; 文件中 Taro 相关依赖的版本修改为 &nbsp;<code>~3.5.0-canary.0</code>，再重新安装依赖。</p><blockquote><p>如果安装失败或打开项目失败，可以删除 &nbsp;<strong>node_modules</strong>、<strong>yarn.lock</strong>、<strong>package-lock.json</strong>&nbsp; 后重新安装依赖再尝试。</p></blockquote><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-安装-taro-适配鸿蒙插件">2. 安装 taro 适配鸿蒙插件<a href="#2-安装-taro-适配鸿蒙插件" class="hash-link" aria-label="2. 安装 taro 适配鸿蒙插件的直接链接" title="2. 安装 taro 适配鸿蒙插件的直接链接">​</a></h3><p>（1）Taro 项目中安装鸿蒙插件 <code>@tarojs/plugin-platform-harmony</code></p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">$ yarn add </span><span class="token operator" style="color:#393A34">--</span><span class="token plain">dev @tarojs</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">plugin</span><span class="token operator" style="color:#393A34">-</span><span class="token plain">platform</span><span class="token operator" style="color:#393A34">-</span><span class="token plain">harmony</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>（2）在 config/index.js 中增加编译配置</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OMZr">config/index.js</div><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 配置使用插件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">plugins</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'@tarojs/plugin-platform-harmony'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">mini</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 如果使用开发者工具的预览器（previewer）进行预览的话，需要使用 development 版本的 react-reconciler。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 因为 previewer 对长串的压缩文本解析有问题。（真机/远程真机没有此问题）</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">debugReact</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 如果需要真机断点调试，需要关闭 sourcemap 的生成</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">enableSourceMap</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// harmony 相关配置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">harmony</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 【必填】鸿蒙应用的绝对路径</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">projectPath</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> path</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">resolve</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">cwd</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'../MyApplication'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 【可选】HAP 的名称，默认为 'entry'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">hapName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'entry'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 【可选】JS FA 的名称，默认为 'default'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">jsFAName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'default'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-准备鸿蒙运行环境">3. 准备鸿蒙运行环境<a href="#3-准备鸿蒙运行环境" class="hash-link" aria-label="3. 准备鸿蒙运行环境的直接链接" title="3. 准备鸿蒙运行环境的直接链接">​</a></h3><blockquote><p>开发鸿蒙软件需要用到 HUAWEI DevEco Studio，它提供了模板创建、开发、编译、调试、发布等服务。</p></blockquote><p>主要包括以下内容：</p><p>（1）注册开发者账号</p><p>（2）下载 DevEco Studio 安装包</p><p>（3）启动 DevEco Studio，根据工具引导下载 HarmonyOS SDK</p><p>（4）新建 MyApplication JS 项目</p><p>（5）使用预览器或真机查看应用效果</p><blockquote><p><a href="https://juejin.cn/post/6972109475347955749" target="_blank" rel="noopener noreferrer">《初窥鸿蒙》</a> <a href="https://developer.harmonyos.com/cn/develop/deveco-studio" target="_blank" rel="noopener noreferrer">《华为开发者工具》</a><a href="https://developer.harmonyos.com/cn/documentation" target="_blank" rel="noopener noreferrer">《鸿蒙开发文档》</a></p></blockquote><h3 class="anchor anchorWithStickyNavbar_mCi1" id="4-项目运行">4. 项目运行<a href="#4-项目运行" class="hash-link" aria-label="4. 项目运行的直接链接" title="4. 项目运行的直接链接">​</a></h3><p>运行命令</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">$ taro build —</span><span class="token operator" style="color:#393A34">-</span><span class="token plain">type harmony —</span><span class="token operator" style="color:#393A34">-</span><span class="token plain">watch</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>若您在步骤 2(2) 设置好了打包输出到鸿蒙项目的路径，即可查看 Taro 适配鸿蒙的应用效果。</p><blockquote><p>testHarmony 为您通过 DevEco Studio 创建的 JS 项目。</p></blockquote><p><img loading="lazy" src="https://img13.360buyimg.com/imagetools/jfs/t1/218013/26/7034/2593615/61b05b43Ec927d51c/f2990eef38343eca.png" alt="运行效果图" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="四最后">四、最后<a href="#四最后" class="hash-link" aria-label="四、最后的直接链接" title="四、最后的直接链接">​</a></h2><p>接下来我们会继续完善对鸿蒙的适配工作，预计会在 2022 年 Q1 发布 v3.5 正式版。</p><p>同时也希望社区有更多的开发者参与共建，无论是提出 Issues、在论坛发帖、提交 PR 还是帮助建设周边生态等，对我们来说都是宝贵的财富，让我们一起把 Taro 建设的更强。</p><p>Taro 团队衷心感谢一路走来大家对我们的支持，正是因为大家的期待、信赖敦促我们走向更好。</p><p>最后，该版本鸿蒙的适配由京东内鸿蒙共建小组共同完成，感谢以下同学：<a href="https://github.com/AdvancedCat" target="_blank" rel="noopener noreferrer">@AdvancedCat</a>、<a href="https://github.com/jiaozitang" target="_blank" rel="noopener noreferrer">@jiaozitang</a>、<a href="https://github.com/huozhongyi123" target="_blank" rel="noopener noreferrer">@huozhongyi123</a>、<a href="https://github.com/troy-sxj" target="_blank" rel="noopener noreferrer">@troy-sxj</a>、<a href="https://github.com/JSZabc" target="_blank" rel="noopener noreferrer">@JSZabc</a>、<a href="https://github.com/crazyonebyone" target="_blank" rel="noopener noreferrer">@crazyonebyone</a>、<a href="https://github.com/evernoteHW" target="_blank" rel="noopener noreferrer">@evernoteHW</a>、<a href="https://github.com/soulhat" target="_blank" rel="noopener noreferrer">@soulhat</a>、<a href="https://github.com/xueshuai" target="_blank" rel="noopener noreferrer">@xueshuai</a>、<a href="https://github.com/LuMeiling" target="_blank" rel="noopener noreferrer">@LuMeiling</a></p>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[Taro v3.4 发布 beta 版本 —— 支持使用 Preact 进行开发]]></title>
            <link>https://docs.taro.zone/blog/2021-11-24-Taro-3.4-beta</link>
            <guid>https://docs.taro.zone/blog/2021-11-24-Taro-3.4-beta</guid>
            <pubDate>Wed, 24 Nov 2021 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>项目体积是困扰小程序开发者的一大问题，如果开发者使用 Taro React 进行开发，更是不得不引入接近 100K 的 React 相关依赖，这让项目体积变得更加捉襟见肘。因此，Taro v3.4 的主要方向，是探索对于 Preact 的支持。</p><blockquote><p><a href="https://preactjs.com/" target="_blank" rel="noopener noreferrer">Preact</a> 是一款体积超小的类 React 框架，提供和 React 几乎一致的 API，而体积只有 5k 左右。</p></blockquote><h2 class="anchor anchorWithStickyNavbar_mCi1" id="支持使用-preact">支持使用 Preact<a href="#支持使用-preact" class="hash-link" aria-label="支持使用 Preact的直接链接" title="支持使用 Preact的直接链接">​</a></h2><p>Taro v3.4 正式实现了对 Preact 的支持，下文将简单介绍适配思路及用法。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="适配思路">适配思路<a href="#适配思路" class="hash-link" aria-label="适配思路的直接链接" title="适配思路的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="1-运行时改造">1. 运行时改造<a href="#1-运行时改造" class="hash-link" aria-label="1. 运行时改造的直接链接" title="1. 运行时改造的直接链接">​</a></h4><p>Taro 在小程序环境模拟实现了<a href="/docs/next/implement-note#%E8%BF%90%E8%A1%8C%E6%97%B6">类浏览器环境</a>，因此理论上任意的前端框架都可以在 Taro 中使用。</p><p>对于 Preact，它与 React 最大的不同在于没有实现<a href="https://reactjs.org/docs/events.html" target="_blank" rel="noopener noreferrer">合成事件系统</a>，而是直接使用浏览器的事件方法，此外还使用了少量和 React 不一样的 DOM API。</p><p>对于事件的适配，Taro 已经提供了浏览器规范的事件方法，因此只需要再处理 Preact 的事件名与小程序事件名的差异。而对于 DOM 方法，则需要额外实现 Preact 使用到的 DOM API。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="2-兼容-react-生态">2. 兼容 React 生态<a href="#2-兼容-react-生态" class="hash-link" aria-label="2. 兼容 React 生态的直接链接" title="2. 兼容 React 生态的直接链接">​</a></h4><p>Preact 使用了 <code>preact/compat</code> 去磨平与 React 的 API 差异，让 React 的各种生态库可以直接运行在 Preact 上。得益于此，开发时我们可以使用任意的 React 生态库，甚至对 React、ReactDOM 的 API 引用也不需要修改，只需要简单地配置 <a href="https://preactjs.com/guide/v10/switching-to-preact#setting-up-compat" target="_blank" rel="noopener noreferrer">alias</a> 即可：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Webpack config</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">resolve</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">alias</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">react</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'preact/compat'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string-property property" style="color:#36acaa">'react-dom/test-utils'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'preact/test-utils'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string-property property" style="color:#36acaa">'react-dom'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'preact/compat'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string-property property" style="color:#36acaa">'react/jsx-runtime'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'preact/jsx-runtime'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="用法介绍">用法介绍<a href="#用法介绍" class="hash-link" aria-label="用法介绍的直接链接" title="用法介绍的直接链接">​</a></h3><blockquote><p><a href="/docs/next/preact">文档地址</a></p></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="新项目">新项目<a href="#新项目" class="hash-link" aria-label="新项目的直接链接" title="新项目的直接链接">​</a></h4><p>运行 <code>taro init</code> 时，框架选择 <strong>Preact</strong> 即可创建基于 Preact 的项目。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="现有的-react-项目">现有的 React 项目<a href="#现有的-react-项目" class="hash-link" aria-label="现有的 React 项目的直接链接" title="现有的 React 项目的直接链接">​</a></h4><ol><li><p>将 CLI、项目中 Taro 相关的依赖更新到 <code>v3.4.0-beta</code> 版本。</p></li><li><p>安装依赖：</p></li></ol><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">yarn</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> preact @tarojs/plugin-framework-react</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ol start="3"><li>修改 Taro 编译配置：</li></ol><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OMZr">config/index.js</div><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">framework</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'preact'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ol start="4"><li>修改 Babel 配置：</li></ol><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OMZr">babel.config.js</div><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">presets</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">      </span><span class="token string" style="color:#e3116c">'taro'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">framework</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'preact'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ol start="5"><li>如果项目使用了 TypeScript，请打开 <code>skipLibCheck</code> 配置，以避免和其它 React 生态库配合使用时报类型错误：</li></ol><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OMZr">tsconfig.json</div><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line theme-code-block-highlighted-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">"skipLibCheck"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="vue-3-支持-composition-api-版本的小程序生命周期钩子">Vue 3 支持 Composition API 版本的小程序生命周期钩子<a href="#vue-3-支持-composition-api-版本的小程序生命周期钩子" class="hash-link" aria-label="Vue 3 支持 Composition API 版本的小程序生命周期钩子的直接链接" title="Vue 3 支持 Composition API 版本的小程序生命周期钩子的直接链接">​</a></h2><blockquote><p><a href="/docs/next/composition-api">文档地址</a></p></blockquote><p>Vue3 提供了 <a href="https://v3.vuejs.org/guide/composition-api-introduction.html#why-composition-api" target="_blank" rel="noopener noreferrer">Composition API（组合式 API）</a> 特性，和传统的 Options API 不同，Composition API 提供了全新的编码方式 ，可以让我们更好地去组织和复用代码逻辑。</p><p>过去 Taro 只提供了 Options API 版本的小程序生命周期钩子，开发者往往对于这些钩子和 <code>setup</code> 函数内部该如何通讯、如何共享数据等问题感到困惑，更是不能很好地兼容 <a href="https://v3.vuejs.org/api/sfc-script-setup.html#basic-syntax" target="_blank" rel="noopener noreferrer">script setup</a> 语法。</p><p>因此 Taro v3.4 提供了 Composition API 版本的小程序生命周期钩子，让开发者更方便地使用 <code>setup</code> 语法，例子：</p><div class="language-html codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-html codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">script</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">setup</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">  </span><span class="token script language-javascript keyword module" style="color:#00009f">import</span><span class="token script language-javascript"> </span><span class="token script language-javascript imports punctuation" style="color:#393A34">{</span><span class="token script language-javascript imports"> useDidShow </span><span class="token script language-javascript imports punctuation" style="color:#393A34">}</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword module" style="color:#00009f">from</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'@tarojs/taro'</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">  </span><span class="token script language-javascript function" style="color:#d73a49">useDidShow</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"> </span><span class="token script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token script language-javascript"> </span><span class="token script language-javascript console class-name">console</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">log</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'onShow'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="运行时体积优化">运行时体积优化<a href="#运行时体积优化" class="hash-link" aria-label="运行时体积优化的直接链接" title="运行时体积优化的直接链接">​</a></h2><p>目前 Taro 对于前端框架的适配层代码都放在了运行时库 <code>@tarojs/runtime</code> 里，意思即当开发者使用 React 时，还是会包含 Vue2、Vue3 的适配层代码。（Tree Shaking 失败的原因是使用了 Webpack Provider Plugin 导出 <code>@tarojs/runtime</code> 里的 BOM API）</p><p>因此，Taro v3.4 以 Taro 插件的形式去实现对于各前端框架的适配，其中一个好处是可以把上述运行时适配层的代码拆分到各个插件内。加上对运行时代码的写法优化，3.4 版本的运行时减少了约 <strong>30k</strong> 的空间。</p><p>另一个好处是现在开发者可以通过编写 Taro 插件去支持任意的前端框架，而几乎不需要改动 Taro 的核心代码。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="升级指南">升级指南<a href="#升级指南" class="hash-link" aria-label="升级指南的直接链接" title="升级指南的直接链接">​</a></h2><h4 class="anchor anchorWithStickyNavbar_mCi1" id="1-安装-v340-beta-的-cli-工具">1. 安装 <code>v3.4.0-beta</code> 的 CLI 工具：<a href="#1-安装-v340-beta-的-cli-工具" class="hash-link" aria-label="1-安装-v340-beta-的-cli-工具的直接链接" title="1-安装-v340-beta-的-cli-工具的直接链接">​</a></h4><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i -g @tarojs/cli@beta</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="2-更新项目依赖">2. 更新项目依赖<a href="#2-更新项目依赖" class="hash-link" aria-label="2. 更新项目依赖的直接链接" title="2. 更新项目依赖的直接链接">​</a></h4><blockquote><p>如果安装失败或打开项目失败，可以删除 <strong>node_modules</strong>、<strong>yarn.lock</strong>、<strong>package-lock.json</strong> 后重新安装依赖再尝试。</p></blockquote><p>修改 <code>package.json</code> 文件中 Taro 相关依赖的版本修改为 <code>~3.4.0-beta.0</code>，再重新安装依赖。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="3breaking-changes安装对应的框架适配插件">3.【Breaking Changes】安装对应的框架适配插件<a href="#3breaking-changes安装对应的框架适配插件" class="hash-link" aria-label="3.【Breaking Changes】安装对应的框架适配插件的直接链接" title="3.【Breaking Changes】安装对应的框架适配插件的直接链接">​</a></h4><p>因为 Taro v3.4 把各前端框架的适配逻辑拆分到对应的插件中，因此旧项目升级时需要安装对应框架的适配插件：</p><ul><li>使用 React，请安装 <code>@tarojs/plugin-framework-react</code></li><li>使用 Vue2，请安装 <code>@tarojs/plugin-framework-vue2</code></li><li>使用 Vue3，请安装 <code>@tarojs/plugin-framework-vue3</code></li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="其他">其他<a href="#其他" class="hash-link" aria-label="其他的直接链接" title="其他的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="breaking-changes">Breaking Changes<a href="#breaking-changes" class="hash-link" aria-label="Breaking Changes的直接链接" title="Breaking Changes的直接链接">​</a></h3><ul><li>百度小程序使用 <code>onInit</code> 代替 <code>onLoad</code> 生命周期，以优化首次启动时间。</li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="最后">最后<a href="#最后" class="hash-link" aria-label="最后的直接链接" title="最后的直接链接">​</a></h2><p>接下来 Taro 的重心将会放在编译系统升级（如升级 Webpack5）和优化 H5 能力（如输出 SSR 方案、优化路由系统等）上。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="鸿蒙应用--openharmony">鸿蒙应用 &amp;&amp; OpenHarmony<a href="#鸿蒙应用--openharmony" class="hash-link" aria-label="鸿蒙应用 &amp;&amp; OpenHarmony的直接链接" title="鸿蒙应用 &amp;&amp; OpenHarmony的直接链接">​</a></h3><p>Taro 迭代的另一条主线是对<strong>鸿蒙应用 &amp;&amp; OpenHarmony</strong> 的适配，Taro 与 OpenHarmony 团队成立了跨平台 UI 兴趣组（SIG-CROSS-PLATFORM-UI），联合社区共同展开适配工作。目前第一阶段的开发工作即将完成，12 月初会发布首个可用的体验版本。</p><p>相关咨询：</p><ul><li><a href="https://github.com/NervJS/taro/discussions/categories/%E9%B8%BF%E8%92%99-openharmony-%E9%80%82%E9%85%8D%E5%B0%8F%E7%BB%84" target="_blank" rel="noopener noreferrer">鸿蒙 &amp;&amp; OpenHarmony 适配小组</a></li><li><a href="https://github.com/NervJS/taro/projects/2" target="_blank" rel="noopener noreferrer">适配进度</a></li></ul><p>鸿蒙 &amp; OpenHarmony 交流群：</p><p><img loading="lazy" src="http://storage.360buyimg.com/taro-jd-com/static/contact_taro_harmony_qr.png" class="img_ZOZK"></p>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[Taro React Native 重大更新：帮助开发者高效开发APP]]></title>
            <link>https://docs.taro.zone/blog/2021-10-14-Taro-React-Native-update</link>
            <guid>https://docs.taro.zone/blog/2021-10-14-Taro-React-Native-update</guid>
            <pubDate>Thu, 14 Oct 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[三个方向去优化开发流程，全面降低上手成本，让 Taro 开发 APP，变得无比轻松。]]></description>
            <content:encoded><![CDATA[<p>Taro React Native 重大更新来了，全方位降低上手成本，提升开发体验。全流程自动化，让开发者摆脱原生环境配置，专注前端开发。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="背景">背景<a href="#背景" class="hash-link" aria-label="背景的直接链接" title="背景的直接链接">​</a></h2><p>Taro 3.2.0 正式版本发布至今，已过去半年。在此期间，有不少社区开发者已经使用上 Taro 来开发 APP 了。看到社区的使用量越来越多，开发团队也是收获满满。</p><p>同时我们也收到了很多来自开发者的反馈， 主要集中于开发环境配置复杂、组件和 API 的完善度不够及使用上的 BUG 等。对于组件和 API 的完善度及使用上的 BUG，我们都是尽可能地及时地处理并发布新版本。然而，对于开发者反馈的开发环境配置的问题，却很难复现及解决。</p><p>首先 Android + iOS + React Native + Taro，4 个技术的各种环境配置，会让很多开发者望而却步。其次开发者面对的环境问题千奇百怪，很多问题难以通过远程协助解决。不少开发者在调研阶段，因为无法顺利运行，便放弃了使用。对于一个跨平台框架来说，主要目的是提效，而非给开发者带来更多困难。开发环境配置问题的解决，显得尤为重要。</p><p>这次我们从以下三个方向去优化整个开发流程，全面降低上手成本，让 Taro 开发 APP，变得无比轻松。</p><ol><li>为 Taro 提供 react-native 模板，项目的初始化，只需要几个命令。</li><li>与 GitHub Actions 进行集成，不再需要本地安装原生开发环境，打包及发布交给 CI 去做。</li><li>提供 Taro Playground APP，可以通过应用商店或者 GitHub 下载安装，进行项目调试。</li></ol><h2 class="anchor anchorWithStickyNavbar_mCi1" id="react-native-开发模板">react-native 开发模板<a href="#react-native-开发模板" class="hash-link" aria-label="react-native 开发模板的直接链接" title="react-native 开发模板的直接链接">​</a></h2><p>目前使用 Taro 开发 React Native APP 时，我们需要一个原生壳工程，在另外一个仓库<!-- -->[1]<!-- -->。对于新手来说，通常会造成一些困惑：</p><ol><li>没有接触过 React Native 开发的开发者首先需要理解 React Native 的开发流程，然后完成两个仓库的初始化。</li><li>两个仓库都需要安装依赖，并且需要保持某些依赖版本的一致性。当有依赖更新时，需要在两个仓库中进行操作，非常容易遗漏。</li><li>项目依赖原生的运行环境。开发者经常遇到安装过程报错，无法运行的场景。一些依赖包的下载需要切换源或依赖特定网络环境。</li></ol><p>这些问题对新手入门很不友好，为此我们提供了一个初始化模板<!-- -->[2]<!-- -->。初始化项目使用 <code>taro init [project]</code> 选择 react-native 模板。</p><p>初始化完成后，便可使用进入开发。以下为一些常用命令：</p><div class="language-shell codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-shell codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 更新相关依赖。在初始化完成后或 Taro 版本更新后执行，用于同步 peerDependencies。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">yarn</span><span class="token plain"> upgradePeerdeps</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 打包 js bundle 及静态资源。在初始化完成后执行，用于打包默认使用的 bundle。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">yarn</span><span class="token plain"> build:rn --platform ios</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 启动 bundle server</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">yarn</span><span class="token plain"> start</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 启动 iOS</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">yarn</span><span class="token plain"> ios</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 启动安卓</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">yarn</span><span class="token plain"> android</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>具体操作可以查看录屏：</p><video controls="" width="600" src="https://wos2.58cdn.com.cn/DeFazYxWvDti/frsupload/6820cfd5e0346eac050e7c3f0df78f65.mp4"></video><p>通过模板方式进行初始化的项目，有几个优势：</p><ol><li>Taro 仓库与壳工程仓库进行整合，不再需要管理双仓库。当然习惯双仓库模式的开发者，仍然可以正常使用。</li><li>当 Taro 进行升级时，可以通过执行 <code>yarn upgradePeerdeps</code>进行依赖同步。这里我们将 Taro 依赖的 React Native 相关库写入了 peerDependencies 中，然后通过 install-peerdeps 去完成依赖的同步。</li><li>集成了 GitHub Actions，可通过 workflow 完成 APP 的打包。</li></ol><h2 class="anchor anchorWithStickyNavbar_mCi1" id="github-actions">GitHub Actions<a href="#github-actions" class="hash-link" aria-label="GitHub Actions的直接链接" title="GitHub Actions的直接链接">​</a></h2><p>要解决开发环境的各种问题，通常的做法就是提供一个稳定的环境用于打包发布，在企业里是各种 CI/CD 平台。但对于开源项目来讲，就需要一个公开的平台，每个人都能使用，当然最好是免费的。于是我们想到了 GitHub Actions。</p><p>GitHub Actions 是 GitHub 提供的持续集成服务，于 2018 年 10 月推出。功能非常强大，并且免费（每月有限额），同时私有仓库也能够使用，非常契合我们的需求。</p><p>通过模板初始化的项目，可在 <code>.github/workflows</code> 目录中看到 4 个文件。分别为 iOS 和 Android 的 release 包和 debug 包的打包工作流。模板为了简化过程，设置为通过 <code>git push</code> 即可触发打包，可根据自身情况，配置合适自身业务场景的工作流。打包生成的产物可以在 Artifacts 中找到，也可以使用 <code>softprops/action-gh-release@v1</code> action，将产物发布到项目的 Release 中。</p><img loading="lazy" referrerpolicy="no-referrer" src="https://pic4.58cdn.com.cn/nowater/fangfe/n_v275b68e363e1d459ead128302fe0f8581.jpg" width="600px" class="img_ZOZK"><p>这样一来新手便可以不需要关注原生环境以及 APP 打包的问题。开发时，可以安装 debug 包加载本地的 jsbundle，进行调试。发布时，交给 CI 进行打包，产物再提交到应用市场，整个过程完全不需要 AS 与 XCode。当然这里还有一些必要配置需要做，比如 APP 的签名等，将在后面的章节讲解。</p><p>GitHub Actions 功能非常强大，Taro 就用它来做打包发布等工作，可参考文档<!-- -->[3]<!-- -->或查看资料做进一步探索，做点有趣的事情。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="github-actions-配置">GitHub Actions 配置<a href="#github-actions-配置" class="hash-link" aria-label="GitHub Actions 配置的直接链接" title="GitHub Actions 配置的直接链接">​</a></h2><p>在 Taro 项目模板里面我们提供了一个 CI 脚本模板，开发者仍然需要进行一些配置，才能够开始打包。下面是打包 Android APP 的基础配置说明，iOS 同理：</p><ul><li><p>配置打包的环境变量</p><div class="language-yml codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-yml codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">env</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">APP_ID</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> com.taro.demo </span><span class="token comment" style="color:#999988;font-style:italic"># 应用 ID</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">APP_NAME</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Taro Demo </span><span class="token comment" style="color:#999988;font-style:italic"># 应用名称</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">VERSION_NAME</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 1.0.0 </span><span class="token comment" style="color:#999988;font-style:italic"># 应用版本号</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">VERSION_CODE</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># 用于应用市场、程序内部识别版本，判断新旧版本，一般递增处理</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">KEYSTORE_FILE</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> debug.keystore </span><span class="token comment" style="color:#999988;font-style:italic"># 签名文件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">KEYSTORE_PASSWORD</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> android </span><span class="token comment" style="color:#999988;font-style:italic"># 密码</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">KEYSTORE_KEY_ALIAS</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> androiddebugkey </span><span class="token comment" style="color:#999988;font-style:italic"># 别名</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">KEYSTORE_KEY_PASSWORD</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> android </span><span class="token comment" style="color:#999988;font-style:italic"># 别名的密码</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>通过 github secrets 管理秘钥配置</p><img loading="lazy" referrerpolicy="no-referrer" src="https://pic8.58cdn.com.cn/nowater/fangfe/n_v257e556d325fa4ee99a96e3a46a976f18.png" width="600px" class="img_ZOZK"><p>通常我们不应该把密钥等敏感信息直接写在配置文件中，而是置于加密信息中。在 GitHub Actions 中，可以使用加密机制进行处理<!-- -->[4]<!-- -->。如图，在 <code>setting</code> -&gt; <code>secret</code> 配上 CI 需要的 secret。然后在 workflow 中通过相应变量进行使用，如 <code>${{secrets.DEBUG_KEYSTORE_PASSWORD}}</code>。</p></li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="壳工程-github-actions-方案">壳工程 GitHub Actions 方案<a href="#壳工程-github-actions-方案" class="hash-link" aria-label="壳工程 GitHub Actions 方案的直接链接" title="壳工程 GitHub Actions 方案的直接链接">​</a></h2><p>对于壳工程与项目工程分开的场景，利用 CI 命令将两个项目进行合并也可以实现打包自动化。具体流程如下：</p><ol><li><p>壳工程和业务项目合并</p><p>因为 GitHub Actions 只能在当前项目下进行操作，所以需要将壳工程（taro-native-shell）合并到项目工程下。</p></li><li><p>合并项目和壳工程的 package.json</p><p>在有原生依赖的情况下，必须保证壳工程和业务项目的原生依赖版本一致，不然打包可能会报错。</p></li><li><p>安装依赖</p><p>在业务项目工程下安装合并后的 package.json 依赖。</p></li><li><p>软链依赖</p><p>将安装到业务项目下的依赖软链至壳工程项目 node_module 下。</p><div class="language-sh codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-sh codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">ln -s ./node_modules ./taro-native-shell/node_modules</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>业务项目编译</p><p>执行 <code>taro/cli build:rn</code> 编译命令，打包生成 jsbundle 与静态资源。</p></li><li><p>将编译产物移动到原生壳工程</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token literal-property property" style="color:#36acaa">rn</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">appName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'taroDemo'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">output</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">ios</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./ios/main.jsbundle'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">iosAssetsDest</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./ios'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">android</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./android/app/src/main/assets/index.android.bundle'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">androidAssetsDest</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./android/app/src/main/res'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">iosSourcemapOutput</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./ios/main.map'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">androidSourcemapOutput</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./android/app/src/main/assets/index.android.map'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>taro 编译 rn 输出静态资源，需要将资源移到原生项目中。</p></li><li><p>编译原生 APP</p><p>到 ios 和 android 目录里分别执行对应的打包命令。</p></li><li><p>上传 APP</p><p>将打包后的 APP 进行上传，提供下载链接。</p><div class="language-yml codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-yml codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># iOS</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Upload iOS Products</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">uses</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> actions/upload</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">artifact@v2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">with</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> app</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">$</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> env.BUILD_TYPE </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">path</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">|</span><span class="token scalar string" style="color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">      ${{ github.workspace }}/ios/taroDemo.ipa</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">      ${{ github.workspace }}/ios/taroDemo.app.dSYM.zip</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="language-yml codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-yml codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Android</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Upload Android Products</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">uses</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> actions/upload</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">artifact@v2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">with</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> app</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">$</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> env.BUILD_TYPE </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">path</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> github.workspace </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain">/android/app/build/outputs/apk/$</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> env.BUILD_TYPE </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain">/app</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">$</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> env.BUILD_TYPE </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain">.apk</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在 iOS 侧，release workflow 还集成了上传至 APP Store 命令：</p><div class="language-yml codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-yml codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Upload app to App Store Connect</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">env</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">APP_STORE_CONNECT_USERNAME</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> env.APP_STORE_CONNECT_USERNAME </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">APP_STORE_CONNECT_PASSWORD</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> env.APP_STORE_CONNECT_PASSWORD </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">run</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">|</span><span class="token scalar string" style="color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">    cd ios</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">    xcrun altool --upload-app -t ios -f "taroDemo.ipa" -u "$APP_STORE_CONNECT_USERNAME" -p "$APP_STORE_CONNECT_PASSWORD"</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li></ol><p>上面整个流程对于开发者来说理解成本太高，配置过于繁琐，所以我们将前 6 个步骤封装成一个 GitHub action<!-- -->[5]<!-- -->，开发者只需要添加一些配置项就能完成上面的流程。</p><div class="language-yml codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-yml codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> taro</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">native</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">publish</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">uses</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> shinken008/taro</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">native</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">publish@0.4.0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">with</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">REPO</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> env.SHELL_REPO </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">REPO_REF</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> env.SHELL_REPO_REF </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">REPO_PATH</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> taro</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">native</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">shell</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">BUILD_CMD</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> yarn build</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">rn</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">IOS_BUNDLE</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ios/main.jsbundle</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">IOS_ASSETS</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ios</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ANDROID_BUNDLE</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> android/index.android.bundle</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ANDROID_ASSETS</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> android</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">PLATFORM</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> android</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>对应的需要拉取的另一个仓库的配置：</p><div class="language-yml codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-yml codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">env</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># 壳工程</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">SHELL_REPO</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> NervJS/taro</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">native</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">shell</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># 壳工程ref</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">SHELL_REPO_REF</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 0.63.2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># 壳工程目录</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">SHELL_REPO_PATH</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> taro</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">native</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">shell</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>配置介绍:</p><ul><li>REPO： 壳工程地址</li><li>REPO_REF： 壳工程分支</li><li>SHELL_REPO_PATH： 壳工程目录</li><li>IOS_BUNDLE： 编译 iOS 后的 js bundle 地址</li><li>IOS_ASSETS： 编译 iOS 后的其他静态文件（图片等）地址</li><li>ANDROID_BUNDLE： 编译 Android 后的 js bundle 地址</li><li>ANDROID_ASSETS： 编译 Android 后的其他静态文件（图片等）地址</li><li>PLATFORM：编译的目标平台 ios/android</li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="模板提供的-ios-打包方案">模板提供的 iOS 打包方案<a href="#模板提供的-ios-打包方案" class="hash-link" aria-label="模板提供的 iOS 打包方案的直接链接" title="模板提供的 iOS 打包方案的直接链接">​</a></h2><p>iOS APP 的打包过程相对繁琐，这里我们直接使用了一个优秀的工具 fastlane<!-- -->[6]<!-- -->。fastlane 是一个为 iOS 和 Android 开发者提供的工具，可以自动执行繁琐的任务，如生成屏幕截图、处理配置文件和发布应用程序。</p><p>打包过程中的 info plist 文件修改、版本号修改、签名设置都可以交给 fastlane 去处理，经过 fastlane 的封装，开发者处理这些繁琐的任务，只需要添加几行配置即可。</p><p>但是要让 fastlane 在 GitHub Actions 使用，还需要几步操作。因为证书（Certificate）与描述文件（Provisioning Profiles）并不存储在项目仓库中，而每次工作流都是发生在随机的主机上的，这就需要我们在打包前，先将证书与描述文件导入到当前主机中。</p><p>Release 证书的导入过程如下：</p><ol><li><p>将证书的 <code>p12</code> 文件转成 <code>base64</code> 字符串。</p><div class="language-shell codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-shell codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">cat</span><span class="token plain"> Certificates.p12 </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> base64 </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> pbcopy</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>将第一步内容保存在项目的 <code>secret</code> 中，<code>key</code> 为 <code>RELEASE_SIGNING_CERTIFICATE_P12_DATA</code></p></li><li><p>将 <code>p12</code> 文件的密码保存在项目的 <code>secret</code> 中，<code>key</code> 为 <code>RELEASE_SIGNING_CERTIFICATE_PASSWORD</code></p></li><li><p>将 <code>secret</code> 内配置的相关信息导入到主机中。</p><div class="language-shell codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-shell codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">security </span><span class="token function" style="color:#d73a49">import</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin class-name">echo</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$SIGNING_CERTIFICATE_P12_DATA</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> base64 --decode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    -f pkcs12 </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    -k build.keychain </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    -P </span><span class="token variable" style="color:#36acaa">$SIGNING_CERTIFICATE_PASSWORD</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    -T /usr/bin/codesign</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li></ol><p>描述文件的导入过程，与证书的导入过程类似，均已封装在 workflow 中。</p><p>要将生成的 ipa 文件上传至 testflight 或者 APP Store 上，还需要提供用户名（APP_STORE_CONNECT_USERNAME）与密码（APP_STORE_CONNECT_PASSWORD），可参考文档进行生成<!-- -->[7]<!-- -->。</p><p>至于证书与描述文件的生成，可查阅 iOS 开发相关文章<!-- -->[8]<!-- -->，这里不再赘述。fastlane 配置的更多细节可查看 <code>ios/fastlane/Fastfile</code> 文件。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="模板提供的-android-打包方案">模板提供的 Android 打包方案<a href="#模板提供的-android-打包方案" class="hash-link" aria-label="模板提供的 Android 打包方案的直接链接" title="模板提供的 Android 打包方案的直接链接">​</a></h2><p>Android 的打包过程相对简单，直接调用 <code>gradlew</code> 命令即可。除了配置 APP 的基础信息，还需要为应用进行签名。可参考 Android 应用签名相关文档<!-- -->[9]<!-- -->，生成签名文件，置于 <code>android/app</code> 目录中。</p><p>签名文件也可通过命令行工具生成：</p><div class="language-shell codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-shell codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">keytool -genkey -alias android -keyalg RSA -validity </span><span class="token number" style="color:#36acaa">99999</span><span class="token plain"> -keystore release.keystore</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>打包相关参数，通过 <code>gradlew</code> 的 <code>-P, --project-prop</code> 参数进行传入，如 <code>./gradlew assembledebug -Papp_id=${{ env.APP_ID }}</code>，其默认值在 <code>android/gradle.properties</code> 文件中定义。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="taro-playground-app">Taro Playground APP<a href="#taro-playground-app" class="hash-link" aria-label="Taro Playground APP的直接链接" title="Taro Playground APP的直接链接">​</a></h2><p>基于 GitHub Actions 与 Taro 模板，我们完成了项目初始化与打包过程的自动化。但对于想要体验 Taro 开发 APP 的开发者来说，仍然太过繁琐。为此，我们开发了 Taro Playground APP，并完全开源<!-- -->[10]<!-- -->。一方面可以展示组件和 API 的使用示例，另一方面提供了动态加载 jsbundle 的功能，便于开发人员进行本地代码的调试。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="本地调试">本地调试<a href="#本地调试" class="hash-link" aria-label="本地调试的直接链接" title="本地调试的直接链接">​</a></h3><p>开发者可以在 Taro Playground 仓库的 Releases 页面进行安装包下载<!-- -->[11]<!-- -->，也可扫描以下二维码安装 APP。</p><table><thead><tr><th>Android</th><th>iOS</th></tr></thead><tbody><tr><td><img loading="lazy" src="https://pic3.58cdn.com.cn/nowater/fangfe/n_v295dd481b6b2f446592350e3187716d03.png" class="img_ZOZK"></td><td><img loading="lazy" src="https://wos.58cdn.com.cn/IjGfEdCbIlr/ishare/pic_d3137b5937U5d1XdWcU735Xd7b5a5a37.png" class="img_ZOZK"></td></tr></tbody></table><p>Taro 工程中通过 <code>yarn dev:rn --qr</code> 启动 bundler server，打印包含 IP 及端口信息的二维码。通过 Taro Playground APP 扫描该二维码，即可加载 jsbundle 进行调试，需要保证手机与电脑处于同一个局域网中。</p><p>具体操作可以查看录屏：</p><video controls="" width="600" src="https://wos2.58cdn.com.cn/DeFazYxWvDti/frsupload/03e7ae6c0a8de6be1f665e23404aaa38.mp4"></video><h3 class="anchor anchorWithStickyNavbar_mCi1" id="示列代码">示列代码<a href="#示列代码" class="hash-link" aria-label="示列代码的直接链接" title="示列代码的直接链接">​</a></h3><p>Taro Playground 项目提供了较全面的示例代码，开发者可以参考，避免一些可能遇到的坑，如有问题，欢迎 pr。</p><img loading="lazy" referrerpolicy="no-referrer" src="https://pic4.58cdn.com.cn/nowater/fangfe/n_v28c80c04fd8124114b58268d187f50a21.png" width="300px" class="img_ZOZK"><img loading="lazy" referrerpolicy="no-referrer" src="https://pic4.58cdn.com.cn/nowater/fangfe/n_v28658913ed1aa419da53a4c36da5dc526.png" width="300px" class="img_ZOZK"><h2 class="anchor anchorWithStickyNavbar_mCi1" id="总结">总结<a href="#总结" class="hash-link" aria-label="总结的直接链接" title="总结的直接链接">​</a></h2><p>通过上述多方面的优化，极大地降低了使用 Taro 开发 APP 的成本。大部分场景下，只需要掌握 Taro 和 React Native，再加上一些配置，即可完成 APP 的开发与发布。</p><p>使用过程中，如遇任何问题，可添加 "58 技术小秘书" 或 "Taro 小助手" 为好友，备注 "Taro RN"，加入官方交流群寻求帮助。</p><img loading="lazy" referrerpolicy="no-referrer" src="https://wos.58cdn.com.cn/IjGfEdCbIlr/ishare/pic_13XUU513XUVaU5U7U5Wc3559U75aXUVa.jpg" width="300px" class="img_ZOZK"><img loading="lazy" referrerpolicy="no-referrer" src="https://pic8.58cdn.com.cn/nowater/fangfe/n_v282625210493c4a3fac202d6cf372458e.png" width="300px" class="img_ZOZK"><p>后续，我们还将带来支持 React Native 的 Taro UI 以及包含详细教程的技术小册，尽请期待。</p><p>同时我们也在征集社区优秀使用案例，欢迎开发者提交案例到案例仓库中<!-- -->[12]<!-- -->。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="相关链接">相关链接<a href="#相关链接" class="hash-link" aria-label="相关链接的直接链接" title="相关链接的直接链接">​</a></h2><p>[1]<!-- --> 壳工程地址：<a href="https://github.com/NervJS/taro-native-shell" target="_blank" rel="noopener noreferrer">https://github.com/NervJS/taro-native-shell</a></p><p>[2]<!-- --> 模板源码地址：<a href="https://github.com/NervJS/taro-project-templates/tree/v3.4/react-native" target="_blank" rel="noopener noreferrer">https://github.com/NervJS/taro-project-templates/tree/v3.4/react-native</a></p><p>[3]<!-- --> GitHub Action 文档：<a href="https://docs.github.com/cn/actions" target="_blank" rel="noopener noreferrer">https://docs.github.com/cn/actions</a></p><p>[4]<!-- --> GitHub Action 加密机制：<a href="https://docs.github.com/cn/actions/reference/encrypted-secrets" target="_blank" rel="noopener noreferrer">https://docs.github.com/cn/actions/reference/encrypted-secrets</a></p><p>[5]<!-- --> Taro React Native Publish Action：<a href="https://github.com/shinken008/taro-native-publish" target="_blank" rel="noopener noreferrer">https://github.com/shinken008/taro-native-publish</a></p><p>[6]<!-- --> fastlane 官网：<a href="https://docs.fastlane.tools" target="_blank" rel="noopener noreferrer">https://docs.fastlane.tools</a></p><p>[7]<!-- --> AppleID 密码生成：<a href="https://support.apple.com/en-us/HT204397" target="_blank" rel="noopener noreferrer">https://support.apple.com/en-us/HT204397</a></p><p>[8]<!-- --> 使用 GitHub Action 发布 iOS 应用：<a href="https://betterprogramming.pub/deploy-an-ios-app-to-testflight-or-the-app-store-using-github-actions-c4d7082b1430" target="_blank" rel="noopener noreferrer">https://betterprogramming.pub/deploy-an-ios-app-to-testflight-or-the-app-store-using-github-actions-c4d7082b1430</a></p><p>[9]<!-- --> 安卓签名文件生成：<a href="https://developer.android.com/studio/publish/app-signing#generate-key" target="_blank" rel="noopener noreferrer">https://developer.android.com/studio/publish/app-signing#generate-key</a></p><p>[10]<!-- --> Taro Playground 源码：<a href="https://github.com/wuba/taro-playground" target="_blank" rel="noopener noreferrer">https://github.com/wuba/taro-playground</a></p><p>[11]<!-- --> Taro Playground APP 下载：<a href="https://github.com/wuba/taro-playground/releases" target="_blank" rel="noopener noreferrer">https://github.com/wuba/taro-playground/releases</a></p><p>[12]<!-- --> Taro 案例提交：<a href="https://github.com/NervJS/taro-user-cases" target="_blank" rel="noopener noreferrer">https://github.com/NervJS/taro-user-cases</a></p>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[Taro 正式发布 3.3 版本：支持使用 H5 标签与框架 DevTools]]></title>
            <link>https://docs.taro.zone/blog/2021-08-13-Taro-3.3</link>
            <guid>https://docs.taro.zone/blog/2021-08-13-Taro-3.3</guid>
            <pubDate>Fri, 13 Aug 2021 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>距离 Taro 3.3 第一个 alpha 版本的发布，已过去三个月的时间。期间我们不断地对 3.3 版本进行打磨，除了优化 H5 同构方案外，还引入了一些提升开发体验的新特性和修复了若干重要问题。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="支持使用-h5-标签">支持使用 H5 标签<a href="#支持使用-h5-标签" class="hash-link" aria-label="支持使用 H5 标签的直接链接" title="支持使用 H5 标签的直接链接">​</a></h2><p>Taro 3.3 最重要的特性就是支持使用 H5 标签进行开发，让开发者在小程序环境中复用部分的 Web 生态。</p><p>例如在 React 中可以这样使用：</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">Index</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">Hello World!</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p>相关背景及实现思路在 <a href="/blog/2021-04-22-Taro-3.3-alpha">《Taro 3.3 alpha 发布》</a> 一文中有详细说明。</p></blockquote><h3 class="anchor anchorWithStickyNavbar_mCi1" id="用法">用法<a href="#用法" class="hash-link" aria-label="用法的直接链接" title="用法的直接链接">​</a></h3><p>首先需要安装插件 <code>@tarojs/plugin-html</code>：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i @tarojs/plugin-html</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>然后配置使用即可：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// config/index.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">plugins</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'@tarojs/plugin-html'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>插件的详细用法请见<a href="/docs/use-h5">文档</a>。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="示例项目">示例项目<a href="#示例项目" class="hash-link" aria-label="示例项目的直接链接" title="示例项目的直接链接">​</a></h3><p>为了验证同构功能的可用性和效果，我们对 CSS 样式库 WEUI、React 组件库 Antd Design Mobile、Vue2 组件库 VantUI 的所有组件进行了测试。结果显示有相当一部分的组件能直接运行在小程序环境中。</p><p>示例项目仓库地址：</p><ul><li><a href="https://github.com/NervJS/taro-weui" target="_blank" rel="noopener noreferrer">兼容 WeUI 的例子</a></li><li><a href="https://github.com/NervJS/taro-antd-mobile" target="_blank" rel="noopener noreferrer">兼容 Antd Design Mobile 的例子</a></li><li><a href="https://github.com/NervJS/taro-vant" target="_blank" rel="noopener noreferrer">兼容 VantUI 的例子</a></li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="whats-news">What's news<a href="#whats-news" class="hash-link" aria-label="What's news的直接链接" title="What's news的直接链接">​</a></h3><p>在开发 <code>@tarojs/plugin-html</code> 插件的时候，我们发现需要为 Taro 模拟的 DOM APIs 新增一些方法，如 <code>getBoundingClientRect</code> 等。但是对于不使用这个插件的开发者来说这些 API 是冗余的，会增大包体积。因此我们对 <code>@tarojs/runtime</code> 进行了重构，把 DOM APIs 做成可插拔式，让开发者可以选择在最终的编译结果里只存在哪些 DOM APIs。</p><p>本次对运行时的重构也是为了 Taro 3.4 版本作铺垫。React、Vue2 和 Vue3 这些框架运行时需要的 DOM APIs 是不尽相同的，但是目前假设开发者使用了 React，编译结果里仍然会存在着只有 Vue2 或 Vue3 依赖的 DOM APIs。Taro 3.4 将会把对各 Web 开发框架的支持逻辑抽离成为一个个插件，如：<code>@tarojs/plugin-framework-react</code> 等，目的是剔除掉冗余代码。</p><p>例如，假设开发者没有使用渲染 HTML 文本的 API（如 React 的 <code>dangerouslySetInnerHTML</code>）。这时可以修改 Taro 编译配置的 <code>mini.runtime.enableInnerHTML</code> 选项，令最终的打包结果剔除掉相关代码：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// config/index.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">mini</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">runtime</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">enableInnerHTML</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="小程序支持使用框架的-devtools">小程序支持使用框架的 DevTools<a href="#小程序支持使用框架的-devtools" class="hash-link" aria-label="小程序支持使用框架的 DevTools的直接链接" title="小程序支持使用框架的 DevTools的直接链接">​</a></h2><p>自 Taro 3.x 改造为运行时框架后，有不少同学抱怨小程序开发者工具的 <code>AppData</code> 面板看到的都是 Taro 虚拟 DOM 的序列化结果，而不是页面的 data 数据。为了缓解这个问题，并进一步地提升开发者的开发体验，Taro 对 React DevTools 和 Vue DevTools 进行了适配。只需要简单地配置，就能够和开发 Web 应用时一样使用框架的 DevTools。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="react-devtools">React DevTools<a href="#react-devtools" class="hash-link" aria-label="React DevTools的直接链接" title="React DevTools的直接链接">​</a></h3><p>安装并配置使用 <code>@tarojs/plugin-react-devtools</code> 即可，详细用法请见<a href="/docs/react-devtools">文档</a>。</p><p>效果图：</p><p><img loading="lazy" src="https://storage.360buyimg.com/cjj-pub-images/react-devtools.png" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="vue-devtools">Vue DevTools<a href="#vue-devtools" class="hash-link" aria-label="Vue DevTools的直接链接" title="Vue DevTools的直接链接">​</a></h3><p>安装并配置使用 <code>@tarojs/plugin-vue-devtools</code> 即可，详细用法请见<a href="/docs/vue-devtools">文档</a>。</p><p>效果图：</p><p><img loading="lazy" src="https://storage.360buyimg.com/cjj-pub-images/vue-devtools.png" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="修复百度小程序-flex-布局的问题">修复百度小程序 flex 布局的问题<a href="#修复百度小程序-flex-布局的问题" class="hash-link" aria-label="修复百度小程序 flex 布局的问题的直接链接" title="修复百度小程序 flex 布局的问题的直接链接">​</a></h2><p>在过去很长一段时间里，Taro 3 在百度小程序里是不支持使用 flex 布局的。原因是逻辑层的每个 Taro DOM 节点都在渲染层使用一个对应的 <code>&lt;template&gt;</code> 进行渲染，而百度小程序会把 <code>&lt;template&gt;</code> 渲染成一个真实节点。</p><p>非常感谢 <a href="https://github.com/SyMind" target="_blank" rel="noopener noreferrer">@SyMind</a> 的 PR，帮助 Taro 绕过了此问题。从 Taro 3.2 开始，部分组件在百度小程序中也能够使用 flex 布局。</p><blockquote><p>相关的讨论和提交可以浏览 <a href="https://github.com/NervJS/taro/issues/6015" target="_blank" rel="noopener noreferrer">#6015</a> <a href="https://github.com/NervJS/taro/pull/9529" target="_blank" rel="noopener noreferrer">#9529</a></p></blockquote><h2 class="anchor anchorWithStickyNavbar_mCi1" id="升级指南">升级指南<a href="#升级指南" class="hash-link" aria-label="升级指南的直接链接" title="升级指南的直接链接">​</a></h2><blockquote><p>从 v2.x 升级的同学需要先按 <a href="/docs/next/migration">迁移指南</a> 进行操作。</p></blockquote><p>从 v3.x 升级的同学，首先需要安装 v3.3 的 CLI 工具：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i -g @tarojs/cli</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>然后手工修改 <code>package.json</code> 文件中 Taro 相关依赖的版本，再重新安装依赖；或者使用 <code>taro update project</code> 更新依赖。</p><blockquote><p>如果安装失败或打开项目失败，可以删除 <strong>node_modules</strong>、<strong>yarn.lock</strong>、<strong>package-lock.json</strong> 后重新安装依赖再尝试。</p></blockquote><h2 class="anchor anchorWithStickyNavbar_mCi1" id="the-end">The End<a href="#the-end" class="hash-link" aria-label="The End的直接链接" title="The End的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro-34">Taro 3.4<a href="#taro-34" class="hash-link" aria-label="Taro 3.4的直接链接" title="Taro 3.4的直接链接">​</a></h3><p>下一个 minor 版本，我们会考虑把对各个 Web 开发框架的支持逻辑拆分为对应的插件。让开发者可以以插件的形式为 Taro 拓展支持一门新的开发框架，我们会运用此能力进行对 <strong>Preact</strong> 的支持，希望能进一步减少用户的包体积。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="鸿蒙应用--openharmony">鸿蒙应用 &amp;&amp; OpenHarmony<a href="#鸿蒙应用--openharmony" class="hash-link" aria-label="鸿蒙应用 &amp;&amp; OpenHarmony的直接链接" title="鸿蒙应用 &amp;&amp; OpenHarmony的直接链接">​</a></h3><p>近期我们也一直在探索对<strong>鸿蒙应用 &amp;&amp; OpenHarmony</strong>的适配，目前已有一系列的讨论和开发计划，也有可以运行的 Demo，详情请见 <a href="https://github.com/NervJS/taro/discussions/categories/%E9%B8%BF%E8%92%99-openharmony-%E9%80%82%E9%85%8D%E5%B0%8F%E7%BB%84" target="_blank" rel="noopener noreferrer">鸿蒙 &amp;&amp; OpenHarmony 适配小组</a>。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="roadmap">RoadMap：<a href="#roadmap" class="hash-link" aria-label="RoadMap：的直接链接" title="RoadMap：的直接链接">​</a></h4><p><img loading="lazy" src="https://storage.360buyimg.com/cjj-pub-images/hos-roadmap.png" class="img_ZOZK"></p><p>欢迎各位开发者参与讨论和共建～</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="快应用">快应用<a href="#快应用" class="hash-link" aria-label="快应用的直接链接" title="快应用的直接链接">​</a></h3><p>此外，社区大佬 <a href="https://github.com/bgfist" target="_blank" rel="noopener noreferrer">@bgfist</a> 正在为 Taro 开发支持<strong>快应用</strong>的功能。也欢迎各位开发者参与讨论和共建，相关 <a href="https://github.com/NervJS/taro/pull/9743" target="_blank" rel="noopener noreferrer">PR</a>。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="感恩">感恩<a href="#感恩" class="hash-link" aria-label="感恩的直接链接" title="感恩的直接链接">​</a></h3><p>时光飞逝，1 年前 Taro 3 正式发布，Taro 逐渐成为被社区喜欢、信赖的开源项目。未来我们也将继续做好 Taro 的开源工作，同时也希望社区有更多的开发者参与进来，无论是提出 Issues、在论坛发帖、提交 PR 还是帮助建设周边生态等。让我们一起把 Taro 做好，做强。</p><p>最后，衷心感谢为 Taro 从 v3.2 走到 v3.3 贡献过代码的各位同学！</p>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[Taro 3.3 alpha 发布：用 ant-design 开发小程序？]]></title>
            <link>https://docs.taro.zone/blog/2021-04-22-Taro-3.3-alpha</link>
            <guid>https://docs.taro.zone/blog/2021-04-22-Taro-3.3-alpha</guid>
            <pubDate>Thu, 22 Apr 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[本文将介绍基于 Taro 3.0 实现 H5 同构的思路与问题，以及我们尝试适配了三大移动端 UI 框架 **WEUI**、**Ant Design Mobile**、**VantUI** 的实验结果。]]></description>
            <content:encoded><![CDATA[<p>小程序的设计并没有完全遵循 Web 规范，导致小程序生态和传统 Web 开发生态之间的割裂，海量优秀的 Web 物料并不能直接用于小程序开发。因而 Taro 在相当一段时间内生态都相对薄弱，UI 框架选择不多的问题更是深深困扰着开发者。</p><p>另一方面，业界有着存量的 H5 应用，中短期内 H5 应用适配到小程序端的需要还会存在。我们希望能减少 H5 应用迁移到小程序端的成本，甚至能够直接运行在小程序端。</p><p>Taro 团队一直在思考如何最大限度地在小程序环境中复用 Web 生态，直到 Taro 3.0 诞生后，这种想法有了落地的可能。下文将介绍基于 Taro 3.0 实现 H5 同构的思路与问题，以及我们尝试适配了三大移动端 UI 框架 <strong>WEUI</strong>、<strong>Ant Design Mobile</strong>、<strong>VantUI</strong> 的实验结果。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="一实现思路">一、实现思路<a href="#一实现思路" class="hash-link" aria-label="一、实现思路的直接链接" title="一、实现思路的直接链接">​</a></h2><p>Taro 3.0 是一款重运行时的跨端框架，它通过模拟实现浏览器的 BOM 和 DOM API 实现了对 React、Vue 等 Web 开发框架的兼容。</p><p>既然已经有了浏览器环境的 BOM 和 DOM API，Taro 应用和 Web 应用之间的鸿沟在于小程序组件和 HTML 标签之间的差异。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="支持渲染-h5-标签">支持渲染 H5 标签<a href="#支持渲染-h5-标签" class="hash-link" aria-label="支持渲染 H5 标签的直接链接" title="支持渲染 H5 标签的直接链接">​</a></h3><p>Taro3 的渲染数据流如下：</p><p><strong>前端框架 -&gt; Taro DOM -&gt; 小程序 data</strong></p><p>HTML 标签和小程序组件的标签名、属性、事件是有差异的，而前端框架无需感知这些差异。</p><p>因此前端框架适配层、Taro DOM 层不需要改动，只要在 <strong>Taro DOM 序列化为小程序 data</strong> 这一步作映射即可。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="1-标签名映射">1. 标签名映射<a href="#1-标签名映射" class="hash-link" aria-label="1. 标签名映射的直接链接" title="1. 标签名映射的直接链接">​</a></h4><p>HTML 标签相对小程序组件封装程度更低、功能更简单，可以看作是小程序组件的子集。因此可以按一定的规则，把 HTML 标签映射为小程序组件，如：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Taro DOM 的序列化数据</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">nn</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'img'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 映射结果</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">nn</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'image'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>完整的标签名映射规则请看：<a href="https://github.com/NervJS/taro-rfcs/blob/html-support/rfcs/0004-rendering-html.md#%E4%B8%80%E6%A0%87%E7%AD%BE%E5%90%8D%E5%BD%B1%E5%B0%84" target="_blank" rel="noopener noreferrer">RFC 附录一</a></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="2-属性映射">2. 属性映射<a href="#2-属性映射" class="hash-link" aria-label="2. 属性映射的直接链接" title="2. 属性映射的直接链接">​</a></h4><p>如果 HTML 标签的属性能在对应小程序组件的属性上找到对应，则进行映射，如：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Taro DOM 的序列化数据</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">nn</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'a'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">href</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'xxx'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">target</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'_blank'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 映射结果</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">nn</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'navigator'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">url</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'xxx'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">openType</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'navigate'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>完整的属性名映射规则请看：<a href="https://github.com/NervJS/taro-rfcs/blob/html-support/rfcs/0004-rendering-html.md#%E4%BA%8C%E5%B1%9E%E6%80%A7%E5%90%8D%E5%BD%B1%E5%B0%84" target="_blank" rel="noopener noreferrer">RFC 附录二</a></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="3-事件映射">3. 事件映射<a href="#3-事件映射" class="hash-link" aria-label="3. 事件映射的直接链接" title="3. 事件映射的直接链接">​</a></h4><p>把 HTML 特有的事件在小程序端找到相似的事件进行映射，如：</p><table><thead><tr><th align="left">HTML 事件</th><th align="left">小程序组件事件</th></tr></thead><tbody><tr><td align="left">click</td><td align="left">tap</td></tr></tbody></table><p>完整的事件映射规则请看：<a href="https://github.com/NervJS/taro-rfcs/blob/html-support/rfcs/0004-rendering-html.md#%E4%B8%89%E4%BA%8B%E4%BB%B6%E5%BD%B1%E5%B0%84" target="_blank" rel="noopener noreferrer">RFC 附录三</a></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="样式">样式<a href="#样式" class="hash-link" aria-label="样式的直接链接" title="样式的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="1-标签选择器">1. 标签选择器<a href="#1-标签选择器" class="hash-link" aria-label="1. 标签选择器的直接链接" title="1. 标签选择器的直接链接">​</a></h4><p>前文介绍了我们会把 HTML 标签映射为小程序组件，但是 H5 应用中使用到的 CSS 标签选择器就会失效。</p><p>因此 Taro 使用了类名去进行模拟：</p><ol><li>为所有 H5 标签都加上类名： <code>h5-${tagName}</code>。</li></ol><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 源代码</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 渲染结果</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">view </span><span class="token keyword" style="color:#00009f">class</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"h5-div"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ol start="2"><li>使用 postcss 插件处理标签名选择器：</li></ol><div class="language-scss codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-scss codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 标签名选择器</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token selector" style="color:#00009f">div </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 经 postcss 插件处理后变为类名选择器</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token selector" style="color:#00009f">.h5-div </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="2-浏览器默认样式">2. 浏览器默认样式<a href="#2-浏览器默认样式" class="hash-link" aria-label="2. 浏览器默认样式的直接链接" title="2. 浏览器默认样式的直接链接">​</a></h4><p>Taro 提供两种内置的浏览器默认样式，可以直接引入生效：</p><ul><li><code>@tarojs/taro/html.css</code>: W3C HTML4 的内置样式。只有 HTML4 标签样式，体积较小，兼容性强，能适应大多数情况。</li><li><code>@tarojs/taro/html5.css</code>: Chrome(Blink) HTML5 的内置样式。内置样式丰富，包括了大多数 HTML5 标签，体积较大，不一定支持所有小程序容器。</li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="限制">限制<a href="#限制" class="hash-link" aria-label="限制的直接链接" title="限制的直接链接">​</a></h3><p>理想很美好，但现实却略显骨感。即使 Taro 能实现 BOM、DOM API，支持使用 HTML 标签等，同构方案还是存在着一些框架层面抹平不了的差异。以下列举出若干主要限制：</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="1-获取元素尺寸">1. 获取元素尺寸<a href="#1-获取元素尺寸" class="hash-link" aria-label="1. 获取元素尺寸的直接链接" title="1. 获取元素尺寸的直接链接">​</a></h4><p>在 H5 中我们可以调用 DOM API 同步获取元素的尺寸：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// h5</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> el </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token dom variable" style="color:#36acaa">document</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">getElementById</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'#inner'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> res </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">getBoundingClientRect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">res</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>但是在小程序中，获取元素尺寸的 API 是异步的：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 小程序</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> query </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token maybe-class-name">Taro</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createSelectorQuery</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">query</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">select</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'#inner'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">boundingClientRect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">exec</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">res</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">res</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>因此不能兼容那些使用了同步 DOM API 去获取元素尺寸的组件。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="2-dom-api-差异">2. DOM API 差异<a href="#2-dom-api-差异" class="hash-link" aria-label="2. DOM API 差异的直接链接" title="2. DOM API 差异的直接链接">​</a></h4><p><code>&lt;canvas&gt;</code>、<code>&lt;video&gt;</code>、<code>&lt;audio&gt;</code> 等标签在 H5 端可以直接调用 <code>HTMLElement</code> 上的方法：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// h5</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> el </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token dom variable" style="color:#36acaa">document</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">getElementById</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'myVideo'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">el</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">play</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>但是在 Taro 中，要调用组件上的原生方法，必须先创建对应的 <code>Context</code>：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 小程序</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> ctx </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token maybe-class-name">Taro</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createVideoContext</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'myVideo'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">play</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="3-样式限制">3. 样式限制<a href="#3-样式限制" class="hash-link" aria-label="3. 样式限制的直接链接" title="3. 样式限制的直接链接">​</a></h4><p>部分样式或 CSS 选择器在小程序中不支持，如：</p><ul><li>通配符 <!-- -->*</li><li>媒体查询</li><li>属性选择器，当属性不是对应小程序组件的内置属性时</li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="二使用方法">二、使用方法<a href="#二使用方法" class="hash-link" aria-label="二、使用方法的直接链接" title="二、使用方法的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="升级-330-alpha-版本">升级 3.3.0-alpha 版本<a href="#升级-330-alpha-版本" class="hash-link" aria-label="升级 3.3.0-alpha 版本的直接链接" title="升级 3.3.0-alpha 版本的直接链接">​</a></h3><p>首先需要安装 v3.3 的 CLI 工具：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i -g @tarojs/cli@alpha</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>然后进入项目，把 <code>package.json</code> 文件中 taro 相关依赖的版本修改为 <code>^3.3.0-alpha.2</code>，再重新安装依赖（建议先把 <strong>node_modules</strong> 文件夹删除）。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="安装同构插件">安装同构插件<a href="#安装同构插件" class="hash-link" aria-label="安装同构插件的直接链接" title="安装同构插件的直接链接">​</a></h3><p>为了节省项目空间，同构功能是可选的，以 Taro 插件的形式提供。</p><p>首先开发者需要安装插件 <code>@tarojs/plugin-html</code>：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i @tarojs/plugin-html</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>然后配置使用此插件：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// config/index.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">plugins</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'@tarojs/plugin-html'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="三示例项目">三、示例项目<a href="#三示例项目" class="hash-link" aria-label="三、示例项目的直接链接" title="三、示例项目的直接链接">​</a></h2><p>为了验证同构功能的可用性和效果，我们对 CSS 样式库 <strong>WEUI</strong>、React 组件库 <strong>Antd Design Mobile</strong>、Vue2 组件库 <strong>VantUI</strong> 的所有组件进行了测试。</p><p>测试效果比较理想，甚至稍微超出我们的预期，配合各组件库自身的按需加载能力，能以小巧的体积使用丰富的组件，相信各位开发者会喜欢这个功能。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="weui">WEUI<a href="#weui" class="hash-link" aria-label="WEUI的直接链接" title="WEUI的直接链接">​</a></h3><p>仓库地址：<a href="https://github.com/NervJS/taro-weui" target="_blank" rel="noopener noreferrer">taro-weui</a></p><p>WEUI 是一个 CSS 的样式库，与框架无关，兼容性比较高，大部分组件能直接使用。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="antd-mobile-design">Antd Mobile Design<a href="#antd-mobile-design" class="hash-link" aria-label="Antd Mobile Design的直接链接" title="Antd Mobile Design的直接链接">​</a></h3><p>仓库地址：<a href="https://github.com/NervJS/taro-antd-mobile" target="_blank" rel="noopener noreferrer">taro-antd-mobile</a></p><blockquote><p>Antd Mobile Design 官方已经相当久没有维护，此适配项目属于实验性质。</p></blockquote><p>能直接兼容使用的组件大概为 80%，主要问题在于：</p><ol><li>组件库里广泛使用了 SVG，目前并不支持。</li><li>不能使用需要同步获取元素尺寸的部分组件。</li></ol><h3 class="anchor anchorWithStickyNavbar_mCi1" id="vantui">VantUI<a href="#vantui" class="hash-link" aria-label="VantUI的直接链接" title="VantUI的直接链接">​</a></h3><p>仓库地址：<a href="https://github.com/NervJS/taro-vant" target="_blank" rel="noopener noreferrer">taro-vant</a></p><p>VantUI 的组件十分丰富，能直接兼容使用的组件大概为 70%。部分开发者会在 Taro 中配合使用 Vant Weapp，但 Vant Weapp 只能运行在微信小程序，因此对 VantUI 的直接适配是一个很好的补充。</p><p>适配过程主要遇到的问题有：</p><ol><li>少量组件内置的 SVG ICON 不能显示。</li><li>不能使用需要同步获取元素尺寸的部分组件。</li><li>Vue <code>&lt;transition&gt;</code> 组件需要额外适配。</li></ol><h2 class="anchor anchorWithStickyNavbar_mCi1" id="四共建倡议">四、共建倡议<a href="#四共建倡议" class="hash-link" aria-label="四、共建倡议的直接链接" title="四、共建倡议的直接链接">​</a></h2><p>同构方案还在持续优化中，部分实现还没有最终定稿。欢迎各位开发者到我们的论坛下留言，提出您的宝贵意见～：<a href="https://github.com/NervJS/taro/discussions/9029" target="_blank" rel="noopener noreferrer">同构方案 RFC</a>。</p><p>另外，上述三款 Web UI 框架很多目前没能兼容的组件，只要针对小程序环境做一点兼容工作，是可以进行使用的。一款框架的生态需要官方和社区共同努力建设，单靠 Taro 团队及 300 多位贡献者的人力没办法撑起整个社区的生态。我们希望这三个兼容性示例项目能起到抛砖引玉的作用，吸引广大开发者进行共建，一起完善上述组件库，甚至不断地引入更多的 Web 端生态库，让跨端开发变得更轻松。十分期待您的参与～</p>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[Taro 3.2 版本正式发布：React Native 支持，王者归来]]></title>
            <link>https://docs.taro.zone/blog/2021-04-08-taro-3.2</link>
            <guid>https://docs.taro.zone/blog/2021-04-08-taro-3.2</guid>
            <pubDate>Thu, 08 Apr 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Taro 3.2 版本正式发布：React Native 支持，王者归来]]></description>
            <content:encoded><![CDATA[<p><strong>Taro</strong> 是一个开放式 <strong>跨端跨框架</strong> 解决方案，支持使用 React/Vue/Nerv 等框架来开发 <code>微信/京东/百度/支付宝/字节跳动/QQ小程序/H5/React Native</code> 等应用。</p><p>自从 Taro 3 发布以来，不少开发者期待 Taro 3 可以支持 React Native。基于 58 团队在 React Native 方向的技术积累，我们与 Taro 团队达成战略合作伙伴关系。由 58 团队主导开发 Taro 3 React Native，共同推进 Taro 的发展。</p><p>去年 12 月开始，已经完成了 <a href="https://mp.weixin.qq.com/s/-7G7NMHX8ol99QxkswFOxg" target="_blank" rel="noopener noreferrer">Taro 3 React Native 的支持</a>，共发布了 9 个 canary 版本与 4 个 beta 版，经过较长时间的验证，如今终于迎来了 3.2 正式版。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="一highlights">一、Highlights<a href="#一highlights" class="hash-link" aria-label="一、Highlights的直接链接" title="一、Highlights的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-更快编译速度">1. 更快编译速度<a href="#1-更快编译速度" class="hash-link" aria-label="1. 更快编译速度的直接链接" title="1. 更快编译速度的直接链接">​</a></h3><p>在 Taro 3 之前版本中，Taro 作为一个编译型框架，React Native 打包 <code>bundle</code> 会分为两个过程。首先，对 Taro 源码进行大量的 AST 操作转换成 React Native 的代码，然后再利用 Metro 打包成 <code>bundle</code>，整个编译速度有待进一步提升。</p><p>Taro 3 React Native 对整个编译系统进行较大调整，不再生成中间代码，而是直接利用 Metro 生成 <code>bundle</code>，通过运行时适配 Taro 3 标准，这种方式使得编译过程更简单，大大提升了编译速度，且给我们带来了更多的好处:</p><ul><li>利用 Metro 打包 React Native ，通过多级缓存以及 hasteFS 让打包速度更快；</li><li>更加贴合 React Native 生态，社区基于 Metro 打包优化方案对接更加容易；</li></ul><p><img loading="lazy" src="https://wos2.58cdn.com.cn/DeFazYxWvDti/frsupload/d8b1baf30804ca721e8ece597dd4e9b6.gif" alt="Taro 2 与 Taro 3 启动过程对比" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-source-map-支持">2. <code>source-map</code> 支持<a href="#2-source-map-支持" class="hash-link" aria-label="2-source-map-支持的直接链接" title="2-source-map-支持的直接链接">​</a></h3><p>在旧版本的 Taro React Native 中，通过将 Taro 代码转换为 React Native 的中间代码，再利用 Metro 打包成 <code>bundle</code>。 这种方式不仅编译速度不佳，利用中间代码打包成 <code>bundle</code>，不能支持到<code>source-map</code>，调试起来不够直观。</p><p>Taro 3 对编译系统进行较大调整，不再生成中间代码，而是直接利用 Metro 对 Taro 源码进行打包，这样在提升编译速度的同时，也天然支持<code>source-map</code>，对于开发体验是一个巨大的提升，能够达到与开发原生 React Native 应用一致的体验。</p><p><img loading="lazy" src="https://wos.58cdn.com.cn/IjGfEdCbIlr/ishare/pic_5ad3XU5aV9WcU7d1Wbd1d3U5WcVaVa7b.png" alt="基于 source-map 进行debug" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-多-react-native-版本支持拥抱最新版-064">3. 多 React Native 版本支持，拥抱最新版 0.64<a href="#3-多-react-native-版本支持拥抱最新版-064" class="hash-link" aria-label="3. 多 React Native 版本支持，拥抱最新版 0.64的直接链接" title="3. 多 React Native 版本支持，拥抱最新版 0.64的直接链接">​</a></h3><p>过去 Taro 对移动端的支持，React Native 的版本都是相对固定的，修改 React Native 版本存在较多的不便。</p><p>Taro 3 对系统架构进行了全面升级，React Native 版本取决于开发者项目中的依赖，<strong>目前支持 0.60 以上的 React Native 版本，当然最新的 0.64 也是完全支持</strong>。</p><p>在 React Native 0.64 的版本中，iOS 端完成 了对 Hermes 引擎的支持，默认启用了内联引用，也支持 React 17 等等，想体验这些新特性的开发者只需升级对应依赖即可。</p><p><strong>新项目升级</strong></p><p>Taro 模板默认依赖 React 17、TypeScript 4，运行 React Native 端会自动安装 0.64 的版本，直接使用即可。</p><p><strong>旧项目升级</strong></p><blockquote><p>从 v2.x 升级的同学需要先按 <a href="/docs/next/migration/">迁移指南</a>进行操作。</p></blockquote><p>如果是 Taro 3 的旧项目，手动升级对应的依赖：</p><ul><li><code>react: ^17.0.0</code></li><li><code>react-dom: ^17.0.0</code></li><li><code>typescript: ^4.1.0</code></li><li><code>@typescript-eslint/parser: ^4.15.1</code></li><li><code>@typescript-eslint/eslint-plugin: ^4.15.1</code></li><li><code>react-native:^0.64.0</code></li></ul><p>设置 ESLint 配置：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// .eslintrc</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">extends</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'taro/react'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">rules</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string-property property" style="color:#36acaa">'react/jsx-uses-react'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'off'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string-property property" style="color:#36acaa">'react/react-in-jsx-scope'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'off'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="4-更丰富-api-与组件">4. 更丰富 API 与组件<a href="#4-更丰富-api-与组件" class="hash-link" aria-label="4. 更丰富 API 与组件的直接链接" title="4. 更丰富 API 与组件的直接链接">​</a></h3><p>要真正做到一套代码，多端复用，非常重要的一点就是组件和 API 的覆盖度，在 Taro 3.2 之前，适配 React Native 已经实现了很多常用 API 和组件，但与小程序，H5 之间还存在较大的差距，需进一步完善。在新的版本中，我们进一步提升组件和 API 的覆盖度，共新增 <strong>45</strong> 个 API 与 <strong>8</strong> 个组件。</p><p>在如今多媒体时代，视频、音频等相关组件与 API 都是必不可少的一部分，在 Taro 3 中，我们也都已经集成进来了，除了多媒体相关，还有扫码，VirtualList 等等，大大丰富了 API 与组件。更多内容请参考<a href="/docs/apis/about/desc">文档</a>。</p><p><strong>新增的 API:</strong></p><table><thead><tr><th>API</th><th>功能</th></tr></thead><tbody><tr><td>scanCode</td><td>扫码</td></tr><tr><td>show/hideTabBar...</td><td>tabBar 相关</td></tr><tr><td>createCameraContext</td><td>相机相关</td></tr><tr><td>...</td><td>...</td></tr></tbody></table><p><strong>新增的组件</strong></p><table><thead><tr><th>API</th><th>功能</th></tr></thead><tbody><tr><td>Video</td><td>视频</td></tr><tr><td>Camera</td><td>相机</td></tr><tr><td>VirtualList</td><td>长列表</td></tr><tr><td>...</td><td>...</td></tr></tbody></table><h3 class="anchor anchorWithStickyNavbar_mCi1" id="5-api-与组件按需引入">5. API 与组件按需引入<a href="#5-api-与组件按需引入" class="hash-link" aria-label="5. API 与组件按需引入的直接链接" title="5. API 与组件按需引入的直接链接">​</a></h3><p>在 React Native 应用中，打包后最终 <code>bundle</code> 的大小会影响我们首次打开页面的时间，首次打开时间过长，对用户的体验存在一定的影响。</p><p>在 Taro 3 中，我们也对打包后 <code>bundle</code> 大小进行了优化。在实际的业务场景中，可能并不会使用到 Taro 提供的所有组件和 API ，但仍然会将所有的组件与 API 及相关依赖打包到最终的 <code>bundle</code> 中，这无疑会增加 <code>bundle</code> 的大小。</p><p>为了减少打包后的大小，我们对组件和 API 实现了按需引入的方式，比如：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Taro</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/taro'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token maybe-class-name">Taro</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">scanCode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">options</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">//实际打包后的引入会根据页面使用的引入</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> scanCode </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/taro-rn/dist/lib/scanCode'</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>因此，这种实现按需引入，对于项目中未使用的组件与 API 以及对于依赖的库，都不会打包到<code>bundle</code>的中，能够大大减少最终<code>bundle</code>的大小，进一步提升 App 首次打开页面时间。同时因为按需引入的存在，壳工程可自主进行优化定制，降低 APP 包大小。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="6-与-react-native-应用融合">6. 与 React Native 应用融合<a href="#6-与-react-native-应用融合" class="hash-link" aria-label="6. 与 React Native 应用融合的直接链接" title="6. 与 React Native 应用融合的直接链接">​</a></h3><p><strong>有开发者提到，已有 react native 的项目中，在不修改原有业务的情况下，是否便于接入呢？</strong></p><p>在 Taro 3 我们给出了肯定的答案，Taro 3 的整个编译体系进行了升级，直接基于 Taro 源码进行打包 ，通过自定义<code>transformer</code>，运行时适配的方式，生成支持 Taro 的 Metro 配置，并与业务的配置进行合并，根据最终 Metro 配置完成<code>bundle</code>的打包。这种方案比较容易扩展。</p><p>我们通过提供支持 Taro 的打包 Metro 配置并提供运行时方法，开发者只需与现有业务打包配置结合，并用运行时方法进行包装处理。这样可以比较方便的与现有导航系统结合，也无需修改原有代码，即可以轻松接入。 详细可参考<a href="https://github.com/wuba/Taro-Code-In-React-Native" target="_blank" rel="noopener noreferrer">Demo</a>。</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">//metro.config.js 参考配置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">Supporter</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'@tarojs/rn-supporter'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> supporter </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Supporter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> taroMetroConfig </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> supporter</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">getMetroConfig</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> busConfig </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">resetCache</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">mergeConfig</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">busConfig</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> taroMetroConfig</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="7-不跨端使用">7. 不跨端使用<a href="#7-不跨端使用" class="hash-link" aria-label="7. 不跨端使用的直接链接" title="7. 不跨端使用的直接链接">​</a></h3><p><strong>没有跨端需求，是否就不需要使用 Taro 呢？</strong></p><p>对于当前没有跨端的需求，仅需要开发 React Native 应用或者小程序的开发者，我们仍然推荐使用 Taro ，主要有以下几点原因：</p><ul><li>支持快速到其他端，便于后期扩展；</li><li>可使用 Taro 组件与 API，提升开发速度；</li><li>相比 React Native 样式的写法，写法更加简单便捷，并且支持多种预编译语言；</li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="二升级指南">二、升级指南<a href="#二升级指南" class="hash-link" aria-label="二、升级指南的直接链接" title="二、升级指南的直接链接">​</a></h2><blockquote><p>从 v2.x 升级的同学需要先按 <a href="/docs/next/migration/">迁移指南</a>进行操作。</p></blockquote><p>从 v3.x 升级的同学，首先需要安装 v3.2 的 CLI 工具：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i -g @tarojs/cli</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>然后进入项目，删除 <strong>node_modules</strong>、<strong>yarn.lock</strong>、<strong>package-lock.json</strong>。</p><p>最后把 <code>package.json</code> 文件中 Taro 相关依赖的版本修改为 <code>^3.2.0</code>，再重新安装依赖，至此升级结束。</p><blockquote><p>目前 Taro 3 仅支持 React 开发的项目运行在 React Native 端。</p></blockquote><h2 class="anchor anchorWithStickyNavbar_mCi1" id="三未来规划">三、未来规划<a href="#三未来规划" class="hash-link" aria-label="三、未来规划的直接链接" title="三、未来规划的直接链接">​</a></h2><p>Taro 3 已完成 <strong>React Native</strong> 端的适配，未来我们将从组件和 API 的覆盖度，使用成本等几个方面继续完善。不断提升 API 与组件的覆盖度，尽可能减少与 H5、小程序端的差异。通过增加新手教程，减少项目初始化步骤，提供更多的案例等方面，不断降低使用成本。</p><p>Taro 也提供了微信群方便大家与维护者们更及时沟通交流：</p><p><img loading="lazy" src="https://wos.58cdn.com.cn/IjGfEdCbIlr/ishare/a758bc2a-127a-4363-83f2-6f1c00e977afimage.png" alt="微信群" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="四感谢">四、感谢<a href="#四感谢" class="hash-link" aria-label="四、感谢的直接链接" title="四、感谢的直接链接">​</a></h2><p>Taro 团队衷心感谢各位参与过本项目开源建设的朋友，无论是为 Taro 提交过代码、建设周边生态，还是反馈过问题，甚至只是茶余饭后讨论、吐槽 Taro 的各位。</p><p>现诚挚的邀请您与 Taro 团队交流您的使用情况，也希望能够提出更多宝贵的意见，有你相伴，Taro 会更加精彩。</p><p><img loading="lazy" src="https://wos.58cdn.com.cn/IjGfEdCbIlr/ishare/e675214c-912b-46fb-98ba-3629d3e455d9image.png" alt="问卷调查" class="img_ZOZK"></p><p>最后，特别感谢为 Taro 从 v3.1 走到 v3.2 贡献过代码的各位同学，排名不分先后：</p><ul><li>@KeenV</li><li>@baranwang</li><li>@LiHDong</li><li>@changcllong</li><li>@liuchuzhang</li><li>@baranwang</li><li>@SyMind</li><li>@ryougifujino</li><li>@inarol</li><li>@huaoguo</li><li>@zhiqingchen</li><li>@zhenglong</li><li>@Qiuz</li><li>@shinken008</li><li>@skychx</li><li>@iambool</li><li>@iChengbo</li><li>@nickyefei</li><li>@xzj</li><li>@yechunxi</li><li>@Victor</li><li>@xieweilyg</li><li>@Dugz</li><li>@haojie</li><li>@b2nil</li><li>@doublethinkio</li><li>@CodeDaraW</li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="作者简介">作者简介<a href="#作者简介" class="hash-link" aria-label="作者简介的直接链接" title="作者简介的直接链接">​</a></h2><ul><li>陈志庆：58 同城 前端架构师，技术委员会委员</li><li>叶春喜：58 同城 资深前端开发工程师</li></ul>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[Taro 正式发布 3.1 版本]]></title>
            <link>https://docs.taro.zone/blog/2021-03-10-taro-3-1-lts</link>
            <guid>https://docs.taro.zone/blog/2021-03-10-taro-3-1-lts</guid>
            <pubDate>Wed, 10 Mar 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[自 Taro 3.1 体验版推出后，我们不断地根据社区的反馈意见对 3.1 版本进行打磨。经历了 12 个 beta 版本后，终于迎来了 3.1 正式版。]]></description>
            <content:encoded><![CDATA[<p>自 Taro 3.1 体验版推出后，我们不断地根据社区的反馈意见对 3.1 版本进行打磨。主要改进了<strong>开放式架构</strong>、引入了 <code>CustomWrapper</code> 组件以解决性能问题、提出了原生小程序<strong>渐进式混合使用 Taro</strong> 的解决方案。</p><p>经历了 12 个 beta 版本后，终于迎来了 3.1 正式版 🎉</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="一highlights">一、Highlights<a href="#一highlights" class="hash-link" aria-label="一、Highlights的直接链接" title="一、Highlights的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-开放式架构">1. 开放式架构<a href="#1-开放式架构" class="hash-link" aria-label="1. 开放式架构的直接链接" title="1. 开放式架构的直接链接">​</a></h3><p>近年来业界推出的小程序平台越来越多，但 Taro 核心维护的平台只有 6 个，因此常常有同学提出能不能支持某某平台的 Feature Request。</p><p>基于目前的架构，支持一个新的平台开发复杂度高，同时社区也难以参与贡献。</p><p>为此我们把 Taro 打造成为一个开放式框架，<strong>通过端平台插件能支持任意小程序平台</strong>：</p><ul><li><strong>插件开发者</strong>无需修改 Taro 项目源码，即可<a href="/docs/next/platform-plugin/how">编写出一个端平台插件</a>。</li><li><strong>插件使用者</strong>只需<a href="/docs/next/platform-plugin/#%E7%AB%AF%E5%B9%B3%E5%8F%B0%E6%8F%92%E4%BB%B6%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95">安装、配置端平台插件</a>，即可把代码编译到指定平台。</li></ul><p>基于开放式架构，我们新增了一些有趣的插件，也十分期待大家利用它施展创意。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="新增的插件">新增的插件：<a href="#新增的插件" class="hash-link" aria-label="新增的插件：的直接链接" title="新增的插件：的直接链接">​</a></h4><table><thead><tr><th align="left">插件</th><th align="left">功能</th></tr></thead><tbody><tr><td align="left"><a href="https://github.com/NervJS/taro-plugin-platform-weapp-qy" target="_blank" rel="noopener noreferrer">@tarojs/plugin-platform-weapp-qy</a></td><td align="left">编译企业微信小程序</td></tr><tr><td align="left"><a href="https://github.com/NervJS/taro-plugin-platform-alipay-dd" target="_blank" rel="noopener noreferrer">@tarojs/plugin-platform-alipay-dd</a></td><td align="left">编译钉钉小程序</td></tr><tr><td align="left"><a href="https://github.com/NervJS/taro-plugin-platform-alipay-iot" target="_blank" rel="noopener noreferrer">@tarojs/plugin-platform-alipay-iot</a></td><td align="left">编译支付宝 IOT 小程序</td></tr><tr><td align="left"><a href="https://github.com/NervJS/taro-plugin-inject" target="_blank" rel="noopener noreferrer">@tarojs/plugin-inject</a></td><td align="left">为所有小程序平台快速新增 <strong>API</strong>、<strong>组件</strong>，<strong>调整组件属性</strong>等</td></tr></tbody></table><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-新增小程序性能优化组件-customwrapper">2. 新增小程序性能优化组件 CustomWrapper<a href="#2-新增小程序性能优化组件-customwrapper" class="hash-link" aria-label="2. 新增小程序性能优化组件 CustomWrapper的直接链接" title="2. 新增小程序性能优化组件 CustomWrapper的直接链接">​</a></h3><p>Taro3 使用 <code>&lt;template&gt;</code> 进行渲染，所有的 <code>setData</code> 都由页面对象调用。如果页面结构比较复杂，应用更新的性能就会下降。</p><p>为此我们引入了一个基础组件 <code>CustomWrapper</code>，它的作用是创建一个原生自定义组件。对后代节点的 <code>setData</code> 将由此自定义组件进行调用，<strong>达到局部更新的效果</strong>，从而提升更新性能。</p><p>开发者可以使用 <code>CustomWrapper</code> 去包裹遇到更新性能问题的组件：</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">CustomWrapper</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">GoodsList</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Item</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Item</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    // ...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">GoodsList</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">CustomWrapper</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p>更详细的性能优化原理请见<a href="/blog/2021-02-08-taro-jxpp#2-%E6%B8%B2%E6%9F%93%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96">《Taro 助力京喜拼拼项目性能体验优化》</a></p></blockquote><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-原生小程序渐进式混合使用-taro-开发">3. 原生小程序渐进式混合使用 Taro 开发<a href="#3-原生小程序渐进式混合使用-taro-开发" class="hash-link" aria-label="3. 原生小程序渐进式混合使用 Taro 开发的直接链接" title="3. 原生小程序渐进式混合使用 Taro 开发的直接链接">​</a></h3><p>过去我们对在 Taro 项目中混合使用原生的支持度较高。相反地，对在原生项目中混合使用 Taro 却没有太重视。但是市面上有着存量的原生开发小程序，他们接入 Taro 开发的改造成本往往非常大，最后只得放弃混合开发的想法。</p><p><a href="/blog/2021-02-08-taro-jxpp">经过与京喜拼拼项目的合作</a>，也驱使了我们更加关注这部分需求。Taro 推出了一套完整的原生项目混合使用 Taro 的方案。</p><p><a href="/docs/taro-in-miniapp">方案</a>主要支持了三种场景：</p><ul><li>在原生项目中使用 Taro 开发的页面。（已完成）</li><li>在原生项目的分包中运行完整的 Taro 项目。（已完成）</li><li>在原生项目中使用 Taro 开发的自定义组件。（将在 v3.1.3 版本中支持）</li></ul><p>希望以上方案能满足打算逐步接入 Taro 开发的同学。更多意见也欢迎在 <a href="https://github.com/NervJS/taro" target="_blank" rel="noopener noreferrer">Github</a> 上给我们留言。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="4-拥抱-react-17typescript-4">4. 拥抱 React 17、TypeScript 4<a href="#4-拥抱-react-17typescript-4" class="hash-link" aria-label="4. 拥抱 React 17、TypeScript 4的直接链接" title="4. 拥抱 React 17、TypeScript 4的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="41-使用方法">4.1. 使用方法<a href="#41-使用方法" class="hash-link" aria-label="4.1. 使用方法的直接链接" title="4.1. 使用方法的直接链接">​</a></h4><h5 class="anchor anchorWithStickyNavbar_mCi1" id="新项目">新项目：<a href="#新项目" class="hash-link" aria-label="新项目：的直接链接" title="新项目：的直接链接">​</a></h5><p>模板默认依赖 React 17、TypeScript 4，可以直接使用。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="旧项目">旧项目：<a href="#旧项目" class="hash-link" aria-label="旧项目：的直接链接" title="旧项目：的直接链接">​</a></h5><p>手动升级项目依赖：</p><ul><li><code>react: ^17.0.0</code></li><li><code>react-dom: ^17.0.0</code></li><li><code>typescript: ^4.1.0</code></li><li><code>@typescript-eslint/parser: ^4.15.1</code></li><li><code>@typescript-eslint/eslint-plugin: ^4.15.1</code></li></ul><p>设置 ESLint 配置：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// .eslintrc</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">extends</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'taro/react'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">rules</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string-property property" style="color:#36acaa">'react/jsx-uses-react'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'off'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string-property property" style="color:#36acaa">'react/react-in-jsx-scope'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'off'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="42-react-默认支持-new-jsx-transform">4.2. React 默认支持 <a href="https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html" target="_blank" rel="noopener noreferrer">New JSX Transform</a><a href="#42-react-默认支持-new-jsx-transform" class="hash-link" aria-label="42-react-默认支持-new-jsx-transform的直接链接" title="42-react-默认支持-new-jsx-transform的直接链接">​</a></h4><p>New JSX Transform 让开发者<strong>不再需要在书写 JSX 前先引入 React</strong>。</p><p>如果不希望打开此功能，可以修改 Babel 配置的 <code>reactJsxRuntime</code> 选项为 <code>classes</code>：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// babel.config.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">presets</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string" style="color:#e3116c">'taro'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">framework</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'react'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">ts</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">reactJsxRuntime</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'classes'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="43-react-h5-端默认开启-fast-refresh">4.3. React H5 端默认开启 <a href="https://github.com/facebook/react/issues/16604#issuecomment-528663101" target="_blank" rel="noopener noreferrer">fast-refresh</a><a href="#43-react-h5-端默认开启-fast-refresh" class="hash-link" aria-label="43-react-h5-端默认开启-fast-refresh的直接链接" title="43-react-h5-端默认开启-fast-refresh的直接链接">​</a></h4><p>如果不希望打开此功能，可以修改 Taro 配置和 Babel 配置：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// config/index.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">h5</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">devServer</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">hot</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// babel.config.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">presets</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string" style="color:#e3116c">'taro'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">framework</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'react'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">ts</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">hot</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="二breakings">二、Breakings<a href="#二breakings" class="hash-link" aria-label="二、Breakings的直接链接" title="二、Breakings的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-react">1. React<a href="#1-react" class="hash-link" aria-label="1. React的直接链接" title="1. React的直接链接">​</a></h3><p>项目的 React 版本必须 <strong>&gt;= 16.14.0</strong>，或使用 <strong>17.0.0+</strong></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-vue2">2. Vue2<a href="#2-vue2" class="hash-link" aria-label="2. Vue2的直接链接" title="2. Vue2的直接链接">​</a></h3><blockquote><p>修复 Vue2 入口组件生命周期多次触发的问题，#7179</p></blockquote><p>用户编写的入口组件需要修改如下：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// app.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 3.0 中需要创建 Vue 对象</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">App</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Vue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 3.1 中只需要返回对象字面量</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">App</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-linaria">3. Linaria<a href="#3-linaria" class="hash-link" aria-label="3. Linaria的直接链接" title="3. Linaria的直接链接">​</a></h3><p>使用 <code>Linaria</code> 时，需要修改 <code>linaria.config.js</code> 的内容。</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OMZr">linaria.config.js</div><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">rules</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">action</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'linaria/evaluators'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">shaker</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// 此处的正则有改变</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">test</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex" style="color:#36acaa">node_modules[\/\\](?!@tarojs)</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">action</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ignore'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="三特性">三、特性<a href="#三特性" class="hash-link" aria-label="三、特性的直接链接" title="三、特性的直接链接">​</a></h2><ul><li>组件 <code>View</code> 增加 <code>catchMove</code> 属性，解决<a href="/docs/next/react-overall#%E9%98%BB%E6%AD%A2%E6%BB%9A%E5%8A%A8%E7%A9%BF%E9%80%8F">滚动穿透</a>问题。</li><li>同步所有内置小程序官方最新的 API、组件能力。</li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="四问题修复">四、问题修复<a href="#四问题修复" class="hash-link" aria-label="四、问题修复的直接链接" title="四、问题修复的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-重要">1. 重要<a href="#1-重要" class="hash-link" aria-label="1. 重要的直接链接" title="1. 重要的直接链接">​</a></h3><ul><li>修复百度小程序渲染问题，#7293。</li><li>修复、增强微信小程序转换为 Taro 的能力。</li><li>优化打包体积。</li><li>支付宝小程序支持引用自定义组件。</li><li>修复小程序分享 API 在使用 redux 时无法生效的问题，#7232。</li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-小程序">2. 小程序<a href="#2-小程序" class="hash-link" aria-label="2. 小程序的直接链接" title="2. 小程序的直接链接">​</a></h3><ul><li>修复多端文件没按照 Webpack <code>extension</code> 配置解析的问题，#6786，#7265 。</li><li>修复 style 属性设置失败的问题，#8678。</li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-h5">3. H5<a href="#3-h5" class="hash-link" aria-label="3. H5的直接链接" title="3. H5的直接链接">​</a></h3><ul><li>修复 H5 端 HMR 失效的问题。</li><li>支持路由 <strong>404</strong> 时触发 <code>App.onPageNotFound</code>，#7474。</li><li>修复表单组件 <code>slot</code> 兼容问题，#7363。</li><li>修复 <code>View</code> 和 <code>Text</code> 组件多行截断样式失败问题，#7472 #6741。</li><li>组件的 <code>style</code> 属性支持设置 CSS 变量，#7452。</li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="五升级指南">五、升级指南<a href="#五升级指南" class="hash-link" aria-label="五、升级指南的直接链接" title="五、升级指南的直接链接">​</a></h2><blockquote><p>从 v2.x 升级的同学需要先按 <a href="/docs/next/migration">迁移指南</a> 进行操作。</p></blockquote><p>从 v3.x 升级的同学，首先需要安装 v3.1 的 CLI 工具：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i -g @tarojs/cli</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>然后进入项目，删除 <strong>node_modules</strong>、<strong>yarn.lock</strong>、<strong>package-lock.json</strong>。</p><p>最后把 <code>package.json</code> 文件中 Taro 相关依赖的版本修改为 <code>^3.1.0</code>，再重新安装依赖。至此升级结束。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="六未来规划">六、未来规划<a href="#六未来规划" class="hash-link" aria-label="六、未来规划的直接链接" title="六、未来规划的直接链接">​</a></h2><p>得益于 <a href="https://github.com/wuba" target="_blank" rel="noopener noreferrer">58 技术团队</a> 的全力支持，Taro 3 即将支持 <strong>React Native</strong>，现已推出 3.2.0 的 Beta 版本，3.2.0 正式版将于本月底推出。欢迎抢先体验：<a href="/blog/2020-12-02-taro-3-2-0-cannary-1">《增加 React Native 支持的 Taro 3.2.0 版本测试通告》</a></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="七感谢">七、感谢<a href="#七感谢" class="hash-link" aria-label="七、感谢的直接链接" title="七、感谢的直接链接">​</a></h2><p>开源不易，贵在坚持。Taro 团队衷心感谢各位参与过本项目开源建设的朋友，无论是为 Taro 提交过代码、建设周边生态，还是反馈过问题，甚至只是茶余饭后讨论、吐槽 Taro 的各位。</p><p>现诚挚邀请您与 Taro 官方团队交流您的使用情况，有你相伴，Taro 更加精彩！<a href="https://wj.qq.com/s2/6494361/09cf" target="_blank" rel="noopener noreferrer">问卷地址</a></p><p>最后，特别感谢为 Taro 从 v3.0 走到 v3.1 贡献过代码的各位同学，不分先后：</p><ul><li>@wuchangming</li><li>@SyMind</li><li>@zhuxianguo</li><li>@Songkeys</li><li>@vdfor</li><li>@ZeroTo0ne</li><li>@zhaoguoweiLLHC</li><li>@Spencer17x</li><li>@wingsico</li><li>@w91</li><li>@fjc0k</li><li>@Leechael</li><li>@southorange1228</li><li>@alexlees</li><li>@cncolder</li><li>@rottenpen</li><li>@gcxfd</li><li>@twocucao</li><li>@pengtikui</li><li>@kala888</li><li>@LengYXin</li><li>@iugo</li><li>@jin-yufeng</li><li>@xuchengzone</li><li>@csolin</li><li>@xiaoyao96</li><li>@baranwang</li><li>@fred8617</li><li>@huanz</li><li>@Cslove</li><li>@002huiguo</li><li>@jazzqi</li><li>@Jetsly</li><li>@yuezk</li><li>@lukezhange001</li><li>@k55k32</li><li>@Soul-Stone</li><li>@hisanshao</li><li>@gjc9620</li><li>@younthu</li><li>@digiaries</li><li>@GoodbyeNJN</li><li>@Swordword</li><li>@helsonxiao</li><li>@Ininit</li><li>@atzcl</li><li>@taoqf</li><li>@Aysnine</li><li>@cjz9032</li><li>@z3rog</li><li>@doublethinkio</li><li>@Jackyzm</li><li>@ywzou</li><li>@koalaink</li><li>@mosqlee</li><li>@wangjuerong</li><li>@kdxcxs</li><li>@LiHDong</li><li>@ryougifujino</li><li>@GitaiQAQ</li><li>@logix-o</li><li>@CallMeXYZ</li></ul>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[Taro 助力京喜拼拼项目性能体验优化]]></title>
            <link>https://docs.taro.zone/blog/2021-02-08-taro-jxpp</link>
            <guid>https://docs.taro.zone/blog/2021-02-08-taro-jxpp</guid>
            <pubDate>Mon, 08 Feb 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[京喜拼拼团队技术选型使用 Taro 以便于实现多端需求，因此 Taro 团队有幸参与到 “京喜拼拼” 小程序的性能体验优化工作。本文梳理出一系列 Taro3 写法的最佳实践。]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorWithStickyNavbar_mCi1" id="背景">背景<a href="#背景" class="hash-link" aria-label="背景的直接链接" title="背景的直接链接">​</a></h2><p>2020 年是社区团购风起云涌的一年，互联网大厂纷纷抓紧一分一秒跑步进场。“京喜拼拼”是京东旗下的社区团购平台，依托京东供应链体系，精选低价好货，为社区用户提供次日达等优质服务。</p><p>​ 京喜拼拼团队技术选型使用 Taro 以便于实现多端需求，因此 Taro 团队有幸参与到 “京喜拼拼” 小程序的性能体验优化工作。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="全面体验---梳理-taro-写法最佳实践">全面体验 - 梳理 Taro 写法最佳实践<a href="#全面体验---梳理-taro-写法最佳实践" class="hash-link" aria-label="全面体验 - 梳理 Taro 写法最佳实践的直接链接" title="全面体验 - 梳理 Taro 写法最佳实践的直接链接">​</a></h2><p>我们全面体验后和熟悉业务代码后梳理出一系列 Taro3 写法的最佳实践：</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-性能相关">1. 性能相关<a href="#1-性能相关" class="hash-link" aria-label="1. 性能相关的直接链接" title="1. 性能相关的直接链接">​</a></h3><p>对小程序的性能影响较大的有两个因素，分别是 <code>setData</code> 的<strong>数据量</strong>和单位时间 <code>setData</code> 函数的<strong>调用次数</strong>。</p><p>当遇到性能问题时，在项目中打印 <code>setData</code> 的数据将非常有利于帮助定位问题。开发者可以通过进入 Taro 项目的 <code>dist/taro.js</code> 文件，搜索定位 <code>.setData</code> 的调用位置，然后对数据进行打印。</p><p>在 Taro 中，会对 <code>setData</code> 做 <strong>batch</strong> 捆绑更新操作，因此更多时候只需要考虑 <strong>setData 的数据量大小</strong>问题。</p><p>以下是我们梳理的开发者需要注意的写法问题，有一些问题需要开发者手动调整，一些问题 Taro 可以帮助自动化规避：</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="11-删除楼层节点需要谨慎处理">1.1. 删除楼层节点需要谨慎处理<a href="#11-删除楼层节点需要谨慎处理" class="hash-link" aria-label="1.1. 删除楼层节点需要谨慎处理的直接链接" title="1.1. 删除楼层节点需要谨慎处理的直接链接">​</a></h4><p>假设有一种这样一种结构：</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  &lt;!-- 轮播 --&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Slider</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  &lt;!-- 商品组 --&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Goods</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  &lt;!-- 模态弹窗 --&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">isShowModal </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Modal</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Taro3 目前对节点的删除处理是有缺陷的。当 <code>isShowModal</code> 由 <code>true</code> 变为 <code>false</code> 时，模态弹窗会从消失。此时 <code>Modal</code> 组件的兄弟节点都会被更新，<code>setData</code> 的数据是 <code>Slider</code> + <code>Goods</code> 组件的 DOM 节点信息。</p><p>一般情况下，影响不会太大，开发者无须由此产生心智负担。但倘若待删除节点的兄弟节点的 DOM 结构非常复杂，如一个个楼层组件，删除操作的副作用会导致 <code>setData</code> 数据量较大，从而影响性能。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="解决办法">解决办法：<a href="#解决办法" class="hash-link" aria-label="解决办法：的直接链接" title="解决办法：的直接链接">​</a></h5><p>目前我们可以这样优化，隔离删除操作：</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  &lt;!-- 轮播 --&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Slider</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  &lt;!-- 商品组 --&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Goods</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  &lt;!-- 模态弹窗 --&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">isShowModal </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Modal</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p>我们正在对删除节点的算法进行优化，完全规避这种不必要的 setData，于 v3.1 推出。</p></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="12-基础组件的属性尽量保持引用">1.2. 基础组件的属性尽量保持引用<a href="#12-基础组件的属性尽量保持引用" class="hash-link" aria-label="1.2. 基础组件的属性尽量保持引用的直接链接" title="1.2. 基础组件的属性尽量保持引用的直接链接">​</a></h4><p>假设基础组件（如 <code>View</code>、<code>Input</code> 等）的属性值为非基本类型时，尽量保持对象的引用。</p><p>假设有以下写法：</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Map</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">  </span><span class="token tag attr-name" style="color:#00a4db">latitude</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript number" style="color:#36acaa">22.53332</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">  </span><span class="token tag attr-name" style="color:#00a4db">longitude</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript number" style="color:#36acaa">113.93041</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">  </span><span class="token tag attr-name" style="color:#00a4db">markers</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">[</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">    </span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">      </span><span class="token tag script language-javascript literal-property property" style="color:#36acaa">latitude</span><span class="token tag script language-javascript operator" style="color:#393A34">:</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript number" style="color:#36acaa">22.53332</span><span class="token tag script language-javascript punctuation" style="color:#393A34">,</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">      </span><span class="token tag script language-javascript literal-property property" style="color:#36acaa">longitude</span><span class="token tag script language-javascript operator" style="color:#393A34">:</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript number" style="color:#36acaa">113.93041</span><span class="token tag script language-javascript punctuation" style="color:#393A34">,</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">    </span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag script language-javascript punctuation" style="color:#393A34">,</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">  </span><span class="token tag script language-javascript punctuation" style="color:#393A34">]</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f"></span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>每次渲染时，React 会对基础组件的属性做浅对比，这时发现 <code>markers</code> 的引用不同，就会去更新组件属性。最后导致 <code>setData</code> 次数增多、<code>setData</code> 数据量增大。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="解决办法-1">解决办法：<a href="#解决办法-1" class="hash-link" aria-label="解决办法：的直接链接" title="解决办法：的直接链接">​</a></h5><p>可以通过 <code>state</code>、闭包等手段保持对象的引用：</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Map</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">latitude</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript number" style="color:#36acaa">22.53332</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">longitude</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript number" style="color:#36acaa">113.93041</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">markers</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript keyword" style="color:#00009f">this</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">state</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">markers</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="13-小程序基础组件尽量不要挂载额外属性">1.3. 小程序基础组件尽量不要挂载额外属性<a href="#13-小程序基础组件尽量不要挂载额外属性" class="hash-link" aria-label="1.3. 小程序基础组件尽量不要挂载额外属性的直接链接" title="1.3. 小程序基础组件尽量不要挂载额外属性的直接链接">​</a></h4><p>基础组件（如 <code>View</code>、<code>Input</code> 等）如若设置了非标准的属性，目前这些额外属性会被一并进行 <code>setData</code>，而实际上小程序并不会理会这些属性，所以 <code>setData</code> 的这部分数据是冗余的。</p><p>例如 <code>Text</code> 组件的标准属性有 <code>selectable</code>、<code>user-select</code>、<code>space </code>、<code>decode</code> 四个，如果我们为它设置一个额外属性 <code>something</code>，那么这个额外的属性也是会被 setData。</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">something</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">extra</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p>Taro v3.1 将会自动过滤这些额外属性，届时这个限制将不再存在。</p></blockquote><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-体验相关">2. 体验相关<a href="#2-体验相关" class="hash-link" aria-label="2. 体验相关的直接链接" title="2. 体验相关的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="21-滚动穿透">2.1. 滚动穿透<a href="#21-滚动穿透" class="hash-link" aria-label="2.1. 滚动穿透的直接链接" title="2.1. 滚动穿透的直接链接">​</a></h4><p>在小程序开发中，滑动<strong>蒙层</strong>、<strong>弹窗</strong>等覆盖式元素时，滑动事件会冒泡到页面，使页面元素也跟着滑动，往往我们的解决办法是设置 <code>catchTouchMove</code> 从而阻止冒泡。</p><p>由于 <a href="/docs/react-overall#%E9%98%BB%E6%AD%A2%E6%BB%9A%E5%8A%A8%E7%A9%BF%E9%80%8F">Taro3 事件机制</a>的限制，小程序事件都以 <code>bind</code> 的形式进行绑定。所以和 Taro1、Taro2 不同，调用 <code>e.stopPropagation()</code> 并不能阻止滚动穿透。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="解决办法-2">解决办法：<a href="#解决办法-2" class="hash-link" aria-label="解决办法：的直接链接" title="解决办法：的直接链接">​</a></h5><ol><li>使用样式解决（推荐）</li></ol><p>给需要禁用滚动的组件写一个样式，类似于：</p><div class="language-css codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-css codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  overflow：hidden</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">height</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100</span><span class="token unit">vh</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ol start="2"><li>catchMove</li></ol><p>对于 <code>Map</code> 等极个别组件，使用样式固定宽高也无法阻止滚动，因为这些组件本身就具有滚动的能力。所以第一种办法处理不了冒泡到 <code>Map</code> 组件上的滚动事件。</p><p>这时候可以为 View 组件增加 catchMove 属性：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 这个 View 组件会绑定 catchtouchmove 事件而不是 bindtouchmove</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token plain"> catchMove </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="22-跳转预加载">2.2. 跳转预加载<a href="#22-跳转预加载" class="hash-link" aria-label="2.2. 跳转预加载的直接链接" title="2.2. 跳转预加载的直接链接">​</a></h4><p>在小程序中，从调用 <code>Taro.navigateTo</code> 等跳转类 API，到新页面触发 <code>onLoad</code> 会有一定延时。因此类如网络请求等操作可以提前到调用跳转 API 之前。</p><p>熟悉 Taro 的同学可能会想起 Taro1、Taro2 中的 <code>componentWillPreload</code> 钩子。但 Taro3 不再提供这个钩子，开发者可以使用 <code>Taro.preload()</code> 方法实现跳转预加载：</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">// pages/index.js</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Taro.preload(fetchSomething())</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Taro.navigateTo({ url: '/pages/detail' })</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">// pages/detail.js</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">console.log(getCurrentInstance().preloadData)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="23-建议把-tarogetcurrentinstance-的结果保存下来">2.3. 建议把 Taro.getCurrentInstance() 的结果保存下来<a href="#23-建议把-tarogetcurrentinstance-的结果保存下来" class="hash-link" aria-label="2.3. 建议把 Taro.getCurrentInstance() 的结果保存下来的直接链接" title="2.3. 建议把 Taro.getCurrentInstance() 的结果保存下来的直接链接">​</a></h4><p>开发中我们常常会调用 <code>Taro.getCurrentInstance()</code> 获取小程序的 app、page 对象、路由参数等数据。但<strong>频繁调用</strong>它可能会导致问题。因此推荐把 <code>Taro.getCurrentInstance()</code> 的结果在组件中保存起来，之后直接使用：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Index</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">React</span><span class="token class-name punctuation" style="color:#393A34">.</span><span class="token class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  inst </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token maybe-class-name">Taro</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">getCurrentInstance</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">componentDidMount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">inst</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="难啃的骨头---购物车页">难啃的骨头 - 购物车页<a href="#难啃的骨头---购物车页" class="hash-link" aria-label="难啃的骨头 - 购物车页的直接链接" title="难啃的骨头 - 购物车页的直接链接">​</a></h2><p>我们在低端机上受到了性能的困扰，尤其是在购物车页面卡顿最为明显。通过分析页面结构和反思 Taro 底层实现，我们主要采取了两项优化措施，提升了低端机型滚动的流畅度，同时将点击延时从 1.5s 降到 300ms。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-长列表优化">1. 长列表优化<a href="#1-长列表优化" class="hash-link" aria-label="1. 长列表优化的直接链接" title="1. 长列表优化的直接链接">​</a></h3><p>在 Taro3 中，我们新增了<strong>虚拟列表</strong>这样一个特殊的组件，帮助很多社区的开发者对超长列表进行优化，相信很多同学对虚拟列表的实现原理、包括下图都已经是很熟悉了，但购物车页却给我们提出了新的需求。</p><p><img loading="lazy" src="https://img20.360buyimg.com/ling/jfs/t1/125645/6/13305/50138/5f6aaaa4E2f20eba7/d70a2d2da2d68de1.jpg" alt="虚拟列表" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="11-不限制高度">1.1 不限制高度<a href="#11-不限制高度" class="hash-link" aria-label="1.1 不限制高度的直接链接" title="1.1 不限制高度的直接链接">​</a></h4><p>虚拟列表根据 <code>itemSize</code> 来计算每个节点的位置，如果节点的宽高不确定，在每个节点至少加载完成一次之前，我们很难去判断列表的真实尺寸。这也是为什么在<strong>虚拟列表</strong>的早期版本中我们并没有支持这样的特性，而是选择固定了每个节点的高度，避免让开发者使用虚拟列表时增加心智负担。</p><p>不过这个需求也并非不能完成，简单地调整虚拟列表实现和使用的逻辑，我们就可以轻松实现这个特性。</p><div class="language-diff codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-diff codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">import VirtualList from `@tarojs/components-advanced/dist/components/virtual-list`</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">function buildData (offset = 0) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> return Array(100).fill(0).map((_, i) =&gt; i + offset);</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49"> const Row = React.memo(({ index, style, data }) =&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa"> const Row = React.memo(({ id, index, style, data }) =&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> return (</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token deleted-sign deleted prefix deleted" style="color:#d73a49">-</span><span class="token deleted-sign deleted line" style="color:#d73a49">    &lt;View className={index % 2 ? 'ListItemOdd' : 'ListItemEven'} style={style}&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token deleted-sign deleted line" style="color:#d73a49"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">    &lt;View id={id} className={index % 2 ? 'ListItemOdd' : 'ListItemEven'} style={style}&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">     Row {index}</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   &lt;/View&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> );</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token plain">})</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export default class Index extends Component {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> state = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   data: buildData(0),</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> }</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> render() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   const { data } = this.state</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   const dataLen = data.length</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   return (</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">     &lt;VirtualList</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">       height={500} // 列表的高度</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">       width='100%' // 列表的宽度</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">       itemData={data} // 渲染列表的数据</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">       itemCount={dataLen} // 渲染列表的长度</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">       itemSize={100} // 列表单项的高度</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">       unlimitedSize={true} // 解开列表节点大小限制</span><br></span><span class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">     &gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">       {Row} // 列表单项组件，这里只能传入一个组件</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">     &lt;/VirtualList&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   );</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> }</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>可以看到，我们在新增了 <code>id</code> 传入来帮助获取每个节点在首次加载之后读取它的真实大小，得益于 Taro 跨平台的优势，这是重构<strong>虚拟列表</strong>组件中最简单的一步，有了这个基础，我们就可以将节点的实际大小和它们的位置信息关联到一起，让列表自己调整每个节点的位置，并呈现给用户。</p><p>而对于开发者，如果想要使用这个模式，只需要传入 <code>unlimitedSize</code> 就可以让虚拟列表解开高度限制。当然这并不意味着在使用虚拟列表时可以不需要传入节点大小， <code>itemSize</code> 在这个模式下将作为初始值辅助列表中每个节点位置信息的计算。</p><blockquote><p>如果<code>itemSize</code>和实际大小差别过大，在超长列表中会有较明显的问题，大家需要小心使用哦～</p></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="12-列表底部">1.2 列表底部<a href="#12-列表底部" class="hash-link" aria-label="1.2 列表底部的直接链接" title="1.2 列表底部的直接链接">​</a></h4><p>列表的底部区域可以帮助我们便捷地完成信息的展示，比如上拉加载等，对于虚拟列表也是如此。</p><div class="language-diff codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-diff codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">return (</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> &lt;VirtualList</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   height={500} // 列表的高度</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   width='100%' // 列表的宽度</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   itemData={data} // 渲染列表的数据</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   itemCount={dataLen} // 渲染列表的长度</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   itemSize={100} // 列表单项的高度</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">   renderBottom={&lt;View&gt;我就是底线&lt;/View&gt;}</span><br></span><span class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> &gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   {Row} // 列表单项组件，这里只能传入一个组件</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> &lt;/VirtualList&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">);</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>当然也有同学会注意到，在 <strong>虚拟列表</strong> 文档中是通过 <code>scrollOffset &gt; ((dataLen - 5) * itemSize + 100)</code> 这样的方法来判断是否触底，这是因为我们并没有在 <code>VirtualList</code> 中返回滚动的详细信息，这次我们也返回相关的数据，帮助大家更好地使用虚拟列表。</p><div class="language-diff codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-diff codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">interface VirtualListEvent&lt;T&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> /** 滚动方向，可能值为 forward 往前， backward 往后。 */</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> scrollDirection: 'forward' | 'backward'</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> /** 滚动距离 */</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> scrollOffset: number</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> /** 当滚动是由 scrollTo() 或 scrollToItem() 调用时返回 true，否则返回 false */</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> scrollUpdateWasRequested: boolean</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line"> /** 当前只有 React 支持 */</span><br></span><span class="token-line" style="color:#393A34"><span class="token unchanged line"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">  detail?: {</span><br></span><span class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">    scrollLeft: number</span><br></span><span class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">    scrollTop: number</span><br></span><span class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">    scrollHeight: number</span><br></span><span class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">    scrollWidth: number</span><br></span><span class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">    clientWidth: number</span><br></span><span class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">    clientHeight: number</span><br></span><span class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token inserted-sign inserted prefix inserted" style="color:#36acaa">+</span><span class="token inserted-sign inserted line" style="color:#36acaa">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token inserted-sign inserted line" style="color:#36acaa"></span><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="13-性能优化">1.3 性能优化<a href="#13-性能优化" class="hash-link" aria-label="1.3 性能优化的直接链接" title="1.3 性能优化的直接链接">​</a></h4><p>在虚拟列表中，无论是使用那种布局方式，都会造成页面的回流，所以不论选择哪一种对于浏览器内核渲染页面而言并没有很大的区别。但是如果使用 <code>relative</code>，对于列表来说，需要调整的节点样式要少得多。所以我们在新的虚拟列表中也支持了这样的定位模式，供开发者自由选择。对于低端机型来说，在我们完成整体的渲染性能优化之前，<code>relative</code> 模式已经能够让虚拟列表在低端机型上拥有不错的体验。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-渲染性能优化">2. 渲染性能优化<a href="#2-渲染性能优化" class="hash-link" aria-label="2. 渲染性能优化的直接链接" title="2. 渲染性能优化的直接链接">​</a></h3><p>Taro3 使用小程序的 <code>template</code> 进行渲染，一般情况下并不会使用原生自定义组件。这会导致一个问题，所有的 <code>setData</code> 更新都是由页面对象调用，如果我们的页面结构比较复杂，更新的性能就会下降。</p><p>层级过深时 <code>setData</code> 的数据结构：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">page</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">'root.cn.[0].cn.[0].cn.[0].cn.[0].markers'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>针对这个问题，主要的思路是<strong>借用小程序的原生自定义组件，以达到局部更新的效果</strong>，从而提升更新性能。</p><p>期望的 <code>setData</code> 数据结构：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">component</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">'cn.[0].cn.[0].markers'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>开发者有两种办法可以实现这个优化：</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="21-全局配置项-baselevel">2.1 全局配置项 baseLevel<a href="#21-全局配置项-baselevel" class="hash-link" aria-label="2.1 全局配置项 baseLevel的直接链接" title="2.1 全局配置项 baseLevel的直接链接">​</a></h4><p>对于不支持模板递归的小程序（微信、QQ、京东小程序），在 DOM 层级达到一定数量后，Taro 会使用原生自定义组件协助递归。</p><p>简单理解就是 DOM 结构超过 N 层后，会使用原生自定义组件进行渲染。N 默认是 16 层，可以通过修改配置项 <a href="/docs/config-detail#minibaselevel">baseLevel</a> 修改 N。</p><p>把 <code>baseLevel</code> 设置为 <code>8</code> 甚至 <code>4</code> 层，能非常有效地提升更新时的性能。但是设置是全局性的，会带来若干问题：</p><ol><li><code>flex</code> 布局在跨原生自定义组件时会失效，这是影响最大的一个问题。</li><li><code>SelectorQuery.select</code> 方法的<a href="https://developers.weixin.qq.com/miniprogram/dev/api/wxml/SelectorQuery.select.html" target="_blank" rel="noopener noreferrer">跨自定义组件的后代选择器</a>写法需要增加 <code>&gt;&gt;&gt;</code>：<code>.the-ancestor &gt;&gt;&gt; .the-descendant</code></li></ol><h4 class="anchor anchorWithStickyNavbar_mCi1" id="22-customwrapper-组件">2.2 CustomWrapper 组件<a href="#22-customwrapper-组件" class="hash-link" aria-label="2.2 CustomWrapper 组件的直接链接" title="2.2 CustomWrapper 组件的直接链接">​</a></h4><p>为了解决全局配置不灵活的问题，我们增加了一个基础组件 <code>CustomWrapper</code>。它的作用是创建一个原生自定义组件，对后代节点的 <code>setData</code> 将由此自定义组件进行调用，达到局部更新的效果。</p><p>开发者可以使用它去<strong>包裹遇到更新性能问题的模块</strong>，提升更新时的性能。因为 <code>CustomWrapper</code> 组件需要手动使用，开发者能够清楚“这层使用了自定义组件，需要避免自定义组件的两个问题”。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="例子">例子<a href="#例子" class="hash-link" aria-label="例子的直接链接" title="例子的直接链接">​</a></h5><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">CustomWrapper</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">GoodsList</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Item</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Item</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">GoodsList</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">CustomWrapper</span><span class="token operator" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="十全十美---体验评分平均-95">十全十美 - 体验评分平均 95+<a href="#十全十美---体验评分平均-95" class="hash-link" aria-label="十全十美 - 体验评分平均 95+的直接链接" title="十全十美 - 体验评分平均 95+的直接链接">​</a></h2><p>把开发者工具的体验评分给拉满，这里我们遇到了一个问题，开发者工具会识别所有绑定了点击事件的组件，如果组件的面积过小则提示点击区域过小，会影响“体验项”的评分。但是 <a href="/docs/platform-plugin#%E5%B1%9E%E6%80%A7%E7%B2%BE%E7%AE%80">Taro3 默认会为组件绑定上所有属性和事件</a>。这样会“误伤”一些组件，它们虽然面积很小，实际上并没有点击功能，但因为 Taro3 默认绑定的事件，被开发者工具认为点击区域过小，从而拉低体验评分。</p><p><code>Text</code> 组件的模板，默认绑定了所有属性和事件：</p><div class="language-html codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-html codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">template</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">name</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">tmpl_0_text</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">text</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">selectable</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">{{...}}</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">space</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">{{...}}</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">decode</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">{{...}}</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">user-select</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">{{...}}</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag special-attr attr-name" style="color:#00a4db">style</span><span class="token tag special-attr attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag special-attr attr-value value css language-css punctuation" style="color:#393A34">{</span><span class="token tag special-attr attr-value value css language-css punctuation" style="color:#393A34">{</span><span class="token tag special-attr attr-value value css language-css" style="color:#e3116c">...</span><span class="token tag special-attr attr-value value css language-css punctuation" style="color:#393A34">}</span><span class="token tag special-attr attr-value value css language-css punctuation" style="color:#393A34">}</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">{{...}}</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">{{...}}</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">bindtap</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">  </span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">template</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>因此我们为 <code>View</code>、<code>Text</code>、<code>Image</code> 组件各设立了一个 <strong>static 模板</strong>，当检测到组件没有绑定事件时，则使用 <code>static</code> 模板，避免被“误伤”。</p><p>另一方面，这一举动也能减少小程序 DOM 绑定的事件，对性能稍有提升，而且减少了属性让开发者工具的 <code>xml</code> 面板在调试时更加清晰。但这一方案也存在瑕疵，会导致编译后的 <code>base.wxml</code> 体积略微增大，和性能权衡来看，这仍然是值得的。</p><p><code>Text</code> 组件的 <code>static</code> 模板，没有绑定事件：</p><div class="language-html codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-html codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">template</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">name</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">tmpl_0_static-text</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">text</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">selectable</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">{{...}}</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">space</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">{{...}}</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">decode</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">{{...}}</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">user-select</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">{{...}}</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag special-attr attr-name" style="color:#00a4db">style</span><span class="token tag special-attr attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag special-attr attr-value value css language-css punctuation" style="color:#393A34">{</span><span class="token tag special-attr attr-value value css language-css punctuation" style="color:#393A34">{</span><span class="token tag special-attr attr-value value css language-css" style="color:#e3116c">...</span><span class="token tag special-attr attr-value value css language-css punctuation" style="color:#393A34">}</span><span class="token tag special-attr attr-value value css language-css punctuation" style="color:#393A34">}</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">{{...}}</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">{{...}}</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">  </span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">template</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h5 class="anchor anchorWithStickyNavbar_mCi1" id="优化后的购物车页体验评分">优化后的购物车页体验评分<a href="#优化后的购物车页体验评分" class="hash-link" aria-label="优化后的购物车页体验评分的直接链接" title="优化后的购物车页体验评分的直接链接">​</a></h5><p><img loading="lazy" src="https://storage.jd.com/cjj-pub-images/WechatIMG541.png" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="另一个战场---多端适配原生混合">另一个战场 - 多端适配&amp;原生混合<a href="#另一个战场---多端适配原生混合" class="hash-link" aria-label="另一个战场 - 多端适配&amp;原生混合的直接链接" title="另一个战场 - 多端适配&amp;原生混合的直接链接">​</a></h2><p>以一锅羊蝎子结束了支援之旅后，我们终于迎来了南方的艳阳天。但工作还没结束，仍有两项工作需要跟进。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="适配京东小程序">适配京东小程序<a href="#适配京东小程序" class="hash-link" aria-label="适配京东小程序的直接链接" title="适配京东小程序的直接链接">​</a></h3><p>适配京东小程序的过程比较顺利，需要改动的地方不多。</p><p>在此过程中 Taro3 最主要的升级是<strong>增强了对 HTML 文本的解析能力，增加了对 <code>&lt;style&gt;</code> 标签的支持</strong>。自此完全同步了 <code>wxparse</code> 的能力，开发者使用 React 的 <code>dangerouslySetInnerHTML</code> 或 Vue 的 <code>v-html</code> 即可很好地解析 HTML 文本，不需要单独引入第三方自定义组件去进行解析，统一了多端标准。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro3-与原生项目混合">Taro3 与原生项目混合<a href="#taro3-与原生项目混合" class="hash-link" aria-label="Taro3 与原生项目混合的直接链接" title="Taro3 与原生项目混合的直接链接">​</a></h3><p>过去我们对<strong>在 Taro 项目中混合使用原生</strong>的支持度较高。相反地，对<strong>在原生项目中混合使用 Taro</strong> 却没有太重视。但是市面上有着存量的原生开发小程序，他们接入 Taro 开发的改造成本往往非常大，最后只得放弃混合开发的想法。</p><p>经过本次项目，也驱使了我们更加关注这部分需求，在 Taro v3.0.25 后推出了一套完整的<a href="/docs/taro-in-miniapp">原生项目混合使用 Taro 的方案</a>。</p><p>方案主要支持了三种场景：</p><ol><li>在原生项目中使用 Taro 开发的页面。（已完成）</li><li>在原生项目的分包中运行完整的 Taro 项目。（已完成）</li><li>在原生项目中使用 Taro 开发的自定义组件。（正在开发中）</li></ol><p>希望以上方案能满足希望逐步接入 Taro 的开发同学。更多意见也欢迎在 <a href="https://github.com/NervJS/taro/issues" target="_blank" rel="noopener noreferrer">Github</a> 上给我们留言。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="尾声">尾声<a href="#尾声" class="hash-link" aria-label="尾声的直接链接" title="尾声的直接链接">​</a></h2><p>Taro 团队这次参与到 “京喜拼拼” 小程序的性能体验优化工作，让我们了解到 Taro3 的性能瓶颈所在，也体会到复杂业务的多样性。</p><p>2021 上半年我们将更加聚焦于提升框架开发体验和运行性能、与原生小程序的混合，还有生态建设的工作上。</p><p>最后祝大家春节快乐～新的一年牛气冲天!</p>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[Taro 3.1 beta 发布： 开放式架构新增 4 端支持]]></title>
            <link>https://docs.taro.zone/blog/2020-12-15-taro-3-1-beta</link>
            <guid>https://docs.taro.zone/blog/2020-12-15-taro-3-1-beta</guid>
            <pubDate>Tue, 15 Dec 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[面对小程序平台越来越多的大环境，Taro 是选择偏安一隅，只支持部分的主流小程序，还是成为所有小程序平台开发、多端转换的基础设施]]></description>
            <content:encoded><![CDATA[<p>自 7 月初我们正式发布了 Taro 3，至今半年时间已然略去。期间我们不断地修复着问题，同时也在构想着下一个 minor 版本。</p><p>面对小程序平台越来越多的大环境，Taro 是选择偏安一隅，只支持部分的主流小程序，还是成为所有小程序平台  开发、多端转换的基础设施，我们在 v3.1 给出了答案：<strong>开放式架构</strong>。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="一开放式架构">一、开放式架构<a href="#一开放式架构" class="hash-link" aria-label="一、开放式架构的直接链接" title="一、开放式架构的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="背景">背景<a href="#背景" class="hash-link" aria-label="背景的直接链接" title="背景的直接链接">​</a></h3><p>近年来业界推出的小程序平台越来越多，但 Taro 核心维护的平台只有 6 个（微信、支付宝、百度、头条、QQ、京东小程序），因此常常有同学提出能不能支持某某平台的 Feature Request。</p><p>基于目前的架构，对于单一平台的兼容性代码分布于 Taro 核心库的各个角落，涉及编译时与运行时等部分。支持一个新的平台需要改动所有的这些地方，开发复杂度高，同时社区也难以参与贡献。</p><p>为此我们萌生了打造一个开放式框架的想法。目标是可以通过插件的形式扩展 Taro 的端平台支持能力：</p><ul><li>插件开发者无需修改 Taro 核心库代码，即可编写出一个端平台插件。</li><li>插件使用者只需安装、配置端平台插件，即可把代码编译到指定平台。</li><li>开发者可以继承现有的端平台插件，然后对平台的适配逻辑进行自定义。</li></ul><h4 class="anchor anchorWithStickyNavbar_mCi1" id="端平台插件架构图">端平台插件架构图<a href="#端平台插件架构图" class="hash-link" aria-label="端平台插件架构图的直接链接" title="端平台插件架构图的直接链接">​</a></h4><p><img loading="lazy" src="https://storage.jd.com/cjj-pub-images/platform-plugin-all.png" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro-基于开放式架构的改动">Taro 基于开放式架构的改动<a href="#taro-基于开放式架构的改动" class="hash-link" aria-label="Taro 基于开放式架构的改动的直接链接" title="Taro 基于开放式架构的改动的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="1-同步了小程序最新特性">1. 同步了小程序最新特性<a href="#1-同步了小程序最新特性" class="hash-link" aria-label="1. 同步了小程序最新特性的直接链接" title="1. 同步了小程序最新特性的直接链接">​</a></h4><p>我们把内置支持的 6 个平台封装成了插件，CLI 默认会全部加载这些插件。封装的过程中，我们检索了各小程序最新的组件、API，并全数进行更新与支持，对齐各小程序最新的能力。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="2-新增支持转换的平台">2. 新增支持转换的平台<a href="#2-新增支持转换的平台" class="hash-link" aria-label="2. 新增支持转换的平台的直接链接" title="2. 新增支持转换的平台的直接链接">​</a></h4><p>借助开放式架构，我们编写了若干端平台插件，开发者安装后即可使用：</p><ul><li>企业微信插件：<a href="https://github.com/NervJS/taro-plugin-platform-weapp-qy" target="_blank" rel="noopener noreferrer">@tarojs/plugin-platform-weapp-qy</a></li><li>钉钉小程序插件：<a href="https://github.com/NervJS/taro-plugin-platform-alipay-dd" target="_blank" rel="noopener noreferrer">@tarojs/plugin-platform-alipay-dd</a></li><li>支付宝 IOT 小程序插件：<a href="https://github.com/NervJS/taro-plugin-platform-alipay-iot" target="_blank" rel="noopener noreferrer">@tarojs/plugin-platform-alipay-iot</a></li><li>快手小程序插件（体验版）：<a href="https://github.com/NervJS/taro-plugin-platform-kwai" target="_blank" rel="noopener noreferrer">@tarojs/plugin-platform-kwai</a></li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="对开发者的影响">对开发者的影响<a href="#对开发者的影响" class="hash-link" aria-label="对开发者的影响的直接链接" title="对开发者的影响的直接链接">​</a></h3><p>没有影响，改动属于 Taro 内部架构重构，不会影响开发者使用。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="还可以做什么有意思的事">还可以做什么有意思的事<a href="#还可以做什么有意思的事" class="hash-link" aria-label="还可以做什么有意思的事的直接链接" title="还可以做什么有意思的事的直接链接">​</a></h3><p>除了扩展新的编译平台，我们还可以通过继承于现有的端平台插件，来编写自定义的端平台插件，对平台的适配逻辑进行自定义：</p><blockquote><p>如何编写端平台插件：<a href="/docs/next/platform-plugin/how">文档地址</a></p></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="1-快速修复问题">1. 快速修复问题<a href="#1-快速修复问题" class="hash-link" aria-label="1. 快速修复问题的直接链接" title="1. 快速修复问题的直接链接">​</a></h4><p>由于小程序平台众多，而且它们也在不断地迭代，往往会出现 Taro 对某个小程序新推出的组件或 API 支持不及时的问题。这时开发者首先需要联系 Taro 团队，再等待我们跟进修复、发布新版本后才能正常使用，平均需要等待一周或两周的时间才能得到解决。</p><p>而基于开放式架构，开发者能够通过继承某个端平台插件，迅速开发出自定义端平台插件，完成对这些新组件或 API 的支持，无需等待 Taro 发布版本。</p><p>为微信小程序拓展 API 的例子：</p><ol><li>运行时注入 API：</li></ol><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">/** plugin/apis.ts */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">initNativeApi</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">taro</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 挂载额外 API：Taro.xxx()</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  taro</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method-variable function-variable method function property-access" style="color:#d73a49">xxx</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">/** plugin/runtime.ts */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> mergeReconciler </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/shared'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> initNativeApi </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./apis'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 把 initNativeApi 合并到 reconciler 中。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 这样可以侵入 Taro 运行时并修改 Taro 对象，达到增加 API 的目的</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">mergeReconciler</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  initNativeApi</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ol start="2"><li>端平台插件入口：</li></ol><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">/** plugin/program.ts */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">Weapp</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/plugin-platform-weapp'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 继承微信小程序</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Example</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">Weapp</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  platform </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'example'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 步骤 1 中，runtime 文件的路径</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  runtimePath </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">@tarojs/plugin-platform-example/dist/runtime</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">/** plugin/index.ts */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Example</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./program'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> type </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token maybe-class-name">IPluginContext</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/service'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter literal-property property" style="color:#36acaa">ctx</span><span class="token parameter operator" style="color:#393A34">:</span><span class="token parameter"> </span><span class="token parameter maybe-class-name">IPluginContext</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ctx</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">registerPlatform</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'example'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">useConfigName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'mini'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">fn</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter"> config </span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> program </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Example</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> config</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      program</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">start</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="2-属性精简">2. 属性精简<a href="#2-属性精简" class="hash-link" aria-label="2. 属性精简的直接链接" title="2. 属性精简的直接链接">​</a></h4><p>因为小程序组件的属性和事件都必须静态写死，不可以动态添加，所以 Taro 会把组件的所有属性和事件全部在模板里提前进行绑定。</p><p>但实际项目中很多情况下并不会使用到组件的所有属性和事件，循环这些冗余的属性和事件绑定也会占据很大一部分的体积，另外太多的事件绑定也会在一定程度上降低小程序的性能。</p><p>以下是 View 组件模板的伪代码：</p><div class="language-html codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-html codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">template</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">name</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">tmpl_0_view</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">view</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">hover-class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">hover-stop-propagation</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">hover-start-time</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">hover-stay-time</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">animation</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">onTouchStart</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">onTouchMove</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">onTouchEnd</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">onTouchCancel</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">onLongTap</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">onAnimationStart</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">onAnimationIteration</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">onAnimationEnd</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">onTransitionEnd</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">disable-scroll</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">hidden</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">onAppear</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">onDisappear</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">onFirstAppear</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag special-attr attr-name" style="color:#00a4db">style</span><span class="token tag special-attr attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag special-attr attr-value value css language-css" style="color:#e3116c">...</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">onTap</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">...</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">  </span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">template</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Taro 需要把 <code>View</code> 组件的所有属性和事件提前进行绑定，才能满足不同开发者的使用需求。但可能对于某位开发者来说，整个项目的 <code>View</code> 组件都没有使用到 <code>hover-stop-propagation</code> 这个属性，那么则可以考虑把它精简掉，不编译到 <code>View</code> 组件的模板当中。</p><blockquote><p>需要注意的是，对属性的精简可能会引起不必要的问题、使项目的维护变得困难，特别当项目变大，开发者众多时，需要谨慎设计和使用。</p></blockquote><h2 class="anchor anchorWithStickyNavbar_mCi1" id="二重要问题修复">二、重要问题修复<a href="#二重要问题修复" class="hash-link" aria-label="二、重要问题修复的直接链接" title="二、重要问题修复的直接链接">​</a></h2><p>v3.1 除了包括开放式架构的调整外，也不忘巩固核心。以下是 v3.1 对重要问题的修复情况，有一些在 v3.0 的 patch 版本已经推出，一些则是 v3.1 中才推出，均予以列出：</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-breaking-change修复-vue-中-app-生命周期被调用两次的问题">1. <!-- -->[Breaking Change]<!-- -->修复 Vue 中 App 生命周期被调用两次的问题<a href="#1-breaking-change修复-vue-中-app-生命周期被调用两次的问题" class="hash-link" aria-label="1-breaking-change修复-vue-中-app-生命周期被调用两次的问题的直接链接" title="1-breaking-change修复-vue-中-app-生命周期被调用两次的问题的直接链接">​</a></h3><p>注意，此修复含有【Breaking Change】，如果你正在把 Vue 项目从 v3.0 升级到 v3.1，需要对入口组件进行如下修改：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// app.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// v3.0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Taro 运行时内部本来就会调用一次 new Vue，</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 用户的入口组件多调用一次的话，会导致生命周期函数重复触发</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">App</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Vue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// v3.1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 用户在入口文件中只需要导出入口组件的配置对象，不需要再调用 new Vue</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">App</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-优化反向转换功能">2. 优化反向转换功能<a href="#2-优化反向转换功能" class="hash-link" aria-label="2. 优化反向转换功能的直接链接" title="2. 优化反向转换功能的直接链接">​</a></h3><p>v3.1 中我们对反向转换功能（<code>Taro convert</code>）进行了广泛的验证，修复了大量问题，现已达到比较高可用的状态。</p><blockquote><p><a href="/docs/next/taroize">详细文档</a></p></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="主要变动">主要变动:<a href="#主要变动" class="hash-link" aria-label="主要变动:的直接链接" title="主要变动:的直接链接">​</a></h4><ul><li>支持使用 <code>Behavior</code></li><li>支持使用自定义 tabbar</li><li>支持配置全局 <code>usingComponents</code></li><li>修复 <code>catch</code> 事件不能阻止冒泡的问题</li><li>修复不支持挂载额外属性到 app 对象的问题</li><li>修复循环的 key 值没有被正确编译的问题</li><li>修复编译时没有处理样式中引用的字体的问题</li></ul><h4 class="anchor anchorWithStickyNavbar_mCi1" id="反向转换例子">反向转换例子<a href="#反向转换例子" class="hash-link" aria-label="反向转换例子的直接链接" title="反向转换例子的直接链接">​</a></h4><p>我们尝试使用 <code>taro convert</code> 转换了四个 GitHub 上最热门的开源微信小程序应用，它们转换之后都表现良好：</p><p><a href="https://github.com/EastWorld/wechat-app-mall" target="_blank" rel="noopener noreferrer">EastWorld/wechat-app-mall</a> - 微信小程序商城</p><p><a href="https://github.com/tumobi/nideshop-mini-program" target="_blank" rel="noopener noreferrer">tumobi/nideshop-mini-program</a> - 基于 Node.js + MySQL 开发的开源微信小程序商城</p><p><a href="https://github.com/RebeccaHanjw/weapp-wechat-zhihu" target="_blank" rel="noopener noreferrer">RebeccaHanjw/weapp-wechat-zhihu</a> - 仿知乎</p><p><a href="https://github.com/jectychen/wechat-v2ex" target="_blank" rel="noopener noreferrer">jectychen/wechat-v2ex</a> - V2EX</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-支持阻止小程序滑动穿透">3. 支持阻止小程序滑动穿透<a href="#3-支持阻止小程序滑动穿透" class="hash-link" aria-label="3. 支持阻止小程序滑动穿透的直接链接" title="3. 支持阻止小程序滑动穿透的直接链接">​</a></h3><p>v3.0 推出后反馈最多的问题之一，就是在 <code>touchmove</code> 事件回调中调用 <code>e.stopPropagation()</code> 并不能阻止滑动穿透。</p><p>这是因为 Taro 3 的事件冒泡机制是单独在小程序逻辑层实现，所有事件都是绑定的 <code>bind</code> 而不是 <code>catch</code>。因此<code>touchmove</code> 事件回调中调用 <code>e.stopPropagation()</code> 只会阻止上层组件的事件回调触发，而没有 <code>catchtouchmove</code> 能阻止滑动穿透的能力。</p><p>v3.1 中我们为 <code>View</code> 组件增加了 <code>catchMove</code> 属性，只要 <code>catchMove</code> 属性值为 <code>true</code>，就会使用 <code>catchtouchmove</code> 代替 <code>bindtouchmove</code> 进行事件绑定，从而获得阻止滑动穿透的能力。</p><p>用法：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"parent"</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"modal"</span><span class="token plain"> catchMove</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    滑动 </span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">modal</span><span class="token plain"> 时，并不会令 </span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">parent</span><span class="token plain"> 也一起滑动</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="4-修复使用了-hoc-后分享生命周期方法不触发的问题">4. 修复使用了 HOC 后分享生命周期方法不触发的问题<a href="#4-修复使用了-hoc-后分享生命周期方法不触发的问题" class="hash-link" aria-label="4. 修复使用了 HOC 后分享生命周期方法不触发的问题的直接链接" title="4. 修复使用了 HOC 后分享生命周期方法不触发的问题的直接链接">​</a></h3><p>倘若我们在 v3.0 的 React 框架下，把页面使用 HOC 进行包裹，如 <code>react-redux</code> 的 <strong>@connect</strong>，那么我们设置的一些分享生命周期：<code>onShareAppMessage</code>, <code>onShareTimeline</code> 都将不会被触发。这时需要在页面的配置文件中对应设置 <code>enableShareAppMessage: true</code>、<code>enableShareTimeline: true</code> 才能解决。</p><p>v3.1 会在编译时扫描 <code>onShareAppMessage</code>、 <code>onShareTimeline</code> 是否有被调用，进而自动在页面配置文件中加上对应配置，大部分场景下不需要用户手动设置。</p><blockquote><p>注意，如果分享生命周期被封装在基类或自定义 Hooks 中，还是需要手动加上对应配置。</p></blockquote><h3 class="anchor anchorWithStickyNavbar_mCi1" id="5-修复支付宝小程序使用原生自定义组件报错的问题">5. 修复支付宝小程序使用原生自定义组件报错的问题<a href="#5-修复支付宝小程序使用原生自定义组件报错的问题" class="hash-link" aria-label="5. 修复支付宝小程序使用原生自定义组件报错的问题的直接链接" title="5. 修复支付宝小程序使用原生自定义组件报错的问题的直接链接">​</a></h3><p>在 v3.0，支付宝小程序使用原生自定义组件时，会报“元素不存在”的错误。</p><p>这是因为支付宝小程序中规定，页面引用到的自定义组件，需要在页面对应的 axml 文件中定义。而 Taro 会把自定义组件定义在全局模板文件 base.axml 中。</p><p>因此 v3.1 对</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="6-优化-basexml-模板体积">6. 优化 base.xml 模板体积<a href="#6-优化-basexml-模板体积" class="hash-link" aria-label="6. 优化 base.xml 模板体积的直接链接" title="6. 优化 base.xml 模板体积的直接链接">​</a></h3><p>v3.0 刚推出，很多同学反馈小程序体积过大的问题，其中一个原因是编译产物中 <code>base.xml</code> 这个模板的体积太大了。</p><p>自 v3.0.9 后，我们对模板生成逻辑进行了重构：可能嵌套引用自身的组件，模板默认生成 16 次，如 <code>View</code>。不会嵌套引用自身的组件，模板只会生成一次，如 <code>Map</code>。</p><p>以微信小程序为例，在最极端的情况下体积优化率达 <strong>85%</strong> 以上：</p><p><img loading="lazy" src="https://storage.jd.com/cjj-pub-images/WX20201210-223058.png" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="三升级指南">三、升级指南<a href="#三升级指南" class="hash-link" aria-label="三、升级指南的直接链接" title="三、升级指南的直接链接">​</a></h2><blockquote><p>从 v2.x 升级的同学需要先按 <a href="/docs/next/migration">迁移指南</a> 进行操作。</p></blockquote><p>从 v3.x 升级的同学，首先需要安装 v3.1 的 CLI 工具：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i -g @tarojs/cli@next</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>然后进入项目，把 <code>package.json</code> 文件中 taro 相关依赖的版本修改为 <code>3.1.0-beta.4</code>，再重新安装依赖（建议先把 <strong>node_modules</strong> 文件夹删除）。至此升级结束。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="breaking">Breaking<a href="#breaking" class="hash-link" aria-label="Breaking的直接链接" title="Breaking的直接链接">​</a></h3><p>v3.1 带来了一个 Breaking Change，使用 <strong>Vue</strong> 进行开发的同学需要按指示进行修改。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="四未来规划">四、未来规划<a href="#四未来规划" class="hash-link" aria-label="四、未来规划的直接链接" title="四、未来规划的直接链接">​</a></h2><p>Taro 3 即将支持 <strong>React Native</strong>，欢迎体验：<a href="/blog/2020-12-02-taro-3-2-0-cannary-1">《增加 React Native 支持的 Taro 3.2.0 版本测试通告》</a></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="五感恩社区">五、感恩社区<a href="#五感恩社区" class="hash-link" aria-label="五、感恩社区的直接链接" title="五、感恩社区的直接链接">​</a></h2><p>开源不易，贵在坚持。Taro 团队衷心感谢各位参与过本项目开源建设的朋友，无论是为 Taro 提交过代码、建设周边生态，还是反馈过问题，甚至只是茶余饭后讨论、吐槽 Taro 的各位。</p><p>现诚挚邀请您与 Taro 官方团队交流您的使用情况，有你相伴，Taro 更加精彩！<a href="https://wj.qq.com/s2/6494361/09cf" target="_blank" rel="noopener noreferrer">问卷地址</a></p><p>最后，特别感谢为 Taro 从 v3.0 走到 v3.1 贡献过代码的各位，排名不分先后：</p><ul><li>@wuchangming</li><li>@SyMind</li><li>@zhuxianguo</li><li>@Songkeys</li><li>@vdfor</li><li>@Spencer17x</li><li>@wingsico</li><li>@w91</li><li>@fjc0k</li><li>@Leechael</li><li>@southorange1228</li><li>@alexlees</li><li>@cncolder</li><li>@rottenpen</li><li>@gcxfd</li><li>@twocucao</li><li>@pengtikui</li><li>@kala888</li><li>@LengYXin</li><li>@iugo</li><li>@xuchengzone</li><li>@csolin</li><li>@xiaoyao96</li><li>@zhaoguoweiLLHC</li><li>@baranwang</li><li>@fred8617</li><li>@huanz</li><li>@Cslove</li><li>@002huiguo</li><li>@jazzqi</li><li>@Jetsly</li><li>@yuezk</li><li>@lukezhange001</li><li>@k55k32</li><li>@Soul-Stone</li><li>@hisanshao</li><li>@gjc9620</li><li>@younthu</li><li>@digiaries</li><li>@ZeroTo0ne</li><li>@GoodbyeNJN</li></ul>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[增加 React Native 支持的 Taro 3.2.0 版本测试通告]]></title>
            <link>https://docs.taro.zone/blog/2020-12-02-taro-3-2-0-cannary-1</link>
            <guid>https://docs.taro.zone/blog/2020-12-02-taro-3-2-0-cannary-1</guid>
            <pubDate>Wed, 02 Dec 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[增加 React Native 支持的 Taro 3.2.0 版本测试通告]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorWithStickyNavbar_mCi1" id="相关资料链接">相关资料链接<a href="#相关资料链接" class="hash-link" aria-label="相关资料链接的直接链接" title="相关资料链接的直接链接">​</a></h2><ol start="0"><li><a href="https://github.com/NervJS/taro-rfcs/pull/8" target="_blank" rel="noopener noreferrer">版本内容对应的 RFC</a></li><li><a href="https://github.com/NervJS/taro/tree/feat/react-native" target="_blank" rel="noopener noreferrer">3.2.0 canary 版本分支</a></li><li><a href="https://github.com/NervJS/taro-native-shell/tree/0.63.2" target="_blank" rel="noopener noreferrer">壳工程分支</a></li><li><a href="https://github.com/wuba/taro-react-native" target="_blank" rel="noopener noreferrer">最新进展，可关注 58 开源</a></li></ol><h2 class="anchor anchorWithStickyNavbar_mCi1" id="新版本特性一览">新版本特性一览<a href="#新版本特性一览" class="hash-link" aria-label="新版本特性一览的直接链接" title="新版本特性一览的直接链接">​</a></h2><ol start="0"><li>更快的编译速度<ol><li>基于全新的编译体系，启动速度提升 1 倍</li></ol></li><li>更便捷的开发调试体验<ol><li>去掉了 webpack 后，source-map 的支持更加简单，这对于开发体验是一个巨大的提升</li></ol></li><li>更全面的 API 和组件<ol><li>新增 38 个 API</li><li>新增 7 个组件</li></ol></li><li>更灵活的接入方式<ol><li>支持 React Native 0.60 及以上版本，可自行定制，我们也将探索更多版本的支持</li><li>API 和组件支持按需加载，未使用的原生依赖，无需安装</li><li>架构完全基于 React Native 生态，相关工具、组件等均可轻松接入</li></ol></li><li>更稳定的源码<ol><li>使用 TypeScript、ESlint、Jest，保证代码质量</li><li>欢迎社区贡献</li></ol></li></ol><h2 class="anchor anchorWithStickyNavbar_mCi1" id="如何开始">如何开始<a href="#如何开始" class="hash-link" aria-label="如何开始的直接链接" title="如何开始的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="新仓库启动-bundler">新仓库启动 bundler<a href="#新仓库启动-bundler" class="hash-link" aria-label="新仓库启动 bundler的直接链接" title="新仓库启动 bundler的直接链接">​</a></h3><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain"># 注意：@tarojs/cli 最新版本还未发布，体验版在标签 canary 下</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ yarn global add @tarojs/cli@canary</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 创建并初始化 Taro 项目，框架需选择 React</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ taro init &lt;projectName&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 进入初始化目录</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ cd &lt;projectName&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 设置环境变量DEVTAG，首次使用会安装体验版相关依赖，需要等待一段时间</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 支持 --port 选项手动指定端口</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ DEVTAG=@canary yarn dev:rn --port 8081</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># bundler 启动成功会显示如下信息</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># React-Native Dev server is running on port: 8081</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="原-3x-仓库启动-bundler">原 3.x 仓库启动 bundler<a href="#原-3x-仓库启动-bundler" class="hash-link" aria-label="原 3.x 仓库启动 bundler的直接链接" title="原 3.x 仓库启动 bundler的直接链接">​</a></h3><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain"># 将`package.json`中 tarojs 相关依赖版本改为 `^3.2.0-canary.3`</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 特别注意 `@tarojs/taro-rn` `@tarojs/rn-runner` 可能被安装为 2.x 版本</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 重新安装依赖</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ yarn</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 启动bundler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ yarn dev:rn --port 8081</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># bundler 启动成功会显示如下信息</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># React-Native Dev server is running on port: 8081</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="低版本仓库">低版本仓库<a href="#低版本仓库" class="hash-link" aria-label="低版本仓库的直接链接" title="低版本仓库的直接链接">​</a></h3><p>请先参照<a href="/docs/migration">升级指南</a>，升级至 3.x 版本，再参照上述步骤</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="启动-app">启动 APP<a href="#启动-app" class="hash-link" aria-label="启动 APP的直接链接" title="启动 APP的直接链接">​</a></h3><p>新版本壳工程对应地址为 <a href="https://github.com/NervJS/taro-native-shell/tree/0.63.2" target="_blank" rel="noopener noreferrer">https://github.com/NervJS/taro-native-shell/tree/0.63.2</a></p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain"># 下载壳工程</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ git clone -b 0.63.2 git@github.com:NervJS/taro-native-shell.git</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 安装依赖</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ yarn</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># iOS 需要安装原生依赖</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ npx pod-install</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 构建并启动应用</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ yarn android -- --no-packager</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 加载成功，可以看到 “Hello world”</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 如 IP 或端口号不一致，需自行配置</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>如果你有相关的疑问，<a href="https://github.com/NervJS/taro/issues/8180" target="_blank" rel="noopener noreferrer">点击链接参与讨论</a></p>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[Taro 版本升级权威指南]]></title>
            <link>https://docs.taro.zone/blog/2020-09-01-taro-versions</link>
            <guid>https://docs.taro.zone/blog/2020-09-01-taro-versions</guid>
            <pubDate>Tue, 01 Sep 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[你是否对 Taro 辣么多版本感到非常困惑？你是否对升级 Taro 感到束手无策？本篇文章将为你答疑解惑，全面解读 Taro 开发那些事儿。]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://storage.360buyimg.com/taro-resource/taro_version2.jpg" class="img_ZOZK"></p><blockquote><p><a href="https://github.com/NervJS/taro" target="_blank" rel="noopener noreferrer">Taro</a> 是一款由京东<a href="https://aotu.io" target="_blank" rel="noopener noreferrer">凹凸实验室</a>推出的开放式跨端跨框架解决方案，致力于解决小程序、H5、React Native 的跨端同构问题，支持同时使用 React 和 Vue 来进行开发。本文由 Taro 团队成员隔壁老李撰写，旨在帮助 Taro 开发者厘清当前 Taro 多版本之间关系的那些事儿，同时解答有关 Taro 3、Taro RN 支持以及 Taro UI 的一些困惑。</p></blockquote><p>自从 Taro 在今年 7 月份推出 <a href="https://juejin.im/post/6844904205367377933" target="_blank" rel="noopener noreferrer">3.0 版本</a>，宣布同时支持 React 和 Vue 来开发跨端应用之后，Taro 的关注度得到了进一步地提升，很多开发者开始尝试升级自身项目到 3.0 来体验新的特性，同时，Taro 社区也开始迎来一些新朋友，业界有很多 Vue 开发者在做技术选型时开始将目光投向 Taro。</p><p>但由于 Taro 大版本之间差异较大，而社区内很多关于 Taro 的教程文章以及示例项目目前还停留 Taro 1/2 时代，导致很多开发者使用 Taro 3.0 尝试时出现奇怪的问题，所以 Taro 团队想通过本文帮助大家理解 Taro 各个版本之间的联系，协助大家更好地完成版本迁移，避免出现一些难以解决的奇怪问题。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="区分-taro-版本的火眼金睛">区分 Taro 版本的火眼金睛<a href="#区分-taro-版本的火眼金睛" class="hash-link" aria-label="区分 Taro 版本的火眼金睛的直接链接" title="区分 Taro 版本的火眼金睛的直接链接">​</a></h2><p>Taro 目前有 3 个大版本，但如果按照架构来真正划分时代的话，Taro 1/2 属于第一代架构，而 Taro 3 则属于第二代架构，两代架构差异巨大，甚至可以完全认为是 2 个框架。当然，这只是对于框架内核而言，对于开发者，Taro 团队已经尽量在保证大版本之间的兼容性，着力降低版本迁移的难度。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro-12">Taro 1/2<a href="#taro-12" class="hash-link" aria-label="Taro 1/2的直接链接" title="Taro 1/2的直接链接">​</a></h3><p>Taro 1/2 属于编译型架构，主要通过对类 React 代码进行语法编译转换地方式，得到各个端可以运行的代码，再配合非常轻量的运行时适配，以及根据标准组件库、API 进行差异抹平，从而实现多端适配的目的，整体架构如下。</p><p><img loading="lazy" src="https://storage.360buyimg.com/taro-resource/taro1.jpg" class="img_ZOZK"></p><p>而 Taro 1 与 Taro 2 的都是基于这种架构建立的方案，他们之间的区别主要是 Taro 1 在小程序端是自建构建体系，而 Taro 2 则是所有端都采用 Webpack 进行编译，可以降低 Taro 自身编译系统的复杂度，同时能够让开发者使用 Webpack 的生态来自定义编译过程和结果，可以认为 Taro 2 是 Taro 1 和 3 之间的一个过渡性版本。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro-3">Taro 3<a href="#taro-3" class="hash-link" aria-label="Taro 3的直接链接" title="Taro 3的直接链接">​</a></h3><p>Taro 3 则可以大致理解为解释型架构（相对于 Taro 1/2 而言），主要通过在小程序端模拟实现 DOM、BOM API 来让前端框架直接运行在小程序环境中，从而达到小程序和 H5 统一的目的，而对于生命周期、组件库、API、路由等差异，我们依然可以通过定义统一标准，各端负责各自实现的方式来进行抹平。而正因为 Taro 3 的原理，所以我们可以在 Taro 3 中同时支持 React、Vue 等框架，甚至我们还支持了 jQuery，在不久的将来我们还能支持让开发自定义地去拓展其他框架的支持，比如 Angular，Taro 3 整体架构如下。</p><p><img loading="lazy" src="https://storage.360buyimg.com/taro-resource/taro2.jpg" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="版本选择的取舍">版本选择的取舍<a href="#版本选择的取舍" class="hash-link" aria-label="版本选择的取舍的直接链接" title="版本选择的取舍的直接链接">​</a></h3><p>通过上述内容可以看出，Taro 两代架构之间差异巨大，从而也就导致了他们之间的特性也很不一样。Taro 1/2 和 Taro 3 之间特性的差异主要体现在开发体验与性能上。</p><p>从开发体验上看，Taro 3 明显是优于 Taro 1/2 的，受益于 Taro 3 架构的原理，我们可以在 Taro 3 中使用完整的 React、Vue 语法特性来进行开发，从而在开发体验上让多端开发无限接近于 Web 开发，这对深耕 Web 而初次接触小程序的开发者来说是非常友好的。而对 Taro 熟悉的朋友肯定知道 Taro 1/2 在开发时会有诸多限制，尤其是在 JSX 书写上，我们总会需要一些手段来绕过这些限制，这就导致开发体验小有不足。</p><p>从性能上看，某些情况下 Taro 1/2 会优于 Taro 3，如果你的应用非常复杂，页面节点非常多，有非常多的大规模更新操作，对性能要求比较苛刻的话，Taro 1/2 会是不错的选择，而 Taro 1 和 2 我们更推荐使用 Taro 2。当然，根据我们的测试，对于大部分应用来说 Taro 1/2 和 Taro 3 的性能差异并不明显，我们后续会给出 benchmark 来印证这一点，而且，对于 Taro 3 本身在性能存在劣势的场景，Taro 官方团队已经给出了相应的解决方案来应对。比如，提升首次渲染速度，我们可以使用<a href="/docs/prerender">预渲染</a>；对于无限滚动加载的列表场景，我们提供了<a href="/docs/virtual-list">虚拟列表组件</a>。</p><p>对于开发者来说，开发体验和性能往往是需要权衡来寻找平衡点的，缺一不可，所以现阶段，我们更加推荐使用 Taro 3 来开发多端应用。而且现阶段 Taro 团队的研发重心主要放在 Taro 3 上，新的特性会优先在 Taro 3 上进行尝试，所以，在未来 Taro 3 将更具有想象力，会有更多好玩的东西推出来。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="平滑升级不完全指南">平滑升级不完全指南<a href="#平滑升级不完全指南" class="hash-link" aria-label="平滑升级不完全指南的直接链接" title="平滑升级不完全指南的直接链接">​</a></h2><p>Taro 2 和 Taro 3 都有对应的迁移指南，根据迁移指南往往能规避大部分的问题。</p><p>Taro 1 升级到 Taro 2 的<a href="/docs/2.x/migrate-to-2">迁移指南</a>。</p><p>Taro 1/2 升级到 Taro 3 的<a href="/docs/migration">迁移指南</a>。</p><p>当然，迁移指南肯定无法覆盖到所有问题所有情况，我们在 Taro 交流群的日常交流中总是能观察到一些迁移的问题，所以在这里，我们将梳理一遍迁移指南，同时就一些常见的问题，再做一些说明补充，来解答开发者们的升级困惑。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro-1-升级到-taro-2">Taro 1 升级到 Taro 2<a href="#taro-1-升级到-taro-2" class="hash-link" aria-label="Taro 1 升级到 Taro 2的直接链接" title="Taro 1 升级到 Taro 2的直接链接">​</a></h3><p>Taro 1 升 Taro 2 所需要做的工作并不多，根据<a href="/docs/2.x/migrate-to-2">迁移指南</a>，主要是新增了一个 <code>@tarojs/mini-runner</code> 依赖，以及对编译配置的调整，而在这里容易出问题的往往是在编译配置调整中，所以我们总结了一下针对编译配置的调整内容。</p><ul><li><code>plugins</code> 配置调整，调整前是一个对象，调整后为一个数组，用来配置 <a href="/docs/2.x/config-detail#plugins">Taro 插件</a>，非常值得注意的是这个配置请与 <code>babel</code> 配置里的 <code>plugins</code> 区分开来，后者是用来配置 babel 插件的，这是一个非常常见的配置错误</li><li><code>babel</code>、<code>csso</code>、<code>uglify</code> 等配置从旧的 <code>plugins</code> 配置中移出来了，调整为与 <code>sourceRoot</code> 和 <code>outputRoot</code> 等同级的配置项</li><li><code>weapp</code> 配置项改名为 <code>mini</code></li><li><code>postcss</code> 配置项下去掉 <code>module</code> 这一级配置，原 <code>module</code> 下的配置项直接置于 <code>postcss</code> 下</li></ul><h4 class="anchor anchorWithStickyNavbar_mCi1" id="关于-async-functions-的使用">关于 async functions 的使用<a href="#关于-async-functions-的使用" class="hash-link" aria-label="关于 async functions 的使用的直接链接" title="关于 async functions 的使用的直接链接">​</a></h4><p>同时，从 Taro 2 开始，使用 async functions 不再需要安装 <code>@tarojs/async-await</code> 依赖了，而是通过安装 babel 插件 <code>babel-plugin-transform-runtime</code> 配合 <code>babel-runtime</code> 来实现支持，具体请查看文档<a href="/docs/2.x/async-await">异步编程指南</a>。</p><p>而在 Taro 3 中则不再需要手动安装配置，Taro 的官方 babel 预设 <code>babel-preset-taro</code> 已经内置了相关配置。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro-12-升级到-taro-3">Taro 1/2 升级到 Taro 3<a href="#taro-12-升级到-taro-3" class="hash-link" aria-label="Taro 1/2 升级到 Taro 3的直接链接" title="Taro 1/2 升级到 Taro 3的直接链接">​</a></h3><p>Taro 1/2 升级到 Taro 3 则相对来说要麻烦许多，但是<a href="/docs/migration">迁移指南</a>中其实介绍得已经非常详细了，我们在这里总结一下需要调整的内容。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="文件调整">文件调整<a href="#文件调整" class="hash-link" aria-label="文件调整的直接链接" title="文件调整的直接链接">​</a></h4><p>文件调整主要如下：</p><ul><li><strong>babel 配置</strong>，在项目目录下新增了 <a href="https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md" target="_blank" rel="noopener noreferrer"><code>babel.config.js</code></a> 配置文件来配置 babel，为此，请去掉编译配置（config/index.js）中的 <code>babel</code> 配置，<a href="/docs/migration/#%E7%BC%96%E8%AF%91%E4%BE%9D%E8%B5%96%E5%BA%93">请参见说明</a></li><li><strong>项目/页面配置</strong>，新增项目/页面同名的配置文件 <code>*.config.js</code>（或者 <code>*.config.ts</code>）, <code>*</code> 代表页面/项目文件的文件名，<code>config</code> 文件必须和页面/项目文件在同一文件夹，<a href="/docs/migration/#%E9%A1%B9%E7%9B%AE%E9%A1%B5%E9%9D%A2%E9%85%8D%E7%BD%AE">请参见说明</a></li></ul><h4 class="anchor anchorWithStickyNavbar_mCi1" id="编译配置调整">编译配置调整<a href="#编译配置调整" class="hash-link" aria-label="编译配置调整的直接链接" title="编译配置调整的直接链接">​</a></h4><p>主要参考上述 Taro 1 升级到 Taro 2 时的调整，新增 Taro 3 特有配置 framework 配置，取值为使用的框架（react, nerv, vue, vue3），<a href="/docs/migration/#%E7%BC%96%E8%AF%91%E9%85%8D%E7%BD%AE">请参见说明</a></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="项目依赖调整">项目依赖调整<a href="#项目依赖调整" class="hash-link" aria-label="项目依赖调整的直接链接" title="项目依赖调整的直接链接">​</a></h4><p>在 Taro 3 中有很多旧的项目依赖已经不再需要了，例如之前做平台运行时兼容的 <code>@tarojs/taro-weapp</code>、<code>@tarojs/taro-alipay</code> 等等，而同时也新增了一些新依赖项，例如 <code>@tarojs/runtime</code> 等，具体 Taro 3 会需要哪些依赖，可以通过创建 Taro 示例项目看到，在这里我们列出了 Taro 3 目前仍需使用的 NPM 包名及其具体作用。</p><table><thead><tr><th>NPM 包</th><th align="left">描述</th></tr></thead><tbody><tr><td><a href="https://www.npmjs.com/package/babel-preset-taro" target="_blank" rel="noopener noreferrer"><code>babel-preset-taro</code></a></td><td align="left">给 Taro 项目使用的 babel preset</td></tr><tr><td><a href="https://www.npmjs.com/package/@tarojs/taro" target="_blank" rel="noopener noreferrer"><code>@tarojs/taro</code></a></td><td align="left">暴露给应用开发者的 Taro 核心 API</td></tr><tr><td><a href="https://www.npmjs.com/package/@tarojs/shared" target="_blank" rel="noopener noreferrer"><code>@tarojs/shared</code></a></td><td align="left">Taro 内部使用的 utils</td></tr><tr><td><a href="https://www.npmjs.com/package/@tarojs/api" target="_blank" rel="noopener noreferrer"><code>@tarojs/api</code></a></td><td align="left">暴露给 @tarojs/taro 的所有端的公有 API</td></tr><tr><td><a href="https://www.npmjs.com/package/@tarojs/taro-h5" target="_blank" rel="noopener noreferrer"><code>@tarojs/taro-h5</code></a></td><td align="left">暴露给 @tarojs/taro 的 H5 端 API</td></tr><tr><td><a href="https://www.npmjs.com/package/@tarojs/router" target="_blank" rel="noopener noreferrer"><code>@tarojs/router</code></a></td><td align="left">Taro H5 路由</td></tr><tr><td><a href="https://www.npmjs.com/package/@tarojs/react" target="_blank" rel="noopener noreferrer"><code>@tarojs/react</code></a></td><td align="left">基于 react-reconciler 的小程序专用 React 渲染器</td></tr><tr><td><a href="https://www.npmjs.com/package/@tarojs/cli" target="_blank" rel="noopener noreferrer"><code>@tarojs/cli</code></a></td><td align="left">Taro 开发工具</td></tr><tr><td><a href="https://www.npmjs.com/package/@tarojs/extend" target="_blank" rel="noopener noreferrer"><code>@tarojs/extend</code></a></td><td align="left">Taro 扩展，包含 jQuery API 等</td></tr><tr><td><a href="https://www.npmjs.com/package/@tarojs/helper" target="_blank" rel="noopener noreferrer"><code>@tarojs/helper</code></a></td><td align="left">内部给 CLI 和 runner 使用辅助方法集</td></tr><tr><td><a href="https://www.npmjs.com/package/@tarojs/service" target="_blank" rel="noopener noreferrer"><code>@tarojs/service</code></a></td><td align="left">Taro 插件化内核</td></tr><tr><td><a href="https://www.npmjs.com/package/@tarojs/taro-loader" target="_blank" rel="noopener noreferrer"><code>@tarojs/taro-loader</code></a></td><td align="left">露给 @tarojs/mini-runner 和 @tarojs/webpack-runner 使用的 Webpack loader</td></tr><tr><td><a href="https://www.npmjs.com/package/@tarojs/runner-utils" target="_blank" rel="noopener noreferrer"><code>@tarojs/runner-utils</code></a></td><td align="left">暴露给 @tarojs/mini-runner 和 @tarojs/webpack-runner 的公用工具函数</td></tr><tr><td><a href="https://www.npmjs.com/package/@tarojs/webpack-runner" target="_blank" rel="noopener noreferrer"><code>@tarojs/webpack-runner</code></a></td><td align="left">Taro H5 端 Webpack 打包编译工具</td></tr><tr><td><a href="https://www.npmjs.com/package/@tarojs/mini-runner" target="_blank" rel="noopener noreferrer"><code>@tarojs/mini-runner</code></a></td><td align="left">Taro 小程序 端 Webpack 打包编译工具</td></tr><tr><td><a href="https://www.npmjs.com/package/@tarojs/components" target="_blank" rel="noopener noreferrer"><code>@tarojs/components</code></a></td><td align="left">Taro 标准组件库，H5 版</td></tr><tr><td><a href="https://www.npmjs.com/package/@tarojs/taroize" target="_blank" rel="noopener noreferrer"><code>@tarojs/taroize</code></a></td><td align="left">Taro 小程序反向编译器</td></tr><tr><td><a href="https://www.npmjs.com/package/@tarojs/with-weapp" target="_blank" rel="noopener noreferrer"><code>@tarojs/with-weapp</code></a></td><td align="left">反向转换的运行时适配器</td></tr><tr><td><a href="https://www.npmjs.com/package/eslint-config-taro" target="_blank" rel="noopener noreferrer"><code>eslint-config-taro</code></a></td><td align="left">Taro ESLint 规则</td></tr><tr><td><a href="https://www.npmjs.com/package/eslint-plugin-taro" target="_blank" rel="noopener noreferrer"><code>eslint-plugin-taro</code></a></td><td align="left">Taro ESLint 插件</td></tr></tbody></table><h4 class="anchor anchorWithStickyNavbar_mCi1" id="代码调整">代码调整<a href="#代码调整" class="hash-link" aria-label="代码调整的直接链接" title="代码调整的直接链接">​</a></h4><p>代码调整主要如下：</p><ul><li><strong>API 引入</strong>，前端框架（React/Nerv/Vue）自身的 API 直接从框架引入，与 Web 保持一致，只有 Taro 提供的相关 API，还是从 <code>@tarojs/taro</code> 引入，<a href="/docs/migration/#api">请参见说明</a></li><li><strong>App 代码调整</strong>，对于 React/Nerv 项目，项目入口 App 的 render 函数固定修改为返回 <code>this.props.children</code>，如下</li></ul><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">Component</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'react'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./app.scss'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">App</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">render</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">props</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">children</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token maybe-class-name">App</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ul><li><strong>路由功能</strong>，使用 <code>getCurrentInstance().router</code> 替代 <code>this.$router</code>，<code>getCurrentInstance</code> 作为新 API 从 <code>@tarojs/taro</code> 引入，<a href="/docs/migration/#%E8%B7%AF%E7%94%B1">请参见说明</a></li><li><strong>生命周期</strong>，主要是使用 React 后，带来的生命周期调整，<a href="/docs/migration/#%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F">请参见说明</a></li><li><strong>使用第三方 React 库</strong>，对于 redux、mobx 等 React 生态库，可以直接像 Web 开发那样直接使用，<a href="/docs/migration/#%E4%BD%BF%E7%94%A8%E7%AC%AC%E4%B8%89%E6%96%B9-react-%E5%BA%93">请参见说明</a></li><li><strong>Ref &amp; DOM</strong>，<a href="/docs/migration/#ref--dom">请参见说明</a></li><li><strong>不再需要传入 $scope</strong>，在 Taro 1/2 时调用某些 API 需要传入 <code>this.$scope</code>，相当于传入组件对应的小程序原生对象，而 Taro 3 则不再需要，具体<a href="/docs/migration/#scope-%E5%92%8C-componenttype">请参见说明</a></li><li><strong>样式调整</strong>，组件直接受全局样式影响，不再需要设置 <code>addGlobalClasses</code>，<a href="/docs/migration/#%E6%A0%B7%E5%BC%8F">请参见说明</a></li></ul><p>当然，迁移指南内容并不仅仅局限于版本迁移，由于 Taro 3 相对与 Taro 1/2 有很多 breaking changes，所以建议使用 Taro 3 的开发者都能在开发前阅读一遍<a href="/docs/migration">迁移指南</a>。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="不得不唠叨一下-taro-3-的正确使用姿势">不得不唠叨一下 Taro 3 的正确使用姿势<a href="#不得不唠叨一下-taro-3-的正确使用姿势" class="hash-link" aria-label="不得不唠叨一下 Taro 3 的正确使用姿势的直接链接" title="不得不唠叨一下 Taro 3 的正确使用姿势的直接链接">​</a></h2><p>正如前文所提到的，目前有很多开发者是从 Taro 3 才开始接触 Taro，而目前市面上很多 Taro 相关的教程和项目都是 Taro 1/2 的，这对于很多开发者来说会造成不小的困惑，所以行文到此，我们希望列举一些常见问题来帮助开发者规避掉一些不必要的错误。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro-多版本共存问题">Taro 多版本共存问题<a href="#taro-多版本共存问题" class="hash-link" aria-label="Taro 多版本共存问题的直接链接" title="Taro 多版本共存问题的直接链接">​</a></h3><p>很多开发曾经使用 Taro 旧版本开发过项目，已经在全局安装了 Taro，但是想同时体验到 Taro 3，应该如何进行操作？</p><p>我们提供了两种思路：</p><ul><li>如果是需要新创建 Taro 3 项目，可以使用 <a href="https://github.com/nvm-sh/nvm" target="_blank" rel="noopener noreferrer">nvm</a> 来管理 node 版本，通过安装不同 node 版本来安装不同版本的 Taro CLI，从而解决 Taro 多版本共存的问题</li><li>如果是部分已有项目需要升级到 Taro 3，可以在这些项目本地安装相应版本的 Taro CLI，这样通过 <code>yarn</code> 或者 <code>npm</code> 执行命令的话就会直接使用本地安装的 Taro CLI，安装方式 <code>yarn add @tarojs/cli</code></li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="将-taro-cli-版本与项目中-taro-相关依赖的版本保持一致">将 Taro CLI 版本与项目中 Taro 相关依赖的版本保持一致<a href="#将-taro-cli-版本与项目中-taro-相关依赖的版本保持一致" class="hash-link" aria-label="将 Taro CLI 版本与项目中 Taro 相关依赖的版本保持一致的直接链接" title="将 Taro CLI 版本与项目中 Taro 相关依赖的版本保持一致的直接链接">​</a></h3><p>请时刻注意将 Taro CLI 版本与项目中 Taro 相关依赖的版本保持一致。</p><p>CLI 与项目依赖版本不一致是导致很多问题出现的源头之一。例如，Taro CLI 版本为 3.0.8，那么 Taro 相关依赖的版本也必须是 3.0.8，Taro 相关包名可以从这个<a href="/docs/codebase-overview">列表</a>得知，具体依赖项版本可以使用 <code>taro info</code> 命令或者通过 <code>package.json</code> 就能知晓。</p><p>如果发现不一致的情况可以使用 Taro 升级命令 <code>taro update self [版本号]</code> 和 <code>taro update project [版本号]</code>来分别将 CLI 和项目依赖升级到指定版本；或者也可以手动安装相应版本 CLI，修改 <code>package.json</code> 依赖版本号，然后重装依赖来解决。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="使用路由">使用路由<a href="#使用路由" class="hash-link" aria-label="使用路由的直接链接" title="使用路由的直接链接">​</a></h3><p>在 Taro 3 中使用路由在前文的版本迁移部分已有提及，同时需要了解更多内容可以前往<a href="/docs/router">官方文档</a>查看。</p><p>非常值得注意的是，无论是获取项目传入参数还是页面入参，都是通过 <code>getCurrentInstance().router</code> 来获取的，具体使用如下。</p><div class="language-javascript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-javascript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> getCurrentInstance </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/taro'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">React</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">Component</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'react'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">C</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">componentDidMount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">getCurrentInstance</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">router</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">params</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="如何获取页面节点信息">如何获取页面节点信息<a href="#如何获取页面节点信息" class="hash-link" aria-label="如何获取页面节点信息的直接链接" title="如何获取页面节点信息的直接链接">​</a></h3><p>由于 Taro 3 设计机制的原因，需要在新增的 <code>onReady</code> 生命周期内才能调用 Taro API 正确获取页面节点信息，小程序和 H5 都是如此。</p><p>而同时，在微信小程序中当页面节点嵌套过深时，超过一定层级（默认 16 级，可以通过编译配置 <code>baseLevel</code> 来控制）时，Taro 将转而使用自定义组件，然后再开启新的循环，在这种情况要争取获取页面节点信息的话，需要使用 <strong>跨自定义组件的后代选择器</strong> 来进行选择，可以参见 Taro <a href="https://github.com/NervJS/taro/issues/7411" target="_blank" rel="noopener noreferrer">相关 issue</a>。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro-ui-是不是不支持-taro-3-了">Taro UI 是不是不支持 Taro 3 了<a href="#taro-ui-是不是不支持-taro-3-了" class="hash-link" aria-label="Taro UI 是不是不支持 Taro 3 了的直接链接" title="Taro UI 是不是不支持 Taro 3 了的直接链接">​</a></h3><p>在 Taro 3 中依然可以使用 Taro UI，目前需要安装 Taro UI 的 alpha 版本。</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">yarn</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> taro-ui@next</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Taro UI 作为 Taro 的官方 UI 库，依然处于维护状态，目前主要依靠社区力量在进行维护，同时也非常欢迎更多社区开发人员共同参与到 Taro UI 的迭代中来。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro-ui-有没有-vue-版本">Taro UI 有没有 Vue 版本<a href="#taro-ui-有没有-vue-版本" class="hash-link" aria-label="Taro UI 有没有 Vue 版本的直接链接" title="Taro UI 有没有 Vue 版本的直接链接">​</a></h3><p>目前，Taro 官方没有推出 Vue 版本的 Taro UI 库，但在社区中有 Vue 版本的解决方案，如果使用 Vue 进行开发可以尝试 <a href="https://github.com/psaren/taro-ui-vue" target="_blank" rel="noopener noreferrer">taro-ui-vue</a>。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="为什么-taro-3-打包出来的应用体积巨大">为什么 Taro 3 打包出来的应用体积巨大<a href="#为什么-taro-3-打包出来的应用体积巨大" class="hash-link" aria-label="为什么 Taro 3 打包出来的应用体积巨大的直接链接" title="为什么 Taro 3 打包出来的应用体积巨大的直接链接">​</a></h3><p>很多朋友会发现 Taro 3 的项目在预览的时候打包出来的包大小相比 Taro 1/2 要大上很多，然后非常紧张用了 Taro 3 会不会导致自己的主包超限。</p><p>诚然，在预览的时候 Taro 3 默认生成的包会比较大，主要是因为为了方便调试，预览时引用的 React 和 Vue 用的是调试用库，而且同时预览时会生成 sourceMap，这就导致预览时生成的包会大很多，但是在打包生产包（去掉 --watch）时，最终生成包还是会很小的，对线上不会有太大影响，同时 Taro 团队也正在尝试不断优化生产包大小，进一步优化 Taro 性能。</p><p>如果想要在预览时降低包大小，可以设置 <code>NODE_ENV</code> 为 <code>production</code> 来开启压缩，例如编译微信小程序</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Bash</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token assign-left variable" style="color:#36acaa">NODE_ENV</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">production taro build --type weapp --watch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># CMD</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token builtin class-name">set</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">NODE_ENV</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">production </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> taro build --type weapp --watch</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="在-taro-3-中使用小程序原生页面及组件">在 Taro 3 中使用小程序原生页面及组件<a href="#在-taro-3-中使用小程序原生页面及组件" class="hash-link" aria-label="在 Taro 3 中使用小程序原生页面及组件的直接链接" title="在 Taro 3 中使用小程序原生页面及组件的直接链接">​</a></h3><p>在 Taro 3 中依然可以像 Taro 1/2 那样引入小程序原生页面及组件，且使用方式大体一致，不过在某些情况有些细微差别，比如 <code>slot</code> 的使用，在 Taro 1/2 中可以直接使用 <code>slot</code> 标签，而在 Taro 3 中则需要从 <code>@tarojs/components</code> 中引入 <code>Slot</code> 组件然后再进行使用。</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Taro</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/taro'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">React</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">Component</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'react'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">View</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports maybe-class-name">Text</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports maybe-class-name">Slot</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/components'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Index</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  state </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">show</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">date</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">''</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">render</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> show</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> date </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">state</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">index</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">van-button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">type</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">primary</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript keyword" style="color:#00009f">this</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">showCalendar</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">          显示日历</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">van-button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">van-calendar</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">show</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">show</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">showConfirm</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">type</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">range</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onClose</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript keyword" style="color:#00009f">this</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">closeCalendar</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onConfirm</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript keyword" style="color:#00009f">this</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">onConfirm</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">          </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Slot</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">name</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">title</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">Hello world</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">          </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Slot</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">van-calendar</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>具体使用情况可以参考项目 <a href="https://github.com/NervJS/taro3-vant-sample" target="_blank" rel="noopener noreferrer">taro3-vant-sample</a>。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="有哪些-taro-官方的示例项目">有哪些 Taro 官方的示例项目<a href="#有哪些-taro-官方的示例项目" class="hash-link" aria-label="有哪些 Taro 官方的示例项目的直接链接" title="有哪些 Taro 官方的示例项目的直接链接">​</a></h3><p>目前 Taro 3 的社区示例项目还在完善中，Taro 官方则分别针对 React 和 Vue 提供了示例的组件库项目以供参考，安装最新版本的 Taro CLI，在创建项目时选择社区优质模板源创建即可进行体验。</p><p><img loading="lazy" src="https://storage.360buyimg.com/taro-resource/template.jpg" class="img_ZOZK"></p><p>同时，Taro 官方还提供了一个 <a href="https://github.com/NervJS/TodoMVC" target="_blank" rel="noopener noreferrer">TodoMVC 项目</a>以供参考学习，React 和 Vue 示例分别在 react 和 vue 分支上。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro-物料市场中哪些物料能在-taro-3-中使用">Taro 物料市场中哪些物料能在 Taro 3 中使用<a href="#taro-物料市场中哪些物料能在-taro-3-中使用" class="hash-link" aria-label="Taro 物料市场中哪些物料能在 Taro 3 中使用的直接链接" title="Taro 物料市场中哪些物料能在 Taro 3 中使用的直接链接">​</a></h3><p>目前 Taro 物料市场没有做好针对物料的版本区分，我们会尽快启动这一项工作，为每个物料打上版本标识，当下要识别哪些物料能在 Taro 3 中使用，只能通过物料本身的 Taro 依赖项来进行识别。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="再聊聊-taro-的近期动作">再聊聊 Taro 的近期动作<a href="#再聊聊-taro-的近期动作" class="hash-link" aria-label="再聊聊 Taro 的近期动作的直接链接" title="再聊聊 Taro 的近期动作的直接链接">​</a></h2><p>Taro 社区及官方团队目前主要在集中人力做以下几项工作</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="实现支持平台与框架的自定义扩展">实现支持平台与框架的自定义扩展<a href="#实现支持平台与框架的自定义扩展" class="hash-link" aria-label="实现支持平台与框架的自定义扩展的直接链接" title="实现支持平台与框架的自定义扩展的直接链接">​</a></h3><p>细心的朋友应该已经发现，进入 Taro 3 时代，Taro 的推广 Slogan 已经由「多端统一开发解决方案」变成了「开放式跨端跨框架解决方案」，那么这两者之间有何差异呢？</p><p>可以看出「开放式跨端跨框架解决方案」包含了多端统一开发的特性，同时支持跨框架开发，而且更重要的是能够成为一个开放式的解决方案，我们希望开发者可以根据 Taro 提供的 API 开发一个插件就能实现自己去为 Taro 扩展更多平台与前端框架的支持，例如未来有些新的平台推出小程序，或者有人希望能在 Taro 中使用 Angular 等更多的前端框架，那么就可以通过 Taro 的开放式机制来自行扩展，而不用等待 Taro 官方来进行支持，Taro 将只作为一个跨端适配的平台，所有的可能性都可以让社区自己去自由发掘。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="实现小程序与-web-的同构">实现小程序与 Web 的同构<a href="#实现小程序与-web-的同构" class="hash-link" aria-label="实现小程序与 Web 的同构的直接链接" title="实现小程序与 Web 的同构的直接链接">​</a></h3><p>在当前 Taro 的设计下，使用 Taro 开发必须使用 Taro 标准组件库中的组件，而不能直接使用大家熟悉的 HTML 标签。我们正在努力打破这一藩篱，寻求支持让开发人员可以直接使用 HTML 标签来开发小程序的方案。</p><p>这样，既能进一步让 Taro 的开发体验接近 Web， 同时也能让一些 Web 生态资源可以直接运行在小程序中，极大降低从 Web 迁移到小程序的成本。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="说说缺席的-taro-3-rn-支持">说说缺席的 Taro 3 RN 支持<a href="#说说缺席的-taro-3-rn-支持" class="hash-link" aria-label="说说缺席的 Taro 3 RN 支持的直接链接" title="说说缺席的 Taro 3 RN 支持的直接链接">​</a></h3><p>很多朋友在升级到 Taro 3 之后都会发出疑问：RN 是不再支持了吗？</p><p>Taro 3 没有支持 RN 适配，让很多使用 Taro 开发 RN 应用的朋友措手不及，经常在群里能看到上述灵魂拷问。</p><p>事实上 Taro 并没有抛弃 RN，目前 Taro 3 RN 适配工作已经由 「58 同城」开发团队接管，进行适配支持，目前这项工作已经正在紧锣密鼓进行，相信不久的将来就能看到在 Taro 3 中 RN 的支持王者归来。而这一次的通力协作也意味着 Taro 核心团队正不断成长为一个跨公司的团队，在未来一定会有更多灵感的碰撞，为社区开发者带来更多精彩的功能。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="总结一下">总结一下<a href="#总结一下" class="hash-link" aria-label="总结一下的直接链接" title="总结一下的直接链接">​</a></h2><p>从目前的业界反馈与 Taro 自身规划来看，Taro 3 是一个非常值得尝试和期待的版本，已经有非常多的开发者开始使用 Taro 3 开发应用，在未来我们也会不断完善功能与文档，为大家带来更棒的开发体验。升级到 Taro 3 的过程或许稍显艰辛，但有任何问题，欢迎大家通过交流群和 GitHub issues 向我们进行反馈。</p><p>同时，诚挚邀请您与 Taro 官方团队交流您的使用情况，您的反馈是 Taro 前进的动力！请<a href="https://wj.qq.com/s2/6494361/09cf/" target="_blank" rel="noopener noreferrer">戳此</a>完成 Taro 使用调研问卷，提交问卷，将有可能获得 Taro 团队的红包哦！！感谢一路相伴，Taro 有你更精彩！</p>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[Taro 3 正式版发布：开放式跨端跨框架解决方案]]></title>
            <link>https://docs.taro.zone/blog/2020-07-01-taro-3-0-0</link>
            <guid>https://docs.taro.zone/blog/2020-07-01-taro-3-0-0</guid>
            <pubDate>Wed, 01 Jul 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[今天我们很高兴地在党的生日发布 Taro 3（Taro Next）正式版，希望 Taro 未来的更多两年能像一名共产主义战士一样经受住更多的考验。]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://img13.360buyimg.com/ling/jfs/t1/134284/22/3425/2134519/5efb3a62Ef5a42a26/a2c2c9a6d62cd391.png" class="img_ZOZK"></p><p>从 Taro 第一个版本发布到现在，Taro 已经接受了来自于开源社区两年多的考验。今天我们很高兴地在党的生日发布 Taro 3（Taro Next）正式版，希望 Taro 未来的更多两年能像一名共产主义战士一样经受住更多的考验。以下是 Taro 3 的一些新增特性：</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="跨框架reactnervvue-2vue-3jquery">跨框架：React、Nerv、Vue 2、Vue 3、jQuery<a href="#跨框架reactnervvue-2vue-3jquery" class="hash-link" aria-label="跨框架：React、Nerv、Vue 2、Vue 3、jQuery的直接链接" title="跨框架：React、Nerv、Vue 2、Vue 3、jQuery的直接链接">​</a></h2><p>在旧版本的 Taro，我们以微信小程序的开发规范为基准，使用 React/JSX 的方式来进行开发。而在 Taro 3，我们把这一思路量化为一个编程模型：</p><p>设微信小程序生命周期为一个 <code>interface</code>，不同的框架实例的生命周期虽然不尽相同，但我们可以根据框架生命周期分别新建一个 <code>class</code> 去 <code>implements</code> 小程序生命周期的 <code>interface</code>。相应地，小程序的组件/API/路由规范可以使用同样的思路和模型让不同框架的代码，运行在不同的端上：</p><p><img loading="lazy" src="https://storage.jd.com/taro-source/taro-docs/WechatIMG1393.png" alt="taro" class="img_ZOZK"></p><p>在 Taro 3 中我们内置了 React、Nerv、Vue 2、Vue 3 四种框架的支持，开发者可以通过 <code>taro init</code> 命令创建对应的模板，并编写与框架本身生命周期、调用方法合乎一致的代码（idiomatic code），在语法上也没有任何的限制。</p><p>在四类框架之外，Taro 3 还提供 <a href="/docs/jquery-like">jQuery-like API</a> 的支持，作为现代开发框架的补充和实现跨框架组件的方案。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="跨端h5微信支付宝百度字节跳动小程序">跨端：H5、微信、支付宝、百度、字节跳动...小程序<a href="#跨端h5微信支付宝百度字节跳动小程序" class="hash-link" aria-label="跨端：H5、微信、支付宝、百度、字节跳动...小程序的直接链接" title="跨端：H5、微信、支付宝、百度、字节跳动...小程序的直接链接">​</a></h2><p>和旧版本的 Taro 一样，Taro 3 支持编译到微信、京东、支付宝、百度、字节跳动、QQ 等主流小程序平台。</p><p>和小程序一样，Taro 3 的 H5 端相较于之前版本的 Taro 是一次彻底的重写：基础组件现在全部使用 Web Components 构建，路由系统也完全与前端框架解耦，因此在 H5 端 Taro 也实现了跨框架。不管开发者使用的是 React、Vue 还是 Nerv，都可以同时运行在各种小程序和 H5 上。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="开放式插件系统">开放式插件系统<a href="#开放式插件系统" class="hash-link" aria-label="开放式插件系统的直接链接" title="开放式插件系统的直接链接">​</a></h2><p>Taro 3 在编译时引入了基于 <a href="https://github.com/webpack/tapable" target="_blank" rel="noopener noreferrer">Tapable</a> 的插件系统，开发者可以通过编写插件的方式为 Taro 拓展更多功能或者为自身业务定制个性化功能。在运行时我们则从 <a href="https://github.com/facebook/react/tree/master/packages/react-reconciler" target="_blank" rel="noopener noreferrer">React Reconciler</a> 中得到启发，在 Taro 运行时也实现了一套修改运行时逻辑的 API。</p><p>也就是说，开发者可以在不修改甚至不了解 Taro 源码和实现的情况下通过 Taro 拓展更多端，拓展更多的框架。在下个 Minor 版本（<code>v3.1.0</code>）Taro 就会完成编译时/运行时与端/框架的解耦，届时开发者只要写一个插件就能拓展新的端或框架。未来 Taro 添加新的端或框架也会通过这样的方式进行。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="开源治理与项目治理">开源治理与项目治理<a href="#开源治理与项目治理" class="hash-link" aria-label="开源治理与项目治理的直接链接" title="开源治理与项目治理的直接链接">​</a></h2><p>为了与开发者更高效地交流，Taro 从 Vue.js 和 Ant Design 得到启发，引入了 <a href="https://nervjs.github.io/taro-issue-helper/" target="_blank" rel="noopener noreferrer">Issue Helper 工具</a>。为了让 Taro 在重大特性更新和更改能稳步推进，我们又从 Rust、React 中学习，引入了 <a href="https://github.com/NervJS/taro-rfcs" target="_blank" rel="noopener noreferrer">RFC 流程机制</a>。为了让版本迭代更透明、更规范，Taro 在今后版本发布也会遵守 <a href="https://github.com/NervJS/taro/milestones?direction=asc&amp;sort=due_date" target="_blank" rel="noopener noreferrer">Milestone 机制</a>。</p><p>文档是开发者最好的朋友。在 Taro 3 我们对文档也进行了改版和重新梳理，并推出了新的 <a href="/docs/guide/">渐进式文档</a>，渐进式文档按照开发项目的顺序介绍 Taro 的基础概念和优化技巧，适合喜欢边学边做或对小程序开发没有任何了解的开发者。</p><p>代码质量和稳定性对开源项目非常重要。Taro 也在严格贯彻 Code Review 机制，所有人的代码都要被审核，没有人例外。为了保证我们能更大胆地迭代新特性，在整个 Taro 3 开发期间我们总共添加了三万行测试代码。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="微信小程序转-reactvue">微信小程序转 React/Vue<a href="#微信小程序转-reactvue" class="hash-link" aria-label="微信小程序转 React/Vue的直接链接" title="微信小程序转 React/Vue的直接链接">​</a></h2><p>早在 <a href="https://aotu.io/notes/2018/12/17/taro-1-2/" target="_blank" rel="noopener noreferrer">Taro 1.2 发布</a> 时，我们就提供微信小程序转 Taro 的功能，转换后的微信小程序应用会变成一个多端应用。现在这个功能也完全兼容 Taro Next 的新架构，并且转换后的代码提供 React 和 Vue 两种选项。和以前一样，只需要在微信小程序项目根目录执行命令 <code>taro convert</code> ：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">$ taro convert</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>选择想要转换后的框架即可：</p><p><img loading="lazy" src="https://storage.jd.com/taro-resource/convert.jpg" alt="convert" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="渲染-html-字符串">渲染 HTML 字符串<a href="#渲染-html-字符串" class="hash-link" aria-label="渲染 HTML 字符串的直接链接" title="渲染 HTML 字符串的直接链接">​</a></h2><p>在小程序中渲染 HTML 字符串通常会使用 <a href="https://github.com/icindy/wxParse" target="_blank" rel="noopener noreferrer">wxparse</a> 这样的第三方库，但 <code>wxparse</code> 的 API 复杂，拓展性弱，内部实现不准确，最重要的是现在已经停止了维护。比起 <code>wxparse</code>，Taro 3 的 HTML 字符串渲染提供以下的特性：</p><ul><li>API 与 Web 保持一致，可以直接通过 React 的 <code>dangerouslySetInnerHTML</code> 或 Vue 的 <code>v-html</code> 调用。</li><li>可以通过 CSS 直接控制标签样式</li><li>给已经渲染的 HTML 标签绑定事件</li><li>在 HTML 解析和渲染都提供了钩子函数满足个性化渲染需求</li></ul><p>你可以访问文档<a href="/docs/html">渲染 HTML</a>了解更多信息。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="css-in-js">CSS-In-JS<a href="#css-in-js" class="hash-link" aria-label="CSS-In-JS的直接链接" title="CSS-In-JS的直接链接">​</a></h2><p>在 React 社区有一个著名的 CSS-in-JS 解决方案: <a href="https://github.com/styled-components/styled-components" target="_blank" rel="noopener noreferrer">styled-components</a>。但遗憾的是，<code>styled-components</code> 使用 <code>&lt;style&gt;</code> 标签来动态地控制样式，在小程序端没有类似的方案。但在 Taro 中我们可以通过 <a href="https://github.com/callstack/linaria" target="_blank" rel="noopener noreferrer">linaria</a> 实现同样的功能，<code>linaria</code> 主要提供以下特性：</p><ul><li>近似于 <code>styled-components</code> 的 API</li><li>完整的 TypeScript 支持</li><li>零运行时</li></ul><p>其中零运行时对于打包体积有要求的小程序尤为重要。</p><p>你可以访问文档<a href="/docs/css-in-js">使用 CSS-in-JS</a>了解更多信息。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="虚拟列表virtuallist">虚拟列表（VirtualList）<a href="#虚拟列表virtuallist" class="hash-link" aria-label="虚拟列表（VirtualList）的直接链接" title="虚拟列表（VirtualList）的直接链接">​</a></h2><p>当我们渲染数据量非常大的列表时，框架会根据数据尝试全量渲染视图，这就可能会产生性能问题导致视图无法响应操作一段时间。为了解决这个问题，我们可以采用另一种方式：比起全量渲染数据生成的视图，可以只渲染 <strong>当前可视区域(visable viewport)</strong> 的视图，非可视区域的视图在用户滚动到可视区域再渲染：</p><table><thead><tr><th align="center"><img loading="lazy" src="https://storage.jd.com/taro-resource/difference-in-scrolling.jpg" alt="diff.jpg" class="img_ZOZK"></th></tr></thead><tbody><tr><td align="center"><em>正常渲染和虚拟列表的区别</em></td></tr></tbody></table><table><thead><tr><th align="center"><img loading="lazy" src="https://storage.jd.com/taro-resource/vue.gif" alt="vue.gif" class="img_ZOZK"></th></tr></thead><tbody><tr><td align="center"><em>在开发者工具的直观效果</em></td></tr></tbody></table><p>类似的技术在 Android 开发被称之为 <a href="https://developer.android.com/reference/android/support/v7/widget/RecyclerView" target="_blank" rel="noopener noreferrer"><code>RecyclerView</code></a>，在 React Native 叫做 <a href="https://reactnative.dev/docs/virtualizedlist" target="_blank" rel="noopener noreferrer"><code>VirtualizedList</code></a>，我们统一命名为虚拟列表（Virtual List），这个组件现在内置在 Taro 中，在 React/Vue 或各种小程序及 H5 皆可使用：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">VirtualList</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/components-advanced/dist/components/virtual-list'</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>你可以访问文档<a href="/docs/virtual-list">长列表渲染</a>了解更多信息。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="预渲染">预渲染<a href="#预渲染" class="hash-link" aria-label="预渲染的直接链接" title="预渲染的直接链接">​</a></h2><p>初始化性能是 Taro 3 的性能优化的痛点。原生小程序或编译型框架的初始数据可以直接用于渲染，但 Taro 3 在初始化时会把框架的渲染数据转化为小程序的渲染数据，多了一次 setData 开销。</p><p>为了解决这个问题，Taro 从服务端渲染受到启发，在 Taro CLI 将页面初始化的状态直接渲染为无状态的 wxml，在框架和业务逻辑运行之前执行渲染流程。我们将这一技术称之为预渲染（Prerender），经过 Prerender 的页面初始渲染速度通常会和原生小程序一致甚至更快。</p><p>你可以访问文档<a href="/docs/prerender">预渲染</a>了解更多信息。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="更快的构建速度和-source-map-支持">更快的构建速度和 source-map 支持<a href="#更快的构建速度和-source-map-支持" class="hash-link" aria-label="更快的构建速度和 source-map 支持的直接链接" title="更快的构建速度和 source-map 支持的直接链接">​</a></h2><p>作为一个编译型框架，旧版本的 Taro 会进行大量的 AST 操作，这类操作显著地拖慢了 Taro CLI 的编译速度。而在 Taro 3 中不会操作任何开发者代码的 AST，因此编译速度得到了大幅的提高。</p><p>正因为 AST 操作的取消，Taro 也轻松地实现了 <code>source-map</code> 的支持。这对于开发体验是一个巨大的提升：</p><p><img loading="lazy" src="https://storage.jd.com/taro-source/taro-docs/WechatIMG1402.png" alt="source-map" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="没有枪没有炮没有轮子自己造">没有枪，没有炮，没有轮子自己造<a href="#没有枪没有炮没有轮子自己造" class="hash-link" aria-label="没有枪，没有炮，没有轮子自己造的直接链接" title="没有枪，没有炮，没有轮子自己造的直接链接">​</a></h2><p>把时间节点再跳转回两年前，市面上并没有一个支持使用 React 开发小程序的框架，也没有什么思路或探索能提供指引，我们面对的情况和旧中国「一穷二白」的情况差不多。今天，Taro 已经是<a href="https://www.infoq.cn/article/dCY0AHH71rBBjq3pIfh7" target="_blank" rel="noopener noreferrer">中国活跃度 Top 5</a> 的开源项目。</p><p>办法总是比困难多，即便是在小程序这样相对受限的开发环境。今后，Taro 依然会从开发者的角度出发，不断完善开发体验，提升开发效率，走中国特色发展道路，遇山开山，遇水搭桥，努力成为小程序开发，甚至多端开发的基础技术设施——Taro 3 就是实现这一愿景最坚实的一步，正如一位智者所言：</p><blockquote><p>上车 Taro 最好的时机是从前，其次是现在。</p></blockquote>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[Taro 3.0 RC：React/Vue/Nerv 任你选]]></title>
            <link>https://docs.taro.zone/blog/2020-05-26-taro-3-rc</link>
            <guid>https://docs.taro.zone/blog/2020-05-26-taro-3-rc</guid>
            <pubDate>Tue, 26 May 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Beta 版发布后，社区很多开发者使用上了 Taro Next，他们的帮助和赞誉加上我们内部项目使用的良好反馈，Beta 发布一个月之后，我们非常自信地发布了候选版（RC）版本。]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://img13.360buyimg.com/ling/jfs/t1/131780/5/388/2331933/5ecce529E0df55626/0514d956b8866772.png" class="img_ZOZK"></p><p>在 Taro Next 预览版（Alpha）版本之后，我们没有停下迭代的步伐，经过两个月的开发和数百次提交之后，我们谨慎但坚定地发布 Taro Next 测试（Beta） 版，Beta 版发布后，社区很多开发者使用上了 Taro Next，他们的帮助和赞誉加上我们内部项目使用的良好反馈，Beta 发布一个月之后，我们非常自信地发布了候选版（RC）版本。</p><p>在稳定的 Beta 版本里，Taro 也添加了更多的功能：</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="h5-编译支持">H5 编译支持<a href="#h5-编译支持" class="hash-link" aria-label="H5 编译支持的直接链接" title="H5 编译支持的直接链接">​</a></h2><p>H5 编译支持是我们版本升级最重要的理由之一。这意味着原来开发者写的小程序应用，现在可以直接运行在 Web 中。添加 H5 支持非常简单，只需要在项目根目录安装 <code>@tarojs/webpack-runner</code>，运行 <code>taro build —type h5 —watch</code> 即可：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i -D @tarojs/webpack-runner@next</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ taro build —type h5 —watch</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>和小程序一样，Taro Next 的 H5 端相较于之前版本的 Taro 是一次彻底的重写：基础组件现在全部使用 Web Components 构建，路由系统也完全与前端框架解耦，因此在 H5 端 Taro 也实现了跨框架。不管开发者使用的是 React、Vue 还是 Nerv，都可以同时运行在各种小程序和 H5 上。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="微信小程序转-reactvue">微信小程序转 React/Vue<a href="#微信小程序转-reactvue" class="hash-link" aria-label="微信小程序转 React/Vue的直接链接" title="微信小程序转 React/Vue的直接链接">​</a></h2><p>早在 <a href="https://aotu.io/notes/2018/12/17/taro-1-2/" target="_blank" rel="noopener noreferrer" title="Taro 1.2 发布">Taro 1.2 发布</a> 时，我们就提供微信小程序转 Taro 的功能，转换后的微信小程序应用会变成一个多端应用。现在这个功能也完全兼容 Taro Next 的新架构，并且转换后的代码提供 React 和 Vue 两种选项。和以前一样，只需要在微信小程序项目根目录执行命令 <code>taro convert</code> ：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">$ taro convert</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>选择想要转换后的框架即可：</p><p><img loading="lazy" src="https://img12.360buyimg.com/ling/jfs/t1/128590/20/3003/95813/5eccdc3bE034c85c4/1f66df49f1608b0b.png" alt="Taro convert" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="渲染-html-字符串">渲染 HTML 字符串<a href="#渲染-html-字符串" class="hash-link" aria-label="渲染 HTML 字符串的直接链接" title="渲染 HTML 字符串的直接链接">​</a></h2><p>在小程序中渲染 HTML 字符串通常会使用 <a href="https://github.com/icindy/wxParse" target="_blank" rel="noopener noreferrer" title="wxparse">wxparse</a> 这样的第三方库，但 <code>wxparse</code> 的 API 复杂，拓展性弱，内部实现不准确，最重要的是现在已经停止了维护。比起 <code>wxparse</code>，Taro Next 的 HTML 字符串渲染提供以下的特性：</p><ul><li>API 与 Web 保持一致，可以直接通过 React 的 <code>dangerouslySetInnerHTML</code> 或 Vue 的 <code>v-html</code> 调用。</li><li>可以通过 CSS 直接控制标签样式</li><li>给已经渲染的 HTML 标签绑定事件</li><li>在 HTML 解析和渲染都提供了钩子函数满足个性化渲染需求</li></ul><p>你可以访问文档<a title="渲染 HTML" href="/docs/html">渲染 HTML</a>了解更多信息。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="css-in-js">CSS-in-JS<a href="#css-in-js" class="hash-link" aria-label="CSS-in-JS的直接链接" title="CSS-in-JS的直接链接">​</a></h2><p>在 React 社区有一个著名的 CSS-in-JS 解决方案: <a href="https://github.com/styled-components/styled-components" target="_blank" rel="noopener noreferrer" title="styled-components">styled-components</a>。但遗憾的是，<code>styled-components</code> 使用 <code>&lt;style&gt;</code> 标签来动态地控制样式，在小程序端没有类似的方案。但在 Taro 中我们可以通过 <a href="https://github.com/callstack/linaria" target="_blank" rel="noopener noreferrer" title="linaria">linaria</a> 实现同样的功能，<code>linaria</code> 主要提供以下特性：</p><ul><li>近似于 <code>styled-components</code> 的 API</li><li>完整的 TypeScript 支持</li><li>零运行时</li></ul><p>其中零运行时对于打包体积有要求的小程序尤为重要。</p><p>你可以访问文档<a title="使用 CSS-in-JS" href="/docs/css-in-js">使用 CSS-in-JS</a>了解更多信息。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="虚拟列表virtuallist">虚拟列表（VirtualList）<a href="#虚拟列表virtuallist" class="hash-link" aria-label="虚拟列表（VirtualList）的直接链接" title="虚拟列表（VirtualList）的直接链接">​</a></h2><p>当我们渲染数据量非常大的列表时，框架会根据数据尝试全量渲染视图，这就可能会产生性能问题导致视图无法响应操作一段时间。为了解决这个问题，我们可以采用另一种方式：比起全量渲染数据生成的视图，可以只渲染 <strong>当前可视区域(visable viewport)</strong> 的视图，非可视区域的视图在用户滚动到可视区域再渲染：</p><table><thead><tr><th align="center"><img loading="lazy" src="https://storage.jd.com/taro-resource/difference-in-scrolling.jpg" alt="diff.jpg" class="img_ZOZK"></th></tr></thead><tbody><tr><td align="center"><em>正常渲染和虚拟列表的区别</em></td></tr></tbody></table><table><thead><tr><th align="center"><img loading="lazy" src="https://storage.jd.com/taro-resource/vue.gif" alt="vue.gif" class="img_ZOZK"></th></tr></thead><tbody><tr><td align="center"><em>在开发者工具的直观效果</em></td></tr></tbody></table><p>类似的技术在 Android 开发被称之为 <a href="https://developer.android.com/reference/android/support/v7/widget/RecyclerView" target="_blank" rel="noopener noreferrer" title="RecyclerView">RecyclerView</a>，在 React Native 叫做 <a href="https://reactnative.dev/docs/virtualizedlist" target="_blank" rel="noopener noreferrer" title="VirtualizedList">VirtualizedList</a>，我们统一命名为虚拟列表（Virtual List），这个组件现在内置在 Taro 中，在 React/Vue 或各种小程序及 H5 皆可使用：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">VirtualList</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/components/virtual-list'</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>你可以访问文档<a title="长列表渲染" href="/docs/virtual-list">长列表渲染</a>了解更多信息。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="插件功能">插件功能<a href="#插件功能" class="hash-link" aria-label="插件功能的直接链接" title="插件功能的直接链接">​</a></h2><p>自 2.2 开始，Taro 引入了<a href="/docs/plugin/">插件化机制</a>，允许开发者通过编写插件的方式来为 Taro 拓展更多功能或者为自身业务定制个性化功能。</p><p>该特性在 Taro next 中也可以使用了，官方提供了一个简易的数据 mock 插件 <code>@tarojs/plugin-mock</code> 供大家参考。相关接口和介绍可以通过 <code>官方文档 -&gt; 进阶指南 -&gt; 插件功能</code> 找到。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="向正式版前进">向正式版前进<a href="#向正式版前进" class="hash-link" aria-label="向正式版前进的直接链接" title="向正式版前进的直接链接">​</a></h2><p>在 Beta 版本发布之后，除了 React Native 端兼容这一特性在迭代，我们主要的精力投入到 Taro 本身的开源治理和项目治理中，为了保证重大特性更新和架构更改能够顺利推进，我们推出了 Taro 的 <a href="https://github.com/NervJS/taro-rfcs" target="_blank" rel="noopener noreferrer" title="RFC 流程机制">RFC 流程机制</a>，这将让 Taro 的进化更加透明公开，同时更多的社区参与势必让 Taro 更加完善。</p><p>期间，我们也对文档进行了升级，新文档相比旧版调整了部分目录让信息更合理，查找更方便，内容也更完善，同时也让阅读体验更加友好。</p><p>RC 版本，几乎就不会加入新的功能了，主要着重于除错，而后我们将发布正式版。也就是说：</p><p>如果你更为关注小程序端及 H5 端的开发，现在就是加入 Taro Next 最好的时机！</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="感谢">感谢<a href="#感谢" class="hash-link" aria-label="感谢的直接链接" title="感谢的直接链接">​</a></h2><ul><li><p>感谢 <a href="https://github.com/skywalker512" target="_blank" rel="noopener noreferrer">skywalker512</a> 大佬</p><ul><li>修复了 Current type definition, 删除不能使用的 useScope 定义的问题(<a href="https://github.com/NervJS/taro/pull/5764" target="_blank" rel="noopener noreferrer">#5764</a>)；</li><li>添加了支持 CSS Variables 的特性 (<a href="https://github.com/NervJS/taro/pull/5796" target="_blank" rel="noopener noreferrer">#5796</a>)；</li><li>修复了 windows 下的路径问题(<a href="https://github.com/NervJS/taro/pull/5839" target="_blank" rel="noopener noreferrer">#5839</a>)；</li><li>修复了 animation 定义出错的问题 (<a href="https://github.com/NervJS/taro/pull/5901" target="_blank" rel="noopener noreferrer">#5901</a>)。</li></ul></li><li><p>感谢 <a href="https://github.com/fupengl" target="_blank" rel="noopener noreferrer">fupengl</a> 大佬</p><ul><li>修复了 webpack 配置多余参数导致 webpack,loader 参数校验失败的问题；（<a href="https://github.com/NervJS/taro/pull/5835" target="_blank" rel="noopener noreferrer">#5835</a>）；</li><li>修复了 react 页面卸载生命周期没有调用的问题(<a href="https://github.com/NervJS/taro/pull/5826" target="_blank" rel="noopener noreferrer">#5826</a>)；</li><li>修复了缺少 tt 样式文件匹配的问题；</li><li>修复了 input maxlength 拼写错误 (<a href="https://github.com/NervJS/taro/pull/6005" target="_blank" rel="noopener noreferrer">#6005</a>)；</li><li>修复属性数字类型 0 也是有效值 (<a href="https://github.com/NervJS/taro/pull/6027" target="_blank" rel="noopener noreferrer">#6027</a>)；</li><li>修复了编译原生 tt 小程序样式文件没有 loader 处理的问题 (<a href="https://github.com/NervJS/taro/pull/6088" target="_blank" rel="noopener noreferrer">#6088</a>)。</li></ul></li><li><p>感谢 <a href="https://github.com/wuchangming" target="_blank" rel="noopener noreferrer">wuchangming</a> 大佬</p><ul><li>修复了 webpack <code>resolve.modules</code> 设置绝对路径导致的依赖引用错误 (<a href="https://github.com/NervJS/taro/pull/5858" target="_blank" rel="noopener noreferrer">#5858</a>)；</li><li>修复了 Current.router 获取的数据有误的问题 (<a href="https://github.com/NervJS/taro/pull/5889" target="_blank" rel="noopener noreferrer">#5989</a>)。</li></ul></li><li><p>感谢 <a href="https://github.com/cncolder" target="_blank" rel="noopener noreferrer">cncolder</a> 大佬</p><ul><li>添加 linaria 配置和 webpack 配置 (<a href="https://github.com/NervJS/taro/pull/5880" target="_blank" rel="noopener noreferrer">#5880</a>)；</li><li>html typo (<a href="https://github.com/NervJS/taro/pull/5893" target="_blank" rel="noopener noreferrer">#5893</a>)；</li><li>将 rich-text 属性设置为可选；</li><li>增加路由信息 path 字段 (<a href="https://github.com/NervJS/taro/pull/5898" target="_blank" rel="noopener noreferrer">#5898</a>)；</li><li>ui interaction 中的三个函数参数都是可选的 (<a href="https://github.com/NervJS/taro/pull/6351" target="_blank" rel="noopener noreferrer">#6351</a>)。</li></ul></li><li><p>感谢 <a href="https://github.com/Garfield550" target="_blank" rel="noopener noreferrer">Garfield550</a> 大佬</p><ul><li>给 api 增加 cjs 分发，规范部分包的文件包含 (<a href="https://github.com/NervJS/taro/pull/5991" target="_blank" rel="noopener noreferrer">#5991</a>) ；</li><li>修复组件 animation 属性和 Animation.export() 方法的类型(<a href="https://github.com/NervJS/taro/pull/6025" target="_blank" rel="noopener noreferrer">#6025</a>) 。</li></ul></li><li><p>感谢<a href="https://github.com/fishead" target="_blank" rel="noopener noreferrer">fishead</a> 添加了 framework 检查 (<a href="https://github.com/NervJS/taro/pull/5977" target="_blank" rel="noopener noreferrer">#5977</a>)；</p></li><li><p>感谢 <a href="https://github.com/alexloading" target="_blank" rel="noopener noreferrer">alexloading</a> 大佬修改了 nextTick 适配小程序 setData 回调<a href="https://github.com/NervJS/taro/pull/5978" target="_blank" rel="noopener noreferrer">#5978</a>；</p></li><li><p>感谢 <a href="https://github.com/hvsy" target="_blank" rel="noopener noreferrer">hvsy</a> 、<a href="https://github.com/cuikangyi" target="_blank" rel="noopener noreferrer">cuikangyi</a>、 <a href="https://github.com/yesmeck" target="_blank" rel="noopener noreferrer">yesmeck</a>、<a href="https://github.com/Psli" target="_blank" rel="noopener noreferrer">Psli</a> 大佬 。</p></li></ul>]]></content:encoded>
            <category>v3</category>
        </item>
        <item>
            <title><![CDATA[使用 Taro 快速开发京东小程序]]></title>
            <link>https://docs.taro.zone/blog/2020-04-27-taro-build-jd</link>
            <guid>https://docs.taro.zone/blog/2020-04-27-taro-build-jd</guid>
            <pubDate>Mon, 27 Apr 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[近两年来小程序逐渐成为互联网的一个行业风口，一直备受业界关注，各大厂也接踵推出了自己的小程序。近日京东也推出了小程序：京东小程序。Taro 作为一款专注于多端统一开发的框架，第一时间对京东小程序进行了适配。接下来就跟着小编一起了解一下如何使用 Taro 快速开发京东小程序吧。]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://img20.360buyimg.com/ling/jfs/t1/117964/14/2118/145903/5e9eee0aE8897b3dd/bd435e6161779ad1.png" class="img_ZOZK"></p><p>近两年来小程序逐渐成为互联网的一个行业风口，一直备受业界关注，各大厂也接踵推出了自己的小程序。近日京东也推出了小程序：<a href="https://mp.jd.com/?entrance=taro" target="_blank" rel="noopener noreferrer"><strong>京东小程序</strong></a>。Taro 作为一款专注于多端统一开发的框架，第一时间对京东小程序进行了适配。接下来就跟着小编一起了解一下如何使用 Taro 快速开发京东小程序吧。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="背景">背景<a href="#背景" class="hash-link" aria-label="背景的直接链接" title="背景的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="京东小程序">京东小程序<a href="#京东小程序" class="hash-link" aria-label="京东小程序的直接链接" title="京东小程序的直接链接">​</a></h3><p><a href="https://mp.jd.com/?entrance=taro" target="_blank" rel="noopener noreferrer">京东小程序平台</a>是京东自研技术（能力）开放平台，平台集成京东特色功能，串联商家和用户。为用户提供延展服务，给商家带来新机遇。京东小程序平台不仅支持存量自营、POP 商家自动开通，还支持新型的非电商领域商家参与小程序生态。</p><p>加入京东小程序开放平台的商家，可以利用京东平台完整的营销、交易、支付、会员、物流等能力，更加便捷地构建起自己的特色服务场景。助力商家实现生态场景上的“跃迁”，快速为用户提供一站式服务体验。</p><p>京东小程序只需一次开发即可运行在京东 APP、京东金融 APP、京麦 APP 三端。京东 APP、京东金融 APP 会开放特定的能力，对优质小程序还会开放多个高流量入口，包含扫码、搜索、消息等多种方式触达。而京麦 APP 小程序主要用于商家工具插件的开发定制。</p><p><img loading="lazy" src="https://storage.jd.com/cjj-pub-images/jdapp_3apps.png" alt="jdapp_3apps" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro">Taro<a href="#taro" class="hash-link" aria-label="Taro的直接链接" title="Taro的直接链接">​</a></h3><p><a href="https://taro.jd.com/" target="_blank" rel="noopener noreferrer">Taro</a> 是凹凸实验室推出的一套遵循 React 语法规范的多端统一开发解决方案，也是京东小程序官方推荐的开发框架。</p><p>使用 Taro 开发不但能更规范、更有效率地编码， 畅享 React / Vue 生态的各种工具。更重要的是只需编写一份代码，即可运行在各种平台：京东/微信/百度/支付宝/字节跳动/QQ 小程序、快应用、H5、React-Native 等。</p><p>如今 Taro 正被广泛用于京东各大业务，例如京东购物（微信）、京喜（微信、H5、RN）、京东好物街（微信、百度、字节跳动）、京东到家（微信、H5）等，在业界也被各大型业务广泛应用，如：58 同城、喜茶、腾讯吐个槽社区等等。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="使用-taro-快速开发京东小程序">使用 Taro 快速开发京东小程序<a href="#使用-taro-快速开发京东小程序" class="hash-link" aria-label="使用 Taro 快速开发京东小程序的直接链接" title="使用 Taro 快速开发京东小程序的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="入驻京东小程序">入驻京东小程序<a href="#入驻京东小程序" class="hash-link" aria-label="入驻京东小程序的直接链接" title="入驻京东小程序的直接链接">​</a></h3><p>进入<a href="https://mp.jd.com/?entrance=taro" target="_blank" rel="noopener noreferrer">京东小程序官网</a>，提交相关信息后完成入驻。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="开发">开发<a href="#开发" class="hash-link" aria-label="开发的直接链接" title="开发的直接链接">​</a></h3><p>1.安装 Taro 的 CLI 工具：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> -g @tarojs/cli</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p>已安装 Taro 的同学请注意更新 Taro 版本，Taro 自 1.3.20 后支持转换京东小程序。</p></blockquote><p>2.使用 CLI 创建项目：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">taro init </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">projectName</span><span class="token punctuation" style="color:#393A34">]</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>3.进入项目根目录，使用 CLI 编译项目，根据 <a href="/docs">Taro 文档</a> 进行开发：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">taro build --type jd </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">--watch</span><span class="token punctuation" style="color:#393A34">]</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>4.调试</p><p>申请入驻京东小程序成功后，京东小程序会给开发者发放邀请码，同时提供开发者工具下载，下载后可使用开发者工具进行调试。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="taro-已完全适配京东小程序">Taro 已完全适配京东小程序<a href="#taro-已完全适配京东小程序" class="hash-link" aria-label="Taro 已完全适配京东小程序的直接链接" title="Taro 已完全适配京东小程序的直接链接">​</a></h2><p>Taro 已 100% 完成了转换京东小程序的工作，全面支持京东小程序的各种特性。</p><p>开发中如遇到任何问题或困难，欢迎在 Github Issues、Taro 社区进行反馈，或联系 <strong><a href="mailto:taro@jd.com" target="_blank" rel="noopener noreferrer">taro@jd.com</a></strong>，我们会尽快给予答复与支持。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="示例项目转换效果图">示例项目转换效果图<a href="#示例项目转换效果图" class="hash-link" aria-label="示例项目转换效果图的直接链接" title="示例项目转换效果图的直接链接">​</a></h3><p>小编尝试把 <a href="https://github.com/NervJS/taro" target="_blank" rel="noopener noreferrer">Taro Github</a> 上学习资源中的优秀开源项目：<a href="https://github.com/qit-team/taro-yanxuan" target="_blank" rel="noopener noreferrer">仿严选多端项目</a> 分别转换为微信小程序端和京东小程序端，对比可见转换非常完美，表现效果基本一致。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="微信小程序端">微信小程序端<a href="#微信小程序端" class="hash-link" aria-label="微信小程序端的直接链接" title="微信小程序端的直接链接">​</a></h5><p><img loading="lazy" src="https://storage.jd.com/cjj-pub-images/jdapp_yanxuan_wx_demo.jpg" alt="yanxuan_wx" class="img_ZOZK"></p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="京东小程序端">京东小程序端<a href="#京东小程序端" class="hash-link" aria-label="京东小程序端的直接链接" title="京东小程序端的直接链接">​</a></h5><p><img loading="lazy" src="https://storage.jd.com/cjj-pub-images/jdapp_yanxuan_jd_demo.jpg" alt="yanxuan_jd" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="未来规划">未来规划<a href="#未来规划" class="hash-link" aria-label="未来规划的直接链接" title="未来规划的直接链接">​</a></h2><p>Taro 团队对于京东小程序是充满期待和信心的。为此我们讨论并规划了一系列工作，由底层框架适配，到辅助业务落地、拓展京东小程序生态，再到京东小程序的<a href="https://ling.jd.com/atom/cms/pc/06599/" target="_blank" rel="noopener noreferrer">可视化自助搭建</a>等，目前已经完成物料市场的部分物料适配验证并标记支持京东小程序、体验可视化自助搭建请点击<a href="https://ling.jd.com/atom/cms/pc/06599/" target="_blank" rel="noopener noreferrer">这个链接</a>按照指引申请体验权限。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="期待大家参与共建">期待大家参与共建<a href="#期待大家参与共建" class="hash-link" aria-label="期待大家参与共建的直接链接" title="期待大家参与共建的直接链接">​</a></h2><p>有任何意见建议、业务支持、合作诉求的，尽请发送邮件到 &nbsp;<strong><a href="mailto:taro@jd.com" target="_blank" rel="noopener noreferrer">taro@jd.com</a></strong>，我们会第一时间回复。</p><p>心动不如行动，现在就开始使用 Taro 开发你的<a href="https://mp.jd.com/?entrance=taro" target="_blank" rel="noopener noreferrer">京东小程序</a>吧～</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="了解更多关于京东小程序">了解更多关于京东小程序<a href="#了解更多关于京东小程序" class="hash-link" aria-label="了解更多关于京东小程序的直接链接" title="了解更多关于京东小程序的直接链接">​</a></h2><p><a href="https://taro-club.jd.com/topic/1426/%E4%BA%AC%E4%B8%9C%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%B9%B3%E5%8F%B0-%E4%BB%96%E6%9D%A5%E4%BA%86" target="_blank" rel="noopener noreferrer">《京东小程序平台，他来了》</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[京东小程序 Taro 开发对比原生开发测评]]></title>
            <link>https://docs.taro.zone/blog/2020-04-27-taro-vs-jd</link>
            <guid>https://docs.taro.zone/blog/2020-04-27-taro-vs-jd</guid>
            <pubDate>Mon, 27 Apr 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Taro 已经 100% 支持转换京东小程序，受到了很多同学的关注。当中有欢呼雀跃的声音：“一键转换为京东小程序，终于可以准时下班啦”。也有对 Taro 不太了解的同学提出了一些疑问：“转换的效果如何？”、“转换后代码的性能是否达标？” 等等。]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://img10.360buyimg.com/ling/jfs/t1/112290/35/2116/67015/5e9eee51E11ea7938/886d7ddc2e9e6026.png" class="img_ZOZK"></p><p>Taro 已经 100% 支持转换<a href="https://taro.jd.com/jdmp/index.html" target="_blank" rel="noopener noreferrer">京东小程序</a>，受到了很多同学的关注。当中有欢呼雀跃的声音：“一键转换为京东小程序，终于可以准时下班啦”。也有对 Taro 不太了解的同学提出了一些疑问：“转换的效果如何？”、“转换后代码的性能是否达标？” 等等。</p><p>针对各种疑问，我们从性能与开发体验的角度切入，把京东小程序原生开发与 Taro 开发进行了一番对比。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="性能对比">性能对比<a href="#性能对比" class="hash-link" aria-label="性能对比的直接链接" title="性能对比的直接链接">​</a></h2><p>针对性能的问题，我们分别测试了 Taro 空项目的包大小和 Taro 在长列表中的表现。因为包大小会影响小程序的首次加载速度，而长列表则是常常出现性能瓶颈的场景。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro-空项目包大小">Taro 空项目包大小<a href="#taro-空项目包大小" class="hash-link" aria-label="Taro 空项目包大小的直接链接" title="Taro 空项目包大小的直接链接">​</a></h3><p>目前各小程序都有对主包的大小进行限制，如京东小程序限制为 5M、微信小程序限制为 2M。这是因为初次进入的速度对于用户的体验非常地关键，而主包体积越大下载的时间就最长。因此小程序框架的大小也成为了开发前框架选型的重要参考指标之一，倘若框架体积过大，就会压缩业务逻辑的可用空间。</p><p>下列图片分别是 Taro 运行时框架压缩前后的大小，可以看到压缩后仅为 84k，对主包空间的影响十分小。</p><p>压缩前：</p><p><img loading="lazy" src="https://user-images.githubusercontent.com/11807297/67547702-5bacfd80-f732-11e9-9b99-9cbe93c2e19c.png" alt="QQ20191025-141758" class="img_ZOZK"></p><p>压缩后：</p><p><img loading="lazy" src="https://user-images.githubusercontent.com/11807297/67548124-9e230a00-f733-11e9-91e8-fe5bff2e5e67.png" alt="QQ20191025-142728" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="长列表渲染表现">长列表渲染表现<a href="#长列表渲染表现" class="hash-link" aria-label="长列表渲染表现的直接链接" title="长列表渲染表现的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="benchmark-介绍">benchmark 介绍<a href="#benchmark-介绍" class="hash-link" aria-label="benchmark 介绍的直接链接" title="benchmark 介绍的直接链接">​</a></h4><p>我们参照 <a href="https://github.com/krausest/js-framework-benchmark" target="_blank" rel="noopener noreferrer">js-framework-benchmark</a> 编写了一份 benchmark，测试对比了 Taro 代码与原生代码在长列表场景下的渲染表现。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="测速指标">测速指标<a href="#测速指标" class="hash-link" aria-label="测速指标的直接链接" title="测速指标的直接链接">​</a></h5><ul><li><p>初始化：从进入页面开始到完成 40 个商品的渲染。</p></li><li><p>创建：页面 onLoad 后创建 40 个商品。</p></li><li><p>增加：往已创建了 40 个商品的列表中每次增加 20 个商品。</p></li><li><p>部分更新：在 400 个商品中更新每第 10 个商品的名称。</p></li><li><p>交换：在 400 个商品中交换其中两个商品的位置。</p></li><li><p>选中：点击商品图片，改变商品名称的字体颜色。</p></li></ul><h5 class="anchor anchorWithStickyNavbar_mCi1" id="计时点">计时点<a href="#计时点" class="hash-link" aria-label="计时点的直接链接" title="计时点的直接链接">​</a></h5><p><strong>Taro</strong>：</p><p>开始：事件响应函数的顶部。</p><p>结束：<code>setState</code> 回调函数的顶部。</p><p><strong>原生小程序</strong>：</p><p>开始：事件响应函数的顶部。</p><p>结束：<code>setData</code> 回调函数的顶部。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="其它">其它<a href="#其它" class="hash-link" aria-label="其它的直接链接" title="其它的直接链接">​</a></h5><p>benchmark 仓库：<a href="https://github.com/NervJS/taro-benchmark" target="_blank" rel="noopener noreferrer">Github</a></p><p>Taro 版本：1.3.21</p><p>测试机型：魅蓝 note</p><p>测试方法：每组测试 10 条数据，去除其中最大值与最小值后求平均值</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="测试结果">测试结果<a href="#测试结果" class="hash-link" aria-label="测试结果的直接链接" title="测试结果的直接链接">​</a></h4><p>因为在京东小程序与微信小程序中，setData 的 callback 的触发时机稍有不同，所以<strong>分开列出</strong>。</p><table><thead><tr><th align="center">操作</th><th align="center">Taro jd</th><th align="center">原生京东小程序</th></tr></thead><tbody><tr><td align="center">初始化</td><td align="center">150</td><td align="center">123</td></tr><tr><td align="center">创建</td><td align="center">87</td><td align="center">85</td></tr><tr><td align="center">部分更新</td><td align="center">125</td><td align="center">235</td></tr><tr><td align="center">交换</td><td align="center">140</td><td align="center">213</td></tr><tr><td align="center">选中</td><td align="center">131</td><td align="center">155</td></tr></tbody></table><table><thead><tr><th align="center">操作</th><th align="center">Taro weapp</th><th align="center">原生微信小程序</th></tr></thead><tbody><tr><td align="center">初始化</td><td align="center">1155</td><td align="center">1223</td></tr><tr><td align="center">创建</td><td align="center">500</td><td align="center">408</td></tr><tr><td align="center">部分更新</td><td align="center">167</td><td align="center">307</td></tr><tr><td align="center">交换</td><td align="center">252</td><td align="center">309</td></tr><tr><td align="center">选中</td><td align="center">193</td><td align="center">178</td></tr></tbody></table><p>经测试发现，列表的长度会对增加操作的耗时产生影响：列表越长，增加操作的耗时越久。因此不能简单地对 N 次增加操作求平均增加耗时。这里我们选择使用折线图来展现出随增加操作次数的变化，渲染耗时的变化趋势。</p><p><img loading="lazy" src="https://user-images.githubusercontent.com/11807297/67666146-23155a00-f9a6-11e9-9644-ecdc2d6301ea.png" alt="京东小程序——增加" class="img_ZOZK"></p><p><img loading="lazy" src="https://user-images.githubusercontent.com/11807297/67656291-5350fe80-f98e-11e9-8c00-0123028a7471.png" alt="微信小程序——增加" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="测试结论">测试结论<a href="#测试结论" class="hash-link" aria-label="测试结论的直接链接" title="测试结论的直接链接">​</a></h4><p><strong>创建</strong></p><p>在创建时，Taro 会对数据做一些处理，因此会比原生稍慢。</p><p><strong>初始化</strong></p><p>初始化与创建相比，差别是引入了页面构造耗时。即<strong>初始化耗时 = 页面构造耗时 + 创建操作耗时</strong>。</p><p>Taro 在页面初始化、创建操作时都会对数据进行处理，因此整个初始化耗时会比原生稍慢。</p><p>那为什么微信小程序中 Taro 初始化耗时更短呢？在 benchmark 中 Taro 和原生分别在 <code>componentWillMount</code> 和 <code>onLoad</code> 渲染列表，而 Taro 使用 <strong>Component</strong> 构造页面，<code>componentWillMount</code> 其实是在 <code>attached</code> 生命周期中触发。因为在微信小程序中 <code>attached</code> 比 <code>onLoad</code> 早触发得多，所以会出现如此现象。</p><p><strong>选中</strong></p><p>因为 Taro 只是把回调函数包装了一层，处理了事件参数和 this 等，所以和原生的速度相当。</p><p><strong>部分更新、交换、增加</strong></p><p>Taro 的速度会优于原生。原因是 Taro 会先对将要 setData 的数据和当前 data 的数据做一次 <a href="/docs/2.x/optimized-practice#%E5%B0%8F%E7%A8%8B%E5%BA%8F%E6%95%B0%E6%8D%AE-diff">diff</a>，这能够大大减少 setData 的数据量，加快渲染速度。对比两个折线图可以得知，数据量越大，diff 的优化收益也越大。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro-对小程序的性能优化">Taro 对小程序的性能优化<a href="#taro-对小程序的性能优化" class="hash-link" aria-label="Taro 对小程序的性能优化的直接链接" title="Taro 对小程序的性能优化的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="setdata">setData<a href="#setdata" class="hash-link" aria-label="setData的直接链接" title="setData的直接链接">​</a></h4><p>在小程序中，性能的问题主要在于单次 setData 数据量过大和频繁调用 setData 上。Taro 利用 diff 解决了单次 setData 数据量过大的问题，而对于频繁调用 setData 也有解决的办法。</p><p>Taro 的 setState 遵循 React 规范，不同于 setData 的同步更新，它会异步地去更新视图。因此假设开发者在单次事件循环中多次调用 setState，最后也只会在下一个事件循环中进行一次 setData。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="跳转预加载">跳转预加载<a href="#跳转预加载" class="hash-link" aria-label="跳转预加载的直接链接" title="跳转预加载的直接链接">​</a></h4><p>小程序由 A 页面跳转到 B 页面的过程中，从 A 页面发起跳转到 B 页面触发 onLoad，有着 300~400 毫秒的延时。Taro 提供了 <code>componentWillPreload</code> 钩子，钩子会在发起跳转后立即执行。开发者可以尽早地在钩子里做一些数据拉取的工作，相比在 onLoad 触发后再去拉取数据就能够节省 300~400 毫秒的延时。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="shouldcomponentupdate--taropurecomponent">shouldComponentUpdate &amp; Taro.PureComponent<a href="#shouldcomponentupdate--taropurecomponent" class="hash-link" aria-label="shouldComponentUpdate &amp; Taro.PureComponent的直接链接" title="shouldComponentUpdate &amp; Taro.PureComponent的直接链接">​</a></h4><p>开发者的 Class Component 可以继承 <code>Taro.PureComponent</code>，这样组件在更新前会对新旧 props 和新旧 state 各做一次浅对比，避免不必要的更新。当然开发者可以自己实现 <code>shouldComponentUpdate</code>，通过手动控制新旧 props 和新旧 state 的对比，决定是否更新组件。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="taromemo">Taro.memo<a href="#taromemo" class="hash-link" aria-label="Taro.memo的直接链接" title="Taro.memo的直接链接">​</a></h4><p>如果开发者书写的是函数式组件，则可以利用 <code>Taro.memo</code> 实现 shouldComponentUpdate 的相同功能。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="开发体验对比">开发体验对比<a href="#开发体验对比" class="hash-link" aria-label="开发体验对比的直接链接" title="开发体验对比的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="语法">语法<a href="#语法" class="hash-link" aria-label="语法的直接链接" title="语法的直接链接">​</a></h3><p>京东小程序的原生语法和微信小程序相仿，都是类 MVVM 语法，没有接触过小程序的开发者有一定学习成本。另外样式语法为 css 的子集，其中自适应尺寸单位为 rpx，这样意味着如果我们需要 css 预处理器时需要手动配置工作流，并且在编写样式时处处注意尺寸单位的转换。</p><p>目前 Taro 遵循 React 语法（将来会支持所有 Web 前端框架），JSX 令我们的代码更加灵活。因此拥有 React 开发经验的开发者可以马上上手 Taro 的开发工作。在样式方面 Taro 支持在创建项目时选择是否使用 css 预处理器，选择后会自动配置相应的工作流。对于样式单位 Taro 也会把用户编写的 px 数值自动转换成对应的 rpx 数值，开发者无需再分心处理各平台的样式单位。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="项目结构">项目结构<a href="#项目结构" class="hash-link" aria-label="项目结构的直接链接" title="项目结构的直接链接">​</a></h3><p>原生开发中，页面和组件各由 4 个文件（js、jxml、jxss、json）所组成，代码管理相对麻烦。</p><p>Taro 中页面和组件均由一份 js 文件和一份样式文件组成，创建与维护十分容易。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="开发生态">开发生态<a href="#开发生态" class="hash-link" aria-label="开发生态的直接链接" title="开发生态的直接链接">​</a></h3><p>微信小程序经过不断迭代，相继推出了插件系统和支持引用 npm 包的功能。但京东小程序暂不支持前两者，京东小程序社区也还没打造起来，开发生态资源十分匮乏。</p><p>Taro 中不但能自由引用 npm 包，而且还大量支持 React 社区中沉淀的优秀工具和库，如 react-redux、mobx-react 等。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="开发辅助">开发辅助<a href="#开发辅助" class="hash-link" aria-label="开发辅助的直接链接" title="开发辅助的直接链接">​</a></h3><p>京东小程序原生开发不支持 Typescript，只能在 IDE 的编辑器中有自动补全功能，编码效率不高，同时也容易出错。</p><p>Taro 完美支持 Typescript，自带代码智能提示和代码实时检查功能，能让开发效率大大提升。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="写在最后">写在最后<a href="#写在最后" class="hash-link" aria-label="写在最后的直接链接" title="写在最后的直接链接">​</a></h2><p>看到这里大家可能会问，Taro 性能真的优于原生吗？其实并不然，针对每个场景，我们都可以用原生写出性能最佳的代码。但是这样做工作量太大，实际项目开发中需要掌握效率与优化之间的平衡。Taro 的优势在于能让我们在书写更有效率的代码、拥有更丰富的生态的同时，还带来了不错的性能。</p><p>最后，欢迎大家来使用 Taro 开发各端应用，有任何开发问题或合作意愿请联系 <strong><a href="mailto:taro@jd.com" target="_blank" rel="noopener noreferrer">taro@jd.com</a></strong>，我们会第一时间回复。</p><blockquote><p>相关链接</p></blockquote><blockquote><p><a href="https://taro.jd.com" target="_blank" rel="noopener noreferrer">Taro 官方网站</a></p></blockquote><blockquote><p><a href="/docs">Taro 文档</a></p></blockquote><blockquote><p><a href="https://taro-club.jd.com/" target="_blank" rel="noopener noreferrer">Taro 论坛</a></p></blockquote>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Taro Next H5 跨框架组件库实践]]></title>
            <link>https://docs.taro.zone/blog/2020-4-13-taro-components</link>
            <guid>https://docs.taro.zone/blog/2020-4-13-taro-components</guid>
            <pubDate>Mon, 13 Apr 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[image]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://img30.360buyimg.com/ling/jfs/t1/108543/10/12151/1750297/5e93ef97Ef424d4a7/29cf4e4364e5d3f0.png" alt="image" class="img_ZOZK"></p><p><a href="https://github.com/NervJS/taro" target="_blank" rel="noopener noreferrer">Taro</a> 是一款多端开发框架。开发者只需编写一份代码，即可生成各小程序端、H5 以及 React Native 的应用。</p><blockquote><p><a href="/docs">Taro Next</a> 近期已发布 beta 版本，全面完善对小程序以及 H5 的支持，欢迎体验！</p></blockquote><p>本文将重点介绍对 <strong>Taro H5 端组件库</strong>的改造工作。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="背景">背景<a href="#背景" class="hash-link" aria-label="背景的直接链接" title="背景的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro-next-将支持使用多框架开发">Taro Next 将支持使用多框架开发<a href="#taro-next-将支持使用多框架开发" class="hash-link" aria-label="Taro Next 将支持使用多框架开发的直接链接" title="Taro Next 将支持使用多框架开发的直接链接">​</a></h3><p>过去的 Taro 1 与 Taro 2 只能使用 React 语法进行开发，但下一代的 Taro 框架对整体架构进行了<a href="https://aotu.io/notes/2020/02/03/taro-next-alpha/" target="_blank" rel="noopener noreferrer">升级</a>，支持使用 React、Vue、Nerv 等框架开发多端应用。</p><p>为了支持使用多框架进行开发，Taro 需要对自身的各端适配能力进行改造。本文将重点介绍对 <strong>Taro H5 端组件库</strong>的改造工作。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro-h5">Taro H5<a href="#taro-h5" class="hash-link" aria-label="Taro H5的直接链接" title="Taro H5的直接链接">​</a></h3><p>Taro 遵循以微信小程序为主，其他小程序为辅的组件与 API 规范。</p><p>但浏览器并没有小程序规范的组件与 API 可供使用，例如我们不能在浏览器上使用小程序的 <code>view</code> 组件和 <code>getSystemInfo</code> API。因此我们需要在 H5 端实现一套基于小程序规范的组件库和 API 库。</p><blockquote><p>Taro H5 架构图：</p></blockquote><p><img loading="lazy" src="https://storage.jd.com/cjj-pub-images/WX20200402-140148.png" class="img_ZOZK"></p><p>在 Taro 1 和 Taro 2 中，Taro H5 的组件库使用了 React 语法进行开发。但如果开发者在 Taro Next 中使用 Vue 开发 H5 应用，则不能和现有的 H5 组件库兼容。</p><p>所以本文需要面对的核心问题就是：<strong>我们需要在 H5 端实现 React、Vue 等框架都可以使用的组件库</strong>。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="方案选择">方案选择<a href="#方案选择" class="hash-link" aria-label="方案选择的直接链接" title="方案选择的直接链接">​</a></h3><p>我们最先想到的是使用 Vue 再开发一套组件库，这样最为稳妥，工作量也没有特别大。</p><p>但考虑到以下两点，我们遂放弃了此思路：</p><ol><li>组件库的可维护性和拓展性不足。每当有问题需要修复或新功能需要添加，我们需要分别对 React 和 Vue 版本的组件库进行改造。</li><li>Taro Next 的目标是支持使用任意框架开发多端应用。倘若将来支持使用 Angular 等框架进行开发，那么我们需要再开发对应支持 Angular 等框架的组件库。</li></ol><p>那么是否存在着一种方案，使得只用一份代码构建的组件库能兼容所有的 web 开发框架呢？</p><p>答案就是 <strong>Web Components</strong>。</p><p>但在组件库改造为 Web Components 的过程并不是一凡风顺的，我们也遇到了不少的问题，故借此文向大家娓娓道来。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="web-components-简介">Web Components 简介<a href="#web-components-简介" class="hash-link" aria-label="Web Components 简介的直接链接" title="Web Components 简介的直接链接">​</a></h2><p><a href="https://www.webcomponents.org/introduction" target="_blank" rel="noopener noreferrer">Web Components</a> 由一系列的技术规范所组成，它让开发者可以开发出浏览器原生支持的组件。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="技术规范">技术规范<a href="#技术规范" class="hash-link" aria-label="技术规范的直接链接" title="技术规范的直接链接">​</a></h3><p>Web Components 的主要技术规范为：</p><ul><li>Custom Elements</li><li>Shadow DOM</li><li>HTML Template</li></ul><p>Custom Elements 让开发者可以自定义带有特定行为的 HTML 标签。</p><p>Shadow DOM 对标签内的结构和样式进行一层包装。</p><p><code>&lt;template&gt;</code> 标签为 Web Components 提供复用性，还可以配合 <code>&lt;slot&gt;</code> 标签提供灵活性。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="示例">示例<a href="#示例" class="hash-link" aria-label="示例的直接链接" title="示例的直接链接">​</a></h3><p>定义模板：</p><div class="language-html codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-html codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">template</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">template</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">h1</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">Hello World!</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">h1</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">template</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>构造 Custom Element：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">App</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">HTMLElement</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">constructor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">super</span><span class="token punctuation" style="color:#393A34">(</span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain">arguments</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 开启 Shadow DOM</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> shadowRoot </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">attachShadow</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">mode</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'open'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 复用 &lt;template&gt; 定义好的结构</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> template </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token dom variable" style="color:#36acaa">document</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">querySelector</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'#template'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> node </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> template</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">content</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">cloneNode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    shadowRoot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">appendChild</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">node</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token dom variable" style="color:#36acaa">window</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">customElements</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">define</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'my-app'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token maybe-class-name">App</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>使用：</p><div class="language-html codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-html codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">my-app</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">my-app</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="stencil">Stencil<a href="#stencil" class="hash-link" aria-label="Stencil的直接链接" title="Stencil的直接链接">​</a></h2><p>使用原生语法去编写 Web Components 相当繁琐，因此我们需要一个框架帮助我们提高开发效率和开发体验。</p><p>业界已经有很多成熟的 <a href="https://www.webcomponents.org/libraries" target="_blank" rel="noopener noreferrer">Web Components 框架</a>，一番比较后我们最终选择了 <a href="https://stenciljs.com/docs/introduction" target="_blank" rel="noopener noreferrer">Stencil</a>，原因有二：</p><ol><li>Stencil 由 Ionic 团队打造，被用于构建 Ionic 的组件库，证明经受过业界考验。</li><li>Stencil 支持 JSX，能减少现有组件库的迁移成本。</li></ol><p>Stencil 是一个可以生成 Web Components 的编译器。它糅合了业界前端框架的一些优秀概念，如支持 Typescript、JSX、虚拟 DOM 等。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="示例-1">示例：<a href="#示例-1" class="hash-link" aria-label="示例：的直接链接" title="示例：的直接链接">​</a></h3><p>创建 Stencil Component：</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">Component</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports maybe-class-name">Prop</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports maybe-class-name">State</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> h </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@stencil/core'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@</span><span class="token function maybe-class-name" style="color:#d73a49">Component</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">tag</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'my-component'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">MyComponent</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  @</span><span class="token function maybe-class-name" style="color:#d73a49">Prop</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> first </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">''</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  @</span><span class="token function maybe-class-name" style="color:#d73a49">State</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> last </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'JS'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">componentDidLoad</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'load'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">render</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        Hello, my name is </span><span class="token punctuation" style="color:#393A34">{</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">first</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">last</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>使用组件：</p><div class="language-html codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-html codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">my-component</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">first</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">Taro</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="在-react-与-vue-中使用-stencil">在 React 与 Vue 中使用 Stencil<a href="#在-react-与-vue-中使用-stencil" class="hash-link" aria-label="在 React 与 Vue 中使用 Stencil的直接链接" title="在 React 与 Vue 中使用 Stencil的直接链接">​</a></h2><p>到目前为止一切都那么美好：使用 Stencil 编写出 Web Components，即可以在 React 和 Vue 中直接使用它们。</p><p>但实际使用上却会出现一些问题，<a href="https://custom-elements-everywhere.com/" target="_blank" rel="noopener noreferrer">Custom Elements Everywhere</a> 通过一系列的测试用例，罗列出业界前端框架对 Web Components 的兼容问题及相关 issues。下面将简单介绍 Taro H5 组件库分别对 React 和 Vue 的兼容工作。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="兼容-react">兼容 React<a href="#兼容-react" class="hash-link" aria-label="兼容 React的直接链接" title="兼容 React的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="1-props">1. Props<a href="#1-props" class="hash-link" aria-label="1. Props的直接链接" title="1. Props的直接链接">​</a></h4><h5 class="anchor anchorWithStickyNavbar_mCi1" id="11-问题">1.1 问题<a href="#11-问题" class="hash-link" aria-label="1.1 问题的直接链接" title="1.1 问题的直接链接">​</a></h5><p>React 使用 <code>setAttribute</code> 的形式给 Web Components 传递参数。当参数为原始类型时是可以运行的，但是如果参数为对象或数组时，由于 HTML 元素的 attribute 值只能为字符串或 null，最终给 WebComponents 设置的 attribute 会是 <code>attr="[object Object]"</code>。</p><blockquote><p>attribute 与 property <a href="https://stackoverflow.com/questions/6003819/what-is-the-difference-between-properties-and-attributes-in-html#answer-6004028" target="_blank" rel="noopener noreferrer">区别</a></p></blockquote><h5 class="anchor anchorWithStickyNavbar_mCi1" id="12-解决方案">1.2 解决方案<a href="#12-解决方案" class="hash-link" aria-label="1.2 解决方案的直接链接" title="1.2 解决方案的直接链接">​</a></h5><p>采用 <strong>DOM Property</strong> 的方法传参。</p><p>我们可以把 Web Components 包装一层高阶组件，把高阶组件上的 props 设置为 Web Components 的 property：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">reactifyWebComponent</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter constant" style="color:#36acaa">WC</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">extends</span><span class="token plain"> </span><span class="token maybe-class-name">React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access maybe-class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ref </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token maybe-class-name">React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createRef</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">update</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token known-class-name class-name">Object</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">entries</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">props</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">forEach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter punctuation" style="color:#393A34">[</span><span class="token parameter">prop</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> val</span><span class="token parameter punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prop </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'children'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> prop </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'dangerouslySetInnerHTML'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prop </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'style'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> val </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">typeof</span><span class="token plain"> val </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'object'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword control-flow" style="color:#00009f">for</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> key </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> val</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">ref</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">current</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">style</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">key</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> val</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">key</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">ref</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">current</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">prop</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> val</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">componentDidUpdate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">update</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">componentDidMount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">update</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">render</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> children</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> dangerouslySetInnerHTML </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">props</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token maybe-class-name">React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createElement</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token constant" style="color:#36acaa">WC</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token literal-property property" style="color:#36acaa">ref</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">ref</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          dangerouslySetInnerHTML</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        children</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">MyComponent</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">reactifyWebComponent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'my-component'</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>注意：</p><ul><li>children、dangerouslySetInnerHTML 属性需要透传。</li><li>React 中 style 属性值可以接受对象形式，这里需要额外处理。</li></ul><h4 class="anchor anchorWithStickyNavbar_mCi1" id="2-events">2. Events<a href="#2-events" class="hash-link" aria-label="2. Events的直接链接" title="2. Events的直接链接">​</a></h4><h5 class="anchor anchorWithStickyNavbar_mCi1" id="21-问题">2.1 问题<a href="#21-问题" class="hash-link" aria-label="2.1 问题的直接链接" title="2.1 问题的直接链接">​</a></h5><p>因为 React 有一套<a href="https://reactjs.org/docs/events.html" target="_blank" rel="noopener noreferrer">合成事件系统</a>，所以它不能监听到 Web Components 发出的自定义事件。</p><p>以下 Web Component 的 onLongPress 回调不会被触发：</p><div class="language-html codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-html codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">my-view</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onLongPress</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">{onLongPress}</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">view</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">my-view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h5 class="anchor anchorWithStickyNavbar_mCi1" id="22-解决方案">2.2 解决方案<a href="#22-解决方案" class="hash-link" aria-label="2.2 解决方案的直接链接" title="2.2 解决方案的直接链接">​</a></h5><p>通过 ref 取得 Web Component 元素，手动 <strong>addEventListener</strong> 绑定事件。</p><p>改造上述的高阶组件：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">reactifyWebComponent</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token parameter constant" style="color:#36acaa">WC</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Index</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">React</span><span class="token class-name punctuation" style="color:#393A34">.</span><span class="token class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ref </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token maybe-class-name">React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createRef</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    eventHandlers </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">update</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">clearEventHandlers</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token known-class-name class-name">Object</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">entries</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">props</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">forEach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter punctuation" style="color:#393A34">[</span><span class="token parameter">prop</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> val</span><span class="token parameter punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">typeof</span><span class="token plain"> val </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'function'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> prop</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">match</span><span class="token punctuation" style="color:#393A34">(</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex" style="color:#36acaa">^on[A-Z]</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> event </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> prop</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">substr</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">toLowerCase</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">eventHandlers</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">push</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> val</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">ref</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">current</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addEventListener</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> val</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">clearEventHandlers</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">eventHandlers</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">forEach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter punctuation" style="color:#393A34">[</span><span class="token parameter">event</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> handler</span><span class="token parameter punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">ref</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">current</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">removeEventListener</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> handler</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">eventHandlers</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">componentWillUnmount</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">clearEventHandlers</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="3-ref">3. Ref<a href="#3-ref" class="hash-link" aria-label="3. Ref的直接链接" title="3. Ref的直接链接">​</a></h4><h5 class="anchor anchorWithStickyNavbar_mCi1" id="31-问题">3.1 问题<a href="#31-问题" class="hash-link" aria-label="3.1 问题的直接链接" title="3.1 问题的直接链接">​</a></h5><p>我们为了解决 Props 和 Events 的问题，引入了高阶组件。那么当开发者向高阶组件传入 ref 时，获取到的其实是高阶组件，但我们希望开发者能获取到对应的 Web Component。</p><p>domRef 会获取到 <code>MyComponent</code>，而不是 <code>&lt;my-component&gt;&lt;/my-component&gt;</code></p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">MyComponent</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">ref</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">domRef</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h5 class="anchor anchorWithStickyNavbar_mCi1" id="32-解决方案">3.2 解决方案<a href="#32-解决方案" class="hash-link" aria-label="3.2 解决方案的直接链接" title="3.2 解决方案的直接链接">​</a></h5><p>使用 <a href="https://reactjs.org/docs/forwarding-refs.html#forwarding-refs-to-dom-components" target="_blank" rel="noopener noreferrer">forwardRef</a> 传递 ref。</p><p>改造上述的高阶组件为 forwardRef 形式：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">reactifyWebComponent</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token parameter constant" style="color:#36acaa">WC</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Index</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">React</span><span class="token class-name punctuation" style="color:#393A34">.</span><span class="token class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token method function property-access" style="color:#d73a49">render</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> children</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> forwardRef </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">props</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token maybe-class-name">React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createElement</span><span class="token punctuation" style="color:#393A34">(</span><span class="token constant" style="color:#36acaa">WC</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">ref</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> forwardRef</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> children</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token maybe-class-name">React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">forwardRef</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">props</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> ref</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token maybe-class-name">React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createElement</span><span class="token punctuation" style="color:#393A34">(</span><span class="token maybe-class-name">Index</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain">props</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">forwardRef</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ref </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="4-hosts-classname">4. Host's className<a href="#4-hosts-classname" class="hash-link" aria-label="4. Host's className的直接链接" title="4. Host's className的直接链接">​</a></h4><h5 class="anchor anchorWithStickyNavbar_mCi1" id="41-问题">4.1 问题<a href="#41-问题" class="hash-link" aria-label="4.1 问题的直接链接" title="4.1 问题的直接链接">​</a></h5><p>在 Stencil 里我们可以使用 Host 组件为 host element 添加类名。</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">Component</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports maybe-class-name">Host</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> h </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@stencil/core'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@</span><span class="token function maybe-class-name" style="color:#d73a49">Component</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">tag</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'todo-list'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">TodoList</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">render</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Host</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"todo-list"</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">todo</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">div</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">Host</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>然后在使用 <code>&lt;todo-list&gt;</code> 元素时会展示我们内置的类名 “todo-list” 和 Stencil 自动加入的类名 “hydrated”：</p><p><img loading="lazy" src="https://storage.jd.com/cjj-pub-images/WX20200401-102833.png" class="img_ZOZK"></p><p>但如果我们在使用时设置了动态类名，如： <code>&lt;todo-list class={this.state.cls}&gt;</code>。那么在动态类名更新时，则会把内置的类名 “todo-list” 和 “hydrated” 抹除掉。</p><p><strong>关于类名 “hydrated”：</strong></p><p>Stencil 会为所有 Web Components 加上 <code>visibility: hidden;</code> 的样式。然后在各 Web Component 初始化完成后加入类名 “hydrated”，将 <code>visibility</code> 改为 <code>inherit</code>。如果 “hydrated” 被抹除掉，Web Components 将不可见。</p><p>因此我们需要保证在类名更新时不会覆盖 Web Components 的内置类名。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="42-解决方案">4.2 解决方案<a href="#42-解决方案" class="hash-link" aria-label="4.2 解决方案的直接链接" title="4.2 解决方案的直接链接">​</a></h5><p>高阶组件在使用 ref 为 Web Component 设置 className 属性时，对内置 class 进行合并。</p><p>改造上述的高阶组件：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">reactifyWebComponent</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token parameter constant" style="color:#36acaa">WC</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Index</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">React</span><span class="token class-name punctuation" style="color:#393A34">.</span><span class="token class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">update</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">prevProps</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token known-class-name class-name">Object</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">entries</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">props</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">forEach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter punctuation" style="color:#393A34">[</span><span class="token parameter">prop</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> val</span><span class="token parameter punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prop</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">toLowerCase</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'classname'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">ref</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">current</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">className</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> prevProps</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic">// getClassName 在保留内置类名的情况下，返回最新的类名</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getClassName</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">ref</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">current</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> prevProps</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">props</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> val</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">componentDidUpdate</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">prevProps</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">update</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prevProps</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">componentDidMount</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">update</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token maybe-class-name">React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">forwardRef</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">props</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> ref</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token maybe-class-name">React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createElement</span><span class="token punctuation" style="color:#393A34">(</span><span class="token maybe-class-name">Index</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain">props</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">forwardRef</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ref </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="兼容-vue">兼容 Vue<a href="#兼容-vue" class="hash-link" aria-label="兼容 Vue的直接链接" title="兼容 Vue的直接链接">​</a></h3><p>不同于 React，虽然 Vue 在传递参数给 Web Components 时也是采用 <code>setAttribute</code> 的方式，但 v-bind 指令提供了 <a href="https://cn.vuejs.org/v2/api/#v-bind" target="_blank" rel="noopener noreferrer">.prop</a> 修饰符，它可以将参数作为 DOM property 来绑定。另外 Vue 也能监听 Web Components 发出的自定义事件。</p><p>因此 Vue 在 Props 和 Events 两个问题上都不需要额外处理，但在与 Stencil 的配合上还是有一些兼容问题，接下来将列出主要的三点。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="1-hosts-classname">1. Host's className<a href="#1-hosts-classname" class="hash-link" aria-label="1. Host's className的直接链接" title="1. Host's className的直接链接">​</a></h4><h5 class="anchor anchorWithStickyNavbar_mCi1" id="11-问题-1">1.1 问题<a href="#11-问题-1" class="hash-link" aria-label="1.1 问题的直接链接" title="1.1 问题的直接链接">​</a></h5><p>同上文兼容 React 第四部分，在 Vue 中更新 host element 的 class，也会覆盖内置 class。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="12-解决方案-1">1.2 解决方案<a href="#12-解决方案-1" class="hash-link" aria-label="1.2 解决方案的直接链接" title="1.2 解决方案的直接链接">​</a></h5><p>同样的思路，需要在 Web Components 上包装一层 Vue 的自定义组件。</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">createComponent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">name</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> classNames </span><span class="token parameter operator" style="color:#393A34">=</span><span class="token parameter"> </span><span class="token parameter punctuation" style="color:#393A34">[</span><span class="token parameter punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">computed</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">listeners</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token spread operator" style="color:#393A34">...</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">$listeners</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">render</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">createElement</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">createElement</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword" style="color:#00009f">class</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'hydrated'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain">classNames</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token literal-property property" style="color:#36acaa">on</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">listeners</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">$slots</span><span class="token punctuation" style="color:#393A34">.</span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token maybe-class-name">Vue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">component</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'todo-list'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">createComponent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'todo-list'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'todo-list'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>注意：</p><ul><li>我们在自定义组件中重复声明了 Web Component 该有的内置类名。后续开发者为自定义组件设置类名时，Vue 将会<a href="https://cn.vuejs.org/v2/guide/components-props.html#%E6%9B%BF%E6%8D%A2-%E5%90%88%E5%B9%B6%E5%B7%B2%E6%9C%89%E7%9A%84-Attribute" target="_blank" rel="noopener noreferrer">自动对类名进行合并</a>。</li><li>需要把自定义组件上绑定的事件通过 <a href="https://cn.vuejs.org/v2/guide/components-custom-events.html#%E5%B0%86%E5%8E%9F%E7%94%9F%E4%BA%8B%E4%BB%B6%E7%BB%91%E5%AE%9A%E5%88%B0%E7%BB%84%E4%BB%B6" target="_blank" rel="noopener noreferrer">$listeners</a> 透传给 Web Component。</li></ul><h4 class="anchor anchorWithStickyNavbar_mCi1" id="2-ref">2. Ref<a href="#2-ref" class="hash-link" aria-label="2. Ref的直接链接" title="2. Ref的直接链接">​</a></h4><h5 class="anchor anchorWithStickyNavbar_mCi1" id="21-问题-1">2.1 问题<a href="#21-问题-1" class="hash-link" aria-label="2.1 问题的直接链接" title="2.1 问题的直接链接">​</a></h5><p>为了解决问题 1，我们给 Vue 中的 Web Components 都包装了一层自定义组件。同样地，开发者在使用 ref 时取到的是自定义组件，而不是 Web Component。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="22-解决方案-1">2.2 解决方案<a href="#22-解决方案-1" class="hash-link" aria-label="2.2 解决方案的直接链接" title="2.2 解决方案的直接链接">​</a></h5><p>Vue 并没有 forwardRef 的概念，只可简单粗暴地修改 <code>this.$parent.$refs</code>。</p><p>为自定义组件增加一个 mixin：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> refs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">mounted</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token known-class-name class-name">Object</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">keys</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">$parent</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">$refs</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">length</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> refs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">$parent</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">$refs</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">for</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> key </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> refs</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">refs</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">key</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          refs</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">key</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">$el</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword control-flow" style="color:#00009f">break</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">beforeDestroy</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token known-class-name class-name">Object</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">keys</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">$parent</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">$refs</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">length</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> refs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">$parent</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">$refs</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">for</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> key </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> refs</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">refs</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">key</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">$el</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          refs</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">key</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword control-flow" style="color:#00009f">break</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>注意：</p><ul><li>上述代码没有处理循环 ref，循环 ref 还需要另外判断和处理。</li></ul><h4 class="anchor anchorWithStickyNavbar_mCi1" id="3-v-model">3. v-model<a href="#3-v-model" class="hash-link" aria-label="3. v-model的直接链接" title="3. v-model的直接链接">​</a></h4><h5 class="anchor anchorWithStickyNavbar_mCi1" id="31-问题-1">3.1 问题<a href="#31-问题-1" class="hash-link" aria-label="3.1 问题的直接链接" title="3.1 问题的直接链接">​</a></h5><p>我们在自定义组件中使用了渲染函数进行渲染，因此对表单组件需要额外处理 <a href="https://cn.vuejs.org/v2/guide/render-function.html#v-model" target="_blank" rel="noopener noreferrer">v-model</a>。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="32-解决方案-1">3.2 解决方案<a href="#32-解决方案-1" class="hash-link" aria-label="3.2 解决方案的直接链接" title="3.2 解决方案的直接链接">​</a></h5><p>使用自定义组件上的 <code>model</code> 选项，定制组件使用 <code>v-model</code> 时的 prop 和 event。</p><p>改造上述的自定义组件：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">createFormsComponent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> event</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> modelValue </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'value'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> classNames </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">computed</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">listeners</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token spread operator" style="color:#393A34">...</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">$listeners</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">model</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">prop</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> modelValue</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">event</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'model'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">methods</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">input</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">$emit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'input'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">$emit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'model'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">target</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">value</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">change</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">$emit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'change'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">$emit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'model'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">target</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">value</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">render</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">createElement</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">createElement</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword" style="color:#00009f">class</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'hydrated'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain">classNames</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token literal-property property" style="color:#36acaa">on</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token spread operator" style="color:#393A34">...</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">listeners</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">$slots</span><span class="token punctuation" style="color:#393A34">.</span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">Input</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">createFormsComponent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'taro-input'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'input'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">Switch</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">createFormsComponent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'taro-switch'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'change'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'checked'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token maybe-class-name">Vue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">component</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'taro-input'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token maybe-class-name">Input</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token maybe-class-name">Vue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">component</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'taro-switch'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token maybe-class-name">Switch</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="总结">总结<a href="#总结" class="hash-link" aria-label="总结的直接链接" title="总结的直接链接">​</a></h2><p>当我们希望创建一些不拘泥于框架的组件时，Web Components 会是一个不错的选择。比如跨团队协作，双方的技术栈不同，但又需要公用部分组件时。</p><p>本次对 React 语法组件库进行 Web Components 化改造，工作量不下于重新搭建一个 Vue 组件库。但日后当 Taro 支持使用其他框架编写多端应用时，只需要针对对应框架与 Web Components 和 Stencil 的兼容问题编写一个胶水层即可，总体来看还是值得的。</p><p>关于胶水层，业界兼容 React 的方案颇多，只是兼容 Web Components 可以使用 <a href="https://github.com/BBKolton/reactify-wc" target="_blank" rel="noopener noreferrer">reactify-wc</a>，配合 Stencil 则可以使用官方提供的插件 <a href="https://github.com/ionic-team/stencil-ds-plugins/blob/master/README.md" target="_blank" rel="noopener noreferrer">Stencil DS Plugin</a>。倘若 Vue 需要兼容 Stencil，或需要提高兼容时的灵活性，还是建议手工编写一个胶水层。</p><p>本文简单介绍了 Taro Next、Web Components、Stencil 以及基于 Stencil 的组件库改造历程，希望能为读者们带来一些帮助与启迪。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Taro Next 发布预览版：同时支持 React / Vue / Nerv]]></title>
            <link>https://docs.taro.zone/blog/2020-02-13-taro-next-alpha</link>
            <guid>https://docs.taro.zone/blog/2020-02-13-taro-next-alpha</guid>
            <pubDate>Thu, 13 Feb 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[image]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://img14.360buyimg.com/ling/jfs/t1/103557/3/12087/1051626/5e44b357E4cab8765/d8c821c4a3e1060d.png" alt="image" class="img_ZOZK"></p><p>自 Taro 2.0 起，我们将会启动对整个 Taro 系统架构的革新，这次革新我们将其称之为 Taro Next。Taro Next 革新完成之后，Taro 本身的拓展性、稳定性、可维护性都会大幅提高，相应地，使用 Taro 的开发者也会获得更好的开发体验，降低更多开发成本和学习成本。</p><p>我们目前已经完成了<strong>编译系统和小程序端的重构</strong>，通过 <code>npm i -g @tarojs/cli@next</code> 安装 Taro CLI 预览（alpha）版之后，使用 <code>taro init</code> 创建新项目即可体验 Taro Next 的新特性。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="同时支持-reactvuenerv-三种框架">同时支持 React/Vue/Nerv 三种框架<a href="#同时支持-reactvuenerv-三种框架" class="hash-link" aria-label="同时支持 React/Vue/Nerv 三种框架的直接链接" title="同时支持 React/Vue/Nerv 三种框架的直接链接">​</a></h2><p>在旧版本的 Taro，我们以微信小程序的开发规范为基准，使用 React/JSX 的方式来进行开发。而在 Taro Next，我们把这一思路量化为一个编程模型：</p><p>设微信小程序生命周期为一个 <code>interface</code>，不同的框架实例的生命周期虽然不尽相同，但我们可以根据框架生命周期分别新建一个 <code>class</code> 去 <code>implements</code> 小程序生命周期的 <code>interface</code>。相应地，小程序的组件/API/路由规范可以使用同样的思路和模型让不同框架的代码，运行在不同的端上：</p><p><img loading="lazy" src="https://storage.jd.com/taro-source/taro-docs/WechatIMG1393.png" alt="taro" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="不限制语言语法">不限制语言、语法<a href="#不限制语言语法" class="hash-link" aria-label="不限制语言、语法的直接链接" title="不限制语言、语法的直接链接">​</a></h2><p>由于 Taro Next 的架构出现了变化，表面上来看 Taro 从一个编译型框架变成了一个运行时框架。但究其内核是整体的设计思路出现了变化：从前是「模拟（<code>mock</code>）」，现在是「实现（<code>implements</code>）」。在 Taro Next 我们实现了 React 在小程序中的完整支持，因此这类曾经的 Taro 无法运行的代码在 Taro Next 中完全没有压力：</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">View</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/components'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">Page</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">props</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> view </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token maybe-class-name">React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createElement</span><span class="token punctuation" style="color:#393A34">(</span><span class="token maybe-class-name">View</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> props</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">text</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">view</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token maybe-class-name">React</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access maybe-class-name">Children</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">only</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">props</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">children</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在旧版本的 Taro 中我们对 JavaScript 和 TypeScript 进行了 First Class 的支持，Taro Next 我们更进一步，原理上最终可以编译到 JavaScript 的语言都可以用来构建 Taro 项目，以下是一个在 Vue 中使用 CoffeeScript 的例子：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// config.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">webpackChain</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">chain</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        chain</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">merge</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token literal-property property" style="color:#36acaa">module</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token literal-property property" style="color:#36acaa">rule</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token literal-property property" style="color:#36acaa">test</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex" style="color:#36acaa">\.coffee$</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token literal-property property" style="color:#36acaa">use</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'coffee-loader'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="language-html codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-html codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">template</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">{{ title }}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">{{ text }}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">input</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">v-model</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">text</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">template</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">script</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">lang</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">coffee</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">  </span><span class="token script language-javascript keyword module" style="color:#00009f">export</span><span class="token script language-javascript"> </span><span class="token script language-javascript keyword module" style="color:#00009f">default</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">      </span><span class="token script language-javascript literal-property property" style="color:#36acaa">props</span><span class="token script language-javascript operator" style="color:#393A34">:</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">          </span><span class="token script language-javascript literal-property property" style="color:#36acaa">title</span><span class="token script language-javascript operator" style="color:#393A34">:</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">          </span><span class="token script language-javascript literal-property property" style="color:#36acaa">type</span><span class="token script language-javascript operator" style="color:#393A34">:</span><span class="token script language-javascript"> </span><span class="token script language-javascript known-class-name class-name">String</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">          </span><span class="token script language-javascript literal-property property" style="color:#36acaa">required</span><span class="token script language-javascript operator" style="color:#393A34">:</span><span class="token script language-javascript"> </span><span class="token script language-javascript boolean" style="color:#36acaa">true</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">      </span><span class="token script language-javascript literal-property property" style="color:#36acaa">data</span><span class="token script language-javascript operator" style="color:#393A34">:</span><span class="token script language-javascript"> </span><span class="token script language-javascript operator" style="color:#393A34">-</span><span class="token script language-javascript operator" style="color:#393A34">&gt;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript">          </span><span class="token script language-javascript literal-property property" style="color:#36acaa">text</span><span class="token script language-javascript operator" style="color:#393A34">:</span><span class="token script language-javascript"> </span><span class="token script language-javascript string" style="color:#e3116c">'text'</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="更快的运行速度">更快的运行速度<a href="#更快的运行速度" class="hash-link" aria-label="更快的运行速度的直接链接" title="更快的运行速度的直接链接">​</a></h2><p>运行时性能主要分为两个部分，一是更新性能，二是初始化性能。</p><p>对于更新性能而言，旧版本的 Taro 会把开发者 <code>setState</code> 的数据进行一次全量的 diff，最终返回给小程序是按路径更新的 <code>data</code>。而在 Taro Next 中 diff 的工作交给了开发者使用的框架（React/Nerv/Vue），而框架 diff 之后的数据也会通过 Taro 按路径去最小化更新。因此开发者可以根据使用框架的特性进行更多更细微的性能优化。</p><p>初始化性能则是 Taro Next 的痛点。原生小程序或编译型框架的初始数据可以直接用于渲染，但 Taro Next 在初始化时会把框架的渲染数据转化为小程序的渲染数据，多了一次 <code>setData</code> 开销。</p><p>为了解决这个问题，Taro 从服务端渲染受到启发，在 Taro CLI 将页面初始化的状态直接渲染为无状态的 wxml，在框架和业务逻辑运行之前执行渲染流程。我们将这一技术称之为预渲染（Prerender），经过 Prerender 的页面初始渲染速度通常会和原生小程序一致甚至更快。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="更快的构建速度和-source-map-支持">更快的构建速度和 source-map 支持<a href="#更快的构建速度和-source-map-支持" class="hash-link" aria-label="更快的构建速度和 source-map 支持的直接链接" title="更快的构建速度和 source-map 支持的直接链接">​</a></h2><p>作为一个编译型框架，旧版本的 Taro 会进行大量的 AST 操作，这类操作显著地拖慢了 Taro CLI 的编译速度。而在 Taro Next 中不会操作任何开发者代码的 AST，因此编译速度得到了大幅的提高。</p><p>正因为 AST 操作的取消，Taro Next 也轻松地实现了 <code>source-map</code> 的支持。这对于开发体验是一个巨大的提升：</p><p><img loading="lazy" src="https://storage.jd.com/taro-source/taro-docs/WechatIMG1402.png" alt="source-map" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="不忘初心">不忘初心<a href="#不忘初心" class="hash-link" aria-label="不忘初心的直接链接" title="不忘初心的直接链接">​</a></h2><p>在做到以上各项特性的同时，我们也没有丢掉原来就已经支持的特性：</p><ul><li>支持微信小程序、百度智能小程序、支付宝小程序、QQ 小程序、抖音小程序</li><li>使用原生小程序第三方组件/插件</li><li>多端条件编译</li><li>跨端 API 和样式处理</li></ul><p>这些特性基本涉及到了小程序开发的方方面面，虽然是预览版，但 Taro Next 已经具备了开发生产级小程序的准备，在 Taro 团队内部和兄弟团队也有多款小程序正在使用 Taro Next 进行开发。而在 Taro Next 的 H5 端和移动端，我们还在进行紧张的开发。当 Taro Next 测试（beta）版发布时，使用 Taro Next 构建的一套代码，就可以同时运行在各种小程序、快应用、H5 和移动端当中。在未来，我们还会把 Taro Next 的能力开放出去，让开发者只要写少量的接入代码，就可以使用自己喜欢的任意框架（Angular, Flutter, svelte...）开发小程序或多端应用。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="牢记使命">牢记使命<a href="#牢记使命" class="hash-link" aria-label="牢记使命的直接链接" title="牢记使命的直接链接">​</a></h2><p>正如我们在 Taro 2.0 发布时所言：</p><blockquote><p>节物风光不相待，桑田碧海须臾改。</p><p>20 年代呼啸而来，下一个 10 年，很多框架都会死去，很多技术也会焕然而生，没有什么是不变的，唯一不变的只有变化，我们能做的也只能是拥抱变化。</p></blockquote><p>前端技术一直在高速发展，流行的技术和框架每年都各不相同。但我们始终没有忘记开发 Taro 的初心和使命：<strong>降低开发成本，提高开发体验和开发效率。</strong></p><p>「不忘初心，牢记使命。」</p><p>这就是 Taro 团队拥抱变化的方式。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="参考资料">参考资料<a href="#参考资料" class="hash-link" aria-label="参考资料的直接链接" title="参考资料的直接链接">​</a></h2><ol><li><p><a href="/blog/2020-01-02-gmtc">小程序跨框架开发的探索与实践</a></p></li><li><p><a href="/docs/migration">Taro Next 旧版本迁移指南</a></p></li><li><p><a href="/docs/prerender">Prerender</a></p></li><li><p><a href="https://github.com/NervJS/taro-benchmark/tree/next" target="_blank" rel="noopener noreferrer">性能测试</a></p></li><li><p><a href="/docs/difference-to-others">与其它新型小程序的对比</a></p></li></ol>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Taro 2.0：拥抱社区，拥抱变化]]></title>
            <link>https://docs.taro.zone/blog/2020-01-08-taro-2-0</link>
            <guid>https://docs.taro.zone/blog/2020-01-08-taro-2-0</guid>
            <pubDate>Wed, 08 Jan 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Taro 1.x 版本自去年 9 月份发布以来，已经陪伴大家度过了一年多的时间，在此期间 Taro 一直保持高速成长，发布了多个具有重大意义的版本，让 Taro 成为如今一个功能完善，拥有众多忠实拥趸的多端统一开发框架。]]></description>
            <content:encoded><![CDATA[<p>Taro 1.x 版本自去年 9 月份发布以来，已经陪伴大家度过了一年多的时间，在此期间 Taro 一直保持高速成长，发布了多个具有重大意义的版本，让 Taro 成为如今一个功能完善，拥有众多忠实拥趸的多端统一开发框架。</p><p><img loading="lazy" src="https://storage.360buyimg.com/2.0/alls.jpg" class="img_ZOZK"></p><p>尽管 Taro 一直保持超高的迭代速度，Taro 的整体架构设计没有发生太大变化，这让 Taro 在这个时刻在变化的时代稍显佛系，且对于一个时刻想要突破自己的技术团队来说，常规性质的维护工作，显然无法安抚我们躁动的心，毕竟人的梦想，是永远不会停止的，所以我们决定启动一系列的颠覆式重构设计。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="20">2.0<a href="#20" class="hash-link" aria-label="2.0的直接链接" title="2.0的直接链接">​</a></h2><p>我们首先从 CLI 开始入手进行改造，大家都知道，原来 Taro CLI 的编译构建系统是自研的，整个构建系统逻辑复杂，要考虑的边际条件众多，这就导致了以下问题：</p><ul><li>维护困难，每次需要新增一个功能，例如支持解析 Markdown 文件，就需要直接改动 CLI，不够灵活</li><li>难以共建，CLI 的代码非常复杂，而且逻辑分支众多，让很多想要一起共建的人难以入手</li><li>可扩展性偏低，自研的构建系统，设计之初没有考虑到后续的扩展性，导致开发者想要添加自定义的功能无从下手</li></ul><p>基于以上问题，我们决定使用 Webpack 来实现编译构建，于是诞生了 2.0。</p><p>Taro 2.0 的 CLI 将会变得非常轻量，只会做区分编译平台、处理不同平台编译入参等操作，随后再调用对应平台的 <strong>runner 编译器</strong> 做代码编译操作，而原来大量的 AST 语法操作将会改造成 Webpack Plugin 以及 Loader，交给 Webpack 来处理。</p><p><img loading="lazy" src="https://storage.360buyimg.com/2.0/taro-cli.001.png" alt="taro-cli 2.0" class="img_ZOZK"></p><p>相较于旧的构建系统，新的小程序编译带来了以下优势：</p><ul><li>利于维护，大量的逻辑交由 Webpack 来处理，我们只需要维护一些插件</li><li>更加稳定，相较于自研的构建系统，新的构建会更加稳定，降低一些奇怪错误的出现概率</li><li>可扩展性强，可以通过自行加入 Webpack Loader 与 Plugin 的方式做自己想要的扩展</li><li>各端编译统一，接入 Webpack 后，Taro 各端的编译配置可以实现非常大程度的统一</li></ul><p>可以看到新的构建系统会有很大的进步。同时，更重要的是，基于 Webpack，我们可以在小程序中尝试更多的特性与技术，例如通过 tree shaking 来优化代码包大小等等，让小程序开发更加与业界发展同步，让 Taro 更加拥抱社区。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="migrate-to-20">Migrate to 2.0<a href="#migrate-to-20" class="hash-link" aria-label="Migrate to 2.0的直接链接" title="Migrate to 2.0的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="编译配置调整">编译配置调整<a href="#编译配置调整" class="hash-link" aria-label="编译配置调整的直接链接" title="编译配置调整的直接链接">​</a></h3><p>2.0 整体上与 1.0 是完全兼容的，但是在基于 Webpack 重构后，我们对部分编译配置做了优化调整，所以如果想要将基于 1.x 的旧项目迁移至 2.0，首先需要对编译配置进行调整。</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">projectName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'taro-framework'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">date</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'2019-11-2'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">designWidth</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">750</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">deviceRatio</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token number" style="color:#36acaa">640</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2.34</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token number" style="color:#36acaa">750</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token number" style="color:#36acaa">828</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.81</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">sourceRoot</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'src'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">outputRoot</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'dist'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// babel、csso、uglify 等配置从 plugins 配置中移出来</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">babel</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">sourceMap</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">presets</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'env'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">modules</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">plugins</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string" style="color:#e3116c">'transform-decorators-legacy'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string" style="color:#e3116c">'transform-class-properties'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string" style="color:#e3116c">'transform-object-rest-spread'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 小程序配置从 weapp 改为 mini，可以删掉很多小配置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">mini</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">webpackChain</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">chain</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> webpack</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">cssLoaderOption</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">postcss</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">pxtransform</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">enable</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">config</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">url</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">enable</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">config</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token literal-property property" style="color:#36acaa">limit</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10240</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 设定转换尺寸上限</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 可以删掉很多小配置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">h5</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">publicPath</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">staticDirectory</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'static'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">webpackChain</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">chain</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> webpack</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">postcss</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">autoprefixer</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">enable</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">config</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token literal-property property" style="color:#36acaa">browsers</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">'last 3 versions'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">'Android &gt;= 4.1'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">'ios &gt;= 8'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method-variable function-variable method function property-access" style="color:#d73a49">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">merge</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">NODE_ENV</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'development'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">merge</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> config</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'./dev'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">merge</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> config</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'./prod'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>具体编译配置请参考 <a title="编译配置文档" href="/docs/2.x/config-detail">编译配置文档</a>。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="异步编程调整">异步编程调整<a href="#异步编程调整" class="hash-link" aria-label="异步编程调整的直接链接" title="异步编程调整的直接链接">​</a></h3><p>Taro 2.0 中开启 <code>async functions</code> 支持不再需要安装 <code>@tarojs/async-await</code>，而是直接通过 babel 插件来获得支持。</p><p>在项目根目录下安装包 <code>babel-plugin-transform-runtime</code> 和 <code>babel-runtime</code>。</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">yarn</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> babel-plugin-transform-runtime --dev</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">yarn</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> babel-runtime</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>随后修改项目 <a title="babel 配置" href="/docs/2.x/config-detail#babel">babel 配置</a>，配置插件 <code>babel-plugin-transform-runtime</code>。</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token literal-property property" style="color:#36acaa">babel</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">sourceMap</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">presets</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'env'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">modules</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">plugins</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">'transform-decorators-legacy'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">'transform-class-properties'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">'transform-object-rest-spread'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'transform-runtime'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string-property property" style="color:#36acaa">"helpers"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string-property property" style="color:#36acaa">"polyfill"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string-property property" style="color:#36acaa">"regenerator"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string-property property" style="color:#36acaa">"moduleName"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'babel-runtime'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="新特性尝鲜">新特性尝鲜<a href="#新特性尝鲜" class="hash-link" aria-label="新特性尝鲜的直接链接" title="新特性尝鲜的直接链接">​</a></h3><p>在基于 Webpack 改造后带来全面提升的同时，2.0 也为我们带来了以下新的特性。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="主编译流程钩子">主编译流程钩子<a href="#主编译流程钩子" class="hash-link" aria-label="主编译流程钩子的直接链接" title="主编译流程钩子的直接链接">​</a></h4><p>在 2.0 中，CLI 编译的主流程已经基于 <a href="https://github.com/webpack/tapable" target="_blank" rel="noopener noreferrer" title="Tapable">Tapable</a> 进行改造，并且对外暴露了 <code>hooks</code> 以供使用，在 Taro 编译配置中可以通过 <code>plugins</code> 来配置编译过程插件，调用这些 <code>hooks</code> 来实现自己的需求。</p><p>目前编译主流程暴露了两个钩子 <code>beforeBuild</code> 和 <code>afterBuild</code>，其中，<code>beforeBuild</code> 将在整体编译前触发，可以获取到编译的相关配置，同时也能进行修改；<code>afterBuild</code> 将在 Webpack 编译完后执行，可以获取到编译后的结果。具体使用方式如下。</p><p>首先定义一个插件</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">BuildPlugin</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">apply</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">builder</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    builder</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">hooks</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">beforeBuild</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">tap</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'BuildPlugin'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">config</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">config</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    builder</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">hooks</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">afterBuild</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">tap</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'BuildPlugin'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">stats</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">stats</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>接下来在 <code>plugins</code> 字段中进行配置</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">plugins</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">BuildPlugin</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="为小程序编译添加-loader">为小程序编译添加 Loader<a href="#为小程序编译添加-loader" class="hash-link" aria-label="为小程序编译添加 Loader的直接链接" title="为小程序编译添加 Loader的直接链接">​</a></h4><p>我们有时候可能会面临在小程序中展示 Markdown 语法文件的需求，在 WEB 开发的时候我们可以借助 Webpack 及其 Loader，实现直接引入 <code>md</code> 文件并读取其内容，而在小程序开发中，通过借助 Taro 2.0，我们也能很轻松地实现这一需求。</p><p>一般我们会以如下的方式，来引入一个 <code>.md</code> 文件。</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">mdTxt</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'../../some_markdown.md'</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>而 <code>.md</code> 文件默认是不能直接被识别的，我们需要通过配置相应的 Loader 来实现对这类文件的加载解析，在 Taro 中可以通过 <a title="mini.webpackChain" href="/docs/2.x/config-detail#miniwebpackchain">mini.webpackChain</a> 来为小程序配置自定义 Webpack 配置，我们也可以通过它来配置 Loader。</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">mini</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">webpackChain</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">chain</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      chain</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">merge</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">module</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token literal-property property" style="color:#36acaa">rule</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token literal-property property" style="color:#36acaa">myloader</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token literal-property property" style="color:#36acaa">test</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex" style="color:#36acaa">\.md$</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token literal-property property" style="color:#36acaa">use</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  </span><span class="token literal-property property" style="color:#36acaa">loader</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'raw-loader'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  </span><span class="token literal-property property" style="color:#36acaa">options</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="为小程序编译添加-plugin">为小程序编译添加 Plugin<a href="#为小程序编译添加-plugin" class="hash-link" aria-label="为小程序编译添加 Plugin的直接链接" title="为小程序编译添加 Plugin的直接链接">​</a></h4><p>当我们要把打包后的小程序进行发布的时候，可能会遇到小程序过大的问题，那么我们肯定迫切希望可以看到到底是哪些文件的大小造成了这个影响，我们可以通过使用 <a href="https://www.npmjs.com/package/webpack-bundle-analyzer" target="_blank" rel="noopener noreferrer" title="webpack-bundle-analyzer">webpack-bundle-analyzer</a> 插件对打包体积进行分析。</p><p>在 <a title="mini.webpackChain" href="/docs/2.x/config-detail#miniwebpackchain">mini.webpackChain</a> 中添加如下配置。</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">mini</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">webpackChain</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">chain</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> webpack</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      chain</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">plugin</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'analyzer'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">use</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'webpack-bundle-analyzer'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access maybe-class-name">BundleAnalyzerPlugin</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在运行之后，我们就能在浏览器里看到如下分析效果。</p><p><img loading="lazy" src="https://storage.360buyimg.com/2.0/webpack-bundle-analyzer.jpg" alt="webpack-bundle-analyzer" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="taro-rn-依赖升级到-0599">Taro RN 依赖升级到 0.59.9<a href="#taro-rn-依赖升级到-0599" class="hash-link" aria-label="Taro RN 依赖升级到 0.59.9的直接链接" title="Taro RN 依赖升级到 0.59.9的直接链接">​</a></h4><p>在 2.0 中我们将 RN 端 React 依赖升级到 16.8.0，React Native 依赖升级到 0.59.9。主要原因：</p><ul><li>Google 要求所有 <a href="https://play.google.com/" target="_blank" rel="noopener noreferrer" title="Google Play">Google Play</a> 应用支持 64 位 so 库，而现有 RN 0.55.4 依无法支持 64 位库，为配合 64 位升级，Taro RN 端的 React Native 依赖需要同步升级</li><li>React 16.8.0 是第一个支持 Hook 的版本，React Native 从 0.59 版本开始支持 Hook，此前社区一直在呼吁对 RN 0.55.4 进行升级以直接支持 Hook 的写法</li></ul><p>本次 RN 端属于无缝升级，原有的写法和配置均不变，如果使用 <a href="https://github.com/NervJS/taro-native-shell" target="_blank" rel="noopener noreferrer" title="taro-native-shell">taro-native-shell</a> 的，选择 0.59.9 分支即可；在原生应用集成 RN 的，需要自行升级 React Native 依赖到 0.59.9。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="未来与展望">未来与展望<a href="#未来与展望" class="hash-link" aria-label="未来与展望的直接链接" title="未来与展望的直接链接">​</a></h2><p>正如前文所提到的，Taro 2.0 只是一个开始。</p><p>在 10 年代最后一场 GMTC 全球大前端技术大会上，Taro 团队向大家展示了 小程序跨框架开发的探索与实践 的艰辛旅程，同时也提前曝光了正在紧密开发中的 <a href="https://github.com/NervJS/taro/tree/next" target="_blank" rel="noopener noreferrer" title="Taro Next">Taro Next</a>。</p><p>那是一个完全区别于以往的版本，一条与现在 Taro 截然不同的道路，预示着 Taro 正在革自己的命。</p><p>节物风光不相待，桑田碧海须臾改。</p><p>20 年代呼啸而来，下一个 10 年，很多框架都会死去，很多技术也会焕然而生，没有什么是不变的，唯一不变的只有变化，我们能做的也只能是拥抱变化。</p>]]></content:encoded>
            <category>v2</category>
        </item>
        <item>
            <title><![CDATA[小程序跨框架开发的探索与实践]]></title>
            <link>https://docs.taro.zone/blog/2020-01-02-gmtc</link>
            <guid>https://docs.taro.zone/blog/2020-01-02-gmtc</guid>
            <pubDate>Thu, 02 Jan 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[前言：随着小程序开发的热度上升，小程序开发框架也层出不穷。但目前每个框架都会绑定一个专属 DSL，如类 React 或者类 Vue，在一个框架内，开发者无法根据团队技术栈自由选择 DSL，同时也无法共享框架本身的生态与工具。]]></description>
            <content:encoded><![CDATA[<blockquote><p>前言：随着小程序开发的热度上升，小程序开发框架也层出不穷。但目前每个框架都会绑定一个专属 DSL，如类 React 或者类 Vue，在一个框架内，开发者无法根据团队技术栈自由选择 DSL，同时也无法共享框架本身的生态与工具。</p></blockquote><p>本次分享将为大家介绍 Taro 如何将各种语法的前端框架运行在小程序上，讨论一个框架支持多 DSL 的实现探索，使得开发者可以使用任意热门框架/语法/DSL 来编写小程序应用，同时复用相关生态。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="小程序开发的历程">小程序开发的历程<a href="#小程序开发的历程" class="hash-link" aria-label="小程序开发的历程的直接链接" title="小程序开发的历程的直接链接">​</a></h2><p>2017 年 1 月 9 日凌晨，万众期待的微信小程序正式上线。</p><p>在此之前，京东投入一个前端小团队，经过一个月的封闭式开发，以一周一个版本的速度进行迭代，终于在第一时间发布了自己的 「京东购物」 小程序，尽管功能和界面现在看起来有些简陋，但在当时是完全符合微信小程序「触手可及，用完即走」的理念。</p><p><img loading="lazy" src="https://img12.360buyimg.com/ling/jfs/t1/88088/6/8131/521939/5e02d8a1Eacdf7ce4/f0e6dae4cdd5dce3.jpg" alt="image" class="img_ZOZK"></p><p>当然，随着整个项目的不断迭代，现在的 「京东购物」小程序在设计、交互以及功能复杂度已经全面向 APP 端看齐，这里面的工程化实践已经由 刘慧敏 老师在 <a href="https://gmtc.infoq.cn/2019/beijing/" target="_blank" rel="noopener noreferrer">GMTC 全球大前端技术大会（北京站）2019</a> 进行过分享，有兴趣的可以下载 PPT：<a href="https://ppt.geekbang.org/slide/download?cid=42&amp;pid=2416" target="_blank" rel="noopener noreferrer">京东购物小程序工程化之路</a>。</p><p>当时的微信小程序的开发存在一些缺点，比如依赖管理混乱、工程化流程落后、ES Next 支持不完善、命名规范不统一等。这些问题在现在看来都已经有了各种官方或非官方的解决办法，但是在当时小程序开发的探索阶段，这些问题都是一些痛点问题。</p><p>有句话我个人特别喜欢，那就是「<strong>当一门语言的能力不足，而用户的运行环境又不支持其它选择的时候，这门语言就会沦为 &nbsp;“编译目标”&nbsp; 语言</strong>」。</p><p>纵观整个前端的历史，无论是 CSS 预处理器的大行其道、各种模版的流行，还是 CoffeeScript 乃至 <a href="https://www.typescriptlang.org/" target="_blank" rel="noopener noreferrer">TypeScript</a> 的诞生，都印证了这个说法，微信小程序这里也不例外。因此，各种小程序开发框架如百花齐放，层出不穷。</p><p><img loading="lazy" src="https://img30.360buyimg.com/ling/jfs/t1/99678/10/8202/38241/5e02daafE215f2a8f/e5a69353a39406f2.jpg" alt="image" class="img_ZOZK"></p><p>这些小程序开发框架最主要的区别是 DSL，这点从 logo 颜色上就可以看出来，除了滴滴的 <a href="https://github.com/didi/chameleon" target="_blank" rel="noopener noreferrer">Chameleon</a> 是自定义 DSL 外，其余的绿色的 logo 是遵循了 Vue 语法（如 mpvue ），蓝色的 logo 是遵循了 React 的语法（如 Taro）。</p><p>在微信小程序之后，各大厂商纷纷发布了自己的小程序平台，比如：支付宝、百度、头条、QQ 等，再加上快应用、网易、360、京东等，小程序的赛道越来越拥挤，开发人员需要适配的小程序平台越来越多，因此，各大小程序开发框架也纷纷进行了多端适配。</p><p><img loading="lazy" src="https://img10.360buyimg.com/ling/jfs/t1/104562/35/8164/62130/5e032a3fEf7ea9e86/eabc43ac51e5d8fa.jpg" alt="image" class="img_ZOZK"></p><p>因此，站在这个时间节点反过来回顾整个小程序开发框架的进程，你会发现整个 2018 年乃至 2019 年初，小程序的开发框架主要的区别和重心在于：<strong>DSL</strong> 以及 <strong>多端适配</strong>。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="taro-的起源与初心">Taro 的起源与初心<a href="#taro-的起源与初心" class="hash-link" aria-label="Taro 的起源与初心的直接链接" title="Taro 的起源与初心的直接链接">​</a></h2><p>正所谓「<strong>业务孵化技术，技术服务业务</strong>」，<a href="https://github.com/NervJS/taro" target="_blank" rel="noopener noreferrer">Taro</a> 的诞生源自于业务需求的增加，当时我们的团队需要同时负责：京东购物，TOPLIFE 等业。团队人力资源捉襟见肘，与此同时，以上的业务都或多或少存在多端的需求，比如 微信小程序、H5、React Native(京东的主流 APP 基本都内置了 React Native 渲染引擎)，而且可以预见的是，以后很有可能需要适配更多的小程序平台，而每个端开发一套代码又不现实，会导致：<strong>研发成本上升，代码维护困难</strong>。</p><p>当时我们团队自研了一款 类 React 框架：<a href="https://github.com/NervJS/nerv" target="_blank" rel="noopener noreferrer">Nervjs</a>, 整个团队的技术栈因此全部转向了 React ，而当时市面上又没有一款遵循 React 语法的小程序框架，因此，我们开发了 Taro，希望能够使用 React 语法写小程序的同时，通过「<strong>Write once Run anywhere</strong>」来实现跨端的。</p><p><img loading="lazy" src="https://img14.360buyimg.com/ling/jfs/t1/88233/19/8246/31350/5e032f11E282a0405/0dbf94883f4d2ab5.jpg" alt="image" class="img_ZOZK"></p><p>整个 Taro 框架从 2018 年 6 月 7 日开源至今，一致保持着高速迭代，这些迭代主要集中在三个方面：</p><ul><li><strong>多端适配</strong>：从一个端到多个端</li><li><strong>开发体验</strong>：如支持 React Hooks、CSS Modules、Mobx 等</li><li><strong>社区共建</strong>：如 <a href="https://taro-club.jd.com/" target="_blank" rel="noopener noreferrer">Taro 论坛</a>、<a href="https://taro-ext.jd.com/" target="_blank" rel="noopener noreferrer">Taro 物料市场</a> 等平台，以及后面发布的 <a href="https://taro-club.jd.com/topic/680/taro-%E9%82%80%E4%BD%A0%E5%8A%A0%E5%85%A5%E7%A4%BE%E5%8C%BA%E5%85%B1%E5%BB%BA-%E7%A4%BE%E5%8C%BA%E5%85%B1%E5%BB%BA%E5%80%A1%E8%AE%AE%E4%B9%A6" target="_blank" rel="noopener noreferrer">社区共建计划</a></li></ul><p><img loading="lazy" src="https://img11.360buyimg.com/ling/jfs/t1/99148/6/8241/61075/5e034836Ef0c11f2a/9ef35a2c0edb3947.jpg" alt="image" class="img_ZOZK"></p><p>经过团队 一年多的努力，Taro 得到了社区的广泛认可，截止 2019 年 12 月 18 日，Taro 已拥有 22254 Stars 和 250 名 Contributors，社区主动提交的开发案例 150+：<a href="https://github.com/NervJS/taro-user-cases" target="_blank" rel="noopener noreferrer">taro-user-cases</a>，其中不乏多端案例。</p><p>但是尽管如此，Taro 还是存在一些问题无法解决，或者说：没那么好解决。比如：和 <code>React DSL</code> 强绑定、JSX 适配工作量大、社区贡献复杂等。这些问题归根到底，很大一部分是 Taro 的架构问题。</p><p><img loading="lazy" src="https://img13.360buyimg.com/ling/jfs/t1/107422/16/2534/35902/5e09b28fE332f85c2/03c4693f96aa1816.jpg" alt="image" class="img_ZOZK"></p><p>因此我们团队也一直在等待一次合适的机会，对整个架构进行一次提升，同时修复一些项目快速迭代欠下的技术债。</p><p>最主要的是，单纯的项目维护迭代已经满足不了我们团队躁动的心，我们渴望借此机会进行一次技术突破。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="小程序跨框架开发的探索">小程序跨框架开发的探索<a href="#小程序跨框架开发的探索" class="hash-link" aria-label="小程序跨框架开发的探索的直接链接" title="小程序跨框架开发的探索的直接链接">​</a></h2><p>在讲 Taro 架构之前，我们先来回顾一下小程序的架构。</p><p>微信小程序主要分为 <strong>逻辑层</strong> 和 <strong>视图层</strong>，以及在他们之下的原生部分。逻辑层主要负责 JS 运行，视图层主要负责页面的渲染，它们之间主要通过 <code>Event</code> 和 <code>Data</code> 进行通信，同时通过 <code>JSBridge</code> 调用原生的 API。这也是以微信小程序为首的大多数小程序的架构。</p><p><img loading="lazy" src="https://img12.360buyimg.com/ling/jfs/t1/107177/10/2710/57899/5e09b4d0E0f946516/6f1ce9685dbcb7a8.jpg" alt="image" class="img_ZOZK"></p><p>由于原生部分对于前端开发者来说就像是一个黑盒，因此，整个架构图的原生部分可以省略。同时，我们我们对 逻辑层 和 视图层 也做一下简化，最后可以得到小程序架构图的极简版：</p><p><img loading="lazy" src="https://img13.360buyimg.com/ling/jfs/t1/95252/26/8821/23351/5e09b555Eda94235f/f3c96e14c4c5c218.jpg" alt="image" class="img_ZOZK"></p><p>也就是说，只需要在逻辑层调用对应的 <code>App()/Page()</code> 方法，且在方法里面处理 data、提供生命周期/事件函数等，同时在视图层提供对应的模版及样式供渲染就能运行小程序了。这也是大多数小程序开发框架重点考虑和处理的部分。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro-当前架构">Taro 当前架构<a href="#taro-当前架构" class="hash-link" aria-label="Taro 当前架构的直接链接" title="Taro 当前架构的直接链接">​</a></h3><p>Taro 当前的架构主要分为：<strong>编译时</strong> 和 <strong>运行时</strong>。</p><p>其中编译时主要是将 Taro 代码通过 <a href="https://babeljs.io/" target="_blank" rel="noopener noreferrer">Babel</a> 转换成 小程序的代码，如：<code>JS</code>、<code>WXML</code>、<code>WXSS</code>、<code>JSON</code>。</p><p>运行时主要是进行一些：生命周期、事件、data 等部分的处理和对接。</p><p><img loading="lazy" src="https://img10.360buyimg.com/ling/jfs/t1/106172/7/8929/32332/5e09b68eEba12dd4c/9298bfdd494a3aae.jpg" alt="image" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="taro-编译时">Taro 编译时<a href="#taro-编译时" class="hash-link" aria-label="Taro 编译时的直接链接" title="Taro 编译时的直接链接">​</a></h4><p>有过 Babel 插件开发经验的应该对一下流程十分熟悉，Taro 的编译时也是遵循了此流程，使用 <a href="https://babeljs.io/docs/en/babel-parser" target="_blank" rel="noopener noreferrer">babel-parser</a> 将 Taro 代码解析成抽象语法树，然后通过 <a href="https://babeljs.io/docs/en/babel-types" target="_blank" rel="noopener noreferrer">babel-types</a> 对抽象语法树进行一系列修改、转换操作，最后再通过 <a href="https://babeljs.io/docs/en/babel-generator" target="_blank" rel="noopener noreferrer">babel-generate</a> 生成对应的目标代码。</p><p>详情可以参考：<a href="https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md" target="_blank" rel="noopener noreferrer">babel-handbook</a></p><p><img loading="lazy" src="https://img11.360buyimg.com/ling/jfs/t1/105147/3/8169/18536/5e09b73cEf890be4f/a431388d25b2842d.jpg" alt="image" class="img_ZOZK"></p><p>整个编译时最复杂的部分在于 JSX 编译。</p><p>我们都知道 JSX 是一个 JavaScript 的语法扩展，它的写法千变万化，十分灵活。这里我们是采用 <strong>穷举</strong> 的方式对 JSX 可能的写法进行了一一适配，这一部分工作量很大，<strong>实际上 Taro 有大量的 Commit 都是为了更完善的支持 JSX 的各种写法</strong>。</p><p>但尽管如此，我们也不可能完全覆盖所有的情况，因此还是推荐大家按照官方规范书写 React 代码，同时，我们也提供了丰富的 ESlint 插件来辅助大家书写规范的代码。</p><p><img loading="lazy" src="https://img20.360buyimg.com/ling/jfs/t1/89156/33/8823/16470/5e09b863E5d45cb87/3be78140b1186009.jpg" alt="image" class="img_ZOZK"></p><p>这一块我们团队内部一直有个梗：<strong>如果你使用 Taro 开发感觉 Bug 少，那说明你的 React 代码写得很规范</strong>。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="taro-运行时">Taro 运行时<a href="#taro-运行时" class="hash-link" aria-label="Taro 运行时的直接链接" title="Taro 运行时的直接链接">​</a></h4><p>接下来，我们可以对比一下编译后的代码，可以发现，编译后的代码中，React 的核心 render 方法 没有了。同时代码里增加了 <code>BaseComponent</code> 和 <code>createComponent</code> ,它们是 Taro 运行时的核心。</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 编译前</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain">&nbsp;</span><span class="token imports maybe-class-name">Taro</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports">&nbsp;</span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports">&nbsp;Component&nbsp;</span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain">&nbsp;</span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain">&nbsp;</span><span class="token string" style="color:#e3116c">'@tarojs/taro'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain">&nbsp;</span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports">&nbsp;View</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports">&nbsp;Text&nbsp;</span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain">&nbsp;</span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain">&nbsp;</span><span class="token string" style="color:#e3116c">'@tarojs/components'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain">&nbsp;</span><span class="token string" style="color:#e3116c">'./index.scss'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain">&nbsp;</span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain">&nbsp;</span><span class="token keyword" style="color:#00009f">class</span><span class="token plain">&nbsp;</span><span class="token class-name">Index</span><span class="token plain">&nbsp;</span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain">&nbsp;</span><span class="token class-name">Component</span><span class="token plain">&nbsp;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&nbsp;&nbsp;config&nbsp;</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">&nbsp;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&nbsp;&nbsp;&nbsp;&nbsp;navigationBarTitleText</span><span class="token operator" style="color:#393A34">:</span><span class="token plain">&nbsp;</span><span class="token string" style="color:#e3116c">'首页'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&nbsp;&nbsp;</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&nbsp;&nbsp;</span><span class="token function" style="color:#d73a49">componentDidMount</span><span class="token plain">&nbsp;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">&nbsp;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">&nbsp;</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&nbsp;&nbsp;</span><span class="token function" style="color:#d73a49">render</span><span class="token plain">&nbsp;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">&nbsp;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain">&nbsp;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View&nbsp;className</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">‘index' onClick</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">onClick</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">Hello&nbsp;world!</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&nbsp;&nbsp;</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 编译后</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain">&nbsp;</span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports maybe-class-name">BaseComponent</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports">&nbsp;createComponent</span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain">&nbsp;</span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain">&nbsp;</span><span class="token string" style="color:#e3116c">'@tarojs/taro-weapp'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain">&nbsp;</span><span class="token class-name">Index</span><span class="token plain">&nbsp;</span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain">&nbsp;</span><span class="token class-name">BaseComponent</span><span class="token plain">&nbsp;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&nbsp;&nbsp;</span><span class="token function" style="color:#d73a49">_createDate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="token comment" style="color:#999988;font-style:italic">//process&nbsp;state&nbsp;and&nbsp;props</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&nbsp;&nbsp;</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain">&nbsp;</span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain">&nbsp;</span><span class="token function" style="color:#d73a49">createComponent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token maybe-class-name">Index</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><code>BaseComponent</code> 大概的 UML 图如下，主要是对 React 的一些核心方法：<code>setState</code>、<code>forceUpdate</code> 等进行了替换和重写，结合前面编译后 render 方法被替换，大家不难猜出：<strong>Taro 当前架构只是在开发时遵循了 React 的语法，在代码编译之后实际运行时，和 React 并没有关系</strong>。</p><p><img loading="lazy" src="https://img20.360buyimg.com/ling/jfs/t1/94580/33/9226/44101/5e0d931bE5f13069b/bc2a93e23c4253de.jpg" alt="image" class="img_ZOZK"></p><p>而 <code>createComponent</code> 主要作用是调用 <code>Component()</code> 构建页面；对接事件、生命周期等；进行 <code>Diff Data</code> 并调用 <code>setData</code> 方法更新数据。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="总结">总结<a href="#总结" class="hash-link" aria-label="总结的直接链接" title="总结的直接链接">​</a></h4><p>因此，整个 Taro 当前架构的特点是：</p><ul><li><strong>重编译时，轻运行时</strong>：这从两边代码行数的对比就可见一斑。</li><li>编译后代码与 React 无关：Taro 只是在开发时遵循了 React 的语法。</li><li>直接使用 Babel 进行编译：这也导致当前 Taro 在工程化和插件方面的羸弱。</li></ul><p><img loading="lazy" src="https://img11.360buyimg.com/ling/jfs/t1/89541/6/9134/34300/5e09c074E0f407f81/c2a4f263c165616a.jpg" alt="image" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="其它解决方案的架构">其它解决方案的架构<a href="#其它解决方案的架构" class="hash-link" aria-label="其它解决方案的架构的直接链接" title="其它解决方案的架构的直接链接">​</a></h3><p>小程序开发框架百花齐放，我们也从社区里得到了不少启发。</p><p>接下来我们来看看 遵循 vue 语法的小程序开发框架的代表：<a href="https://github.com/Meituan-Dianping/mpvue" target="_blank" rel="noopener noreferrer">mpvue</a> 是怎样实现的。</p><p><img loading="lazy" src="https://img30.360buyimg.com/ling/jfs/t1/107088/4/8868/43564/5e09c17eEacdd5c10/fa7018caaf5fb782.jpg" alt="image" class="img_ZOZK"></p><p>看过 Vue 源码的同学对上面的文件夹和架构肯定熟悉，本质上，mpvue 就是 fork 了一份 <code>vuejs/vue@2.4.1</code> 的代码，保留了 Vue runtime 能力，同时添加了小程序平台的支持。</p><p>具体在源码中的表现就是：在 Vue 源码的 platforms 文件夹下面增加了 mp 目录，在里面实现了 <code>complier（编译时）</code> 和 <code>runtime （运行时）</code>支持。</p><p>mpvue 的实现同样分为：<strong>编译时</strong>和<strong>运行时</strong>。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="mpvue-编译时">mpvue 编译时<a href="#mpvue-编译时" class="hash-link" aria-label="mpvue 编译时的直接链接" title="mpvue 编译时的直接链接">​</a></h4><p>其中编译时做的事情和 Taro 很类似：将 Vue SFC 写法的代码编译成 小程序代码文件（JS、WXML、WXSS、JSON）。</p><p>最大的区别是 Taro 将 JSX 编译成 小程序模版，而 mpvue 是将 Vue 模版编译成 小程序模版。但是由于 Vue 模版和 小程序模版的相似性，mpvue 在这一块的工作量比 Taro 少得多。</p><p><img loading="lazy" src="https://img14.360buyimg.com/ling/jfs/t1/107613/2/2742/42346/5e09f5c5E7424d037/b2d7a1d5327cdf4f.jpg" alt="image" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="mpvue-运行时">mpvue 运行时<a href="#mpvue-运行时" class="hash-link" aria-label="mpvue 运行时的直接链接" title="mpvue 运行时的直接链接">​</a></h4><p>而 mpvue 的运行时和 Vue 的运行时是强关联的，首先我们来看看 Vue 的运行时。</p><p>一个 .vue 的单文件由三部分构成: <code>template</code>, <code>script</code>, <code>style</code> 。</p><p>橙色路径部分, template 会在编译的过程中，在 <code>vue-loader</code> 中通过 ast 进行分析，最终生成一段 render 函数，执行 render 函数会生成虚拟 dom 树，虚拟 DOM 树是对真实 DOM 树的抽象，树中的节点被称作 vnode 。</p><p>Vue 拿到 虚拟 DOM 树之后，就可以去和上次老的 虚拟 DOM 树 做 <code>patch diff</code> 对比。patch 阶段之后，vue 就会使用真实的操作 DOM 的方法（比如说 <code>insertBefore</code> , <code>appendChild</code> 之类的），去操作 DOM 结点，更新视图。</p><p>同时，绿色路径的部分，在实例化 Vue 的时候，会对数据 data 做响应式的处理，在监测到 data 发生改变时，会调用 render 函数，生成最新的虚拟 DOM 树， 接着对比老的虚拟 DOM 树进行 patch, 找出最小修改代价的 vnode 节点进行修改。</p><p><img loading="lazy" src="https://img14.360buyimg.com/ling/jfs/t1/98880/36/8962/23682/5e09ee76Ee70d5703/898f0c8b2003eafe.jpg" alt="image" class="img_ZOZK"></p><p>而 mpvue 的运行时，会首先将 patch 阶段的 DOM 操作相关方法置空，也就是什么都不做。其次，在创建 Vue 实例的同时，还会偷偷的调用 <code>Page()</code> 用于生成了小程序的 page 实例。然后 运行时的 patch 阶段会直接调用 <code>$updateDataToMp()</code> 方法，这个方法会获取挂在在 page 实例上维护的数据 ，然后通过 <code>setData</code> 方法更新到视图层。</p><p>mpvue 整体原理图也就如下：</p><p><img loading="lazy" src="https://img12.360buyimg.com/ling/jfs/t1/85977/36/9055/54025/5e09efc8Ea2a7e868/60360ca99a89c1a7.jpg" alt="image" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="一些总结与思考">一些总结与思考<a href="#一些总结与思考" class="hash-link" aria-label="一些总结与思考的直接链接" title="一些总结与思考的直接链接">​</a></h4><p>因此，和 Taro 重编译时轻运行时不同，mpvue 算是：<strong>半编译时，半运行时</strong>。这点从代码量的对比也能大致反映出来。</p><p>mpvue 的 WXML 模版和 Taro 一样，也是通过代码编译得到的；不同于 Taro 运行时和 React 无关，mpvue 本质上还是将 Vue 运行在了小程序，且实现了 <code>Vue@2.4.1</code> 绝大部分特性（只有极少数特性由于小程序模版的限制未能实现，如 ：<code>filter</code>、<code>slot</code>、<code>v-html</code>）；且整个框架基于 Webpack 实现了较为完善的工程化。</p><p><img loading="lazy" src="https://img14.360buyimg.com/ling/jfs/t1/92568/25/8880/35958/5e09f1a6E4a7a414c/6c78485432afb527.jpg" alt="mage" class="img_ZOZK"></p><p>其他小程序框架的实现原理和效果上的差异性，也带来了我们的一些思考：</p><ul><li><strong>编译时 OR 运行时</strong>：当初 Taro 选择重编译时的主要原因是处于性能考虑，毕竟<strong>同等条件下，编译时做的工作越多，也就意味着运行时做的工作越少，性能会更好</strong>；另外，重编译时也保证了 Taro 的代码在编译之后的可读性。但是从长远来看，计算机硬件的性能越来越冗余，<strong>如果在牺牲一点可以容忍的性能的情况下换来整个框架更大的灵活性和更好的适配性，我们认为是值得的</strong>。</li><li><strong>模版静态编译 OR 动态构建</strong>：尽管 Taro 和 mpvue 的模版都是通过静态编译生成的，但是社区也不乏动态构建的例子，比如：<a href="https://github.com/remaxjs/remax" target="_blank" rel="noopener noreferrer">Remax</a>。</li><li><strong>DSL 限制</strong>：我们能否实现一个小程序开发框架，摆脱 DSL 的限制？</li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="新架构-taro-next-的适配与实现">新架构 Taro Next 的适配与实现<a href="#新架构-taro-next-的适配与实现" class="hash-link" aria-label="新架构 Taro Next 的适配与实现的直接链接" title="新架构 Taro Next 的适配与实现的直接链接">​</a></h2><p>这一次，我们站在浏览器的角度来思考<strong>前端的本质</strong>：无论开发这是用的是什么框架，React 也好，Vue 也罢，最终代码经过运行之后都是调用了浏览器的那几个 BOM/DOM 的 API ，如：<code>createElement</code>、<code>appendChild</code>、<code>removeChild</code> 等。</p><p><img loading="lazy" src="https://img10.360buyimg.com/ling/jfs/t1/91591/19/8966/30891/5e09f712E6e7f1df8/c3cbbead85810461.jpg" alt="image" class="img_ZOZK"></p><p>因此，我们创建了 <a href="https://github.com/NervJS/taro/tree/next/packages/taro-runtime" target="_blank" rel="noopener noreferrer">taro-runtime</a> 的包，然后在这个包中实现了 <strong>一套 高效、精简版的 DOM/BOM API</strong>（下面的 UML 图只是反映了几个主要的类的结构和关系）：</p><p><img loading="lazy" src="https://img12.360buyimg.com/ling/jfs/t1/107806/8/2825/294525/5e09f810E8d32975f/ec84acddae669131.png" alt="image" class="img_ZOZK"></p><p>然后，我们通过 Webpack 的 <a href="https://webpack.js.org/plugins/provide-plugin/" target="_blank" rel="noopener noreferrer">ProvidePlugin</a> 插件，注入到小程序的逻辑层。</p><p><img loading="lazy" src="https://img13.360buyimg.com/ling/jfs/t1/108453/24/2831/46425/5e09f855E1b8decb1/ce5c38a07b99556c.jpg" alt="image" class="img_ZOZK"></p><p>这样，在小程序的运行时，就有了 <strong>一套高效、精简版的 DOM/BOM API</strong>。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="react-实现">React 实现<a href="#react-实现" class="hash-link" aria-label="React 实现的直接链接" title="React 实现的直接链接">​</a></h3><p>在 <code>DOM/BOM</code> 注入之后，理论上来说，Nerv/Preact 就可以直接运行了。但是 React 有点特殊，因为 <code>React-DOM</code> 包含大量浏览器兼容类的代码，导致包太大，而这部分代码我们是不需要的，因此我们需要做一些定制和优化。</p><p>在 React 16+ ，React 的架构如下：</p><p><img loading="lazy" src="https://img10.360buyimg.com/ling/jfs/t1/93547/36/9145/40000/5e09f948Edd646b65/ce37c0d099b7f702.jpg" alt="image" class="img_ZOZK"></p><p>最上层是 React 的核心部分 <code>react-core</code> ，中间是 <code>react-reconciler</code>，其的职责是维护 <code>VirtualDOM</code> 树，内部实现了 <code>Diff/Fiber</code> 算法，决定什么时候更新、以及要更新什么。</p><p>而 <code>Renderer</code> 负责具体平台的渲染工作，它会提供宿主组件、处理事件等等。例如 <code>React-DOM</code> 就是一个渲染器，负责 DOM 节点的渲染和 DOM 事件处理。</p><p>因此，我们实现了 <a href="https://github.com/NervJS/taro/tree/next/packages/taro-react" target="_blank" rel="noopener noreferrer">taro-react</a> 包，用来连接 <code>react-reconciler</code> 和 <code>taro-runtime</code> 的 BOM/DOM API：</p><p><img loading="lazy" src="https://img13.360buyimg.com/ling/jfs/t1/90851/28/9324/88846/5e0d9374Ec9b1a530/6a494163f77c6dd7.jpg" alt="image" class="img_ZOZK"></p><p>具体的实现主要分为两步：</p><ol><li>实现 <code>react-reconciler</code> 的 <code>hostConfig</code> 配置，即在 <code>hostConfig</code> 的方法中调用对应的 Taro BOM/DOM 的 API。</li><li>实现 render 函数（类似于 <code>ReactDOM.render</code>）方法，可以看成是创建 <code>Taro DOM Tree</code> 的容器。</li></ol><p><img loading="lazy" src="https://img14.360buyimg.com/ling/jfs/t1/93510/31/8909/48959/5e09fa87E1d831f7e/5d6f1f945c23e529.jpg" alt="image" class="img_ZOZK"></p><p>经过上面的步骤，React 代码实际上就可以在小程序的运行时正常运行了，并且会生成 <code>Taro DOM Tree</code>，那么偌大的 Taro DOM Tree 怎样更新到页面呢？</p><p>首先，我们将小程序的所有组件挨个进行<strong>模版化处理</strong>，从而得到小程序组件对应的模版，如下图就是小程序的 view 组件经过模版化处理后的样子：</p><p><img loading="lazy" src="https://img12.360buyimg.com/ling/jfs/t1/85731/39/9039/431602/5e09fbd5Ef650095a/2fefbf82fefa9abe.png" alt="image" class="img_ZOZK"></p><p>然后，我们会：<strong>基于组件的 template，动态 “递归” 渲染整棵树</strong>。</p><p>具体流程为先去遍历 <code>Taro DOM Tree</code> 根节点的子元素，再根据每个子元素的类型选择对应的模板来渲染子元素，然后在每个模板中我们又会去遍历当前元素的子元素，以此把整个节点树递归遍历出来。</p><p><img loading="lazy" src="https://img10.360buyimg.com/ling/jfs/t1/91642/11/9016/45574/5e09fc9dE03db038c/f87d7fc69e04b26e.jpg" alt="image" class="img_ZOZK"></p><p>整个 Taro Next 的 React 实现流程图如下：</p><p><img loading="lazy" src="https://img11.360buyimg.com/ling/jfs/t1/106748/9/9067/61495/5e09fd1eE47733747/a11b3ef8b1a7f791.jpg" alt="image" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="vue-实现">Vue 实现<a href="#vue-实现" class="hash-link" aria-label="Vue 实现的直接链接" title="Vue 实现的直接链接">​</a></h3><p>别看 React 和 Vue 在开发时区别那么大，其实在实现了 BOM/DOM API 之后，它们之间的区别就很小了。</p><p>Vue 和 React 最大的区别就在于运行时的 <code>CreateVuePage</code> 方法，这个方法里进行了一些运行时的处理，比如：生命周期的对齐。</p><p><img loading="lazy" src="https://img30.360buyimg.com/ling/jfs/t1/96356/5/8961/15030/5e0aae8aE452804bd/f4c7df38129d99c5.jpg" alt="image" class="img_ZOZK"></p><p>其他的部分，如通过 BOM/DOM 方法构建、修改 DOM Tree 及渲染原理，都是和 React 一致的。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="flutter-实现">Flutter 实现<a href="#flutter-实现" class="hash-link" aria-label="Flutter 实现的直接链接" title="Flutter 实现的直接链接">​</a></h3><p>提到 Flutter ，就不得不提 <a href="https://flutter.dev/web" target="_blank" rel="noopener noreferrer">Flutter Web</a> ，<strong>Flutter Web 是在标准浏览器 API 之上实现 Flutter 的核心绘图层</strong>，本质上也是最终调用了 BOM/DOM API。因此，理论来说，也是可以进行适配的，但这一块我们并不会投入太多的精力，最终会像快应用一样交给社区来实现和维护。</p><p><img loading="lazy" src="https://img14.360buyimg.com/ling/jfs/t1/101882/34/9079/132553/5e0aaf5dEc32869e0/79597c5309ab55f9.jpg" alt="image" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="更多细节">更多细节<a href="#更多细节" class="hash-link" aria-label="更多细节的直接链接" title="更多细节的直接链接">​</a></h3><p>接下来和大家展开聊一下 Taro Next 更多的细节实现，比如：事件、更新、生命周期。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="事件">事件<a href="#事件" class="hash-link" aria-label="事件的直接链接" title="事件的直接链接">​</a></h4><p>首先的 Taro Next 事件，具体的实现方式如下：</p><ol><li>在 小程序组件的模版化过程中，将所有事件方法全部指定为 调用 ev 函数，如：<code>bindtap</code>、<code>bindchange</code>、<code>bindsubmit</code> 等。</li><li>在 运行时实现 <code>eventHandler</code> 函数，和 eh 方法绑定，收集所有的小程序事件</li><li>通过 <code>document.getElementById()</code> 方法获取触发事件对应的 <code>TaroNode</code></li><li>通过 <code>createEvent()</code> 创建符合规范的 <code>TaroEvent</code></li><li>调用 <code>TaroNode.dispatchEvent</code> 重新触发事件</li></ol><p><img loading="lazy" src="https://img10.360buyimg.com/ling/jfs/t1/103912/33/8987/52864/5e0ab09fEcdcb31c2/d29510c9bc5dd255.jpg" alt="image" class="img_ZOZK"></p><p>可以看到，Taro Next 事件本质上是<strong>基于 Taro DOM 实现了一套自己的事件机制</strong>，这样做的好处之一是，无论小程序是否支持事件的冒泡与捕获，Taro 都能支持。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="更新">更新<a href="#更新" class="hash-link" aria-label="更新的直接链接" title="更新的直接链接">​</a></h4><p>无论是 React 还是 Vue ，最终都会调用 Taro DOM 方法，如：<code>appendChild</code>、<code>insertChild</code> 等。</p><p>这些方法在修改 Taro DOM Tree 的同时，还会调用 <code>enqueueUpdate</code> 方法，这个方法能获取到每一个 DOM 方法最终修改的节点路径和值，如：<code>{root.cn.[0].cn.[4].value:&nbsp;"1"}</code>，并通过 <code>setData</code> 方法更新到视图层。</p><p><img loading="lazy" src="https://img10.360buyimg.com/ling/jfs/t1/86893/14/9016/38824/5e0ac06aE21db32b1/c4bece64d139dd8e.jpg" alt="image" class="img_ZOZK"></p><p>可以看到，这里<strong>更新的粒度是 DOM 级别</strong>，只有最终发生改变的 DOM 才会被更新过去，相对于之前 data 级别的更新会更加精准，性能更好。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="生命周期">生命周期<a href="#生命周期" class="hash-link" aria-label="生命周期的直接链接" title="生命周期的直接链接">​</a></h4><p>相对与其他部分大刀阔斧的升级改造，生命周期可能是变动最小的部分之一。和之前类似，生命周期的实现是在运行时维护的 App 实例 / Page 实例进行了生命周期方法的一一对应。</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">config</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">PageInstance</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">onLoad</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter keyword" style="color:#00009f">this</span><span class="token parameter operator" style="color:#393A34">:</span><span class="token parameter"> </span><span class="token parameter maybe-class-name">MpInstance</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> options</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">//...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">onUnload</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">//...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">onShow</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">safeExecute</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'onShow'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">onHide</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">safeExecute</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'onHide'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">onPullDownRefresh</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">safeExecute</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'onPullDownRefresh'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">//...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="新架构特点">新架构特点<a href="#新架构特点" class="hash-link" aria-label="新架构特点的直接链接" title="新架构特点的直接链接">​</a></h3><p>和之前的架构不同，Taro Next 是 <strong>近乎全运行</strong>。</p><p>新的架构基本解决了之前的遗留问题：</p><ul><li><strong>无 DSL 限制</strong>：无论是你们团队是 React 还是 Vue 技术栈，都能够使用 Taro 开发</li><li><strong>模版动态构建</strong>：和之前模版通过编译生成的不同，Taro Next 的模版是固定的，然后基于组件的 template，动态 “递归” 渲染整棵 Taro DOM 树。</li><li><strong>新特性无缝支持</strong>：由于 Taro Next 本质上是将 React/Vue 运行在小程序上，因此，各种新特性也就无缝支持了。</li><li><strong>社区贡献更简单</strong>：错误栈将和 React/Vue 一致，团队只需要维护核心的 taro-runtime。</li><li><strong>基于 Webpack</strong>：Taro Next 基于 Webpack 实现了多端的工程化，提供了插件功能。</li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="性能优化">性能优化<a href="#性能优化" class="hash-link" aria-label="性能优化的直接链接" title="性能优化的直接链接">​</a></h3><p>前面提到，同等条件下，编译时做的工作越多，也就意味着运行时做的工作越少，性能会更好。Taro Next 的新架构变成 近乎全运行 之后，花了很多精力在性能优化上面。</p><p>再这之前。可以先看一下 Taro Next 的流程和原生小程序的流程对比。</p><p><img loading="lazy" src="https://img20.360buyimg.com/ling/jfs/t1/85061/33/9155/42050/5e0ae1afE263741c9/7a1664f1a6769d7a.jpg" alt="image" class="img_ZOZK"></p><p>可以发现，相比原生小程序，Taro Next 多了红色部分的带来的性能隐患，如：引入 React/Vue 带来的 包的 Size 增加，运行时的损耗、Taro DOM Tree 的构建和更新、DOM data 初始化和更新。</p><p>而我们真正能做的，只有绿色部分，也就是：<strong>Taro DOM Tree 的构建和更新</strong>、<strong>DOM data 初始化和更新</strong>。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="size">Size<a href="#size" class="hash-link" aria-label="Size的直接链接" title="Size的直接链接">​</a></h4><p>首先我们来看包 Size，下面的表格是 TodoMVC 的例子，在原生、Taro Old、Taro Next 等情况下的包大小对比，可以看到，引入 React/Vue 后，包大小在 Gzip 情况下大概增加了 30k 左右。</p><p><img loading="lazy" src="https://img12.360buyimg.com/ling/jfs/t1/100878/15/9005/22516/5e0ae322E690a60fb/9b3a09bf64a51871.jpg" alt="image" class="img_ZOZK"></p><p>不过我们在前面一再强调：和之前模版通过编译生成的不同，Taro Next 的模版是固定的，然后基于组件的 template，动态 “递归” 渲染整棵 Taro DOM 树。也就是说，<strong>Taro Next 的 WXML 大小是有上限的</strong>。</p><p>随着项目的增加，页面越来越多，原生的项目 WXML 体积会不断增加，而 Taro Next 不会。也就是说，当页面的数量超过一个临界点时，Taro Next 的包体积可能会更小。因此，包 Size 的问题不足为虑。</p><p><img loading="lazy" src="https://img30.360buyimg.com/ling/jfs/t1/88749/23/9077/15347/5e0ae41cE71f77070/3f24bf6459abf2e9.jpg" alt="image" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="dom-tree">DOM Tree<a href="#dom-tree" class="hash-link" aria-label="DOM Tree的直接链接" title="DOM Tree的直接链接">​</a></h4><p>在 Taro DOM Tree 的构建和更新阶段，我们实现了一套仅实现了高效的、精简版 DOM/BOM API，而且仅仅实现了必要的。</p><p>Github 上有一个仓库 <a href="https://github.com/jsdom/jsdom" target="_blank" rel="noopener noreferrer">jsdom</a>，基本上是在 Node.js 上实现了一套 Web 标准的 DOM/BOM ，这个仓库的代码在压缩前大概有 2.1M，而 Taro Next 的核心的 DOM/BOM API 代码才 1000 行不到。</p><p>因此，我们最大限度的保证了 Taro DOM Tree 构建和更新阶段的性能。</p><p><img loading="lazy" src="https://img10.360buyimg.com/ling/jfs/t1/93372/24/9137/11878/5e0ae512E79c720f9/ab326eef51d168a5.jpg" alt="image" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="update-date">Update Date<a href="#update-date" class="hash-link" aria-label="Update Date的直接链接" title="Update Date的直接链接">​</a></h4><p>在数据更新阶段，首先前面有提到过，Taro Next 的更新是 DOM 级别的，比 Data 级别的更新更加高效，因为 <strong>Data 粒度更新实际上是有冗余的，并不是所有的 Data 的改变最后都会引起 DOM 的更新</strong>。</p><p>其次，Taro 在更新的时候将 Taro DOM Tree 的 <code>path</code> 进行压缩，这点也极大的提升了性能。</p><p><img loading="lazy" src="https://img20.360buyimg.com/ling/jfs/t1/88800/24/8941/14939/5e0ae644E20bba3bd/053ee53bd79136fb.jpg" alt="image" class="img_ZOZK"></p><p>最终的结果是：在某些业务场景写，<code>add</code>、<code>select</code> 数据，Taro Next 的性能比原生的还要好。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="taro-benchmark">taro-benchmark<a href="#taro-benchmark" class="hash-link" aria-label="taro-benchmark的直接链接" title="taro-benchmark的直接链接">​</a></h4><p>当然，实验的数据总归会有缺陷，最终具体的性能表现，还要靠各种复杂业务场景的检验。大家如果对 Taro Next 的性能感兴趣的，可以自行跑一下 <a href="https://github.com/NervJS/taro-benchmark" target="_blank" rel="noopener noreferrer">taro-benchmark</a> 包，对比一下结果。</p><p>我们也在一直持续的全方位优化 Taro Next 的性能，具体可以关注 Taro Next 的最新的 Commit 。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="总结及展望">总结及展望<a href="#总结及展望" class="hash-link" aria-label="总结及展望的直接链接" title="总结及展望的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="taro-未来规划">Taro 未来规划<a href="#taro-未来规划" class="hash-link" aria-label="Taro 未来规划的直接链接" title="Taro 未来规划的直接链接">​</a></h3><p>Taro Next 将会在不久之后的 3.0 版本正式发布，支持使用 React/Vue 开发跨端小程序，然后在会在后续的迭代中拓展至其他端，并完善对应的生态。</p><p><img loading="lazy" src="https://img20.360buyimg.com/ling/jfs/t1/104194/34/9098/36249/5e0ae850E1926a0d9/4474804a6361b846.jpg" alt="image" class="img_ZOZK"></p><p>Taro 团队还是会将支持的重点放在 React/Vue，Flutter 和 Angular 会像快应用一样，交给社区来适配和维护，快应用就是华为的 <a href="https://github.com/Qiyu8" target="_blank" rel="noopener noreferrer">Qiyu8</a> 和 <a href="https://github.com/Issacpeng" target="_blank" rel="noopener noreferrer">Issacpeng</a> 在帮我们进行适配，非常感谢他们。</p><p>同时，我们还打造了 「Taro 移动端一站式研发平台」，将先前积累的多端开发工作流和工程化的方案进行了统一，并内置了数据监控、组件市场以及可视化搭建，当前正处于内测阶段。</p><p><img loading="lazy" src="https://img13.360buyimg.com/ling/jfs/t1/97914/23/9143/45747/5e0aea39Ed36b0144/2b82d4f2fe7d8553.jpg" alt="image" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="一点思考">一点思考<a href="#一点思考" class="hash-link" aria-label="一点思考的直接链接" title="一点思考的直接链接">​</a></h3><ul><li><strong>业务孵化技术，技术服务业务</strong>：这也是整个 Taro 项目从创建到迭代至今最重要的、感受最深的一点。</li><li><strong>自上而下 OR 自下而上</strong>：从开发者的角度自上而下看，React/Vue 的代码书写方式差异挺大的；然而站在浏览器的角度自下而上的看，它们的差别其实没那么大，都是调用了 BOM/DOM 那几个常用的 API。如果我们再往底层一点，站在渲染层的角度，不同平台之间的差异会不会也没那么大？比如：Flutter。</li><li><strong>Learn Once Write AnyWhere &amp; Write Once Run AnyWhere</strong>：很多开发者更喜欢 React 提出的<code>Learn Once Write AnyWhere</code>，而我们 Taro 的口号是 <code>Write Once Run AnyWhere</code>，这一点也导致我们经常被人喷，这里说一点我自己的想法：<code>Learn Once Write AnyWhere</code>其实本质上对开发者更友好，比如开发者只需要学习 React 技术栈，就可以开发 Web/移动端 应用，但是对项目就没那么友好了，每个项目都得维护一份代码；而 <code>Write Once Run AnyWhere</code> 是对开发者没那么友好（适配的端越多，适配的成本必然也会水涨船高，对开发者要求也很变高），但是根据我们的实践，对项目会更友好，「一套代码，多端适配」。当然，这里适配的粒度，并不一定是项目级别的，其实在我们的具体实践中，有相当一部分是：<strong>业务级甚至是页面级的</strong>。</li></ul><p><img loading="lazy" src="https://img20.360buyimg.com/ling/jfs/t1/106934/5/9153/29316/5e0af1fcE224a87c6/50b9bb5b5fffd7af.jpg" alt="image" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="写在最后">写在最后<a href="#写在最后" class="hash-link" aria-label="写在最后的直接链接" title="写在最后的直接链接">​</a></h2><p>正所谓「单丝不成线，独木不成林」，Taro 发展至今早已不在属于单一团队的项目了，而是整个 Taro 开发社区共同的项目。</p><p>最后，还是借此机会感谢一些社区所有帮助过 Taro 的成长的人，特别是 Taro 的贡献者们，非常感谢！</p><p><img loading="lazy" src="https://img13.360buyimg.com/ling/jfs/t1/94820/21/9064/358921/5e0aeb75E9b70a0a3/3245d0e963686d41.jpg" alt="image" class="img_ZOZK"></p><p>同时也感谢受邀成为 <a href="https://github.com/NervJS/taro-ui" target="_blank" rel="noopener noreferrer">TaroUI</a> 核心维护人员的 <a href="https://github.com/Garfield550" target="_blank" rel="noopener noreferrer">Garfield550</a> (小姐姐)、<a href="https://github.com/yinLiangDream" target="_blank" rel="noopener noreferrer">梁音</a>、<a href="https://github.com/lsqy" target="_blank" rel="noopener noreferrer">ShaoQian Liu</a>，他们将支撑起 TaroUI 的后续迭代与维护。</p><p>当然还有在社区中乐于助人、积极贡献的 <a href="https://github.com/zacksleo" target="_blank" rel="noopener noreferrer">zacksleo</a> 、<a href="https://github.com/fjc0k" target="_blank" rel="noopener noreferrer">Jay Fong</a>、<a href="https://github.com/loveonelong" target="_blank" rel="noopener noreferrer">loveonelong</a>、<a href="https://github.com/lolipop99" target="_blank" rel="noopener noreferrer">lolipop99</a>、<a href="https://github.com/bozaigao" target="_blank" rel="noopener noreferrer">波仔糕</a>、<a href="https://github.com/fwh1990" target="_blank" rel="noopener noreferrer">原罪</a>、<a href="https://github.com/lentoo/" target="_blank" rel="noopener noreferrer">lentoo</a> 、<a href="https://taro-club.jd.com/user/%E7%99%BD%E9%A2%86%E5%A4%8F%E5%85%AC%E5%AD%90" target="_blank" rel="noopener noreferrer">白领夏公子</a> 、<a href="https://github.com/YuanQuan" target="_blank" rel="noopener noreferrer">YuanQuan</a>、 <a href="https://github.com/tourze" target="_blank" rel="noopener noreferrer">tourze</a>、 <a href="https://github.com/lingxiao-Zhu" target="_blank" rel="noopener noreferrer">lingxiaoZhu</a> 等等。</p><p>此外，还要感谢一直默默为 Taro 发展提供宝贵建议的研发团队：腾讯云、数字广东、腾讯 CDC、网易严选、华为开源团队、招联消费金融等等。</p><p>长风破浪会有时，直挂云帆济沧海。</p><p>欢迎关注凹凸实验室博客：<a href="https://aotu.io/" target="_blank" rel="noopener noreferrer">aotu.io</a></p><p>或者关注凹凸实验室公众号（AOTULabs），不定时推送文章：</p><p><img loading="lazy" src="https://img10.360buyimg.com/ling/jfs/t1/108283/1/3007/99032/5e0d8287E22d9f58b/f081485ab0043781.jpg" alt="image" class="img_ZOZK"></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[京喜首页（微信购物入口）跨端开发与优化实践]]></title>
            <link>https://docs.taro.zone/blog/2019-12-03-jingxi-index</link>
            <guid>https://docs.taro.zone/blog/2019-12-03-jingxi-index</guid>
            <pubDate>Tue, 03 Dec 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[image]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://img13.360buyimg.com/ling/jfs/t1/103274/2/4226/201268/5de6455aEf651e021/8013f336e45fc79c.png" alt="image" class="img_ZOZK"></p><p>作者从技术选型、开发实录再到性能优化三个维度对微信购物入口「京喜」首页改版做了简单总结。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="背景介绍">背景介绍<a href="#背景介绍" class="hash-link" aria-label="背景介绍的直接链接" title="背景介绍的直接链接">​</a></h2><p>随着今年的双十一落下帷幕，京喜（原京东拼购）也迎来了首捷。双十一前夕微信购物一级入口切换为京喜小程序，项目顺利通过近亿级的流量考验，在此与大家分享一点自己参与的工作。</p><p>在接手项目前，京喜业务已在线上稳定运行较长时间。但经过一段时间迭代维护后，发现首页存在以下问题：</p><ol><li>H5 版本首页针对不同渠道开发了多套页面，对开发者维护和内容运营来说存在较大挑战，需投入大量人力成本；</li><li>项目技术栈不统一，分别有传统 H5 开发、原生小程序开发、wqVue 框架开发，严重影响项目复杂度，迭代过程苦不堪言；</li><li>H5、小程序以及 RN 三端存在各自构建和发布流程，涉及较多工具及复杂系统流程，影响业务交付效率。</li></ol><p>综上所述，京喜迎来一次改版契机。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="改版目标">改版目标<a href="#改版目标" class="hash-link" aria-label="改版目标的直接链接" title="改版目标的直接链接">​</a></h2><p>从前端角度来看，本次改版要实现以下目标：</p><ul><li>升级并统一项目技术栈，解决项目技术栈混乱的现状；</li><li>使用一套代码，适配微信入口、手 Q 入口、微信小程序、京东 APP、京喜 APP、M 站六大业务场景，减少多套页面的维护成本，提升交付效率；</li><li>通过让 RN 技术在业务上的落地，完善团队在 App 端的技术储备；</li><li>优化页面性能及体验，为下沉市场用户提供优质的产品体验；</li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="技术选型">技术选型<a href="#技术选型" class="hash-link" aria-label="技术选型的直接链接" title="技术选型的直接链接">​</a></h2><p>京喜业务拥有非常丰富的产品形态，涵盖了 H5、微信小程序以及独立 APP 三种不同的端，对支持多端的开发框架有着天然的需求。</p><p><img loading="lazy" src="https://img14.360buyimg.com/ling/jfs/t1/99778/13/1485/65058/5dc00468E7d60a4e0/5bb82c1ec5ce3dd7.jpg" alt="京喜丰富的产品形态" class="img_ZOZK"></p><p>在技术选型上，我们选择团队自研的 <a href="https://github.com/NervJS/taro/" target="_blank" rel="noopener noreferrer" title="Taro">Taro</a> 多端统一开发解决方案。</p><blockquote><p>Taro 是一套遵循 React 语法规范的多端开发解决方案。</p><p>现如今市面上端的形态多种多样，Web、React-Native、微信小程序等各种端大行其道，当业务要求同时在不同的端都要求有所表现的时候，针对不同的端去编写多套代码的成本显然非常高，这时候只编写一套代码就能够适配到多端的能力就显得极为需要。</p><p>使用 Taro，我们可以只书写一套代码，再通过 Taro 的编译工具，将源代码分别编译出可以在不同端（微信/百度/支付宝/字节跳动/QQ 小程序、快应用、H5、React-Native 等）运行的代码。</p></blockquote><p>选它有两个原因，一来是 Taro 已经成熟，内部和外部都有大量实践，内部有京东 7FRESH、京东到家等，外部有淘票票、猫眼试用等多个案例，可以放心投入到业务开发；二来团队成员都拥有使用 Taro 来开发内部组件库的经验，对业务快速完成有保障。</p><p><img loading="lazy" src="https://img13.360buyimg.com/ling/jfs/t1/98736/39/2685/263027/5dd3b44dE4cc40299/6e5a359d73f0ef7f.png" alt="组件库" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="开发实录">开发实录<a href="#开发实录" class="hash-link" aria-label="开发实录的直接链接" title="开发实录的直接链接">​</a></h2><p>由于首页改版的开发排期并不充裕，因此充分地复用已有基础能力（比如像请求、上报、跳转等必不可少的公共类库），能大量减少我们重复的工作量。话虽如此，但在三端统一开发过程中，我们仍遇到不少问题同时也带来解决方案，以下我们一一阐述。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="h5-篇">H5 篇<a href="#h5-篇" class="hash-link" aria-label="H5 篇的直接链接" title="H5 篇的直接链接">​</a></h3><p>我们所有的页面都依赖现有业务的全局公共头尾及搜索栏等组件，这就不可避免的需要将 Taro 开发流程融入到现有开发和发布流程中去。同时公共组件都是通过 <a href="https://en.wikipedia.org/wiki/Server_Side_Includes" target="_blank" rel="noopener noreferrer" title="SSI">SSI</a> 的方式引入和维护的，为了能在运行 <code>npm run dev:h5</code> 时预览到完整的页面效果，需要对 <code>index.html</code> 模版中的 SSI 语法进行解析，<code>index.html</code> 模版文件代码结构大致如下：</p><div class="language-html codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-html codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token doctype punctuation" style="color:#393A34;font-style:italic">&lt;!</span><span class="token doctype doctype-tag" style="color:#999988;font-style:italic">DOCTYPE</span><span class="token doctype" style="color:#999988;font-style:italic"> </span><span class="token doctype name" style="color:#999988;font-style:italic">html</span><span class="token doctype punctuation" style="color:#393A34;font-style:italic">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">html</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">lang</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">zh-CN</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">head</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">meta</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">charset</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">UTF-8</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">meta</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">name</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">viewport</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">      </span><span class="token tag attr-name" style="color:#00a4db">content</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">    </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">title</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">京喜</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">title</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!--#include virtual="/sinclude/common/head_inc.shtml"--&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">head</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">body</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">m_common_header</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag special-attr attr-name" style="color:#00a4db">style</span><span class="token tag special-attr attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag special-attr attr-value value css language-css property" style="color:#36acaa">display</span><span class="token tag special-attr attr-value value css language-css punctuation" style="color:#393A34">:</span><span class="token tag special-attr attr-value value css language-css" style="color:#e3116c">none</span><span class="token tag special-attr attr-value value css language-css punctuation" style="color:#393A34">;</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!--S 搜索框--&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">search_block</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">search_block</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">smartboxBlock</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag special-attr attr-name" style="color:#00a4db">style</span><span class="token tag special-attr attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag special-attr attr-value value css language-css property" style="color:#36acaa">display</span><span class="token tag special-attr attr-value value css language-css punctuation" style="color:#393A34">:</span><span class="token tag special-attr attr-value value css language-css" style="color:#e3116c">none</span><span class="token tag special-attr attr-value value css language-css punctuation" style="color:#393A34">;</span><span class="token tag special-attr attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!--E 搜索框--&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">id</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">app</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">wx_wrap</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">&lt;!--#include virtual="/sinclude/common/foot.shtml"--&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">body</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">html</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>可以看到模版中存在很多类似 <code>&lt;!--#include virtual="..." --&gt;</code> 格式的代码，这些就是通过 SSI 方式引入的 H5 公共组件，它的 <code>virtual</code> 属性指向的文件不存在于本地而是存在于服务器上的，所以我们遇到的第一个问题就是在本地解析这些文件，确保能预览到完整的页面效果，不然开发调试起来就非常的低效。好在 Taro 有暴露出 webpack 的配置，我们可以通过引入自定义加载器（这里就叫 <code>ssi-loader</code>）来解析这些代码的路径，然后请求服务器上的文件内容并进行替换即可，要实现这个功能只需在项目的 <code>config/dev.js</code> 中加入如下代码即可：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">h5</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">webpackChain</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">chain</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> webpack</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      chain</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">merge</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">module</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token literal-property property" style="color:#36acaa">rule</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token literal-property property" style="color:#36acaa">ssiLoader</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token literal-property property" style="color:#36acaa">test</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex" style="color:#36acaa">\.html</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token literal-property property" style="color:#36acaa">use</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  </span><span class="token literal-property property" style="color:#36acaa">loader</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'html-loader'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  </span><span class="token literal-property property" style="color:#36acaa">loader</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ssi-loader'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  </span><span class="token literal-property property" style="color:#36acaa">options</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token literal-property property" style="color:#36acaa">locations</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                      </span><span class="token literal-property property" style="color:#36acaa">include</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'https://wqs.jd.com'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>这样就解决了本地开发调试难点，然后开开心心的进行页面开发。</p><p>当页面开发完成之后，接下来遇到的问题就是如何将前端资源部署到测试和生产环境。由于现有开发和发布流程都是基于内部已有的平台，我们临时定制一套也不太现实，所以需要将它融入到 Taro 的流程中去，这里我们引入了 <code>gulp</code> 来整合各种构建和发布等操作，只要构建出符合发布平台规范的目录即可利用它的静态资源构建、版本控制及服务器发布等能力，这样我们就打通了整个开发和发布流程。</p><p>这套拼凑起来的流程还存在不少的问题，对于新接手的同学有一点小繁琐，有着不少改善的空间，这也是接下来的重点工作方向。另外 Taro 的 H5 端之前是基于 SPA 模式，对于有着多页开发需求的项目来说不太友好，当时反馈给 Taro 团队负责 H5 的同学，很快得到了响应，目前 Taro 已支持 H5 多页开发模式，支持非常迅速。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="小程序篇">小程序篇<a href="#小程序篇" class="hash-link" aria-label="小程序篇的直接链接" title="小程序篇的直接链接">​</a></h3><p>由于开发完 H5 版之后，对应的业务逻辑就已经处理完了，接下来只需要处理小程序下的一些特殊逻辑（比如分享、前端测速上报等）即可，差异比较大的就是开发和发布流程。</p><p>这里讲一下如何在一个原生小程序项目中使用 Taro 进行开发，因为我们的 Taro 项目跟已有的原生小程序项目是独立的两个项目，所以需要将 Taro 项目的小程序代码编译到已有的原生小程序项目目录下，第一步要做的就是调整 Taro 配置 <code>config/index.js</code>，指定编译输出目录以及禁用 app 文件输出防止覆盖已有文件。</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 自定义输出根目录</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">outputRoot</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">argv</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'weapp'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'../.temp'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'dist'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 不输出 app.js 和 app.json 文件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">weapp</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">appOutput</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>由于京喜以前是主购小程序的一个栏目，后面独立成了独立的小程序，但是核心购物流程还是复用的主购小程序，所以这让情况变得更加复杂。这里还是通过 <code>gulp</code> 来进行繁琐的目录文件处理，比如我们的小程序页面和组件都需要继承主购小程序的 <code>JDPage</code> 和 <code>JDComponent</code> 基类，所以在进行文件复制之前需要进行代码替换，代码如下：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// WEAPP</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> basePath </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">../.temp</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> destPaths </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">basePath</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">/pages/index/</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">basePath</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">/pages/components/</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> destFiles </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> destPaths</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">item</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">item</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">**/*.js</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">/*</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> * 基类替换</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">replaceBaseComponent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">files</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> gulp</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">src</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">files </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> destFiles</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">base</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> basePath </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">pipe</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">replace</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex" style="color:#36acaa">\b(Page|Component)(\(require\(['"](.*? "'"")\/npm\/)(.*)(createComponent.*)</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">match</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> p1</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> p2</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> p3</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> p4</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> p5</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> type </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p5 </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">''</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">indexOf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'true'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p5 </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">''</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">indexOf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'!0'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Page'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Component'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">type </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Page'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> p5 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> p5</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">replace</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'))'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'), true)'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 新：page.js基类要多传一个参数</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> reservedParts </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> p2 </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> p4 </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> p5</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic">// const type = p1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic">// const reservedParts = p2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> rootPath </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> p3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> clsName </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> type </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Page'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'JDPage'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'JDComponent'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> baseFile </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> type </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Page'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'page.taro.js'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'component.js'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">🌝 Replace with \`</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">clsName</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">\` successfully: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation keyword" style="color:#00009f">this</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">file</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">path</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation method function property-access" style="color:#d73a49">replace</span><span class="token template-string interpolation punctuation" style="color:#393A34">(</span><span class="token template-string interpolation regex regex-delimiter" style="color:#36acaa">/</span><span class="token template-string interpolation regex regex-source language-regex" style="color:#36acaa">.*?wxapp\/</span><span class="token template-string interpolation regex regex-delimiter" style="color:#36acaa">/</span><span class="token template-string interpolation punctuation" style="color:#393A34">,</span><span class="token template-string interpolation"> </span><span class="token template-string interpolation string" style="color:#e3116c">'wxapp/'</span><span class="token template-string interpolation punctuation" style="color:#393A34">)</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">new (require("</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">rootPath</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">/bases/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">baseFile</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">").</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">clsName</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">)</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">reservedParts</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">pipe</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">gulp</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">dest</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">basePath</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 基类替换</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">gulp</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'replace-base-component'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">replaceBaseComponent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>还有很多类似这样的骚操作，虽然比较麻烦，但是只需要处理一次，后续也很少改动。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="rn-篇">RN 篇<a href="#rn-篇" class="hash-link" aria-label="RN 篇的直接链接" title="RN 篇的直接链接">​</a></h3><p>对于 RN 开发，也是第一次将它落地到实际的业务项目中，所以大部分时候都是伴随着各种未知的坑不断前行，所以这里也友情提示一下，对于从未使用过的技术，还是需要一些耐心的，遇到问题勤查勤问。</p><p>由于京喜 APP 是复用京东技术中台的基础框架和 JDReact 引擎，所以整个的开发和部署都是遵循 JDReact 已有的流程，画了一张大致的流程图如下：</p><p><img loading="lazy" src="https://img10.360buyimg.com/ling/jfs/t1/84738/19/1551/59434/5dc02754E930f59f2/ebe7b01308771a91.png" alt="京喜开发发布流程" class="img_ZOZK"></p><blockquote><p>JDReact 平台是在 Facebook ReactNative 开源框架基础上，进行了深度二次开发和功能扩展。不仅打通了 Android/iOS/Web 三端平台，而且对京东移动端基础业务能力进行了 SDK 级别的封装，提供了统一、易于开发的 API。业务开发者可以通过 JDReact SDK 平台进行快速京东业务开发，并且不依赖发版就能无缝集成到客户端(android/iOS)或者转换成 Web 页面进行线上部署，真正实现了一次开发，快速部署三端。</p></blockquote><p>由于京喜 APP 的 JDReact 模块都是独立的 git 仓库，所以需要调整我们 Taro 项目配置 <code>config/index.js</code> 的编译输出路径如下：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token literal-property property" style="color:#36acaa">rn</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">outPath</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'../jdreact-jsbundle-jdreactpingouindex'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>这样，当我们运行 <code>yarn run dev:rn</code> 进行本地开发时，文件自动编译到了 JDReact 项目，接下来我们就可以用模拟器或者真机来进行预览调试了。当我们在进行本地开发调试的时候，最高效的方式还是推荐用 Taro 官方提供的 <a href="https://github.com/NervJS/taro-native-shell" target="_blank" rel="noopener noreferrer" title="`taro-native-shell`"><code>taro-native-shell</code></a> 原生 React Native 壳子来启动我们的项目，详细的配置参照该项目的 README 进行配置即可。</p><p>由于 React Native 官方提供的 <a href="https://facebook.github.io/react-native/docs/debugging.html#chrome-developer-tools" target="_blank" rel="noopener noreferrer" title="Remote Debugger">Remote Debugger</a> 功能非常弱，推荐使用 <a href="https://github.com/jhen0409/react-native-debugger" target="_blank" rel="noopener noreferrer" title="React Native Debugger">React Native Debugger</a> 来进行本地 RN 调试，提供了更为丰富的功能，基本接近 H5 和小程序的调试体验。</p><p><img loading="lazy" src="https://img20.360buyimg.com/ling/jfs/t1/93257/12/1534/249244/5dc0282eE862961c6/bf7b8367ac970ecf.png" alt="React Native Debugger 界面" class="img_ZOZK"></p><p>这样我们就拥有了一个正常的开发调试环境，接下来就可以进行高效的开发了，由于我们前面在 H5 和小程序版本阶段已经完成了绝大部分的业务逻辑开发，所以针对 RN 版本的主要工作集中在 iOS 和安卓不同机型的样式和交互适配上。</p><p>在样式适配这块，不得不提下 Taro 针对我们常见的场景提供了一些最佳实践，可以作为布局参考：</p><ul><li>固定尺寸（按钮、文字大小、间距）：写 PX / Px / pX</li><li>保持宽高比（比如 banner 图片）：<code>Image</code> 组件处理</li><li>间距固定，内容自适应（比如产品卡片宽度）：使用 <code>flex</code> 布局</li><li>按屏幕等比缩放：使用 px 单位，编译时处理（<code>scalePx2dp</code> 动态计算）</li></ul><h4 class="anchor anchorWithStickyNavbar_mCi1" id="taro-rn-最佳实践集锦">Taro RN 最佳实践集锦<a href="#taro-rn-最佳实践集锦" class="hash-link" aria-label="Taro RN 最佳实践集锦的直接链接" title="Taro RN 最佳实践集锦的直接链接">​</a></h4><p>在实际开发过程中也遇到不少兼容性问题，这里整理出来以供大家参考：</p><ul><li><p>文本要用 <code>&lt;Text&gt;</code> 标签包起来，因为 RN 没有 <code>textNode</code> 的概念；</p></li><li><p>使用 Swiper 时在外面包一个 View，否则设置 <code>margin</code> 后会导致安卓下高度异常；</p></li><li><p><code>Cannot read property 'x' of undefined</code>，Swiper 底层使用的 react-native-swiper 导致的问题，Disable Remote JS Debug 就不会出现。</p></li><li><p>图片默认尺寸不对，RN 不会自动帮助设置图片尺寸，而是交给开发者自己处理，故意这样设计的；</p></li><li><p>Image 组件上不可以设置 onClick</p></li><li><p>实现基线对齐：<code>vertical-align: baseline</code>，用 <code>&lt;Text&gt;</code> 把需要基线对齐的组件包住即可。</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">style</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript literal-property property" style="color:#36acaa">fontSize</span><span class="token tag script language-javascript operator" style="color:#393A34">:</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript number" style="color:#36acaa">20</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">abc</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">style</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript literal-property property" style="color:#36acaa">fontSize</span><span class="token tag script language-javascript operator" style="color:#393A34">:</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript number" style="color:#36acaa">40</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">123</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p>尽量避免使用 <code>line-height</code> ，在安卓和 iOS 下表现不一致，而且即使设置为与 <code>fontSize</code> 相同也会导致裁剪；</p></li><li><p>android 调试生产环境的 bundle，摇手机，选 Dev Setting，取消勾选第一项 Dev 即可；</p></li><li><p>iOS 调试生产环境的 bundle，<code>AppDelegate.m</code> 中增加一行语句关闭 dev 即可：</p><div class="language-objc codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-objc codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">RCTBundleURLProvider sharedSettings</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> setEnableDev</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">false</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 找到这行，并在它的上面增加上面这行</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  jsCodeLocation </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">RCTBundleURLProvider sharedSettings</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> jsBundleURLForBundleRoot</span><span class="token punctuation" style="color:#393A34">:</span><span class="token string" style="color:#e3116c">@"index"</span><span class="token plain"> fallbackResource</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">nil</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p><code>&lt;Text&gt;</code> 与 <code>&lt;View&gt;</code> 支持的 style 属性不相同。</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">&gt; [Text Style Props](https://facebook.github.io/react-native/docs/text-style-props "Text Style Props") &amp; [View Style Props](https://facebook.github.io/</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>react-native/docs/view-style-props)</p></li><li><p>render 方法中不要返回空字符串</p><p>下面的代码在 android 下会报错（empty_string 内容为空字符串）</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">empty_string </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>因为 <code>empty_string &amp;&amp; &lt;Text&gt;&lt;/Text&gt;</code> 的返回值是空字符串，RN 尝试把字符串添加到 View 的 children 时在安卓环境下会报错：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token known-class-name class-name">Error</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">Cannot</span><span class="token plain"> add a child that doesn't have a </span><span class="token maybe-class-name">YogaNode</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></li><li><p><code>border-radius</code> 导致背景色异常，单独给某个角设置圆角时，没有设置圆角的边会出现一块与背景色颜色相同，但半透明的色块。</p><ol><li>添加外层容器设置圆角与超出隐藏</li><li>全部角都设置圆角然后使用 <code>transform:tanslate()</code> 藏起不想要的圆角</li></ol></li><li><p>透明 View 无法点击的问题，给设置了 onClick 的元素添加透明背景色即可：</p></li></ul><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">style</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">backgroundColor</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"transparent"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>不可以用 scss 写，只有写在 JSX 上的才有效，Taro 编译时可能把透明背景色忽略了。</p><ul><li><p>一像素缝隙问题</p><p>可能是 RN 布局引擎的问题，或单位转换以及浏览器渲染中的精度损失问题。可以调整页面结构来绕过。
或者简单粗暴一点，设置负 margin 值盖住缝隙。</p></li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="跨平台开发">跨平台开发<a href="#跨平台开发" class="hash-link" aria-label="跨平台开发的直接链接" title="跨平台开发的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="js-文件">JS 文件<a href="#js-文件" class="hash-link" aria-label="JS 文件的直接链接" title="JS 文件的直接链接">​</a></h4><h5 class="anchor anchorWithStickyNavbar_mCi1" id="1文件拆分的方式">1、文件拆分的方式<a href="#1文件拆分的方式" class="hash-link" aria-label="1、文件拆分的方式的直接链接" title="1、文件拆分的方式的直接链接">​</a></h5><p>要"完美"的编译出三端代码，首先要解决的是公共类库的适配问题，好在兄弟业务团队已经沉淀有完成度较高的三端公共类库，利用 Taro 提供的跨平台开发能力，抹平三端方法名和参数不统一的情况，即可很好的解决公共类库的适配问题，如下所示：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token builtin class-name">.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── goto.h5.js</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── goto.rn.js</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── goto.weapp.js</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── request.h5.js</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── request.rn.js</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── request.weapp.js</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">└── </span><span class="token punctuation" style="color:#393A34">..</span><span class="token plain">.</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>以 <code>request</code> 公共组件为例，三端代码如下：</p><p>request.h5.js</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">request</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@legos/request'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token exports punctuation" style="color:#393A34">{</span><span class="token exports"> request </span><span class="token exports punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>request.rn.js</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">request</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@wqvue/jdreact-request'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token exports punctuation" style="color:#393A34">{</span><span class="token exports"> request </span><span class="token exports punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>request.weapp.js（由于小程序的公共组件没有发布至 npm，这里引用的本地项目源文件）</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> request </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'../../../common/request/request.js'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token exports punctuation" style="color:#393A34">{</span><span class="token exports"> request </span><span class="token exports punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>如遇到需要适配的方法参数不一致或者增加额外处理的情况，可进行再包装确保最终输出的接口一致，如下：</p><p>goto.rn.js</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">jump</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@wqvue/jdreact-jump'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">goto</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">url</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> params </span><span class="token parameter operator" style="color:#393A34">=</span><span class="token parameter"> </span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> options </span><span class="token parameter operator" style="color:#393A34">=</span><span class="token parameter"> </span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">jump</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">url</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> options</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">des</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'m'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> options</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">source</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'JDPingou'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> params</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> goto</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>文件引入的时候我们正常使用就好，Taro 在编译的时候为我们编译对应的平台的文件</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">goto</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./goto.js'</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h5 class="anchor anchorWithStickyNavbar_mCi1" id="2条件编译的方式">2、条件编译的方式<a href="#2条件编译的方式" class="hash-link" aria-label="2、条件编译的方式的直接链接" title="2、条件编译的方式的直接链接">​</a></h5><p>解决了公共类库适配之后，接下来就可以专注于业务代码开发了，同样业务代码在三端也可能存差异的情况，可以用 Taro 提供的环境变量来达到目的，示例代码如下：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">TARO_ENV</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'h5'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">speedReport</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// [测速上报] 首屏渲染完成</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">else</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">TARO_ENV</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'weapp'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  speed</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">mark</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">6</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">report</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// [测速上报] 首屏渲染完成</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">else</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">TARO_ENV</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'rn'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  speed</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">mark</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">7</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">report</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// [测速上报] 首屏渲染完成</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="css-文件">CSS 文件<a href="#css-文件" class="hash-link" aria-label="CSS 文件的直接链接" title="CSS 文件的直接链接">​</a></h4><p>以上是 js 的代码处理方式，对于 css 文件及代码，同样也有类似的处理。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="1文件拆分的方式-1">1、文件拆分的方式<a href="#1文件拆分的方式-1" class="hash-link" aria-label="1、文件拆分的方式的直接链接" title="1、文件拆分的方式的直接链接">​</a></h5><p>比如 RN 相对于 H5 和小程序的样式就存在比较大的差异，RN 支持的样式是 CSS 的子集，所以很多看起来很常见的样式是不支持的，可以通过以下方式进行差异化处理：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">├── index.base.scss</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── index.rn.scss</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── index.scss</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>这里以 <code>index.base.scss</code> 作为三端都能兼容的公共样式（名字可以任取，不一定为 xxx.base.scss），<code>index.rn.scss</code> 则为 RN 端独特的样式，<code>index.scss</code> 则为 H5 和小程序独特的样式，因为 H5 和小程序样式基本上没有什么差异，这里合为一个文件处理。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="2条件编译的方式-1">2、条件编译的方式<a href="#2条件编译的方式-1" class="hash-link" aria-label="2、条件编译的方式的直接链接" title="2、条件编译的方式的直接链接">​</a></h5><p>Taro 也支持样式文件内的条件编译，语法如下：</p><div class="language-scss codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-scss codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">/* #ifdef %PLATFORM% */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 指定平台保留</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">/* #endif */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">/* #ifndef %PLATFORM% */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 指定平台剔除</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">/* #endif */</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><code>%PLATFORM%</code> 的取值请参考 <a title="Taro 内置环境变量" href="/docs/envs">Taro 内置环境变量</a></p><p>以下为示例代码：</p><div class="language-scss codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-scss codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token selector" style="color:#00009f">.selector </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">color</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token hexcode color">#fff</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">/* #ifndef RN */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">box-shadow</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token unit">px</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token unit">px</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token unit">px</span><span class="token plain"> </span><span class="token color function" style="color:#d73a49">rgba</span><span class="token color punctuation" style="color:#393A34">(</span><span class="token color number" style="color:#36acaa">0</span><span class="token color punctuation" style="color:#393A34">,</span><span class="token color"> </span><span class="token color number" style="color:#36acaa">0</span><span class="token color punctuation" style="color:#393A34">,</span><span class="token color"> </span><span class="token color number" style="color:#36acaa">0</span><span class="token color punctuation" style="color:#393A34">,</span><span class="token color"> </span><span class="token color number" style="color:#36acaa">0.1</span><span class="token color punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">/* #endif */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>编译为 H5 和小程序的样式为：</p><div class="language-scss codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-scss codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token selector" style="color:#00009f">.selector </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">color</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token hexcode color">#fff</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">box-shadow</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token unit">px</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token unit">px</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token unit">px</span><span class="token plain"> </span><span class="token color function" style="color:#d73a49">rgba</span><span class="token color punctuation" style="color:#393A34">(</span><span class="token color number" style="color:#36acaa">0</span><span class="token color punctuation" style="color:#393A34">,</span><span class="token color"> </span><span class="token color number" style="color:#36acaa">0</span><span class="token color punctuation" style="color:#393A34">,</span><span class="token color"> </span><span class="token color number" style="color:#36acaa">0</span><span class="token color punctuation" style="color:#393A34">,</span><span class="token color"> </span><span class="token color number" style="color:#36acaa">0.1</span><span class="token color punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>RN 的样式为：</p><div class="language-scss codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-scss codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token selector" style="color:#00009f">.selector </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">color</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token hexcode color">#fff</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>两种方式选其一即可，这样就能开开心心的编写业务代码了。</p><p>有些许遗憾的是产品经理对这次新版首页有着明确的上线优先级：先 H5 版，再微信小程序版，最后是 RN 版，这就为后续 RN 版本跟 H5 和 小程序版本分道扬镳埋下了伏笔，条件允许的话建议优先以 RN 版本为基准进行开发，以免开发完成 H5 和小程序之后发现对结构和样式进行大的调整，因为 RN 对样式确实会弱一些。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="性能优化">性能优化<a href="#性能优化" class="hash-link" aria-label="性能优化的直接链接" title="性能优化的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="图片优化">图片优化<a href="#图片优化" class="hash-link" aria-label="图片优化的直接链接" title="图片优化的直接链接">​</a></h4><p>电商性质的网站，会存在大量的素材或商品图片， 往往这些会对页面造成较大的性能影响。得益于京东图床服务，提供强大的图片定制功能，让我们在图片优化方面省去大量工作。以引入商品图片 <code>"https://img10.360buyimg.com/mobilecms/s355x355_jfs/t1/55430/24/116/143859/5cd27c99E71cc323f/0e8da8810fb49796.jpg!q70.dpg.webp"</code> 为样本，我们对图片应用做了部分优化：</p><ul><li>根据容器大小适当裁剪图片尺寸：s355x355_jfs</li><li>根据网络环境设置图片品质参数：0e8da8810fb49796.jpg!q70</li><li>根据浏览器环境合理选择图片类型：0e8da8810fb49796.jpg!q70.dpg.webp</li></ul><p>为 Image 标签设置 lazyload 属性，这样可以在 H5 和小程序下获得懒加载功能。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="接口聚合直出">接口聚合直出<a href="#接口聚合直出" class="hash-link" aria-label="接口聚合直出的直接链接" title="接口聚合直出的直接链接">​</a></h4><p>起初京喜首页的首屏数据涉及的后端接口多达 20 余个，导致整体数据返回时间较长；为了解决此项痛点，我们联合后端团队，独立开发首屏专用的<strong>聚合直出接口</strong>。一方面，将众多接口请求合并成一个，减少接口联动请求带来的性能损耗；另一方面，将复杂的业务逻辑挪到后端处理，前端只负责视图渲染和交互即可，减少前端代码复杂度；通过此项优化，页面性能和体验得到极大改善。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="缓存优先策略">缓存优先策略<a href="#缓存优先策略" class="hash-link" aria-label="缓存优先策略的直接链接" title="缓存优先策略的直接链接">​</a></h4><p>由于京喜业务主要围绕下沉市场，其用户群体的网络环境会更加复杂，要保障页面的性能，减少网络延时是一项重要措施。</p><p>为了提升用户二次访问的加载性能，我们决定采用<strong>缓存优先策略</strong>。即用户每次访问页面时所请求的主接口数据写入本地缓存，同时用户每次访问都优先加载缓存数据，形成一套规范的数据读取机制。通过优先读取本地缓存数据，可让页面内容在极短时间内完成渲染；另外，本地缓存数据亦可作为页面兜底数据，在用户网络超时或故障时使用，可避免页面空窗的情景出现。</p><p><img loading="lazy" src="https://img20.360buyimg.com/ling/jfs/t1/50956/35/15492/42652/5dc2eaecE30ebcffd/c7e8c52679a3f2fe.jpg" alt="缓存优先策略" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="高性能瀑布流长列表">高性能瀑布流长列表<a href="#高性能瀑布流长列表" class="hash-link" aria-label="高性能瀑布流长列表的直接链接" title="高性能瀑布流长列表的直接链接">​</a></h4><p>首页紧接着首屏区域的是一个支持下滑加载的瀑布流长列表，每次滑到底部都会异步拉取 20 条数据，总计会拉取将近 500 条数据，这在 iOS 下交互体验还比较正常。但是在配置较低的安卓机型下，当滑动到 2 到 3 屏之后就开始出现严重卡顿，甚至会闪退。</p><p>针对这种场景也尝试过用 FlatList 和 SectionList 组件来优化，但是它们都要求规则等高的列表条目，于是不得不自己来实现不规则的瀑布流无限滚动加载。其核心思路是通过判断列表的条目是否在视窗内来决定图片是否渲染，要优化得更彻底些得话，甚至可以移除条目内所有内容只保留容器，以达到减少内容节点以及内存占用，不过在快速进行滑动时比较容易出现一片白框，算是为了性能损失一些体验，整体上来说是可以接受得。</p><p>由于 RN 下在获取元素坐标偏移等数据相对 H5 和小程序要麻烦得到，具体的实现细节可以查看抽离出来的简单实现<a href="https://github.com/aNd1coder/taro-waterfall" target="_blank" rel="noopener noreferrer" title="Taro 高性能瀑布流组件（for RN）">Taro 高性能瀑布流组件（for RN）</a>。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="写在最后">写在最后<a href="#写在最后" class="hash-link" aria-label="写在最后的直接链接" title="写在最后的直接链接">​</a></h2><p><img loading="lazy" src="https://img14.360buyimg.com/ling/jfs/t1/105266/16/1522/210358/5dc18d19E6401cb7a/ad1ab2372f08e6c4.jpg" alt="三端达到像素级别的还原" class="img_ZOZK"></p><p>这篇文章从技术选型、开发实录再到性能优化三个维度对京喜首页改版做了简单总结。整个项目实践下来，证实 Taro 开发框架 已完全具备投入大型商业项目的条件。虽在多端开发适配上耗费了一些时间，但仍比各端独立开发维护工作量要少；在前端资源匮乏的今天，选择成熟的开发工具来控制成本、提升效率，已是各团队的首要工作目标。 同时，京喜作为京东战略级业务，拥有千万级别的流量入口，我们对页面的体验优化和性能改进远不止于此，希望每一次微小的改动能为用户带来愉悦的感受，始终为用户提供优质的产品体验。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Taro 邀你加入社区共建 — 社区共建倡议书]]></title>
            <link>https://docs.taro.zone/blog/2019-10-24-taro-open</link>
            <guid>https://docs.taro.zone/blog/2019-10-24-taro-open</guid>
            <pubDate>Thu, 24 Oct 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[Taro 作为一个多端统一开发解决方案，自诞生以来一直保持着高速的发展态势， 从一个单一的开发框架发展到如今服务数万名开发者的多端解决方案，Taro 的成长有 Taro 团队不忘初心的努力，但更重要的是，离不开社区广大开发者的支持和帮助。]]></description>
            <content:encoded><![CDATA[<p><a href="https://taro.jd.com" target="_blank" rel="noopener noreferrer">Taro</a> 作为一个多端统一开发解决方案，自诞生以来一直保持着高速的发展态势， 从一个单一的开发框架发展到如今服务数万名开发者的多端解决方案，Taro 的成长有 Taro 团队不忘初心的努力，但更重要的是，离不开社区广大开发者的支持和帮助。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="开源面临着什么">开源面临着什么<a href="#开源面临着什么" class="hash-link" aria-label="开源面临着什么的直接链接" title="开源面临着什么的直接链接">​</a></h2><p>我们可以先来看一组数据。</p><p>Taro 从开源到当前行文为止，总共有 <strong>5227</strong> 次 commits，发布了 <strong>235</strong> 个版本，同时有 <strong>3908</strong> 个 issues 被开启，在 Taro 团队和社区的努力下，已经关闭了 <strong>3296</strong> 个，还有近 <strong>600</strong> 个 issues 有待解决，而每周 Taro 的新开启的 issues 也在 <strong>50</strong> 个左右；同时也收获了 <strong>709</strong> 个 PR，合入了 <strong>656</strong> 个。</p><p>除此之外，<a href="https://taro-club.jd.com/" target="_blank" rel="noopener noreferrer">Taro 交流社区</a> 发帖数也达到 <strong>3000</strong> 个，每周以 <strong>200</strong> 个左右的增速在增加，而 <a href="/docs">Taro 官方文档</a> 作为 Taro 入门及开发必备的资源，每周的访问量达到了 <strong>20w</strong>。</p><p>同时 Taro 还有 <strong>20 余</strong> 个 <strong>500</strong> 人左右的开发者交流微信群，以及 10 余个跟各小程序官方团队、各大公司研发团队的研讨群，每天 Taro 团队的开发人员都要遭受大规模的信息轰炸。</p><p>由此可见，Taro 目前拥有庞大且活跃的开发者社区，每天都会产生大量的信息，或是问题，或是建议，或是代码提交，或是有用的轮子。当社区变得越来越大后，如何进行有效的管理，提升社区整体的使用体验，便成了困扰 Taro 团队的问题，迫切需要在方案迭代与社区维护之间寻找到一个平衡点，以使 Taro 可以走得更远，发展得更好。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="来自社区的力量">来自社区的力量<a href="#来自社区的力量" class="hash-link" aria-label="来自社区的力量的直接链接" title="来自社区的力量的直接链接">​</a></h2><p>正所谓「单丝不成线，独木不成林」。</p><p>Taro 发展至今早已不在属于单一团队的项目了，而是整个 Taro 开发社区共同的项目。在 Taro 所属的 NervJS 组织的<a href="https://github.com/orgs/NervJS/people" target="_blank" rel="noopener noreferrer">成员</a> 中也已经加入了像华为、腾讯等其他公司的研发人员，共同建设项目，而整个社区的贡献者也达到了 <strong>230</strong> 位。</p><p><img loading="lazy" src="https://img10.360buyimg.com/ling/jfs/t1/51716/38/13586/794849/5da564c9Ef215f164/cd2069db4f105db4.jpg" alt="WX20191010-150217@2x.png" class="img_ZOZK"></p><p>而在所有的社区贡献中，需要特别感谢：</p><ul><li><a href="https://github.com/nanjingboy/" target="_blank" rel="noopener noreferrer">Tom Huang</a> ，独立为 Taro 开发所有端的 MobX 支持</li><li><a href="https://github.com/js-newbee/" target="_blank" rel="noopener noreferrer">jsNewbee</a>，为 Taro 贡献了一个完整支持 小程序 + H5 + React Native 的<a href="https://github.com/qit-team/taro-yanxuan" target="_blank" rel="noopener noreferrer">多端样例</a></li><li><a href="https://github.com/Qiyu8/" target="_blank" rel="noopener noreferrer">Chunlin</a>，来自华为，为 Taro 完善了快应用的转换支持</li></ul><p>同时也感谢受邀成为 <a href="https://github.com/NervJS/taro-ui" target="_blank" rel="noopener noreferrer">TaroUI</a> 核心维护人员的 <a href="https://github.com/Garfield550" target="_blank" rel="noopener noreferrer">Garfield550</a> (小姐姐)、<a href="https://github.com/yinLiangDream" target="_blank" rel="noopener noreferrer">梁音</a>、<a href="https://github.com/lsqy" target="_blank" rel="noopener noreferrer">ShaoQian Liu</a>，他们将支撑起 TaroUI 的后续迭代与维护。</p><p>当然还有在社区中乐于助人、积极贡献的 <a href="https://github.com/zacksleo" target="_blank" rel="noopener noreferrer">zacksleo</a> 、<a href="https://github.com/fjc0k" target="_blank" rel="noopener noreferrer">Jay Fong</a>、<a href="https://github.com/loveonelong" target="_blank" rel="noopener noreferrer">loveonelong</a>、<a href="https://github.com/lolipop99" target="_blank" rel="noopener noreferrer">lolipop99</a>、<a href="https://github.com/bozaigao" target="_blank" rel="noopener noreferrer">波仔糕</a>、<a href="https://github.com/fwh1990" target="_blank" rel="noopener noreferrer">原罪</a>、<a href="https://github.com/lentoo/" target="_blank" rel="noopener noreferrer">lentoo</a> 、<a href="https://taro-club.jd.com/user/%E7%99%BD%E9%A2%86%E5%A4%8F%E5%85%AC%E5%AD%90" target="_blank" rel="noopener noreferrer">白领夏公子</a> 、YuanQuan、 tourze、 lingxiaoZhu 等等。</p><p>此外，还要感谢一直默默为 Taro 发展提供宝贵建议的研发团队：腾讯云、数字广东、腾讯 CDC、网易严选、华为开源团队、招联消费金融等等</p><p>社区的力量一直在支撑 Taro 前进。</p><p>所以，如今 Taro 面临的社区维护问题我们希望可以交给社区来进行自我管理。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="期待你的加入">期待你的加入<a href="#期待你的加入" class="hash-link" aria-label="期待你的加入的直接链接" title="期待你的加入的直接链接">​</a></h2><p>为了能够让社区更加高效、健康的运转下去，Taro 官方团队希望能够从社区中吸引开发者来共同维护，进行社区共建。</p><p>社区共建本次拟开放三大类角色，Taro 团队会为每一类角色设定一定门槛以及激励机制，从而保证整体质量。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="能力完善">能力完善<a href="#能力完善" class="hash-link" aria-label="能力完善的直接链接" title="能力完善的直接链接">​</a></h3><table><thead><tr><th>头衔</th><th>准入机制</th><th>权益</th></tr></thead><tbody><tr><td>认证开发者</td><td>1. 为 Taro 实现过独立功能特性 <br> 2. 代码 commit 数及代码贡献量达到一定标准</td><td>1. 获取 Taro 项目 developer 权限 <br> 2. 来自 Taro 团队定期礼品与开源认证证书 <br> 3. Readme 及官网、文档展示</td></tr><tr><td>代码贡献者</td><td>为 Taro 提交过 PR</td><td>Readme 及官网、文档展示</td></tr></tbody></table><h3 class="anchor anchorWithStickyNavbar_mCi1" id="社区管理">社区管理<a href="#社区管理" class="hash-link" aria-label="社区管理的直接链接" title="社区管理的直接链接">​</a></h3><table><thead><tr><th>头衔</th><th>准入机制</th><th>权益</th><th>职责</th></tr></thead><tbody><tr><td>社区版主</td><td>1. 通过社区报名，遴选后产生 <br> 2. 通过考察在 Taro 社区的活跃程度，由 Taro 团队进行定向邀请</td><td>1. Readme 及官网、文档展示 <br> 2. 所提出特性会优先考虑在 Taro 版本中加入 <br>3. 来自 Taro 团队定期礼品与开源认证证书</td><td>1. 负责社区各版块的管理<br>2. 定期发布版块相关主题帖 <br> 3. 定期收集版块问题帖子，进行汇总，反馈给 Taro 团队 <br> 4. 对版块内帖子进行审核，剔除无用、违反法律等帖子</td></tr><tr><td>GitHub Issues 管理员</td><td>1. 通过社区报名，遴选后产生 <br> 2. 通过考察在 Taro 社区的活跃程度，由 Taro 团队进行定向邀请</td><td>1. Readme 及官网、文档展示 <br> 2. 所提出特性会优先考虑在 Taro 版本中加入 <br>3. 来自 Taro 团队定期礼品与开源认证证书 <br> 4. 参与 GitHub robot 完善</td><td>1. 负责对 GitHub issues 答疑解惑以及打标分类 <br> 2. 负责每周汇总 issues，并将 issues 提交到 Taro 团队 <br>3. 负责每周精选 issues，并将数据同步到 Taro 社区</td></tr><tr><td>交流群管理员</td><td>1. 通过社区报名，遴选后产生 <br> 2. 通过考察在 Taro 社区的活跃程度，由 Taro 团队进行定向邀请</td><td>1. Readme 及官网、文档展示 <br> 2. 所提出特性会优先考虑在 Taro 版本中加入 <br>3. 来自 Taro 团队定期礼品与开源认证证书 <br>4. 参与 群小助手 完善</td><td>1. 负责群内内容的管理，创造良好的沟通交流环境 <br> 2. 负责群内问题的答疑解惑，引导开发者去社区、GitHub issues 获取答案 <br>3. 负责操作社区群管理工具，整理群内问题，同步到 Taro 社区</td></tr></tbody></table><h3 class="anchor anchorWithStickyNavbar_mCi1" id="知识构建">知识构建<a href="#知识构建" class="hash-link" aria-label="知识构建的直接链接" title="知识构建的直接链接">​</a></h3><table><thead><tr><th>形式</th><th>提交方式</th><th>权益</th></tr></thead><tbody><tr><td>Taro 课程视频录制</td><td>通过邮箱发送个人信息及试讲视频<br>邮箱：<a href="mailto:taro@jd.com" target="_blank" rel="noopener noreferrer">taro@jd.com</a><br>内容：<br>1. 个人信息：主攻技术方向、从业经历、公开分享经验等 <br>2. 试讲视频链接通过遴选后，Taro 团队会与你联系沟通后续的视频录制事宜</td><td>成为 Taro 官方合作伙伴，视频课程经由 Taro 团队审核后，Taro 团队会全力协助进行运营推广</td></tr><tr><td>Taro 开发实践文章撰写</td><td>通过邮箱投稿<br>邮箱：<a href="mailto:taro@jd.com" target="_blank" rel="noopener noreferrer">taro@jd.com</a><br>内容：<br>1. md 文件（若为系列文章请添加大纲） <br>2. 是否在 Taro 渠道首发通过遴选后，Taro 团队会与你联系沟通后续发布事宜</td><td>成为 Taro 官方合作伙伴，文章经由 Taro 团队审核后，会通过 Taro 相关优质渠道进行发布</td></tr></tbody></table><h2 class="anchor anchorWithStickyNavbar_mCi1" id="写在最后">写在最后<a href="#写在最后" class="hash-link" aria-label="写在最后的直接链接" title="写在最后的直接链接">​</a></h2><p>Taro 作为一个多端开发解决方案，还处在初级的发展阶段，是一个发展中的方案，在不久的未来我们会有更多新的功能、特性推向开发者，甚至可能也有颠覆性的东西出现，让 Taro 成为一个更棒的方案。目前 Taro 已经翻过了一座座发展的山峰，但他正在向更高山峰迈进。</p><p>长风破浪会有时，直挂云帆济沧海。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="taro-诚邀你的加入">Taro 诚邀你的加入<a href="#taro-诚邀你的加入" class="hash-link" aria-label="Taro 诚邀你的加入的直接链接" title="Taro 诚邀你的加入的直接链接">​</a></h2><p>联系邮箱：<a href="mailto:taro@jd.com" target="_blank" rel="noopener noreferrer">taro@jd.com</a>
邮件标题：【共建】+ 你想说的话</p><p>加入 Taro 微信群？加「 Taro 小助手」（微信号: jdtester）回复 <code>taro</code> 即可。</p><p><img loading="lazy" src="https://img30.360buyimg.com/ling/jfs/t1/81131/13/700/35411/5cef439fE8acf6ac4/2319513fc07404e6.png" alt="Taro小助手" class="img_ZOZK"></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[玩转 Taro 跨端之 flex 布局篇]]></title>
            <link>https://docs.taro.zone/blog/2019-09-25-taro-flex</link>
            <guid>https://docs.taro.zone/blog/2019-09-25-taro-flex</guid>
            <pubDate>Wed, 25 Sep 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[玩转 Taro 跨端之 flex 布局篇，带你了解 flex 布局在各小程序端的表现详情并给出最佳实践 demo]]></description>
            <content:encoded><![CDATA[<blockquote><p>玩转 Taro 跨端之 flex 布局篇，带你了解 flex 布局在各小程序端的表现详情并给出最佳实践 demo</p></blockquote><p>Taro 是一套遵循 React 语法规范的跨平台开发解决方案，但是目前当我们使用 Taro 的时候，在不同平台上的开发体验还有不一致的地方，所以我们也都期待有一套跨平台统一的解决方案，能够以最小差异的方式向开发者提供更好的开发体验。</p><p>在跨平台开发的过程中，不同平台之间的差异尤其体现在样式的统一上，由于不同平台对样式的支持程度并不一致，Taro 很难能够通过编译的手段来对跨平台样式进行统一，所以，我们需要一个支持跨平台的解决方案来对样式进行统一。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="跨平台样式">跨平台样式<a href="#跨平台样式" class="hash-link" aria-label="跨平台样式的直接链接" title="跨平台样式的直接链接">​</a></h2><p>考虑页面布局和样式 H5 是最为灵活的，小程序次之，React Native 和快应用最弱，统一跨平台样式应当优先对齐短板，也就是要以 React Native 和快应用的约束来管理样式，同时兼顾小程序的限制，而 Flexbox 就是一个很好的解决方案。</p><p>在构建页面的时候，我们可以通过 Flexbox 高效地完成页面代码，虽然并不是所有属性都可以全平台适应的，但是它在全平台都能够得到足够的支持，而且所有平台可以很容易通过它来绘制通用性很高的页面，这也就是为什么我们选择使用 Flexbox 方案来完成这个跨平台演示项目。</p><p><img loading="lazy" src="https://img14.360buyimg.com/ling/jfs/t1/62729/39/11317/546281/5d8a1c6dE2a8f223e/056e2327640ccf83.png" alt="taro flexbox 效果图" class="img_ZOZK"></p><p><a href="https://nervjs.github.io/taro-flexbox/" target="_blank" rel="noopener noreferrer">预览地址</a></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="flexbox-布局-flexible-box-layout">Flexbox 布局 (Flexible Box Layout)<a href="#flexbox-布局-flexible-box-layout" class="hash-link" aria-label="Flexbox 布局 (Flexible Box Layout)的直接链接" title="Flexbox 布局 (Flexible Box Layout)的直接链接">​</a></h3><p>Flexbox 是弹性布局模块(CSS Flexible Box Layout Module)常用的简称，是一种用于在单个维度中显示项目行或列的布局模型。在规范中， Flexbox 被描述为用户界面设计的布局模型。 Flexbox 的关键特性是 flex 布局中的项目可以增长和缩小。可以将空间分配给项目本身，或者在项目之间或周围分配空间。</p><p>在 Flexbox 中，采用 <em>flex 布局</em> 的元素，称为 <strong>flex 容器</strong>(flex container)， flex 容器所有的子元素自动成为容器成员，称为 <strong>flex 元素</strong>(flex item)。<strong>Flex 容器</strong> 默认存在两根轴：水平的主轴(main-axis)和垂直的交叉轴(cross-axis)。<strong>flex 元素</strong> 默认沿主轴排列。主轴的开始位置(与边框的交叉点)叫做 <em>main-start</em> ，结束位置叫做 <em>main end</em> ;交叉轴的开始位置叫做 <em>cross-start</em> ，结束位置叫做 <em>cross-end</em> ;单个项目占据的主轴空间叫做 <em>main-size</em> ，占据的交叉轴空间叫做 <em>cross-size</em> 。</p><p><img loading="lazy" src="https://www.w3.org/TR/css-flexbox-1/images/flex-direction-terms.svg" alt="flex-direction-terms" class="img_ZOZK"></p><p>Flexbox 可以对齐主轴或横轴上的项目，从而提供对一组项目的大小和对齐的高级控制，大多数场景下，使用 flex-direction、align-items 和 justify-content 三个样式属性就已经能满足大多数布局需求，换而言之如果熟悉 Flexbox 就可以应对大多数场景下的布局需求。</p><blockquote><p>注意，设为 Flex 布局以后，子元素的 <em>float</em> 、 <em>clear</em> 和 <em>vertical-align</em> 属性将失效。</p></blockquote><h3 class="anchor anchorWithStickyNavbar_mCi1" id="flex-container-属性">Flex Container 属性<a href="#flex-container-属性" class="hash-link" aria-label="Flex Container 属性的直接链接" title="Flex Container 属性的直接链接">​</a></h3><p>在规范中， Flex Container 上，一共有七个属性可以设置，但是 flex-flow 在 React Native 上是不支持的。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="flex-direction">flex-direction<a href="#flex-direction" class="hash-link" aria-label="flex-direction的直接链接" title="flex-direction的直接链接">​</a></h4><p>flex-direction 属性指定了 flex 元素是如何在 flex 容器中布局的，定义了主轴的方向(正方向或反方向)。</p><p>支持的值如下：
|值|意义|
|--|--|
|row|flex 容器的主轴被定义为与文本方向相同。 主轴起点和主轴终点与内容方向相同。|
|row-reverse|表现和 row 相同，但是置换了主轴起点和主轴终点。|
|column|flex 容器的主轴和块轴相同。主轴起点与主轴终点和书写模式的前后点相同。|
|column-reverse|表现和 column 相同，但是置换了主轴起点和主轴终点。|</p><blockquote><p>需要注意的是，规范下 flex-direction 的默认值是 row ，而在 React Native 中则为 column，这也就是为什么我们会添加了这个的样式</p><div class="language-scss codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-scss codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token selector" style="color:#00009f">.flex </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">display</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> flex</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">flex-direction</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> row</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="flex-wrap">flex-wrap<a href="#flex-wrap" class="hash-link" aria-label="flex-wrap的直接链接" title="flex-wrap的直接链接">​</a></h4><p>flex-wrap 指定 flex 元素单行显示还是多行显示。如果允许换行，这个属性允许控制行的堆叠方向。默认值为 nowrap。</p><p>支持的值如下：
|值|意义|
|--|--|
|nowrap|不换行。flex 元素被摆放到到一行，这可能导致溢出 flex 容器。交叉轴的起点会根据 flex-direction 的值相当于 start 或 before。|
|wrap|flex 元素被打断到多个行中。交叉轴的起点会根据 flex-direction 的值选择等于 start 或 before。交叉轴的终点为确定的交叉轴的起点的另一端。|
|wrap-reverse|和 wrap 的行为一样，但是交叉轴的起点和交叉轴的终点互换。|</p><blockquote><p>使用 flex-wrap 属性的时候，我们需要注意 wrap-reverse 值在 React Native 上是不支持的。</p></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="flex-flow">flex-flow<a href="#flex-flow" class="hash-link" aria-label="flex-flow的直接链接" title="flex-flow的直接链接">​</a></h4><p>flex-flow 属性是 flex-direction 和 flex-wrap 的简写。默认值为 row nowrap。</p><p>语法格式</p><div class="language-conf codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-conf codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">&lt;'flex-direction'&gt; || &lt;'flex-wrap'&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p>flex-flow 属性不被 React Native 和快应用支持</p></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="align-items">align-items<a href="#align-items" class="hash-link" aria-label="align-items的直接链接" title="align-items的直接链接">​</a></h4><p>align-items 属性将所有直接子节点上的 align-self 值设置为一个组。 align-self 属性设置项目在其包含块中在交叉轴方向上的对齐方式。默认值为 stretch。</p><table><thead><tr><th>值</th><th>意义</th></tr></thead><tbody><tr><td>stretch</td><td>flex 元素在交叉轴方向拉伸到与容器相同的高度或宽度（flex 元素不能固定尺寸）</td></tr><tr><td>flex-start</td><td>交叉轴的起点对齐</td></tr><tr><td>flex-end</td><td>交叉轴的终点对齐</td></tr><tr><td>center</td><td>交叉轴的中点对齐</td></tr><tr><td>baseline</td><td>元素第一行文字的基线对齐</td></tr></tbody></table><p>语法格式</p><div class="language-conf codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-conf codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">normal | stretch | &lt;baseline-position&gt; | [ &lt;overflow-position&gt;? &lt;self-position&gt; ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">where</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;baseline-position&gt; = [ first | last ]? baseline</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;overflow-position&gt; = unsafe | safe</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;self-position&gt; = center | start | end | self-start | self-end | flex-start | flex-end</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p>baseline 值不被 React Native 和快应用支持</p><p>space-evenly、start、end、self-start、self-end、left、right、first baseline、last baseline、safe、unsafe 在 flex 布局中通用性低</p></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="align-content">align-content<a href="#align-content" class="hash-link" aria-label="align-content的直接链接" title="align-content的直接链接">​</a></h4><p>align-content 属性设置了如何沿着 flex 容器的交叉轴和在 flex 元素之间和周围分配空间。默认值为 stretch。</p><blockquote><p>该属性对单行弹性盒子模型无效。（即：带有 flex-wrap: nowrap 的 flex 容器）。</p></blockquote><table><thead><tr><th>值</th><th>意义</th></tr></thead><tbody><tr><td>stretch</td><td>拉伸所有 flex 元素来填满剩余空间。剩余空间平均的分配给每一个 flex 元素</td></tr><tr><td>flex-start</td><td>所有 flex 元素从垂直轴起点开始填充。第一个 flex 元素的垂直轴起点边和 flex 容器的垂直轴起点边对齐。接下来的每一个 flex 元素紧跟前一个 flex 元素。</td></tr><tr><td>flex-end</td><td>所有 flex 元素从垂直轴末尾开始填充。最后一个 flex 元素的垂直轴终点和容器的垂直轴终点对齐。同时所有后续 flex 元素与前一个对齐。</td></tr><tr><td>center</td><td>所有 flex 元素朝向容器的中心填充。每 flex 元素互相紧挨，相对于容器居中对齐。容器的垂直轴起点边和第一个 flex 元素的距离相等于容器的垂直轴终点边和最后一个 flex 元素的距离。</td></tr><tr><td>space-between</td><td>所有 flex 元素在容器中平均分布。相邻两 flex 元素间距相等。容器的垂直轴起点边和终点边分别与第一个 flex 元素和最后一个 flex 元素的边对齐。</td></tr><tr><td>space-around</td><td>所有 flex 元素在 flex 容器中平均分布，相邻两 flex 元素间距相等。容器的垂直轴起点边和终点边分别与第一个 flex 元素和最后一个 flex 元素的距离是相邻两 flex 元素间距的一半。</td></tr><tr><td>space-evenly</td><td>flex 元素都沿着主轴均匀分布在指定的 flex 元素中。相邻 flex 元素之间的间距，主轴起始位置到第一个 flex 元素的间距,，主轴结束位置到最后一个 flex 元素的间距，都完全一样。</td></tr></tbody></table><p>语法格式</p><div class="language-conf codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-conf codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">normal | &lt;baseline-position&gt; | &lt;content-distribution&gt; | &lt;overflow-position&gt;? &lt;content-position&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">where</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;baseline-position&gt; = [ first | last ]? baseline</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;content-distribution&gt; = space-between | space-around | space-evenly | stretch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;overflow-position&gt; = unsafe | safe</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;content-position&gt; = center | start | end | flex-start | flex-end</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p>React Native 中需要版本号在 0.58 以上</p><p>且 flex-wrap 属性值需要为 wrap</p><p>同时只对横轴生效(即 flex-direction 属性为 column 或 column-reverse)</p><p>baseline 值不被 React Native 支持</p><p>space-evenly、start、end、left、right、first baseline、last baseline、safe、unsafe 在 flex 布局中通用性低</p></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="justify-content">justify-content<a href="#justify-content" class="hash-link" aria-label="justify-content的直接链接" title="justify-content的直接链接">​</a></h4><p>justify-content 属性定义了浏览器如何分配顺着 flex 容器主轴的 flex 元素之间及其周围的空间。</p><table><thead><tr><th>值</th><th>意义</th></tr></thead><tbody><tr><td>flex-start</td><td>从行首开始排列。每行第一个 flex 元素与行首对齐，同时所有后续的 flex 元素与前一个对齐。</td></tr><tr><td>flex-end</td><td>从行尾开始排列。每行最后一个 flex 元素与行尾对齐，其他元素将与后一个对齐。</td></tr><tr><td>center</td><td>伸缩元素向每行中点排列。每行第一个元素到行首的距离将与每行最后一个元素到行尾的距离相同。</td></tr><tr><td>space-between</td><td>在每行上均匀分配 flex 元素。相邻元素间距离相同。每行第一个元素与行首对齐，每行最后一个元素与行尾对齐。</td></tr><tr><td>space-around</td><td>在每行上均匀分配 flex 元素。相邻元素间距离相同。每行第一个元素到行首的距离和每行最后一个元素到行尾的距离将会是相邻元素之间距离的一半。</td></tr><tr><td>space-evenly</td><td>flex 元素都沿着主轴均匀分布在指定的 flex 元素中。相邻 flex 元素之间的间距，主轴起始位置到第一个 flex 元素的间距,，主轴结束位置到最后一个 flex 元素的间距，都完全一样。</td></tr></tbody></table><p>语法格式</p><div class="language-conf codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-conf codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">normal | &lt;content-distribution&gt; | &lt;overflow-position&gt;? [ &lt;content-position&gt; | left | right ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">where</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;content-distribution&gt; = space-between | space-around | space-evenly | stretch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;overflow-position&gt; = unsafe | safe</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;content-position&gt; = center | start | end | flex-start | flex-end</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p>baseline 值不被 React Native 支持</p><p>stretch、space-evenly、start、end、left、right、first baseline、last baseline、safe、unsafe 在 flex 布局中通用性低</p></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="place-content">place-content<a href="#place-content" class="hash-link" aria-label="place-content的直接链接" title="place-content的直接链接">​</a></h4><p>place-content 属性是 align-content 和 justify-content 的简写。</p><p>语法格式</p><div class="language-conf codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-conf codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">&lt;'align-content'&gt; &lt;'justify-content'&gt;?</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ul><li>如果第二个值不存在，且第一个值适用于用于两者，则第二个值复用第一个</li><li>如果第二个值不存在，且第一个值不适用于用于两者，则整个值无效</li></ul><blockquote><p>place-content 属性不被 React Native 支持</p></blockquote><h3 class="anchor anchorWithStickyNavbar_mCi1" id="flex-item-属性">Flex Item 属性<a href="#flex-item-属性" class="hash-link" aria-label="Flex Item 属性的直接链接" title="Flex Item 属性的直接链接">​</a></h3><p>在 Flex Item 上，同样也有六个属性，而 order 属性在 React Native 上不支持。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="order">order<a href="#order" class="hash-link" aria-label="order的直接链接" title="order的直接链接">​</a></h4><p>order 属性规定了 flex 容器中的 flex 元素在布局时的顺序。flex 元素按照 order 属性的值的增序进行布局。拥有相同 order 属性值的 flex 元素按照它们在源代码中出现的顺序进行布局。默认值为 0。</p><p>语法格式</p><div class="language-conf codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-conf codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">&lt;integer&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p>order 属性不被 React Native 支持</p></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="flex-grow">flex-grow<a href="#flex-grow" class="hash-link" aria-label="flex-grow的直接链接" title="flex-grow的直接链接">​</a></h4><p>flex-grow 属性定义 flex 元素的拉伸因子。</p><p>语法格式</p><div class="language-conf codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-conf codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">&lt;number&gt; | inherit</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p>负值无效</p><p>React Native 上默认值为 0</p></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="flex-shrink">flex-shrink<a href="#flex-shrink" class="hash-link" aria-label="flex-shrink的直接链接" title="flex-shrink的直接链接">​</a></h4><p>flex-shrink 属性指定了 flex 元素的收缩规则。flex 元素仅在默认宽度之和大于容器的时候才会发生收缩，其收缩的大小是依据 flex-shrink 的值。默认值为 1。</p><p>语法格式</p><div class="language-conf codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-conf codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">&lt;number&gt; | inherit</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p>负值是不被允许的。</p><p>React Native 上默认值为 1</p></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="flex-basis">flex-basis<a href="#flex-basis" class="hash-link" aria-label="flex-basis的直接链接" title="flex-basis的直接链接">​</a></h4><p>flex-basis 指定了 flex 元素在主轴方向上的初始大小。如果不使用 box-sizing 改变盒模型的话，那么这个属性就决定了 flex 元素的内容盒（content-box）的尺寸。</p><blockquote><p>注意：如果一个 flex 元素同时设置了 flex-basis (auto 除外)和 width (或者 flex-direction: column 时设置了 height )，flex-basis 权级更高。</p></blockquote><p>语法规范</p><div class="language-conf codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-conf codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">content | &lt;'width'&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ul><li>&lt;'width'&gt;<ul><li>width 值可以是<code> &lt;length&gt;</code>;</li><li>该值也可以是一个相对于其父弹性盒容器主轴尺寸的百分数 。</li><li>负值是不被允许的。</li><li>默认为 0。</li></ul></li><li>content<ul><li>基于 flex 元素的内容自动调整大小。</li></ul></li></ul><blockquote><p>React Native 上使用 ScrollView 组件会导致属性失效</p><p>如果没有足够空间，组件不会发生收缩 (应该是设置了 flex-shrink 属性值默认为 0)</p></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="flex">flex<a href="#flex" class="hash-link" aria-label="flex的直接链接" title="flex的直接链接">​</a></h4><p>flex 规定了 flex 元素如何伸长或缩短以适应 flex 容器中的可用空间。这是一个简写属性，用来设置 flex-grow, flex-shrink 与 flex-basis。</p><p>语法格式</p><div class="language-conf codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-conf codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">none | [ &lt;'flex-grow'&gt; &lt;'flex-shrink'&gt;? || &lt;'flex-basis'&gt; ]</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ul><li>initial<ul><li>元素会根据自身宽高设置尺寸。</li><li>它会缩短自身以适应 flex 容器，但不会伸长并吸收 flex 容器中的额外自由空间来适应 flex 容器 。</li><li>相当于将属性设置为"flex: 0 1 auto"。</li></ul></li><li>auto<ul><li>元素会根据自身的宽度与高度来确定尺寸，但是会伸长并吸收 flex 容器中额外的自由空间，也会缩短自身来适应 flex 容器。</li><li>这相当于将属性设置为 "flex: 1 1 auto"。</li></ul></li><li>none<ul><li>元素会根据自身宽高来设置尺寸。</li><li>它是完全非弹性的：既不会缩短，也不会伸长来适应 flex 容器。</li><li>相当于将属性设置为"flex: 0 0 auto"。</li></ul></li></ul><blockquote><p>在 React Native 中只能为 number 类型</p><ul><li>当 flex &gt; 0 时，组件大小将与其弹性值成比例。因此，flex 设置为 2 的组件将占用空间的两倍作为 flex 设置为 1 的组件</li><li>当 flex = 0 时，组件根据 width 和 height 确定大小，且不会发生变化。</li><li>当 flex = -1 时，组件通常根据 width 和 height 确定大小。但是，如果没有足够的空间，组件将收缩到 minWidth 和 minHeight。</li></ul><p>在快应用中，flex 的快捷值设置均是无效值</p></blockquote><h4 class="anchor anchorWithStickyNavbar_mCi1" id="align-self">align-self<a href="#align-self" class="hash-link" aria-label="align-self的直接链接" title="align-self的直接链接">​</a></h4><p>align-self 会对齐当前 flex 行中的 flex 元素，并覆盖 align-items 的值. 如果任何 flex 元素的侧轴方向 margin 值设置为 auto，则会忽略 align-self。</p><p>语法格式</p><div class="language-conf codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-conf codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">auto | normal | stretch | &lt;baseline-position&gt; | &lt;overflow-position&gt;? &lt;self-position&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">where</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;baseline-position&gt; = [ first | last ]? baseline</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;overflow-position&gt; = unsafe | safe</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;self-position&gt; = center | start | end | self-start | self-end | flex-start | flex-end</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p>baseline 值不被 React Native 和快应用支持</p><p>start、end、self-start、self-end、first baseline、last baseline、safe、unsafe 在 flex 布局中通用性低</p></blockquote><h2 class="anchor anchorWithStickyNavbar_mCi1" id="组件化开发">组件化开发<a href="#组件化开发" class="hash-link" aria-label="组件化开发的直接链接" title="组件化开发的直接链接">​</a></h2><p>不同的平台如 Web、React-Native、微信小程序等各有特色，平台之间的差异很大，会导致很多额外的开发成本。那么如果我们想要完成一个跨平台项目该怎么做呢？</p><p>我们开始从比较容易入手的方向考虑，如果采用模块化组件或是 css-in-js 的方案去完成样式的构建会是一个好的方案么？</p><p>在目前的前端生态中，模块化组件开发会是个很棒的方案，覆盖模式下构建开箱即用的组件同时可以提供方法来覆盖样式再好不过了，但是如果放到小程序开发的模式中，这就会有个很严重的问题，那就是如果我们在层级样式表中写到的样式，是不能直接传给组件来覆盖样式的，组件和组件的隔离在不同小程序中很难被打破。</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">/* CustomComp.js */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">CustomComp</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">static</span><span class="token plain"> defaultProps </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">className</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">''</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">render</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript keyword" style="color:#00009f">this</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">props</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">className</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">这段文本的颜色不会由组件外的 class 决定</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">/* MyPage.js */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">MyPage</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">render</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">CustomComp</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">red-text</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="language-css codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-css codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">/* MyPage.scss */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token selector class" style="color:#00009f">.red-text</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">color</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token color">red</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>如果大家尝试上述的写法，会发现 red-text 类中的样式并没有生效，那么在这种情况下我们如果考虑是使用 css-in-js 会好么？很遗憾，如果你使用它，我们将不会为这些需要运行时处理的样式补全前缀。</p><p>这两个方案都不是合适的方案，那么我们该怎么做呢？试着去打破小程序的组件限制么？我们在微信小程序官方的文档中找到 externalClasses 这个方法，可以先来尝试。</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">/* CustomComp.js */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">CustomComp</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">static</span><span class="token plain"> externalClasses </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'my-class'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">render</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">my-class</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">这段文本的颜色由组件外的 class 决定</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">/* MyPage.js */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">MyPage</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">render</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">CustomComp</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">red-text</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="language-css codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-css codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">/* MyPage.scss */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token selector class" style="color:#00009f">.red-text</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">color</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token color">red</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>但是这也并非所有的开发平台都能够提供给开发者相关的方法，所以我们只能转换目光到另一个 addGlobalClass 方法上，这个方法不仅在所有小程序都能够支持，Taro 在 React Native 端上也提供了同样的方法给大家，这样我们也可以避开 css modules 这个体验稍差的方法。</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">/* CustomComp.js */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">CustomComp</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">static</span><span class="token plain"> options </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">addGlobalClass</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">render</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">red-text</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">这段文本的颜色由组件外的 class 决定</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="language-css codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-css codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">/* 组件外的样式定义 */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token selector class" style="color:#00009f">.red-text</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">color</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token color">red</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="写在最后">写在最后<a href="#写在最后" class="hash-link" aria-label="写在最后的直接链接" title="写在最后的直接链接">​</a></h2><p>在项目中，我们已经将所有通用支持的方法写到 scss 文件中，如果大家需要可以直接使用我们已经提供的 flexbox 样式，按如下方法在自己全局的层级样式表中引入我们已经提供的样式。</p><div class="language-scss codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-scss codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">@import</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'https://raw.githubusercontent.com/NervJS/taro-flexbox/master/flexbox-demo/src/asset/flex.scss'</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>那么关于 Flex 布局的知识，如果文中有遗漏的，大家可以跟着我们的项目来梳理知识，也可以到 <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Flexible_Box_Layout#%E5%8F%82%E8%80%83" target="_blank" rel="noopener noreferrer">MDN</a> 上查看相关的文档，值得注意的是在 Flexbox 布局中 gap、row-gap、column-gap 属性在 Grid 布局中普遍支持，但是在 Flex 布局中却只有 Firefox 完成了适配，所以暂且不表,</p><p>同样 justify-content 属性的 space-evenly 值在 web 端通用性很低，不建议使用。</p><blockquote><p>gap、row-gap、column-gap in flex</p><p><img loading="lazy" src="https://github.com/NervJS/taro-flexbox/wiki/space-evently-desktop.png?raw=true" alt="gap_in_flex" class="img_ZOZK"></p></blockquote><blockquote><p>space-evently on desktop</p><p><img loading="lazy" src="https://github.com/NervJS/taro-flexbox/wiki/space-evently-mobile.png?raw=true" alt="space-evently-desktop" class="img_ZOZK"></p></blockquote><blockquote><p>space-evently on mobile</p><p><img loading="lazy" src="https://github.com/NervJS/taro-flexbox/wiki/gap_in_flex.png?raw=true" alt="space-evently-mobile" class="img_ZOZK"></p></blockquote><p>希望这篇文章可以为你提供一些帮助。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="属性对照表">属性对照表<a href="#属性对照表" class="hash-link" aria-label="属性对照表的直接链接" title="属性对照表的直接链接">​</a></h3><table><thead><tr><th>属性</th><th align="center">Chrome</th><th align="center">Firefox</th><th align="center">Android webview</th><th align="center">Safari on iOS</th><th align="center">微信小程序</th><th align="center">支付宝小程序</th><th align="center">百度小程序</th><th align="center">抖音小程序</th><th align="center">QQ 小程序</th><th align="center">React Native</th></tr></thead><tbody><tr><td>flex-direction</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">部分差异</td></tr><tr><td>flex-wrap</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">wrap-reverse 不支持</td></tr><tr><td>flex-flow</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">不支持</td></tr><tr><td>align-items</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">baseline 不支持</td></tr><tr><td>align-content</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">0.58+ (部分差异)</td></tr><tr><td>justify-content</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">baseline 不支持</td></tr><tr><td>place-content</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">不支持</td></tr><tr><td>order</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">不支持</td></tr><tr><td>flex-grow</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td></tr><tr><td>flex-shrink</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td></tr><tr><td>flex-basis</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">部分差异</td></tr><tr><td>flex</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">语法不同</td></tr><tr><td>align-self</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">✔</td><td align="center">baseline 不支持</td></tr></tbody></table><h3 class="anchor anchorWithStickyNavbar_mCi1" id="相关链接">相关链接<a href="#相关链接" class="hash-link" aria-label="相关链接的直接链接" title="相关链接的直接链接">​</a></h3><ul><li>预览地址：<a href="https://nervjs.github.io/taro-flexbox/" target="_blank" rel="noopener noreferrer">https://nervjs.github.io/taro-flexbox/</a></li><li>项目地址：<a href="https://github.com/NervJS/taro-flexbox" target="_blank" rel="noopener noreferrer">https://github.com/NervJS/taro-flexbox</a></li><li>参考地址<ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout#Reference" target="_blank" rel="noopener noreferrer">https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout#Reference</a></li><li><a href="https://facebook.github.io/react-native/docs/flexbox" target="_blank" rel="noopener noreferrer">https://facebook.github.io/react-native/docs/flexbox</a></li><li><a href="https://github.com/startheart/cml-flexbox" target="_blank" rel="noopener noreferrer">https://github.com/startheart/cml-flexbox</a></li></ul></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[使用 React Hooks 重构你的小程序]]></title>
            <link>https://docs.taro.zone/blog/2019-07-10-taro-hooks</link>
            <guid>https://docs.taro.zone/blog/2019-07-10-taro-hooks</guid>
            <pubDate>Wed, 10 Jul 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[image]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://storage.360buyimg.com/taro-club-img/b42116392c909d0680788853011c70db" alt="image" class="img_ZOZK"></p><blockquote><p>本文由余澈在 GMTC 全球大前端技术 2019 的演讲：<a href="https://gmtc.infoq.cn/2019/beijing/presentation/1706" target="_blank" rel="noopener noreferrer">《使用 React Hooks 重构你的小程序》</a>整理而成。</p></blockquote><h2 class="anchor anchorWithStickyNavbar_mCi1" id="背景">背景<a href="#背景" class="hash-link" aria-label="背景的直接链接" title="背景的直接链接">​</a></h2><p>一直关注小程序开发的朋友应该会注意到，最开始小程序就是为了微型创新型业务打造的一个框架，最多只能运行 1m 的包。可是后来发现很多厂商把越来越多的业务搬到了小程序上，小程序的能力也在不断地开放，变得越来越强大。于是后来打包限制上升到了 2m，然后引入了分包，现在已经已经可以上传 8m 的小程序。其实这个体积已经可以实现非常巨型非常复杂的业务了。就从 Taro 的用户来看，例如京东购物小程序和 58 同城小程序不管从代码的数量还是复杂度都不亚于 PC 端业务，所以我们可以说前端开发的复杂度正在向小程序端转移。</p><p>而小程序开发其实也是前端开发的一个子集，在整个前端业界，我们又是怎么解决复杂度这个问题的呢？</p><p>首先我们看看 React：React Core Team 成员，同时也是 Redux 的作者 Dan Abramov 在 2018 年的 ReactConf 向大家首次介绍了 React Hooks。React Hooks 是为了解决 Class Component 的一些问题而引入的：</p><ul><li>Class Component 组件间的逻辑难以复用。因为 JavaScript 不像 Go 或 C++ 一样，Class 可以多重继承，类的逻辑的复用就成了一个问题；</li><li>复杂组件难以理解。Class Component 经常会在生命周期做一些数据获取事件监听的副作用函数，这样的情况下我们就很难把组件拆分为更小的力度；</li><li>Class 令人迷惑。很多新手应该会被 Class 组件绑定事件的 <code>this</code> 迷惑过，绑定事件可以用 bind，可以直接写箭头函数，也可以写类属性函数，但到底哪种方法才是最好的呢？而到了 ES 2018，class 还有多种语法，例如装饰器，例如 private fileds 这些奇奇怪怪的语法也为新手增加了更多的困惑。</li></ul><p>而对于 Vue 而言也有相同的问题，Vue 的作者尤玉溪老师在 VueConf China 2019 也给 Vue 3.0 引入了一个叫 <em>Functional-based API</em> 的概念，它是受 React Hooks 启发而增加的新 API。由于 Vue 2.0 组件组合的模式是对象字面量形式，所以 <em>Functional-based API</em> 可以作为 Mixins 的替代，配合新的响应式 API 作为新的组件组合模式。那么对于 Vue 3.0 我们还知之甚少，以后的 API 也有可能改变，但或许是英雄所见略同，React 和 Vue 对于降低前端开发复杂度这一问题都不约而同地选择了 Hooks 这一方案，这到底是为什么呢？</p><p><img loading="lazy" src="https://i.loli.net/2019/07/02/5d1af3a5e84b022890.png" alt="why_hooks.png" class="img_ZOZK"></p><p>我们可以一下之前的组件组合方案，首先是 <em>Mixins</em>，红色圈的 <em>Mixins</em>，黄色的是组件，我们知道 <em>Mixins</em> 其实就是把多个对象组合成一个对象，<em>Mixins</em> 的过程就有点像调用 <code>Object.assgin</code> 方法。那 <em>Mixins</em> 有什么问题呢？首先是命名空间耦合，如果多个对象同名参数，这些参数就会耦合在一起；其次由于 <em>Mixins</em> 必须是运行时才能知道具体有什么参数，所以是 TypeScript 是无法做静态检查的；第三是组件参数不清晰，在 <em>Mixins</em> 中组件的 props 和其他参数没什么两样，很容易被其它的 <em>Mixins</em> 覆盖掉。</p><p>为了解决 <em>Mixins</em> 的问题，后来发展出了高阶组件（<em>HOC</em>）的方式，高阶组件就和图里一样，一个组件嵌套着另外的组件。它的确解决了 <em>Mixins</em> 的一些问题，例如命名空间解耦，由于每次都会生成新组件，就不存在命名空间问题了；其次它也能很好地做静态检查；但它依然没有办法处理组件 props 的问题，props 还是有可能会在高阶组件中被更改；而且它还有了新的问题，每多用一次高阶组件，都会多出一个组件实例。</p><p>最后我们来看一下 Hooks，紫色的圈圈是 Hooks，就像图里一样，Hooks 都在同一个组件里，Hooks 之间还可以相互调用。因为 Hooks 跑在一个普通函数式组件里，所以他肯定是没有命名空间的问题，同时 TypeScript 也能对普通函数做很好的静态检查，而且 Hooks 也不能更改组件的 Props，传入的是啥最后可用的就是啥；最后 Hooks 也不会生成新的组件，所以他是单组件实例。</p><p><img loading="lazy" src="https://i.loli.net/2019/07/02/5d1af4ef2dd5a56009.png" alt="taroxhooks.png" class="img_ZOZK"></p><p>在 Taro 1.3 版本，我们实现了一大堆特性，其中的重头戏就是 React Hooks 的支持。虽然 React Hooks 正式稳定的时间并不长，但我们认为这个特性能有效地简化开发模式，提升开发效率和开发体验。即便 Hooks 的生态和最佳实践还尚未完善，但我们相信未来 Hooks 会成为 React 开发模式的主流，也会深刻地影响其它框架未来的 API 构成。所以在 Taro 的规划中我们也把 Hooks 放在了很重要的位置。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="什么是-hooks">什么是 Hooks？<a href="#什么是-hooks" class="hash-link" aria-label="什么是 Hooks？的直接链接" title="什么是 Hooks？的直接链接">​</a></h2><p>相信笔者扯了那么多，大家对 Hooks 应该产生了一些兴趣，那什么是 Hooks 呢？简单来说，Hooks 就是一组在 React 组件中运行的函数，让你在不编写 Class 的情况下使用 state 及其它特性。具体来说，Hooks 可以表现为以下的形式：</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="usestate-与内部状态"><code>useState</code> 与内部状态<a href="#usestate-与内部状态" class="hash-link" aria-label="usestate-与内部状态的直接链接" title="usestate-与内部状态的直接链接">​</a></h3><p>我们可以看一个原生小程序的简单案例，一个简单计数器组件，点击按钮就 + 1，相信每位前端开发朋友都可以轻松地写一个计数器组件。但我们可以稍微改一下代码，把事件处理函数改为箭头函数。如果是这样代码就跑不了了。事实上在原生开发中 <code>this</code> 的问题是一以贯之的，所以我们经常要开个新变量把 <code>this</code> 缓存起来，叫做 <code>self</code> 什么的来避免类似的问题。我们之前也提到过，如果采用 ES6 的 Class 来组织组件同样也会遇到 <code>this</code> 指向不清晰的问题。</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function maybe-class-name" style="color:#d73a49">Page</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">data</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">count</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">increment</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 这里写箭头函数就跑不了了</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">count</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">count</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>再来看看我们的 hooks 写法，我们引入了一个叫 useState 的函数，它接受一个初始值参数，返回一个元组，如果是写后端的同学应该对这个模式比较熟悉，就像 Koa 或者 Go 一样，一个函数返回两个值或者说叫一个元组，不过我们返回的第一个参数是当前的状态，一个是设置这个状态的函数，每次调用这个设置状态的 <code>setState</code> 函数都会使得整个组件被重新渲染。然后用 ES6 的结构语法把它俩解构出来使用。</p><p>然后我们在定义一个增加的函数，把他绑定到 <code>onClick</code> 事件上。</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">Counter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 返回一个值和一个设置值的函数</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 每次设置值之后会重新渲染组件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setCount</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">increment</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">setCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">count </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">You clicked </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"> times</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">increment</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">Click me</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>同样是非常简单的代码。如果你熟悉 Taro 之前的版本的话就会知道这样的代码在以前的 Taro 是跑不了的，不过 Taro 1.3 之后事件传参可以传入任何合法值，你如果想直接写箭头函数或者写一个柯里化的函数也是完全没有问题的。</p><p>大家可以发现我们使用的 Hooks 就是一个非常简单非常 normal 的函数，没有 <code>this</code> 没有 class，没有类构造函数，没有了 <code>this</code>，再也不会出现那种 <code>this</code>、<code>self</code> 傻傻分不清楚的情况。</p><p>大家可以记住这个简单的计数器组件，以后之后讲的很多案例是基于这个组件做的。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="useeffect-与副作用"><code>useEffect</code> 与副作用<a href="#useeffect-与副作用" class="hash-link" aria-label="useeffect-与副作用的直接链接" title="useeffect-与副作用的直接链接">​</a></h3><p>接下来我们看一个稍微复杂一些的例子，一个倒计时组件，我们点击按钮就开始倒计时，再点击就停止倒计时。在我们这个组件里有两个变量，<code>start</code> 用于控制是否开始计时，<code>time</code> 就是我们的倒计时时间。这里注意我们需要多次清除 <code>interval</code>，而在现实业务开发中，这个 <code>touchStart</code> 函数可能会复杂得多，一不小心就会提前清除 <code>interval</code> 或忘记清除。</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function maybe-class-name" style="color:#d73a49">Page</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">data</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">time</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">start</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">toggleStart</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">start</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">start</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">start</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">interval</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">setInterval</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token literal-property property" style="color:#36acaa">time</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">time</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">else</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">clearInterval</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">interval</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">onUnload</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">clearInterval</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">interval</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="language-html codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-html codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">bindtap</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">toggleStart</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">{{time}}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>而我们 Hooks 的例子会是这样：我们引入了一个 <code>useEffect</code> 函数。之前我们提到过，每次调用 <code>useState</code> 返回的 <code>setState</code> 函数都会重新调用整个函数，其实就包括了 <code>useEffect</code> 函数，<code>useEffect</code> 接受两个参数。第一个就是副作用，也就是 <code>effect</code> 函数，他不接受也不返回任何参数。
第二个参数是依赖数组，当数组中的变量变化时就会调用，第一个参数 <code>effect</code> 函数。
<code>Effect</code> 函数还可以返回一个函数，这个函数在下一次 <code>effect</code> 函数被调用时或每次组件被注销时或者就会调用，我们可以在这里清楚掉一些事件的订阅或者 interval 之类可能会导致内存泄露的行为。
在我们这个例子中，当 <code>start</code> 每次变化就会重新跑一次 <code>effect</code> 函数，每隔一秒会设置一次 <code>time</code> 的值让它减一，但这样的写法是有问题的。</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">Counter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setStart</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setTime</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">60</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">useEffect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// effect 函数，不接受也不返回任何参数</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> interval</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      interval </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">setInterval</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// setTime(time - 1) ❌ time 在 effect 闭包函数里是拿不到准确值的</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">setTime</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">t</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> t </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// ✅ 在 setTime 的回调函数参数里可以拿到对应 state 的最新值</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">clearInterval</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">interval</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// clean-up 函数，当前组件被注销时调用</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 依赖数组，当数组中变量变化时会调用 effect 函数</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript function" style="color:#d73a49">setStart</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript operator" style="color:#393A34">!</span><span class="token tag script language-javascript" style="color:#00009f">start</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>因为我们在 <code>setInterval</code> 这个函数的闭包中，我们捕捉到 <code>time</code> 这个变量的值不能和最新的值对应得上，<code>time</code> 的值有可能在我们意料之外地被更改了多次。解决的方案也很简单，之前我们提到过 <code>useState</code> 返回的 <code>setState</code> 方法，可以接受一个函数作为参数，而这个函数的参数，就是 <code>state</code> 最新的值，所以只要我们传入一个函数就好了。这是其中一种方法。</p><p>还有另一种方法是使用 <code>useRef</code> Hooks，<code>useRef</code> 可以返回一个可变的引用，它会生成一个对象，对象里这个有 <code>current</code> 属性，而 <code>current</code> 的值是可变的。在我们这个例子里，每次更改 <code>currentTime.current</code> 都是同步的，而且 <code>currentTime</code> 是一个引用，所以 <code>currentTime.current</code> 一定是可控的。</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">Counter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setStart</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setTime</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">60</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> currentTime </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useRef</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 生成一个可变引用</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">useEffect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// effect 函数，不接受也不返回任何参数</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> interval</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      interval </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">setInterval</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">setTime</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">currentTime</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">current</span><span class="token operator" style="color:#393A34">--</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// currentTime.current 是可变的</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">clearInterval</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">interval</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// clean-up 函数，当前组件被注销时调用</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 依赖数组，当数组中变量变化时会调用 effect 函数</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript function" style="color:#d73a49">setStart</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript operator" style="color:#393A34">!</span><span class="token tag script language-javascript" style="color:#00009f">start</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>虽然说我们可以 <code>useRef</code> 来解决这个问题，但是没必要这样做。因为 <code>setTime</code> 传递一个回调函数的方法显然可读性更高。真正有必要的是把我们的 <code>interval</code> 变量作为一个 ref，我们在函数最顶层的作用域把 <code>interval</code> 作为一个 ref，这样我们就可以在这个函数的任何一个地方把他清除，而原来的代码中我们把 <code>interval</code> 作为一个普通的变量放在 effect 函数里，这样如果我们有一个事件也需要清除 interval，这就没法做到了。但是用 <code>useRef</code> 生成可变引用就没有这个限制。</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">Counter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setStart</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setTime</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">60</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> interval </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useRef</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// interval 可以在这个作用域里任何地方清除和设置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">useEffect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// effect 函数，不接受也不返回任何参数</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      interval</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">current</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">setInterval</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">setTime</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">t</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> t </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// ✅ 在 setTime 的回调函数参数里可以拿到对应 state 的最新值</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">clearInterval</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">interval</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">current</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// clean-up 函数，当前组件被注销时调用</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 依赖数组，当数组中变量变化时会调用 effect 函数</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript function" style="color:#d73a49">setStart</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript operator" style="color:#393A34">!</span><span class="token tag script language-javascript" style="color:#00009f">start</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="usecontext-与跨组件通信"><code>useContext</code> 与跨组件通信<a href="#usecontext-与跨组件通信" class="hash-link" aria-label="usecontext-与跨组件通信的直接链接" title="usecontext-与跨组件通信的直接链接">​</a></h3><p>接下来我们再来看一个跨组件通信的例子，例如我们有三个组件，<em>page</em> 组件有一个 <em>child</em> 组件，<em>child</em> 组件有一个 <em>counter</em> 组件，而我们 <em>counter</em> 组件的 <code>count</code> 值和 <code>setCount</code> 函数，是由 <em>page</em> 组件传递下来的。这种情况在一个复杂业务的开发中也经常能遇到，在原生小程序开发中我们应该怎么做呢？</p><p>我们需要手动的把我们 <code>counter</code> 的值和函数手动地依次地传递下去，而这样的传递必须是显式的，你需要在 JavaScript 中设置 props 的参数，也需要在 WXML 里设置 props 的参数，一个也不能少，少了就跑不动。我们还注意到即便 child 组件没有任何业务逻辑，他也必须要设置一个 <code>triggerEvent</code> 的函数和 props 的类型声明。这样的写法无疑是非常麻烦而且限制很大的。</p><div class="language-html codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-html codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- page.wxml --&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">child</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- child.wxml --&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">counter</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- counter.wxml --&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    You clicked {{count}} times</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">butto</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">bindtap</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">increment</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Click me</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// page.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function maybe-class-name" style="color:#d73a49">Page</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">data</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">count</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">increment</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">count</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">count</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// child.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function maybe-class-name" style="color:#d73a49">Component</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">properties</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">count</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token known-class-name class-name">Number</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">methods</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">increment</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">triggerEvent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'increment'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// counter.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function maybe-class-name" style="color:#d73a49">Component</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">properties</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">count</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token known-class-name class-name">Number</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">methods</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">increment</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">triggerEvent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'increment'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>而我们可以看看 Hooks 的写法，首先我们用 <code>Taro.createContext</code> 创建一个 <code>context</code> 对象，在我们 <em>page</em> 组件里把我们的 <code>count</code> 和 <code>setCount</code> 函数作为一个对象传入到 <code>Context.Provider</code> 的 <code>value</code> 里。然后在我们的 <em>Counter</em> 组件，我们可以使用 <code>useContext</code> 这个 Hooks 把我们的 <code>count</code> 和 <code>setCount</code> 取出来，就直接可以使用了。</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">CounterContext</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token maybe-class-name">Taro</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createContext</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// page.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function maybe-class-name" style="color:#d73a49">Page</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setCount</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">CounterContext.Provider</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">value</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f"> count</span><span class="token tag script language-javascript punctuation" style="color:#393A34">,</span><span class="token tag script language-javascript" style="color:#00009f"> setCount </span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Child</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">CounterContext.Provider</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// child.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function maybe-class-name" style="color:#d73a49">Child</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Counter</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// counter.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function maybe-class-name" style="color:#d73a49">Counter</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> count</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setCount </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useContext</span><span class="token punctuation" style="color:#393A34">(</span><span class="token maybe-class-name">CounterContext</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">You clicked </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"> times</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript function" style="color:#d73a49">setCount</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript" style="color:#00009f">count </span><span class="token tag script language-javascript operator" style="color:#393A34">+</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript number" style="color:#36acaa">1</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">Click me</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>大家可以发现使用 Context 的代码比原来的代码精简了很多，参数不需要一级一级地显式传递，<em>child</em> 组件也和事实一样，没有一行多余的逻辑。但精简不是最大的好处。最大的好处是大家可以发现我们的 Context 可以传递一个复杂的对象，熟悉小程序原生开发的同学可能会知道，所有 props 的传递都会被小程序序列化掉，如果传递了一个复杂的对象最终会变成一个 JSON。但是用 Taro 的 context 则没有这层限制，你可以传入一个带有函数的对象，也可以传入像是 <code>imutabale</code> 或者 <code>obserable</code> 这样复杂的对象。在 taro 1.3 我们对 props 系统进行了一次重构，Taro 的 context 和 props 一样，属性传递没有任何限制，想传啥就传啥。</p><p>另外一个值得注意的点的是，context 的传递可以无视父级组件的更新策略，在这个例子中即便我们通过 <code>shouldComponentUpdate()</code> 禁止了 <em>child</em> 组件的更新，但 <em>counter</em> 作为它的子组件依然是可以更新的。这个特性可以让我们做性能优化的时候更为灵活一些。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="hooks-在小程序实战">Hooks 在小程序实战<a href="#hooks-在小程序实战" class="hash-link" aria-label="Hooks 在小程序实战的直接链接" title="Hooks 在小程序实战的直接链接">​</a></h2><p>讲完了 Hooks 的基本使用，有些同学会觉得：咦，我怎么觉得你这几个东西感觉平平无奇，没什么特别的。但实际上这些基础的 Hooks 单独拿出来看的确不能玩出什么花样，但他们组合起来却能迸发强大的力量。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="自定义-hooks">自定义 Hooks<a href="#自定义-hooks" class="hash-link" aria-label="自定义 Hooks的直接链接" title="自定义 Hooks的直接链接">​</a></h3><p>大家在业务开发可能会遇到这样的需求，实现一个双击事件，如果你是从 H5 开发过来的可能会直接写 <code>onDoubleClick</code>，但很遗憾，小程序组件是没有 <code>doubleClick</code> 这个事件的。当然，如果你使用 Taro 又用了 TypeScript 就不会犯这样的错误，编辑器就回直接给你报错 <code>Text</code> 组件没有这个属性。</p><p>于是你就自己实现了一个双击事件，代码大概是这样，有一个上次点击的时间作为状态，每次触发单机事件的时候和上次点击的时间做对比，如果间隔过小，那他就是一个双击事件。代码非常简单，但我们不禁就会产生一个问题问题，每一次给一个组件加单击事件，我们就每次都加这么一坨代码吗？</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">EditableText</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter"> title </span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">lastClickTime</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setClickTime</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">editing</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setEditing</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">editing </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">TextInput</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">editing</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">editing</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">          </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript parameter" style="color:#00009f">e</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">            </span><span class="token tag script language-javascript keyword" style="color:#00009f">const</span><span class="token tag script language-javascript" style="color:#00009f"> currentTime </span><span class="token tag script language-javascript operator" style="color:#393A34">=</span><span class="token tag script language-javascript" style="color:#00009f"> e</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">timeStamp</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">            </span><span class="token tag script language-javascript keyword" style="color:#00009f">const</span><span class="token tag script language-javascript" style="color:#00009f"> gap </span><span class="token tag script language-javascript operator" style="color:#393A34">=</span><span class="token tag script language-javascript" style="color:#00009f"> currentTime </span><span class="token tag script language-javascript operator" style="color:#393A34">-</span><span class="token tag script language-javascript" style="color:#00009f"> lastClickTime</span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">            </span><span class="token tag script language-javascript keyword control-flow" style="color:#00009f">if</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript" style="color:#00009f">gap </span><span class="token tag script language-javascript operator" style="color:#393A34">&gt;</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript number" style="color:#36acaa">0</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript operator" style="color:#393A34">&amp;&amp;</span><span class="token tag script language-javascript" style="color:#00009f"> gap </span><span class="token tag script language-javascript operator" style="color:#393A34">&lt;</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript number" style="color:#36acaa">300</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">              </span><span class="token tag script language-javascript comment" style="color:#999988;font-style:italic">// double click</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">              </span><span class="token tag script language-javascript function" style="color:#d73a49">setEditing</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript boolean" style="color:#36acaa">true</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">            </span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">            </span><span class="token tag script language-javascript function" style="color:#d73a49">setClickTime</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript" style="color:#00009f">currentTime</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag script language-javascript" style="color:#00009f">          </span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"></span><br></span><span class="token-line" style="color:#393A34"><span class="token tag" style="color:#00009f">        </span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">          </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">title</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>这个时候我们就可以写一个自定义 Hooks，代码和原来的代码也差不多，<code>useDoubleClick</code> 不接受任何参数，但当我们调用 <code>useDoubleClick</code> 时候返回一个名为 <code>textOnDoubleClick</code> 的函数，在在 Text 组件的事件传参中，我们再在 <code>textOnDoubleClick</code> 函数中传入一个回调函数，这个回调函数就是触发双击条件时候的函数。当我们给这个自定义 Hooks 做了柯里化之后，我们就可以做到知道 Hook 使用时才暴露回调函数:</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useDoubleClick</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">lastClickTime</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setClickTime</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">callback</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> currentTime </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">timeStamp</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> gap </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> currentTime </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> lastClickTime</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">gap </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> gap </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">300</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      callback </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">callback</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">setClickTime</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">currentTime</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">EditableText</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter"> title </span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">editing</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setEditing</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> textOnDoubleClick </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useDoubleClick</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">editing </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">TextInput</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">editing</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">editing</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript function" style="color:#d73a49">textOnDoubleClick</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript arrow operator" style="color:#393A34">=&gt;</span><span class="token tag script language-javascript" style="color:#00009f"> </span><span class="token tag script language-javascript function" style="color:#d73a49">setEditing</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript boolean" style="color:#36acaa">true</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">title</span><span class="token punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>柯里化函数好像有一点点绕，但一旦我们完成了这一步，这种我们的自定义 hooks 就可以像多次调用：</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">EditableText</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter"> title </span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> textOnDoubleClick </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useDoubleClick</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> buttonOnDoubleClick </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useDoubleClick</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 任何实现单击类型的组件都有自己独立的双击状态</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript function" style="color:#d73a49">textOnDoubleClick</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript spread operator" style="color:#393A34">...</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">title</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Text</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript function" style="color:#d73a49">buttonOnDoubleClick</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript spread operator" style="color:#393A34">...</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>每一个大家不妨试想如果按照我们传统的 render props 实现，每次都要多写一个 container 组件，如果用 Mixins 或高阶组件来实现就更麻烦，我们需要基于每个不同类型的组件创造一个新的组件。而使用 Hooks，任何一个实现了单机类型的组件都可以通过我们的自定义 Hook 实现双击效果，不管从它的内部实现来看，还是从它暴露的 API 来看都是非常优雅的。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="性能优化">性能优化<a href="#性能优化" class="hash-link" aria-label="性能优化的直接链接" title="性能优化的直接链接">​</a></h3><p>接下来我们谈一下性能优化，相信大家也有过这种情况，有一个数组，他只需拿到他的 props 要渲染一次，从此之后他就再也不需要更新了。对于传统而言的 Class Component 我们可以设置 <code>shouldComponentUpdate()</code> 返回 <code>false</code>。</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Numbers</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">shouldComponentUpdate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">render</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token function" style="color:#d73a49">expensive</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">props</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">array</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">i</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">i</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>而对于函数式组件而言，我们也可以做一样的事情。Taro 和 React 一样提供 <code>Taro.memo</code> API，他的第一个参数接受一个函数式组件，第二个参数和我们的 <code>shouldComponentUpdate()</code> 一样，判断组件在什么样的情况下需要更新。如果第二个参数没有传入的话，<code>Taro.memo</code> 的效果就和 <code>Taro.PureComponent</code> 一样，对新旧 props 做一层浅对比，如果浅对比不相等则更新组件。</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">Numbers</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter"> array </span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token function" style="color:#d73a49">expensive</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">array</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">i</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">i</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token maybe-class-name">Taro</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">memo</span><span class="token punctuation" style="color:#393A34">(</span><span class="token maybe-class-name">Numbers</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>第二种情况我们可以看看我们的老朋友，计数器组件。但是这个计数器组件和老朋友有两点不一样：第一是每次点击 + 1，计数器需要调用 <code>expensive</code> 函数循环 1 亿次才能拿到我们想要的值，第二点是它多了一个 <code>Input</code> 组件。在我们真实的业务开发中，这种情况也很常见：我们的组件可能需要进行一次昂贵的数据处理才能得到最终想要的值，但这个组件又还有多个 state 控制其它的组件。在这种情况下，我们如果正常书写业务逻辑是有性能问题的：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">Counter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setCount</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">val</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setValue</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">''</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">expensive</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> sum </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">for</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> i </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> i </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> count </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1e9</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> i</span><span class="token operator" style="color:#393A34">++</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      sum </span><span class="token operator" style="color:#393A34">+=</span><span class="token plain"> i</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> sum</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Text</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token maybe-class-name">You</span><span class="token plain"> clicked </span><span class="token punctuation" style="color:#393A34">{</span><span class="token function" style="color:#d73a49">expensive</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> times</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">Text</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Button</span><span class="token plain"> onClick</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">setCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">count </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token maybe-class-name">Click</span><span class="token plain"> me</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">Button</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Input</span><span class="token plain"> value</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">val</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> onChange</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">setValue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">detail</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">value</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>因为我们 <code>count</code> 的值跟 <code>Input</code> 的值没有关系，但我们每次改变 <code>Input</code> 的值，就会触发这个组件重新渲染。也就是说这个循环一亿次的 <code>expensive()</code> 函数就会重新调用。这样情况显然是不能接受的。为了解决这个问题，我们可以使用 <code>useMemo</code> API。<code>useMemo</code> 的签名和 <code>useEffect</code> 有点像，区别就在于 <code>useMemo</code> 的第一个函数有返回值，这个函数返回的值同时也是 <code>useMemo</code> 函数的返回值。而第二个参数同样是依赖数组，只有当这个数组的数据变化时候，<code>useMemo</code> 的函数才会重新计算，如果数组没有变化，那就直接从缓存中取数据。在我们这个例子里我们只需要 <code>count</code> 变化才进行计算，而 Input value 变化无需计算。</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#d73a49">Counter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setCount</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">val</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setValue</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">''</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> expensive </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useMemo</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> sum </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">for</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> i </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> i </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> count </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> i</span><span class="token operator" style="color:#393A34">++</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      sum </span><span class="token operator" style="color:#393A34">+=</span><span class="token plain"> i</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> sum</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// ✅ 只有 count 变化时，回调函数才会执行</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Text</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token maybe-class-name">You</span><span class="token plain"> </span><span class="token maybe-class-name">Clicked</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">expensive</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> times</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">Text</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Button</span><span class="token plain"> onClick</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">setCount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">count </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token maybe-class-name">Click</span><span class="token plain"> me</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">Button</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Input</span><span class="token plain"> value</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">val</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> onChange</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">setValue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">detail</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">value</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>我们刚才提到的两个 memo 的 API ，他的全称其实是 <em>Memoization</em>。由于 Hooks 都是在普通函数中运行的，所以我们要做好性能优化，一定要好好利用缓存和记忆化这一技术。</p><blockquote><p>在计算机科学中，记忆化（Memoization）是一种提高程序运行速度的优化技术。通过储存大计算量函数的返回值，当这个结果再次被需要时将其从缓存提取，而不用再次计算来节省计算时间。</p></blockquote><h3 class="anchor anchorWithStickyNavbar_mCi1" id="大规模状态管理">大规模状态管理<a href="#大规模状态管理" class="hash-link" aria-label="大规模状态管理的直接链接" title="大规模状态管理的直接链接">​</a></h3><p>提到状态管理，React 社区最有名的工具当然是 Redux。在 <code>react-redux@7</code> 中新引用了三个 API:</p><ol><li><code>useSelector</code>。它有点像 <code>connect()</code> 函数的第一个参数 <code>mapStateToProps</code>，把数据从 state 中取出来；</li><li><code>useStore</code> 。返回 <code>store</code> 本身；</li><li><code>useDispatch</code>。返回 <code>store.dispatch</code>。</li></ol><p>在 Taro 中其实你也可以使用我们之前提到过的 <code>createContext</code> 和 <code>useContext</code> 直接就把 <code>useStore</code> 和 <code>useDispatch</code> 实现了。而基于 <code>useStore</code> 和 <code>useDispatch</code> 以及 <code>useState</code>，<code>useMemo</code>，<code>useEffect</code> 也可以实现 <code>useSelector</code>。也就是说 <code>react-redux@7</code> 的新 API 全都是普通 Hooks 构建而成的自定义 Hooks。当然我们也把 <code>react-redux@7</code> 的新功能移植到了 <code>@tarojs/redux</code>，在 Taro 1.3 版本你可以直接使用这几个 API。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="hooks-的实现">Hooks 的实现<a href="#hooks-的实现" class="hash-link" aria-label="Hooks 的实现的直接链接" title="Hooks 的实现的直接链接">​</a></h2><p>我们现在对 Hooks 已经有了以下的了解，一个合法的 Hooks ，必须满足以下需求才能执行:</p><ul><li>只能在函数式函数中调用</li><li>只能在函数最顶层中调用</li><li>不能在条件语句中调用</li><li>不能在循环中调用</li><li>不能在嵌套函数中调用</li></ul><p>我想请大家思考一下，为什么一个 Hook 函数需要满足以上的需求呢？我想请大家以可以框架开发者的角度去思考下这个问题，而不是以 API 的调用者的角度去逆向地思考。当一个 Hook 函数被调用的时，这个 Hook 函数的内部实现应该可以访问到当前正在执行的组件，但是我们的 Hooks API 的入参却没有传入这个组件，那究竟是怎么样的设计才可以让我们的 hook 函数访问到正在执行的组件，也能够准确地定位自己呢？</p><p>聪明的朋友或许已经猜到了，这些所有线索都指向一个结果，<strong>Hooks 必须是一个按顺序执行的函数</strong>。也就是说，不管整个组件执行多少次，渲染多少次，组件中 Hooks 的顺序都是不会变的。</p><p>我们还知道另外一条规则，Hooks 是 React 函数内部的函数，于是我们就可以知道，要实现 Hooks 最关键的问题在于两个:</p><ol><li>找到正在执行的 React 函数</li><li>找到正在执行的 Hooks 的顺序。</li></ol><p>我们可以设置一个全局的对象叫 <code>CurrentOwner</code>，它有两个属性，第一个是 <code>current</code>，他是正在执行的 <code>Taro</code> 函数，我们可以在组件加载和更新时设置它的值，加载或更新完毕之后再设置为 <code>null</code>；第二个属性是 <code>index</code>，它就是 <code>CurrentOwner.current</code> 中 Hooks 的顺序，每次我们执行一个 Hook 函数就自增 1。</p><div class="language-typescript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-typescript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> CurrentOwner</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  current</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">null</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> Component</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token builtin">any</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">any</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  index</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">number</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 正在执行的 Taro 函数,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 在组件加载和重新渲染前设置它的值</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  current</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Taro 函数中 hooks 的顺序</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 每执行一个 Hook 自增</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  index</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在 React 中其实也有这么一个对象，而且你还可以使用它，它叫做 <code>__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner</code>，也就是说如果你想给 React 15 实现 Hooks，其实也可以做到的。但也正如它的名字一样，如果你用了说不定就被 fire 了，被优化了，所以更好的方案还是直接使用我们 taro。</p><p>接下来我们来实现我们的 <code>getHook</code> 函数，同样很简单，如果 <code>CurrenOwner.current</code> 是 <code>null</code>，那这就不是一个合法的 hook 函数，我们直接报错。如果满足条件，我们就把 hook 的 <code>index</code> + 1，接下来我们把组件的 Hooks 都保存在一个数组里，如果 <code>index</code> 大于 Hooks 的长度，说明 Hooks 没有被创造，我们就 push 一个空对象，避免之后取值发生 runtime error。然后我们直接返回我们的 Hook。</p><div class="language-typescript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-typescript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getHook</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Hook </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">CurrentOwner</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">current </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">throw</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Error</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">invalid hooks call: hooks can only be called in a taro component.</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> index </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> CurrentOwner</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">index</span><span class="token operator" style="color:#393A34">++</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// hook 在该 Taro 函数中的 ID</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> hooks</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Hook</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> CurrentOwner</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">current</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">hooks </span><span class="token comment" style="color:#999988;font-style:italic">// 所有的 hooks</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">index </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> hooks</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">length</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 如果 hook 还没有创建</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    hooks</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">push</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> Hook</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 对象就是 hook 的内部状态</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> hooks</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">index</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 返回正在执行的 hook 状态</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>既然我们已经找到了我们正在执行的 Hooks，完整地实现 Hooks 也就不难了。之前我们讨论过 <code>useState</code> 的签名，现在我们一步一步地看他的实现。</p><p>首先如果 <code>initState</code> 是函数，直接执行它。其次调用我们我们之前写好的 <code>getHook</code> 函数，它返回的就是 Hook 的状态。接下来就是 useState 的主逻辑，如果 hook 还没有状态的话，我们就先把正在执行的组件缓存起来，然后 <code>useState</code> 返回的，就是我们的 <code>hook.state</code>, 其实就是一个数组，第一个值当然就是我们 <code>initState</code>，第一个参数是一个函数，它如果是一个函数，我们就执行它，否则就直接把参数赋值给我们 的 <code>hook.state</code> 第一个值，赋值完毕之后我们把当前的组件加入到更新队列，等待更新。</p><div class="language-typescript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-typescript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token generic-function function" style="color:#d73a49">useState</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name constant" style="color:#36acaa">S</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">initialState</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">S</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">S</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token constant" style="color:#36acaa">S</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Dispatch</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">SetStateAction</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token constant" style="color:#36acaa">S</span><span class="token operator" style="color:#393A34">&gt;&gt;</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">isFunction</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">initialState</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 如果 initialState 是函数</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    initialState </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">initialState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 就直接执行</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> hook </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getHook</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> HookState</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token constant" style="color:#36acaa">S</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 找到该函数中对应的 hook</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">isUndefined</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">hook</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">state</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 如果 hook 还没有状态</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    hook</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">component </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Current</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">current</span><span class="token operator" style="color:#393A34">!</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 正在执行的 Taro 函数，缓存起来</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    hook</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">state </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// hook.state 就是我们要返回的元组</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      initialState</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">action</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        hook</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">state</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">isFunction</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">action</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">action</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">hook</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">state</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> action</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">enqueueRender</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">hook</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">component</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 加入更新队列</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> hook</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">state </span><span class="token comment" style="color:#999988;font-style:italic">// 已经创建 hook 就直接返回</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>最后我们把 <code>hook.state</code> 返回出去就大功告成了。</p><p>Taro 的 Hooks 总共有八个 API， <code>useState</code> 的实现大家可以发现非常简单，但其实它的代码量和复杂度是所有 Hooks 的实现中第二高的。所以其实 Hooks 也没有什么黑科技，大家可以放心大胆地使用。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="总结与展望">总结与展望<a href="#总结与展望" class="hash-link" aria-label="总结与展望的直接链接" title="总结与展望的直接链接">​</a></h2><p>在 2018 年 Ember.js 的作者提出过一个观点，<em>Compilers are the New Frameworks</em>，编译器即框架。什么意思呢？就拿 React 来举例，单单一个 React 其实没什么用，你还需要配合 <em>create-react-app</em>, <em>eslint-plugin-react-hooks</em>, <em>prettier</em> 等等编译相关的工具最终才能构成一个框架，而这些工具也恰巧是 React Core Team 的人创造的。而这样趋势不仅仅发生在 React 身上，大家可以发现在 2018 年，尤玉溪老师的主要工作就是开发 <em>vue-cli</em>。而对一些更激进的框架，例如 <em>svelte</em>，它的框架就是编译器，编译器就是框架。</p><p>而到了 2019 年，我想提出一个新概念，叫框架即生态。就拿 Taro 来说，使用 Taro 你可以复用 React 生态的东西，同时 Taro 还有 <code>taro doctor</code>，Taro 开发者社区，Taro 物料市场，还有腾讯小程序·云开发等等多个合作伙伴一起构成了 Taro 生态，而整个 Taro 生态才是框架。在过去的半年，我们持续改进并优化了 Taro 框架的表现，以上提到的特性与功能在 Taro 1.3 全部都可以正常使用。而在框架之外，我们也深耕社区，推出了 Taro 物料市场和 Taro 开发者社区，并和腾讯小程序·云开发合作举办了物料开发竞赛。现在，我们诚挚邀请你一起来参与社区贡献，让小程序开发变得更好、更快、更方便：</p><ul><li>Taro 官网：<a href="https://taro.jd.com/" target="_blank" rel="noopener noreferrer">https://taro.jd.com/</a></li><li>Taro 物料市场：<a href="https://taro-ext.jd.com/" target="_blank" rel="noopener noreferrer">https://taro-ext.jd.com/</a></li><li>Taro 开发者社区：<a href="https://taro-club.jd.com/" target="_blank" rel="noopener noreferrer">https://taro-club.jd.com/</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Taro 「物料市场」及「交流社区」 惊喜上线]]></title>
            <link>https://docs.taro.zone/blog/2019-06-21-taro-ext-club</link>
            <guid>https://docs.taro.zone/blog/2019-06-21-taro-ext-club</guid>
            <pubDate>Fri, 21 Jun 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[我们不仅带来了焕然一新的 Taro 官网，还一次性推出了 Taro 物料市场 和 Taro 开发者社区。帮助 Taro 实现从 多端统一开发框架 到 多端统一开发解决方案 的转型。]]></description>
            <content:encoded><![CDATA[<p>我们不仅带来了焕然一新的 <a href="https://taro.jd.com" target="_blank" rel="noopener noreferrer">Taro 官网</a>，还一次性推出了 <a href="https://taro-ext.jd.com" target="_blank" rel="noopener noreferrer">Taro 物料市场</a> 和 <a href="https://taro-club.jd.com" target="_blank" rel="noopener noreferrer">Taro 开发者社区</a>。帮助 Taro 实现从 <strong>多端统一开发框架</strong> 到 <strong>多端统一开发解决方案</strong> 的转型。</p><p>Taro 物料市场：<a href="https://taro-ext.jd.com/" target="_blank" rel="noopener noreferrer">https://taro-ext.jd.com/</a>
Taro 开发者社区：<a href="https://taro-club.jd.com/" target="_blank" rel="noopener noreferrer">https://taro-club.jd.com/</a></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="物料市场--让每一个轮子产生价值">物料市场 — “让每一个轮子产生价值”<a href="#物料市场--让每一个轮子产生价值" class="hash-link" aria-label="物料市场 — “让每一个轮子产生价值”的直接链接" title="物料市场 — “让每一个轮子产生价值”的直接链接">​</a></h2><p>随着 Taro 受到越来越多的关注，许多 Taro 项目也正式投入生产，涵盖包括 电商、工具、服务 等多种类型，也涌现了许多优秀案例。</p><p>但项目开发中，开发者都不得不面临重复的逻辑开发，对现有的逻辑进行 Taro 代码的适配等问题，严重影响开发效能。</p><p><strong>开源的时代，我们不希望闭门造车，我们诚邀开发者们共同打造完整的 多端适配 生态，一起沉淀优秀的开发物料，进而赋能更多的开发者。</strong></p><p>因此，物料市场应运而生。</p><p>我们将物料划分为 4 个大类：组件、UI 库、模版、SDK。每个物料都配有说明文档，并对支持的端进行标记，方便接入使用。</p><p>物料的获取方式也相当灵活，可通过 ZIP/TAR 包下载，也可直接通过作者提供的 NPM 或 GitHub 地址下载。</p><p>此外，还可以对物料进行评论、收藏和打分，这将作为衡量物料质量的一个指标，为筛选物料提供参考。</p><p><img loading="lazy" src="https://img12.360buyimg.com/ling/jfs/t1/60468/2/4173/311061/5d249803Ef06153d5/bf6c7bf4ae9deda0.png" alt="https://taro-ext.jd.com" class="img_ZOZK"></p><p>后续，我们将加入打赏功能，开发者通过发布优质的物料，给与别人便利的同时，自己可以收获一定的奖励。此外，我们还会不断完善物料质量评定标准，制定用户积分体系，等等。这都将促进整个生态更加健康而全面的发展。</p><blockquote><p>如果你对物料市场有任何建议，或有意愿参与物料市场的建设，欢迎随时与我们联系：<a href="mailto:taro@jd.com" target="_blank" rel="noopener noreferrer">taro@jd.com</a>。</p></blockquote><h2 class="anchor anchorWithStickyNavbar_mCi1" id="开发者社区--让每一次交流被沉淀">开发者社区 — “让每一次交流被沉淀”<a href="#开发者社区--让每一次交流被沉淀" class="hash-link" aria-label="开发者社区 — “让每一次交流被沉淀”的直接链接" title="开发者社区 — “让每一次交流被沉淀”的直接链接">​</a></h2><p>目前，Taro 共收到了 2k 多个 GitHub Issue，并建立了十多个开发交流微信群（每个群上限人数 500）。</p><p>我们希望与开发者进行及时且顺畅的交流，更希望每一次的问题答疑、经验交流都能被沉淀，分享给更多开发者，同时，我们渴望开发者之间可以开放互助，因此我们打造了一个全新的开发者社区。</p><p>开发者可通过社区多个版块交流问题、共享经验、发布案例，等等。</p><p>Taro 社区基于 NodeBB 开发，共设有 12 个版块，包括 Taro、Taro UI 及其对应转换的 8 个端，有效地对内容做了归类。此外，还设置了 新闻 版块，用于发布 Taro 新闻、公告、规划等；招聘 版块，让 Taro 求职者和招聘者快速匹配；灌水 版块，提供一个健康文明的交流（you）平台。</p><p><img loading="lazy" src="https://img30.360buyimg.com/ling/jfs/t1/44845/24/4669/389115/5d249802Ea7d24923/6652e638c889128c.png" alt="https://taro-club.jd.com" class="img_ZOZK"></p><p>为了加强社区内容的管理，我们沉淀了一套自动化的社区管理解决方案：通过智能分析微信群聊记录，对有效信息进行筛选和整合，自动同步至社区；并使用插件 GitHub Embed 将 GitHub Issue 直接导入，从而打通 GitHub、微信、社区 三方平台，旨在让社区内容更及时、更全面。</p><blockquote><p>如果你对开发者社区有任何建议，或有意愿参与社区的建设与管理工作，欢迎随时与我们联系：<a href="mailto:taro@jd.com" target="_blank" rel="noopener noreferrer">taro@jd.com</a>。</p></blockquote><h2 class="anchor anchorWithStickyNavbar_mCi1" id="官网--全新面貌蓄势待发">官网 — “全新面貌，蓄势待发”<a href="#官网--全新面貌蓄势待发" class="hash-link" aria-label="官网 — “全新面貌，蓄势待发”的直接链接" title="官网 — “全新面貌，蓄势待发”的直接链接">​</a></h2><p>开源近一年，Taro 成长了许多。一开始，我们试图用 React 来实现微信小程序开发；而现在，我们期待的是，Taro 在移动端开发、多端适配领域可以无往不利。</p><p>Taro 正在经历由内而外的成长，因此，我们对 Taro 品牌进行了全新升级，旨在赋予其独特的品牌风格。</p><p>全新的官网将作为品牌化的一个窗口，不仅带来了全新的视觉体验，内容上更是增加了生态能力和业界合作的展示，并丰富了应用案例和学习资料，方便开发者迅速上手 Taro。</p><p><img loading="lazy" src="https://img30.360buyimg.com/ling/jfs/t1/74056/33/4068/719706/5d249804E8e1ee173/c6196956418ed998.png" alt="https://taro.jd.com" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="愿景">愿景<a href="#愿景" class="hash-link" aria-label="愿景的直接链接" title="愿景的直接链接">​</a></h2><p>这一切仅仅是起步，未来我们还将推出更多平台、工具、服务，努力将 Taro 打造为名副其实的移动端解决方案。</p><p>希望通过我们的点滴努力，创造出更好的移动端开发环境，让工作回归简单、让生活日益丰满。</p><p>相关网址：</p><ul><li>Taro 官网：<a href="https://taro.jd.com" target="_blank" rel="noopener noreferrer">https://taro.jd.com</a></li><li>Taro 物料市场: <a href="https://taro-ext.jd.com" target="_blank" rel="noopener noreferrer">https://taro-ext.jd.com</a></li><li>Taro 社区：<a href="https://taro-club.jd.com" target="_blank" rel="noopener noreferrer">https://taro-club.jd.com</a></li><li>GitHub: <a href="https://github.com/NervJS/taro" target="_blank" rel="noopener noreferrer">https://github.com/NervJS/taro</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Taro 1.3 震撼升级：全面支持 JSX 语法和 HOOKS]]></title>
            <link>https://docs.taro.zone/blog/2019-06-13-taro-1-3</link>
            <guid>https://docs.taro.zone/blog/2019-06-13-taro-1-3</guid>
            <pubDate>Thu, 13 Jun 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[在 Taro 1.2 发布之后，Taro 在业界收获了巨大的赞誉和关注：GitHub 上 Star 数量超过 19000 粒，NPM 下载量也稳居同类开发框架之首，同时 Taro 团队也和腾讯、百度、华为等数十家业界巨头的研发团队展开了深入和有效的合作。]]></description>
            <content:encoded><![CDATA[<p>在 Taro 1.2 发布之后，Taro 在业界收获了巨大的赞誉和关注：GitHub 上 Star 数量超过 19000 粒，NPM 下载量也稳居同类开发框架之首，同时 Taro 团队也和腾讯、百度、华为等数十家业界巨头的研发团队展开了深入和有效的合作。</p><p>Taro 1.3 是我们酝酿最久的版本：经历了横跨 6 个月的开发时间，近 2000 次的代码提交，近百位开发者的共同参与。我们终于在今天骄傲地发布了 Taro 1.3。</p><p>Taro 1.3 的特性包括但不限于：</p><ul><li>支持快应用和 QQ 小程序的开发</li><li>全面支持 JSX 语法和 React Hooks</li><li>大幅提高 H5 性能和可用性</li><li>Taro Doctor</li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="支持快应用和-qq-小程序的开发">支持快应用和 QQ 小程序的开发<a href="#支持快应用和-qq-小程序的开发" class="hash-link" aria-label="支持快应用和 QQ 小程序的开发的直接链接" title="支持快应用和 QQ 小程序的开发的直接链接">​</a></h2><p>快应用的开发模式非常特别，它的 API、组件系统、组件库和其他小程序端差异非常大，并且快应用只是一个标准，各家安卓厂商对运行时的实现也各不相同。而这块「硬骨头」终于也被 Taro 啃下了。</p><p>QQ 小程序作为新兴的小程序类容器，大家普遍对它知之甚少，但 Taro 也率先实现了对 QQ 小程序的支持。</p><p>支持快应用和 QQ 小程序意味着 Taro 真正对业界主流小程序实现了「全覆盖」，不管你的业务要支持哪一个小程序端，只要维护一套代码，Taro 就能生成对应小程序平台的代码。同时 Taro 也成为了业界首个同时支持微信小程序、百度智能小程序、抖音小程序、支付宝小程序、快应用、QQ 小程序共 6 端小程序的开发框架。</p><p><img loading="lazy" src="https://i.loli.net/2019/06/12/5d00a06c3241c16517.png" alt="taro.jd.com.png" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="全面支持-jsx-语法和-react-hooks">全面支持 JSX 语法和 React Hooks<a href="#全面支持-jsx-语法和-react-hooks" class="hash-link" aria-label="全面支持 JSX 语法和 React Hooks的直接链接" title="全面支持 JSX 语法和 React Hooks的直接链接">​</a></h2><p>作为使用 React 和 JSX 语法的开发框架，Taro 早期的版本在编译器和编辑器检查工具都对语法做了高强度的限制。而在 Taro 1.3 中，开发者可以充分发挥自己的创造力和想象力，可以任意地写 <code>if-else</code>，可以任意地写匿名函数，可以把 JSX 放在类函数中，也可以放在普通函数中，等等。只要编译器和和 ESLint 不报错，就可以这么写。</p><p>虽然 React Hooks 正式稳定的时间并不长，但我们认为这个特性能有效地简化开发模式，提升开发效率和开发体验。即便 Hooks 的生态和最佳实践还尚未完善，但我们相信未来 Hooks 会成为 React 开发模式的主流，也会深刻地影响其它框架未来的 API 构成。所以我们优先把 React Hooks 带到了 Taro 中，还写了两个小例子展示如何在 Taro 中使用 Hooks：</p><p>V2EX: <a href="https://github.com/NervJS/taro-v2ex-hooks" target="_blank" rel="noopener noreferrer">https://github.com/NervJS/taro-v2ex-hooks</a></p><p>TodoMVC: <a href="https://github.com/NervJS/taro-todomvc-hooks" target="_blank" rel="noopener noreferrer">https://github.com/NervJS/taro-todomvc-hooks</a></p><p><img loading="lazy" src="https://i.loli.net/2019/06/12/5d00a06a479f225719.png" alt="carbon.png" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="全新生命周期和-context-api">全新生命周期和 Context API<a href="#全新生命周期和-context-api" class="hash-link" aria-label="全新生命周期和 Context API的直接链接" title="全新生命周期和 Context API的直接链接">​</a></h2><p>在 Taro 1.3 我们还实现了 React 16 的新生命周期函数 <code>static getDerivedStateFromProps()</code> 和 <code>getSnapshotBeforeUpdate()</code>。当新的生命周期函数注入到类组件时，老的生命周期函数将不会被调用，没有使用新的生命周期函数则不会影响原有生命周期的调用。就多数情况而言，我们更推荐使用新的生命周期来构建你的类组件，因为这样能减少一次渲染和更新的开销。更多详细信息可以查看<a href="/docs/apis/about/tarocomponent#static-getderivedstatefromprops">相关文档</a>。</p><p>Taro 1.3 还实现了 React 16 的 <code>createContext</code>、<code>contextType</code> 和 <code>useContext</code> API。新 Context 通过声明式的 API 来传递组件的更新，使得 Taro 跨组件通信和共享状态更为直观。同时，例如 <code>react-redux</code> 这样的热门库也正在基于 <code>Context</code> 和 <code>Hooks</code> 进行重构，我们也非常期待与社区一起探索 React/Taro 新的开发与设计模式。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="大幅提高-h5-性能和可用性">大幅提高 H5 性能和可用性<a href="#大幅提高-h5-性能和可用性" class="hash-link" aria-label="大幅提高 H5 性能和可用性的直接链接" title="大幅提高 H5 性能和可用性的直接链接">​</a></h2><p>作为除微信小程序之外需求量最高的端，我们一直都部署了重要的开发战力在 H5 端。而在 Taro 1.3 中，我们优化了编译代码的方式，实现了资源最小引入和按需引入，将原有最小项目的编译大小降低了 80% 左右。这对于网络状况不佳的 H5 端无疑是巨大的提升。</p><p>H5 端的 API 数量和质量也得到了大幅地增长，Taro 1.3 新增了 28 个 H5 API，解决了上百个 H5 相关的 issue。</p><p>关于 H5 端性能更感兴趣可以查看文章:<a href="https://aotu.io/notes/2019/02/28/taro-h5-optimize/" target="_blank" rel="noopener noreferrer">《决战性能之巅 - Taro H5 转换与优化升级》</a>。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="taro-doctor">Taro Doctor<a href="#taro-doctor" class="hash-link" aria-label="Taro Doctor的直接链接" title="Taro Doctor的直接链接">​</a></h2><p>我们还从 Flutter Doctor 中得到启发，开发了 Taro Doctor。 Taro Doctor 就像一个医生一样，可以诊断项目的依赖、设置、结构，以及代码的规范是否存在问题，并尝试给出解决方案。</p><p>但和真正的医生不一样，Taro Doctor 不需要排队挂号，也不用花钱。你只需要在终端运行命令：<code>taro doctor</code>，就像图里一样：</p><p><img loading="lazy" src="https://i.loli.net/2019/04/19/5cb992ee1b2f1.png" alt="taro-doctor.png" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="还有更多">还有更多<a href="#还有更多" class="hash-link" aria-label="还有更多的直接链接" title="还有更多的直接链接">​</a></h2><p>除了以上的特性之外，Taro 1.3 还做了许多额外的工作，这些工作可能对日常开发影响不大，但为 Taro 的稳定性以及将来更多的可能性夯实了基础：</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="组件传参props系统重构">组件传参（props）系统重构<a href="#组件传参props系统重构" class="hash-link" aria-label="组件传参（props）系统重构的直接链接" title="组件传参（props）系统重构的直接链接">​</a></h3><p>在 Taro 1.0 到 1.2 的小程序端，我们一直使用原生小程序框架的组件传参系统，但小程序组件系统没办法传递函数的值，也无法传递非具名参数，并且各小程序组件的实现各不相同。为了解决这些问题，在 Taro 1.3 中我们自己实现了一套组件传参系统。新系统会使得传参相关的代码更为可靠，同时也是我们支持更多 JSX 语法的基础。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="命令行工具cli-重构">命令行工具（CLI） 重构<a href="#命令行工具cli-重构" class="hash-link" aria-label="命令行工具（CLI） 重构的直接链接" title="命令行工具（CLI） 重构的直接链接">​</a></h3><p>在 Taro 1.3，我们将命令行工具使用 TypeScript 进行了重构并逐步添加更多测试用例。重构之后我们可以更加大胆地为 CLI 添加新功能，替换老旧依赖。同时我们也会将 CLI 的功能以 API 的形式暴露出来，赋能给其它开发工具和我们的合作伙伴。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="移动端容器更换">移动端容器更换<a href="#移动端容器更换" class="hash-link" aria-label="移动端容器更换的直接链接" title="移动端容器更换的直接链接">​</a></h3><p>我们和京东的 <a href="https://ares.jd.com/platform" target="_blank" rel="noopener noreferrer">ARES</a> 团队合作，把原有的移动端容器 <a href="https://expo.io" target="_blank" rel="noopener noreferrer">expo</a> 替换为深度定制的 JDReact。JDReact 大幅提升了 Taro 移动端的可控性，可以让我们突破 <code>expo</code> 的掣肘，引入原生移动端代码，提供定制功能和 API，并且性能和稳定性的表现都会更好。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="支持开发小程序插件">支持开发小程序插件<a href="#支持开发小程序插件" class="hash-link" aria-label="支持开发小程序插件的直接链接" title="支持开发小程序插件的直接链接">​</a></h3><p>小程序插件是小程序带来的一个非常优秀的特性，可以极大地提高代码复用率，降低包大小，为开发者带来诸多便利，目前微信、支付宝小程序已经支持插件功能。而从 1.3 版本开始，Taro 支持直接开发微信与支付宝小程序插件，这意味着 Taro 项目将和小程序插件无缝对接，不再有开发模式切换的成本。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="支持小程序云开发">支持「小程序·云开发」<a href="#支持小程序云开发" class="hash-link" aria-label="支持「小程序·云开发」的直接链接" title="支持「小程序·云开发」的直接链接">​</a></h3><p>「小程序·云开发」是微信小程序联合腾讯云团队提供的一个非常强大的功能，它是一款 Serverless 服务，为开发者提供了「云函数」、「云数据库」和「云文件存储」三大能力，并且将这些能力封装成特定的接口，可以帮助开发者快速构建微信小程序的后端服务。为了让 Taro 开发者能够享受到「小程序·云开发」的能力，Taro 也加入了对「小程序·云开发」的支持，为「小程序·云开发」提供了初始化模板，并且将小程序云相关的 API 进行了封装，方便开发者进行使用。同时，「小程序·云开发」已提供 H5 版本的 SDK，Taro 支持将小程序、H5 的调用方式进行统一封装，帮助开发者快速打造 Serverless 的多端应用。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="升级兼容性">升级兼容性<a href="#升级兼容性" class="hash-link" aria-label="升级兼容性的直接链接" title="升级兼容性的直接链接">​</a></h2><p>正如前面所提到，Taro 1.3 是一个酝酿时间最久，拥有特性最多的大版本，对 Taro 底层也进行了不小的重构，所以，1.3 版本的升级带了以下 2 个兼容性问题。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="jsx-中的事件监听函数必须绑定作用域">JSX 中的事件监听函数必须绑定作用域<a href="#jsx-中的事件监听函数必须绑定作用域" class="hash-link" aria-label="JSX 中的事件监听函数必须绑定作用域的直接链接" title="JSX 中的事件监听函数必须绑定作用域的直接链接">​</a></h4><p>在之前的 Taro 版本中，JSX 中绑定的事件监听函数，是可以不需要绑定任何作用域，就能访问到组件实例的，例如</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Taro</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">Component</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports maybe-class-name">Config</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/taro'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">View</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports maybe-class-name">Button</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/components'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Test</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  state </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">hello</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'noclick'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">clickHandler</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">hello</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'click'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">render</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">styles</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">index</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript keyword" style="color:#00009f">this</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">clickHandler</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">点击</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>上述例子中，<code>&lt;Button /&gt;</code> 按钮绑定的点击事件，在之前版本中是能够正常执行的，Taro 会默认将 <code>clickHandler</code> 的作用域绑定为当前组件实例，但是这并不符合 React 中的实际情况，所以，在 1.3 版本中，我们对这一问题进行了修复，现在 JSX 中的事件监听函数必须绑定作用域，否则就会报错。</p><p>上述代码中 JSX 部分可以修改为如下</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">render</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">styles</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">index</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript keyword" style="color:#00009f">this</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript method function property-access" style="color:#d73a49">clickHandler</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript method function property-access" style="color:#d73a49">bind</span><span class="token tag script language-javascript punctuation" style="color:#393A34">(</span><span class="token tag script language-javascript keyword" style="color:#00009f">this</span><span class="token tag script language-javascript punctuation" style="color:#393A34">)</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">点击</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>或者你也可以在 <code>constructor</code> 中将函数进行提前绑定作用域，</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">constructor</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">clickHandlerBind</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">clickHandler</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">bind</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">render</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript" style="color:#00009f">styles</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">index</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">onClick</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:#393A34">=</span><span class="token tag script language-javascript punctuation" style="color:#393A34">{</span><span class="token tag script language-javascript keyword" style="color:#00009f">this</span><span class="token tag script language-javascript punctuation" style="color:#393A34">.</span><span class="token tag script language-javascript property-access" style="color:#00009f">clickHandlerBind</span><span class="token tag script language-javascript punctuation" style="color:#393A34">}</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">点击</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">Button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>还有一种做法是，将 <code>clickHandler</code> 写成箭头函数，这种方式在新旧版本中均可以正常运行。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="暂时无法在原生应用中使用-taro-组件">暂时无法在原生应用中使用 Taro 组件<a href="#暂时无法在原生应用中使用-taro-组件" class="hash-link" aria-label="暂时无法在原生应用中使用 Taro 组件的直接链接" title="暂时无法在原生应用中使用 Taro 组件的直接链接">​</a></h4><p>在之前版本中，使用 Taro 编译后的组件是可以直接用在原生项目中的，以提升复用性，但 1.3 版本由于组件的 props 系统彻底重构了，升级 1.3 后暂时无法在原生项目中使用 Taro 组件，我们正在积极处理这个问题，在后续版本中将继续支持这一特性。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="在框架之外">在框架之外<a href="#在框架之外" class="hash-link" aria-label="在框架之外的直接链接" title="在框架之外的直接链接">​</a></h2><p>Taro 团队除了 1.3 版本中完善多端适配，提高框架开发体验和开发效率之外，我们还在生态建设上付出了诸多努力，其中包括全新升级的官网，物料/插件市场，独立的社区/论坛。</p><p>我们认为，Taro 能够安身立命的本钱是作为开发框架的硬实力，但真正决定 Taro 能走多远却是生态、社区以及合作伙伴。
我们在 1.3 已经把实力大幅增强，现在邀请你一起参与或观察 Taro 在生态和社区的建设：</p><ul><li>Taro 官网：<a href="https://taro.jd.com" target="_blank" rel="noopener noreferrer">https://taro.jd.com</a></li><li>Taro 物料市场: <a href="https://taro-ext.jd.com" target="_blank" rel="noopener noreferrer">https://taro-ext.jd.com</a></li><li>Taro 社区：<a href="https://taro-club.jd.com" target="_blank" rel="noopener noreferrer">https://taro-club.jd.com</a></li><li>GitHub: <a href="https://github.com/NervJS/taro" target="_blank" rel="noopener noreferrer">https://github.com/NervJS/taro</a></li></ul>]]></content:encoded>
            <category>v1</category>
        </item>
        <item>
            <title><![CDATA[小程序框架全面测评]]></title>
            <link>https://docs.taro.zone/blog/2019-03-12-mini-program-framework-full-review</link>
            <guid>https://docs.taro.zone/blog/2019-03-12-mini-program-framework-full-review</guid>
            <pubDate>Tue, 12 Mar 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[image]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://storage.jd.com/taro-resource/review.jpg" alt="image" class="img_ZOZK"></p><p>最近前端届多端框架频出，相信很多有代码多端运行需求的开发者都会产生一些疑惑：这些框架都有什么优缺点？到底应该用哪个？</p><p>作为 Taro 开发团队一员，笔者想在本文尽量站在一个客观公正的角度去评价各个框架的选型和优劣。但宥于利益相关，本文的观点很可能是带有偏向性的，大家可以带着批判的眼光去看待，权当抛砖引玉。</p><p>那么，当我们在讨论多端框架时，我们在谈论什么：</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="多端">多端<a href="#多端" class="hash-link" aria-label="多端的直接链接" title="多端的直接链接">​</a></h2><p>笔者以为，现在流行的多端框架可以大致分为三类：</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="1-全包型">1. 全包型<a href="#1-全包型" class="hash-link" aria-label="1. 全包型的直接链接" title="1. 全包型的直接链接">​</a></h4><p>这类框架最大的特点就是从底层的渲染引擎、布局引擎，到中层的 DSL，再到上层的框架全部由自己开发，代表框架是 Qt 和 Flutter。这类框架优点非常明显：性能（的上限）高；各平台渲染结果一致。缺点也非常明显：需要完全重新学习 DSL（QML/Dart），以及难以适配中国特色的端：小程序。</p><p>这类框架是最原始也是最纯正的的多端开发框架，由于底层到上层每个环节都掌握在自己手里，也能最大可能地去保证开发和跨端体验一致。但它们的框架研发成本巨大，渲染引擎、布局引擎、DSL、上层框架每个部分都需要大量人力开发维护。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="2-web-技术型">2. Web 技术型<a href="#2-web-技术型" class="hash-link" aria-label="2. Web 技术型的直接链接" title="2. Web 技术型的直接链接">​</a></h4><p>这类框架把 Web 技术（JavaScript，CSS）带到移动开发中，自研布局引擎处理 CSS，使用 JavaScript 写业务逻辑，使用流行的前端框架作为 DSL，各端分别使用各自的原生组件渲染。代表框架是 React Native 和 Weex，这样做的优点有：</p><ol><li>开发迅速</li><li>复用前端生态</li><li>易于学习上手，不管前端后端移动端，多多少少都会一点 JS、CSS</li></ol><p>缺点有：</p><ol><li>交互复杂时难以写出高性能的代码，这类框架的设计就必然导致 <code>JS</code> 和 <code>Native</code> 之间需要通信，类似于手势操作这样频繁地触发通信就很可能使得 UI 无法在 16ms 内及时绘制。React Native 有一些声明式的组件可以避免这个问题，但声明式的写法很难满足复杂交互的需求。</li><li>由于没有渲染引擎，使用各端的原生组件渲染，相同代码渲染的一致性没有第一种高。</li></ol><h4 class="anchor anchorWithStickyNavbar_mCi1" id="3-javascript-编译型">3. JavaScript 编译型<a href="#3-javascript-编译型" class="hash-link" aria-label="3. JavaScript 编译型的直接链接" title="3. JavaScript 编译型的直接链接">​</a></h4><p>这类框架就是我们这篇文章的主角们：<code>Taro</code>、<code>WePY</code> 、<code>uni-app</code> 、 <code>mpvue</code> 、 <code>chameleon</code>，它们的原理也都大同小异：先以 JavaScript 作为基础选定一个 DSL 框架，以这个 DSL 框架为标准在各端分别编译为不同的代码，各端分别有一个运行时框架或兼容组件库保证代码正确运行。</p><p>这类框架最大优点和创造的最大原因就是小程序，因为第一第二种框架其实除了可以跨系统平台之外，也都能编译运行在浏览器中。(Qt 有 Qt for WebAssembly, Flutter 有 Hummingbird，React Native 有 <code>react-native-web</code>, Weex 原生支持)</p><p>另外一个优点是在移动端一般会编译到 React Native/Weex，所以它们也都拥有 Web 技术型框架的优点。这看起来很美好，但实际上 React Native/Weex 的缺点编译型框架也无法避免。除此之外，<strong>编译型框架的抽象也不是免费的</strong>：当 bug 出现时，问题的根源可能出在运行时、编译时、组件库以及三者依赖的库等等各个方面。在 Taro 开源的过程中，我们就遇到过 Babel 的 bug，React Native 的 bug，JavaScript 引擎的 bug，当然也少不了 Taro 本身的 bug。相信其它原理相同的框架也无法避免这一问题。</p><p>但这并不意味着这类为了小程序而设计的多端框架就都不堪大用。首先现在各巨头超级 App 的小程序百花齐放，框架会为了抹平小程序做了许多工作，这些工作在大部分情况下是不需要开发者关心的。其次是许多业务类型并不需要复杂的逻辑和交互，没那么容易触发到框架底层依赖的 bug。</p><p>那么当你的业务适合选择编译型框架时，在笔者看来首先要考虑的就是选择 DSL 的起点。因为有多端需求业务通常都希望能快速开发，一个能够快速适应团队开发节奏的 DSL 就至关重要。不管是 React 还是 Vue（或者类 Vue）都有它们的优缺点，大家可以根据团队技术栈和偏好自行选择。</p><p>如果不管什么 DSL 都能接受，那就可以进入下一个环节：</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="生态">生态<a href="#生态" class="hash-link" aria-label="生态的直接链接" title="生态的直接链接">​</a></h2><p>以下内容均以各框架现在（2019 年 3 月 11 日）已发布稳定版为标准进行讨论。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="开发工具">开发工具<a href="#开发工具" class="hash-link" aria-label="开发工具的直接链接" title="开发工具的直接链接">​</a></h4><p>就开发工具而言 <code>uni-app</code> 应该是一骑绝尘，它的文档内容最为翔实丰富，还自带了 IDE 图形化开发工具，鼠标点点点就能编译测试发布。</p><p>其它的框架都是使用 CLI 命令行工具，但值得注意的是 <code>chameleon</code> 有独立的语法检查工具，<code>Taro</code> 则单独写了 ESLint 规则和规则集。</p><p>在语法支持方面，<code>mpvue</code>、<code>uni-app</code>、<code>Taro</code> 、<code>WePY</code> 均支持 TypeScript，四者也都能通过 <code>typing</code> 实现编辑器自动补全。除了 API 补全之外，得益于 TypeScript 对于 JSX 的良好支持，Taro 也能对组件进行自动补全。</p><p>CSS 方面，所有框架均支持 <code>SASS</code>、<code>LESS</code>、<code>Stylus</code>，Taro 则多一个 <code>CSS Modules</code> 的支持。</p><p>所以这一轮比拼的结果应该是：</p><p><code>uni-app</code> &gt; <code>Taro</code> &gt; <code>chameleon</code> &gt; <code>WePY</code>、<code>mpvue</code></p><p><img loading="lazy" src="https://storage.jd.com/taro-resource/develop_tools.png" alt="开发工具" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="多端支持度">多端支持度<a href="#多端支持度" class="hash-link" aria-label="多端支持度的直接链接" title="多端支持度的直接链接">​</a></h4><p>只从支持端的数量来看，<code>Taro</code> 和 <code>uni-app</code> 以六端略微领先（移动端、H5、微信小程序、百度小程序、支付宝小程序、抖音小程序），<code>chameleon </code> 少了抖音小程序紧随其后。</p><p>但值得一提的是 <code>chameleon</code> 有一套自研<a href="//cmljs.org/doc/framework/polymorphism/intro.html" target="_blank" rel="noopener noreferrer">多态协议</a>，编写多端代码的体验会好许多，可以说是一个能戳到多端开发痛点的功能。<code>uni-app</code> 则有一套独立的<a href="://uniapp.dcloud.io/platform" target="_blank" rel="noopener noreferrer">条件编译语法</a>，这套语法能同时作用于 <code>js</code>、样式和模板文件。<code>Taro</code> 可以在业务逻辑中根据环境变量使用条件编译，也可以直接使用<a href="/docs/envs">条件编译文件</a>（类似 React Native 的方式）。</p><p>在移动端方面，<code>uni-app</code> 基于 <code>weex</code> 定制了一套 <code>nvue</code> 方案 弥补 <code>weex</code> API 的不足；<code>Taro</code> 则是暂时基于 <code>expo</code> 达到同样的效果；<code>chameleon</code> 在移动端则有一套 SDK 配合多端协议与原生语言通信。</p><p>H5 方面，<code>chameleon</code> 同样是由多态协议实现支持，<code>uni-app</code> 和 <code>Taro</code> 则是都在 H5 实现了一套兼容的组件库和 API。</p><p><code>mpvue</code> 和 <code>WePY</code> 都提供了转换各端小程序的功能，但都没有 h5 和移动端的支持。</p><p>所以最后一轮对比的结果是：</p><p><code>chameleon</code> &gt; <code>Taro</code>、<code>uni-app</code> &gt; <code>mpvue</code> &gt; <code>WePY</code></p><p><img loading="lazy" src="https://storage.jd.com/taro-resource/duoduan.png" alt="多端支持" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="组件库工具库demo">组件库/工具库/demo<a href="#组件库工具库demo" class="hash-link" aria-label="组件库/工具库/demo的直接链接" title="组件库/工具库/demo的直接链接">​</a></h4><p>作为开源时间最长的框架，<code>WePY</code> 不管从 Demo，组件库数量 ，工具库来看都占有一定优势。</p><p><code>uni-app</code> 则有自己的插件市场和 UI 库，如果算上收费的框架和插件比起 <code>WePy</code> 也是完全不遑多让的。</p><p><code>Taro </code> 也有官方维护的跨端 UI 库 <code>taro-ui</code> ，另外在状态管理工具上也有非常丰富的选择（Redux、MobX、dva），但 demo 的数量不如前两个。但 <code>Taro</code> 有一个转换微信小程序代码为 Taro 代码的工具，可以弥补这一问题。</p><p>而 <code>mpvue</code> 没有官方维护的 UI 库，<code>chameleon</code> 第三方的 demo 和工具库也还基本没有。</p><p>所以这轮的排序是：</p><p><code>WePY</code> &gt; <code>uni-app</code> 、<code>taro</code> &gt; <code>mpvue</code> &gt; <code>chameleon</code></p><p><img loading="lazy" src="https://storage.jd.com/taro-resource/component.png" alt="组件库/工具库/demo" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="接入成本">接入成本<a href="#接入成本" class="hash-link" aria-label="接入成本的直接链接" title="接入成本的直接链接">​</a></h4><p>接入成本有两个方面：</p><p>第一是框架接入原有微信小程序生态。由于目前微信小程序已呈一家独大之势，开源的组件和库（例如 <code>wxparse</code>、<code>echart</code>、<code>zan-ui</code> 等）多是基于原生微信小程序框架语法写成的。目前看来 <code>uni-app</code> 、<code>Taro</code>、<code>mpvue</code> 均有文档或 demo 在框架中直接使用原生小程序组件/库，<code>WePY</code> 由于运行机制的问题，很多情况需要小改一下目标库的源码，<code>chameleon</code> 则是提供了一个按步骤大改目标库源码的迁移方式。</p><p>第二是原有微信小程序项目部分接入框架重构。在这个方面 Taro 在京东购物小程序上进行了大胆的实践，具体可以查看文章<a href="//aotu.io/notes/2018/09/11/taro-in-jd/" target="_blank" rel="noopener noreferrer">《Taro 在京东购物小程序上的实践》</a>。其它框架则没有提到相关内容。</p><p>而对于两种接入方式 Taro 都提供了 <code>taro convert</code> 功能，既可以将原有微信小程序项目转换为 Taro 多端代码，也可以将微信小程序生态的组件转换为 Taro 组件。</p><p>所以这轮的排序是：</p><p><code>Taro</code> &gt; <code>mpvue</code> 、 <code>uni-app</code> &gt; <code>WePY</code> &gt; <code>chameleon</code></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="流行度">流行度<a href="#流行度" class="hash-link" aria-label="流行度的直接链接" title="流行度的直接链接">​</a></h4><p>从 GitHub 的 star 来看，<code>mpvue</code> 、<code>Taro</code>、<code>WePY</code> 的差距非常小。从 NPM 和 CNPM 的 CLI 工具下载量来看，是 Taro（3k/week）&gt; mpvue (2k/w) &gt; WePY (1k/w)。但发布时间也刚好反过来。笔者估计三家的流行程度和案例都差不太多。</p><p><code>uni-app</code> 则号称有上万案例，但不像其它框架一样有一些大厂应用案例。另外从开发者的数量来看也是 <code>uni-app</code> 领先，它拥有 20+ 个 QQ 交流群（最大人数 2000）。</p><p>所以从流行程度来看应该是：</p><p><code>uni-app</code> &gt; <code>Taro</code>、<code>WePY</code>、<code>mpvue</code> &gt; <code>chameleon</code></p><p><img loading="lazy" src="https://storage.jd.com/taro-resource/popluar.png" alt="流行度" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="开源建设">开源建设<a href="#开源建设" class="hash-link" aria-label="开源建设的直接链接" title="开源建设的直接链接">​</a></h4><p>一个开源作品能走多远是由框架维护团队和第三方开发者共同决定的。虽然开源建设不能具体地量化，但依然是衡量一个框架/库生命力的非常重要的标准。</p><p>从第三方贡献者数量来看，<code>Taro</code> 在这一方面领先，并且 <code>Taro</code> 的一些核心包/功能（MobX、CSS Modules、alias）也是由第三方开发者贡献的。除此之外，腾讯开源的 <code>omi</code> 框架小程序部分也是基于 Taro 完成的。</p><p><code>WePY</code> 在腾讯开源计划的加持下在这一方面也有不错的表现；<code>mpvue</code> 由于停滞开发了很久就比较落后了；可能是产品策略的原因，<code>uni-app</code> 在开源建设上并不热心，甚至有些部分代码都没有开源；<code>chameleon</code> 刚刚开源不久，但它的代码和测试用例都非常规范，以后或许会有不错的表现。</p><p>那么这一轮的对比结果是：</p><p><code>Taro</code> &gt; <code>WePY</code> &gt; <code>mpvue</code> &gt; <code>chameleon</code> &gt; <code>uni-app</code></p><p>最后补一个总的生态对比图表：</p><p><img loading="lazy" src="https://storage.jd.com/taro-resource/all.png" alt="总表" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="未来">未来<a href="#未来" class="hash-link" aria-label="未来的直接链接" title="未来的直接链接">​</a></h2><p>从各框架已经公布的规划来看：</p><p><code>WePY</code> 已经发布了 <code>v2.0.alpha</code> 版本，虽然没有公开的文档可以查阅到 <code>2.0</code> 版本有什么新功能/特性，但据其作者介绍，<code>WePY 2.0</code> 会放大招，是一个「对得起开发者」的版本。笔者也非常期待 2.0 正式发布后 <code>WePY</code> 的表现。</p><p><code>mpvue</code> 已经发布了 <code>2.0</code> 的版本，主要是更新了其它端小程序的支持。但从代码提交， issue 的回复/解决率来看，<code>mpvue</code> 要想在未来有作为首先要打消社区对于 <code>mpvue</code> 不管不顾不更新的质疑。</p><p><code>uni-app</code> 已经在生态上建设得很好了，应该会在此基础之上继续稳步发展。如果 <code>uni-app</code> 能加强开源开放，再加强与大厂的合作，相信未来还能更上一层楼。</p><p><code>chameleon</code> 的规划比较宏大，虽然是最后发的框架，但已经在规划或正在实现的功能有：</p><ul><li>快应用和端拓展协议</li><li>通用组件库和垂直类组件库</li><li>面向研发的图形化开发工具</li><li>面向非研发的图形化页面搭建工具</li></ul><p>如果 <code>chameleon</code> 把这些功能都做出来的话，再继续完善生态，争取更多第三方开发者，那么在未来 <code>chameleon</code> 将大有可为。</p><p><code>Taro</code> 的未来也一样值得憧憬。Taro 即将要发布的 <code>1.3</code> 版本就会支持以下功能：</p><ul><li>快应用支持</li><li>Taro Doctor，自动化检查项目配置和代码合法性</li><li>更多的 JSX 语法支持，1.3 之后限制生产力的语法只有 <code>只能用 map 创造循环组件</code> 一条</li><li>H5 打包体积大幅精简</li></ul><p>同时 <code>Taro</code> 也正在对移动端进行大规模重构；开发图形化开发工具；开发组件/物料平台以及图形化页面搭建工具。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="结语">结语<a href="#结语" class="hash-link" aria-label="结语的直接链接" title="结语的直接链接">​</a></h2><p>那说了那么多，到底用哪个呢？</p><p>如果不介意尝鲜和学习 DSL 的话，完全可以尝试 <code>WePY</code> 2.0 和 <code>chameleon</code> ，一个是酝酿了很久的 2.0 全新升级，一个有专门针对多端开发的多态协议。</p><p><code>uni-app</code> 和 <code>Taro</code> 相比起来就更像是「水桶型」框架，从工具、UI 库，开发体验、多端支持等各方面来看都没有明显的短板。而 <code>mpvue</code> 由于开发一度停滞，现在看来各个方面都不如在小程序端基于它的 <code>uni-app</code> 。</p><p>当然，Talk is cheap。如果对这个话题有更多兴趣的同学可以去 GitHub 另行研究，有空看代码，没空看提交：</p><ul><li>chameleon: <a href="https://github.com/didi/chameleon" target="_blank" rel="noopener noreferrer">https://github.com/didi/chameleon</a></li><li>mpvue: <a href="https://github.com/Meituan-Dianping/mpvue" target="_blank" rel="noopener noreferrer">https://github.com/Meituan-Dianping/mpvue</a></li><li>Taro: <a href="https://github.com/NervJS/taro" target="_blank" rel="noopener noreferrer">https://github.com/NervJS/taro</a></li><li>uni-app: <a href="https://github.com/dcloudio/uni-app" target="_blank" rel="noopener noreferrer">https://github.com/dcloudio/uni-app</a></li><li>WePY: <a href="https://github.com/Tencent/wepy" target="_blank" rel="noopener noreferrer">https://github.com/Tencent/wepy</a></li></ul><p>(按字母顺序排序)</p>]]></content:encoded>
            <category>v1</category>
        </item>
        <item>
            <title><![CDATA[决战性能之巅 - Taro H5 转换与优化升级]]></title>
            <link>https://docs.taro.zone/blog/2019-02-28-taro-h5-optimize</link>
            <guid>https://docs.taro.zone/blog/2019-02-28-taro-h5-optimize</guid>
            <pubDate>Thu, 28 Feb 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[image]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://img10.360buyimg.com/img/jfs/t1/21860/12/8740/42390/5c790470E1d0bbce9/9f9bb78d01f7564b.png" alt="image" class="img_ZOZK"></p><p>作为一个多端开发框架，Taro 从项目发起时就已经支持编译到 H5 端。随着 Taro 多端能力的不断成熟，我们对 Taro H5 端应用的要求也不断提升。我们已经不再满足于“能跑”，更希望 Taro 能跑得快。</p><p>我们经常收到用户反馈：为什么使用 Taro 脚手架创建的空项目，打包后代码体积却有 400KB+；也有用户在 Issue 中提到，Taro 的部分 Api 占用空间巨大，事实上功能却并不完美，等等。作为一个开源项目，我们非常重视社区开发者们的意见。所以在最新版本中，我们对 Taro H5 端的性能表现进行了优化。</p><p>作为运行时的基础，每一个 Taro 的 H5 端应用都需要引入 @tarojs/components 和 @tarojs/taro-h5 等基础依赖包。在编译、打包之后，这些依赖包大约会在首屏占用 400KB 以上的空间。如果开发者还使用了 UI 库，例如 Taro-UI，基础体积还会更大，这严重限制了 Taro H5 端应用的性能优化空间。</p><p>事实上，我们在 H5 端应用中并不会使用到全部的 Taro 组件和 Api。将这些依赖包全部打包进应用是没有必要，也是不合理的。进行死码删除（Dead code elimination），进一步缩减代码体积，就是我们的优化方向之一。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="效果">效果<a href="#效果" class="hash-link" aria-label="效果的直接链接" title="效果的直接链接">​</a></h2><p>在介绍具体细节之前，我们先看一看优化的效果，因为这可能会让你更有兴趣了解后面的内容。用一句话来说，效果非常显著。</p><p>我们建立了一个空项目，在项目配置中加入了<code>webpack-bundle-analyzer</code>插件以查看编译分析。下图是优化前的打包文件分析结果：</p><p><img loading="lazy" src="https://img11.360buyimg.com/img/jfs/t1/19501/21/8687/76054/5c77fb63E5d040dc5/12bcea35b5b780fb.png" alt="before" class="img_ZOZK"></p><p>而在优化后，对比非常明显：</p><p><img loading="lazy" src="https://img12.360buyimg.com/img/jfs/t1/24494/22/8574/92373/5c77fb63E4402b50f/2312218a7dccedf7.png" alt="after" class="img_ZOZK"></p><p>优化前生成的代码总大小为 455KB，而在优化后仅剩约 96KB，仅是原来的 1/5 左右。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="你需要做什么">你需要做什么<a href="#你需要做什么" class="hash-link" aria-label="你需要做什么的直接链接" title="你需要做什么的直接链接">​</a></h2><p>很简单，作为使用者，你不需要做任何代码上的改动，只需要将 Taro 更新到最新版本即可。但在看不见的地方，Taro 却默默地做了许多工作。下面会从原理出发，详细介绍 Taro 的工作。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="原理">原理<a href="#原理" class="hash-link" aria-label="原理的直接链接" title="原理的直接链接">​</a></h2><p><a href="https://en.wikipedia.org/wiki/Dead_code_elimination" target="_blank" rel="noopener noreferrer">死码删除（Dead code elimination）</a>是一种代码优化技术，可以删除对应用程序执行结果没有影响的代码。Web Fundamentals 的一篇文章有提到，treeshaking 是由 Rollup 提出的一种死码删除的形式。</p><blockquote><p>Tree shaking is a form of dead code elimination. The term was popularized by Rollup, but the concept of dead code elimination has existed for some time.</p><p>-- <a href="https://developers.google.com/web/fundamentals/performance/optimizing-javascript/tree-shaking/" target="_blank" rel="noopener noreferrer">Reduce JavaScript Payloads with Tree Shaking</a>, Jeremy Wagner</p></blockquote><p>通过在构建时进行静态分析，编译工具可以分析出我们代码中真正的依赖关系。treeshaking 把我们的代码想象成一棵树，代码的每个依赖项看作树上的节点。将未使用过的依赖项从构建结果中移除，这就是 treeshaking 的基本思想。</p><p>那么，假设我们手头有一段代码，我们要怎样辨别其中可以删除的部分呢？答案是，通过分析副作用：</p><div class="language-javascript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-javascript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// add.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> add（a</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> b）</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> a </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> b</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// add2.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'这是一个log'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> add2（a</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> b）</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> a </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> b</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// index.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">add</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./add.js'</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 没有副作用，可以删除</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">add2</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./add2.js'</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 有副作用，不能直接删除</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// EOF</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>副作用这个名词对于了解函数式编程的同学肯定不陌生。修改外部状态，或者是产生输出等等，都是副作用；而存在副作用的代码，是不能被直接移除的。类似上面这个代码示意，add2 模块就是存在副作用的。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="站在巨人的肩膀上">站在巨人的肩膀上<a href="#站在巨人的肩膀上" class="hash-link" aria-label="站在巨人的肩膀上的直接链接" title="站在巨人的肩膀上的直接链接">​</a></h3><p>除了 Rollup 之外，支持 treeshaking 的工具/插件还有很多，比如 babel-plugin-transform-dead-code-elimination、uglify、terser 等。 webpack 在 v2 之后就内置了对 treeshaking 的支持，并在 webpack@4 中对 treeshaking 功能进行了扩展。</p><p>Taro H5 端在构建过程中，使用 webpack 作为构建的核心。在 webpack 中使用 treeshaking 功能有几个需要注意的地方：</p><ol><li>如果是 npm 模块，需要<code>package.json</code>中存在<code>sideEffects</code>字段，并且准确配置了存在副作用的源代码。</li><li>必须使用 ES6 模块语法。由于诸如<code>babel-preset-env</code>之类的 babel 预配置包默认会对代码的模块机制进行改写，还需要将<code>modules</code>设置为<code>false</code>，将模块解析的工作直接交给 webpack。</li><li>需要工作在 webpack 的<code>production</code>模式下。</li></ol><p>webpack 的 treeshaking 工作主要分为两步。第一步是在模块级别移除未使用且无副作用的模块，这一步由 webpack 的内置插件完成；第二步是在文件级别移除未使用的代码，这一步由代码压缩工具 Terser 完成的。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="移除未使用的模块">移除未使用的模块<a href="#移除未使用的模块" class="hash-link" aria-label="移除未使用的模块的直接链接" title="移除未使用的模块的直接链接">​</a></h3><p>前面我们提到，需要在<code>package.json</code>中配置<code>sideEffects</code>字段。</p><p>在 <a href="https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free" target="_blank" rel="noopener noreferrer">webpack 文档</a> 中有提到，这一举动正是为了让 webpack 正确地识别到没有副作用的代码模块。</p><p>在 webpack 中，模块依赖分析是由内置插件 <a href="https://sourcegraph.com/github.com/webpack/webpack/-/blob/lib/optimize/SideEffectsFlagPlugin.js" target="_blank" rel="noopener noreferrer">SideEffectsFlagPlugin</a> 进行的。</p><p><img loading="lazy" src="https://m.360buyimg.com/img/jfs/t1/14100/24/8582/236551/5c77fbb1Ef1ec35cc/751d4324349728b8.png" alt="image-20190225220418363" class="img_ZOZK"></p><p>经过 <a href="https://sourcegraph.com/github.com/webpack/webpack/-/blob/lib/optimize/SideEffectsFlagPlugin.js" target="_blank" rel="noopener noreferrer">SideEffectsFlagPlugin</a>处理后，没有使用过并且没有副作用的模块都会被打上<code>sideEffectFree</code>标记。</p><p>在 <a href="https://sourcegraph.com/github.com/webpack/webpack/-/blob/lib/optimize/ModuleConcatenationPlugin.js" target="_blank" rel="noopener noreferrer">ModuleConcatenationPlugin</a> 中，带着<code>sideEffectFree</code>标记的模块将不会被打包：</p><p><img loading="lazy" src="https://m.360buyimg.com/img/jfs/t1/16074/5/8686/211783/5c77fbacE50bc5fe1/681ba09abcf55c11.png" alt="image-20190222111301698" class="img_ZOZK"></p><p>来到这里，webpack 完成了在模块级别对未使用模块的排除。接下来，依靠 Terser，webpack 可以在文件级别，对未使用、无副作用的代码进行移除。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="移除未使用的代码">移除未使用的代码<a href="#移除未使用的代码" class="hash-link" aria-label="移除未使用的代码的直接链接" title="移除未使用的代码的直接链接">​</a></h3><p>在 CommonJS 规范中，我们通过<code>require</code>函数来引入模块，通过<code>module.exports</code>进行导出。这意味着我们可以在代码中的任何地方用任何方式引入和导出模块：可以是在某个需要等待用户输入的回调函数中，或者是在符合某个条件才进行引入等等。所以在使用 ES6 的模块系统之前，对 Javascript 做编译时的依赖关系分析是近乎不可能的（并不是完全不可能。prepack 通过实现一个 JS 解释器，甚至可以在编译时提前进行静态计算）。</p><div class="language-javascript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-javascript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// utils.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method-variable function-variable method function property-access" style="color:#d73a49">add</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">a</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> b</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> a </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> b</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method-variable function-variable method function property-access" style="color:#d73a49">minus</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">a</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> b</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> a </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> b</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// index.js;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> utils </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'./utils.js'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">utils</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>像上面这段代码，虽然我们最终只使用了<code>add</code>函数，但<code>minus</code>函数也会在最终的打包代码中出现，因为在编译时无法快速得知是否使用了<code>minus</code>函数。</p><p>在 ES6 的模块系统中，我们使用<code>import</code>/<code>export</code>语法来进行模块的引入和导出。与 CommonJS 规范不同的是，这套新的模块系统存在一些限制：<code>import</code>/<code>export</code>行为只能在代码的顶层、默认使用严格模式等等。这些限制使代码模块的导入与导出变得静态化，模块间的依赖关系在开发时已经确定，编译器也更容易解析我们的代码。</p><div class="language-javascript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-javascript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// utils.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">a</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> b</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> a </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> b</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">minus</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">a</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> b</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> a </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> b</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// index.js;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> add </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./utils.js'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在使用 ES6 模块系统改造后，我们可以清楚地看到，<code>minus</code>函数确实没有被使用过，所以可以安全地将其从最终打包代码中移除。</p><p>当然，具体的分析过程非常复杂。变量提升、object 取值操作、<code>for(var i in list)</code> 语句、自执行函数、函数传参（<code>onClick(function a () {…})</code>）等等，都有可能导致意料之外的情况，从而导致 treeshaking 失效。如果想了解 Terser 的具体处理过程，百度/Google 会是最好的老师。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="taro-做了什么">Taro 做了什么<a href="#taro-做了什么" class="hash-link" aria-label="Taro 做了什么的直接链接" title="Taro 做了什么的直接链接">​</a></h2><p>Taro 需要对依赖包做一些修改。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="组件的-es-模块化">组件的 ES 模块化<a href="#组件的-es-模块化" class="hash-link" aria-label="组件的 ES 模块化的直接链接" title="组件的 ES 模块化的直接链接">​</a></h3><p>在进行组件库的 ES 模块化改造之前，如果要发布 @tarojs/components 包，Taro 会执行命令 <code>yarn build</code>，使用 webpack 对源代码进行打包，输出为<code>dist/index.js</code>文件。由于 webpack 并不支持输出为 ES 模块，所以这是个 UMD 模块。</p><p><img loading="lazy" src="https://m.360buyimg.com/img/jfs/t1/22354/16/8646/21917/5c77fbacEdee5c349/e6af50961d434f09.png" alt="image-20190225154632128" class="img_ZOZK"></p><p>这个文件占据了 462KB 的空间，并且由于模块规范等问题，无法进行 treeshaking。所以就算开发者在 Taro 的项目中只引入了两个组件，最终的打包结果也会包含所有的内置组件。</p><p>事实上，@tarojs/components 的源码本身是使用 ESM 规范的：</p><p><img loading="lazy" src="https://m.360buyimg.com/img/jfs/t1/14453/5/8776/198331/5c77fbacE24aec263/74ceb998fe9f5059.png" alt="image-20190225160508956" class="img_ZOZK"></p><p>所以只要让 webpack 直接解析组件库的源码，我们立即就可以享受到 webpack 自带 treeshaking 带来的好处了。</p><p><img loading="lazy" src="https://m.360buyimg.com/img/jfs/t1/21781/18/8661/46118/5c77fbacE28c35efd/ba6ed6939fa041ba.png" alt="image-20190225162018328" class="img_ZOZK"></p><p>同时，我们也在<code>sideEffects</code>属性中对样式文件做了标记，帮助 webpack 对样式代码的副作用进行识别，在项目编译的代码中保留样式代码。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="api-的-es-模块化">Api 的 ES 模块化<a href="#api-的-es-模块化" class="hash-link" aria-label="Api 的 ES 模块化的直接链接" title="Api 的 ES 模块化的直接链接">​</a></h3><p>同样，以前在发布 @tarojs/taro-h5 之前，Taro 也需要执行命令 <code>yarn build</code>，使用 Rollup 对源代码进行打包，输出为<code>dist/index.js</code>文件：</p><p><img loading="lazy" src="https://m.360buyimg.com/img/jfs/t1/29124/32/8757/41440/5c77fbacEa1b2206c/fba8b10d73136327.png" alt="image-20190225162654885" class="img_ZOZK"></p><p>这个文件占据了 262KB 的空间。同样，只要是个 Taro 的 H5 端应用，生成的代码都会全量引入这个文件。</p><p>我们对 @tarojs/taro-h5 进行模块化改造的思路与 @tarojs/components 相同。我们希望 @tarojs/taro-h5 模块本身遵守 ESM 模块规范，那就只需要标记一下<code>sideEffects</code>，再修改一下模块入口就好。</p><p><img loading="lazy" src="https://m.360buyimg.com/img/jfs/t1/16633/12/8583/53807/5c77fbacE239aa77a/0d515de24217f5b5.png" alt="image-20190225165957461" class="img_ZOZK"></p><p>粗略一看，@tarojs/taro-h5 还挺 “ESM” 的，但这还不够。我们还需要将这些 Api 以 namedExports 的形式导出，开发者使用<code>import { XXX } from '@tarojs/taro-h5'</code>导入 Api 即可。</p><p><img loading="lazy" src="https://m.360buyimg.com/img/jfs/t1/14351/23/8588/28303/5c77fbacEf0ec647b/0b8652e5a5bb49a7.png" alt="image-20190225172609369" class="img_ZOZK"></p><p>那么问题来了。在 Taro 项目中，我们一直使用的是 defaultImport，并不会使用 Api 的 <code>namedExports</code> 形式：</p><div class="language-javascript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-javascript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Taro</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/taro-h5'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token maybe-class-name">Taro</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">navigateTo</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token maybe-class-name">Taro</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">getSystemInfo</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Taro.xxx ...</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>只要 Api 是通过对<code>Taro</code>变量取属性获取，<code>Taro</code>变量就需要具备所有的 Api，treeshaking 也就无从谈起。</p><p>有没有办法把 defaultImport 修改为 namedImports 呢？答案是肯定的。我们写了一个 babel 插件 babel-plugin-transform-taroapi，将指定的 Api 调用替换为 namedImports，未指定的变量则保留属性取值的形式。具体源码可以在<strong>这里</strong>查看。</p><div class="language-javascript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-javascript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// const apis = new Set(['navigateTo', 'navigateBack', ...])</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">babel</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">preset</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'babel-preset-env'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">plugins</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// ...,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'babel-plugin-transform-taroapi'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">packageName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/taro-h5'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        apis</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>这个插件接受一个对象作为配置参数：<code>packageName</code>属性指定需要进行替换的模块名，<code>apis</code>接受一个 Set 对象，也就是所有 Api 的列表。</p><p>为了避免后期手动维护 Api 列表的情况，我们给 @tarojs/taro-h5 模块加了一个编译任务，通过一个简单的 Rollup 插件，在执行<code>yarn build</code>命令时生成一份 Api 列表：</p><p><img loading="lazy" src="https://m.360buyimg.com/img/jfs/t1/11020/15/9616/262595/5c77fbadE6f554c3f/c4d4bc42d65508cd.png" alt="image-20190225210238592" class="img_ZOZK"></p><p>下面是编译前后的代码对比。可以看到，在编译后<code>setStorage</code>、<code>getStorage</code>的调用都被替换为 namedImports。</p><div class="language-javascript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-javascript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 编译前</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Taro</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/taro-h5'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token maybe-class-name">Taro</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">initPxTransform</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token maybe-class-name">Taro</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setStorage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token maybe-class-name">Taro</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'getStorage'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 编译后</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Taro</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> setStorage </span><span class="token imports keyword module" style="color:#00009f">as</span><span class="token imports"> _setStorage</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> getStorage </span><span class="token imports keyword module" style="color:#00009f">as</span><span class="token imports"> _getStorage </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/taro-h5'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token maybe-class-name">Taro</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">initPxTransform</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">_setStorage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">_getStorage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>到这里，虽然过程比较艰辛，但我们对 @tarojs/taro-h5 的模块化改造终于完成了。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="最后">最后<a href="#最后" class="hash-link" aria-label="最后的直接链接" title="最后的直接链接">​</a></h2><p>截至目前，Taro 在 H5 端的完成度已经很高，但是并不完美。未来，在对已有问题进行修复的同时，我们还将继续为 Taro H5 端带来更多新的特性，比如对社区中呼声相当高的<code>switchTab</code>、页面滚动监听<code>onPageScroll</code>、下拉刷新<code>onPullDownRefresh</code>等 Api 的支持，更加统一的页面切换动画，以及更加稳定的多页面模式等等。</p><p>Taro 发展到现在，离不开社区的支持。非常感谢在 github、微信群中踊跃反馈的开发者们。如果你对 Taro 有什么想法或建议，Taro 非常欢迎你来吐槽或观光：</p><p><a href="https://github.com/NervJS/taro" target="_blank" rel="noopener noreferrer">https://github.com/NervJS/taro</a></p>]]></content:encoded>
            <category>v1</category>
        </item>
        <item>
            <title><![CDATA[Taro UI 2.0 发布：新增自定义主题功能，适配更多小程序]]></title>
            <link>https://docs.taro.zone/blog/2019-02-25-taro-ui-2.0</link>
            <guid>https://docs.taro.zone/blog/2019-02-25-taro-ui-2.0</guid>
            <pubDate>Mon, 25 Feb 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[转眼间，Taro UI 发布已有半年，感谢大家的支持，让我们收获了 GitHub 1400+ star。在此期间，我们不断完善组件库的功能和特性，新增了许多组件和小工具，包括但不限于:]]></description>
            <content:encoded><![CDATA[<p>转眼间，Taro UI 发布已有半年，感谢大家的支持，让我们收获了 <a href="https://github.com/NervJS/taro-ui" target="_blank" rel="noopener noreferrer">GitHub</a> 1400+ star。在此期间，我们不断完善组件库的功能和特性，新增了许多组件和小工具，包括但不限于:</p><ul><li>新增日历、索引选择、区域选择、图片选择等十一个组件</li><li>适配支付宝小程序、百度小程序</li><li>新增自定义主题功能</li><li>新增主题生成器，以帮助开发者更好地使用自定义主题功能</li><li>新增 Issue Helper，以帮助开发者更规范地填写 Issue</li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="新增组件">新增组件<a href="#新增组件" class="hash-link" aria-label="新增组件的直接链接" title="新增组件的直接链接">​</a></h2><p>在 1.0 版本发布之后，我们又陆续新增了如下十一个组件：</p><ul><li>视图组件：页面提示、 分隔符、倒计时、 幕帘 、步骤条</li><li>操作反馈：全局信息组件</li><li>表单：图片选择器、区域选择器、索引选择器、日历组件、搜索栏</li></ul><p>其中，在社区里呼声最高的组件，非日历组件莫属。由于日历组件复杂度偏高，要适配多端环境难度偏大，纵观市面上的小程序 UI 组件库，包含日历组件的寥寥无几。尽管如此，我们团队的大鱼兄仍独自挑起重担，几乎完美地实现了该组件，此处掌声献给大鱼兄。</p><p><strong>日历组件功能预览：</strong></p><p><img loading="lazy" src="https://misc.aotu.io/jimczj/calender.gif" alt="calender" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="适配支付宝小程序百度小程序">适配支付宝小程序、百度小程序<a href="#适配支付宝小程序百度小程序" class="hash-link" aria-label="适配支付宝小程序、百度小程序的直接链接" title="适配支付宝小程序、百度小程序的直接链接">​</a></h2><p>在 1.0 版本适配微信小程序时，我们遇到了很多困难。比如：</p><ul><li><p>微信小程序自定义组件使用 Shadow DOM 进行渲染，引起了以下几种问题：</p><ul><li>组件之间无法使用相邻选择器，如组件间加 border 的需求，最终只能通过新增 props 给开发者自行控制</li><li>无法自定义 flex 布局组件，最终只能提供样式给开发者自行使用</li></ul></li><li><p>Component 组件对应 wxss 文件的样式，只对组件 wxml 内的节点生效。经过不断探索，才发现 addGlobalClass 这一属性，需在自定义组件内激活 addGlobalClass 选项，才能使自定义组件被 app.wxss 或页面的 wxss 中的所有的样式定义影响。。</p></li><li><p>原生组件的使用限制。由于原生组件脱离在 WebView 渲染流程外，原生组件的层级是最高的，所以页面中的其他组件无论设置 z-index 为多少，都无法盖在原生组件上。由此导致模态框等组件无法遮挡 input、textarea 等原生组件，造成穿透效果。 可喜的是，微信官方团队已经在改善该问题，对小程序原生组件引入了同层渲染模式。通过同层渲染，小程序原生组件可与其他内置组件处于相同层级，不再有特殊的使用限制。<a href="https://developers.weixin.qq.com/community/develop/doc/000aa28d030f60a3c4183eecb5d801?from=timeline" target="_blank" rel="noopener noreferrer">详见</a>。</p></li><li><p>小程序不支持 requestAnimationFrame，无法很好地实现 js 动画。</p></li></ul><p>克服完上述微信小程序的困难后，支付宝小程序和百度小程序的适配工作大多是解决样式和某些 API 的差异。得益于 Taro 良好的支持，Taro UI 的适配工作暂时告一段落。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="新增自定义主题功能">新增自定义主题功能<a href="#新增自定义主题功能" class="hash-link" aria-label="新增自定义主题功能的直接链接" title="新增自定义主题功能的直接链接">​</a></h2><p>Taro UI 1.0 发布时只有一套默认的主题配色，为满足业务和品牌上多样化的视觉需求，UI 库支持一定程度的样式定制。</p><p>Taro UI 的组件样式是使用 SCSS 编写的，如果你的项目中也使用了 SCSS，那么可以直接在项目中改变 Taro UI 的样式变量。</p><p>新建一个主题样式文件（例如 custom-variables.scss）并覆盖<a href="https://github.com/NervJS/taro-ui/blob/dev/src/style/variables/default.scss" target="_blank" rel="noopener noreferrer">默认主题变量</a>：</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">/* 改变主题变量，具体变量名可查看 taro-ui/dist/style/variables/default.scss 文件 */</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$color-brand: #6190E8;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">/* 引入 Taro UI 默认样式 */</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@import "~taro-ui/dist/style/index.scss";</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>之后在项目的入口文件中引入以上的样式文件即可（无需重复引入组件的默认样式）。</p><p>目前，我们额外定制了京东主题和 7Fresh 主题色，可通过扫描以下二维码查看。</p><p><strong>京东主题：</strong></p><p><img loading="lazy" src="https://misc.aotu.io/jimczj/taro-ui-red.png" alt="image" class="img_ZOZK"></p><p><strong>7Fresh 主题：</strong></p><p><img loading="lazy" src="https://misc.aotu.io/jimczj/taro-ui-purple.png" alt="image" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="新增主题生成器">新增主题生成器<a href="#新增主题生成器" class="hash-link" aria-label="新增主题生成器的直接链接" title="新增主题生成器的直接链接">​</a></h2><p>为了让开发者更好地使用自定义主题功能，我们还新增了主题生成器。开发者可以使用该工具方便地定制 UI 主题。</p><p><strong>主题生成器地址：</strong> <a href="https://nervjs.github.io/taro-ui-theme-preview/" target="_blank" rel="noopener noreferrer">https://nervjs.github.io/taro-ui-theme-preview/</a></p><p><strong>效果预览：</strong></p><p><img loading="lazy" src="https://misc.aotu.io/jimczj/theme-preview.gif" alt="theme-preview" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="新增-issue-helper">新增 Issue Helper<a href="#新增-issue-helper" class="hash-link" aria-label="新增 Issue Helper的直接链接" title="新增 Issue Helper的直接链接">​</a></h2><p>虽然我们配置了 Issue Template，但仍有部分开发者没有根据规范填写 Issue。我们排查问题时经常需要再次询问版本号信息、复现代码等等，这不仅消耗我们维护项目的精力，还降低了 Issue 处理效率。因此我们参考了 Ant 和 iView 团队的做法，制作了 Issue Helper 页面，帮助开发者更规范地填写 Issue。</p><p><strong>Taro UI Issue Helper 地址：</strong> <a href="https://nervjs.github.io/taro-ui-issue-helper/" target="_blank" rel="noopener noreferrer">https://nervjs.github.io/taro-ui-issue-helper/</a></p><p>此外，我们发现重复制作 Issue Helper 页面是一件很浪费劳动力的事情，于是将 Issue Helper 封装成一个命令行工具，开发者可以通过简单配置 <code>config.js</code>，执行命令 <code>issue-helper build</code> 就可以生成所需要的页面。</p><p><strong>Issue Helper 工具仓库地址：</strong> <a href="https://github.com/jimczj/issue-helper" target="_blank" rel="noopener noreferrer">https://github.com/jimczj/issue-helper</a></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="未来计划">未来计划<a href="#未来计划" class="hash-link" aria-label="未来计划的直接链接" title="未来计划的直接链接">​</a></h2><ul><li>适配抖音小程序</li><li>国际化 i18n</li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="致谢">致谢<a href="#致谢" class="hash-link" aria-label="致谢的直接链接" title="致谢的直接链接">​</a></h2><p>感谢大家对 Taro UI 的使用与反馈，我们会致力于将 Taro UI 打造成高质量组件库，不断丰富组件功能与特性，紧跟 Taro 的步伐适配更多平台。</p><p>最后，欢迎关注并使用我们的组件库：</p><p><a href="https://github.com/NervJS/taro-ui" target="_blank" rel="noopener noreferrer">https://github.com/NervJS/taro-ui</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Taro 1.2：将已有微信小程序转换为多端应用]]></title>
            <link>https://docs.taro.zone/blog/2018-12-18-taro-1-2</link>
            <guid>https://docs.taro.zone/blog/2018-12-18-taro-1-2</guid>
            <pubDate>Tue, 18 Dec 2018 00:00:00 GMT</pubDate>
            <description><![CDATA[在距离 Taro 1.1 发布 之后一个月，经历了 500 多次提交和 17 个预览版本的迭代之后，我们有信心在今天发布 Taro 1.2 正式版。]]></description>
            <content:encoded><![CDATA[<p>在距离 <a href="https://aotu.io/notes/2018/11/05/taro-1-1/" target="_blank" rel="noopener noreferrer">Taro 1.1 发布</a> 之后一个月，经历了 <strong>500</strong> 多次提交和 <strong>17</strong> 个预览版本的迭代之后，我们有信心在今天发布 Taro 1.2 正式版。</p><p>Taro 1.2 比起 Taro 1.1 和其它的小程序开发框架，增加了许多首次在小程序框架上实现的功能，这些功能包括但不限于：</p><ul><li><a href="#taroize">微信小程序转多端应用</a></li><li><a href="#tt">字节跳动（头条）小程序支持</a></li><li><a href="#css">CSS Modules 支持</a></li><li><a href="#mobx">MobX 支持</a></li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="taroize">微信小程序转多端应用<a href="#taroize" class="hash-link" aria-label="微信小程序转多端应用的直接链接" title="微信小程序转多端应用的直接链接">​</a></h2>就像博客大战时代博客服务商提供了「搬家」工具一样，Taro 现在也提供了「搬家」工具让你方便地入驻 Taro 社区。转换原有微信小程序应用非常简单，只需要安装 Taro 命令行工具，定位到待转项目根目录，运行命令：<div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">$ taro convert</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Taro 就会把转换好的代码生成在根目录下的 <code>taroConvert</code> 文件夹。转换后的代码是高可读性的 JSX 代码，没有额外的庞大组件库开销。你可以轻易地进行二次开发，或者直接将这些代码通过 <code>taro build</code> 命令生成任意 Taro 已经支持平台的应用。</p><p>我们已经尝试使用 <code>taro convert</code> 成功转换了四个 GitHub 上最热门的开源微信小程序应用，它们转换之后都表现良好：</p><ul><li><a href="https://github.com/EastWorld/wechat-app-mall" target="_blank" rel="noopener noreferrer">EastWorld/wechat-app-mall ★5000+</a> - 微信小程序商城</li><li><a href="https://github.com/tumobi/nideshop-mini-program" target="_blank" rel="noopener noreferrer">tumobi/nideshop-mini-program ★3000+</a> - 基于 Node.js + MySQL 开发的开源微信小程序商城</li><li><a href="https://github.com/RebeccaHanjw/weapp-wechat-zhihu" target="_blank" rel="noopener noreferrer">RebeccaHanjw/weapp-wechat-zhihu ★1000+</a> - 仿知乎</li><li><a href="https://github.com/jectychen/wechat-v2ex" target="_blank" rel="noopener noreferrer">jectychen/wechat-v2ex ★400+</a> - V2EX</li></ul><p><code>taro convert</code> 不仅仅能支持转换微信小程序应用，它还能转换微信小程序第三方组件。例如 <a href="https://github.com/icindy/wxParse" target="_blank" rel="noopener noreferrer">wxParse</a> 经过 <code>taro convert</code> 编译之后还能突破原有只能解析 11 层 HTML 嵌套的限制，理论上可以解析无限层 HTML 嵌套。</p><p>更多详情可以访问 <a href="/docs/taroize">文档</a> 了解更多。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="tt">字节跳动（头条）小程序支持<a href="#tt" class="hash-link" aria-label="字节跳动（头条）小程序支持的直接链接" title="字节跳动（头条）小程序支持的直接链接">​</a></h2>作为一个「多端统一开发框架」，Taro 当然也不会放过抖音小程序。在 Taro 1.2，你可以通过如下命令进行抖音小程序端的编译预览及打包：<div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># npm script</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run dev:tt</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run build:tt</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 仅限全局安装</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ taro build --type tt --watch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ taro build --type tt</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># npx 用户也可以使用</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ npx taro build --type tt --watch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ npx taro build --type tt</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>选择抖音小程序模式，你需要下载并打开<a href="https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/developer-instrument/download/developer-instrument-update-and-download" target="_blank" rel="noopener noreferrer">抖音小程序开发者工具</a>，然后在项目编译完后选择项目根目录下 dist 目录进行预览。</p><p>相应地，Taro 的平台标识（<a href="/docs/envs#processenvtaro_env">process.env.TARO_ENV</a>）也增加了新成员<code>tt</code>，代表抖音小程序。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="css">CSS Modules 支持<a href="#css" class="hash-link" aria-label="CSS Modules 支持的直接链接" title="CSS Modules 支持的直接链接">​</a></h2><p>在 Taro 最早开源的时候我们就支持了 <code>SCSS</code> 、 <code>LESS</code> 和 <code>Stylus</code>，而在 Taro 1.2 中我们百尺竿头更进一步，给社区带来了 JSX 的好伙伴：CSS Modules。</p><p>对于 Taro 而言支持 CSS Modules 有着重要的意义。Taro 在 1.0 的时候已经支持组件的 <code>style</code> 传入 JavaScript 对象，支持了 CSS Modules 之后组件的 <code>className</code> 也能做这点——这意味着 Taro 完全可以做到 JSX 的一项特性：「Everything in JavaScript」。同时，通过编辑器的自动补全和校验功能，开发者再也不用担心 CSS 类误输入和 CSS 类的命名问题。</p><p>你可以查阅文档 <a href="/docs/css-modules">CSS Modules 的使用</a> 找到 CSS Modules 的开启方法。</p><p>CSS Modules 的支持主要由 Taro 社区的个人开发者 <a href="https://github.com/MrKou47" target="_blank" rel="noopener noreferrer">@MrKou47 (MrKou47) </a> 开发，感谢他的贡献。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="mobx">MobX 支持<a href="#mobx" class="hash-link" aria-label="MobX 支持的直接链接" title="MobX 支持的直接链接">​</a></h2><p>在小程序应用变得日趋复杂的今天，越来越多的开发者选择使用独立的状态管理工具。在 Taro 1.1 你可以选择 Redux 的体系的 <code>redux-thunk</code> 、<code>dva-core</code>、<code>redux-saga</code> 等工具。在 Taro 1.2 我们又带来了 <code>taro-mobx</code>。</p><p>在 Taro 中使用 MobX 非常简单，你可以通过 <code>taro init &lt;your app&gt;</code> 命令创建一个全新的 MobX 模板项目。也可以查阅 <a href="/docs/mobx">使用 MobX</a> 文档将 <code>taro-mobx</code> 接入已有项目。</p><p>MobX 的支持主要由 Taro 社区的个人开发者 <a href="https://github.com/nanjingboy" target="_blank" rel="noopener noreferrer">@nanjingboy (Tom Huang)</a> 开发，感谢他的贡献。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="更多特性">更多特性<a href="#更多特性" class="hash-link" aria-label="更多特性的直接链接" title="更多特性的直接链接">​</a></h2><p>面对社区里给力的个人贡献者，Taro 官方团队也没有闲着。在 Taro 1.2 开发期间通过 commit 关闭了 169 个 issue 的同时，我们还披星戴月带来了更多特性。其中值得一提的是：</p><ul><li>H5 路由系统彻底重构</li><li>小程序组件的 props 支持传入 JSX 元素</li><li>支持 JSX 元素写在 <code>switch-case</code> 中，并支持 <code>if-else</code> 和 <code>switch-case</code> 的多重嵌套</li><li>支持引用别名（alias）</li><li>与百度小程序和抖音小程序官方通力合作，抹平了一大波和微信小程序的差异</li></ul><p>你可以通过 <a href="https://github.com/NervJS/taro/blob/master/CHANGELOG.md" target="_blank" rel="noopener noreferrer">CHANGELOG</a> 查阅完整的功能迭代纪录。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="写在最后">写在最后<a href="#写在最后" class="hash-link" aria-label="写在最后的直接链接" title="写在最后的直接链接">​</a></h2><p>Taro 1.2 最为激动人心的功能显然是「微信小程序转 Taro」 功能，通过这个功能已有的微信小程序应用能转化更好维护的 Taro（类 React） 代码，也能把 Taro 代码转化为多端应用。</p><p>但更重要的是，本次 Taro 1.2 更新的两个核心功能点均由个人开发者贡献——这完全体现了 Taro 社区的活跃、强大和开放。我们也会长期地维护与社区开发者们的关系，与开发者们一同成长，互利互赢。这也是 Taro 选择开源的意义所在。</p><p>如果你有任何想法，或者没有想法，Taro 都非常欢迎你来吐槽或观光：</p><p><a href="https://github.com/NervJS/taro" target="_blank" rel="noopener noreferrer">https://github.com/NervJS/taro</a></p>]]></content:encoded>
            <category>v1</category>
        </item>
        <item>
            <title><![CDATA[Taro 1.1 发布，全面支持微信/百度/支付宝小程序]]></title>
            <link>https://docs.taro.zone/blog/2018-11-05-taro-1-1</link>
            <guid>https://docs.taro.zone/blog/2018-11-05-taro-1-1</guid>
            <pubDate>Mon, 05 Nov 2018 00:00:00 GMT</pubDate>
            <description><![CDATA[在 9 月 16 日的掘金小程序大会上，Taro 正式发布了 1.0 版本。]]></description>
            <content:encoded><![CDATA[<p>在 9 月 16 日的掘金小程序大会上，Taro 正式发布了 1.0 版本。</p><p>作为一个多端统一开发框架，Taro 1.0 版本为广大开发者带来了许多激动人心的特性，帮助开发者们更方便快捷地开发多端应用。</p><p>前进的脚步没有停下，历时一个多月，Taro 1.1 版本正式来袭！</p><p>从 1.0 到 1.1，期间产生了 <strong>500</strong> 余次 Commit，发布了 <strong>23</strong> 个<a href="https://github.com/NervJS/taro/blob/master/CHANGELOG.md" target="_blank" rel="noopener noreferrer">版本</a>，Taro 一直保持高速的迭代频率，只为不断打磨，为它注入更多优秀的特性，让更多开发者受益。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="taro-11-正式来袭">Taro 1.1 正式来袭<a href="#taro-11-正式来袭" class="hash-link" aria-label="Taro 1.1 正式来袭的直接链接" title="Taro 1.1 正式来袭的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="更丰富的平台转换支持">更丰富的平台转换支持<a href="#更丰富的平台转换支持" class="hash-link" aria-label="更丰富的平台转换支持的直接链接" title="更丰富的平台转换支持的直接链接">​</a></h3><p>自从<strong>微信小程序</strong>诞生以来，小程序的优势大家有目共睹，各大互联网厂商开始摩拳擦掌，纷纷布局小程序领域，其中势头最劲的当属<strong>百度智能小程序</strong>和<strong>支付宝小程序</strong>。对于前端而言，既是机遇，因为平台愈多，带来的机会愈多；也是挑战，因为不同的平台总会各有差异，为每个平台维护一份代码，成本显然太高。</p><p>为了降低多端应用的开发成本，在已全面支持 <strong>微信小程序</strong> 的基础上，Taro 在 1.1 版本中加入了对 <strong>百度智能小程序</strong> 和 <strong>支付宝小程序</strong> 的支持。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="支持百度智能小程序">支持百度智能小程序<a href="#支持百度智能小程序" class="hash-link" aria-label="支持百度智能小程序的直接链接" title="支持百度智能小程序的直接链接">​</a></h4><p>升级到 1.1 版本后，即可获得将现有项目转换成百度智能小程序的能力。</p><p>使用如下命令进行百度智能小程序端编译预览及打包：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># npm script</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run dev:swan</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run build:swan</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 仅限全局安装</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ taro build --type swan --watch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ taro build --type swan</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># npx 用户也可以使用</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ npx taro build --type swan --watch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ npx taro build --type swan</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>选择百度智能小程序模式，需要你下载并打开<a href="https://smartprogram.baidu.com/docs/develop/devtools/show_sur/" target="_blank" rel="noopener noreferrer">百度开发者工具</a>，然后在项目编译完后选择项目根目录下 <code>dist</code> 目录进行预览。</p><p>目前已有使用 Taro 开发的首款百度智能小程序 <strong>“京东好物街”</strong> 正式上线，可以使用百度 APP，扫描以下二维码进行体验：</p><p><img loading="lazy" src="https://img13.360buyimg.com/ling/jfs/t1/10931/36/2138/22454/5be02d3fE815a306d/0fc91ade32e59948.jpg" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="支持支付宝小程序">支持支付宝小程序<a href="#支持支付宝小程序" class="hash-link" aria-label="支持支付宝小程序的直接链接" title="支持支付宝小程序的直接链接">​</a></h4><p>支付宝小程序的转换方式与百度智能小程序一致。</p><p>你可以使用如下命令进行百度智能小程序端编译预览及打包：</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># npm script</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run dev:alipay</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run build:alipay</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 仅限全局安装</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ taro build --type alipay --watch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ taro build --type alipay</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># npx 用户也可以使用</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ npx taro build --type alipay --watch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ npx taro build --type alipay</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>选择支付宝小程序模式，需要你下载并打开<a href="https://docs.alipay.com/mini/developer/getting-started/" target="_blank" rel="noopener noreferrer">支付宝小程序开发者工具</a>，然后在项目编译完后选择项目根目录下 <code>dist</code> 目录进行预览。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="新增平台标识">新增平台标识<a href="#新增平台标识" class="hash-link" aria-label="新增平台标识的直接链接" title="新增平台标识的直接链接">​</a></h4><p>在加入 <strong>百度智能小程序</strong> 和 <strong>支付宝小程序</strong> 支持后，目前 Taro 已经提供了对 <strong>5</strong> 个平台的支持，分别是 微信/百度/支付宝/React Native/H5，为了方便开发者书写平台差异化代码，Taro 为每个平台提供了平台标识，并可以通过 <code>process.env.TARO_ENV</code> 来获得，其取值分别如下</p><ul><li><code>weapp</code>，代表微信小程序</li><li><code>h5</code>，代表 H5</li><li><code>rn</code>，代表 React Native</li><li><code>swan</code>，代表百度智能小程序，<strong>1.1 版本新增</strong></li><li><code>alipay</code>，代表支付宝小程序，<strong>1.1 版本新增</strong></li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="开放多端-ui-库打包能力">开放多端 UI 库打包能力<a href="#开放多端-ui-库打包能力" class="hash-link" aria-label="开放多端 UI 库打包能力的直接链接" title="开放多端 UI 库打包能力的直接链接">​</a></h3><p>在 1.0 版本中，Taro 的 <code>build</code> 命令新增了 <code>--ui</code> 参数，来将按照一定规则组织的代码打包成可以在 Taro 中使用的多端 UI 库。 并且，基于这一功能，Taro 推出了首个可以跨多端使用的多端 UI 库 <a href="https://taro-ui.aotu.io/#/" target="_blank" rel="noopener noreferrer">Taro UI</a>。</p><p>为了完善 Taro 生态，扩展 Taro 社区，在 1.1 版本中开放了多端 UI 库的打包能力，并提供了详细的<a href="/docs/ui-lib">开发指南</a>。</p><p>同时 Taro 也提供了官方的多端 UI 库<a href="https://github.com/NervJS/taro-ui-sample" target="_blank" rel="noopener noreferrer">示例</a>，这是一个完整规范的 UI 库开发例子，包含必要的项目改造以及测试套件，基于这个示例，开发者可以快速地开发一个多端 UI 库项目。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="还有更多">还有更多<a href="#还有更多" class="hash-link" aria-label="还有更多的直接链接" title="还有更多的直接链接">​</a></h3><p>在 1.0 版本中，Taro 提供了对 <strong>微信小程序</strong>、<strong>H5</strong> 以及 <strong>React Native</strong> 的支持，而 1.1 版本，在原有支持的基础之上，对每一个平台进行不断完善，让其更加丰富完整。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="微信小程序">微信小程序<a href="#微信小程序" class="hash-link" aria-label="微信小程序的直接链接" title="微信小程序的直接链接">​</a></h4><ul><li><code>F</code> 调用 <code>this.setState</code> 时，JSON Diff 算法 Bug 修复</li><li><code>F</code> 修复 <code>this.setState</code> 回调里再调用 <code>this.setState</code> 的 Bug</li><li><code>A</code> 新增 <code>componentWillPreload</code> 生命周期，用于数据预加载</li><li><code>A</code> 支持在同一作用域对 JSX 赋值</li><li><code>F</code> 多层 Map 循环嵌套包含条件判断时问题修复</li><li><code>F</code> 中文字符被编译成 unicode 码问题修复</li><li><code>F</code> 三元表达式解析问题修复</li><li><code>A</code> 提升字符串模板的性能</li></ul><h4 class="anchor anchorWithStickyNavbar_mCi1" id="h5">H5<a href="#h5" class="hash-link" aria-label="H5的直接链接" title="H5的直接链接">​</a></h4><ul><li><code>A</code> H5 支持分包配置</li><li><code>F</code> 修复 H5 后台页面依然执行生命周期的问题</li><li><code>F</code> 修复 PUT 、DELETE 等请求 body 为对象时无法发送 body 的问题</li><li><code>A</code> 增加 <code>setTabBarStyle</code> 与 <code>setTabBarItem</code> API</li><li><code>A</code> 增加 <code>arrayBufferToBase64</code> 与 <code>base64ToArrayBuffer</code> API</li><li><code>A</code> 编译打包支持将公共 npm 包抽离成 lib 库</li></ul><h4 class="anchor anchorWithStickyNavbar_mCi1" id="react-native">React Native<a href="#react-native" class="hash-link" aria-label="React Native的直接链接" title="React Native的直接链接">​</a></h4><ul><li><code>A</code> 支持 TypeScript</li><li><code>F</code> 修复项目初始化后 less 编译报错</li><li><code>A</code> 添加 config.window.navigationStyle 配置</li><li><code>A</code> 添加 <code>showNavigationBarLoading</code> 与 <code>hideNavigationBarLoading</code> API</li><li><code>A</code> 增加 <code>arrayBufferToBase64</code> 与 <code>base64ToArrayBuffer</code> API</li><li><code>F</code> 修复样式支持警告</li><li><code>A</code> watch 模式时代码按需编译</li><li><code>F</code> 修复同一文件夹下多个 JS 文件样式引用错误</li><li><code>F</code> app.json 的 expo 配置改为可覆盖</li><li><code>A</code> 支持 <code>Taro.pxTransform</code></li><li><code>F</code> iconPath 和 selectedPath 的为同一个路径导致的重复引用的报错</li><li><code>A</code> 在 config 中添加 rn 的 expo 配置</li><li><code>A</code> 支持 deviceRatio 自定义</li></ul><p>更完整的功能迭代记录请参阅 <a href="https://github.com/NervJS/taro/blob/master/CHANGELOG.md" target="_blank" rel="noopener noreferrer">CHANGELOG</a></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="未来规划">未来规划<a href="#未来规划" class="hash-link" aria-label="未来规划的直接链接" title="未来规划的直接链接">​</a></h2><p>Taro 将会继续保持高效迭代，以满足日益增长的多端开发需求</p><p>目前已经确认的开发计划可以参见 <a href="https://github.com/NervJS/taro/blob/master/PLANS.md" target="_blank" rel="noopener noreferrer">Taro 版本开发计划</a>。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="小程序转-taro-代码开启内测">小程序转 Taro 代码开启内测<a href="#小程序转-taro-代码开启内测" class="hash-link" aria-label="小程序转 Taro 代码开启内测的直接链接" title="小程序转 Taro 代码开启内测的直接链接">​</a></h3><p>值得注意的是，激动人心的小程序转 Taro 代码功能已经开发完成，进入内测阶段，可以通过如下命令安装到 <code>canary</code> 版本，进行体验</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i -g @tarojs/cli@canary</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>欢迎针对这一功能，提供宝贵意见，意见入口<a href="https://github.com/NervJS/taro/issues/955" target="_blank" rel="noopener noreferrer">请戳</a> 👈👈👈</p><p>同时，为了帮助 Taro 更好地成长，让 Taro 更好地回馈所有开发者，欢迎您给 Taro 提供<a href="https://github.com/NervJS/taro/issues" target="_blank" rel="noopener noreferrer">优秀想法</a>，或者直接贡献<a href="https://github.com/NervJS/taro/pulls" target="_blank" rel="noopener noreferrer">代码</a>。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[多端统一开发框架 Taro 1.0 正式发布]]></title>
            <link>https://docs.taro.zone/blog/2018-09-18-taro-1-0-0</link>
            <guid>https://docs.taro.zone/blog/2018-09-18-taro-1-0-0</guid>
            <pubDate>Tue, 18 Sep 2018 00:00:00 GMT</pubDate>
            <description><![CDATA[在 2018.6.7 我们对外开源了 多端统一开发框架——Taro。]]></description>
            <content:encoded><![CDATA[<p>在 <code>2018.6.7</code> 我们对外开源了 <a href="https://aotu.io/notes/2018/06/07/Taro/" target="_blank" rel="noopener noreferrer"><code>多端统一开发框架——Taro</code></a>。</p><p>Taro 是一个多端统一开发框架，它支持使用 React 的开发方式来编写可以同时在微信小程序、Web 、React Native 等多个平台上运行的应用，帮助开发者提升开发效率，改善开发体验，降低多端研发成本。</p><p>自开源以来，Taro 一直广受业界关注，其原理与思想也得到了广泛开发者的认可，这对于我们来说无疑是一件令人振奋的事。但是由于初期 Taro 测试与实现方式的不足，导致在开源期间 Bug 较多，受到了一些质疑。为此，我们痛定思痛，积极接受了开源社区的意见与帮助，并努力探索提升 Taro 稳定性与性能的方式。经过不断地迭代完善，已经让 Taro 浴火重生。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="100-真的来了">1.0.0 真的来了<a href="#100-真的来了" class="hash-link" aria-label="1.0.0 真的来了的直接链接" title="1.0.0 真的来了的直接链接">​</a></h2><p>Taro 开源到现在 3 个月时间，累计发布 70 余日常版本及 20 余 Taro 1.0.0 的 <code>beta</code> 版本，经过近百个版本的迭代优化，我们亲身体会到 Taro 的 BUG 反馈越来越少，Taro 越来越健壮且完善，因此，我们有信心推出 1.0.0 正式版。</p><p>Taro 1.0.0 正式版在延续了之前版本优秀特性的同时，增加了更加丰富的特性与功能，并进行了多项小程序端/H5 端的转换优化，同时带来了对 React Native 的转换支持。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="全新的小程序组件化">全新的小程序组件化<a href="#全新的小程序组件化" class="hash-link" aria-label="全新的小程序组件化的直接链接" title="全新的小程序组件化的直接链接">​</a></h4><p>开源之初，由于种种原因，Taro 的微信小程序端组件化采用的是小程序 <code>&lt;template /&gt;</code> 标签来实现的，利用小程序 <code>&lt;template /&gt;</code> 标签的特性，将组件 JS 文件编译成 JS + WXML 模板，在父组件（页面）的模板中通过 <code>&lt;template /&gt;</code> 标签引用子组件的 WXML 模板来进行拼接，从而达到组件化的目的。</p><p>实践证明，<code>Template</code> 模板方案是一个失败的组件化方案，Taro 开源初期的 Bug 主要来源于此。因为这一方案将 JS 逻辑与模板拆分开了，需要手工来保证 JS 与模板中数据一致，这样在循环组件渲染、组件多重嵌套的情况下，要保证组件正确渲染与 <code>props</code> 正确传递的难度非常大，实现的成本也非常高。而且，囿于小程序 <code>&lt;template /&gt;</code> 标签的缺陷，一些功能（例如自定义组件包含子元素，等）无法实现。</p><p>所以，在经过艰辛的探索与实践之后，我们采用了小程序原生组件化来作为 Taro 的小程序端组件化方案，并且通过一些处理，绕开了小程序组件化的诸多限制，为 Taro 的稳定性打下了坚实基础，并带来了以下好处：</p><ul><li>小程序端组件化更加健壮</li><li>尽可能减少由于框架带来的性能问题</li><li>依托官方组件化，方便以后解锁更多可能</li></ul><h4 class="anchor anchorWithStickyNavbar_mCi1" id="全面支持小程序生态">全面支持小程序生态<a href="#全面支持小程序生态" class="hash-link" aria-label="全面支持小程序生态的直接链接" title="全面支持小程序生态的直接链接">​</a></h4><p>为了更好地帮助开发者使用 Taro 开发小程序，在 <code>1.0.0</code> 版本中，我们加强了对小程序生态的支持，主要提现在以下几个方面。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="支持引用小程序端第三方组件库">支持引用小程序端第三方组件库<a href="#支持引用小程序端第三方组件库" class="hash-link" aria-label="支持引用小程序端第三方组件库的直接链接" title="支持引用小程序端第三方组件库的直接链接">​</a></h5><p>受益于小程序组件化重构，我们在 Taro 中支持了直接引用小程序端第三方组件库，让 Taro 可以融入到小程序生态中，为开发者提供更多便利。</p><p>目前实测已经支持了如下知名第三方组件库</p><ul><li><a href="https://github.com/ecomfe/echarts-for-weixin" target="_blank" rel="noopener noreferrer">echarts-for-weixin</a>，ECharts 的微信小程序版本</li><li><a href="https://github.com/TalkingData/iview-weapp" target="_blank" rel="noopener noreferrer">iview-weapp</a>，iview 的微信小程序版本</li><li><a href="https://youzan.github.io/vant-weapp/#/" target="_blank" rel="noopener noreferrer">vant-weapp</a>，轻量、可靠的小程序 UI 组件库</li><li><a href="https://github.com/icindy/wxParse" target="_blank" rel="noopener noreferrer">wxParse</a>，微信小程序富文本解析自定义组件，支持 HTML 及 markdown 解析</li></ul><p><img loading="lazy" src="https://img20.360buyimg.com/uba/jfs/t26989/167/305293473/64582/33fab1f9/5b8e82fdN4c7f85a1.png" class="img_ZOZK"></p><p>当然，Taro 不仅仅只支持上述组件库，只要是按原生小程序规范编写的组件库都能在 Taro 中正常使用。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="支持-taro-代码与原生小程序代码混写">支持 Taro 代码与原生小程序代码混写<a href="#支持-taro-代码与原生小程序代码混写" class="hash-link" aria-label="支持 Taro 代码与原生小程序代码混写的直接链接" title="支持 Taro 代码与原生小程序代码混写的直接链接">​</a></h5><p>与此同时，Taro 支持原生小程序代码与 Taro 代码混写，因此，可以使用 Taro 愉快地将旧的小程序项目慢慢地改造成 Taro 项目了。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="支持小程序端分包加载以及插件引用">支持小程序端分包加载以及插件引用<a href="#支持小程序端分包加载以及插件引用" class="hash-link" aria-label="支持小程序端分包加载以及插件引用的直接链接" title="支持小程序端分包加载以及插件引用的直接链接">​</a></h5><p>我们在 Taro 中也加入对原生小程序分包加载功能的支持，配置的方式与原生小程序基本一致，只需要在 <code>app.js</code> 入口文件中加入 <code>subPackage</code> 字段即可。</p><div class="language-javascript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-javascript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Taro</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">Component</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/taro'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">App</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">pages</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'pages/index'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'pages/logs'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">subPackages</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">root</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'moduleA'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">pages</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'pages/rabbit'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'pages/squirrel'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>同时，Taro 也支持了使用小程序的插件功能，可以在 Taro 中直接引用第三方的插件</p><div class="language-javascript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-javascript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Taro</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">Component</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/taro'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">View</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/Components'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">PageA</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">usingComponents</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string-property property" style="color:#36acaa">'hello-component'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'plugin://myPlugin/hello-component'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">render</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">hello</span><span class="token operator" style="color:#393A34">-</span><span class="token plain">component</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">hello</span><span class="token operator" style="color:#393A34">-</span><span class="token plain">component</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_mCi1" id="小程序-setstate-性能优化">小程序 setState 性能优化<a href="#小程序-setstate-性能优化" class="hash-link" aria-label="小程序 setState 性能优化的直接链接" title="小程序 setState 性能优化的直接链接">​</a></h4><p>在最初的开源版本中，小程序端 <code>setState</code> 仅仅是对小程序 <code>setData</code> 做了一次异步封装，最终调用 <code>setData</code> 更新的时候还是传入了完整数据。</p><p>但众所周知，小程序的视图层目前使用 WebView 作为渲染载体，而逻辑层是由独立的 JavascriptCore 作为运行环境，在架构上，WebView 和 JavascriptCore 都是独立的模块，并不具备数据直接共享的通道。在调用 <code>setData</code> 之后，会将数据使用 <code>JSON.stringify</code> 进行序列化，再拼接成脚本，然后再传给视图层渲染，这样的话，当数据量非常大的时候，小程序就会变得异常卡顿，性能很差。</p><p><img loading="lazy" src="https://img13.360buyimg.com/img/jfs/t27457/22/362930291/23083/36eaf574/5b8f8ef1Nf68a4646.jpg" class="img_ZOZK"></p><p>Taro 在框架级别帮助开发者进行了优化，在 <code>setData</code> 之前进行了一次<strong>数据 Diff</strong>，找到数据的最小更新路径，然后再使用此路径来进行更新。例如</p><div class="language-javascript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-javascript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 初始 state</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">state</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">a</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">b</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">x</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">y</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 调用 this.setState</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">a</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">b</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">x</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">y</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在优化之前，会直接将 <code>this.setState</code> 的数据传给 <code>setData</code>，即</p><div class="language-javascript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-javascript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">$scope</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">a</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">b</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">x</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">y</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>而在优化之后的数据更新则变成了</p><div class="language-javascript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-javascript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">$scope</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">'a[0]'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">'a[1]'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">'b.x.y'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>这样的优化对于小程序来说意义非常重大，可以避免因为数据更新导致的性能问题。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="更加丰富的-jsx-语法支持">更加丰富的 JSX 语法支持<a href="#更加丰富的-jsx-语法支持" class="hash-link" aria-label="更加丰富的 JSX 语法支持的直接链接" title="更加丰富的 JSX 语法支持的直接链接">​</a></h4><p>前面已经提到 Taro 使用 React 语法规范来开发多端应用，这样就必然是采用 JSX 来作为模板，所以 Taro 需要将 JSX 编译成各个端支持的模板，其中以小程序端最为复杂，Taro 需要将 JSX 编译成小程序的 WXML 模板。</p><p>在开源的过程中，Taro 支持的 JSX 写法一直在不断完善，力求让开发体验更加接近于 React，主要包括以下语法支持：</p><ul><li>支持 Ref，提供了更加方便的组件和元素定位方式</li><li>支持 this.props.children 写法，方便进行自定义组件传入子元素</li><li>在循环体内执行函数和表达式</li><li>定义 JSX 作为变量使用</li><li>支持复杂的 if-else 语句</li><li>在 JSX 属性中使用复杂表达式</li><li>在 style 属性中使用对象</li><li>只有使用到的变量才会作为 state 加入到小程序 data，从而精简小程序数据</li></ul><p>目前，除了 Taro 官方 ESLint 插件 <a href="https://github.com/NervJS/taro/tree/master/packages/eslint-plugin-taro" target="_blank" rel="noopener noreferrer">eslint-plugin-taro</a> 中限制的语法之外，其他 JSX 语法基本都支持，而在原生组件化的帮助下，未来将会把 ESLint 的限制也逐条取消。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="react-native-端转换支持">React Native 端转换支持<a href="#react-native-端转换支持" class="hash-link" aria-label="React Native 端转换支持的直接链接" title="React Native 端转换支持的直接链接">​</a></h4><p>在 Taro 1.0.0 中，我们将正式推出 React Native 端的转换支持，可以将现有 Taro 项目转换成 RN 版本，但需要注意对样式的处理，因为 RN 支持的样式非常有限，它是属于 CSS 的子集，具体请移步 <a href="/docs/1.x/before-dev-remind">RN 端转换注意事项</a>。</p><p>Taro 的 RN 端基于 <a href="https://expo.io/" target="_blank" rel="noopener noreferrer">Expo</a> 来进行编译构建 RN 程序，开发方式和编译命令与其他端类似，代码目录结构与现有 Taro 项目也基本一致</p><p>RN 编译预览模式</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># npm script</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run dev:rn</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 仅限全局安装</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ taro build --type rn --watch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># npx用户也可以使用</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ npx taro build --type rn --watch</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>执行完命令之后，Taro 将会开始编译文件，最终获得 RN 端的编译指引，并且启动你的应用了</p><p><img loading="lazy" src="https://img14.360buyimg.com/uba/jfs/t27748/263/340701379/478755/c8b632f8/5b8e8539N35b82790.jpg" class="img_ZOZK"></p><p>React Native 端完整开发流程请移步 <a href="/docs/react-native">React Native 开发流程介绍</a>。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="更加健全的-typescript-支持">更加健全的 TypeScript 支持<a href="#更加健全的-typescript-支持" class="hash-link" aria-label="更加健全的 TypeScript 支持的直接链接" title="更加健全的 TypeScript 支持的直接链接">​</a></h4><p><img loading="lazy" src="https://img14.360buyimg.com/img/jfs/t25927/210/1256109652/8732/ad4062ce/5b8f8ef1N50c24d7b.jpg" class="img_ZOZK"></p><p>随着前端项目的复杂度和规模的增加，近年来越来越多的项目开始使用 TypeScript 进行开发。Taro 也注意到了这一趋势，在开源之初就提供了对 TypeScript 的支持。在社区的帮助下，Taro 对 TypeScript 的支持也更加健全和完善：</p><ul><li><code>@tarojs/cli</code> 可以直接创建 TypeScript 项目</li><li><code>@tarojs/components</code> 包含了所有组件的类型定义</li><li><code>@tarojs/taro</code> 的所有 API 也都包含了类型定义</li><li><code>@tarojs/redux</code> 和 <code>taro-ui</code> 也都全部内置了类型定义</li><li>构建系统的编译器从 Babel 切换到原生 TypeScript 编译器，支持所有 TypeScript 语法</li></ul><h4 class="anchor anchorWithStickyNavbar_mCi1" id="h5-端转换优化">H5 端转换优化<a href="#h5-端转换优化" class="hash-link" aria-label="H5 端转换优化的直接链接" title="H5 端转换优化的直接链接">​</a></h4><p>同时，Taro 在 H5 端的转换中，也进行诸多转换优化，修复了之前版本 H5 下诸多 Bug，让 H5 端路由系统更加健壮，同时开放了 Webpack 配置，可以让开发者自由地进行相关配置。</p><p>最后，附上 Taro 完整的 <a href="https://github.com/NervJS/taro/blob/master/CHANGELOG.md" target="_blank" rel="noopener noreferrer">迭代历程</a>。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="重生之路">重生之路<a href="#重生之路" class="hash-link" aria-label="重生之路的直接链接" title="重生之路的直接链接">​</a></h2><p>正如上文提到，Taro 1.0.0 带来许多新的优秀特性，而同时，在社区内的 Bug 反馈已经愈来愈少，这也是开源期间不断努力打磨的结果。</p><p><img loading="lazy" src="https://img20.360buyimg.com/uba/jfs/t1/5087/4/5578/33760/5ba072d5E58ffa438/8c289d58d3f8527b.png" class="img_ZOZK"></p><p>从 <code>6.7</code> 到行文截止，Taro 一共经历了 <strong>1800</strong> 余次提交，平均每天近 <strong>20</strong> 次，最多的一天达 <strong>30</strong> 次。每一次提交都是进步，每一次提交都让 Taro 更加强大。经过这么多次迭代之后，已经让 Taro 获得重生，尤其是小程序组件化重构完成之后，Taro 从旧版架构的泥潭中一跃而出，成为更加健壮的开发框架。</p><p>在我们自己不断反思、优化的同时，也积极融入开源社区，依托社区的力量去建设 Taro。</p><p>GitHub ISSUES 是检验一个开源项目靠谱程度的标准之一，到目前为止，一共收到了 <strong>500</strong> 余个 ISSUES，已关闭近 <strong>400</strong> 个，关闭率在 <strong>80%</strong> 左右，未关闭的也大部分是一些功能的迭代需求。在 Taro 开发团队内部更是要求每一个 ISSUE 的响应时间不能超过 <strong>24</strong> 小时。正是因为这些 ISSUES ，让我们不断意识到 Taro 的不足，让我们知道如何去进行迭代。</p><p>同时，我们也一直鼓励社区的开发者积极提 PR，一个优秀的开源项目需要依靠整个社区的力量才能完善起来，到目前为止，一共收到了 <strong>120</strong> 余个 PR，已几近全部合入，这些 PR 为 Taro 注入了许多新鲜血液，让 Taro 更加健壮，我们也期望能有更多的开发者可以加入进来，一起来让 Taro 更加美好。</p><p>在 GitHub 上交流之余，我们也为开发者们开通了<a href="https://github.com/NervJS/taro/issues/198" target="_blank" rel="noopener noreferrer">官方微信群</a>供大家一起讨论 Taro 与技术，目前已有超过 <strong>1700</strong> 位开发者在关注、使用 Taro ，期待更多开发者的加入。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="开发者生态完善">开发者生态完善<a href="#开发者生态完善" class="hash-link" aria-label="开发者生态完善的直接链接" title="开发者生态完善的直接链接">​</a></h2><p>在开源之初，Taro 一直处于封闭的状态，没有适配的 UI 库，也无法使用第三方组件库，而这些对开发效率的桎梏非常严重，社区内对此反馈较多。</p><p>所以，在 <code>1.0.0-beta</code> 版本开发期间，Taro 团队开发了多端 UI 库打包功能，提供了 <code>taro build --ui</code> 命令来将按照一定规则组织的代码打包成可以在 Taro 中使用的多端 UI 库。</p><p><img loading="lazy" src="https://img12.360buyimg.com/img/jfs/t25957/217/1263982871/58035/52bedb06/5b8f8ef2Nfde1c993.jpg" class="img_ZOZK"></p><p>并且，基于这一功能，我们推出了首个可以跨多端使用的多端 UI 库 <a href="https://taro-ui.aotu.io/" target="_blank" rel="noopener noreferrer">Taro UI</a>，目前已经支持了微信小程序与 H5 端，不久之后将完成 React Native 端的适配，可以同步提供给 React Native 端使用。</p><p><img loading="lazy" src="https://img10.360buyimg.com/img/jfs/t1/3616/25/135/84031/5b8f8ef2Ebd101c2f/0ec4bd1dae310555.jpg" class="img_ZOZK"></p><p>目前，多端 UI 库打包功能还处于内测阶段，在 Taro 1.0.0 发布之后，这一功能将同步发布，这样更多开发者就可以为 Taro 开发更丰富的多端组件库和 UI 库了。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="未来规划">未来规划<a href="#未来规划" class="hash-link" aria-label="未来规划的直接链接" title="未来规划的直接链接">​</a></h2><p>Taro 将会继续保持迭代，目前已经规划了如下重要功能。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="便捷测试">便捷测试<a href="#便捷测试" class="hash-link" aria-label="便捷测试的直接链接" title="便捷测试的直接链接">​</a></h4><p>在编译时与运行时提供代码诊断的功能，分析代码优劣，判定代码写法是否规范，以便帮助开发者规避一些由于写法带来的问题。</p><p>同时将提供一套测试方案，方便开发者书写并运行组件测试用例，提升代码质量。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="多端同步调试">多端同步调试<a href="#多端同步调试" class="hash-link" aria-label="多端同步调试的直接链接" title="多端同步调试的直接链接">​</a></h4><p>目前 Taro 只能一次调试一个端，这对于开发多端应用来说效率略低，所以，计划提供微信小程序/ H5 / React Native 端同时调试的功能，可以一键启动多端同时编译，从而获得多端同步预览。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="微信小程序h5-代码转-taro-代码">微信小程序/H5 代码转 Taro 代码<a href="#微信小程序h5-代码转-taro-代码" class="hash-link" aria-label="微信小程序/H5 代码转 Taro 代码的直接链接" title="微信小程序/H5 代码转 Taro 代码的直接链接">​</a></h4><p>目前已支持 Taro 代码到小程序代码、 H5 代码的转换，在未来，将提供逆向转换功能，帮助开发者将原本就存在的小程序/H5 项目直接转换成 Taro 项目，从而让原本只能运行在一端的项目获得多端运行的能力，降低开发者的重构成本。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="与-react-新特性保持同步">与 React 新特性保持同步<a href="#与-react-新特性保持同步" class="hash-link" aria-label="与 React 新特性保持同步的直接链接" title="与 React 新特性保持同步的直接链接">​</a></h4><p>Taro 是遵循 React 语法规范的，但是 React 一直在迭代在变化，Taro 作为 React 的追随者也将会保持与 React 新特性同步，让 Taro 最大程度接近 React 开发体验。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="快应用端支持">快应用端支持<a href="#快应用端支持" class="hash-link" aria-label="快应用端支持的直接链接" title="快应用端支持的直接链接">​</a></h4><p>目前 Taro 已经完成了快应用端组件库与 API 的适配，快应用端的文件转换与模板转换也正在开发中，不久的将来就会发布支持快应用端转换的版本。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="支付宝小程序与百度智能小程序支持">支付宝小程序与百度智能小程序支持<a href="#支付宝小程序与百度智能小程序支持" class="hash-link" aria-label="支付宝小程序与百度智能小程序支持的直接链接" title="支付宝小程序与百度智能小程序支持的直接链接">​</a></h4><p>已预研支付宝小程序与百度智能小程序转换的可行性，即将进入开发。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="多端可视化拖拽搭建">多端可视化拖拽搭建<a href="#多端可视化拖拽搭建" class="hash-link" aria-label="多端可视化拖拽搭建的直接链接" title="多端可视化拖拽搭建的直接链接">​</a></h4><p>目前 Taro 是依靠开发者手工编写代码来获得多端应用的，Taro 未来计划提供一个多端可视化拖拽搭建的功能，可以通过拖拽组件的方式来生成多端应用。</p><p>同时，Taro 将联合各大公司小程序开发团队，推出丰富的行业模板，为各行业应用可视化搭建提供完整的解决方案。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="使用案例">使用案例<a href="#使用案例" class="hash-link" aria-label="使用案例的直接链接" title="使用案例的直接链接">​</a></h2><p>在开源期间，随着 Taro 的逐步完善，越来越多的开发者加入到 Taro 的使用、开发中，产生了更多更优秀的使用案例。</p><p><img loading="lazy" src="https://img13.360buyimg.com/uba/jfs/t1/4426/11/5716/492741/5ba072ccE63cc3586/8dd883d27d6a40d1.png" alt="qrcode.png" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="特别鸣谢">特别鸣谢<a href="#特别鸣谢" class="hash-link" aria-label="特别鸣谢的直接链接" title="特别鸣谢的直接链接">​</a></h2><p>Taro 的发展离不开广大开源爱好者的帮助，在此特别鸣谢广大 Taro 的使用者以及 Taro 主要贡献者（排名不分先后）。</p><ul><li><a href="https://github.com/aijiacy" target="_blank" rel="noopener noreferrer">aijiacy</a></li><li><a href="https://github.com/AlexStacker" target="_blank" rel="noopener noreferrer">AlexStacker</a></li><li><a href="https://github.com/AsukaSong" target="_blank" rel="noopener noreferrer">AsukaSong</a></li><li><a href="https://github.com/atzcl" target="_blank" rel="noopener noreferrer">atzcl</a></li><li><a href="https://github.com/Boshen" target="_blank" rel="noopener noreferrer">Boshen</a></li><li><a href="https://github.com/Bless-L" target="_blank" rel="noopener noreferrer">Bless-L</a></li><li><a href="https://github.com/beidan" target="_blank" rel="noopener noreferrer">beidan</a></li><li><a href="https://github.com/Chen-jj" target="_blank" rel="noopener noreferrer">Chen-jj</a></li><li><a href="https://github.com/cuitianze" target="_blank" rel="noopener noreferrer">cuitianze</a></li><li><a href="https://github.com/dogbutcat" target="_blank" rel="noopener noreferrer">dogbutcat</a></li><li><a href="https://github.com/finian" target="_blank" rel="noopener noreferrer">finian</a></li><li><a href="https://github.com/frontlich" target="_blank" rel="noopener noreferrer">frontlich</a></li><li><a href="https://github.com/guotie" target="_blank" rel="noopener noreferrer">guotie</a></li><li><a href="https://github.com/icodytan" target="_blank" rel="noopener noreferrer">icodytan</a></li><li><a href="https://github.com/JerrySir" target="_blank" rel="noopener noreferrer">JerrySir</a></li><li><a href="https://github.com/js-newbee" target="_blank" rel="noopener noreferrer">js-newbee</a></li><li><a href="https://github.com/jas0ncn" target="_blank" rel="noopener noreferrer">jas0ncn</a></li><li><a href="https://github.com/jinjinjin0731" target="_blank" rel="noopener noreferrer">jinjinjin0731</a></li><li><a href="https://github.com/kdong007" target="_blank" rel="noopener noreferrer">kdong007</a></li><li><a href="https://github.com/kenberkeley" target="_blank" rel="noopener noreferrer">kenberkeley</a></li><li><a href="https://github.com/Lizhooh" target="_blank" rel="noopener noreferrer">Lizhooh</a></li><li><a href="https://github.com/Littly" target="_blank" rel="noopener noreferrer">Littly</a></li><li><a href="https://github.com/lolipop99" target="_blank" rel="noopener noreferrer">lolipop99</a></li><li><a href="https://github.com/lijinke666" target="_blank" rel="noopener noreferrer">lijinke666</a></li><li><a href="https://github.com/looch" target="_blank" rel="noopener noreferrer">looch</a></li><li><a href="https://github.com/ladjzero" target="_blank" rel="noopener noreferrer">ladjzero</a></li><li><a href="https://github.com/limichange" target="_blank" rel="noopener noreferrer">limichange</a></li><li><a href="https://github.com/leeenx" target="_blank" rel="noopener noreferrer">leeenx</a></li><li><a href="https://github.com/luckyadam" target="_blank" rel="noopener noreferrer">luckyadam</a></li><li><a href="https://github.com/ly525" target="_blank" rel="noopener noreferrer">ly525</a></li><li><a href="https://github.com/Manjiz" target="_blank" rel="noopener noreferrer">Manjiz</a></li><li><a href="https://github.com/mclockw" target="_blank" rel="noopener noreferrer">mclockw</a></li><li><a href="https://github.com/Mr-Prune" target="_blank" rel="noopener noreferrer">Mr-Prune</a></li><li><a href="https://github.com/missmimia" target="_blank" rel="noopener noreferrer">missmimia</a></li><li><a href="https://github.com/mushan0x0" target="_blank" rel="noopener noreferrer">mushan0x0</a></li><li><a href="https://github.com/Pines-Cheng" target="_blank" rel="noopener noreferrer">Pines-Cheng</a></li><li><a href="https://github.com/rojer95" target="_blank" rel="noopener noreferrer">rojer95</a></li><li><a href="https://github.com/ronffy" target="_blank" rel="noopener noreferrer">ronffy</a></li><li><a href="https://github.com/Songkeys" target="_blank" rel="noopener noreferrer">Songkeys</a></li><li><a href="https://github.com/Simbachen" target="_blank" rel="noopener noreferrer">Simbachen</a></li><li><a href="https://github.com/smoothdvd" target="_blank" rel="noopener noreferrer">smoothdvd</a></li><li><a href="https://github.com/soulhat" target="_blank" rel="noopener noreferrer">soulhat</a></li><li><a href="https://github.com/thewei" target="_blank" rel="noopener noreferrer">thewei</a></li><li><a href="https://github.com/wowlusitong" target="_blank" rel="noopener noreferrer">wowlusitong</a></li><li><a href="https://github.com/xunge0613" target="_blank" rel="noopener noreferrer">xunge0613</a></li><li><a href="https://github.com/YikaJ" target="_blank" rel="noopener noreferrer">YikaJ</a></li><li><a href="https://github.com/yuche" target="_blank" rel="noopener noreferrer">yuche</a></li><li><a href="https://github.com/zaaack" target="_blank" rel="noopener noreferrer">zaaack</a></li><li><a href="https://github.com/zacksleo" target="_blank" rel="noopener noreferrer">zacksleo</a></li><li><a href="https://github.com/ZodiacSyndicate" target="_blank" rel="noopener noreferrer">ZodiacSyndicate</a></li><li><a href="https://github.com/zuoge85" target="_blank" rel="noopener noreferrer">zuoge85</a></li><li><a href="https://github.com/zuorichongxian" target="_blank" rel="noopener noreferrer">zuorichongxian</a></li></ul>]]></content:encoded>
            <category>v1</category>
        </item>
        <item>
            <title><![CDATA[Taro 在京东购物小程序上的实践]]></title>
            <link>https://docs.taro.zone/blog/2018-09-11-taro-in-jd</link>
            <guid>https://docs.taro.zone/blog/2018-09-11-taro-in-jd</guid>
            <pubDate>Tue, 11 Sep 2018 00:00:00 GMT</pubDate>
            <description><![CDATA[在 Taro 发布之后，京东购物小程序也开始了部分页面基于 Taro 的重构.]]></description>
            <content:encoded><![CDATA[<p>在 Taro 发布之后，京东购物小程序也开始了部分页面基于 Taro 的重构.</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="taro-简介">Taro 简介<a href="#taro-简介" class="hash-link" aria-label="Taro 简介的直接链接" title="Taro 简介的直接链接">​</a></h2><p><code>Taro</code> 是一个基于 <code>React</code> 语法规范的多端统一开发框架，大家可以通过 <a href="https://taro.aotu.io/" target="_blank" rel="noopener noreferrer">taro.aotu.io</a> 进一步了解。而前段时间 Taro 发布后，京东购物小程序就开始了部分页面基于 Taro 的重构工作，本文便是对商品分类页使用 Taro 进行代码重构的一些实践分享。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="混合开发模式">混合开发模式<a href="#混合开发模式" class="hash-link" aria-label="混合开发模式的直接链接" title="混合开发模式的直接链接">​</a></h2><p>过去的京东购物小程序未使用任何第三方框架，而是在原生小程序模式的基础上，进行了页面/组件基类、网络请求、本地存储、页面跳转等模块的封装。由于项目庞大（涉及 100 多个页面），把整个项目直接改造成 Taro 的开发方式肯定是不可行的，于是采用这么一种原生小程序与 Taro 相混合的开发模式，将部分旧页面使用 Taro 重构，部分新的页面则直接使用 Taro 进行开发。这里以商品分类页为例，先来看下原京东购物小程序项目的目录结构：</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">├── dist</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   ├── app.js</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   ├── app.json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   ├── app.wxss</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   ├── assets/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   ├── common/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   ├── libs/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   └── pages</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│       ├── cate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│       │   ├── components/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│       │   ├── index.js</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│       │   ├── index.json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│       │   ├── index.wxml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│       │   └── index.wxss</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│       └── index/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── src/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── README.md</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── gulpfile.js</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── package.json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">└── node_modules/</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="1-初始化-taro">1. 初始化 Taro<a href="#1-初始化-taro" class="hash-link" aria-label="1. 初始化 Taro的直接链接" title="1. 初始化 Taro的直接链接">​</a></h3><p>在项目根目录处运行命令 <code>taro init jdwxa-taro</code> 进行初始化，完成后会新增一个名为 <code>jdwxa-taro</code> 的目录，Taro 相关的源代码就写在该目录中：</p><div class="codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-text codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">├── dist/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── src/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">└── jdwxa-taro</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ├── config</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   ├── dev.js</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   ├── index.js</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   └── prod.js</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ├── node_modules/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ├── package.json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ├── project.config.json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    └── src</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ├── app.js</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ├── app.scss</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ├── index.html</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        └── pages</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            └── cate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                ├── index.js</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                └── index.scss</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="2-taro-配置">2. Taro 配置<a href="#2-taro-配置" class="hash-link" aria-label="2. Taro 配置的直接链接" title="2. Taro 配置的直接链接">​</a></h3><p>独立的 Taro 项目会将包括 <code>app.js</code>、<code>app.json</code>、<code>app.wxss</code> 及页面文件均生成在 dist/ 目录中，而混合开发模式下只需要生成单个页面，这里需要对 Taro 进行一些配置，打开并编辑 <code>config/index.js</code> 文件：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">outputRoot</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'../dist'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">weapp</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">appOutput</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">npm</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">dir</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'../../dist/common'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'taro'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>如代码所示，<code>outputRoot</code> 字段为生成目标页面的存放路径，这里把它指向顶层（即原项目）的 dist/ 目录；<code>weapp</code> 部分，我们把 <code>appOutput</code> 设置成 false, 这样就不会生成 app.js、app.json、app.wxss 三个文件了，<code>npm</code> 字段则表示 Taro 运行时框架文件的存放目录，这里遵循原项目的规范，把它指定为 common/ 目录。这样 Taro 编译生成的目标文件就完美地融入进了原小程序项目。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="3-页面开发">3. 页面开发<a href="#3-页面开发" class="hash-link" aria-label="3. 页面开发的直接链接" title="3. 页面开发的直接链接">​</a></h3><p>页面开发过程中，跟原生小程序最大的不同就是 <code>React</code> + <code>JSX</code> 的编码方式了，习惯了原生小程序的同学可能要一些适应过程，具体的编码就不细说了，这里提几点注意事项：</p><ul><li>与小程序的 <code>setData</code> 方法不同，Taro 用于更新页面数据的 <code>setState</code> 是异步的，相关代码的执行时序需要特别注意；</li><li>为了方便 JSX 模板的书写，原先很长的 WXML 内容建议拆分成一些小的组件；</li><li>关于旧组件的复用，无论是小程序原生组件、普通 JS 模块、样式文件或是第三方组件库，都能很好的进行引入调用，这点无需担心；</li><li>目前对于 Taro 编译生成的目标代码，调试起来会有些困难，但对 <code>SourceMap</code> 的支持正在积极开发中。</li></ul><h3 class="anchor anchorWithStickyNavbar_mCi1" id="4-最终效果">4. 最终效果<a href="#4-最终效果" class="hash-link" aria-label="4. 最终效果的直接链接" title="4. 最终效果的直接链接">​</a></h3><p>如今重构后的商品分类页已经在线上稳定运行有一段时间了，可以扫描下面的小程序码进行体验：</p><p><img loading="lazy" src="https://img10.360buyimg.com/img/s360x360_jfs/t1/2914/23/355/264599/5b912565Ecd448c81/fd0362f0724e06e7.jpg" alt="小程序码 of 京东购物小程序 - 商品分类页" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="taro-带来的收益">Taro 带来的收益<a href="#taro-带来的收益" class="hash-link" aria-label="Taro 带来的收益的直接链接" title="Taro 带来的收益的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="多端运行">多端运行<a href="#多端运行" class="hash-link" aria-label="多端运行的直接链接" title="多端运行的直接链接">​</a></h3><p>最大的收益便是可以生成多端版本，避免重复工作、节省开发成本。以分类页为例，只需运行 <code>npm run build:h5</code> 便可生成 H5 版本的分类页，运行效果和小程序一致，大家可以扫描下面的二维码进行体验：</p><p><img loading="lazy" src="https://storage.360buyimg.com/mtd/home/cate-taro-h51536239496540.png" alt="Taro 生成的 H5 版商品分类页" class="img_ZOZK"></p><p>注：以上仅为 Taro 生成的示例页面，由于一些业务组件尚未完全适配两端，所以 H5 版本暂时没有正式投入使用。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="性能提升">性能提升<a href="#性能提升" class="hash-link" aria-label="性能提升的直接链接" title="性能提升的直接链接">​</a></h3><p>小程序项目中遇到的性能问题，大多是频繁地调用 setData 造成的，这是由于每调用一次 <code>setData</code>，小程序内部都会将该部分数据在逻辑层（运行环境 <code>JSCore</code>）进行类似序列化的操作，将数据转换成字符串形式传递给视图层（运行环境 <code>WebView</code>），视图层通过反序列化拿到数据后再进行页面渲染，这个过程下来有一定性能开销。</p><p>所以开发过程中，我们建议尽量对 <code>setData</code> 进行合并，减少调用次数，例如：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">foo</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Strawberry'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">foo</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Strawberry'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">bar</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Fields'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">baz</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Forever'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>以上代码调用了 3 次 <code>setData</code>，造成不必要的性能开销，应对其进行合并：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">foo</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Strawberry'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">bar</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Fields'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">baz</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Forever'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>而使用 Taro 之后，更新数据时调用的 <code>setState</code> 为异步方法，它会自动地对同一事件循环里的多次 <code>setState</code> 调用进行合并处理，此外还会进行数据 <code>diff</code> 优化，自动剔除那些未变更的数据，从而有效避免了此类性能问题。例如：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 初始时</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">state</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">foo</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'1967'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">bar</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">foo</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Strawberry'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">bar</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Fields'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">baz</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Forever'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 第一次更新</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">bar</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">foo</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Norwegian'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">bar</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Fields'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">baz</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Forever'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 紧接着进行第二次更新</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">foo</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'1967'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">bar</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">foo</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Norwegian'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">bar</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Wood'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">baz</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Forever'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>以上代码虽然经过两次 <code>setState</code>，但只有 bar.foo 和 bar.bar 的数据更新了，此时 Taro 内部会自动对数据进行合并、并剔除重复数据，最终执行代码为：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// this.$scope 在小程序环境中为 page 实例</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">$scope</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">'bar.foo'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Norwegian'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">'bar.bar'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Wood'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="其他收益">其他收益<a href="#其他收益" class="hash-link" aria-label="其他收益的直接链接" title="其他收益的直接链接">​</a></h3><p>比起原生小程序开发，Taro 带来了许多激动人心的特性（如支持 TypeScript、NPM、丰富的 JSX 语法、更高级的 ES 特性等等），不仅提升了开发体验，对自动化测试、持续构建等也会有不小的帮助。</p><p>举个例子，京东购物小程序里封装了一个 <code>getImg</code> 方法，该方法接受一个图片 url 及可选的宽高作为参数，然后根据设备类型决定是否使用 webp 格式、根据当前网络环境应用适当的图片压缩率、自动处理协议头和域名转换，最后生成符合目标大小的图片 url。我们要求所有的图片都必须经过 <code>getImg</code> 方法处理后才能进行展示，但由于 JS 方法只能在逻辑层进行调用，处理好后再传递给 <code>WXML</code> 进行展示，使得很难在自动化工具中进行检测，及时发现未调用 <code>getImg</code> 输出图片的情况。</p><p>而使用 Taro 之后，可以直接在 <code>JSX</code> 模板的 <code>Image</code> 标签输出时对 <code>src</code> 调用 <code>getImg</code> 方法进行处理，将此种写法作为规范明确后，就很容易通过自动化工具进行检测了：</p><div class="language-js codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-js codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">render</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Image</span><span class="token plain"> src</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token function" style="color:#d73a49">getImg</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">url</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">750</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>So 对于现有项目来说，不需要进行整体重构，也能很好的将 Taro 集成进去。还等什么，赶紧试试吧~</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[首个多端 UI 组件库 - Taro UI 发布]]></title>
            <link>https://docs.taro.zone/blog/2018-08-24-the-birth-of-taro-ui</link>
            <guid>https://docs.taro.zone/blog/2018-08-24-the-birth-of-taro-ui</guid>
            <pubDate>Fri, 24 Aug 2018 00:00:00 GMT</pubDate>
            <description><![CDATA[image]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://misc.aotu.io/jimczj/2018-08-27taro-ui.jpg" alt="image" class="img_ZOZK"></p><p><a href="https://github.com/NervJS/taro" target="_blank" rel="noopener noreferrer">Taro</a> 是由凹凸实验室倾力打造的多端开发解决方案，旨在让一套代码在多端运行。为了进一步丰富 Taro 的开发生态，让开发者有更好的开发体验和更快的开发速度，凹凸实验室自主设计了一套 UI 组件库，经过两个多月的精雕细琢，终于跟随 Taro 1.0 版本正式发布。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="taro-ui">Taro UI<a href="#taro-ui" class="hash-link" aria-label="Taro UI的直接链接" title="Taro UI的直接链接">​</a></h2><p>Taro UI 是一款由凹凸实验室打造、基于 Taro 编写的多端 UI 组件库。除了高颜值，Taro UI 最大的亮点就是支持多端运行。Taro UI 借助 Taro 支持多端运行的特点，只需解决不同平台 CSS 的表现差异问题，就可以在微信小程序/ H5 / ReactNative 等多端适配运行。</p><p><strong>Github</strong>：<a href="https://github.com/NervJS/taro-ui" target="_blank" rel="noopener noreferrer">https://github.com/NervJS/taro-ui</a></p><p><strong>文档</strong>：<a href="https://taro-ui.aotu.io/" target="_blank" rel="noopener noreferrer">https://taro-ui.aotu.io/</a></p><p><strong>H5 版本预览</strong>：</p><p><img loading="lazy" src="https://user-images.githubusercontent.com/13499146/44632148-8a054080-a9a8-11e8-85a8-dfafd073dfdf.png" alt="image" class="img_ZOZK"></p><p><strong>微信小程序预览</strong>：</p><p><img loading="lazy" src="https://user-images.githubusercontent.com/13499146/44643836-8e5f4700-aa04-11e8-87bd-d930eb04e87c.png" alt="image" class="img_ZOZK"></p><p>第一版组件共有六个模块、三十三个组件，未来还将继续丰富组件，增加一些常用业务组件。</p><p><img loading="lazy" src="https://user-images.githubusercontent.com/13499146/44502719-6d75b980-a6c5-11e8-8491-b6b47d87ee3d.png" alt="image" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="特性">特性<a href="#特性" class="hash-link" aria-label="特性的直接链接" title="特性的直接链接">​</a></h2><ul><li><strong>简单易用</strong>：支持 npm 安装，自动处理 npm 资源之间的依赖关系</li><li><strong>框架支持</strong>：基于 Taro 开发组件，与 Taro 无缝衔接</li><li><strong>多端适配</strong>：一套组件可以在微信小程序/ H5 / ReactNative 等多端适配运行</li><li><strong>样式美观</strong>：小明哥(<a href="https://github.com/at-ui/at-ui" target="_blank" rel="noopener noreferrer">AT-UI</a> 设计者、主程)亲自设计，细节把关，符合现代扁平化设计审美</li><li><strong>组件丰富</strong>：提供丰富的基础组件，覆盖大部分使用场景，满足各种功能需求</li><li><strong>按需引用</strong>：可按需使用独立的组件，不必引入所有文件，可最小化注入到项目中</li><li><strong>多套主题</strong>：内置多套主题颜色，任君选择（将在 1.1 版本开放此特性）</li></ul><h2 class="anchor anchorWithStickyNavbar_mCi1" id="快速开始">快速开始<a href="#快速开始" class="hash-link" aria-label="快速开始的直接链接" title="快速开始的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="安装-taro">安装 Taro<a href="#安装-taro" class="hash-link" aria-label="安装 Taro的直接链接" title="安装 Taro的直接链接">​</a></h3><p>安装 Taro 开发工具 @tarojs/cli</p><p>使用 npm 或者 yarn 全局安装，或者直接使用 <a href="https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b" target="_blank" rel="noopener noreferrer">npx</a></p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> -g @tarojs/cli</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">yarn</span><span class="token plain"> global </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> @tarojs/cli</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="初始化项目">初始化项目<a href="#初始化项目" class="hash-link" aria-label="初始化项目的直接链接" title="初始化项目的直接链接">​</a></h3><p>使用命令创建模板项目</p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">$ taro init myApp</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="安装-taro-ui">安装 Taro UI<a href="#安装-taro-ui" class="hash-link" aria-label="安装 Taro UI的直接链接" title="安装 Taro UI的直接链接">​</a></h3><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token builtin class-name">cd</span><span class="token plain"> myApp</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> i taro-ui</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="使用">使用<a href="#使用" class="hash-link" aria-label="使用的直接链接" title="使用的直接链接">​</a></h3><p>在代码中 <code>import</code> 组件并按照文档说明进行使用</p><p><code>import { AtButton } from 'taro-ui'</code></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="示例">示例<a href="#示例" class="hash-link" aria-label="示例的直接链接" title="示例的直接链接">​</a></h3><p>在 <code>/myApp/src/pages/index/index.jsx</code> 文件添加以下代码</p><div class="language-jsx codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-jsx codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Taro</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">Component</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports maybe-class-name">Config</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/taro'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">View</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/components'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">AtButton</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'taro-ui'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./index.scss'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Index</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">config</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">Config</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">navigationBarTitleText</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'首页'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">render</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">className</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">index</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag class-name" style="color:#00009f">AtButton</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">type</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">primary</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text">按钮文案</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">AtButton</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag class-name" style="color:#00009f">View</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="编译并预览">编译并预览<a href="#编译并预览" class="hash-link" aria-label="编译并预览的直接链接" title="编译并预览的直接链接">​</a></h3><p>进入项目目录开始开发，可以选择小程序预览模式，或者 H5 预览模式，若使用微信小程序预览模式，则需要自行下载并打开微信开发者工具，选择预览项目根目录。</p><p><strong>微信小程序编译预览模式</strong></p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># npm script</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run dev:weapp</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 仅限全局安装</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ taro build --type weapp --watch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># npx用户也可以使用</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ npx taro build --type weapp --watch</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><strong>H5 编译预览模式</strong></p><div class="language-bash codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-bash codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># npm script</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run dev:h5</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 仅限全局安装</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ taro build --type h5 --watch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># npx用户也可以使用</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$ npx taro build --type h5 --watch</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_mCi1" id="意见反馈">意见反馈<a href="#意见反馈" class="hash-link" aria-label="意见反馈的直接链接" title="意见反馈的直接链接">​</a></h2><p>如果有任何的意见或者建议，欢迎在 <a href="https://github.com/NervJS/taro-ui" target="_blank" rel="noopener noreferrer">Github</a> 创建 issue，感谢你的支持和贡献。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[为何我们要用 React 来写小程序 - Taro 诞生记]]></title>
            <link>https://docs.taro.zone/blog/2018-06-25-the-birth-of-taro</link>
            <guid>https://docs.taro.zone/blog/2018-06-25-the-birth-of-taro</guid>
            <pubDate>Mon, 25 Jun 2018 00:00:00 GMT</pubDate>
            <description><![CDATA[从去年微信小程序的诞生，到今年的逐渐火热，以及异军突起的轻应用、百度小程序等的出现，前端可以延伸的领域已经越来越广，当然也意味着业务在不断扩大。这时候，如何通过技术手段来提升开发效率，应对不断增长的业务，就是一个值得探索的话题。本文将对 Taro 诞生的故事，进行深入浅出地介绍，记录下这个忙碌的春夏之交发生的故事。]]></description>
            <content:encoded><![CDATA[<p>从去年微信小程序的诞生，到今年的逐渐火热，以及异军突起的轻应用、百度小程序等的出现，前端可以延伸的领域已经越来越广，当然也意味着业务在不断扩大。这时候，如何通过技术手段来提升开发效率，应对不断增长的业务，就是一个值得探索的话题。本文将对 Taro 诞生的故事，进行深入浅出地介绍，记录下这个忙碌的春夏之交发生的故事。</p><p><img loading="lazy" src="https://img20.360buyimg.com/uba/jfs/t22954/193/251445690/357583/7232c10b/5b2a15e2N23cf1e55.jpg" alt="image" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="让人又爱又恨的微信小程序">让人又爱又恨的微信小程序<a href="#让人又爱又恨的微信小程序" class="hash-link" aria-label="让人又爱又恨的微信小程序的直接链接" title="让人又爱又恨的微信小程序的直接链接">​</a></h2><p>自 <code>2017-1-9</code> 微信小程序（以下简称小程序）诞生以来，就伴随着赞誉与争议不断。从发布上线时的不被大多数人看好，到如今的逐渐火热，甚至说是如日中天也不为过，小程序用时间与实践证明了自己的价值。同时于开发者来说，小程序的生态不断在完善，许多的坑已被踩平，虽然还是存在一些令人诟病的问题，但已经足见微信的诚意了。这个时候要是还没有上手把玩过小程序，就显得非常 OUT 了。</p><p>小程序对于前端程序员来说应该算得上是福音了，用前端相关的技术，获得丝般顺滑的 <code>Native</code> 体验，前端们又可以在产品小姐姐面前硬气一把了。可以说小程序给前端程序员打开了一扇新的大门，大家都应该感谢微信，但是从开发的角度来说，小程序的开发体验就非常值得商榷了，不仅语法上显得有些不伦不类，而且有些莫名其妙的坑也经常让人不经意间感叹一下和谐社会，从市面上层出不穷的小程序开发框架就可见一斑。以下就盘点部分小程序开发的痛点。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="代码组织与语法">代码组织与语法<a href="#代码组织与语法" class="hash-link" aria-label="代码组织与语法的直接链接" title="代码组织与语法的直接链接">​</a></h3><p>在小程序中，一个页面 <code>page</code> 可能拥有 <code>page.js</code>、<code>page.wxss</code>、<code>page.wxml</code> 、<code>page.json</code> 四个文件</p><p><img loading="lazy" src="https://img13.360buyimg.com/img/jfs/t24499/361/448655041/15273/6552407d/5b307974Nfaccdf2d.jpg" class="img_ZOZK"></p><p>这样在开发的时候就需要来回进行文件切换，尤其是在同时开发模板和逻辑的时候，切来切去会显得尤其麻烦，影响开发效率，但小程序原生只支持这么写，就显得比较尴尬了。</p><p>而在语法上，小程序的语法可以说既像 <code>React</code> ，又像 <code>Vue</code>，不能说显得有点不伦不类吧，但在使用上总是感觉有些别扭，对于开发者来说，等于又要学习一套新的语法，提升了学习成本。而且，小程序的模板由于没有编辑器插件的支持，书写的时候也没有智能提示与 lint 检查，书写起来显得有些麻烦。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="命名规范">命名规范<a href="#命名规范" class="hash-link" aria-label="命名规范的直接链接" title="命名规范的直接链接">​</a></h3><p>在小程序中到处可见规范不统一的情况</p><p>例如组件的属性，以最简单的 <code>&lt;button /&gt;</code> 组件为例，在小程序官方文档中，该组件的属性部分截图如下，大家可以感受下</p><p><img loading="lazy" src="https://img10.360buyimg.com/img/jfs/t23614/365/454746445/128330/71fef006/5b307975Ne1ae5de9.jpg" class="img_ZOZK"></p><p><code>&lt;button /&gt;</code> 组件属性名既有以中划线分割多个单词的情况 <code>session-form</code>，也有多个单词连写的情况 <code>bindgetphonenumber</code>。当然这也不是最严重的，你可以说事件绑定的规范就是 <code>bind + 事件名</code> ，而其他属性的规范就是中划线分割单词，我一度以为小程序就是这个作为标准，直到我看到了 <code>&lt;progress /&gt;</code> 组件</p><p><img loading="lazy" src="https://img12.360buyimg.com/img/jfs/t23056/19/454899133/104795/d15cd017/5b307975Nd43ac751.jpg" class="img_ZOZK"></p><p>这和说好的不一样啊喂！</p><p><img loading="lazy" src="https://img11.360buyimg.com/img/jfs/t23071/12/457583038/19273/4ae32c30/5b307974N88af3974.jpg" class="img_ZOZK"></p><p>同样的情况也出现在 <code>页面</code> 与 <code>组件</code> 的生命周期方法中，<code>页面</code> 的生命周期方法有 <code>onLoad</code>、<code>onReady</code>、<code>onUnload</code> 等，但到了 <code>组件</code> 中则是 <code>created</code>、<code>attached</code> 、<code>ready</code> 等，这样规范又不统一了，为啥 <code>页面</code> 的生命周期方法是 <code>on+Xxx</code> 的格式，但到了 <code>组件</code> 里缺不一样了呢，有点费解。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="开发方式">开发方式<a href="#开发方式" class="hash-link" aria-label="开发方式的直接链接" title="开发方式的直接链接">​</a></h3><p>小程序官方提供了 <code>微信开发工具</code> 作为开发编译工具，而对于代码本身没有提供一个类似 <code>webpack</code> 的工程化开发工具，来解决开发中的一些问题，所以小程序原生的开发方式显得不那么现代化，这也是很多小程序开发框架致力于解决的问题。例如，在小程序开发中</p><ul><li><strong>不能使用 <code>npm</code> 管理依赖</strong>，在小程序中需要手动把第三方代码文件下载到本地，然后再 <code>require</code> 进行使用，显得不那么优雅</li><li><strong>不能使用 Sass 等 CSS 预处理器</strong>，由于没有预编译的概念，小程序开发中无法使用市面上流行的 CSS 预处理器，这样会使得样式代码难以管理</li><li><strong>不完整的 ES Next 语法支持</strong>，小程序默认只能支持极少一部分 ES6 规范的语法，而 ES 是不断往前发展的，一些非常优秀的新语法特性就不能使用了</li><li><strong>手动的文件处理</strong>，像图片压缩、代码压缩等等的一些文件操作，必须手工来处理，显得有些繁琐</li></ul><p>以上就是从开发者的角度看到的一些小程序的开发问题，不过纵然有千般困难，我们总要面对，作为新时代的前端开发工程师，我们不能一味忍受问题，要保持技术的头脑，以技术作为武器，用技术手段去提升的我们开发体验。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="突发奇想我能不能用-react-来写小程序">突发奇想：我能不能用 React 来写小程序<a href="#突发奇想我能不能用-react-来写小程序" class="hash-link" aria-label="突发奇想：我能不能用 React 来写小程序的直接链接" title="突发奇想：我能不能用 React 来写小程序的直接链接">​</a></h2><p>目前前端界言及前端框架，必离不开依然保持着统治地位的 <code>React</code> 与 <code>Vue</code>，这两个都是非常优秀的前端 UI 框架，而且在网上也经常能看到两个框架的粉丝之间热情交流，碰撞出一些思想火花，显得社区异常活跃。</p><p>而我们团队也在去年勇敢地抛弃了历史包袱，非常荣幸地引入了 <code>React</code> 开发方式，让我们团队丢掉了煤油灯，开始通上了电。而且也研发出了一款优秀的类 <code>React</code> 框架 <code>Nerv</code> ，让我们和 <code>React</code> 开发思想结合得更深。</p><p>与小程序的开发方式相比，<code>React</code> 明显显得更加现代化、规范化，而且 <code>React</code> 天生组件化更适合我们的业务开发，<code>JSX</code> 也比字符串模板有更强的表现力。那么这时候我们就在思考，我们能不能用 <code>React</code> 来写小程序？</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="理性地探索">理性地探索<a href="#理性地探索" class="hash-link" aria-label="理性地探索的直接链接" title="理性地探索的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_mCi1" id="类比">类比<a href="#类比" class="hash-link" aria-label="类比的直接链接" title="类比的直接链接">​</a></h4><p>通过对比体验 小程序和 <code>React</code> ，我们还是能发现两者之间相似的地方</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="生命周期">生命周期<a href="#生命周期" class="hash-link" aria-label="生命周期的直接链接" title="生命周期的直接链接">​</a></h5><p>小程序的生命周期和 <code>React</code> 的生命周期，在很大程度上是类似的，我们甚至能找到他们之间的对应关系</p><p>app 及页面的生命周期</p><table><thead><tr><th>小程序</th><th align="center">React</th></tr></thead><tbody><tr><td>onLaunch</td><td align="center">componentWillMount</td></tr><tr><td>onLoad</td><td align="center">componentWillMount</td></tr><tr><td>onReady</td><td align="center">componentDidMount</td></tr><tr><td>onShow</td><td align="center">不支持，需要特殊处理</td></tr><tr><td>onHide</td><td align="center">不支持，需要特殊处理</td></tr><tr><td>onUnload</td><td align="center">componentWillUnmount</td></tr></tbody></table><p>可以看出，对于 <code>app</code> 及 <code>页面</code> 来说，除了 <code>onShow</code> 与 <code>onHide</code> 两个方法，其他方法都能在 <code>React</code> 中找到对应。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="数据更新方式">数据更新方式<a href="#数据更新方式" class="hash-link" aria-label="数据更新方式的直接链接" title="数据更新方式的直接链接">​</a></h5><p>在 <code>React</code> 中，组件的内部数据是用 <code>state</code> 来进行管理的，而在小程序中组件的内部数据都是用 <code>data</code> 来进行管理，两者具有一定相似性。而同时在 <code>React</code> 中，我们更新数据使用的是 <code>setState</code> 方法，传入新的数据或者生成新数据的函数，从而更新相应视图。在小程序中，则对应的有 <code>setData</code> 方法，传入新的数据，从而更新视图。</p><p>两者都是以数据驱动视图的方式进行更新，而且 <code>api</code> 神似。</p><h5 class="anchor anchorWithStickyNavbar_mCi1" id="事件绑定">事件绑定<a href="#事件绑定" class="hash-link" aria-label="事件绑定的直接链接" title="事件绑定的直接链接">​</a></h5><p>小程序中绑定事件使用的是 <code>bind + 事件名</code> 的方式，例如点击事件，小程序中是 <code>bindtap</code></p><div class="language-handlebars codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-handlebars codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">view</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">bindtap</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag attr-value" style="color:#e3116c">handlClick</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">1</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>而在 <code>React</code> 里，则是 <code>on + 事件名</code> 的方式，例如点击事件， <code>React</code> web 中是 <code>onClick</code></p><div class="language-javascript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-javascript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token plain"> onClick</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">handlClick</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token number" style="color:#36acaa">1</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>虽然看上去不一样，但其实是可以类比的，我们只需要在编译时将 <code>on + 事件名</code> 的形式编译成 <code>bind + 事件名</code> 的形式就可以了。</p><blockquote><p>如此看来，两者之间有些相似，用 <code>React</code> 来写小程序貌似是可行的，但接下来我们就发现了巨大的差异。</p></blockquote><h3 class="anchor anchorWithStickyNavbar_mCi1" id="巨大的差异">巨大的差异<a href="#巨大的差异" class="hash-link" aria-label="巨大的差异的直接链接" title="巨大的差异的直接链接">​</a></h3><p><code>React</code> 与小程序之间最大的差异就是他们的模板了，在 <code>React</code> 中，是使用 <code>JSX</code> 来作为组件的模板的，而小程序则与 <code>Vue</code> 一样，是使用字符串模板的。这样两者之间就有着巨大的差异了。</p><p>JSX</p><div class="language-javascript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-javascript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">render</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token plain"> className</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'index'</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">state</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">list</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">item</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> idx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token plain"> key</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">idx</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">item</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Button</span><span class="token plain"> onClick</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">goto</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">走你</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">Button</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>小程序模板</p><div class="language-htmlbars codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-htmlbars codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token plain">&lt;view class='index'&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  &lt;view wx:key='{idx}' wx:for='{{list}}' wx:for-item='item' wx:for-index='idx'&gt;{{item}}&lt;/view&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  &lt;view bindtap='goto'&gt;走你&lt;/view&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;/view&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>众所周知，<code>JSX</code> 其实本质上就是 <code>JS</code>，我们可以在里面写任意的逻辑代码，这样一来就比字符串模板的表现力与操作性要强多了，况且，小程序的字符串模板功能比较羸弱，只有一些比较基本的功能。那这样的话，要如何来实现用 <code>JSX</code> 来写小程序模板呢。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="编译原理的力量">编译原理的力量<a href="#编译原理的力量" class="hash-link" aria-label="编译原理的力量的直接链接" title="编译原理的力量的直接链接">​</a></h3><p>我们可以仔细来分析我们的需求，我们期望使用 <code>JSX</code> 来书写小程序模板，但小程序显然是不支持执行 <code>JSX</code> 代码的（要是支持的话，Taro 应该也就不存在了吧），我们也不能期望微信能给我们开个后门来跑 <code>JSX</code>。那么这个时候我们就想，我们要是能够将 <code>JSX</code> 编译成小程序模板就好了。</p><p>事实上在我们平时的开发中，这种编译的操作到处可见，<code>babel</code> 就是我们最常用的 <code>JS 代码编译器</code>，一般浏览器是不能支持一些非常新的语法特性的，但我们又想使用它们，这个时候就可以借助 <code>babel</code> 来将我们的高版本的 ES 代码，编译成浏览器可以运行的 ES 代码。而我们像要将 <code>JSX</code>编译成小程序模板，也是同样的道理。我们首先来了解一下 <code>Babel</code> 的运行机制。</p><p><code>Babel</code> 作为一个 <code>代码编译器</code> ，能够将 ES6/7/8 的代码编译成 ES5 的代码，其核心利用的就是计算中非常基础的编译原理知识，将输入语言代码，通过编译器执行，输出目标语言的代码。编译原理的一般过程就是，输入源程序，经过词法分析、语法分析，构造出语法树，再经过语义分析，理解程序正确与否，再对语法树做出需要的操作与优化，最终生成目标代码。</p><p><img loading="lazy" src="https://m.360buyimg.com/img/jfs/t23551/19/471755927/52473/e349bf6c/5b307975N60463097.jpg" class="img_ZOZK"></p><p><code>Babel</code> 的编译过程亦是如此，主要包含三个阶段</p><ul><li>解析过程，在这个过程中进行词法、语法分析，以及语义分析，生成符合 <a href="https://github.com/estree/estree" target="_blank" rel="noopener noreferrer">ESTree 标准</a> 虚拟语法树(AST)</li><li>转换过程，针对 AST 做出已定义好的操作，<code>babel</code> 的配置文件 <code>.babelrc</code> 中定义的 <code>preset</code> 、 <code>plugin</code> 就是在这一步中执行并改变 AST 的</li><li>生成过程，将前一步转换好的 AST 生成目标代码的字符串</li></ul><p>为了更好地理解这些过程，大家可以利用 <a href="https://astexplorer.net/" target="_blank" rel="noopener noreferrer">Ast Explorer</a> 这个网站接一下自己的代码，感受一下每一部分代码所对应的 AST 结构。</p><p><img loading="lazy" src="https://img30.360buyimg.com/img/jfs/t22843/37/455454462/106864/1d96f394/5b307975Nf78e5829.jpg" class="img_ZOZK"></p><p>可以看到，一份源码经过编译器解析后，会变成类似如下的结构</p><div class="language-javascript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-javascript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Program"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">start</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">end</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">78</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">loc</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> start</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> end </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">sourceType</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"module"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">body</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"VariableDeclaration"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"VariableDeclaration"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"FunctionDeclaration"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"ExpressionStatement"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>其中，<code>body</code> 里包含的就是我们示例代码的语法树结构，第一个 <code>VariableDeclaration</code> 对应的是 <code>const a = 1</code>，第三个 <code>FunctionDeclaration</code> 对应的则是 <code>function sum (a, b) { }</code>，分别就是 JS 中的变量定义与函数定义，每一个树节点里都会包含许多子节点，这样就形成了一个树形结构，更多的节点类型，请参考 <a href="https://github.com/babel/babel/tree/master/packages/babel-types" target="_blank" rel="noopener noreferrer">babel types</a>。</p><p>当然我们在这儿只是简单介绍下编译原理与 <code>babel</code>，编译原理是一门非常深奥的课程， <code>babel</code> 也是一个非常优秀的工具，希望在后续的文章中能和大家再详细探讨这一部分内容。</p><p>再次回到我们的需求，将 <code>JSX</code> 编译成小程序模板，非常幸运的是 <code>babel</code> 的核心编译器 <code>babylon</code> 是支持对 <code>JSX</code> 语法的解析的，我们可以直接利用它来帮我们构造 AST，而我们需要专注的核心就是如何对 AST 进行转换操作，得出我们需要的新 AST，再将新 AST 进行递归遍历，生成小程序的模板。</p><p><code>JSX</code> 代码</p><div class="language-javascript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-javascript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token plain"> className</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"index"</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Button</span><span class="token plain"> className</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"add_btn"</span><span class="token plain"> onClick</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">props</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">add</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">Button</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Button</span><span class="token plain"> className</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"dec_btn"</span><span class="token plain"> onClick</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">props</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">dec</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">Button</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Button</span><span class="token plain"> className</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"dec_btn"</span><span class="token plain"> onClick</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">props</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">asyncAdd</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    async</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">Button</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">props</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">counter</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">num</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token constant" style="color:#36acaa">A</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Button</span><span class="token plain"> onClick</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">goto</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">走你</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">Button</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Image</span><span class="token plain"> src</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">sd</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>编译生成小程序模板</p><div class="language-handlebars codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-handlebars codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">import</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">src</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag attr-value" style="color:#e3116c">../../components/A/A.wxml</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">import</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">block</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">view</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag attr-value" style="color:#e3116c">index</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag attr-value" style="color:#e3116c">add_btn</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">bindtap</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag attr-value" style="color:#e3116c">add</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">+</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag attr-value" style="color:#e3116c">dec_btn</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">bindtap</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag attr-value" style="color:#e3116c">dec</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">-</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag attr-value" style="color:#e3116c">dec_btn</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">bindtap</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag attr-value" style="color:#e3116c">asyncAdd</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">async</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token handlebars language-handlebars delimiter punctuation" style="color:#393A34">{{</span><span class="token handlebars language-handlebars variable" style="color:#36acaa">counter</span><span class="token handlebars language-handlebars punctuation" style="color:#393A34">.</span><span class="token handlebars language-handlebars variable" style="color:#36acaa">num</span><span class="token handlebars language-handlebars delimiter punctuation" style="color:#393A34">}}</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">template</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">is</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag attr-value" style="color:#e3116c">A</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">data</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag attr-value handlebars language-handlebars delimiter punctuation" style="color:#393A34">{{</span><span class="token tag attr-value handlebars language-handlebars punctuation" style="color:#393A34">.</span><span class="token tag attr-value handlebars language-handlebars punctuation" style="color:#393A34">.</span><span class="token tag attr-value handlebars language-handlebars punctuation" style="color:#393A34">.</span><span class="token tag attr-value handlebars language-handlebars variable" style="color:#36acaa">$$A</span><span class="token tag attr-value handlebars language-handlebars delimiter punctuation" style="color:#393A34">}}</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">template</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">button</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">bindtap</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag attr-value" style="color:#e3116c">goto</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">走你</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">button</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">image</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">src</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag attr-value handlebars language-handlebars delimiter punctuation" style="color:#393A34">{{</span><span class="token tag attr-value handlebars language-handlebars variable" style="color:#36acaa">sd</span><span class="token tag attr-value handlebars language-handlebars delimiter punctuation" style="color:#393A34">}}</span><span class="token tag attr-value punctuation" style="color:#393A34">'</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">view</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">block</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>这时候，聪明的你应该就能发现问题的难点所在了，要知道小程序的模板只是字符串，而 <code>JSX</code> 则是真正的 JS 代码扩展，其语法之丰富，显然不是字符串模板所能比，在这一步中，我们要做的操作，包括但不仅限于如下</p><ul><li>理解三目运算符与逻辑表达式，例如三目运算符 <code>abc ? : &lt;View&gt;1&lt;/View&gt; : &lt;View&gt;2&lt;/View&gt;</code> 需要编译成 <code>&lt;view wx:if="{{abc}}"&gt;1&lt;/view&gt;&lt;view wx:else&gt;2&lt;/view&gt;</code></li><li>理解数组 <code>map</code> 语法，例如 <code>map</code> 的使用 <code>abc.map(item =&gt; &lt;View&gt;item&lt;/View&gt;)</code> 需要编译成 <code>&lt;view wx:for="{{abc}}" wx:for-item="item"&gt;item&lt;/view&gt;</code></li><li>等等</li></ul><p>以上仅仅是我们转换规则的冰山一角，<code>JSX</code> 的写法极其灵活多变，我们只能通过穷举的方式，将常用的、React 官方推荐的写法作为转换规则加以支持，而一些比较生僻的，或者是不那么推荐的写的写法则不做支持，转而以 <code>eslint</code> 插件的方式，提示用户进行修改。目前我们支持的 <code>JSX</code> 转换规则，大致能覆盖到 <code>JSX</code> 80% 的写法操作。</p><p>关于 JSX 转小程序模板这一部分，我们将在后续的技术原理分析系列文章中，详细为大家介绍。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="还能不能干点别的">还能不能干点别的<a href="#还能不能干点别的" class="hash-link" aria-label="还能不能干点别的的直接链接" title="还能不能干点别的的直接链接">​</a></h2><p>经过我们一次次的探索，以及一波波猛如虎的操作，我们已经可以将类 React 代码转成小程序可以跑的代码了，也就是说我们已经可以正式以 React 的方式来写小程序的代码了。喜大普奔！但是我们激动之余，冷静下来继续思考，我们还能不能干点别的有意思的事情呢。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="分析一下需求">分析一下需求<a href="#分析一下需求" class="hash-link" aria-label="分析一下需求的直接链接" title="分析一下需求的直接链接">​</a></h3><p>我们发现，在平常的工作中，我们业务通常有一些多端的需求，就是要求小程序要有，H5 要有，甚至 RN 也能有就最好了，我猜产品经理还看不上快应用，不然肯定要求我们快应用也上一套吧，反正你们不是经常号称代码优秀、高度可复用么。这个时候，你就会发现，差不多的界面和逻辑，你可能需要重复写上好几轮，这时候要是有个多端代码生成工具就好了，只写一份代码，可以多端运行。Write once, run anywhere，相信是所有工程师的梦想。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="依然编译原理的力量">依然编译原理的力量<a href="#依然编译原理的力量" class="hash-link" aria-label="依然编译原理的力量的直接链接" title="依然编译原理的力量的直接链接">​</a></h3><p>这时候我们回忆一下前文的内容，将一份代码编译成多端代码，这不正是编译原理干的事么，我们可以输入一份源代码，针对不同的端设定好对应的转换规则，再一键转换出对应端的代码。而且由于我们已经遵循 React 语法了，那我们再转成 H5 端（使用 Nerv）与 RN 端（使用 React）也就有了天然的优势。</p><p><img loading="lazy" src="https://img11.360buyimg.com/img/jfs/t23863/65/477773801/39493/d1292897/5b307974Na1febb30.jpg" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="设计思路补完">设计思路补完<a href="#设计思路补完" class="hash-link" aria-label="设计思路补完的直接链接" title="设计思路补完的直接链接">​</a></h3><p>但是仔细思考我们又会发现，仅仅将代码按照对应语法规则转换过去后，还远远不够，因为不同端会有自己的原生组件，端能力 API 等等，代码直接转换过去后，可能不能直接执行。例如，小程序中普通的容器组件用的是 <code>&lt;view /&gt;</code>，而在 H5 中则是 <code>&lt;div /&gt;</code>；小程序中提供了丰富的端能力 API，例如网络请求、文件下载、数据缓存等，而在 H5 中对应功能的 API 则不一致。</p><p>所以，为了弥补不同端的差异，我们需要订制好一个统一的组件库标准，以及统一的 API 标准，在不同的端依靠它们的语法与能力去实现这个组件库与 API，同时还要为不同的端编写相应的运行时框架，负责初始化等等操作。通过以上这些操作，我们就能实现一份一键生成多端的需求了。在 Taro 最初的设计中，我们组件库与 API 的标准就是源自小程序的，因为我们觉得既然已经有定义好的组件库与 API 标准，那为啥不直接拿来使用呢，这样不仅省去了定制标准的冥思苦想，同时也省去了为小程序开发组件库与 API 的麻烦，只需要让其他端来向小程序靠齐就好。</p><p>可能有些人会有疑问，既然是为不同的端实现了对应的组件库与端能力 API （小程序除外，因为组件库和 API 的标准都是源自小程序），那么是怎么能够只写一份代码就够了呢？因为我们有编译的操作，在书写代码的时候，只需要引入标准组件库 <code>@tarojs/components</code> 与运行时框架 <code>@tarojs/taro</code> ，代码经过编译之后，会变成对应端所需要的库。</p><p><img loading="lazy" src="https://img14.360buyimg.com/img/jfs/t21535/241/1645070830/74027/775c8a15/5b307976Nce466138.jpg" class="img_ZOZK"></p><p>既然组件库以及端能力都是依靠不同的端做不同实现来抹平差异，那么同样的，如果我们想为 Taro 引入更多的功能支持的话，有时候也需要按照这个套路来。例如，为了提升开发便利性，我们为 Taro 加入了 <code>Redux</code> 支持，我们的做法就是，在小程序端，我们实现了 <code>@tarojs/redux</code> 这个库来作为小程序的 <code>Redux</code> 辅助库，并且以他作为基准库，它具有和 <code>react-redux</code> 一致的 API，在书写代码的时候，引用的都是 <code>@tarojs/redux</code> ，经过编译后，在 H5 端会替换成 <code>nerv-redux</code>（<code>Nerv</code>的 <code>Redux</code> 辅助库），在 RN 端会替换成 <code>react-redux</code>。这样就实现了 <code>Redux</code> 在 Taro 中的多端支持。</p><p><img loading="lazy" src="https://img30.360buyimg.com/uba/jfs/t22360/120/839096197/151922/229ceba4/5b1a6fcdNed7d4039.jpg" class="img_ZOZK"></p><p>以上就是 Taro 的整体设计思路，里面还有很多细节没有展开去阐述，可能大家会觉得有些意犹未尽，后续我们将会产出一系列的文章来阐述 Taro 的技术细节，例如 《Taro 开发工具原理分析》、《Taro 代码编译的背后》、《深入浅出 JSX 转小程序模板》等等。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="最后的最后">最后的最后<a href="#最后的最后" class="hash-link" aria-label="最后的最后的直接链接" title="最后的最后的直接链接">​</a></h2><p>Taro 从立项之初到现在已经差不多有了三个月左右的时间，从最初的激烈讨论方案，各种思想的碰撞，到方案逐渐成型，进入火热的开发迭代，再到现在的小程序端和 H5 端顺利支持，从而决定走向开源。这一路走来，收获颇丰，既有跟团队小伙伴一起创造的激动，也有无数个日夜加班的苦思。Taro 是凹凸实验室的诚意之作，我们也将会一直维护下去，希望 Taro 能越来越好，帮助更多人创造更多价值。</p><p>项目官网：<a href="https://taro.jd.com/" target="_blank" rel="noopener noreferrer">https://taro.jd.com/</a></p><p>项目 GitHub：<a href="https://github.com/NervJS/taro" target="_blank" rel="noopener noreferrer">https://github.com/NervJS/taro</a></p><p><strong>同时，有任何关于 Taro 希望沟通交流的，欢迎~</strong></p><p><img loading="lazy" src="https://img20.360buyimg.com/uba/jfs/t20197/283/1687168874/136042/2b4d811f/5b30a65cN9d1f03f1.png" class="img_ZOZK"></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[多端统一开发框架 - Taro]]></title>
            <link>https://docs.taro.zone/blog/2018-06-07-Taro</link>
            <guid>https://docs.taro.zone/blog/2018-06-07-Taro</guid>
            <pubDate>Thu, 07 Jun 2018 00:00:00 GMT</pubDate>
            <description><![CDATA[image]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://img11.360buyimg.com/uba/jfs/t21205/91/853520716/145629/b03d7fa7/5b19f383N6a30536b.jpg" alt="image" class="img_ZOZK"></p><p>使用 Taro，我们可以只书写一套代码，再通过 Taro 的编译工具，将源代码分别编译出可以在不同端（微信小程序、H5、App 端等）运行的代码。同时 Taro 还提供开箱即用的语法检测和自动补全等功能，有效地提升了开发体验和开发效率。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="taro-是什么">Taro 是什么？<a href="#taro-是什么" class="hash-link" aria-label="Taro 是什么？的直接链接" title="Taro 是什么？的直接链接">​</a></h2><p>Taro 是由凹凸实验室打造的一套遵循 React 语法规范的多端统一开发框架。</p><p>现如今市面上端的形态多种多样，Web、App 端(React Native)、微信小程序等各种端大行其道，当业务要求同时在不同的端都要求有所表现的时候，针对不同的端去编写多套代码的成本显然非常高，这时候只编写一套代码就能够适配到多端的能力就显得极为需要。</p><p>使用 Taro，我们可以只书写一套代码，再通过 Taro 的编译工具，将源代码分别编译出可以在不同端（微信小程序、H5、App 端等）运行的代码。同时 Taro 还提供开箱即用的语法检测和自动补全等功能，有效地提升了开发体验和开发效率。</p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="taro-能提供什么">Taro 能提供什么？<a href="#taro-能提供什么" class="hash-link" aria-label="Taro 能提供什么？的直接链接" title="Taro 能提供什么？的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_mCi1" id="一次编写多端运行">一次编写，多端运行<a href="#一次编写多端运行" class="hash-link" aria-label="一次编写，多端运行的直接链接" title="一次编写，多端运行的直接链接">​</a></h3><p>既然是一个多端解决方案，Taro 最重要的能力当然是写一套代码输出多端皆可运行的代码。目前 Taro 已经支持一套代码同时生成 H5 和小程序，App 端(React Native)端也即将支持，同时诸如快应用等端也将得到支持。</p><p>同时 Taro 也已经投入到了生产环境使用，目前已经支撑了一个 3 万行代码小程序 <a href="https://www.toplife.com/" target="_blank" rel="noopener noreferrer">TOPLIFE</a> 的开发并上线。京东购物 小程序和 一起有局 小程序也在使用 Taro 部分重构中，即将上线。未来也将接入更多业务。</p><p><img loading="lazy" src="https://img14.360buyimg.com/uba/jfs/t21817/73/625556299/346228/96240192/5b14a81eN8e6a43db.png" alt="qrcode.png" class="img_ZOZK"></p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="现代前端开发流程">现代前端开发流程<a href="#现代前端开发流程" class="hash-link" aria-label="现代前端开发流程的直接链接" title="现代前端开发流程的直接链接">​</a></h3><p>和微信自带的小程序框架不一样，Taro 积极拥抱社区现有的现代开发流程，包括但不限于：</p><ul><li>NPM 包管理系统</li><li>ES6+ 语法</li><li>自由的资源引用</li><li>CSS 预处理器和后处理器（SCSS、Less、PostCSS）</li></ul><p>对于微信小程序的编译流程，我们从 <a href="https://parceljs.org/" target="_blank" rel="noopener noreferrer">Parcel</a> 得到灵感，自研了一套打包机制将 AST 不断传递，因此代码分析的速度得到了很大的提高。一台 2015 年 的 15 寸 RMBP 在编译上百个组件时仅需要大约 15 秒左右。</p><h3 class="anchor anchorWithStickyNavbar_mCi1" id="和-react-完全一致的-api-和组件化系统">和 React 完全一致的 API 和组件化系统<a href="#和-react-完全一致的-api-和组件化系统" class="hash-link" aria-label="和 React 完全一致的 API 和组件化系统的直接链接" title="和 React 完全一致的 API 和组件化系统的直接链接">​</a></h3><p>在 Taro 中，你不用像小程序一样区分什么是 <code>App</code> 组件，什么是 <code>Page</code> 组件，什么是 <code>Component</code> 组件，Taro 全都是 <code>Component</code> 组件，并且和 React 的生命周期完全一致。可以说，一旦你掌握了 React，那就几乎掌握了 Taro。而学习 React 的资源也几乎是汗牛充栋，完全不用担心学不会。</p><p>Taro 和 React 一样，同样使用声明式的 JSX 语法。相比起字符串的模板语法，JSX 在处理精细复杂需求的时候会更得心应手。</p><div class="language-javascript codeBlockContainer_nCaG theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_OzzQ"><pre tabindex="0" class="prism-code language-javascript codeBlock_JYvI thin-scrollbar"><code class="codeBlockLines__Tnz"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 一个典型的 Taro 组件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Taro</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">Component</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/taro'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">View</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports maybe-class-name">Button</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@tarojs/components'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Home</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">Component</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">constructor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">props</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">super</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">props</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">state</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">title</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'首页'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">list</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">componentWillMount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">componentDidMount</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">componentWillUpdate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">nextProps</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> nextState</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">componentDidUpdate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">prevProps</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> prevState</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">shouldComponentUpdate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">nextProps</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> nextState</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">add</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// dosth</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">render</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> list</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> title </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">state</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token plain"> className</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"index"</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token plain"> className</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"title"</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">title</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token plain"> className</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"content"</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">list</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">item</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">View</span><span class="token plain"> className</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"item"</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">item</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token maybe-class-name">Button</span><span class="token plain"> className</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"add"</span><span class="token plain"> onClick</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">add</span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            添加</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">Button</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token operator" style="color:#393A34">/</span><span class="token maybe-class-name">View</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup_lxv5"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_cFPs" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_BYBy"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_O1IT"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_mCi1" id="良好的开发效率和体验">良好的开发效率和体验<a href="#良好的开发效率和体验" class="hash-link" aria-label="良好的开发效率和体验的直接链接" title="良好的开发效率和体验的直接链接">​</a></h3><p>鉴于 Taro 的语法和 React 完全一样，因此编辑器/IDE 能够对 Taro 的支持和 React 是几乎一样的。现代的编辑器默认都对 JSX 进行了支持，如果没有，找一个插件也是非常容易的事情。但毕竟我们做 Taro 就是为了提升开发效率和开发体验，而真正使用 Taro 的人就是我们自己或正坐在我们旁边的同事。因此在此基础上，我们又对 Taro 开发体验进行了进一步加强。</p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="自定义-eslint-规则">自定义 ESLint 规则<a href="#自定义-eslint-规则" class="hash-link" aria-label="自定义 ESLint 规则的直接链接" title="自定义 ESLint 规则的直接链接">​</a></h4><p>我们之前提到过，当学会了 React，其实也差不多会 Taro 了。其中很重要的一个原因就是我们对 Taro 不支持的语法和特性单独写了 ESLint 规则：开发者只管写代码，写到不支持的语法/特性编辑器会报错，并给出报错信息和一个文档地址描述。</p><p><img loading="lazy" src="https://img11.360buyimg.com/uba/jfs/t22168/93/735088023/931233/8689f95e/5b176e0cN26618692.gif" alt="Taro eslint" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="类型安全和运行时检测">类型安全和运行时检测<a href="#类型安全和运行时检测" class="hash-link" aria-label="类型安全和运行时检测的直接链接" title="类型安全和运行时检测的直接链接">​</a></h4><p>JSX 的本质就是 JavaScript 的语法增强，所以例如没有 <code>import</code> 组件等语法错误在编译期就能发现。开发者也可以使用 <code>TypeScript</code> 或 <code>Flow</code> 来对代码的可靠性进一步增强，或使用 <code>PropsType</code> 在运行时进一步保障代码的鲁棒性。</p><p><img loading="lazy" src="https://img12.360buyimg.com/uba/jfs/t20401/15/759531786/1118417/1b827131/5b176e0cN7b17f4ec.gif" alt="typings.gif" class="img_ZOZK"></p><h4 class="anchor anchorWithStickyNavbar_mCi1" id="高效的自动补全和-es6-语法">高效的自动补全和 ES6+ 语法<a href="#高效的自动补全和-es6-语法" class="hash-link" aria-label="高效的自动补全和 ES6+ 语法的直接链接" title="高效的自动补全和 ES6+ 语法的直接链接">​</a></h4><p>Taro 的所有 API（包括微信小程序等端能力接口）都有智能的提醒和自动补全，包括接口的参数和返回值。</p><p><img loading="lazy" src="https://img20.360buyimg.com/uba/jfs/t20455/36/730770188/1715215/573ff07e/5b176e15Nd65937f7.gif" alt="Taro 自动补全" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="taro-的设计思路">Taro 的设计思路<a href="#taro-的设计思路" class="hash-link" aria-label="Taro 的设计思路的直接链接" title="Taro 的设计思路的直接链接">​</a></h2><p>我们的初心就是做一款能够适配多端的解决方案，结合业务场景、技术选型和前端历史发展进程，我们的解决方案必须满足下述要求：</p><ul><li>代码多端复用，不仅能运行在时下最热门的 H5、微信小程序、React Native，对其他可能会流行的端也留有余地和可能性。</li><li>完善和强大的组件化机制，这是开发复杂应用的基石。</li><li>与目前团队技术栈有机结合，有效提高效率。</li><li>学习成本足够低</li><li>背后的生态强大</li></ul><p>同时满足这几个需求并不容易，在我们经过充分地调研和思考之后发现只有 React 体系能够满足我们的需求。而对于微信小程序而言，使用 React 完全没有办法进行开发——直到我们从 <a href="https://github.com/facebook/codemod" target="_blank" rel="noopener noreferrer">codemod</a> 得到灵感：</p><blockquote><p>在一个优秀且严格的规范限制下，从更高抽象的视角（语法树）来看，每个人写的代码都差不多。</p></blockquote><p>也就是说，对于微信小程序这样不开放不开源的端，我们可以先把 React 代码分析成一颗抽象语法树，根据这颗树生成小程序支持的模板代码，再做一个小程序运行时框架处理事件和生命周期与小程序框架兼容，然后把业务代码跑在运行时框架就完成了小程序端的适配。</p><p>对于 React 已经支持的端，例如 Web、React Native 甚至未来的 React VR，我们只要包一层组件库再做些许样式支持即可。鉴于时下小程序的热度和我们团队本身的业务侧重程度，组件库的 API 是以小程序为标准，其他端的组件库的 API 都会和小程序端的组件保持一致。</p><p><img loading="lazy" src="https://img30.360buyimg.com/uba/jfs/t22360/120/839096197/151922/229ceba4/5b1a6fcdNed7d4039.jpg" alt="Taro原理" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="技术选型与权衡">技术选型与权衡<a href="#技术选型与权衡" class="hash-link" aria-label="技术选型与权衡的直接链接" title="技术选型与权衡的直接链接">​</a></h2><p>在我们前面社区已经有多个优秀的框架以小程序为核心对多端适配进行了探索，我们将各个开发框架的主要特点和特性进行了对比并制成图表。大家可以结合团队技术栈、技术需求以及框架特点、特性进行选型和权衡。</p><p><img loading="lazy" src="https://img13.360buyimg.com/uba/jfs/t22033/293/837111837/251330/3f89b58d/5b18f6edNa2311261.png" alt="Taro 对比" class="img_ZOZK"></p><h2 class="anchor anchorWithStickyNavbar_mCi1" id="结语">结语<a href="#结语" class="hash-link" aria-label="结语的直接链接" title="结语的直接链接">​</a></h2><p>经过数个月的开发，Taro 从第一次 commit 到发展成包括 16 个包，十多位同学共同参与的大型项目。与此同时，Taro 也在生产环境支撑了数个复杂业务线上项目的开发，将来也会支撑更多业务。</p><p>Taro 的技术方案和实现也根植于社区，我们也希望为技术社区的发展壮大贡献一份自己的力量。秉持着凹凸实验室长久以来开源、开放、共享的优良传统，我们今天将 Taro 全部代码开源，为广大开发者快速开发多端项目提供一整套技术解决方案。未来，我们也将继续拓展 Taro 现有能力，支持更多端能力，继续完善开发者体验，提高开发者效率，帮助更多开发者，同时也从社区中汲取养分，让 Taro 变得更加强大。</p><p>官网：<a href="https://taro.jd.com/" target="_blank" rel="noopener noreferrer">https://taro.jd.com/</a></p><p>GitHub: <a href="https://github.com/nervjs/taro" target="_blank" rel="noopener noreferrer">https://github.com/nervjs/taro</a></p><p>如果你还没听过 Nerv，可以来这里看看：<a href="https://nerv.aotu.io/" target="_blank" rel="noopener noreferrer">https://nerv.aotu.io/</a></p>]]></content:encoded>
        </item>
    </channel>
</rss>