likes
comments
collection
share

前端工程化-使用plop生成项目模板文件

作者站长头像
站长
· 阅读数 45

概述

使用方法

  1. 安装插件
pnpm i plop -D 
// pnpm install -g plop
  1. 创建配置文件plopfile.js

在项目跟目录下,创建配置文件plopfile.js,内容如下:

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

module.exports = (plop) => {
  plop.setGenerator('view', viewGenerator);
};

plopfile.js中导出一个函数,该函数接受plop对象作为它的第一个参数;plop对象公开包含setGenerator(name, config)函数的plop api对象。

  1. 新建模板和脚本命令文件

在项目根目录下新建文件夹(如plop-templates),放置模板(view/index.vue)和脚本命令(view/prompt.js)文件;

目录结构如图:

前端工程化-使用plop生成项目模板文件

view/index.vue模板如下:

<template>
    <div class="{{name}}Container"></div>
</template>

<script setup lang="ts" name="{{name}}">
    import { getCurrentInstance, ref, reactive, onMounted } from "vue"; 、
    import { useRouter } from "vue-router";
    
    const {
        proxy: { $Urls, $Request, $Modal }
    } = getCurrentInstance() as any;

    const router = useRouter();
    
    defineExpose({});

    onMounted(() => { 

    });
</script>

<style lang="scss" scoped>
    @import "./index.scss";
</style>

提示:这里的模板内容,大家可以根据自己的项目需求,进行自定义设置 ,主要目的是方便项目整体复用。

view/index.scss模板如下:

.{{name}}Container{ }

router/index.vue模板如下:

{ 
    path: "/{{name}}", 
    name: "{{name}}", 
    component: () => import("@/views/{{name}}/index.vue"), 
    meta: { 
         title: '{{menu}}' 
    }
 },

view/prompt.js脚本命名如下:

