前言:为了将src/pages.json中的subPackages缩小,避免pages.json文件数据过长,我做了一些工作,但最后感觉稍微有点鸡肋,于是记录下来,给有需要的小伙伴使用。
先讲讲基于vue2创建的uniapp项目,我的项目目录树如下:
css
项目文件夹
├── mergePages.js # 用来处理分包pages.json合并的文件
├── package.json
├── src/
| ├── App.vue
| ├── components/
| ├── main.js
| ├── pages/ # 主包
| | ├── home/
| ├── pages-a/ # 分包a
| | ├── pages/
| | └── pages.json
| ├── pages-b/ # 分包b
| | ├── pages/
| | └── pages.json
| ├── pages.json
├── vue.config.js
众所周知,只有pages.json
中的subPackages
字段可以用来写分包的路径,如:
json
{
...,
"subPackages": [
{
"root": "pages-a",
"pages": [
{
"path": "pages/todo",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
]
},
{
"root": "pages-b",
"pages": [
{
"path": "pages/todo",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
]
}
],
...
}
但随着页面越来越多,每个人都必须看着这一大串JSON进行编辑,另外也很容易造成git冲突,不得不去解决代码合并问题,因此我产生了个想法,利用node把所有分包的路径全部拆解到各自的分包。
在这里请回去看上面的目录树,会发现分包a与分包b都有各自的pages.json,我把它写在了分包的根目录中。
接着,贴上我写好的代码mergePages.js
:
js
const fs = require("fs");
const path = require("path");
const json5 = require("json5");
let jsonList = [];
// 对page.json中的subPackages进行清空
clearJsonList("./src/pages.json", "subPackages");
// 检测json中是否有注释,有的话删除
function removeCommentsFromJson5File(file) {
const fileContent = fs.readFileSync(file, "utf-8");
const jsonData = json5.parse(fileContent);
const jsonString = JSON.stringify(jsonData, null, 2);
fs.writeFileSync(file, jsonString);
return fs.readFileSync(file, "utf-8");
}
// 拷贝并删除文件注释
function copyToNoCommentFile(sourcePath, destinationPath) {
// 读取源文件的内容
const content = fs.readFileSync(sourcePath, "utf-8");
// 将内容写入目标文件
fs.writeFileSync(destinationPath, content);
return removeCommentsFromJson5File(destinationPath);
}
// 清空总json文件的列表
function clearJsonList(baseJSONFile, keyName) {
const data1 = JSON.parse(fs.readFileSync(baseJSONFile, "utf-8"));
data1[keyName] = [];
fs.writeFileSync(baseJSONFile, JSON.stringify(data1, null, 2));
}
// 获取父级文件夹名字
function getParentFolderName(filePath) {
const parentFolder = path.dirname(filePath);
const folderName = path.basename(parentFolder);
return folderName;
}
// 获取指定文件夹路径中的json
function findPagesJsonFiles(rootDir) {
// 递归遍历目录
function traverseDirectory(currentDir) {
const files = fs.readdirSync(currentDir);
files.forEach((file) => {
const fullPath = path.join(currentDir, file);
const stats = fs.statSync(fullPath);
if (stats.isDirectory()) {
if (!fullPath.includes("pages-")) return; // 必须以pages-开头的文件夹才可以进入递归才可以
// 如果是文件夹,则继续递归遍历
traverseDirectory(fullPath);
} else if (stats.isFile() && file === "pages.json" && getParentFolderName(fullPath).includes("pages-")) {
// 如果是文件且文件名为 'pages.json',同时父级文件夹包含pages-字符串
jsonList.push(fullPath);
}
});
}
traverseDirectory(rootDir);
}
// 将所有json文件合并到总json文件的指定位置
function mergeJsonFiles(baseJSONFile, keyName, fileContent) {
const data1 = JSON.parse(fs.readFileSync(baseJSONFile, "utf-8"));
// 将fileContent中的data字段的值放到baseJSONFile的subPackages字段中
let obj = JSON.parse(fileContent);
data1[keyName].push(obj);
// 保存合并后的数据到baseJSONFile
fs.writeFileSync(baseJSONFile, JSON.stringify(data1, null, 2));
}
findPagesJsonFiles(__dirname + "/src");
for (var i = 0; i < jsonList.length; i++) {
// 获取拷贝并已去除注释的json文件
let jsonFileNoComment = copyToNoCommentFile(jsonList[i], jsonList[i].replace(/\./g, "1."));
mergeJsonFiles(__dirname + "/src/pages.json", "subPackages", jsonFileNoComment);
}
// 针对src/pages.json的局部更新(bFilePath传入总pages.json,aDatas表示修改的文件内容)
function updateBFileWithAData(bFilePath, aDatas) {
let aData = JSON.parse(aDatas)
try {
// 读取b文件内容
const bContent = fs.readFileSync(bFilePath, "utf8");
const bData = JSON.parse(bContent);
// 找到b文件中与aData的root相同的项,更新其内容
bData.subPackages.forEach((subPackage) => {
if (subPackage.root === aData.root) {
subPackage.pages = aData.pages;
}
});
// 写入更新后的内容到b文件
const updatedBContent = JSON.stringify(bData, null, 2);
fs.writeFileSync(bFilePath, updatedBContent, "utf8");
} catch (error) {
console.error(`Error updating file: ${error.message}`);
}
}
// 删除所有pages1.json文件的方法
function deletePages1JsonFiles(directory) {
// 读取目录中的文件列表
const files = fs.readdirSync(directory);
// 遍历文件列表
files.forEach((file) => {
const filePath = path.join(directory, file);
// 获取文件的状态信息
const stats = fs.statSync(filePath);
if (stats.isDirectory()) {
// 如果是子目录,递归调用删除函数
deletePages1JsonFiles(filePath);
} else if (stats.isFile() && file === "pages1.json") {
// 如果是文件且文件名为 'pages1.json',则删除文件
fs.unlinkSync(filePath);
}
});
}
// 导出这几个方法,提供给vue.config.js使用
module.exports = {
deletePages1JsonFiles,
clearJsonList,
getParentFolderName,
copyToNoCommentFile,
updateBFileWithAData
};
然后,在vue.config.js
中,需要做两件事情,一是监听项目启动,二是监听项目被ctrl+c
打断:
插播一下,这里需要先安装chokidar插件:npm i chokidar -D
js
const { deletePages1JsonFiles, clearJsonList, updateBFileWithAData, getParentFolderName, copyToNoCommentFile } = require("./mergePages");
const chokidar = require("chokidar");
const path = require("path");
const srcDirectory = path.resolve(__dirname, "src");
module.exports = {
transpileDependencies: ["uview-ui", "@dcloudio/uni-ui"],
lintOnSave: false,
configureWebpack: () => {
// 在这里执行 chokidar 相关的监听逻辑
const watcher = chokidar.watch(srcDirectory, {
persistent: true,
});
watcher
.on('add', fullpath => console.log(`File ${fullpath} has been added`))
.on('change', fullpath => {
// 执行你的逻辑...
if(!fullpath.includes('pages.json')) return;
// 如果修改的文件是pages.json,判断父级文件夹是否为pages-开头
if(!getParentFolderName(fullpath).startsWith("pages-")) return;
// 将内容拷贝到pages1.json并删除注释,然后获取到最新的json
let jsonFileNoComment = copyToNoCommentFile(fullpath, fullpath.replace(/pages\./g, 'pages1.'));
updateBFileWithAData(__dirname + "/src/pages.json", jsonFileNoComment);
})
.on('unlink', fullpath => console.log(`File ${fullpath} has been removed`));
return {
plugins: [
{
apply(compiler) {
// 在监听到 SIGINT 信号时退出进程
process.on("SIGINT", () => {
console.log("收到项目进程终止信号...");
// 删除所有pages1.json文件
deletePages1JsonFiles(__dirname + "/src");
// 对page.json中的subPackages进行清空
clearJsonList(__dirname + "/src/pages.json", "subPackages");
process.exit();
});
},
},
],
};
},
};
至此,绝大部分事情我们已经ok了,然后找到package.json
写脚本:
json
{
"scripts": {
"merge": "node mergePages.js",
"dev": "npm run merge && npm run dev:mp-weixin",
"build": "npm run merge && npm run build:mp-weixin",
"dev:mp-weixin": "cross-env UNI_OUTPUT_DIR=dist/dev/MP-WEIXIN-DEV NODE_ENV=development UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch --mode dev",
"build:mp-weixin": "cross-env UNI_OUTPUT_DIR=dist/build/mp-weixin-build NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build --mode dev",
}
}
当运行 npm run dev
或 npm run build
之前,都会先运行 npm run merge
,此时 node 会运行这份mergePages.js
文件,并且覆盖 src/pages.json
, 大致效果如:
json
{
...,
"subPackages": [
{
"root": "pages-a",
"pages": [
{
"path": "pages/todo",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
]
},
{
"root": "pages-b",
"pages": [
{
"path": "pages/todo",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
]
}
],
...
}
而当你修改分包内任何一份 pages.json
后,vue.config.js
中的监听器会监听到文件的变化,从而生成一份pages1.json
(目的是为了删除pages.json中的注释,在项目停止运行后会删除),然后找到 src/pages.json
中对应的分包,进行内容覆盖,从而实现项目热更新。
这里特别强调: src/pages.json
作为被覆盖的目标文件,不允许写注释。 虽然json文件本身就不允许写注释,但由于uniapp支持,所以这里需要强调一下。
最后,你还需要给项目的 .gitignore
文件添加一个禁止提交 pages1.json
与 src/pages.json
的代码,避免其他开发人员在项目运行的状态下,误将这两份文件提交:
git
pages1.json
src/pages.json
到此就完成啦!
======================================================
彩蛋:
如果想要实现文章开头的"目录树",可以全局安装:
shell
npm i tree-cli -g
然后校验是否安装成功:
shell
treee --version
最后直接在项目根目录下运行这个指令:
shell
# 最深查询20层级文件,忽略node_modules与.git,输出到tree.txt文件
treee -l 20 -f --ignore 'node_modules/' '.git/' -o tree.txt