前言

文中的脚手架工具已经发布到 npm,可以直接安装使用

经常写 vue 项目的小伙伴应该都有这种经历:

  • vue create hello-world

  • 选择各种 preset

  • cd hello-world

  • npm install

  • npm run serve

  • 安装各种周边

  • npm i vuex

  • npm i vue-router

  • npm i ...

  • ...

每新开一个项目就得按照这些步骤跑一遍,还要经常因为版本的问题,导致一些奇奇怪怪的 bugs 出现。

so,能不能把基本都会使用的周边给弄成一个模板,然后每次新开项目的时候直接采用类似 copy 的形式来开发呢?

正文

认识 vue-cli

找到 vue-cli 核心的代码:
vue-cli/packages/@vue/cli/bin/vue.js

可以发现,其实就是根据命令行交互得到的参数,然后生成对应的项目。

create 命令为例:

program
  .command("create <app-name>")
  .description("create a new project powered by vue-cli-service")
  .option(
    "-p, --preset <presetName>",
    "Skip prompts and use saved or remote preset"
  )
  .option("-d, --default", "Skip prompts and use default preset")
  .option(
    "-i, --inlinePreset <json>",
    "Skip prompts and use inline JSON string as preset"
  )
  .option(
    "-m, --packageManager <command>",
    "Use specified npm client when installing dependencies"
  )
  .option(
    "-r, --registry <url>",
    "Use specified npm registry when installing dependencies (only for npm)"
  )
  .option(
    "-g, --git [message]",
    "Force git initialization with initial commit message"
  )
  .option("-n, --no-git", "Skip git initialization")
  .option("-f, --force", "Overwrite target directory if it exists")
  .option("--merge", "Merge target directory if it exists")
  .option("-c, --clone", "Use git clone when fetching remote preset")
  .option("-x, --proxy <proxyUrl>", "Use specified proxy when creating project")
  .option("-b, --bare", "Scaffold project without beginner instructions")
  .option("--skipGetStarted", 'Skip displaying "Get started" instructions')
  .action((name, options) => {
    if (minimist(process.argv.slice(3))._.length > 1) {
      console.log(
        chalk.yellow(
          "\n Info: You provided more than one argument. The first one will be used as the app's name, the rest are ignored."
        )
      );
    }
    // --git makes commander to default git to true
    if (process.argv.includes("-g") || process.argv.includes("--git")) {
      options.forceGit = true;
    }
    require("../lib/create")(name, options);
  });

其实就是输入完 project name 之后 执行 require('../lib/create')(name, options) 里面的方法:

vue-cli/packages/@vue/cli/lib/Creator.js

思路

参考 vue-cli 的实现方式,我们可以实现一个适用于自己的 cli,提前编写好项目模板,只需要输入简单的交互指令,就可以生成属于我们自己的项目内容。

具体实现

准备好项目模板

Realzzz1874/v3-tpl

const TPLS = {
  v3: {
    url: "github:Realzzz1874/v3-tpl#fvue-tpl/v3",
    desc: "v3-tpl",
    repo: "https://github.com/Realzzz1874/v3-tpl/tree/fvue-tpl/v3",
  },
};

安装脚手架所需要的依赖包

"dependencies": {
  "chalk": "^5.0.1",
  "commander": "^9.4.0",
  "download-git-repo": "^3.0.2",
  "handlebars": "^4.7.7",
  "inquirer": "^9.1.0",
  "log-symbols": "^5.1.0",
  "ora": "^6.1.2"
}

开始干吧!

#!/usr/bin/env node

import fs from "fs";
import { program } from "commander";
import download from "download-git-repo";
import inquirer from "inquirer";
import handlebars from "handlebars";
import ora from "ora";
import chalk from "chalk";
import logSymbols from "log-symbols";

const log = chalk.hex("##1DBD21");
console.log(log("fvue: A fast vue project cli!"));

const TPLS = {
  v3: {
    url: "github:Realzzz1874/v3-tpl#fvue-tpl/v3",
    desc: "v3-tpl",
    repo: "https://github.com/Realzzz1874/v3-tpl/tree/fvue-tpl/v3",
  },
};

program.version("1.0.0");
program
  .command("init <tpl> <proj>")
  .description("init tpl")
  .action((tpl_name, proj_name) => {
    const { url, repo } = TPLS[tpl_name];
    console.log(`github repo: ${repo}`);
    const tpl_loading = ora("tpl downloading...");
    tpl_loading.start();
    download(
      url,
      proj_name,
      {
        clone: true,
      },
      (err) => {
        if (err) {
          tpl_loading.fail();
          console.log(logSymbols.error, chalk.red("tpl download fail."));
        } else {
          tpl_loading.succeed("tpl download success.");
          const log = chalk.hex("#FFA500");
          console.log(logSymbols.success, log("tpl init success."));

          inquirer
            .prompt([
              {
                type: "input",
                name: "name",
                message: "please input proj name:",
              },
              {
                type: "input",
                name: "description",
                message: "please input proj description:",
              },
              {
                type: "input",
                name: "author",
                message: "please input proj author:",
              },
            ])
            .then(async (answers) => {
              const package_path = `${proj_name}/package.json`;
              const package_content = fs.readFileSync(package_path, "utf-8");
              const package_result = await handlebars.compile(package_content)(
                answers
              );
              fs.writeFileSync(package_path, package_result);
              const log = chalk.hex("#FFA500");
              console.log(logSymbols.success, log("proj init success."));
            });
        }
      }
    );
  });

program
  .command("list")
  .description("tpl list")
  .action(() => {
    for (let key in TPLS) {
      console.log(`   - ${key} (${TPLS[key].desc})`);
    }
  });
program.parse(process.argv);

使用方法

list
fvue list
fvue: A fast vue project cli!
   - v3 (v3-tpl)
init
fvue init <tpl> <proj>

just like:

fvue init v3 myVueProject
fvue: A fast vue project cli!
github repo: https://github.com/Realzzz1874/v3-tpl/tree/fvue-tpl/v3
✔ tpl download success.
✔ tpl init success.
? please input proj name: myVueProject
? please input proj description: my fast vue project
? please input proj author: realzzz
✔ proj init success.

结语

文中的脚手架工具已经发布到 npm,可以直接安装使用

npm i vuefast -g
# OR
yarn add vuefast -g

安装之后,可以直接使用 fvue 命令:

# list
fvue list

#init
fvue init <tpl> <proj>