当前位置: 首页 > 工具软件 > Plop > 使用案例 >

使用plop快速生成前端组件

解河
2023-12-01

痛点

在项目开发中,我们都会有这样的经历,当我们要写一个新的控制器/组件/指令,通常有两种方式:

  • 第一,建立一个新文件,从头开始写
  • 第二,从现有的控制器/组件/指令文件中找一个功能或结构类似的,复制过来,改一改

无论是第一种还是第二种,平均都要花费10~15分钟的时间。如果一个项目中只发生几次这样的情况,那么这个时间成本我们还是勉强可以接受的,但是如果遇到中大型项目,有上百个组件、指令、控制器的时候,累积起来花费的时间成本是非常可怕的。

所以,我们现在有了第三种选择——plop。

plop能做什么

简要来说,我们可以使用plop来自动生成具有同一结构的代码文件。

什么是同构文件

所谓同构文件,就是那些具有相同的书写格式或页面结构的文件,典型地,我们在编辑器里新建html文件,在初始状态,它们都是以下样子:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  
</body>
</html>

或者,我们的每一个vue组件,可能都具有以下结构:

<template>
	<div>
		//...
	</div>
</template>

<script>

import { ... } from '...'

export default {
  name: '...',
  props: {
   //...
  },
  computed: {
  },
  data(){
  	return {
  	//...
  	}
  },
  mounted(){
  	//...	
  },
  created(){},
  components:{
  	//...
  }
}
</script>

<style scoped>
//...
</style>

再或者,我们的angular 指令可能都具有这样的结构:

angular.module('xxxModule', []).directive('xxx', [...,function() {
    // Runs during compile
    return {
        restrict: 'E',
        replace: true,
        template: "...",
        scope: {
           ...
        },
        link: function(scope, iElement, iAttrs) {
        	...
        }
    };
}]);

像这样所有具有类似相同结构的文件,我们称之为同构文件

plop 如何工作

plop通过命令行提问的方式获取必要信息,然后帮助我们生成我们想要的文件。

1.运行 plop

项目中加入了plop和进行了相关配置后,如果想要生成某一种类型的文件(可以是组件、指令、控制器等等任何具有同构模式的文件),直接在项目根目录下运行命令行工具,输入启动命令:

$ plop 

然后plop就会输出提前设置好的问题:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a3AHGkuV-1607591681872)(D:\document\个人文章\Seafile\doc\tech\plop\plop.png)]

2.回答问题

如上图所示,问题可能会是一个选择题(提供选项,使用键盘上下方向键选择),也可能是一个填空题(需要你输入答案),如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nncwAF79-1607591681875)(D:\document\个人文章\Seafile\doc\tech\plop\input.png)]

我们也可以看到,上面的选择题让我们选择一种生成器,一个生成器对应一种同构模式,我们选择了第二项component ,根据我们的配置,这种生成器会生成一个vue的组件文件,选择了生成器后,会让我们输入组件的名称(即上图所示的填空题), 我们输入组件名称后,又会继续问我们这个组件文件中要包含哪些部分:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jyba585c-1607591681877)(D:\document\个人文章\Seafile\doc\tech\plop\select.png)]

这是一个多选题,如果我们的组件只需要script部分,那么我们可以使用空格将其它两项的星号处置空,如果我们要的是一个包含以上三对标签的标准组件文件,那么我们默认将它们全部选中(即前面的括号中有星号)。选择结束,按下回车键,plop 就会帮助我们生成我们要的文件了,它会告诉我们生成的文件所在的位置:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iKFvrDYb-1607591681881)(D:\document\个人文章\Seafile\doc\tech\plop\position.png)]

3. 得到结果

我们根据命令行的提示,找到生成的文件,看看其中的内容:

// src/components/TestCom/index.vue

<template>
  <div />
</template>

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

<style lang="scss" scoped>

</style>

这就是plop帮我们生成的组件文件,而以上回答问题的过程总共花费时间不超过30秒。

配置

当然,要达到以上的效果,需要我们提前对plop 进行配置,我们看一下我们以上使用的示例是如何在项目中配置的。

1.安装plop

这一步比较简单,就是使用npm install 命令将plop 分别安装到全局环境和项目工程的开发依赖目录中。

2. 配置plopfile.js

