1. vue2和vue3的区别
- vue3的源码采用
monorepo管理方式,实现了从模块管理到包管理的转变 - vue2采用的是
Flow来做静态类型检测,而vue3使用typescript重构代码,增强类型检测。 - vue2的方法都放在
实例对象上,而vue3中都是函数形式,所以vue3支持tree-shaking,不使用就不会被打包 - vue2的数据劫持是通过
defineProperty,而这也是vue2最大的性能问题,所以vue3中使用Proxy实现数据劫持 - vue3对模板编译进行了优化,编译时生成
Block tree可以收集动态节点,减少比较 - vue3采用
compositionApi进行组织功能,优化复用逻辑,相较于optionApi类型推断更加便捷 - 增加了
Fragment,Teleport,Suspense组件
2. Vue架构分析
2.1 Monorepo
Monorepo是管理代码的一种方式,它是指在一个项目仓库(repo)下管理多个包
- 一个仓库中维护多个包
- 便于版本管理、依赖管理,模块间的引用,调用都非常的方便
- 缺点就是仓库的体积会变大
2.2 Vue3的项目结构
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:完整版本,包括运行时和编译器
3. 基于rollup搭建vue3环境
3.1 安装依赖
typescript:支持typescriptrollup:打包工具rollup-plugin-typescript2:rollup 和 ts的 桥梁@rollup/plugin-node-resolve:解析node第三方模块@rollup/plugin-json:支持引入jsonexeca:开启子进程方便执行命令
npm install typescript rollup rollup-plugin-typescript2 @rollup/plugin-node-resolve @rollup/plugin-json execa -D
3.2 workspace配置
初始化配置文件
nmp iniy -y // 初始化 package.json 文件
npx tsc --init // 初始化 ts 配置文件
在根目录的
package.json文件中配置workspace
{ // 在package.json 中添加以下字段
"private":true,
"workspaces":[
"packages/*"
],
// ...
}
目录结构,以及包配置
-
创建
packages文件夹用于管理包模块,文件夹下就是vue的各个包模块了 -
创建
reactivity文件夹,其中src中是核心代码,package.json则是管理当前包的配置文件
{
"name": "@vue/reactivity",
"version": "1.0.0",
"description": "",
"main": "index.js",
"module": "dist/reactivity.esm-bundler.js",
"author": "",
"license": "ISC",
"buildOptions":{ // 自定义配置,rollup打包时使用
"name":"VueReactivity",
"formats":[
"esm-bundler",
"cjs",
"global"
]
}
}
创建软链
yarn install
3.3 配置scripts脚本
在根目录的
packages.json中配置scripts字段
{
"scripts": {
"dev":"node script/dev.js", // 打包单个包
"build":"node script/build.js" // 打包所有包
}
}
根目录下,创建
scripts文件夹,用于区分打包环境
> script
- build.js
- dev.js
build.js
// 把 packages 目录下的所有包都进行打包
const fs = require('fs');
const execa = require('execa'); // 开子进程使用 rollup 打包
const targets = fs.readdirSync('packages').filter(f => {
if(!fs.statSync(`packages/${f}`).isDirectory()){
return false;
}
return true;
})
// 对我们目标进行一次打包,并行打包
async function build(target){
// rollup -c --environment TARGET:reactivity
await execa('rollup',['-c','--environment',`TARGET:${target}`],{stdio:'inherit'}); // 将子进程打包的信息共享给父进程
}
function runParallel(targets,iteratorFn){
const res = [];
for(const item of targets){
const p = iteratorFn(item);
res.push(p);
}
return Promise.all(res);
}
runParallel(targets,build)
dev.js
// 只针对具体的某个包打包
const fs = require('fs');
const execa = require('execa'); // 开启子进程 进行打包, 最终还是使用rollup来进行打包
const target = 'reactivity' // 后期可根据传入的target做打包
async function build(target){
await execa('rollup',['-cw','--environment',`TARGET:${target}`],{stdio:'inherit'}); // 当子进程打包的信息共享给父进程
}
build(target)
3.4 rollup配置
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'
// 根据环境变量中的target属性 获取对应模块中的 pakcage.json
const packagesDir = path.resolve(__dirname, 'packages');
// packageDir 打包的基准目录
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 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'
}
}
const packageOptions = pkg.buildOptions; // 打包的选项
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(),
]
}
}
// rollup 最终需要到出配置
export default packageOptions.formats.map(format => createConfig(format, outputConfigs[format]));
自此就搭建好了
vue3最基本的环境