vue3源码架构认识

182 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

了解一下vue3源码用的架构,pnpm + monorepo + esbuild + ts

pnpm

优点:快,节省磁盘空间

不像npm每次安装都会在项目下安装整套依赖,pnpm会有一个存储,统一安装在一个地方,安装过的依赖可以硬链接连接,不下载重复依赖。

越来越多选择pnpm进行包管理,之前是yarn+learn。

解决Phatom(幻影依赖),全部依赖声明都扁平化在根目录node_modules,pnpm则只处理显式声明的依赖,对于只在单package声明的依赖,只通过软链的形式链接在.pnpm 解决Doppelgangers(分身),不同包用了不同的版本的同一依赖,混乱出现,存在依赖的依赖中。pnpm则是通过软链的形式引用,各个版本的express平铺在.pnpm中。

安装时的格式,举个例子:

安装 vue 时,还要下载vue它的依赖们。
node_modules
 - .pnpm 这里放vue的依赖们
 - vue 
  • 之前npm用一些包,比如安装a,b是a包的依赖,可以直接用,现在不行了,pnpm下找不到,但可以配置打平依赖
#.npmrc
// 幽灵依赖。羞耻提升。
shamefully-hoist = true

monorepo

在vue3项目下有很多的库,如响应式等,需要进行包的管理。

  • pnpm-workspace.yaml里面配置package为包,共享依赖
packages:
  - 'packages/*'
// 因为 分包,所以在根目录下安装依赖时会报错,需要加-w确认是在根目录下
pnpm i typescript minimist esbuild -w

创建package准备

reactivity为例子。

packages文件夹下创建reactivity,执行pnpm init,提示输入名字 @vue/reactivity

package.json 里 加入自定义配置

 "buildOptions": {
     //引用的名字
    "name": "VueReactivity",
        // 打包的格式
    "formats": [
      "esm-bundler",
      "cjs",
      "global"
    ]
  },
  • 想用公用包的功能 shared,需要 tsconfig.js里配置,让其不会去 pnpm里去找,而是去项目里找
import { isObject } from "@vue/shared";
# tsconfig.js 为了在package里的包互相引用
baseUrl:'.',
paths:{
 @vue/*:['packages/*/src']
}

打包

开发运行用 esbuild ,go写的,打包很快,生成用 rollup,多格式打包更好

运行

github1s.com/vuejs/core/…

# `package.json` 
"scripts": {
    "dev": "node scripts/dev.js",
        //运行 `reactivity` 时
        node scripts/dev.js reactivity -f global
  • scripts/dev.js
//node scripts/dev.js reactivity -f global
const { build } = require('esbuild')
const args = minimist(process.argv.slice(2))
// 得到 {_:[reactivity],f:global}
// 获取执行命令时 打包的参数
const target = args._.length ? args._[0] : 'reactivity'
const formats = args.f || 'global'; // esm-bunlder global cjs
// 开发环境只打包一个
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}.${formats}.js`
)
build({
    //入口
  entryPoints: [resolve(__dirname, `../packages/${target}/src/index.ts`)],
  outfile,
  bundle: true, //所有包打包一起
  external,
  sourcemap: true,
  format: outputFormat,
  globalName: pkg.buildOptions?.name,//打包的全局的名字,var VueReactivity = (function (exports) {
  platform: format === 'cjs' ? 'node' : 'browser',
  watch: { //监控文件变化,热更新
    onRebuild(error) {
      if (!error) console.log(`rebuilt: ${relativeOutfile}`)
    }
  }
}).then(() => {
  console.log(`watching: ${relativeOutfile}`)
})

compositionAPI优点

1 vue2用的optionsAPI,编写复杂业务逻辑需要反复横跳

2 vue2属性用this访问,指向不明

3 vue2很多未使用方法和属性会被打包,所有全局api都在vue对象上公开,vue3 tree-shaking更好,代码更好压缩

4 组件逻辑共享,vue2用 mixins 实现,有命名冲突等问题,v3 更方便。

采用ts

2采用flow进行类型判断,对ts不友好,vue3用ts重构,对ts友好