在项目根目录中创建 plopfile.js 文件,内容如下:

const viewGenerator = require('./plop-templates/view/prompt')
const componentGenerator = require('./plop-templates/component/prompt')

module.exports = function(plop) {
  plop.setGenerator('view', viewGenerator)  // setGenerator(生成器名称,生成器配置)
  plop.setGenerator('component', componentGenerator) // 生成器名称会在命令行中提问时显示,见上文图
}

我们看到,在这个配置中,我们实际上做的工作就是在plop 对象中注册了两种类型的生成器,一种是生成 vue的页面视图组件的(viewGenerator), 另一种是生成普通组件的(componentGenerator)

我们以我们上文中用到的 componentGenerator 为例,来看看它们的配置:

3.配置生成器(Ganerator)

这里就不再具体解释了,看以下代码中的注释就可以了解生成器的各项配置说明:

//  ./plop-templates/component/prompt
const { notEmpty } = require('../utils.js')  // 引入工具方法 notEmpty,用来判断变量是否为空

module.exports = {
  description: 'generate vue component', //生成器描述,会在提问时缀在生成器名称后面,以中划线连接,见上文图
  prompts: [{ // 第一个问题
    type: 'input',   // 提问类型, input代表需要用户在命令行中输入
    name: 'name',   // 答案字段名称,这里为‘name’,代表组件名称
    message: 'component name please', // 答案提示,会在命令行等待用户输入前显示
    validate: notEmpty('name') // 验证器,用户输入的名称不能为空
  },
  //第二个问题
  {
    type: 'checkbox',  //提问类型为多选
    name: 'blocks', // 用户输入的答案会被存入 `blocks` 字段
    message: 'Blocks:', // 提示语
    choices: [{    // 可选项,这里有三种标签,默认都为选中
      name: '<template>',
      value: 'template',
      checked: true
    },
    {
      name: '<script>',
      value: 'script',
      checked: true
    },
    {
      name: 'style',
      value: 'style',
      checked: true
    }
    ],
    validate(value) {  // 验证用户的答案,这里vue组件至少需要script或template标签中的一种
      if (value.indexOf('script') === -1 && value.indexOf('template') === -1) {
        return 'Components require at least a <script> or <template> tag.'
      }
      return true
    }
  }
  ],  // 动作配置,即用户输入答案之后执行操作
  actions: data => {
    const name = '{{properCase name}}'   // 定义name字段值,这里是将用户输入的组件名称转为驼峰格式
    const actions = [{  // 要执行的操作列表,这里只有一种,就是 add,代表新增文件
      type: 'add',
      path: `src/components/${name}/index.vue`,  // 生成文件的路径,name为变量【此处使用handlebars模板语法】
      templateFile: 'plop-templates/component/index.hbs', // 模板路径,新生成的文件内容就是基于这个模板,该模板指定使用 handlebars 模板语法
      data: {     //要传给模板的数据
        name: name,  // 名称
        template: data.blocks.includes('template'),  // template 布尔值,是否生成template内容
        script: data.blocks.includes('script'), // script 布尔值,是否生成 script 内容
        style: data.blocks.includes('style') //  style 布尔值,是否生成 style 内容
      }
    }]

    return actions   
  }
}

我们看到,生活器做的工作就是对用户输入的答案就行一系列处理后,封装成数据传递给模板文件,然后模板文件根据传入的数据来决定生成什么及哪些内容。

我们看看以上配置中配置的模板文件的内容:

4.配置模板文件

// plop-templates/component/index.hbs

{{#if template}}
<template>
  <div />
</template>
{{/if}}

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

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

</style>
{{/if}}

在模板文件中,我们定义了该类型生成器对应的同构模式的基本模板,并且根据传入的数据来决定生成的文件中包含什么内容和哪些内容。

以上示例中,模板会根据三个标签类型的布尔值决定要不要生成这个标签内容,也会将传入的名称变量直接赋值给组件的 name 属性,当然,我们还可以在模板中定义更多的变量,然后由用户输入,再由生成器配置传递给模板来渲染。

至此,我们就了解了plop的基本用法和基本流程以及原理,以上只是一个简单示例,在实际业务中,我们还可以写出更加复杂的生成器,来服务于各种不同类型复杂同构模式的文件生成,具体请参考我翻译的polp文档中文版。

 类似资料: