Vue3源码实现(一)- 环境搭建
前言
最近在看 Vue3 源码,为了加深理解和串联起来整体的逻辑,下面我们也构建一个自己的 mini-vue ,麻雀虽小,五脏俱全。本文先把环境搭起来。
开发环境搭建
搭建 Monorepo 环境
Vue3 使用的是 pnpm
workspace
来实现 monorepo
( pnpm 是快速、节省磁盘空间的包管理器,采用符号链接的方式来管理模块 )
-
全局安装
pnpm
:npm install pnpm -g #全局安装pnpm pnpm init -y # 初始化配置文件
-
配置
.npmrc
文件在这里 vue3 源码 并没有配置该文件,因为没有对应的需求,我是在 pnpm 上找到的,便于我们后续引用一些包的依赖,例如:我们在全局安装了 vue ,默认情况下,它里面的依赖不会被提升到
node_modules
下,所以我们添加了如下配置,该设置可以将所依赖的模块提升到node_modules
中,在 npm 下是不需要配置的shamefully-hoist=true 或者 public-hoist-pattern=*
效果如下图:左侧是没有添加配置,右侧是加了配置的,
node_module
里面的依赖有了明显的不同 -
配置
pnpm-workspace.yaml
文件该配置定义了 工作空间的根目录,也就是我们的
packages
目录packages: - 'packages/*'
当我们配置了该文件后,以后安装依赖会报如下错误,解决办法就是 在最后追加
-w
的标识,表示我要安装在workspace-root
的下面 -
安装开发时依赖
pnpm install typescript esbuild minimist -w -D
-
配置
tsconfig.json
:目的是在packages
里面 一个模块引用其他的模块,使用 ts 来支持导入,不至于导入报错,编辑器飘红{ "compilerOptions": { "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ "module": "ESNext", /* Specify what module code is generated. */ "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ "baseUrl": ".", /* Specify the base directory to resolve non-relative module names. */ "lib": ["esnext", "dom"], "paths": { "@vue/*": ["packages/*/src"] }, /* Specify a set of entries that re-map imports to additional lookup locations. */ "sourceMap": true, "outDir": "dist", /* Specify an output folder for all emitted files. */ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ /* Type Checking */ "strict": true, /* Enable all strict type-checking options. */ "skipLibCheck": true, /* Skip type checking all .d.ts files. */ "resolveJsonModule": true } }
-
开发时打包配置:
scripts/dev.js
:基于esbuild
实现开发环境打包,使用minimest
来解析命令行参数,进一步可以设置打包产物的路径 和 输出产物的类型格式,如iife || cjs || esm
。const args = require("minimist")(process.argv.slice(2)); const { build } = require("esbuild"); const { resolve } = require("path"); const target = args._[0]; const format = args.f; const pkg = require(resolve(__dirname, `../packages/${target}/package.json`)) const outputFormat = format.startsWith('global') ? 'iife' : format === 'cjs' ? 'cjs' : 'esm' const outfile = resolve(__dirname, `../packages/${target}/dist/${target}.${format}.js`) build({ entryPoints: [resolve(__dirname, `../packages/${target}/src/index.ts`)], outfile, bundle: true, sourcemap: true, format: outputFormat, globalName: pkg.buildOptions.name, platform: format === 'cjs' ? 'node' : 'browser', watch: { onRebuild(error) { if (!error) console.log('rebuild~~'); } } }).then(() => { console.log('watching~~'); })
-
设置
package.json
的scripts
命令 执行上一步脚本,并通过设置参数作为入参。"scripts": { "dev": "node scripts/dev.js reactivity -f global" }
响应式模块配置先行
因为下一节我们重点实现响应式模块,所以这里我们先配置下打包相关。
./packages
├─ reactivity
├─ src
└─ index.ts
└─ package.json
// packages/reactivity/package.json
{
"name": "@vue/reactivity", // 和 core 保持一致
"version": "1.0.0",
"description": "",
"main": "index.js",
"buildOptions": {
"name": "VueReactivity", // 打包后暴露出去的方法名字
"formats": [ // 定义打包格式
"global",
"cjs",
"esm-bundler"
]
}
}
转载自:https://juejin.cn/post/7123886199311499271