重点!!记小程序分包异步化的注意点

一、分包异步化

微信小程序在2.11.2之后就已经支持分包异步化了,因此引入了俩个特性,支持 跨分包自定义组件引用 与 跨分包 JS 代码引用,应用这俩个特性我们可以去优化主包体积。

1、自定义组件的使用限制

自定义组件的使用受到其所在包的限制:
  • 如果组件位于主包中,则它可以被主包和所有分包中的页面引用。
  • 如果组件位于某个分包中,则它只能被该分包内的页面引用,无法被其他分包或主包中的页面直接引用,但是可以使用跨分包自定义组件引用。
  • 分包异步原理允许主包异步加载并使用子包的组件,未加载完成的时候,使用占位组件展示,加载完成则会替换为真实的组件
  • 分包没法直接访问另一个分包的资源,除非两个分包都已经加载过了。这个问题可以使用「分包异步化」解决。

2、跨分包自定义组件引用

跨分包自定义组件引用,需要搭配占位组件的配置才可以实现,根据微信小程序官方文档描述如下:
0
配置完成之后,可以在对应的主包页面直接使用该组件(),实际上还是存在部分问题的,主要如下:
(1) 在分包未加载完成之前,该组件为占位组件,如果是view,text等基础组件,则slots的内容会直接显示出来,影响用户体验
(2) 该方案只支持直接在页面使用分包组件,页面内的子组件引入均无效
(3) 该方案在遇到分包比较大的时候容易造成过长的加载时间等待
虽然存在着部分问题,但是我们依然可以将部分业务组件分离到分包中,然后使用跨包组件,从而减少主包的体积,在我这个项目中我对接的腾讯云IM就是通过这种形式,将消息列表对接到tabbar中,否则按照以往的经验这一部分肯定占据主包大量的空间。

3、以下是一个简单的分包配置示例:

subPackages是专门存放分包页面的,一个分包为一项,所以它是集合,root则是当前分包的跟路径
{ 
"pages": ["pages/home/home"], // 主包页面
"subpackages": [
{
"root": "pages-three", // 分包A
"pages": [
{
"path" : "three-render/three-render",
"style" : { "navigationBarTitleText" : "子包pages-three的测试页面" }
}
]
},
{ "root": "packageB", // 分包B
"pages": ["pages/pageB/pageB"]
}
]
} // 主包想要加载分包A里面的three-render页面的组件,修改page.json如下: {
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页",
"usingComponents": { "three-js": "/pages-three/threeJs/threeJs" // 组件的路径 },
"componentPlaceholder": { "three-js": "view" // 未完成时的占位组件 }
}
}
]
}

  

注意点

重点:我们必须在子包的某个页面中,去使用这个组件,否则打包的时候会找不到文件,只要在子包的某个页面,导入并渲染了组件,打包的时候threeJs就会在文件中了
 

4、解决主包页面引入子包页面组件,打包体积大问题

因为uniapp开发的小程序使用的node_modules里面的包永远会打包到主包的vendor.js中,即使是你没有在主包中直接使用。

解决方案

要解决也很简单,把node_modules中的npm包移动到子包的目录中
 
0
然后可以修改npm的导入路径,或者在配置文件中统一设置别名,这样会更方便
// vite.config.js import path from "path"; import { defineConfig } from "vite"; import uni from "@dcloudio/vite-plugin-uni"; export default defineConfig({ plugins: [ uni(), ], resolve: { alias: { "three-platformize": path.resolve(__dirname, "./pages-three/npm/three-platformize"), }, }, });
这样打包three就会进入子包了

二、三方框架无法直接使用分包异步化,微信原生语法无影响。如一定要在第三方框架中使用「异步化」这个特性,可以使用-分包插件异步化

1、requirePlugin

// 使用回调函数风格的调用 
requirePlugin( 'live-player-plugin', (livePlayer) => { console.log(livePlayer.getPluginVersion()); 
 }, 
({ mod, errMsg }) => { 
console.error(`path: ${mod}, ${errMsg}`); 
}, ); 
// 或者使用 Promise 风格的调用
 requirePlugin .async('live-player-plugin') 
