masterkong / blog

文章区
21 stars 2 forks source link

基于Vue-cli 3的定制脚手架命令行工具开发实践 #10

Open masterkong opened 5 years ago

masterkong commented 5 years ago

基于vue-cli 3的定制脚手架命令行工具开发实践

起因

在之前的项目中,脚手架都是采用 yeoman 开发,但逐渐暴露出几个不足之处:

vue-cli 的出现解决了上述两个问题,想到就可以在vue-cli的基础上定制项目内部需求,各取所长,让专业的人做专业的事。

实现

原理很简单,使用npm发布一个node命令行工具,将vue-cli命令进行封装,注意是将vue-cli命令进行封装,而不依赖 @vue/cli。指定一个特殊命令(本文采用 go 命令),将定制好的 preset 交给 vue-cli处理,而其他情况则完全透转给 vue-cli。

具体实现分为两大部分:定制preset和node命令行工具的实现

定制preset

一个 Vue CLI preset 是一个包含创建新项目所需预定义选项和插件的 JSON 对象,让用户无需在命令提示中选择它们。

Preset 主要由三个特殊文件构成:

preset.json: 包含 preset 数据的主要文件(必需)。

//preset.json
{
"useConfigFiles": true,
"plugins": {
"@vue/cli-plugin-babel": {},
"@vue/cli-plugin-eslint": {
"config": "airbnb",
"lintOn": ["save","commit"]
}
},
//"router": true,
//"routerHistoryMode": true,
//"vuex": true,
"cssPreprocessor": "less"
}

可以看出preset.json主要由两大块组成:

  • plugins是vue-cli提供的插件配置,如果不熟悉的话,可以使用 vue create 实际创建一个项目,保存成一个preset,然后再通过 vue config 查看preset中相关插件的配置。

prompts.js: 一个可以通过命令行对话为 generator 收集选项的 prompts 文件

//prompts.js
module.exports = [
  {
    name: "vuex",
    type: "confirm",
    message: `是否需要使用vuex`,
    default: false
  },{
    name: "router",
    type: "confirm",
    message: `是否需要使用vue-router`,
    default: false
  }
];

这里提供的是一些交互命令,使脚手架具有更强的灵活性。产生的所有选项都会交给后面的generator.js进行进一步的处理。

generator.js:允许一个 generator 向 package.json 注入额外的依赖或字段,并向项目中添加文件。

//generator.js
module.exports = (api, options, rootOptions) => {
  //安装一些基础公共库以及项目内部库
  api.extendPackage({
    dependencies: {
      "lodash": "^4.0.0",
    },
    devDependencies: {

    }
  });

  if(options.vuex){
    api.extendPackage({
      dependencies: {
        "vuex": "^3.0.1"
      }
    });
  }

  if(options.router){
    api.extendPackage({
      dependencies: {
        "vue-router": "^3.0.1"
      }
    });
  }

  //以EJS模板文件的方式生成项目文件
  api.render({
    './src/main.js': './template/main.js'
  },options);

  api.render({
    './f2eci.json': './template/f2eci.json',
    './vue.config.js': './template/vue.config.js'
  });

}

generator中提供的api参数具有很强大的功能,可以实现代码级别上的定制。

api.extendPackage即可以直接添加依赖包,也可以根据prompt交互式命令结果来动态添加依赖包。

api.render可以根据传入的参数,以EJS模板渲染的文件生成项目代码文件。

node命令行工具

命令行工具的实现很简单,有兴趣的话可以参考【译】使用Node.js创建命令行脚本工具。核心就是当执行 my-vuecli go my-project 时,会把前面定义的 preset 转交给vue-cli处理。

#!/usr/bin/env node
const path = require('path');
const preset = path.resolve(__dirname,'my-preset');

const program = require('commander');
const execa = require('execa');

program
  .version(require('./package').version)
  .description('基于vue-cli的定制脚手架')
  .usage('<command> [options]');

program
  .command('go <project-name>')
  .description('使用定制preset创建vue-cli项目')
  .action(function (project) {
    //最核心就是这里
    let command = `vue create ${project} --preset ${preset}`;
    const child = execa.shell(command, {
        stdio: 'inherit'
    });
  })

//除了专有的go命令,其他的命令都转交给vue-cli
program
  .command('*')
  .action(function(){
      let command = process.argv.slice(2);
      command.unshift('vue');

      const child = execa.shell(command.join(' '),{
          stdio:'inherit'
      });
  });

program.parse(process.argv);

使用

将定制好的脚手架publish为一个npm包,再配合 npm install -g @vue/cli 就可以愉快的使用了.

最后附上 demo源码地址

参考文献