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

通过分包的方式来减少主包的体积相信大家都已经很常见了,是一种很好的手段,但可能会遇到的一个问题就是分包里的组件或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}`)
})

文章参考:

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

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

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

相关推荐
白羊@12 分钟前
鸿蒙案例---生肖抽卡
前端·javascript·华为·harmonyos
橙某人19 分钟前
🍊🍊🍊在网格中进行拖动布局-Javascript、Vue
前端·javascript·vue.js
若川21 分钟前
Taro 4 已发布:11. Taro 是如何解析入口配置 app.config.ts 和页面配置的?
前端·javascript·微信小程序
八了个戒25 分钟前
「数据可视化 D3系列」入门第一章:Hello D3.js
开发语言·前端·javascript·数据可视化·canvas
·薯条大王26 分钟前
Node.js 操作 MySQL 数据库
javascript·数据库·mysql
二川bro35 分钟前
深度解析 Vue 项目 Webpack 分包与合包 一文读懂
前端·vue.js·webpack
getapi36 分钟前
flutter底部导航代码解释
前端·javascript·flutter
nui11140 分钟前
汽配快车道解决chrome backgroud.js(Service Worker) XMLHttpRequest is not defined问题
前端·javascript·chrome
团酱1 小时前
ESLint常见错误
开发语言·javascript·ecmascript
CodeCraft Studio1 小时前
PDF处理控件Aspose.PDF指南:使用 C# 从 PDF 文档中删除页面
前端·pdf·c#