npm init @vitejs/app 到底干了什么

我心飞翔 分类:javascript

本篇文章来自团队小伙伴 @小明额额额 的一次学习分享,希望跟大家分享与探讨。

求积硅步以致千里,勇于探享生活之美。

npm init @vitejs/app 到底干了什么

背景

最近在闲暇时间学习尤大大的新框架 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 目录,那我们就再深入的看看这个目录里面有什么?

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,岂不更美哉?

不单如此,我们还可以建立公司内部或对外的模板库项目,将所有常用的模板项目维护到一个项目中,统一维护管理,岂不快哉?(坑已挖好,活已安排,下次一定)...

以上便是本次分享的全部内容,希望对你有所帮助 ^_^

喜欢的话别忘了动动手指,点赞、收藏、关注三连一波带走。


关于我们

我们是万拓科创前端团队,左手组件库,右手工具库,各种技术野蛮生长。

一个人跑得快,不如一群人跑得远。欢迎加入我们的小分队,牛年牛气轰轰往前冲。

回复

我来回复
  • 暂无回复内容