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

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

文章参考:

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

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

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

相关推荐
我要洋人死几秒前
导航栏及下拉菜单的实现
前端·css·css3
川石课堂软件测试3 分钟前
性能测试|docker容器下搭建JMeter+Grafana+Influxdb监控可视化平台
运维·javascript·深度学习·jmeter·docker·容器·grafana
科技探秘人12 分钟前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人12 分钟前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR18 分钟前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香20 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q24985969323 分钟前
前端预览word、excel、ppt
前端·word·excel
小华同学ai28 分钟前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
problc32 分钟前
Flutter中文字体设置指南:打造个性化的应用体验
android·javascript·flutter
Gavin_91537 分钟前
【JavaScript】模块化开发
前端·javascript·vue.js