.then((livePlayer) => { console.log(livePlayer.getPluginVersion()); }) 
.catch(({ mod, errMsg }) => { console.error(`path: ${mod}, ${errMsg}`); });

 

如上,解决思路:微信官方提供的 requirePlugin,Webpack 不会进行编译,则可以正常访问小程序宿主环境的的 requirePlugin API,从而达成异步加载异步的目的。
2、既然要插件,先去微信官方注册一个插件,这部分可以搜官方文档,主要代码如下:
// xxx-plugin/index.js,插件代码只是加载 SDK,并且导出 import TIM from 'tim-wx-sdk'; module.exports = { TIM, };
3、在分包页面中引入插件。
{ "plugins":
 {  
   "xxx-plugin": { 
    "version": "dev-01055b63731de071ffb850464bd5c7b1",
     "provider": "xxx-plugin appid" } 
  } 
}

 

4、上面的 utils 封装改一下。
// utils/im.js // 
let TIM = null; 
requirePlugin.async('xxx-plugin').then(({ TIM: modTIM }) => {
TIM = modTIM;
})
.catch(({ mod, errMsg }) => {
  console.error(`direct-service-plugin path: ${mod}, ${errMsg}`); 
 }); 
// 暴露出去 
export { TIM }; 
//更新一下更好的写法 
// utils/im.js 
let TIM = {}; 
import { loadPluginPackage } from '@/utils/async-load'; 
const TIMSdk = loadPluginPackage('xxx-plugin');
 TIMSdk.then((mod) => { TIM = mod.TIM; });
 // 导出出去 
export { TIM, TIMSdk };

 

5、加载插件的封装
// @/utils/async-load.ts 
/**@param pluginName 
* @returns Promise<any> 
* 加载插件的方法 
**/  
export async function loadPluginPackage(pluginName: string): Promise<any> { try { 
// @ts-ignore 
const mod = await requirePlugin.async(pluginName); 
return mod; 
} catch ({ mod, errMsg }) { 
console.error( `loadPluginPackage '${pluginName}' errpr path: ${mod}, ${errMsg}`, ); 
return {};
 } 
}

 

6、插件使用的时候
// app.js ;
(async init( ) { 
const { TIM } = await loadPluginPackage('xxx-plugin')
 TIM.login({ userID: 'xxxxx', userSig: 'userSig', }) 
})()

 

这样还是有问题,如果有多个地方都是用这个 JS,每个地方都要写一下加载插件的方法,可以在小程序启动的时候做一次加载就可以,后面所有用到的地方都用同一个 Promise 就行。
7、修改utils/im.js
let TIM = {};
 import { loadPluginPackage } from '@/utils/async-load';
 const TIMSdk = loadPluginPackage('xxx-plugin'); 
TIMSdk.then((mod) => { TIM = mod.TIM; }); 
// 导出出去 
export { TIM, TIMSdk };

 

TIMSdk,是一个默认执行一次的 Promise,加载过一次之后,后续调用 TIMSdk 拿到的都是同一个结果。
使用方式
// app.js 
import { TIM, TIMSdk } from '@/utils/im';
 /** * * IM初始化 */ 
const init = async ( ) => {
 // 要用的时候 await 一下即可 a
wait TIMSdk;
 const tim = TIM.create({ SDKAppID: config.SDKAppID, });
 }; 
init();

 

8、上面写法太繁琐,完美方式,顶部直接、await
// app.js 
const { TIM } = await loadPluginPackage('xxx-plugin');

 

一定要注意基础库的依赖,要用这个方案,必须要把最低基础库限制拉到 2.11.2。

三、控制小程序代码包大小主要几个手段

  1. 静态资源,能走 CDN 的,全部走 CDN。
  2. 能分包的页面或者组件,全部放到分包里面去,主包只留不能拆分的,提升分包加载速度可以使开启分包预下载。(preloadRule)
  3. 如果资源一定要在主包引用且大小不可控,那就使用「分包异步化」或者「分包插件异步化」来处理。「分包异步化」和「分包插件异步化」两者的选择建议:
posted @ 2025-06-03 21:54  ~小晨晨  阅读(455)  评论(0)    收藏  举报