前情

开发是个创造型的职业,也是枯燥的职业,因为开发绝大多数都是每天在业务的代码中无法自拨,说到开发工作,就永远都逃不开新建文件的步骤,特别现在组件化开发胜行,每天都是在新建新建组件的道路上一去不返,我们做的最多就是直接拷贝一个旧代码组件,重命下名再删减删减完成新组件的创建

思考

对于这种组件,整体基础结构是一样的,我们可不可以有更好的方式一键生成了,就避免了反反复的拷贝删减动作,有一天我在逛博客论坛的时候我发现了Plop.js,发现它正是解决这种场景的

Plop.js介绍

官网:Consistency Made Simple : PLOP

官网的介绍:Plop is a little tool that saves you time and helps your team build new files with consistency,翻译就是:Plop是一个小工具,可以节省您的时间,并帮助您的团队构建一致的新文件

工作主流程:我用过后对它的工作整体流程理解是这样的,通过plopfile.js定义一个一个生成器,每一个生成器根据你传的配置再调用指定目录(一般是plop-template目录)下的hbs模板文件,再通过模板渲染生成最终符合特定结构的文件

需求描述

最近我手上的项目主要是uni-app项目,我们就以uni-app项目做实验,我每天可能都会重复的工作有新建组件、页面、API接口文件,如下图所示

实战

生成组件

定义组件生成器:

module.exports = function (plop) {
// 导入所需模块
const { exec } = require('child_process');
const path = require('path');
const fs = require('fs'); // 定义打开文件的函数
function openFile(filePath) {
const fullPath = path.resolve(process.cwd(), filePath);
if (fs.existsSync(fullPath)) {
console.log(`\n正在打开文件: ${fullPath}`); // 根据操作系统选择打开方式
const isWin = process.platform === 'win32'; // Windows - 尝试使用cursor打开,如果你是用的vs code,请把cursor换成code即可
exec(`cursor "${fullPath}"`, (error) => {
if (error) {
// 如果VS Code不可用,尝试使用默认程序打开
console.log(`打开文件失败: ${error},文件路径: ${fullPath}`);
}
});
}
return '文件已创建';
} // 新建组件 组件生成器
plop.setGenerator("component", {
description: "新建组件",
prompts: [
{
type: "input",
name: "name",
message: "要新建的组件名:",
},
],
actions: [
{
type: "add",
path: "components/{{pascalCase name}}/{{pascalCase name}}.vue",
templateFile: "plop-templates/Component.vue.hbs",
},
// 实现生成文件后主动打开的功能
function(answers) {
const filePath = `components/${plop.getHelper('pascalCase')(answers.name)}/${plop.getHelper('pascalCase')(answers.name)}.vue`;
return openFile(filePath);
}
],
});
};

这里多做了一个功能,当生成完页面后,使用cursor编辑器主动打开当前生成的文件,其中openFile就是打开当前生成的文件,这个可有可无,加了体验感觉会好一些

模板文件plop-templates/Component.vue.hbs:

<template>
<div @click="handleClick">\{{ msg }}-\{{ msgIn }}</div>
</template> <script setup>
import { ref, onMounted } from 'vue'; defineOptions({
name: "{{camelCase name}}"
}) const props = defineProps({
msg: {
type: String,
default: 'Hello uniapp!'
}
}) const emit = defineEmits(['update:msg']); const msgIn = ref('Hello world!'); const handleClick = () => {
emit('update:msg', props.msg + ' ' + msgIn.value);
} onMounted(() => {
console.log('---- onMounted ----');
})
</script> <style lang="scss"> </style>

演示动图:

生成页面

定义生成页生成器:

module.exports = function (plop) {
// 导入所需模块
const { exec } = require('child_process');
const path = require('path');
const fs = require('fs'); // 定义打开文件的函数
function openFile(filePath) {
const fullPath = path.resolve(process.cwd(), filePath);
if (fs.existsSync(fullPath)) {
console.log(`\n正在打开文件: ${fullPath}`); // 根据操作系统选择打开方式
const isWin = process.platform === 'win32'; // Windows - 尝试使用cursor打开
exec(`cursor "${fullPath}"`, (error) => {
if (error) {
// 如果VS Code不可用,尝试使用默认程序打开
console.log(`打开文件失败: ${error},文件路径: ${fullPath}`);
}
});
}
return '文件已创建';
} // 添加页面到pages.json
function addPageToConfig(answers) {
try {
const pagesConfigPath = path.resolve(process.cwd(), 'pages.json'); if (!fs.existsSync(pagesConfigPath)) {
return '无法找到pages.json';
} // 读取pages.json文件内容
let fileContent = fs.readFileSync(pagesConfigPath, 'utf8'); // 查找pages数组的结束括号位置
const lastBracketIndex = fileContent.lastIndexOf(']');
if (lastBracketIndex === -1) {
return '无法在pages.json中找到pages数组';
} // 检查pages数组是否为空
const pagesArrayContent = fileContent.substring(
fileContent.indexOf('"pages"'),
lastBracketIndex
); // 是否需要添加逗号(如果数组中已有内容)
const needComma = pagesArrayContent.includes('{'); // 新页面的配置信息
const newPageConfig = `${needComma ? ',' : ''}
{
"path": "pages/${plop.getHelper('pascalCase')(answers.name)}/${plop.getHelper('pascalCase')(answers.name)}",
"style": {
"navigationBarTitleText": "${answers.title}"
}
}`; // 将新页面添加到数组末尾
fileContent = fileContent.substring(0, lastBracketIndex) +
newPageConfig +
fileContent.substring(lastBracketIndex); // 保存更新后的文件
fs.writeFileSync(pagesConfigPath, fileContent, 'utf8'); return 'pages.json已更新';
} catch (error) {
console.error('更新pages.json时出错:', error);
return `更新配置失败: ${error.message}`;
}
} // 新建页面
plop.setGenerator("page", {
description: "新建页面",
prompts: [
{
type: "input",
name: "name",
message: "要新建的页面名:",
},
{
type: "input",
name: "title",
message: "要新建的页面标题:",
},
],
actions: [
{
type: "add",
path: "pages/{{pascalCase name}}/{{pascalCase name}}.vue",
templateFile: "plop-templates/page.vue.hbs",
},
addPageToConfig,
function(answers) {
const filePath = `pages/${plop.getHelper('pascalCase')(answers.name)}/${plop.getHelper('pascalCase')(answers.name)}.vue`;
return openFile(filePath);
}
],
});
};

