黑客与画家
黑客与画家
  • 发布:2022-02-07 18:43
  • 更新:2024-01-23 16:36
  • 阅读:6790

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,但测试过单个分包配置的场景,项目使用的时候请谨慎。

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

11 关注 分享
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 DCloud_UNI_HRK

要回复文章请先登录注册

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

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

3***@qq.com

回复 3***@qq.com :
自己 回复 自己, 偶尔发现 , 真机 调试时, 切换 为 真机调试1.0 版, 就不报错了,
有点意思
2022-11-08 20:36
3***@qq.com

3***@qq.com

回复 黑客与画家 :
哈喽, 老板, 你 是 上面的方式 实现 异步后, 有没有 使用 过 小程序开发 工具的 真机 调试, 预览 没问题的, 但是 真机调试 会报错...


runtime.js:87 MiniProgramError
TypeError
Error: TypeError
line:1,column:42266,TypeError: undefined is not an object (evaluating 'u.functions') Stack:@https://lib/WAServiceMainContext.js:1:42266
pages/dxd/ccc/zujian-create-component@https://res.servicewechat.com/weapp/debug/wxec20ab3ddcf5d428/1/pages/dxd/app-service.js:1231:125
@https://usr//app-service.js:1321:52
@[native code]
p@https://lib/WAServiceMainContext.js:1:42612
__webpack_require__@[native code]
__webpack_require__@https://usr//app-service.js:1328:49
@https://usr//app-service.js:1282:62
@[native code]
p@https://lib/WAServiceMainContext.js:1:42612
checkDeferredModules@[native code]
checkDeferredModules@https://usr//app-service.js:1287:49
@https://usr//app-service.js:1269:75
@[native code]
p@https://lib/WAServiceMainContext.js:1:42612
webpackJsonpCallback@[native code]
webpackJsonpCallback@https://usr//app-service.js:1270:49
@https://res.servicewechat.com/weapp/debug/wxec20ab3ddcf5d428/1/pages/dxd/app-service.js:1228:75
S@https://lib/WASubContext.js:1:769487
global code@https://res.servicewechat.com/weapp/debug/wxec20ab3ddcf5d428/1/pages/dxd/app-service.js:1237:17
@[native code]
batchLoadJsFiles@https://lib/WAServiceMainContext.js:1:345899
loadJsFiles@https://lib/WAServiceMainContext.js:1:2858145
@https://lib/WAServiceMainContext.js:1:2871502
i@https://lib/WASubContext.js:1:116459
@https://lib/WASubContext.js:1:570934
batchInjectSubPackages@https://lib/WASubContext.js:1:693677
@https://lib/WASubContext.js:1:695994
@https://lib/WASubContext.js:1:693227
@https://lib/WASubContext.js:1:48389
n@https://lib/WASubContext.js:1:24724
@https://lib/WASubContext.js:1:35175
E@https://lib/WASubContext.js:1:34907
@https://lib/WASubContext.js:1:34945
f@https://lib/WASubContext.js:1:122748
@https://lib/WASubContext.js:1:90173
@https://lib/WASubContext.js:1:118217
p@https://lib/WAServiceMainContext.js:1:364809
@https://lib/WAServiceMainContext.js:1:353069
_onNativeTimer@
2022-11-08 18:04
3***@qq.com

3***@qq.com

回复 DDante :
哈喽, 老板, 你有没有 测试过 微信开发工具的 真机 调试, 使用 异步后, 预览没问题,
但是 一真机调试 , 就 报错


runtime.js:87 MiniProgramError
TypeError
Error: TypeError
line:1,column:42266,TypeError: undefined is not an object (evaluating 'u.functions') Stack:@https://lib/WAServiceMainContext.js:1:42266
pages/dxd/ccc/zujian-create-component@https://res.servicewechat.com/weapp/debug/wxec20ab3ddcf5d428/1/pages/dxd/app-service.js:1231:125
@https://usr//app-service.js:1321:52
@[native code]
p@https://lib/WAServiceMainContext.js:1:42612
__webpack_require__@[native code]
__webpack_require__@https://usr//app-service.js:1328:49
@https://usr//app-service.js:1282:62
@[native code]
p@https://lib/WAServiceMainContext.js:1:42612
checkDeferredModules@[native code]
checkDeferredModules@https://usr//app-service.js:1287:49
@https://usr//app-service.js:1269:75
@[native code]
p@https://lib/WAServiceMainContext.js:1:42612
webpackJsonpCallback@[native code]
webpackJsonpCallback@https://usr//app-service.js:1270:49
@https://res.servicewechat.com/weapp/debug/wxec20ab3ddcf5d428/1/pages/dxd/app-service.js:1228:75
S@https://lib/WASubContext.js:1:769487
global code@https://res.servicewechat.com/weapp/debug/wxec20ab3ddcf5d428/1/pages/dxd/app-service.js:1237:17
@[native code]
batchLoadJsFiles@https://lib/WAServiceMainContext.js:1:345899
loadJsFiles@https://lib/WAServiceMainContext.js:1:2858145
@https://lib/WAServiceMainContext.js:1:2871502
i@https://lib/WASubContext.js:1:116459
@https://lib/WASubContext.js:1:570934
batchInjectSubPackages@https://lib/WASubContext.js:1:693677
@https://lib/WASubContext.js:1:695994
@https://lib/WASubContext.js:1:693227
@https://lib/WASubContext.js:1:48389
n@https://lib/WASubContext.js:1:24724
@https://lib/WASubContext.js:1:35175
E@https://lib/WASubContext.js:1:34907
@https://lib/WASubContext.js:1:34945
f@https://lib/WASubContext.js:1:122748
@https://lib/WASubContext.js:1:90173
@https://lib/WASubContext.js:1:118217
p@https://lib/WAServiceMainContext.js:1:364809
@https://lib/WAServiceMainContext.js:1:353069
_onNativeTimer@
global code@
global @ runtime.js:87
runtime.js:87 Component is not found in path "pages/dxd/ccc/zujian" (using by "pages/base/index/index")
2022-11-08 18:03
DDante

DDante

感谢兄弟给了我灵感,后面我会发布一篇心得,现在告诉大家使用分包异步组件的方案
在 pages.json 中组件下的 style 字段中配置要使用的异步组件占位即可, 因为这里面的 style 中的配置最终会合并到小程序组件 .json 中去
示例:
"style": {
"navigationStyle": "custom",
"componentPlaceholder": {
"simple-clinic-card": "view",
"guidance-info": "view",
"quick-guide": "view"
}
}
2022-11-02 17:05