npm init @vitejs/app 到底干了什么
背景最近在闲暇时间学习尤大大的新框架 ViteJs 的时候发现,创建一个新的基于 Vite 的项目时,使用的命令方式是:npm init @vitejs/app复制代码这跟我们熟悉的 CLI 创建 Vue 项目的命令完全不一样:vue create project-name复制代码「在不安装脚手架工具的情况下,还能直接使用 npm 创建项目?」带着好奇心小小的探究了一下。首先我们要知道这个命令 n
背景
最近在闲暇时间学习尤大大的新框架 ViteJs 的时候发现,创建一个新的基于 Vite 的项目时,使用的命令方式是:
npm init @vitejs/app
复制代码
这跟我们熟悉的 CLI 创建 Vue 项目的命令完全不一样:
vue create project-name
复制代码
「在不安装脚手架工具的情况下,还能直接使用 npm 创建项目?」带着好奇心小小的探究了一下。
首先我们要知道这个命令 npm init @vitejs/app 是要做什么?
一番谷歌 + 官网文档,实际上这个命令就是要创建一个新的 Vite 项目,且基于某个模版,这个模版可以是 Vue / React 或其它。那么它到底是如何创建的?带着这个问题我们先来重温下 npm init。
复习 npm init
npm init 对于我们来说应该非常熟悉了,通常我们使用 npm init 初始化一个 package.json 文件来起手一个项目。大多数小伙伴只用到这,但是这个命令后面是可以携带参数的?这个参数能干什么呢?对于我这见惯大场面的 cv 程序员来说,翻手就是一个谷歌。谷歌大佬告诉我们 npm-init:
npm init <initializer>通常被用于创建一个新的或者已经存在的 npm 包。
initializer 在这里是一个名为 create-<initializer> 的 npm 软件包,该软件包将由 npx 来安装,然后执行其 package.json 中 bin 属性对应的脚本,会创建或更新 package.json 并运行一些与初始化相关的操作。
官方说明也给出了命令相对应的一些示例:
| 命令 | 等同 |
|---|---|
npm init foo |
npx create-foo |
npm init @usr/foo |
npx @usr/create-foo |
npm init @usr |
npx @usr/create |
文章开头的命令 npm init @vitejs/app 正好匹配到了第二条示例,对应起来应该是这样:
npm init @vitejs/app -> npx @vitejs/create-app
复制代码
从上面的解释可以看出,在命令行中运行 npm init @vitejs/app,实际上是通过了 npx 运行了名为 @vitejs/create-app 这个包,那么我们就去 Vite 官方仓库找一找有没有叫 create-app 的文件?
查看 ViteJs 源码发现 packages 文件夹中确实存在一个 create-app 目录,那我们就再深入的看看这个目录里面有什么?