生成页面比生成组件需要多做一个功能,就生成页面后,需要向pages.json中添加路由申明,其中addPageToConfig就实现路由申明的

模板文件plop-templates/page.vue.hbs:

<template>
<div>\{{ msg }}</div>
</template> <script setup>
import { ref, onMounted } from 'vue'; defineOptions({
name: "{{camelCase name}}"
}) const msg = ref('Hello uniapp!'); onMounted(() => {
console.log('---- onMounted ----');
}) </script> <style lang="scss"> </style>

演示动图:

生成接口文件

定义接口文件生成器:

module.exports = function (plop) {
// 导入所需模块
const { exec } = require('child_process');
const path = require('path');
const fs = require('fs'); // 定义打开文件的函数
function openFile(filePath) {
const fullPath = path.resolve(process.cwd(), filePath);
if (fs.existsSync(fullPath)) {
console.log(`\n正在打开文件: ${fullPath}`); // 根据操作系统选择打开方式
const isWin = process.platform === 'win32'; // Windows - 尝试使用cursor打开
exec(`cursor "${fullPath}"`, (error) => {
if (error) {
// 如果VS Code不可用,尝试使用默认程序打开
console.log(`打开文件失败: ${error},文件路径: ${fullPath}`);
}
});
}
return '文件已创建';
} // 新建接口文件
plop.setGenerator("api", {
description: "新建接口文件",
prompts: [
{
type: "input",
name: "name",
message: "要新建的接口文件名:",
},
],
actions: [
{
type: "add",
path: "api/{{pascalCase name}}.js",
templateFile: "plop-templates/api.js.hbs",
},
function(answers) {
const filePath = `api/${plop.getHelper('pascalCase')(answers.name)}.js`;
return openFile(filePath);
}
],
});
};

模板文件plop-templates/api.js.hbs:

import request from "../utils/request.js";

/**
* 获取数据
*/
export const queryList = async (id) => {
return request.request(`/api/test/${id}`, {}, {
method: "GET"
})
};

至此,生成模板文件的需求也就基本完成了,生成api接口文件就不录屏了,跟生成组件基本是一样的流程

代码我上传到gitee仓库,可以clone下来跑跑试试,仓库地址:xiewu/plopjsTest

小结

通过上面几个示例我相信你已经基本了解了Plop.js的大致使用方式,上面示例工程还是可以优化,对于uni-app项目,很大可能需要代码分包的,而上面代码只是实现了在主包中增加组件,这我也尝试做了,你可以把上面代码clone下来后切换到feat-complete分支即可。

这里只是抛砖引玉,对于聪明的你,也一定知道怎么把Plop.js应用到自己的项目中了,也期待你的留言和分享

Plop.js入门好文推荐:

Plop.js:一键生成代码模板,提升开发效率的利器-CSDN博客

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

