前端自动化工具 plop.js

前言

平常大家开发每个页面骨架大致相似,每次开发新页面都要一步一步的配置,相当繁琐。有了Plop,就可以实现自动化了,Plop 旨在根据模板文件自动化创建组件,接下来我就来介绍一下Plop如何帮助我们在开发过程中提高工作效率。

安装

可参考 https://github.com/plopjs/plop

1
2
3
4
5
6
npm install --save-dev plop
//或
yarn add -D plop

#当然你也可以把它全局安装
npm install -g plop

然后在项目的根目录创建一个plopfile.js

1
module.exports = function (plop) {};

快速上手

配置

大致涉及这些文件:

1
2
3
4
5
6
7
8
9
10
11
12

├── plop-templates # templates模板
│ ├── cpv # cpv
│ ├── index.hbs # 模板定义
│ └── prompt.js # 配置
│ ├── view # view
│ ├── component # component
│ ├── store # store
│ └── utils.js # utils
├── plopfile.js # plopfile配置
├── package.json
└── yarn.lock
1
2
3
4
5
6
7
8
9
10
11
12
//plopfile.js
const cpvGenerator = require('./plop-templates/cpv/prompt');
const viewGenerator = require('./plop-templates/view/prompt');
const componentGenerator = require('./plop-templates/component/prompt');
const storeGenerator = require('./plop-templates/store/prompt.js');

module.exports = function(plop) {
plop.setGenerator('cpv', cpvGenerator);
plop.setGenerator('view', viewGenerator);
plop.setGenerator('component', componentGenerator);
plop.setGenerator('store', storeGenerator);
};

先来一个简单点的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//plop-templates/view/index.hbs
{{#if template}}
<template>
<div class="{{ properCase name }}"></div>
</template>
{{/if}}

{{#if script}}
<script>
export default {
name: '{{ properCase name }}',
props: {},
data() {
return {}
},
created() { },
mounted() { },
methods: {}
}
</script>
{{/if}}

{{#if style}}
<style lang="less" scoped>

</style>
{{/if}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//plop-templates/view/prompt.js
const { notEmpty } = require('../utils.js');

module.exports = {
description: 'generate a view',
prompts: [
{
type: 'input',
name: 'name',
message: 'view name please',
validate: notEmpty('name')
},
{
type: 'checkbox',
name: 'blocks',
message: 'Blocks:',
choices: [
{
name: '<template>',
value: 'template',
checked: true
},
{
name: '<script>',
value: 'script',
checked: true
},
{
name: 'style',
value: 'style',
checked: true
}
],
validate(value) {
if (value.indexOf('script') === -1 && value.indexOf('template') === -1) {
return 'View require at least a <script> or <template> tag.';
}
return true;
}
}
],
actions: data => {
const name = '{{name}}';
const actions = [
{
type: 'add',
path: `src/view/${name}/index.vue`,
templateFile: 'plop-templates/view/index.hbs',
data: {
name: name,
template: data.blocks.includes('template'),
script: data.blocks.includes('script'),
style: data.blocks.includes('style')
}
}
];

return actions;
}
};
1
2
3
//plop-templates/utils.js
exports.notEmpty = name => v =>
!v || v.trim() === '' ? `${name} is required` : true
1
2
3
4
5
6
//package.json
{
"scripts": {
"new": "plop",
},
}

运行

1
yarn new

20201129170600.png

20201129170727.png

20201129170821.png

20201129170920.png

这样即可创建完成了。

使用

plop命令相关主要借助promptInquirer

prompt 主要是命令行输入表单控件,它的相关功能:

  • 提示用户输入
  • 支持默认值和字段
  • 验证
  • 各种提示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//表单
var schema = {
properties: {
name: {
pattern: /^[a-zA-Z\s\-]+$/,
message: 'Name must be only letters, spaces, or dashes',
required: true
},
password: {
hidden: true
}
}
};
//
prompt.start();

//获取用户名,密码
prompt.get(schema, function (err, result) {
console.log('Command-line input received:');
console.log(' name: ' + result.name);
console.log(' password: ' + result.password);
});
1
2
3
4
5
6
7
8
9
10
11
12
//这是可用于验证和提示控件的属性的概述:
{
description: 'Enter your password', // 描述
type: 'string', // 类型
pattern: /^\w+$/, // 正则
message: 'Password must be letters', //警告提示
hidden: true, // 如果为true,则不展示
replace: '*', // 替换
default: 'lamepassword', // 默认
required: true // 是否必填
before: function(value) { return 'v' + value; } //回调执行前,执行
}

Inquirer 交互式命令行工具 相关功能:

  • 提供错误回调
  • 询问操作者问题
  • 获取并解析用户输入
  • 检测用户回答是否合法
  • 管理多层级的提示
参数 含义
type 类型 相关值有:input,number,confirm, list,rawlist,expand,checkbox,password,editor
name 存储当前字段的变量
message 问题的描述
default 默认值
choices 列表选项
validate 验证
filter 过滤
transformer 对用户回答的显示效果进行处理
when 根据前面问题的回答,判断当前问题是否需要被回答
pageSize 渲染行数
prefix 修改 message 默认前缀
suffix 修改 message 默认后缀
askAnswered 如果答案已经存在,则强制提示该问题。
loop 启用列表循环。默认值:true

有了这些配置,我们就可以定义自己的模板了。

最后贴一下部分配置代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//在指定文件创建模板和它的组件

const { notEmpty } = require('../utils.js');
const fs = require('fs');

let components = [];
const files = fs.readdirSync('src/view/');
files.forEach(item => {
let stat = fs.lstatSync('src/view/' + item);
if (stat.isDirectory() === true) {
components.push(item);
}
});

module.exports = {
description: 'generate a cpv',
prompts: [
{
type: 'input',
name: 'name',
message: 'cpv name please',
validate: notEmpty('name')
},
{
type: 'list',
name: 'dirName',
message: 'choose name please',
choices: components,
validate: notEmpty('dirName')
},
{
type: 'checkbox',
name: 'blocks',
message: 'Blocks:',
choices: [
{
name: '<template>',
value: 'template',
checked: true
},
{
name: '<script>',
value: 'script',
checked: true
},
{
name: 'style',
value: 'style',
checked: true
}
],
validate(value) {
if (value.indexOf('script') === -1 && value.indexOf('template') === -1) {
return 'View require at least a <script> or <template> tag.';
}
return true;
}
}
],
actions: data => {
const name = '{{name}}';
const dirName = '{{dirName}}';
const actions = [
{
type: 'add',
path: `src/view/${dirName}/${name}/index.vue`,
templateFile: 'plop-templates/cpv/index.hbs',
data: {
name: name,
template: data.blocks.includes('template'),
script: data.blocks.includes('script'),
style: data.blocks.includes('style')
}
},
{
type: 'add',
path: `src/view/${dirName}/component/CPV${name}.vue`,
templateFile: 'plop-templates/cpv/component.hbs',
data: {
name: name,
template: data.blocks.includes('template'),
script: data.blocks.includes('script'),
style: data.blocks.includes('style')
}
}
];

return actions;
}
};

参考