因为近期使用到 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
鼓励一下吧!