使用Plop.js高效生成模板文件的更多相关文章

  1. 在js内生成PDF文件并下载的功能实现(不调用后端),以及生成pdf时换行的格式不被渲染,word-break:break-all

    在js内生成PDF文件并下载的功能实现(不调用后端),以及生成pdf时换行的格式不被渲染,word-break:break-all 前天来了个新需求, 有一个授权书的文件要点击下载, 需要在前端生成, ...

  2. python template生成模板文件

    简介 在实际项目中,可能会出现需要批量生成特定格式或者特定内容的文件,因此,使用template文件生成便适用于在文件中大部分格式内容都是一致的,部分内容需要替换的情况. 模板文件 name: $NA ...

  3. JS在生成csv文件时,","逗号问题处理.

    在生成csv文件时,发现一个问题,因为csv文件本身是依靠逗号进行分列的,所以内容中有逗号时也被强制分列了,处理方法很简单,为内容加上双引号(英文格式)就可以了. 如: "11111,222 ...

  4. POI生成EXCEL文件

    POI生成EXCEL文件 一.背景 根据指定格式的JSON文件生成对应的excel文件,需求如下 支持多sheet 支持单元格合并 支持插入图片 支持单元格样式可定制 需要 标题(title),表头( ...

  5. windows&lunix下node.js实现模板化生成word文件

    最近在做了一个小程序!里面有个功能就是根据用户提交的数据,自动生成一份word文档返回给用户.我也是第一次做这功能,大概思路就是先自己弄一份word模板,后台接受小程序发过来的数据,再根据这些数据将相 ...

  6. 根据PDF模板生成PDF文件(基于iTextSharp)

    根据PDF模板生成PDF文件,这里主要借助iTextSharp工具来完成.场景是这样的,假如要做一个电子协议,用过通过在线填写表单数据,然后系统根据用户填写的数据,生成电子档的协议.原理很简单,但是每 ...

  7. FluentData-新型轻量级ORM 利用T4模板 批量生成多文件 实体和业务逻辑 代码

    FluentData,它是一个轻量级框架,关注性能和易用性. 下载地址:FlunenData.Model 利用T4模板,[MultipleOutputHelper.ttinclude]批量生成多文件 ...

  8. 利用html模板生成Word文件(服务器端不需要安装Word)

    利用html模板生成Word文件(服务器端不需要安装Word) 由于管理的原因,不能在服务器上安装Office相关组件,所以只能采用客户端读取Html模板,后台对模板中标记的字段数据替换并返回给客户端 ...

  9. js实现图片旋转、模板文件查看图片大图之记录篇[二]

    一个小小的前端需求送给大家,使用js实现图片旋转,并且点击图片能够实现规定格式的大图. 主要使用的是jQuery的delegate()方法实现图片旋转,该方法主要的功能就是给某个组件绑定一个或一组事件 ...

  10. [js高手之路]Node.js+jade+mongodb+mongoose实现爬虫分离入库与生成静态文件

    接着这篇文章[js高手之路]Node.js+jade抓取博客所有文章生成静态html文件继续,在这篇文章中实现了采集与静态文件的生成,在实际的采集项目中, 应该是先入库再选择性的生成静态文件.那么我选 ...

随机推荐

  1. Selenium KPI接口 屏幕截图

    屏幕截图功能常用的有两种: savescreenshot()及 getscreenshotasfile(). 使用格式 self.driver.save_screenshot('baidu.png') ...

  2. composer 2 升级操作

    update composer composer self-update // or sudo composer self-update 回滚到版本1 composer self-update --r ...

  3. KTransformer实战DeepSeek-R1-1.58bit量化模型

    技术背景 在上一篇文章中,我们介绍过KTransformers大模型高性能加载工具的安装和使用方法.但是当时因为是在一个比较老旧的硬件上面进行测试,其实并没有真正的运行起来.现在补一个在KTransf ...

  4. 记一次QT的QSS多个控件设置同一个样式的问题

    文章目录 Qt样式表的格式问题 问题的引入 qss 选择器 问题所在 Reference Qt样式表的格式问题 问题的引入 最近在进行样式设计的时候,发现了一个问题,具体如下: 我是将所有样式写到.q ...

  5. windows本地认证

    windows本地认证 本地认证概述 本地认证最简单的例子就是我们的电脑上存储着自己的账号密码,无论电脑是否联网,只要能开机,就可以输入账号密码登录到电脑中,工作组就是采用本地认证. 那认证流程是什么 ...

  6. Grafana导入 json 文件的 dashboard 错误 Templating Failed to upgrade legacy queries Datasource xxx not found

    前言 编辑或者修改后的 dashboard 保存为 json 文件,在其他环境导入使用,报错 Failed to upgrade legacy queries Datasource xxxxxxx w ...

  7. Math类、System类--java进阶day05

    1.Math类 Math类里所有方法都被static修饰,说明它是一个工具类,不需要创建对象,直接类名调用 2.Math方法展示 . 3.System类 SYstem方法展示 1.currentTim ...

  8. ro在xe10.3上的安装

    在学习研究RO. RO9.2.101.1295在xe10.3上安装遇到新问题.记录处理的办法: 没有采用执行exe安装的方法.而是采用复制源代码后编译安装. 1.把生成的bpl.dcp安装到默认目录, ...

  9. python批量下载网易云音乐文件到本地

    现在听歌大多数只支持在线听,下载要钱,没网络就白搭了.好吧,用技术手段解决免费.下载.批量等一些列问题 整个脚本的逻辑和流程是,把歌曲地址都存在一个txt中,然后循环每次取一条链接,分析链接对应歌曲的 ...

  10. 最强AI数字人,口型、表情、动作全同步!Kairos下载介绍

    在数字化浪潮汹涌澎湃的今天,视频合成技术如同一颗璀璨的明星,照亮了内容创作的广阔天地 Kairos是一款顶级数字人制作工具,它基于先进的 AI 算法,能够快速克隆出用户的数字分身,并且精准匹配外貌.声 ...