- 本文参加了由 公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
- 这是源码共读的第9期,链接:juejin.cn
准备工作
先克隆源码到本地
cmd
git clone git@github.com:vuejs/create-vue.git
安装依赖,打包可执行文件
cmd
pnpm install // 安装依赖
pnpm build // 输出outfile.cjs文件
用 node 启动 outfile.cjs 就可以启动脚手架了
cmd
node outfile.cjs
package.json
在看源码之前先热个身,先来看看 package.json 里都有什么
json
// package.json
{
"name": "create-vue",
"version": "3.7.5",
"description": "An easy way to start a Vue project",
// 默认是 CommonJS 规范,Module表示此包采用 ES module 模块化
"type": "module",
// bin 定义命令,使该包安装为一个可执行命令
"bin": {
"create-vue": "outfile.cjs"
},
// 包下载安装完成时包括的所有文件
"files": [
"outfile.cjs",
"template"
],
// 规定该包 node 运行环境版本
"engines": {
"node": ">=v16.20.0"
},
// npm 脚本命令
"scripts": {
"prepare": "husky install",
"format": "prettier --write .",
"build": "zx ./scripts/build.mjs",
"snapshot": "zx ./scripts/snapshot.mjs",
"pretest": "run-s build snapshot",
"test": "zx ./scripts/test.mjs",
"prepublishOnly": "zx ./scripts/prepublish.mjs"
},
"repository": {
"type": "git",
"url": "git+https://github.com/vuejs/create-vue.git"
},
"keywords": [],
"author": "Haoqun Jiang <haoqunjiang+npm@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/vuejs/create-vue/issues"
},
"homepage": "https://github.com/vuejs/create-vue#readme",
"devDependencies": {
"@tsconfig/node18": "^18.2.2",
"@types/eslint": "^8.44.2",
"@types/node": "^18.17.17",
"@types/prompts": "^2.4.4",
"@vue/create-eslint-config": "^0.3.2",
"@vue/tsconfig": "^0.4.0",
"ejs": "^3.1.9", // 模板编译
"esbuild": "^0.18.20", // 打包
"esbuild-plugin-license": "^1.2.2",
"husky": "^8.0.3",
"kolorist": "^1.8.0", // 命令行美化
"lint-staged": "^14.0.1",
"minimist": "^1.2.8", // 解析命令行选项
"npm-run-all2": "^6.0.6", // 提供npm脚本并行或者串行执行
"prettier": "^3.0.3",
"prompts": "^2.4.2", // 命令行提示语
"zx": "^7.2.3" // google开发的用于写复杂命令脚本的工具,用于替代bash
},
"lint-staged": {
"*.{js,ts,vue,json}": [
"prettier --write"
]
},
"dependencies": {
"pnpm": "^8.6.5"
}
}
脚手架是如何通过一个命令创建项目的?
npm create
我们可以在 Vue3 的官方网站上看到推荐直接使用 npm create vue@latest
来创建我们的项目,而实际上 npm create vue@latest
就是在使用 create-vue
脚手架,显然这跟 vue-cli
有很大的区别。
vue-cli
需要我们通过 npm install -g @vue/cli
命令在全局安装,该包会在 node 下全局安装一个 vue
命令文件,调用 vue xxx
命令实际上就是在调用这个 vue
文件,然后在 vue
中通过 node.js
运行了 /node_modules/@vue/cli/bin/vue.js
来创建项目。
那么 npm create vue
是为何不需要 npm install
就能直接运行脚手架脚本呢?
从 npm 的官方文档发现 create
是 init
的一个别名,也就是说 npm create
就等于 npm init
。
-
npm init / npm create
:初始化一个 package.json 文件
-
npm init <initializer> / npm create <initializer>
:本质上是调用
npm exec create-<initializer>
命令, 也就相当于npx create-<initializer>
。<initializer>
可以理解为一个简称,它真正的包名为create-<initializer>
,也只有符合这种命名方式的npm包才可以执行这样的命令。
因此,以下命令可以达到与npm create vue@latest
同样的效果:
bash
npm init vue@latest
npx create-vue@latest
npm exec create-vue@latest
npm create vue
执行流程
首先查找本地的 create-vue
npm包,在其中找到 package.json
文件 bin
属性指明的可执行文件,即 outfile.cjs
,当本地不存在 create-vue
包时,会从远程源下载该包。
json
// package.json
{
"bin": {
"create-vue": "outfile.cjs"
},
}
outfile.cjs
是什么时候产生的?
通过 package.json
我们可以看到 outfile.cjs
是通过 build
命令执行 build.mjs
脚本打包生成的,但是我们在 npm create vue
命令期间,是不会调用 build
命令的。
js
// build.mjs
await esbuild.build({
bundle: true,
entryPoints: ['index.ts'], // 入口文件
outfile: 'outfile.cjs', // 输出可执行文件
format: 'cjs',
platform: 'node',
target: 'node14',
plugins: [
...
]
})
这就与 npm
包发布机制有关,npm publish
会将 package.json
中 files
属性指明的文件/文件夹发布到 npm
包中。create-vue
在发布npm包之前调用 prepublishOnly
命令,而在 prepublish.mjs
脚本中可以发现 build
命令的身影,也就是这个阶段生成的 outfile.cjs
可执行文件。
好了,热身结束,可以去下一节看看源码了create-vue 源码解析(二)。