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

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

文章参考:

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

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

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

相关推荐
ekskef_sef37 分钟前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
sunshine6411 小时前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻1 小时前
Vue(四)
前端·javascript·vue.js
蜜獾云1 小时前
npm淘宝镜像
前端·npm·node.js
dz88i81 小时前
修改npm镜像源
前端·npm·node.js
Jiaberrr2 小时前
解锁 GitBook 的奥秘:从入门到精通之旅
前端·gitbook
程序员_三木2 小时前
Three.js入门-Raycaster鼠标拾取详解与应用
开发语言·javascript·计算机外设·webgl·three.js
兔C2 小时前
微信小程序的轮播图学习报告
学习·微信小程序·小程序
顾平安3 小时前
Promise/A+ 规范 - 中文版本
前端
聚名网3 小时前
域名和服务器是什么?域名和服务器是什么关系?
服务器·前端