这是 Vue3.x 源码实现的第一篇,介绍 vue3 开发环境的搭建工作
1、项目初始化
shell
mkdir mini-vue3-core
pnpm init -y
2、monorepo 架构
vue3 源码采用 monorepo 架构,在一个仓库里面管理多个包,每个包可作为单独依赖存在
根目录创建 pnpm-workspace.yaml
文件
packages:
- 'packages/*'
3、安装依赖
shell
pnpm install
typescript
rollup
rollup-plugin-typescript2
@rollup/plugin-json
@rollup/plugin-node-resolve
@rollup/plugin-commonjs
minimist
execa@4 -D -w
创建 ts 配置: pnpm tsc --init
, 修改配置如下
json
{
"compilerOptions": {
"outDir": "dist",
"sourceMap": true,
"target": "es2016", // 目标语法
"module": "esnext", // 模块格式
"moduleResolution": "node", // 模块解析方式
"resolveJsonModule": true, // 解析 json 模块
"esModuleInterop": true, // 允许通过 es6 语法引入 commonjs 模块
"strict": false,
"jsx": "preserve", // jsx 不转译
"lib": ["ESNext", "DOM"], // 支持的类库
"baseUrl": ".",
"paths": {
"@vue/*": ["packages/*/src"]
}
}
}
4、创建 reactivity , shared 模块
每个模块都是单独的一个项目
json
// packages/reactivity/package.json
{
"name": "@vue/reactivity",
"version": "1.0.0",
"main": "index.js",
"module": "dist/reactivity.esm-bundler.js", // 给 webpack 使用
"upkg": "dist/reactivity.global.js", // 全局使用
"buildOptions": {
"name": "VueReactivity",
"formats": ["esm-bundler", "cjs", "global"]
}
}
json
// packages/shared/package.json
{
"name": "@vue/shared",
"version": "1.0.0",
"main": "index.js",
"module": "dist/shared.esm-bundler.js",
"buildOptions": {
"formats": ["esm-bundler", "cjs"]
}
}
模块 @vue/shared
中集成一些公共方法等,可在其它模块中使用
js
// packages/shared/src/index.ts
export function isObject(value: unknown) {
return typeof value === 'object' && value !== null
}
模块 @vue/reactivity
中想要使用模块 @vue/shared
中导出的方法前,需要安装 @vue/shared
模块
在项目根目录执行
shell
pnpm install @vue/shared@workspace --filter @vue/reactivity
@workspace
: 指向当前空间--filter
: 指定安装目录
在 @vue/reactivity
模块中之间导入就可以使用了
import { isObject } from "@vue/shared";
5、打包配置
json
"scripts": {
"dev": "node scripts/dev.js reactivity -f global -s",
},
新增 dev
命令, 执行 scripts/dev.js
, 并传递参数
- 只打包 reactivity 模块,
- -f:输出格式 global,
- -s: 开启 sourceMap
js
// path: scripts/dev.js
const minimist = require('minimist')
const execa = require('execa')
const args = minimist(process.argv.slice(2))
// 获取执行命令时候的打包参数
const target = args._.length ? args._[0] : 'reactivity'
const formats = args.f || 'global'
const sourceMap = args.s || false
execa(
'rollup',
[
'-wc', // --watch --config
'--environment', // 传参
[
`TARGET:${target}`,
`FORMATS:${formats}`,
sourceMap ? `SOURCE_MAP:true` : '',
]
.filter(Boolean)
.join(','),
],
{
stdio: 'inherit', // 子进程的输出在当前命令行中输出
}
)
脚本 dev.js
文件中会通过 execa 开启单独线程执行 rollup
进行打包,并传递了参数, rollup
配置会默认从根目录查找
js
// rollup.config.js
import path from 'path'
import ts from 'rollup-plugin-typescript2'
import json from '@rollup/plugin-json'
import { nodeResolve } from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
const packageFormats = process.env.FORMATS && process.env.FORMATS.split(',')
const sourcemap = process.env.SOURCE_MAP
// 1、需要根据 target 找到要打包的模块
const packagesDir = path.resolve(__dirname, 'packages')
// 打包入口
const packageDir = path.resolve(packagesDir, process.env.TARGET)
// 入口文件
const resolve = (p) => path.resolve(packageDir, p)
const name = path.basename(packageDir)
const pkg = require(resolve('package.json'))
const outputConfig = {
'esm-bundler': {
file: resolve(`dist/${name}.esm-bundler.js`),
format: 'es',
},
cjs: {
file: resolve(`dist/${name}.cjs.js`),
format: 'cjs',
},
global: {
file: resolve(`dist/${name}.global.js`),
format: 'iife',
},
}
const pkgConfigs = packageFormats || pkg.buildOptions.formats
function createConfig(format, output) {
output.sourcemap = sourcemap
output.exports = 'named'
let external = []
if (format === 'global') {
output.name = pkg.buildOptions.name
} else {
external = [...Object.keys(pkg.dependencies)]
}
return {
input: resolve('src/index.ts'),
output,
external,
plugins: [json(), ts(), commonjs(), nodeResolve()],
}
}
// 返回数组,会依次进行打包
export default pkgConfigs.map((format) =>
createConfig(format, outputConfig[format])
)
此时,执行 npm run dev
就会在 packages/reactivity/dist/
目录下生成 reactivity.global.js
文件
自此,vue3 源码开发环境初步搭建完成,拜 👋