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

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

文章参考:

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

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

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

相关推荐
漂流瓶jz34 分钟前
Webpack如何实现万物皆可import?loader的使用/配置/手写实践
前端·javascript·webpack
ZC跨境爬虫1 小时前
跟着 MDN 学CSS day_41:显式轨道、隐式网格与区域命名放置
前端·javascript·css·ui·交互
修己xj2 小时前
告别手动存图!这款叫 Fatkun 的浏览器插件,简直是素材收集神器
前端
袋鼠云数栈2 小时前
从前端到基础设施,ACOS 如何打通企业全链路可观测
运维·前端·人工智能·数据治理·数据智能
AskHarries2 小时前
系统提示词、开发者指令和用户输入的优先级
java·前端·数据库
Moment3 小时前
长上下文会最终杀死 Rag 吗?
前端·javascript·后端
qcx233 小时前
【系统学AI】25 论文导读 ①:两篇改变 AI 的开山之作——Attention Is All You Need & ReAct
前端·人工智能·react.js·transformer
kyriewen4 小时前
大文件上传最全指南:分片、断点续传、秒传,一篇就够了
前端·javascript·面试
我叫黑大帅5 小时前
解决聊天页内部滚轮改为页面滚动问题
javascript·后端·面试
郑洁文5 小时前
基于Python的Web命令执行漏洞自动化检测系统
前端·python·网络安全·自动化