Skip to content

npm create vite 是如何实现的?

Published:

一、前言

根据 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、用 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-vuevite-plugin-reactvite-plugin-legacy 等都是使用 unbuild 进行构建的。

unbuild 是一个基于 Rollup 的构建工具,主要有以下优势:

对于 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 项目,可以将任何项目模板、代码片段放入仓库内。

我会把它当做模板源仓库,持续更新,欢迎关注。