Vue3
响应式模块
从零手写今天课程是正式课第一天,珠峰架构课正式课上课时间是 周三、周五晚上8-10 周日全天,想报名参加的同学可以联系右下角客服老师微信
课程计划安排
上课时进度可能略有调整
- 07-18 Vue3开发环境搭建,Vue3
compositionAPI
的使用,手写Vue3响应式模块原理 (monorepo
环境搭建,rolllup
环境打包项目) - 07-21 Vue3响应式源码分析及代码调试
- 07-23 手写Vue3初始化流程实现、虚拟DOM实现
- 07-25 Vue3中组件实现原理,手写
Vue3diff
算法实现watchApi
异步渲染 、生命周期实现 - 07-28 vue3中模板编译原理,template编译render函数原理
- 07-30 手写模板编译 transform 核心逻辑,及
codegen
原理 - 08-01 Vue3生态源码剖析
Vuex
实现原理 - 08-04 Vue3路由实现原理
- 08-06 Vue3路由实现原理
- 08-08 ts课程:类、接口、泛型、兼容性、条件、类型保护、声明文件.....等
- 08-11
vite
开发环境搭建及原理剖析 - 08-13
vite
开发环境搭建及原理剖析 - 08-15 Vue3 +TS编写组件库,编写常用组件
- 08-18 组件库的讲解 ,08-20 组件库的讲解
区别介绍
- 源码采用
monorepo
方式进行管理,将模块拆分到package目录中 Vue3
采用ts
开发,增强类型检测。Vue2
则采用flow
Vue3
的性能优化,支持tree-shaking, 不使用就不会被打包Vue2
后期引入RFC , 使每个版本改动可控 rfcs
内部代码优化
Vue3
劫持数据采用proxyVue2
劫持数据采用defineProperty
。defineProperty
有性能问题和缺陷Vue3
中对模板编译进行了优化,编译时 生成了Block tree,可以对子节点的动态节点进行收集,可以减少比较,并且采用了patchFlag
标记动态节点 jsxVue3
采用compositionApi
进行组织功能,解决反复横跳,优化复用逻辑 (mixin带来的数据来源不清晰、命名冲突等), 相比optionsApi
类型推断更加方便- 增加了
Fragment
,Teleport
,Suspense
组件
Vue3
架构分析
一.Monorepo
介绍
1.Monorepo
是管理项目代码的一个方式,指在一个项目仓库(repo
)中管理多个模块/包(package)
- 一个仓库可维护多个模块,不用到处找仓库
- 方便版本管理和依赖管理,模块之间的引用,调用都非常方便
缺点:仓库体积会变大。
Vue3
项目结构
2.reactivity
:响应式系统runtime-core
:与平台无关的运行时核心 (可以创建针对特定平台的运行时 - 自定义渲染器)runtime-dom
: 针对浏览器的运行时。包括DOM API
,属性,事件处理等runtime-test
:用于测试server-renderer
:用于服务器端渲染compiler-core
:与平台无关的编译器核心compiler-dom
: 针对浏览器的编译模块compiler-ssr
: 针对服务端渲染的编译模块compiler-sfc
: 针对单文件解析size-check
:用来测试代码体积template-explorer
:用于调试编译器输出的开发工具shared
:多个包之间共享的内容vue
:完整版本,包括运行时和编译器
+---------------------+
| |
| @vue/compiler-sfc |
| |
+-----+--------+------+
| |
v v
+---------------------+ +----------------------+
| | | |
+-------->| @vue/compiler-dom +--->| @vue/compiler-core |
| | | | |
+----+----+ +---------------------+ +----------------------+
| |
| vue |
| |
+----+----+ +---------------------+ +----------------------+ +-------------------+
| | | | | | |
+-------->| @vue/runtime-dom +--->| @vue/runtime-core +--->| @vue/reactivity |
| | | | | |
+---------------------+ +----------------------+ +-------------------+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
3.安装依赖
依赖 | |
---|---|
typescript | 支持typescript |
rollup | 打包工具 |
rollup-plugin-typescript2 | rollup 和 ts的 桥梁 |
@rollup/plugin-node-resolve | 解析node第三方模块 |
@rollup/plugin-json | 支持引入json |
execa | 开启子进程方便执行命令 |
npm install typescript rollup rollup-plugin-typescript2 @rollup/plugin-node-resolve @rollup/plugin-json execa -D
1
workspace
配置
4.npm init -y && npx tsc --init
1
{
"private":true,
"workspaces":[
"packages/*"
]
}
1
2
3
4
5
6
2
3
4
5
6
目录结构配置
C:.
│ package.json # 配置运行命令
│ rollup.config.js # rollup配置文件
│ tsconfig.json # ts配置文件 更改为esnext
│ yarn.lock
│
├─packages # N个repo
│ └─reactivity
│ │ package.json
│ └─src
│ index.ts
│
└─scripts # 打包命令
build.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
配置模块名称及打包选项
{
"name": "@vue/reactivity",
"version": "1.0.0",
"description": "",
"main": "index.js",
"module": "dist/reactivity.esm-bundler.js",
"author": "",
"license": "ISC",
"buildOptions":{
"name":"VueReactivity",
"formats":[
"esm-bundler",
"cjs",
"global"
]
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
创建软链
yarn install
二.构建环境搭建
packages
下模块进行打包
1.对
scripts/build.js
const fs = require('fs');
const execa = require('execa')
// 过滤packages目录下所有模块
const targets = fs.readdirSync('packages').filter(f => {
if (!fs.statSync(`packages/${f}`).isDirectory()) {
return false;
}
return true;
})
// 开始并行打包
runParallel(targets, build)
async function runParallel(source, iteratorFn) {
const ret = [];
for (const item of source) {
const p = iteratorFn(item)
ret.push(p);
}
return Promise.all(ret);
}
async function build(target) {
await execa(
'rollup',
[
'-c',
'--environment',
`TARGET:${target}`
],
{ stdio: 'inherit' }
)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
rollup
配置
2.
rollup.config.js
import path from 'path';
import ts from 'rollup-plugin-typescript2'
import json from '@rollup/plugin-json'
import resolvePlugin from '@rollup/plugin-node-resolve'
const packagesDir = path.resolve(__dirname, 'packages'); // 获取packages目录
const packageDir = path.resolve(packagesDir, process.env.TARGET); // 获取要打包的目标目录
const name = path.basename(packageDir); // 获取打包的名字
const resolve = p => path.resolve(packageDir, p);
const pkg = require(resolve(`package.json`)) // 获取目标对应的package.json
const packageOptions = pkg.buildOptions; // 打包的选项
const outputConfigs = {
'esm-bundler': {
file: resolve(`dist/${name}.esm-bundler.js`), // webpack打包用的
format: `es`
},
'cjs': {
file: resolve(`dist/${name}.cjs.js`), // node使用的
format: 'cjs'
},
'global': {
file: resolve(`dist/${name}.global.js`), // 全局的
format: 'iife'
}
}
function createConfig(format, output) {
output.name = packageOptions.name;
output.sourcemap = true;
return {
input: resolve(`src/index.ts`), // 入口
output,
plugins:[
json(),
ts({
tsconfig:path.resolve(__dirname,'tsconfig.json')
}),
resolvePlugin(),
]
}
}
// 根据模块配置信息选择性打包
export default packageOptions.formats.map(format => createConfig(format, outputConfigs[format]));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
3.开发环境打包
const execa = require('execa')
const target = 'reactivity'
execa('rollup', [
'-wc',
'--environment',
`TARGET:${target}`
], {
stdio: 'inherit'
}
)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10