黑客与画家
黑客与画家
  • 发布:2022-02-07 18:43
  • 更新:2025-11-25 17:30
  • 阅读:12550

uniapp使用微信小程序分包异步化能力临时方案

分类:uni-app

背景

参考问题:uniapp开发微信小程序如何使用分包异步化特性,目前(2022/02/07)uniapp中的pages.json配置不支持分包异步化的特性(按照微信官方文档配置,构建后并不会在app.json文件生成对应的配置,猜测是因为分包异步化中的pages为空,构建代码过滤了,有空查看构建源码确认一下)。

这里应该由uniapp官方支持一下这个功能,目前项目需要,先用下面的临时方案

解决方案

思路:在uniapp构建完成后,添加自己的构建脚本,做以下的事情

  1. 读取pages.json
  2. 判断pages.json中是否有配置分包异步化
  3. 把分包异步化相关配置写入app.json
  4. 寻找用到分包组件的地方(小程序的页面 or 组件json配置文件),注入组件占位(不注入的话小程序会报错导致分包内组件无法使用)

构建脚本源码

/* eslint-disable @typescript-eslint/no-require-imports */  
const fs = require('fs');  
const path = require('path');  

console.log('开始处理异步化分包...');  

// 读取pages.json  
const pagesConfig = (() => {  
  const configPath = path.resolve(__dirname, '../../../pages.json'); // @NOTE 这里要根据脚本执行的路径改一下  
  const pages = fs.readFileSync(configPath, 'utf8');  
  // @NOTE 移除注释  
  let pagesJson = pages.replace(/\/\*.*\*\//g, '');  
  pagesJson = pagesJson.replace(/\/\/.*/g, '');  
  return JSON.parse(pagesJson);  
})();  

// 读取page.json中的异步分包(没有配置pages)  
const asyncPackages = (pagesConfig.subPackages || []).filter(package => !package.pages || package.pages.length === 0);  
// console.log(pagesConfig, asyncPackages);  

// 写入app.json  
const distPath = path.resolve(__dirname, '../../../../dist/build/mp-weixin'); // @NOTE 这里要根据脚本执行的路径改一下  
const appJsonPath = path.resolve(distPath, 'app.json');  
const appJson = JSON.parse(fs.readFileSync(appJsonPath, 'utf8'));  
if (!appJson.subPackages) {  
  appJson.subPackages = [];  
}  
asyncPackages.forEach((package) => {  
  const hasInject = appJson.subPackages.find(pack => pack.root === package.root);  
  if (hasInject) {  
    return;  
  }  
  appJson.subPackages.push({  
    root: package.root,  
    pages: [],  
  });  
});  
fs.writeFileSync(appJsonPath, JSON.stringify(appJson));  

// 寻找用到分包组件的地方,注入组件占位(不注入的话小程序会报错导致分包内组件无法使用)  
const ignorePaths = [];  
ignorePaths.push(appJsonPath); // 过滤app.json  
asyncPackages.forEach((package) => {  
  ignorePaths.push(path.join(distPath, package.root)); // 过滤分包的内容  
});  
const injectPlaceholder = (filepath) => {  
  // 判断是否用到了分包的组件  
  const jsonConfig = require(filepath);  
  if (!jsonConfig.usingComponents) {  
    return;  
  }  
  const subPackageComponents = [];  
  // @TODO 可以考虑使用map来加快查找速度  
  Object.keys(jsonConfig.usingComponents).forEach((componentName) => {  
    const componentPath = jsonConfig.usingComponents[componentName];  
    const targetSubPackage = asyncPackages.find(package => componentPath.startsWith(`/${package.root}`));  
    if (targetSubPackage) {  
      // 防止重复添加  
      // eslint-disable-next-line @typescript-eslint/prefer-optional-chain  
      if (jsonConfig.componentPlaceholder && jsonConfig.componentPlaceholder[componentName]) {  
        return;  
      }  
      subPackageComponents.push(componentName);  
    }  
  });  
  if (subPackageComponents.length === 0) {  
    return;  
  }  
  console.log('开始处理: ', filepath);  
  if (!jsonConfig.componentPlaceholder) {  
    jsonConfig.componentPlaceholder = {};  
  }  
  subPackageComponents.forEach((name) => {  
    jsonConfig.componentPlaceholder[name] = 'view'; // 占位符全用view组件  
  });  
  fs.writeFileSync(filepath, JSON.stringify(jsonConfig));  
  console.log('处理完成: ', filepath);  
};  
findJSON(distPath, ignorePaths, injectPlaceholder);  
console.log('异步化分包处理完成');  

function findJSON(folder, ignorePaths, cb) {  
  fs.readdirSync(folder).forEach((filename) => {  
    const filepath = path.join(folder, filename);  
    const isIgnore = ignorePaths.some(ignorePath => filepath.startsWith(ignorePath));  
    if (isIgnore) {  
      return;  
    }  
    const stat = fs.statSync(filepath);  
    if (filename.endsWith('.json')) {  
      cb(filepath);  
      return;  
    }  
    if (stat.isDirectory()) {  
      findJSON(filepath, ignorePaths, cb);  
    }  
  });  
}  

最后

目前用这个方式解决了分包异步化中使用分包内的组件问题,至于分包内的js使用,大家可以验证一下,我暂时没有这个场景,所以没有验证。

上面的代码编写没有review,但测试过单个分包配置的场景,项目使用的时候请谨慎。

最后还是希望官方大佬支持一下分包异步化这个特性。

12 关注 分享
L***@163.com 2***@qq.com 1***@qq.com 1***@qq.com dashuai 1***@qq.com z***@163.com DDante 1***@qq.com 1***@qq.com HRK_01 3***@qq.com

要回复文章请先登录注册

3***@qq.com

3***@qq.com

根据这个帖子思路将部分自定义组件设置成了异步化分包,之前都是可以的,但在微信小程序基础库版本 3.7.12至最新的几个版本,异步化分包里的父子组件无法正常通讯了,例如子组件 inject 无法正常获取父组件的变量了,有人遇到过这个问题吗?
2025-11-25 17:30
DCloud_UNI_JBB

DCloud_UNI_JBB

我写了一个插件,支持针对具体的 vue 文件添加 `componentPlaceholder`,细节详见这个 [帖子](https://ask.dcloud.net.cn/article/42114)
2025-09-26 14:55
用户2737541

用户2737541

回复 1***@qq.com :
同遇到这个问题, 想问下你们解决了没有
2025-02-12 15:56
1***@qq.com

1***@qq.com

请教个问题 如果这么做 主包引入分包的组件 就不会在构建的时候 被打进来 从而增加主包的体积
2024-01-23 16:36
1***@qq.com

1***@qq.com

要在pages.json中同时配置
{
"path": "pages/buy",
"style": {
"usingComponents": {
"form": "../../packageA/components/form"
},
"componentPlaceholder":{
"form": "view"
}
}

},
2023-11-23 19:59
j***@163.com

j***@163.com

回复 DDante :
谢谢,亲测在style中加可用。
2023-09-15 12:28
1***@qq.com

1***@qq.com

你们这个方法很好,如果直接在pags.json里一个一个加,那么uni-app会自动分包组件当成微信组件,从而影响事件的回调结果
2023-05-09 15:45
1***@qq.com

1***@qq.com

是不是可以精进下,固定分析每个包对于依赖的引用,按需注入异步化分包路径和占位符信息?
2023-05-09 15:43
DDante

DDante

回复 骑猪o0o找牛 :
没有报错啊,我使用的我提供的那个 hack 的方法去做了,项目生产环境也没有问题啊,见 https://ask.dcloud.net.cn/article/40142?notification_id-1187693
2023-01-05 16:34
骑猪o0o找牛

骑猪o0o找牛

回复 骑猪o0o找牛 :
自己 回复 自己, 偶尔发现 , 真机 调试时, 切换 为 真机调试1.0 版, 就不报错了,
有点意思
2022-11-08 20:36