Skip to content

Commit 8d4a7e1

Browse files
committed
docs: ✏️ 更新 taro 文章
1 parent 7899abd commit 8d4a7e1

File tree

3 files changed

+200
-39
lines changed

3 files changed

+200
-39
lines changed

docs/taro/mini-plugin-2/README.md

Lines changed: 200 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ theme: smartblue
99

1010
大家好,我是[若川](https://ruochuan12.github.io),欢迎关注我的[公众号:若川视野](https://mp.weixin.qq.com/s/MacNfeTPODNMLLFdzrULow)。我倾力持续组织了 3 年多[每周大家一起学习 200 行左右的源码共读活动](https://juejin.cn/post/7079706017579139102),感兴趣的可以[点此扫码加我微信 `ruochuan02` 参与](https://juejin.cn/pin/7217386885793595453)。另外,想学源码,极力推荐关注我写的专栏[《学习源码整体架构系列》](https://juejin.cn/column/6960551178908205093),目前是掘金关注人数(6k+人)第一的专栏,写有几十篇源码文章。
1111

12-
截至目前(`2025-03-25`),目前最新是 [`4.0.9`](https://github.com/NervJS/taro/releases/tag/v4.0.9),官方`4.0`正式版本的介绍文章暂未发布。官方之前发过[Taro 4.0 Beta 发布:支持开发鸿蒙应用、小程序编译模式、Vite 编译等](https://juejin.cn/post/7330792655125463067)
12+
截至目前(`2025-04-15`),目前最新是 [`4.0.10`](https://github.com/NervJS/taro/releases/tag/v4.0.10),官方`4.0`正式版本的介绍文章暂未发布。官方之前发过[Taro 4.0 Beta 发布:支持开发鸿蒙应用、小程序编译模式、Vite 编译等](https://juejin.cn/post/7330792655125463067)
1313

1414
计划写一个 Taro 源码揭秘系列,博客地址:[https://ruochuan12.github.io/taro](https://ruochuan12.github.io/taro) 可以加入书签,持续关注[若川](https://juejin.cn/user/1415826704971918)
1515

@@ -27,18 +27,21 @@ theme: smartblue
2727

2828
前面 4 篇文章都是讲述编译相关的,CLI、插件机制、初始化项目、编译构建流程。
2929
第 5-7 篇讲述的是运行时相关的 Events、API、request 等。
30-
第 10 篇接着继续追随第 4 篇和第 8、9 篇的脚步,分析 TaroMiniPlugin webpack 的插件实现。
31-
我们继续分析 TaroMiniPlugin webpack 的插件实现。
30+
第 10 篇接着继续追随第 4 篇和第 8、9 篇的脚步,分析 TaroMiniPlugin webpack 的插件实现(全流程概览)
31+
第 11 篇,我们继续分析 TaroMiniPlugin webpack 的插件实现。分析 Taro 是如何解析入口文件和页面的?
3232

33-
关于克隆项目、环境准备、如何调试代码等,参考[第一篇文章-准备工作、调试](https://juejin.cn/post/7378363694939783178#heading-1)。后续文章基本不再过多赘述。
33+
关于克隆项目、环境准备、如何调试代码等,参考[第一篇文章-准备工作、调试](https://juejin.cn/post/7378363694939783178#heading-1)[第 4 篇 npm run dev:weapp](https://juejin.cn/post/7403193330271682612#heading-2)。后续文章基本不再过多赘述。
3434

3535
学完本文,你将学到:
3636

3737
```bash
38-
1. Taro 到底是怎样转换成小程序的?
38+
1. Taro 是如何解析入口文件和页面的?
39+
2. 如何读取文件
3940
等等
4041
```
4142

43+
![taro-mini-plugin](./images/taro-webpack.png)
44+
4245
我们先来看 TaroMiniPlugin 结构
4346

4447
```ts
@@ -47,10 +50,12 @@ export default class TaroMiniPlugin {
4750
// 省略
4851
}
4952
/**
50-
* 插件入口
51-
*/
53+
* 插件入口
54+
*/
5255
apply (compiler: Compiler) {
5356
this.context = compiler.context
57+
// 根据 webpack entry 配置获取入口文件路径
58+
// @returns app 入口文件路径
5459
this.appEntry = this.getAppEntry(compiler)
5560
// 省略代码
5661
/** build mode */
@@ -65,7 +70,12 @@ export default class TaroMiniPlugin {
6570
}
6671
```
6772

68-
插件入口 apply 方法
73+
插件入口 apply 方法,获取到 `this.appEntry` `src/app.[js|jsx|ts|tsx]`,调用 `run` 方法。
74+
75+
这里是
76+
this.appEntry = "/Users/ruochuan/git-source/github/taro4-debug/src/app.ts"
77+
78+
本文主要讲述 `run` 方法。
6979

7080
```ts
7181
export default class TaroMiniPlugin {
@@ -127,7 +137,13 @@ graph TD
127137
end
128138
```
129139

130-
## getAppConfig 获取入口配置文件配置
140+
## 2. getAppConfig 获取入口配置文件配置
141+
142+
```ts
143+
this.appConfig = await this.getAppConfig();
144+
```
145+
146+
入口文件是 `src/app.[js|jsx|ts|tsx]`
131147

132148
```ts
133149
async getAppConfig (): Promise<AppConfig> {
@@ -184,7 +200,9 @@ appConfig 配置
184200
}
185201
```
186202

187-
### compileFile 读取页面、组件的配置,并递归读取依赖的组件的配置
203+
其中 compileFile 函数的实现。
204+
205+
### 2.1 compileFile 读取页面、组件的配置,并递归读取依赖的组件的配置
188206

189207
```ts
190208
/**
@@ -202,8 +220,8 @@ appConfig 配置
202220

203221
// 递归收集依赖的第三方组件
204222
if (usingComponents) {
205-
// 省略...
206-
}
223+
// 省略...
224+
}
207225

208226
this.filesConfig[this.getConfigFilePath(file.name)] = {
209227
content: fileConfig,
@@ -212,23 +230,31 @@ appConfig 配置
212230
}
213231
```
214232

233+
compileFile 相对比较复杂,我们省略一些代码。
234+
235+
`filesConfig` 如下图所示:
236+
215237
![filesConfig](./images/filesConfig.png)
216238

217-
#### readConfig 读取配置
239+
包含路径和内容。
240+
241+
### 2.2 readConfig 读取配置
242+
243+
[页面配置](https://docs.taro.zone/docs/page-config)
244+
245+
![page.config](./images/page.config.png)
218246

219247
```ts
248+
// packages/taro-helper/src/utils.ts
220249
export function readConfig<T extends IReadConfigOptions> (configPath: string, options: T = {} as T) {
221250
let result: any = {}
222251
if (fs.existsSync(configPath)) {
223252
if (REG_JSON.test(configPath)) {
224253
result = fs.readJSONSync(configPath)
225254
} else {
226255
result = requireWithEsbuild(configPath, {
227-
customConfig: {
228-
},
229-
customSwcConfig: {
230-
},
231-
})
256+
// 省略若干代码
257+
})
232258
}
233259

234260
result = getModuleDefaultExport(result)
@@ -239,7 +265,103 @@ export function readConfig<T extends IReadConfigOptions> (configPath: string, op
239265
}
240266
```
241267

242-
## getPages 获取页面信息
268+
#### 2.2.1 requireWithEsbuild
269+
270+
```ts
271+
// packages/taro-helper/src/esbuild/index.ts
272+
273+
import { Config, transformSync } from '@swc/core'
274+
import esbuild from 'esbuild'
275+
import requireFromString from 'require-from-string'
276+
// 省略若干代码
277+
278+
/** 基于 esbuild 的 require 实现 */
279+
export function requireWithEsbuild(
280+
id: string,
281+
{ customConfig = {}, customSwcConfig = {}, cwd = process.cwd() }: IRequireWithEsbuildOptions = {}
282+
) {
283+
const { outputFiles = [] } = esbuild.buildSync(
284+
// 省略若干代码
285+
)
286+
287+
// Note: esbuild.buildSync 模式下不支持引入插件,所以这里需要手动转换
288+
const { code = '' } = transformSync(
289+
outputFiles[0].text,
290+
defaults(customSwcConfig, {
291+
jsc: { target: 'es2015' },
292+
})
293+
)
294+
return requireFromString(code, id)
295+
}
296+
```
297+
298+
#### 2.2.2 getModuleDefaultExport
299+
300+
```ts
301+
export const getModuleDefaultExport = (exports) => (exports.__esModule ? exports.default : exports)
302+
```
303+
304+
#### 2.2.3 readPageConfig 读取页面 js
305+
306+
```ts
307+
// packages/taro-helper/src/utils.ts
308+
309+
export function readPageConfig(configPath: string) {
310+
let result: any = {}
311+
const extNames = ['.js', '.jsx', '.ts', '.tsx', '.vue']
312+
313+
// check source file extension
314+
extNames.some((ext) => {
315+
const tempPath = configPath.replace('.config', ext)
316+
if (fs.existsSync(tempPath)) {
317+
try {
318+
result = readSFCPageConfig(tempPath)
319+
} catch (error) {
320+
result = {}
321+
}
322+
return true
323+
}
324+
})
325+
return result
326+
}
327+
```
328+
329+
#### 2.2.4 readSFCPageConfig
330+
331+
```ts
332+
// packages/taro-helper/src/utils.ts
333+
334+
// read page config from a sfc file instead of the regular config file
335+
function readSFCPageConfig(configPath: string) {
336+
if (!fs.existsSync(configPath)) return {}
337+
338+
const sfcSource = fs.readFileSync(configPath, 'utf8')
339+
const dpcReg = /definePageConfig\(\{[\w\W]+?\}\)/g
340+
const matches = sfcSource.match(dpcReg)
341+
342+
let result: any = {}
343+
344+
if (matches && matches.length === 1) {
345+
const callExprHandler = (p: any) => {
346+
const { callee } = p.node
347+
if (!callee.name) return
348+
if (callee.name && callee.name !== 'definePageConfig') return
349+
350+
const configNode = p.node.arguments[0]
351+
result = exprToObject(configNode)
352+
p.stop()
353+
}
354+
const configSource = matches[0]
355+
const program = (babel.parse(configSource, { filename: '' }))?.program
356+
357+
program && babel.traverse(program, { CallExpression: callExprHandler })
358+
}
359+
360+
return result
361+
}
362+
```
363+
364+
## 3. getPages 获取页面信息
243365

244366
```ts
245367
/**
@@ -263,6 +385,12 @@ getPages () {
263385
const { newBlended, frameworkExts, combination } = this.options
264386
const { prerender } = combination.config
265387

388+
// 拆分到下方
389+
```
390+
391+
![image](./images/image.png)
392+
393+
```ts
266394
this.prerenderPages = new Set(validatePrerenderPages(appPages, prerender).map(p => p.path))
267395
this.getTabBarFiles(this.appConfig)
268396
this.pages = new Set([
@@ -285,9 +413,7 @@ getPages () {
285413
}
286414
```
287415

288-
![alt text](./images/image.png)
289-
290-
## getPagesConfig 读取页面及其依赖的组件的配置
416+
## 4. getPagesConfig 读取页面及其依赖的组件的配置
291417

292418
```ts
293419
/**
@@ -307,7 +433,9 @@ getPagesConfig () {
307433
}
308434
```
309435

310-
## getDarkMode 收集 dark mode 配置中的文件
436+
![image](./images/image.png)
437+
438+
## 5. getDarkMode 收集 dark mode 配置中的文件
311439

312440
```ts
313441
/**
@@ -322,7 +450,7 @@ getPagesConfig () {
322450
}
323451
```
324452

325-
## getConfigFiles 往 this.dependencies 中新增或修改所有 config 配置模块
453+
## 6. getConfigFiles 往 this.dependencies 中新增或修改所有 config 配置模块
326454

327455
```ts
328456
/**
@@ -350,7 +478,20 @@ getPagesConfig () {
350478
}
351479
```
352480

353-
## addEntries 在 this.dependencies 中新增或修改 app、模板组件、页面、组件等资源模块
481+
## 7. addEntries 在 this.dependencies 中新增或修改 app、模板组件、页面、组件等资源模块
482+
483+
```ts
484+
// packages/taro-helper/src/constants.ts
485+
export enum META_TYPE {
486+
ENTRY = 'ENTRY',
487+
PAGE = 'PAGE',
488+
COMPONENT = 'COMPONENT',
489+
NORMAL = 'NORMAL',
490+
STATIC = 'STATIC',
491+
CONFIG = 'CONFIG',
492+
EXPORTS = 'EXPORTS',
493+
}
494+
```
354495

355496
```ts
356497
/**
@@ -364,25 +505,13 @@ getPagesConfig () {
364505
this.addEntry(path.resolve(__dirname, '..', 'template/comp'), 'comp', META_TYPE.STATIC)
365506
}
366507
this.addEntry(path.resolve(__dirname, '..', 'template/custom-wrapper'), 'custom-wrapper', META_TYPE.STATIC)
367-
// 拆分
508+
// 拆分到下方
368509
}
369510
```
370511

371-
```ts
372-
// packages/taro-helper/src/constants.ts
373-
export enum META_TYPE {
374-
ENTRY = 'ENTRY',
375-
PAGE = 'PAGE',
376-
COMPONENT = 'COMPONENT',
377-
NORMAL = 'NORMAL',
378-
STATIC = 'STATIC',
379-
CONFIG = 'CONFIG',
380-
EXPORTS = 'EXPORTS',
381-
}
382-
```
512+
遍历页面添加到依赖项中。
383513

384514
```ts
385-
386515
this.pages.forEach(item => {
387516
if (item.isNative) {
388517
this.addEntry(item.path, item.name, META_TYPE.NORMAL, { isNativePage: true })
@@ -396,7 +525,11 @@ export enum META_TYPE {
396525
this.addEntry(item.path, item.name, META_TYPE.PAGE)
397526
}
398527
})
528+
```
529+
530+
遍历组件添加到依赖项中。
399531

532+
```ts
400533
this.components.forEach(item => {
401534
if (item.isNative) {
402535
this.addEntry(item.path, item.name, META_TYPE.NORMAL, { isNativePage: true })
@@ -412,4 +545,32 @@ export enum META_TYPE {
412545
})
413546
```
414547

548+
## 8. addEntry 在 this.dependencies 中新增或修改模块
549+
550+
```ts
551+
/**
552+
* 在 this.dependencies 中新增或修改模块
553+
*/
554+
addEntry (entryPath: string, entryName: string, entryType: META_TYPE, options = {}) {
555+
let dep: TaroSingleEntryDependency
556+
if (this.dependencies.has(entryPath)) {
557+
dep = this.dependencies.get(entryPath)!
558+
dep.name = entryName
559+
dep.loc = { name: entryName }
560+
dep.request = entryPath
561+
dep.userRequest = entryPath
562+
dep.miniType = entryType
563+
dep.options = options
564+
} else {
565+
dep = new TaroSingleEntryDependency(entryPath, entryName, { name: entryName }, entryType, options)
566+
}
567+
this.dependencies.set(entryPath, dep)
568+
}
569+
```
570+
571+
this.dependencies 是这样的结构。TODO:
572+
573+
## 9. 总结
574+
575+
415576
最后可以持续关注我[@若川](https://juejin.cn/user/1415826704971918),欢迎关注我的[公众号:若川视野](https://mp.weixin.qq.com/s/MacNfeTPODNMLLFdzrULow)。我倾力持续组织了 3 年多[每周大家一起学习 200 行左右的源码共读活动](https://juejin.cn/post/7079706017579139102),感兴趣的可以[点此扫码加我微信 `ruochuan02` 参与](https://juejin.cn/pin/7217386885793595453)。另外,想学源码,极力推荐关注我写的专栏[《学习源码整体架构系列》](https://juejin.cn/column/6960551178908205093),目前是掘金关注人数(6k+人)第一的专栏,写有几十篇源码文章。
299 KB
Loading
1.62 MB
Loading

0 commit comments

Comments
 (0)