前情

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

思考

对于这种组件,整体基础结构是一样的,我们可不可以有更好的方式一键生成了,就避免了反反复的拷贝删减动作,有一天我在逛博客论坛的时候我发现了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. Go map字典排序

    前言 我们已经知道 Go 语言的字典是一个无序集合,如果你想要对字典进行排序,可以通过分别为字典的键和值创建切片,然后通过对切片进行排序来实现. 按照键进行排序 如果要对字典按照键进行排序,可以这么做 ...

  2. PaddleOCR学习笔记3-通用识别服务

    今天优化了下之前的初步识别服务的python代码和html代码. 采用flask + paddleocr+ bootstrap快速搭建OCR识别服务. 代码结构如下: 模板页面代码文件如下: uplo ...

  3. 业余无线电之配置Orbitron My DDE 自动推送多普勒频率至SDRSharp程序中

    配置Orbitron My DDE 推送多普勒频率至SDR (By:BI8EJM) Start Edit Time 2021/8/16 23:03 要实现的功能:通过本次设置,让Orbitron程序自 ...

  4. Cython二进制逆向系列(三)运算符

    Cython二进制逆向系列(三)运算符 在开始前,先给出本文用到的py源代码 def test1(x, y): # 数学运算符 a = x + y b = x - y c = x * y d = x ...

  5. 【SpringCloud】SpringCloud config分布式配置中心

    SpringCloud config分布式配置中心 概述 分布式系统面临的---配置问题 微服务意味着要将单体应用中的业务拆分成一个个子服务 ,每个服务的粒度相对较小,因此系统中会出现大量的服务.由于 ...

  6. CSS定位的写法

    如上图,商品添加完成后,需要验证商品是否添加成功,通过验证商品列表内是否存在指定名称的商品即可实现验证 浏览器自动获取的xpath=//*[@id="ProductName-divrid53 ...

  7. 【自用】MySQL数据库基本操作

    docker 中下载 mysql docker pull mysql 启动 docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=12 ...

  8. SpringMVC的执行过程

    环境准备 package org.example.springmvclearn; public record Greeting(long id, String content) { } package ...

  9. 解决React Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, ... useEffect cleanup function.

    在写react程序时遇到警告: Warning: Can't perform a React state update on an unmounted component. This is a no- ...

  10. GitLab——重置(reset)和还原(revert)

    Git 命令 reset 和 revert 的区别 - 知乎 (zhihu.com) 总结: git reset --hard 9201d9b19dbf5b4ceaf90f92fd4e4019b685 ...