@vitejs/create-app 项目做了什么?
在 create-app 文件夹下的 package.json 中发现 bin 入口:
{
"name": "@vitejs/create-app",
// ...没错了,就是这
"bin": {
"create-app": "index.js",
"cva": "index.js"
},
}
复制代码
bin 属性配置了 create-app 的执行入口文件 index.js ,那我们就去看看 index.js 中做了什么操作,源码过长,部分精简以便享用,感兴趣的小伙伴可深挖:
// https://github.com/vitejs/vite/blob/main/packages/create-app/index.js
// 省略非关键代码
const TEMPLATES = [
yellow('vanilla'),
green('vue'),
green('vue-ts'),
//...
]
async function init() {
let targetDir = argv._[0]
if (!targetDir) {
// 第一步:确定用户录入的 Project name
const { name } = await prompt({
type: 'input',
name: 'name',
message: `Project name:`,
initial: 'vite-project'
})
targetDir = name
}
const root = path.join(cwd, targetDir)
console.log(`\nScaffolding project in ${root}...`)
// 第二步:检查是否存在同名目录且是否为空目录
if (!fs.existsSync(root)) {
fs.mkdirSync(root, { recursive: true })
} else {
const existing = fs.readdirSync(root)
if (existing.length) {
//...
}
}
// 第三步 校验并选择模版
let template = argv.t || argv.template
let message = 'Select a template:'
if (!template || !isValidTemplate) {
const { t } = await prompt({
type: 'select',
name: 't',
message,
choices: TEMPLATES
})
template = stripColors(t)
}
const templateDir = path.join(__dirname, `template-${template}`)
const write = (file, content) => {
const targetPath = renameFiles[file]
? path.join(root, renameFiles[file])
: path.join(root, file)
if (content) {
fs.writeFileSync(targetPath, content)
} else {
copy(path.join(templateDir, file), targetPath)
}
}
// 第四步:拷贝并写入文件
const files = fs.readdirSync(templateDir)
for (const file of files.filter((f) => f !== 'package.json')) {
write(file)
}
const pkg = require(path.join(templateDir, `package.json`))
// 第五步:拷贝 package.json 提示安装运行
write('package.json', JSON.stringify(pkg, null, 2))
// ...提示 `npm install / npm run dev`
}
// 省略功能函数...
init().catch((e) => {
console.error(e)
})
复制代码
上面的代码主要逻辑如下:
- 第一步:确定 Project name ,用户输入或默认;
- 第二步:检查本地是否存在同名目录,并判断是否为空目录;
- 第三步:选择要创建的模板,vue、vue-ts、react 等;
- 第四部(核心):根据选择的模板匹配到项目下以
template-开头的目录,将目录中的所有文件拷贝到本地项目目录中; - 第五步:拷贝修改完 name 的新 package.json 到新项目中,并提示安装依赖和运行;
到此我们已经搞清楚 npm init @vitejs/app 背后是通过执行 create-app 中 bin 所指定的脚本文件。而这个脚本文件(这里是 index.js)所做的工作,就是根据一些配置和模版项目,利用 node 将所有模版文件拷贝到本地项目目录中,从而完成了一个根据模板创建项目的系列操作。
创建项目可用 CLI 脚手架,为何要如此大费周章呢?莫急且看。
npm init VS vue create
我们使用 vue create 来创建项目时,背后是 Vue-CLI 给予我们的能力。所以我们得首先安装 Vue-Cli,然后才可以使用它来创建项目。而 npm init 则跳过了 CLI 这部分,它基于指定脚本来实现,所以与 vue create 对比,它的优点:
- 项目即工具,更加简单直接;
- 不用安装额外的 CLI 工具,多一个工具就多一个使用成本;
- 更新方便,无需同时维护模板和 CLI 工具;
所以,总的来说使用 npm init 创建项目更加简单和纯粹。
加深 package.json 认识
不难发现,create-app 入口文件关键是 package.json 文件的 bin 属性,那么 bin 和 main 有什么区别呢?
| main | bin |
|---|---|
| 属性是一个 module ID,是程序的主要的入口点,当然如果不设置,默认值就是 index.js | 如果此 npm 包带有 bin 属性,那么此 npm 包的可执行文件就会被链接到当前项目的 ./node_modules/.bin 中,此后在命令行中就很方便的执行这个包,比如:node node_modules/.bin/myapp,更加详细的解释可参照 package.json bin |
不得不说,package.json 中每个属性都有它的用处,只有仔细的阅读和分析其的含义和用法,才会有创造轮子的那点灵光啊!
学完能做什么?
公司新的项目层出不穷,目前项目还在做微前端的重构,微前端中的各个子模块都需要使用一个模板 Ctrl + C / Ctrl + V 来创建,虽然对于我们 cv 程序员来说是小 case,但是如果我们有了自己的 create-app,我们翻手一个 npm init @company/app,岂不更美哉?
不单如此,我们还可以建立公司内部或对外的模板库项目,将所有常用的模板项目维护到一个项目中,统一维护管理,岂不快哉?(坑已挖好,活已安排,下次一定)...
以上便是本次分享的全部内容,希望对你有所帮助 ^_^
作者:VANTOP前端团队
链接:https://juejin.cn/post/6948202986573135908
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
更多推荐


所有评论(0)