开发一个简易版的脚手架 | 青训营笔记

851 阅读4分钟

这是我参与「第四届青训营 」笔记创作活动的第3天

这一篇来介绍以下脚手架的开发, 此脚手架参考了 vue-cli2 和 vue-cli3 版本, 最后以简单快速上手为依据选择只开发模板选择的功能。

自研脚手架的起因是 之前在用 vue 开发的时候, 遇到 node-sass 这个东西, 以及一些其他的包, 就是因为版本的问题导致一直报错报错, 排查了很久, 最后还是找能正常运行的项目复制粘贴解决的。 从那时起就打算做自定义脚手架了。

本来几个月前就写好了, 但当时的功能不是很完善, 直到最近 系统的学习了 webpack 之后, 就决定更新下 脚手架, 并写个博客记录下。

我的脚手架仓库

此博客以收录到我的个人博客

大致功能

  1. 用户输入 wjy create <projectName>, 准备创建项目
  2. 解析用户命令, 弹出交互语句, 询问以什么方式创建
  3. 用户选择模板后, 弹出选择什么模板
  4. 根据用户的选择 创建 package.json, 主要是项目名
  5. 根据选择渲染模板
  6. 提示完成

项目目录树

|-- bin
|   |-- wjy
|-- lib
    |-- ConfigTransform.js
    |-- Creator.js
    |-- Generator.js
    |-- PromptModuleAPI.js
    |-- create.js
    |-- promptCretor.js
    |-- tplCreator.js
    |-- generator
    |   |-- authTpl
    |   |   |-- index.js
    |   |   |-- template
    |   |-- baseTpl
    |   |   |-- index.js
    |   |   |-- template
    |   |-- reactTpl
    |   |   |-- index.js
    |   |   |-- template
    |   |-- vueTpl
    |       |-- index.js
    |       |-- template
    |-- promptModules
    |   |-- authTpl.js
    |   |-- baseTpl.js
    |   |-- reactTpl.js
    |   |-- vueTpl.js
    |-- utils

处理用户命令

首先我们要处理 用户输入的 wjy create <projectname>

bin/wjy 文件

#! /usr/bin/env node
const program = require("commander")

// 创建项目指令

program
    .command("create <project-name>") // 增加创建指令
    .description("create a new project powered by vue-cli-service") // 增加描述信息
    .action((name) => {
        // 没有插件选项,直接进行create name
        require("../lib/create")(name);
    })


program.parse(process.argv)

它注册了一个 create 命令, 并在 package.json 中添加

"bin":{
    "wjy":"./bin/wjy"
}

npm link, 就可以将 wjy 注册成全局指令, 之后就可以用 wjy create...了

与用户交互-询问选择什么创建项目

bin/create.js 中写入如下初始代码

async function create(name) {
  const type = await inquirer.prompt({
    name: "mode",
    message: "选择功能创建项目:",
    type: "list",
    choices: () => [
      {
        name: "从模板创建",
        value: "tpl",
        short: "tpl",
      },
    ],
  });
  
  console.log(type.mode)
}

这主要是用来选择通过什么方式来创建项目,我这里为了简单就只写了一个从模板创建, 接下来的就是重头戏了。

从模板中创建项目

生成选项信息

接下来我们要实现以下功能

image.png

首先看 Creator.jstplCreator.js 这里是最后生成 choices 中的数组的文件. 这里定义了一个 featurePrompt .

class Creator {
  constructor() {
    this.featurePrompt = [];
  }

  getFinalPrompts() {
    const prompts = [this.featurePrompt];
    return prompts;
  }
}

module.exports = Creator;

tplCreator 是专门用来进行模板创建的, 它继承了Creator, 并重写了 featurePrompt

const Creator = require("./Creator");
class tplCreator extends Creator {
  constructor() {
    super();
    this.featurePrompt = {
      name: "features",
      message: "选择一个模板创建项目:",
      type: "checkbox",
      choices: [],
    };
  }
}

module.exports = tplCreator;

然后, 还有一个往里面注入数据的API

module.exports = class PromptModuleAPI {
  constructor(creator) {
    this.creator = creator;
  }

  // 功能选项注入
  injectFeature(feature) {
    this.creator.featurePrompt.choices.push(feature);
  }
};

那么,以上三个都准备好了后, 我们只要调用这个功能选项注入的API即可。 大家看 promotModules 文件夹下的文件. 这里就是实现注入每个具体模板的选项。

module.exports = (api) => {
  api.injectFeature({
    name: "权限认证登录模板",
    value: "authTpl",
    short: "authTpl",
    description: "权限认证登录模板, 已经封装好动态路由添加、登录功能",
    checked: false,
  });
};

到这里, 我们把每个模板的信息都调用了 这个注入 API, 接下来就考虑怎么获取这里面的数据, 现在看下 create.js

if (type.mode === "tql") {
  var creator = new tplCreator(); // 生成一个模板构造器的实例
  // 获取获取提示语
  const promptModules = getPromptModules(type.mode);
  // 实现API, 注入 creator 实例
  const promptAPI = new PromptModuleAPI(creator);
  // 执行每个提示语, 让他们调用 injectFeature 这个功能, 使所有
  // 的选项都注入到 creator 下的 `featurePrompt` 数组里
  promptModules.forEach((m) => m(promptAPI));
}

// 获取提示语,通过在 promptModules 下用 require获取
function getPromptModules(mode) {
  let res = null;
  switch (mode) {
    case "tpl":
      res = ["authTpl", "baseTpl", "reactTpl", "vueTpl"].map((file) =>
        require(`./promptModules/${file}`)
      );
      break;
    default:
      process.exit(1);
      break;
  }
  return res;
}

最后使用如下代码进行选择

// 弹出提示语进行交互,获取最终prompt
const answers = await inquirer.prompt(creator.getFinalPrompts());

console.log(answers) // {features:reactTpl}

根据选择的模板生成文件

这个主要是 Generator 这个函数起作用, 这个函数这里就不直接写了, 主要是将 generator 文件下的先解析成二进制文件, 再写入给定的目录下即可。

总结

现在已经有了 vue,react,basic,auth 这个模板库的选择, 接下来的工作

  • vue, react 分别搭建一个后台管理系统
  • 尝试自定义组件, 并在脚手架中加上选择特定组件进行创建的功能