【话说Vue3】工程&架构分析

376 阅读5分钟

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情

前言

Vue作为与React、Angular并驾齐驱的前端框架”三巨头“之一,已广泛的应用在了前端的各个领域,从spa到ssr处处都有Vue的身影, 三者孰好孰坏的话题,一直都是”切图仔们“热衷的话题。本系列文章 先将这一热门(烫手)话题按下不表😁,抱着学习的态度,来试着还原一个 mini 版的 Vue3。作为系列文章的开篇,我们先来看看这 Vue3 的”外观“吧。

目录划分:

core-main
├── README.md             
├── api-extractor.json     
├── package.json
├── packages              -- 项目子包
├── pnpm-lock.yaml        -- pnpm 依赖版本管理文件
├── pnpm-workspace.yaml   -- monorepo 项目的标志
├── rollup.config.js      -- rollup 打包配置文件
├── scripts               -- 各种执行脚本文件
└── tsconfig.json         -- ts 的配置文件

这里只选择性的列举了一部分,通过查看目录结构,我们至少可以得出以下结论:

  1. Vue3 的包管理工具是采用的 pnpm;
  2. 通过 pnpm 来实现的 monorepo 架构,将子包都放在 packages 目录下了;
  3. 采用的是 rollup 来作为构建工具;
  4. Vue3 的开发语言选用的是 ts;

为什么是 pnpm?

pnpm 是一个包管理器,本质上与 npm/yarn 是一样的,但它却有几个优势:

  1. 依赖安装速度快;

  2. 磁盘空间利用高;

    同一个包不会重复安装,比如 10 个项目都依赖了 lodash,在 npm/yarn 中,lodash 可能会被安装 10 次,而每个版本可能只是因为仅仅改变了一个文件,pnpm 的做法是将新增的文件添加到了存储中心,这样就极大程度地复用之前版本的代码,

  3. 天然支持 monorepo

    只需要在项目更目录新增一个 pnpm-workspace.yaml

  4. 避免依赖包的非法访问

    比如在 a 包中依赖了 lodash,那么我们在项目的 package.json 文件中不用显式的声明 lodash 依赖;这就是所谓的”幽灵依赖“,这样不是很好吗?但我们考虑一下场景: 如果有天 a 包中移除了 lodash 依赖,那么可能会导致我们自己的项目异常;

为什么是 ts?

在 vue2.x 时代,尤大更中意的是 Facebook(现在应该叫 Meta 了)开源的 flow,但没想到 flow 最后烂尾了,而 typescript 生态则越来越丰富,其他的正如Vue 官方说的: 构建时通过静态分析检测许多常见错误。这减少了生产中出现运行时错误的机会,也使我们能够更自信地重构大规模应用程序中的代码。TypeScript 还通过 ide 中基于类型的自动补全改进了开发者的体验。

package.json

{
  "private": true,
  "version": "3.2.36",
  "packageManager": "pnpm@7.1.0",
  "scripts": {
    "dev": "node scripts/dev.js",
    "build": "node scripts/build.js",
    "release": "node scripts/release.js",
    "preinstall": "node ./scripts/preinstall.js",
  },
  "types": "test-dts/index.d.ts",
  "tsd": {
    "directory": "test-dts"
  },
  "gitHooks": {
    "pre-commit": "lint-staged",
    "commit-msg": "node scripts/verifyCommit.js"
  },
  "lint-staged": {
    "*.js": [
      "prettier --write"
    ],
    "*.ts?(x)": [
      "eslint",
      "prettier --parser=typescript --write"
    ]
  },
  "engines": {
    "node": ">=16.5.0" // 针对node版本也做了要求
  },
  "devDependencies": {
    "@microsoft/api-extractor": "^7.15.1",
    "@vue/reactivity": "workspace:*",
    "@vue/runtime-core": "workspace:*",
    "@vue/runtime-dom": "workspace:*",
    "esbuild": "^0.14.35",
    "lint-staged": "^10.2.10",
    "rollup": "~2.38.5",
    "vue": "workspace:*",
    "yorkie": "2.0.0"
  }
}