module.exports = {
	description: "新建一个页面",
	prompts: [
		{
			type: "input",
			name: "name",
			message: "页面名称:",
			validate(name) {
				if (!name) {
					return "请输入页面名称";
				}
				return true;
			}
		},
		{
			type: "checkbox",
			name: "blocks",
			message: "需要包含什么:",
			choices: [
				{
					name: "<template>",
					value: "template",
					checked: true
				},
				{
					name: "<script>",
					value: "script",
					checked: true
				},
				{
					name: "<style>",
					value: "style",
					checked: true
				}
			],
			validate(value) {
				if (!value.includes("template") || !value.includes("script") || !value.includes("style")) {
					return "<template>|<script>|<style> 是必须的";
				}
				return true;
			}
		},
		{
			type: "confirm",
			name: "wantCps",
			message: "你想要给新页面添加组件吗?"
		},
		{
			type: "input",
			name: "cpsName",
			message: "组件名称:",
			when: function (answer) {
				// 当wantCps为true的时候才会到达这步
				return answer.wantCps; // 只有我return true才会这个confirm
			},
			validate(name) {
				if (!name) {
					return "请输入组件名称";
				}
				return true;
			}
		},
		{
			type: "confirm",
			name: "wantRouter",
			message: "你想要给新页面添加路由吗?"
		},
		{
			type: "input",
			name: "menu",
			message: "左侧菜单名称:",
			when: function (answer) {
				return answer.wantRouter;
			},
			validate(name) {
				if (!name) {
					return "请输入菜单名称";
				}
				return true;
			}
		}
	],
	actions: data => {
		const name = "{{camelCase name}}";
		let actions = [
			{
				type: "add",
				path: `src/views/${name}/index.vue`,
				templateFile: "plop-templates/view/index.vue"
			},
			{
				type: "add",
				path: `src/views/${name}/index.scss`,
				templateFile: "plop-templates/view/index.scss"
			},
			{
				type: "add",
				path: `src/views/${name}/interface.ts`
			}
		];

		let cpsName = "{{properCase cpsName}}";
		const cpsItem = [
			{
				type: "add",
				path: `src/views/${name}/components/${cpsName}/index.vue`,
				templateFile: "plop-templates/view/components/index.cps.vue"
			},
			{
				type: "add",
				path: `src/views/${name}/components/${cpsName}/index.scss`,
				templateFile: "plop-templates/view/components/index.cps.scss"
			}
		];

		const routerItem = [
			{
				type: "append",
				pattern: /routes*\:* \[/,
				path: "src/router/index.ts",
				templateFile: "plop-templates/router/index.vue",
				data: {
					name: "{{name}}",
					menu: "{{menu}}"
				}
			}
		];

		if (data.wantCps && data.wantRouter) {
			return [...actions, ...cpsItem, ...routerItem];
		} else if (data.wantCps) {
			return [...actions, ...cpsItem];
		} else if (data.wantRouter) {
			return [...actions, ...routerItem];
		}
		return actions;
	}
};

  1. 设置自定义命令

在package.json 添加 script脚本

{
  "scripts": {
    "plop": "plop"
  },
}
  1. 运行命令
pnpm run plop view

操作方法和文件生成

输入命令 pnpm run plop view

前端工程化-使用plop生成项目模板文件

生成文件如下:

前端工程化-使用plop生成项目模板文件

dataScreen/index.vue模板内容如下:

前端工程化-使用plop生成项目模板文件

dataScreen/index.scss模板样式如下:

前端工程化-使用plop生成项目模板文件

router/index.ts路由内容如下:

前端工程化-使用plop生成项目模板文件

Plofile API

plopfile api是由plop对象公开的方法的集合,大部分工作都是由setGenerator完成的,当然也有很多其他有用的方法 。

主要方法

这些是创建plopfile时通常使用的方法。

MethodParametersReturnsDescription
setGeneratorString, GeneratorConfigPlopGenerator设定一个生成器
setHelperString, Function设定一个辅助方法
setPartialString, String设定一个片段
setActionTypeString, CustomAction注册一个自定义动作类型
setPromptString, InquirerPrompt注册一个自定义的提示器类型
loadArray[String], Object, Object从另一个plopfile或npm模块加载生成器、辅助类或片段

setGenerator

配置对象中prompts和actions属性,是必须的;description属性是可选的;prompts数组会被传递给 inquirer。而 actions 数组是要执行的操作列表。

示例如下:

module.exports = function (plop) {
    // 创建一个生成器
plop.setGenerator('component', {
    description: '新增一个公共组件',//可选;描述,在终端里生成器后面显示的内容
    prompts: [], // 提示,用于捕获用户输入
    actions: []  // 行为,具体执行的内容
    });
};

GeneratorConfig 接口

PropertyTypeDefaultDescription
description[String]简短说明生成器的用途
promptsArray[InquirerQuestion]向用户提的问题
actionsArray[ActionConfig]可执行的动作

setHelper

setHelper主要用于自定义相应一个工具方法,setHelper直接对应于handlebars方法registerHelper。

示例如下:

module.exports = function (plop) {
    plop.setHelper('upperCase', function (text) {
        return text.toUpperCase();
    });
    // or in es6/es2015
    plop.setHelper('upperCase', (txt) => txt.toUpperCase());
};

setPartial

setPartial直接对应于handlebars的方法方法registerPartial。

module.exports = function (plop) { 
    plop.setPartial('myTitlePartial', '<h1>{{titleCase name}}</h1>'); 
    // used in template as {{> myTitlePartial }} 
};

其他方法

其他的方法,如setPartial、setActionType、setPrompt、load等等,具体用法,大家可以参见官网介绍;

Plop Api

plop最基本和最核心的内容:prompts和actions

prompts

提示:prompts数组里的type类型:input, number, confirm, list, rawlist, expand, checkbox, password, editor。当然也是支持inquirer插件的。

actions

actions函数配置属性包含:

name

认识:name用于生成文件或文件夹的名称;

name命名规则如下:

  1. camelCase:将字符串转为驼峰表示法,一般应用于文件夹命名。示例:(changeFormatToThis)
  2. properCase/pascalCase:单词首字母大写表示法,一般应用于组件文件夹命名。示例:(ChangeFormatToThis)
  3. lowerCase :小写表示法 (change format to this)
  4. snakeCase:下划线表示法(change_format_to_this)
  5. dashCase/kebaCase:短划线表示法(change-format-to-this)
  6. dotCase:点语法表示法(change.format.to.this)
  7. pathCase:路径表示法(change/format/to/this)
  8. sentenceCase: 整句首字母大写法(Change format to this)
  9. constantCase: 全部大写下划线连接 (CHANGE_FORMAT_TO_THIS)
  10. titleCase :标题表示法 (Change Format To This)
ActionConfig

属性如下:

提示:在ActionConfig里面,data属性可以是一个返回普通对象的函数;也可以是一个返回Promise对象的函数,但这个Promise对象必须resolve一个对象 。

内置动作

您可以在GeneratorConfig中使用几种类型的内置动作。您可以指定操作的类型(所有路径都基于plopfile的位置)和要使用的模板 。

Add

add动作被用来向你的项目中新增一个文件;

相关属性如下:

PropertyTypeDefaultDescription
pathString指定生成文件的路径。它本身是一个handlebars模板,用户输入的文件名称将作为变量嵌入其中
templateString被用来创建新文件的handlebars模板
templateFileString一个包含模板的文件路径
skipIfExistsBooleanfalse如果要创建的文件已经存在,则跳过(而非报错)
transformFunction一个可选函数,可用于在将文件写入磁盘之前转换模板结果
skipFunction继承自ActionConfig
forceBooleanfalse继承自ActionConfig(如果文件存在则覆盖)
dataObject{}继承自ActionConfig
abortOnFailBooleantrue继承自ActionConfig
AddMany

addMany 动作可以一次创建多个文件。

细节:如果您希望添加的文件名是唯一的,则templateFiles glob位于的路径可以在它们的文件/文件夹名称中使用handlebars语法(例如:{{dashCase name}}.spec.js)。

相关属性如下:

PropertyTypeDefaultDescription
destinationString规定新文件要创建到的目录,它是一个handlebars模板,意味着该属性中定义的目录路径可以是动态的
baseString将文件添加到目标文件夹时,可以排除模板路径的哪些部分
templateFilesGlob匹配要添加的多个模板文件的glob模式
stripExtensions[String]['hbs']应从template中剥离的文件扩展名在添加到目标时文件名
globOptionsObject更改与要添加的模板文件匹配方式的glob选项
verboseBooleantrue打印每个成功添加的文件路径
transformFunction一个可选函数,可用于在将文件写入磁盘之前转换模板结果
skipFunction继承自ActionConfig
skipIfExistsBooleanfalse继承自add(如果文件存在,则跳过)
forceBooleanfalse继承自ActionConfig(如果文件存在则覆盖)
dataObject{}继承自ActionConfig
abortOnFailBooleantrue继承自ActionConfig
Modify

modify修改动作将使用pattern属性进行匹配,从而修改或替换指定路径(path)下的文件。

PropertyTypeDefaultDescription
pathString要修改的文件的路径(是一个handlebars模板)
patternRegExpend‑of‑file一个正则表达式,用来匹配需要被替换的文本
templateString一个handlebars模板,用它取代所匹配的模式。捕获组的值有1、1、12等
templateFileString是一个包含模板的路径
transformFunction一个可选函数,可用于在将文件写入磁盘之前转换模板结果
skipFunction继承自ActionConfig
dataObject{}继承自ActionConfig
abortOnFailBooleantrue继承自ActionConfig
Append

append 追加操作是一个常用功能,它是modify接口的子集。它用于将数据追加到文件中的特定位置。

PropertyTypeDefaultDescription
pathString需要被修改的文件路径(是一个handlebars模板)
patternRegExp, String一个正则表达式,用于匹配附加文本的正则表达式
uniqueBooleantrue是否删除相同的项
separatorStringnew line分割符
templateString用于入口的handlebars模板
templateFileString是一个包含模板的路径
dataObject{}继承自ActionConfig
abortOnFailBooleantrue继承自ActionConfig

动态actions

如果actions需要根据prompts的answer来决定,那么可以使用动态actions;

示例如下:

module.exports = function (plop) {
    plop.setGenerator('test', {
        prompts: [{
            type: 'confirm',
            name: 'wantTacos',
            message: 'Do you want tacos?'
        }],
        actions: function(data) {
            var actions = [];

            if(data.wantTacos) {
                actions.push({
                    type: 'add',
                    path: 'folder/{{dashCase name}}.txt',
                    templateFile: 'templates/tacos.txt'
                });
            } else {
                actions.push({
                    type: 'add',
                    path: 'folder/{{dashCase name}}.txt',
                    templateFile: 'templates/burritos.txt'
                });
            }

            return actions;
        }
    });

源码地址

参考资料

  1. plopjs
  2. Inquirer.js
  3. handlebarsjs
转载自:https://juejin.cn/post/7362833213960208420
评论
请登录