提高生产力-自动化模板Plop

1,793 阅读6分钟

image.png

首先,携全家祝大家元旦快乐。

此次讲述重点为plop自动化模版功能。利用公司内部的网关项目,进行本次实践,为了便于项目结构理解,稍讲述该项目结构,如下:

API 网关是微服务架构中的一种服务,它为客户端提供共享层和 API,以便与内部服务进行通信。 我们可以为PC、小程序和公共 API 提供网关服务,他们是来自多个其他微服务的接口。

在我们实际的项目当中,针对目录结构的约定如下:

image.png

image.png

image.png

根据官网结构在实际业务中演变成以服务为模块划分:

image.png

此时就出现了大量重复性工作,在新增1服务时,需手动创建2文件夹3文件,在新增1接口时,手动添加3关联调用,经常出现copy代码,导致调用不通问题排查,大大降低了开发效率。

那么如何解决这个问题呢?某技术大牛说过,我们要用code去解决code过程中带来的痛点,我们以码农自居时,千万别妄自菲薄,2022了朋友萌,不要再用男耕女织的方式进行开发了,我们要学会利用各种工具提高生产力,提高产出,余出来的时间水水群,摸摸鱼不好吗。

铺垫完了,那么我们就来说说,如何解决男耕女织的痛点:自动化。

对于这种需要反复的任务,例如:添加路由,新增一组配置,组件库开发,大量结构一致页面,创建单测片段等场景,我们都必须考虑让机器代替手工,虽然人的思维是没问题的,但是在批量生产面前,效率和质量,自动化处理能做的更加准确,同时对于规范代码也有很大的帮助,利用自动化抽象模板,产出既一样又不一样的代码~

重点来了:Plop神器不知道是啥?可以去看文档 github.com/plopjs/plop

接下来我会把我配置的过程给大家贴出来,供大家参考(入门级介绍,大佬轻喷),本次主要介绍用模版直接生成内容及在已有内容的文件中追加模板产出的内容两个场景。

1、安装 plop :npm install --save-dev plop

然后在package.json中scripts中添加一行 "plop": "plop"

完事直接运行 npm run plop

2、在根目录下创建plopfile.js、plop-template文件夹、然后plop-template文件夹中创建文件夹add,相应创建下图的几个文件,因为本次是为了贴合controller,service,router的模版更改,制作对应的模板。

最后贴出我的目录结构就是这样,如果你项目路径有变化,请严格对照修改你的path。

image.png

添加如下代码:

// 创建add
const addGenerator = require('./plop-template/add/prompt.js');
module.exports = function(plop) {
  plop.setGenerator('add', addGenerator);
};

场景一:创建文件add 首先我们的添加controller文件的命令配置

module.exports = {
  description: '创建新服务配置', // 描述这个generate的作用
  prompts: [{
    type: 'input', // 问题的类型
    name: 'moduleName', // 问题对应得到答案的变量名,可以在acitons中使用该变量
    message: '文件名称:', // 在命令行中的问题
    default: 'pages',
    validate: moduleName => {
      const reg = /^[^0-9][a-zA-Z0-9_]+$/;
      return reg.test(moduleName);
    },
  }],
  actions: data => { // 这里可以通过data获取输入的pathname
    const { moduleName } = data;
    const bigModuleName = moduleName.charAt(0).toUpperCase() + moduleName.slice(1);
    const actions = [
      {
        type: 'add', // 操作类型 添加文件
        path: `app/controller/${moduleName}/index.js`, // 添加的文件的路径
        templateFile: 'plop-template/add/controller.hbs', // 模版文件的路径
        data: {
          name: moduleName,
          Name: bigModuleName,
        },
      }
    ];
    return actions;
  },
};

这就是plop的执行命令配置了; 我们已经在plopfile.js文件中引入了该文件,当我们执行plop时,该文件会被解析; 其中description是当前动作的名称,prompts配置是问答模式下命令的配置,action是利用在收集用户问答模式下收集来的参数,进行模版创建追加修改等动作的依据。

创建controller.hbs , 那么我们的controller模板中写,

'use strict';

const Controller = require('egg').Controller;


class {{ Name }}Controller extends Controller {
  constructor(config) {
    super(config);
    this.service{{ Name }} = this.ctx.service.{{ name }}.index;
  }
  // -- APPEND HERE --
  /**
  *@params
  */
  async index() {
    this.ctx.body = await this.service{{Name}}.index();
  }
}

module.exports = {{ Name }}Controller;

已知在配置时我们将Name,name作为参数进行了传递,那么在拉取的模板中,就会使用对应的参数显示; 其中,// -- APPEND HERE --是作为后续追加内容做的占位符,后续我们会讲到;

