在公司的项目中,很多需求是报表开发,但是报表页面间的相似度很高,同事就说能不能提高下开发效率,想个办法解决重复的工作,然后我就去找了下解决方案,找到了通过指令创建模板的神器--plop,在尝试之后发现可行,于是总结下使用方法
安装
plop是一个微型的脚手架工具,它的特点是可以根据一个模板文件批量的生成文本或者代码,不再需要手动复制粘贴,省事省力
前端工程可以通过安装依赖获取 plop 的能力
// 全局安装
npm i -g plop
// 本地安装
npm i --save-dev plop
使用
package.json 配置
安装完依赖,在package.json中配置以下命令
"scripts": {
"plop": "plop"
},
这样,就可以通过npm run plop 来使用plop了
编写模板
我们先写一个要生成的模板的样子,在src下新建一个文件夹template(随便建在哪都可以),然后在文件夹下新增文件component.hbs
plop使用Handlebars作为模板引擎,模板引擎能处理动态内容,在后面的用户自定义输入内容会用到
// src/template/component.hbs
import React, { useEffect, useState } from 'react';
export default () => {
useEffect(() => {
}, []);
return (
<div>
{{#if firstBtn}}
<button>第一个btn</button>
{{/if}}
{{#if secondBtn}}
<button>第二个btn</button>
{{/if}}
</div>
);
};
编写入口文件
使用plop需要一个入口文件,在根目录创建plopfile.js,当我们运行plop命令的时候,会以这个文件作为入口文件,这个文件需要导出一个函数
module.exports = (plop) => {};
这个函数接收到了plop,我们可以通过plop的setGenerator来创建模板,此函数接受两个参数,第一个参数为 Generator 的名称,第二个参数是配置项
module.exports = (plop) => {
plop.setGenerator('component', {
// 生成器描述
description: '创建一个组件',
// 询问用户的问题
prompts: [],
// 询问完成后获取到用户输入的内容执行的动作
actions: (data) => {
// 需要执行的动作
const actions = [];
return actions;
},
});
};
prompts
prompts配置项是一个数组,会根据数组中的顺序在终端让用户输入问题,比如我们把prompts配置成以下内容:
prompts: [
{
// 交互类型 input,number,confirm,list,rawlist,expand,checkbox,password,editor
type: 'input',
// 变量名,存储用户输入的信息,在后面会用到
name: 'name',
// 问题
message: '请输入组件名称',
// 输入项验证,返回文本则在终端提示用户文本内容,返回true则通过验证进行下一步
validate(val) {
if (!val) {
return '组件名称不可为空'
}
return true;
},
// 用户未输入的默认值
default:'testComponment',
//当满足函数内的条件时,当前问题才能出现,返回值是Boolean值,函数的参数是之前所有会话的答案
// when:(answers)=>{
// return true/false
// }
},
{
type: 'confirm',
name: 'firstBtn',
default: true,
message: '组件是否需要第一个按钮?',
when:(answers)=>{
// 当组件名不等于test时,询问组件是否需要第一个按钮
return answers.name!=='test'
}
},
{
type: 'confirm',
name: 'secondBtn',
default: true,
message: '组件是否需要第二个按钮?'
},
],
配置完问题,就可以在终端输入npm run plop 查看配置效果了
接收到用户输入的内容后,就可以在actions中执行操作
actions
actions是一个函数,可以接收到prompts中用户输入的内容,返回一个数组,数组中包括了要执行的内容
actions: (data) => {
// 用户输入内容都在data中,可用于js判断操作
const { name, firstBtn, secondBtn } = data;
const actions = [];
actions.push({
// type string 动作的类型 add(新增文件) append(在某个位置插入) modify(修改文件)
type: 'add',
// 操作的路径
path: './src/pages/{{properCase name}}/index.jsx',
// 文件模板路径 (内容比较少时,可直接用template)
templateFile: 'src/template/component.hbs',
});
//可以根据data的不同输入给actions push不同的操作,如更新路由文件:
if (新增个选项询问是否更新路由,判断用户是否需要更新路由) {
actions.push({
type: 'append',
pattern: /(?=(\/\/ flag))/, //正则匹配 //flag,在前面插入
path: './src/router.js',
template: `{
// 测试报表
name: '测试报表',
path: '/{{name}}',
component: './src/pages/{{properCase name}}/index.jsx',
},\n`,
});
}
return actions;
};
其中 properCase 将 dir 转为大驼峰的格式,常用的关键词:
camelCase: changeFormatToThis 小驼峰properCase: ChangeFormatToThis 大驼峰snakeCase: change_format_to_this 下划线分割
我们在actions里只用到了name属性,firstBtn, secondBtn会自动传入component.hbs模板文件中进行处理,在firstBtn选择yes,secondBtn选择no的情况下,生成的模板文件是这样:
import React, { useEffect, useState } from 'react';
export default () => {
useEffect(() => {}, []);
return (
<div>
<button>第一个btn</button>
</div>
);
};
多模板
一个项目中可能有多套模板,每套模板有不同的处理逻辑,其实很简单,只要多增加一个plop.setGenerator配置就行了,比如
module.exports = (plop) => {
plop.setGenerator('component', {
description: '创建一个组件',
prompts: [],
actions: (data) => {
const actions = [];
return actions;
},
});
plop.setGenerator('report', {
description: '创建一个报表',
prompts: [],
actions: (data) => {
const actions = [];
return actions;
},
});
};
这时候运行npm run plop,就会出现多模板选择。
配置单个setGenerator时不会出现,是因为只有一个生成器时默认使用该生成器