uniapp开发小程序:将pages.json中subPackages分拆至分包

前言:为了将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 devnpm 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.jsonsrc/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
相关推荐
丁总学Java18 分钟前
微信小程序,点击bindtap事件后,没有跳转到详情页,有可能是app.json中没有正确配置页面路径
微信小程序·小程序·json
瑶琴AI前端1 小时前
uniapp组件实现省市区三级联动选择
java·前端·uni-app
mosen8682 小时前
Uniapp去除顶部导航栏-小程序、H5、APP适用
vue.js·微信小程序·小程序·uni-app·uniapp
qq22951165022 小时前
微信小程序的汽车维修预约管理系统
微信小程序·小程序·汽车
尚梦9 小时前
uni-app 封装刘海状态栏(适用小程序, h5, 头条小程序)
前端·小程序·uni-app
小飞哥liac12 小时前
微信小程序的组件
微信小程序
stormjun14 小时前
Java基于微信小程序的私家车位共享系统(附源码,文档)
java·微信小程序·共享停车位·私家车共享停车位小程序·停车位共享
尚学教辅学习资料16 小时前
基于SSM+uniapp的营养食谱系统+LW参考示例
java·uni-app·ssm·菜谱
Bessie23416 小时前
微信小程序eval无法使用的替代方案
微信小程序·小程序·uni-app
shenweihong16 小时前
javascript实现md5算法(支持微信小程序),可分多次计算
javascript·算法·微信小程序