packagge.json 文件内容较多,我这里剔除掉了一些,着重来看下与项目工程相关的配置:

npm 的钩子函数(生命周期)

所谓钩子函数,就是当我们执行一些 npm 脚本命令前后触发的一些钩子函数,比如当我们执行 npm/yarn/pnpm install 命令时,会先触发 preinstall ,之后再触发 postinstall/install ,它们的关系类似于这样:

image.png

执行结果类似于这样:

image.png

除此之外,还有像 ”publish“ 等等。更多的请看看官方文档

Vue3 这边在执行 install 之前会先执行

"preinstall": "node ./scripts/preinstall.js",

我们来看看这个文件

// scripts/preinstall.js
if (!/pnpm/.test(process.env.npm_execpath || '')) {
  console.warn(
    `\u001b[33mThis repository requires using pnpm as the package manager ` +
      ` for scripts to work properly.\u001b[39m\n`
  )
  process.exit(1)
}

这里用 process.env.npm_execpath 获取了一次包管理器的安装路径,npm、yarn、pnpm的路径不同,类似于这样:

image.png

以此来强制我们是不是使用的pnpm来作为包管理工具。

代码风格检查与提交规范

这里的提交规范校验用的是 yorkie,这个依赖包是尤大 fork 的 husky,主要是将git hooks的读取位置改在了 package.json 文件中:

  "gitHooks": {
    "pre-commit": "lint-staged",
    "commit-msg": "node scripts/verifyCommit.js"
  },
  "lint-staged": {
    "*.js": [
      "prettier --write"
    ],
    "*.ts?(x)": [
      "eslint",
      "prettier --parser=typescript --write"
    ]
  },

在 git commit 之前做了代码美化与eslint校验,并校验了commit信息格式。

workspace: *

"@vue/reactivity": "workspace:*",
"@vue/runtime-core": "workspace:*",
"@vue/runtime-dom": "workspace:*",

在 pnpm 模式下 package.json 有一种特殊的写法:"workspace:*",为了指明这是工作区的依赖,防止自动去 npm 上寻找导致混乱,当使用此协议时,pnpm 将拒绝解析除本地 workspace 包含的 package 之外的任何内容。

例如,如果 bar 中有 "foo":"^1.0.0" 的这个依赖项,则 foo@1.0.0 链接到 bar。 但是,如果 bar 的依赖项中有 "foo": "2.0.0",而 foo@2.0.0 在工作空间中并不存在,此时会从 npm registry 上安装foo@2.0.0,如果设置为 "foo": "workspace:2.0.0" 时,安装将会失败,因为 "foo@2.0.0" 不存在于此 workspace 中

@microsoft/api-extractor

这个文件主要是与@microsoft/api-extractor这个插件配合使用

  • 可以产生三种不同的输出类型:

  • API Report —API Extractor 可以跟踪项目主入口点的所有导出,并生成一个报告作为 API 审查工作流的基础。

  • .d.ts Rollups—类似于 Webpack 可以将所有 JavaScript 文件集中到一个包中进行分发,API Extractor 可以将你的 TypeScript 声明处理成一个单独的.d。ts 文件。

  • API Documentation—API Extractor 可以为每个项目生成“doc model”JSON 文件。这个 JSON 文件包含提取的类型签名和文档注释。API -documenter 配套工具可以使用这些文件生成 API 参考网站。

总结

到这里,我们主要分析了 Vue3 的目录结构、将 pnpm 作为包管理工具的优势、package.json 文件的一些关键信息,如果把 Vue3 看成一颗洋葱,那么我们现在可能仅仅是看了个轮廓。在接下的系列文章中,我们一步步来抽丝剥茧,慢慢将这颗洋葱剥开,实现一个 mini 版的 Vue。下篇我们继续:开发环境的搭建。