到此处一个完整的例子就完成了,相应的我添加了service和router的配置及模板。我们可以尝试执行npm run plop,看控制台会提示内容是否成功;

第一步问答:

image.png

成功了的样子

image.png 看一下产出结果

image.png

还不错,是我想要的东西。

如果出错了,没关系,错误提示还是能很明显的告诉我们是因为什么出错了

image.png

如果成功了,那恭喜你,我们可以进行下个例子的讲解了;

场景二、追加内容可以利用append,也可使用modify

我们已经从上个例子中创建了js文件,那么后续追加新的接口,怎么添加呢?

在plop的命令中有多种内容追加方式,我此处采用了最笨最直接的方式来实现-----modify配合占位符(// -- APPEND HERE --)

我们来看下append命令的编写过程,首先在plopfile.js中相应位置添加

const appendGenerator = require('./plop-template/append/prompt.js');
plop.setGenerator('append', appendGenerator);

append文件夹中prompt.js

'use strict';
module.exports = {
  description: '添加新接口', // 描述这个generate的作用
  prompts: [{
    type: 'input', // 问题的类型
    name: 'serviceName', // 问题对应得到答案的变量名,可以在acitons中使用该变量
    message: '为哪个服务添加新接口:', // 在命令行中的问题
    default: 'bsvc',
    validate: serviceName => {
      const reg = /^[^0-9][a-zA-Z0-9_]+$/;
      return reg.test(serviceName);
    },
  }, {
    type: 'input', // 问题的类型
    name: 'pathname', // 问题对应得到答案的变量名,可以在acitons中使用该变量
    message: '路径名称,多个使用逗号分隔(eg: controller,service,router)', // 在命令行中的问题
    default: 'controller,service,router',
  }, {
    type: 'input', // 问题的类型
    name: 'interfaceName', // 问题对应得到答案的变量名,可以在acitons中使用该变量
    message: '接口名称,多个使用逗号分隔(eg: getUsers,getList)', // 在命令行中的问题
    default: 'getUsers',
  }],
  actions: data => { // 这里可以通过data获取输入的pathname
    const { interfaceName, serviceName, pathname } = data;
    const bigInterfaceName = interfaceName.charAt(0).toUpperCase() + interfaceName.slice(1);
    const bigServiceName = serviceName.charAt(0).toUpperCase() + serviceName.slice(1);
    const pathnameList = pathname.split(',');
    const actions = [];
    if (pathnameList.includes('controller')) {
      actions.push({
        type: 'modify', // 操作类型 添加文件
        path: `app/controller/${serviceName}/index.js`, // 添加的文件的路径
        templateFile: 'plop-template/append/controller.hbs', // 模版文件的路径
        pattern: /(\/\/ -- APPEND HERE --)/gi,
        data: {
          name: serviceName,
          Name: bigServiceName,
          interfaceInfo: {
            interfaceName,
            bigInterfaceName,
          },
        },
      });
    }
    if (pathnameList.includes('service')) {
      actions.push({
        type: 'modify', // 操作类型 添加文件
        path: `app/service/${serviceName}/index.js`, // 添加的文件的路径
        templateFile: 'plop-template/append/service.hbs', // 模版文件的路径
        pattern: /(\/\/ -- APPEND HERE --)/gi,
        data: {
          name: serviceName,
          Name: bigServiceName,
          interfaceInfo: {
            interfaceName,
            bigInterfaceName,
          },
        },
      });
    }
    if (pathnameList.includes('router')) {
      actions.push({
        type: 'modify', // 操作类型 添加文件
        path: `app/router/${serviceName}.js`, // 添加的文件的路径
        templateFile: 'plop-template/append/router.hbs', // 模版文件的路径
        pattern: /(\/\/ -- APPEND HERE --)/gi,
        data: {
          name: serviceName,
          Name: bigServiceName,
          interfaceInfo: {
            interfaceName,
            bigInterfaceName,
          },
        },
      });
    }
    return actions;
  },
};

配置命令内容大家经过上一个例子应该能理解了大部分,

在action中我们新增了 pattern: /(// -- APPEND HERE --)/gi参数来对应'modify'进行repalce,道理通俗易懂且傻瓜,没什么难度

OK再次执行npm run plop, 你会发现,我们的命令动作增加了一个

image.png

执行结果

image.png

值得注意的是, // -- APPEND HERE --占位符要一直携带,不然你下次替换谁呢,对吧

通过以上实例,本次模板自动化实践就结束了,撒花🎉

image.png

本年度最后一天,掘金萌新,祭出第一篇文章,文笔不好,敬请谅解,以上代码存在较多需要优化内容,仅供初次尝试的同学查看,也请各位大佬斧正。