微信小程序分包异步化实践——解决主包过大问题

通过分包的方式来减少主包的体积相信大家都已经很常见了,是一种很好的手段,但可能会遇到的一个问题就是分包里的组件或js库除了在分包内使用外主包也想使用,这就会产生一个问题,分包的内容主包是无法调用的,那如何才能调用?

这里就引出分包异步化的操作了,也是最近无意中看到的,发现还挺好用的,之前原生小程序用echarts都是分包里分出了1M的空间,主包又想用echarts无论再怎么精简也需要600多kb,而且重复使用确实不够优雅。

那么什么是分包异步化?

在小程序中,不同的分包对应不同的下载单元;因此,除了非独立分包可以依赖主包外,分包之间不能互相使用自定义组件或进行 require。「分包异步化」特性将允许通过一些配置和新的接口,使部分跨分包的内容可以等待下载后异步使用,从而一定程度上解决这个限制。

通过将echarts分包异步化后可以看到我主包少了1M的空间,这900kb的主包已经非常舒服了。要说唯一的缺点就是需要略微等一小会等他把分包的组件加载完成后才会进行渲染,但基本不影响观感。

主要是将echarts这部分省了出来

下面说一下具体的操作:

分包异步化组件

这里以echarts为例,这个是官网下载的微信小程序版的echarts的ec-canvas

这个pages_echarts是一个分包

这个分包开启预加载

接下来是主包中的使用:

在页面或组件中加载封装好的echarts组件,这里需要注意的是在componentPlaceholder这里需要声明一下未加载完成前的替代组件,这里用view去替代

渲染原理

基础库尝试渲染一个组件时,会首先递归检查 usingComponents,收集其将使用到的所有组件的信息;在这个过程中,如果某个被使用到的组件不可用,基础库会先检查其是否有对应的占位组件。如果没有,基础库会中断渲染并抛出错误;如果有,则会标记并在后续渲染流程中使用占位组件替换该不可用的组件进行渲染。不可用的组件会在当前渲染流程结束后尝试准备(下载分包或注入代码等);等到准备过程完成后,再尝试渲染该组件(实际上也是在执行这个流程),并替换掉之前渲染的占位组件。

json 复制代码
{
    "component": true,
    "usingComponents": {
        "t-chart": "/pages_echarts/BaseChart/BaseChart"
    },
    "componentPlaceholder": {
        "t-chart": "view"
    }
}

然后页面中正常使用即可

下面是我封装的BaseChart,仅供参考

BaseChart.wxml

html 复制代码
<view style="height: {{height}};width:100%">
    <ec-canvas
        id="{{chartId}}"
        canvas-id="{{canvasId}}"
        ec="{{ ec }}"
    ></ec-canvas>
</view>

BaseChart.json

json 复制代码
{
    "component": true,
    "usingComponents": {
        "ec-canvas": "/pages_echarts/ec-canvas/ec-canvas"
    }
}

BaseChart.js

js 复制代码
import * as echarts from '@/pages_echarts/ec-canvas/echarts'

Component({
    properties: {
        height: {
            type: String
        },
        option: {
            type: Object
        },
    },
    observers: {
        'option': function (val) {
            setTimeout(() => {
                const chart = this.initChart(val || {})
            }, 0)
        }
    },
    data: {
        chartId: '',
        canvasId: '',
        ec: {
            lazyload: true
        }
    },
    lifetimes: {
        attached () {
            this.setData({
                chartId: `chart_${this.__wxExparserNodeId__}`,
                canvasId: `canvas_${this.__wxExparserNodeId__}`
            })
            this.chartComp = this.selectComponent('#' + this.data.chartId)
        }
    },
    methods: {
        initChart (option) {
            this.chartComp && this.chartComp.init((canvas, width, height, dpr) => {
                const chart = echarts.init(canvas, null, {
                    width: width,
                    height: height,
                    devicePixelRatio: dpr
                })
                if (option.tooltip) {
                    Object.assign(option.tooltip, {
                        confine: true,
                        shadowBlur: 0,
                        shadowColor: '#e7e8e8',
                        shadowOffsetY: 0,
                        shadowOffsetX: 0,
                        borderColor: '#e7e8e8',
                        padding: [
                            5, // 上
                            20, // 右
                            5, // 下
                            20, // 左
                        ]
                    })
                }
                chart.setOption(option)
                this.chart = chart
                return chart
            })
        }
    }
})

分包异步化JS

由于目前还没有这方面的需求,待后续有需求时再更新文章。

下面的是官方的示例,有需要的可以试试

一个分包中的代码引用其它分包的代码时,为了不让下载阻塞代码运行,我们需要异步获取引用的结果。如:

js 复制代码
// subPackageA/index.js
// 使用回调函数风格的调用
require('../subPackageB/utils.js', utils => {
  console.log(utils.whoami) // Wechat MiniProgram
}, ({mod, errMsg}) => {
  console.error(`path: ${mod}, ${errMsg}`)
})
// 或者使用 Promise 风格的调用
require.async('../commonPackage/index.js').then(pkg => {
  pkg.getPackageName() // 'common'
}).catch(({mod, errMsg}) => {
  console.error(`path: ${mod}, ${errMsg}`)
})

在其它分包中的插件也可以通过类似的方法调用:

js 复制代码
// 使用回调函数风格的调用
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}`)
})

文章参考:

微信官方文档------分包异步化

分包异步化------小程序代码体积最佳实践

微信小程序第三方库的分包异步化实践

相关推荐
旧林84324 分钟前
第八章 利用CSS制作导航菜单
前端·css
yngsqq35 分钟前
c#使用高版本8.0步骤
java·前端·c#
尘浮生1 小时前
Java项目实战II基于微信小程序的校运会管理系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea
Myli_ing1 小时前
考研倒计时-配色+1
前端·javascript·考研
余道各努力,千里自同风1 小时前
前端 vue 如何区分开发环境
前端·javascript·vue.js
PandaCave1 小时前
vue工程运行、构建、引用环境参数学习记录
javascript·vue.js·学习
软件小伟1 小时前
Vue3+element-plus 实现中英文切换(Vue-i18n组件的使用)
前端·javascript·vue.js
醉の虾2 小时前
Vue3 使用v-for 渲染列表数据后更新
前端·javascript·vue.js
张小小大智慧2 小时前
TypeScript 的发展与基本语法
前端·javascript·typescript
hummhumm2 小时前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j