因为近期使用到 Taro 编写小程序,出于好奇,准备研读一下 Taro 的源码。
在上一篇文章 Taro 源码解读 - taro build 篇 中,已经讲解了 taro-cli 的实现原理,然后以 taro build 为案例解释了核心 Kernel + 钩子的运行机制,以及最终到达 webpack 构建阶段。
本篇文章将会是对 taro build 篇的一个补充,着重介绍运行 taro build 后,最终 webpack 实现的打包机制,以及简单介绍一下 Taro Next 从编译时到运行时的转变。
话不多说,我们开始吧。
miniRunner 概览
miniRunner 其实是一个函数,我们先来整体看看 miniRunner 所做的事情吧(如下图)
我们来逐行解析一下代码实现:
| 代码行数 | 解释 | ||
|---|---|---|---|
第 21 行 | 定义构建 mode,也就是 `"production" | "development" | "none"` |
第 24 行 | 完善构建配置,这里主要是完善一些 sass loader 的配置 | ||
第 27~37 行 | 根据项目配置生成 webpack 的构建配置 | ||
第 39~80 行 | 使用 webpack 进行代码编译 |
从上面的分析可以看出,miniRunner 主要做的工作就是根据项目配置组装 webpack 配置,然后根据 webpack 配置生成编译后的代码。
接下来,我们重点关注项目自带的 webpackChain 配置(第 27 行),看看默认的配置是什么样的吧~
默认配置
这里我们以 taro build --type weapp 命令为例,将 react 技术栈的代码编译到微信小程序平台。
我们先来看看默认配置(如下图)
我们从上面的配置中可以看到 framework(框架)是 react,platform(目标平台)是 weapp(微信)。下面我们来看看这份配置生成的 webpackChain,也就是 miniRunner 中的第 27 行代码(如下图)
接下来的解析也是对 webpack 配置的解析科普,可能会比较枯燥,大家耐心看完就知道内部编译所做的事情了。
基础配置
我们先找到 buildConf 函数(如下图)
从上图的第 27 行中可以看出,内部使用 getBaseConf 来获取一个初始设置(如下图)
下面我们来逐行解析一下基础配置项,如下:
- 第
9行:源文件使用的扩展名,这里包括'.js', '.jsx', '.ts', '.tsx', '.mjs', '.vue',值得注意的是,mjs指的是 JavaScript modules 模块 - 第
10行:指定导入模块时使用package.json中的哪个字段,这里的配置将优先使用browser属性解析文件,其次是module,最后是main。 - 第
11行:符号链接(symlink)解析到它们的符号链接位置(symlink location)。相关资料可以参考 当 webpack 遇上 symlink。 - 第
12~15行:告诉 webpack 解析模块时应该搜索的目录,这里对应的就是node_modules目录。 - 第
16~21行:这里是一些运行时的模块,将其指向本地 node_modules 顶层,以保证状态一致。 - 第
23~27行:解析webpack loader包,指定node_modules目录。 - 第
28~30行:代码包是包含副作用的,不希望被tree shaking优化。 - 第
33~35行:添加taro自带的MultiPlatformPlugin插件,这个我们在后面在展开介绍。
构建项配置
在梳理完了基础配置后,我们来继续探究 buildConf 函数(如下图)。
从上图可以看出,第 98~100 行时,将 copy 属性解析为 copy-webpack-plugin 插件,加入到 webpack 中(如下图)
接下来几行代码则展示了一些从 React 到 微信小程序 中间的玄妙之处。(如下图)
从第 103 行可以看出,如果 framework 是 react,miniRunner 内部则会将 react-dom 指向 @tarojs/react。
在 react 体系中,react 库实现了 ReactComponent 和 ReactElement 的核心部分,而 react-dom 库负责通过操作 DOM 来实现 react 在浏览器中的渲染更新操作。在小程序中,并不能直接操作 DOM 树或者说没有传统的 DOM 树,此时直接使用 react-dom 则会导致报错。所以,taro 实现了一套在小程序上的 仿 react-dom 运行时,以保证 React 可以正常在小程序端渲染、更新节点。
我们也可以这么理解,
react-dom是浏览器端的render,react-native是原生 APP 的render,而@tarojs/react是小程序上的render。
我们接着往下看(如下图)
在第 119 行中,将一些常量进行收集,后续用 definePlugin 进行处理(如下图)
我们接着往下看(如下图)
在第 120~133 行中,主要是根据 isBuildPlugin 变量(是否为打包插件)来确定 entryRes(入口资源)、defaultCommonChunks(默认的 chunk)。
接下来,miniRunner 注册了一系列的插件(如下图)
此时注册的插件包括:
| 代码行数 | 解释 |
|---|---|
第 134 行 | 定义 definePlugin 插件,用于定义一些全局变量 |
第 135 行 | 定义 TaroMiniPlugin 插件,该插件主要负责将 framework 源文件转换为 platform 平台代码 |
第 157 行 | 定义 MiniCssExtractPlugin 插件,该插件主要负责将所有的 css 文件提取到一个文件中 |
第 39~80 行 | 定义 ProvidePlugin 插件,该插件负责了一个核心功能,就是将运行时环境从浏览器环境切换到 taro 的运行时环境,比如将 window 替换成 @tarojs/runtime 中导出的 window |
TaroMiniPlugin 和 @tarojs/runtime 大家先做个笔记,我们后面还要再展开解析。
我们先接着往下看。(如下图)
在第 173~186 行中,主要是配置 js 和 css 文件的压缩插件(如下图)
在接下来,webpackChain 又合并了一系列的基础参数(如下图)
我们来进行逐行解析:
- 第
189行:提供mode配置选项,告知webpack使用相应模式的内置优化。 - 第
190行:控制是否生成source-map。 - 第
191行:入口文件,也就是app.js。 - 第
192行:定义代码编译后的生产目录。 - 第
200行:指定目标(target)环境。 - 第
203行:合并alias别名选项。 - 第
204行:配置module,这里主要是配置一些不同的loader。 - 第
226行:配置plugin插件。 - 第
227行:手动配置了一些编译选项优化。
在合并完了一系列参数配置后,buildConf 最后进行了 vue 的兼容处理,最后将 chain 返回。(如下图)
配置项概览
在了解完了内部的 webpackChain(buildConf) 组成后,我们来继续回到 miniRunner 中来看代码(如下图)
在上图第 30 行中,miniRunner 将内部 webpackChain 和开发者设置的 webpackChain 相结合,得到最终的 webpackChain(如下图)。
我们来看看由这份 webpackChain 最终生成的 webpack 配置长什么样子吧~(如下图)
配置很长,我们还是需要关注两个比较重要的部分
一是 taro 内部插件 - TaroMiniPlugin(如下图)
二是 taro 内部 loader - miniTemplateLoader(如下图)
可以说,只要搞懂了 TaroMiniPlugin 和 miniTemplateLoader,就能搞懂从 React 到小程序的流程。
小结
TaroMiniPlugin 和 miniTemplateLoader 部分的内容比较复杂,我们会在后面单独开设两个章节进行讲解。
那么,本次对 miniRunner 的梳理到这里就算完成啦。
最后我们画一个流程图来帮助大家理解(如下图)
最后一件事
如果您已经看到这里了,希望您还是点个赞再走吧~
您的点赞是对作者的最大鼓励,也可以让更多人看到本篇文章!
如果觉得本文对您有帮助,请帮忙在 github 上点亮 star 鼓励一下吧!