一、前言
根据 Vite 官方文档介绍,搭建一个项目只需要从一行命令开始:xxx create vite
,其中 xxx 指的是你所使用的包管理工具(本文以 npm 为例)。
执行这行命令后,会进入了一个终端问卷环节,输入项目名称、选择框架、语言等分支后,一个模板项目就出现了。
✔ Project name: … vite-project
✔ Select a framework: › Vue
✔ Select a variant: › TypeScript
Scaffolding project in vite-project...
Done. Now run:
cd vite-project
npm install
npm run dev
这种使用方式与我们以往常用的 CLI 工具有所不同,我们并没有预先在全局安装脚手架,却能顺利的搭建项目模板,这是如何做到的呢?
想弄清楚这点,要先从 npm CLI 中的 init 命令说起。
二、npm init
在 npm CLI 的文档中,能够找到 npm init
命令,create 则是 init 的一个别名。
它的效果如下:
npm init foo --> npm exec create-foo
npm init @user/foo --> npm exec @user/create-foo
这意味着,只要我们执行了 npm init vite
命令,它会在本地寻找或者去远程下载一个名为 create-vite
的 npm 包,exec
命令会从其 package.json
文件中寻找 bin
字段,执行对应的二进制或者 js 文件。
我们在 npm 官网上可以看到,create-vite 包的目录结构如下:
create-vite
├─dist
| └index.mjs
├─template-vue-ts
├─template-vue
├─template-vanilla-ts
├─template-vanilla
├─template-svelte-ts
├─template-svelte
├─template-react-ts
├─template-react
├─template-preact-ts
├─template-preact
├─template-lit-ts
├─template-lit
├─index.js
├─package.json
|...
这里面所有的以 template
开头的文件夹,就是我们创建 Vite 项目选择不同分支后最终获取到的对应的项目模板。
接下来,就需要去深入了解 create-vite 项目的源码了。
三、create-vite
从它的入口文件中可以分析出,整体的运行逻辑如下:
- 1、获取终端可选参数:项目名称、模板名称
- 2、使用 prompts 工具创建问卷,包含多个题目和单选分支
- 3、根据问卷结果,匹配对应的模板文件夹
- 4、拷贝模板到本地仓库,进行重置项目名称等后处理操作
对于参数的获取以及文件操作的部分不多说了,这里主要介绍其中涉及到的几个工具。
1、用 prompts 创建终端问卷
prompts,它是一个轻量级的创建终端问卷的工具,支持文本、数字、密码、单选、多选等多种类型的题目。除了 create-vite
之外,我们经常使用的 Taro CLI、Leo 脚手架等也都是基于它实现的终端问卷功能。
一个单选题可以这样定义:
{
type: 'select',
name: 'framework',
message: '请选择一个框架',
choices: [
{
title: 'React',
description: 'The library for web and native user interfaces',
value: 'react'
},
{
title: 'Vue',
description: '渐进式 JavaScript 框架',
value: 'vue'
}
],
initial: 1
}
如果我们想实现多级关联的问题,prompts 也支持将这些字段定义为一个回调函数,函数的参数正是前面几个问题的值:
{
choices: (prev) => {
if (prev === "react") return []; // js、ts、ts+swc...
else if (prev === "vue") return []; // js、ts、nuxt...
else return [];
};
}
prompts 还提供了自定义校验值的功能,如果要限制输入的数字要满足某个条件,可以这样:
{
type: 'number',
message: '请输入你的年龄',
validate: value => value < 18 ? '年龄不符合要求' : true
}
对于 Vite 初始化项目中的部分分支,比如 Nuxt、Svelte、electron-vite 等,其本身有另一套 create 工具,不需要在 create-vite
项目中重复做相同的事情。
在遇到这些问卷分支时,可以通过 spawn 调用系统命令执行 npm create vue/svelte/electron-vite
,将当前的任务转向对应的 create 工具。
2、用 kolorist 给终端加点颜色
为了实现更丰富的终端界面效果,create-vite
还用到了 kolorist,它提供了约 40 种不同的终端输出文字样式,包括文本颜色、背景色以及粗体下划线等多种字体格式。
在学习 kolorist 的代码之前,我们先了解下给终端文字添加颜色的原理。
在终端中输入:echo '\x1b[31m这里是红色的字体\x1b[0m'
,你会得到一串红色的「这里是红色的字体」。
其中 \x1b
指的是 ASCII 中的 ESC,ESC[31m
表示从此开始输出内容的前景色均为红色,ESC[0m
则表示重置所有样式。
于是我们可以封装出这样一个支持在终端输出红色的方法:
const red = (str) => {
const open = "\x1b[31m";
const close = "\x1b[0m";
return open + str + close;
};
console.log(red("这里是红色的字体"));
不同的颜色、样式有着不同编码,在此基础上封装一个创建方法的「工厂」,再加亿点点细节:
// 创建颜色/样式的工厂
const createColor = (start, end, level) => {
const open = `\x1b[${start}m`;
const close = `\x1b[${end}m`;
const regex = new RegExp(`\\x1b\\[${end}m`, 'g');
return (str) => {
// level 用于判断当前终端环境支持的色彩标准
return options.enabled && options.supportLevel >= level
? open + ('' + str).replace(regex, open) + close
: '' + str;
};
}
// 创建一个红色方法
const red = createColor(31, 39); // 31 为红色,39 指的是重置到默认颜色
// 创建一个粗体方法
const bold = createColor(1, 22);
......
没错,这正是 kolorist 的核心代码,详细内容可见 kolorist
在与 prompts 搭配使用时,只需将经过方法处理后的字符串作为配置参数即可:
{
choices: [
{
title: bold('React'), // 粗体标题
description: red('The library for web and native user interfaces'), // 红色描述
value: 'react'
},
],
}
3、用 unbuild 构建项目
create-vite
项目的构建工具选择的是 unbuild,除此之外,Vite 生态中的很多基础插件,比如 vite-plugin-vue
、vite-plugin-react
、vite-plugin-legacy
等都是使用 unbuild 进行构建的。
unbuild 是一个基于 Rollup 的构建工具,主要有以下优势:
- 1、配置简单。它默认集成了 Rollup 生态中很多优秀的插件,不需要复杂的配置,开箱即用。
- 2、默认支持 TypeScript,并提供 Commonjs 与 ESM 模块输出和类型文件生成功能。
- 3、构建速度快。采用了 esbuild 进行 js 代码转换,比 Rollup 更快。
- 4、内置了一个基于 esbuild 的轻量级的 file-to-file 工具 mkdist,能够在保持目录结构的情况下快速迁移文件夹。
对于 create-vite
这样一个规模很小的单文件入口项目,只需要下面简单的配置即可:
// create-vite/build.config.ts
export default {
entries: ["src/index"], // 入口文件
clean: true,
// 支持 Rollup 相关配置
rollup: {
inlineDependencies: true,
esbuild: {
minify: true
}
}
};
四、最后
在简单了解了 create-vite
的基本原理后,你也可以实现一个自己的 create 项目并发布到 npm 上。
这里是我发布的 create-eiinu,你可以打开终端,执行下面的命令来测试效果:
npm create eiinu
yarn create eiinu
pnpm create eiinu
效果如下:
✔ Input your packageName. … eiinu-project
✔ Pick your platform. › Taro
✔ Pick your framework. › Vue
✔ Pick the version of nutui. › NutUI 4
Done.
它目前内置了我平时经常用到的一些项目模板,可以通过这种方式快速地拉取这些模板到本地。
这种方式是通用的,并不局限于 npm 项目,可以将任何项目模板、代码片段放入仓库内。
我会把它当做模板源仓库,持续更新,欢迎关注。