如何构建框架
提升用户的开发体验
- 有良好的提示
- 可以结合DevTools自定义打印格式
控制代码的体积
- 区分开发环境和生产环境,让警告、异常代码成为dead code ,最终构建时不会存在这些代码
Tree shaking
- 使用rollup 或者wepack 可以进行简单Tree Shaking
- rollup 或者wepack中,合理的运用
/*#__PURE__*/
可以将代码作为dead code 处理。
输出怎样的构建产物
- 希望在HTML中直接通过script标签引入使用: 所以你输出了IIFE格式资源
- 希望通过type=modle的script标签引入使用:输出了ESM资源
- 希望服务端渲染,即nodeJs中require使用:输出了cjs资源
错误处理
- 统一错误处理
- 用户可以注册错误处理程序
vue3框架如何构建
Monorepo管理项目
Monorepo 一种项目管理方式。概念上很好理解,就是把多个项目放在一个仓库里面。
一般 Monorepo 的目录如下所示,在 packages 存放多个子项目,并且每个子项目都有自己的
package.json
├── packages | ├── pkg1 | | ├── package.json | ├── pkg2 | | ├── package.json ├── package.json
pnpm管理包
pnpm就是一个包管理工具,原生支持Monorepo,比npm和yarn更快一些。
优势
-
包安装速度极快。
pnpm在绝多大数场景下,包安装的速度都是明显优于 npm/yarn,速度会比 npm/yarn 快 2-3 倍。
-
磁盘空间利用非常高效。
用 npm/yarn 的时候,如果 100 个项目都依赖 lodash,那么 lodash 很可能就被安装了 100 次,磁盘中就有 100 个地方写入了这部分代码。
使用 pnpm 只会安装一次,磁盘中只有一个地方写入,后面再次使用都会直接使用
hardlink
;即使一个包的不同版本,pnpm 也会极大程度地复用之前版本的代码。举个例子,比如 lodash 有 100 个文件,更新版本之后多了一个文件,那么磁盘当中并不会重新写入 101 个文件,而是保留原来的 100 个文件的hardlink
,仅仅写入那一个新增的文件
。 -
支持 monorepo
-
安全性高
举个栗子
vue3中使用pnpm,workpace来实现Monorepo。那么如何实现的?
-
安装pnpm
npm install pnpm -g //全局安装pnpm pnpm init -y //初始化配置文件package.json
-
根目录下新建pnpm-workpace.yaml文件 和packages目录
yaml 是一种配置文件的格式,在这里相当于告诉pnpm需要Monorepo的环境;
接下来我们写的所有包都会放在packages目录
# pnpm-workpace.yaml packages: - 'packages/*' # 声明所有包都在packages目录下,并且这些包都可以基于node_modules下的包来构建
-
安装共享的包(如:pnpm install vue -w)
-w : 表示放在根模块下,作为一个共享的包。(即:workspace-root)
// 目录结构 ├── node_modules | ├── .pnpm // 依赖的模块都放在这里 | ├── vue // 举个栗子 : vue 中的响应式模块、运行时模块等这些vue依赖的模块,都放在了.pnpm目录下
此时假设我们想用使用vue 中的响应式模块,由于它放在了.pnpm目录下,使用起来并不方便;为了开发方便我们希望在使用pnpm安装的时候也能像npm一样,将依赖的包都放在node_modules下。
根目录下新建.npmrc文件。(pnpm的的配置文件)
// .npmrc shamefully-hoist = true // 羞耻的提升
// 加了.npmrc配置的目录结构 ├── node_modules | ├── .pnpm | ├── nanoid | ├── csstype | ├── vue | ├── ... // 不难发现vue中的幽灵依赖都出现在了node_modules中,可以随时使用
这时候我们就可以安装vue3需要的共享模块
支持TS
命令行解析工具(minimist)
打包工具(esbuild)
pnpm install typescript minimist esbuild
-
packages中进行配置
c:\vue3\packages\reactivity> pnpm init // 生成配置文件package.json // package.json中配置打包格式等信息 { "name": "@vue/reactivity", "version": "1.0.0", "description": "", "main": "index.js", "buildOptions": { "name": "VueReactivity", "formats": [ "global", "cjs", "esm-bundler" ] } }
-
让TS支持在包内导入
pnpm tsc --init
根目录下新建tsconfig.json文件
// tsconfig.json { "compilerOptions": { //输出的目录, "outDir": "dist", //采用sourcemap, "sourceMap": true, //目标语法, "target": "es2016", //模块格式, "module": "esnext", //模块解析方式, "moduleResolution": "node", //严格模式, "strict": false, //解析json模块, "resolveJsonModule": true, //允许通过es6语法引入commonjs模块, "esModuleInterop": true, //jsx不转义, "jsx": "preserve", //支持的类库esnext及dom, "lib": [ "esnext", "dom" ], // 以当前路径为基准,如果你的路径 "@vue/*" 我就帮你找到 "packages/*/src" "baseUrl": ".", "paths": { "@vue/*": [ "packages/*/src" ] } } }
此时就已经完成了环境的搭建工作,可以开始尝试开发vue3的响应式模块等了。
-
实现构建流程
在package.json中scripts加入打包命令
// package.json { "name": "vue3-code", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "dev": "node scripts/dev.js reactivity -f global" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "esbuild": "^0.14.43", "minimist": "^1.2.6", "typescript": "^4.7.3" } }
新建scripts\dev.js文件,配置构建产物信息
//解析命令行 const args = require('minimist')(process.argv.slice(2)) //node scripts/dev.js reactivity -f global const {resolve} = require('path'); //node中的内置模块 const {build} = require('esbuild') const target = args._[0] || 'reactivity'; const format = args.f || 'global'; //开发环境只打包某一个 (找到对应包下的package.json) const pkg = require(resolve(__dirname, `../packages/${target}/package.json`)) //iife立即执行函数(function(){})() //cjs node中的模块 module.exports //esm 刘览器中的esModule模块 import const outputFormat = format.startsWith('global') ? 'iife' : format == 'cjs' ? 'cjs' : 'esm' const outfile = resolve(__dirname, `../packages/${target}/dist/${target}.${format}.js`) // 天生支持TS 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(`rebuilt~~`) } } }).then(() => { console.log('watching~~') })
此时就已经完成了环境的搭建工作,可以开始尝试在packagess开发vue3的响应式模块等了。
最后一句
学习心得!若有不正,还望斧正。希望掘友们不要吝啬对我的建议。