共计 8158 个字符,预计需要花费 21 分钟才能阅读完成。
一、前言
京东到家小程序最后只有微信小程序,随着业务的倒退,同样的性能须要反对容器越来越多,包含支付宝小程序、京东小程序、到家 APP、京东 APP 等,然而每个端离开实现要面临研发老本高、不统一等问题。
为了进步研发效率,通过技术选型采纳了 taro3+ 原生混合开发模式,本文次要解说咱们是如何基于 taro 框架,进行多端能力的摸索和性能优化。
二、多端能力的摸索
1. 到家小程序基于 taro3 的架构流程图
框架分层解释
1. 配置层:次要蕴含编译配置、路由配置、分包加载、拓展口子。
2. 视图层:次要实现 App 生命周期初始化、页面初始化、注入宿主事件、解析配置为页面和组件绑定事件和属性。
3. 组件库:是一个独自保护的我的项目,多端组件库包含业务组件和原子组件,可由视图层依据配置动静加载组件。
// 渲染主入口
render() {let { configData, isDefault, isLoading} = this.state;
const pageInfo = {...this.pageInfoValue, ...this._pageInfo}
return (<MyContext.Provider value={pageInfo}>
<View
className={style.bg}
>
{// 动静渲染模板组件
configData &&
configData.map((item, key) => {return this.renderComponent(item, key);
})
}
</View>
{isLoading && <Loading></Loading>}
</MyContext.Provider>
);
}
// 渲染组件 注入下发配置事件和属性
renderComponent(item, key) {const AsyncComponent = BussinesComponent[item.templateName];
if (AsyncComponent) {
return (
<AsyncComponent
key={key}
dataSource={item.data}
{...item.config}
pageEvent={pageEvent}
></AsyncComponent>
);
} else {return null;}
}
4. 逻辑层:包含业务解决逻辑,申请、异样、状态、性能、公共工具类,以及与根底库对接的适配能力。
5. 根底库: 提供根本能力,定位、登录、申请、埋点等根底性能,次要是抹平各端根底性能的差别。
2、根底库
1. 对立接口,分端实现,差别外部抹平
对于根底库咱们采纳分端实现的形式,即对立接口的多端文件。
根底库如何对接在我的项目里,批改 config/index.js,联合 taro 提供的 MultiPlatformPlugin 插件编译。
const baseLib = '@dj-lib/login'
// 减少别名,便于后续根底库调整切换
alias: {'@djmp': path.resolve(__dirname, "..", `./node_modules/${baseLib}/build`),
},
// 批改 webpack 配置,h5 和 mini 都要批改
webpackChain(chain, webpack) {chain.resolve.plugin('MultiPlatformPlugin')
.tap(args => {args[2]["include"] = [`${baseLib}`]
return args
})
}
业务里应用形式
import {goToLogin} from '@djmp/login/index';
goToLogin()
2. 高复用
根底库不应该耦合框架,那么根底库应该如何设计,使其既能满足 taro 我的项目又能满足原生我的项目应用呢?
npm 根底库 在 taro 通过编译后生成为 vendors 文件
npm 根底库 在小程序原生我的项目 npm 构建后 生成 miniprogram\_npm
一样的根底库通过编译后会存在 2 种状态,多占了一份空间呢。
咱们对小程序包体积大小是比拟敏感的,为了节约空间,那么如何让 taro 应用小程序的 miniprogram\_npm 呢?
先简略说一下思路,更改 webpack 的配置项,通过 externals 配置解决公共办法和公共模块的引入,保留这些引入的语句,并将引入形式设置成 commonjs 相对路径的形式,具体代码如下所示。
const config = {
// ...
mini: {
// ...
webpackChain (chain) {
chain.merge({
externals: [(context, request, callback) => {const externalDirs = ['@djmp/login']
const externalDir = externalDirs.find(dir => request.startsWith(dir))
if (process.env.NODE_ENV === 'production' && externalDir) {const res = request.replace(externalDir, `../../../../${externalDir.substr(1)}`)
return callback(null, `commonjs ${res}`)
}
callback()},
],
})
}
// ...
}
// ...
}
3、组件库
想要实现跨端组件,难点有三个
第一:如何在多个技术栈中找到最失当的磨平计划,不同的计划会导致 开发适配的老本不同,而人效晋升才是咱们最终想要实现的目标;
第二:如何在一码多端实现组件之后,确保没有对各个组件的性能产生影响
第三:如何在各我的项目中进行跨端组件的应用
基于以上,在咱们曾经确定的以 Taro 为根底开发框架的前提下,咱们进行了整体跨端组件计划实现的规划设计:
在组件层面,划分为三层:UI 根底组件和业务组件 为最底层;容器组件是中间层,最上层是业务模板组件;咱们首先从 UI 根底组件与业务组件动手,进行计划的最终确认;
调研过程中,UI 组件和业务组件次要从 API、款式、逻辑三个方面去调研跨端的复用率:
通过以上调研得出结论:API 层面仍须要应用各自技术栈进行实际,通过属性统一的形式进行 API 层面的磨平;款式上,根底都应用 Sass 语法,通过 babel 工具在转化过程中生成各端可辨认的款式模式;逻辑上根本是平移,不须要做改变;所以当咱们想做跨端组件时,咱们最大工作量在于:API 的磨平和款式的跨端写法的摸索;
例:图片组件的磨平:
基于以上,跨端组件的复用计划通过调研是可行的,然而接下来,咱们该如何保障转化后的组件可能和原生组件的性能媲美呢?咱们的跨端组件又该如何在各个我的项目中应用呢?
在这个过程中,咱们次要调研比照两种计划:
第一:间接利用 Taro 提供的跨端编辑性能进行转换,转换编译成 RN . 微信小程序 以及 H5;
第二:通过 babel 进行编译,间接转换成 RN 原生代码,微信小程序原生代码,以及 H5 原生代码
比照方向 | 原码大小 | 编译老本 | 生成的组件性能 |
---|---|---|---|
Taro 间接编译 | 大(携带了 Taro 环境) | 中(Taro 间接提供,但须要各端调试) | 与原生雷同 |
通过 babel 本义 | 小(只有以后组件的源码代码) | 中(须要开发 Babel 本义组件) | 与原生雷同 |
通过以上几组比照,咱们最终选用了 babel 本义的形式。在我的项目中应用时,公布到 Npm 服务器上,供各个我的项目进行应用。
计划落地与将来布局:
在确认整体的计划方向之后,咱们进行了我的项目的落地,首先搭建了跨端组件库的运行我的项目:可能反对预览京东小程序、微信小程序以及 H5 的组件生成的页面;以下是整个组件从生成到公布到对应我的项目的全副流程。
目前曾经实现了个 5 种 UI 组件的实现,4 种业务组件;其中优惠券模块曾经落地在到家小程序我的项目中,并曾经积淀了跨端组件的设计规定和计划。将来一年中,会持续跨端组件的实现与落地,从 UI、业务层到简单容器以及简单页面中。
4、工程化构建
1. 构建微信小程序
因为存在多个 taro 我的项目由不同业务负责,须要将 taro 聚合编译后的产物,和微信原生聚合在一起,能力形成残缺的小程序我的项目。
上面是设计的构建流程。
为了使其自动化, 缩小人工操作,在 迪迦公布后盾( 到家自研的小程序公布后盾 ) 创立依赖工作即可,实现整体构建并上传。
其中执行【依赖工作】这个环节会进行,taro 我的项目聚合编译,并将产物合并到原生我的项目。
迪迦公布后盾
2. 构建京东小程序
yarn deploy:jd 版本号 形容
// 集成 CI 上传工具 jd-miniprogram-ci
const {upload, preview} = require('jd-miniprogram-ci')
const path = require('path')
const privateKey = 'xxxxx'
// 要上传的目录 - 正式
const projectPath = path.resolve(__dirname, '../../', `dist/jddist`)
// 要上传的目录 - 本地调试
const projectPathDev = path.resolve(__dirname, '../../', `dist/jddevdist`)
const version = process.argv[2]
const desc = process.argv[3]
// 预览版
preview({
privateKey: privateKey,
projectPath: projectPathDev,
base64: false,
})
// 体验版
upload({
privateKey: privateKey,
projectPath: projectPath,
uv: version,
desc: desc,
base64: false,
})
3. 构建公布 h5
yarn deploy:h5
h5 的利用通常采纳 cdn 资源 +html 入口 这种模式。先公布 cdn 资源进行预热,在公布 html 入口进行上线。
次要进行 3 个操作
1. 编译出 h5dist 产物,即 html+ 动态资源
2. 动态资源,利用集成 @jd/upload-oss-tools 工具上传到 cdn。
3. 触发【行云部署编排】公布 html 文件入口
对于 cdn: 咱们集成了 cdn 上传工具,辅助疾速上线。
// 集成 @jd/upload-oss-tools 上传工具
const UploadOssPlugin = require("@jd/upload-oss-tools");
const accessKey = new Buffer.from('xxx', 'base64').toString()
const secretKey = new Buffer.from('xxx', 'base64').toString()
module.exports = function (localFullPath, folder) {return new Promise((resolve) => {console.log('localFullPath', localFullPath)
console.log('folder', folder)
// 初始化上传利用
let _ploadOssPlugin = new UploadOssPlugin({
localFullPath: localFullPath, // 被上传的本地绝对路径,自行配置
access: accessKey, // http://oss.jd.com/user/glist 生成的 access key
secret: secretKey, // http://oss.jd.com/user/glist 生成的 secret key
site: "storage.jd.local",
cover: true, // 是否笼罩近程空间文件 默认 true
printCdnFile: true, // 是否手动刷新 cdn 文件 默认 false
bucket: "wxconfig", // 空间名字 仅能由小写字母、数字、点号 (.)、中划线(-) 组成
folder: folder, // 空间文件夹名称 非必填(1、默认创立以后文件所在的文件夹,2、屏蔽字段或传 undefined 则依照 localFullPath 的门路一层层创立文件夹)ignoreRegexp: "", // 排除的文件规定, 间接写正则不加双引号,无规则时空字符串。正则字符串,匹配到的文件和文件夹都会疏忽
timeout: "", // 上传申请超时的毫秒数 单位毫秒,默认 30 秒
uploadStart: function (files) { }, // 文件开始上传回调函数,返回文件列表参数
uploadProgress: function (progress) { }, // 文件上传过程回调函数,返回文件上传进度
uploadEnd: (res) =>{console.log('上传实现')
resolve()},
// 文件上传完毕回调函数,返回 {上传文件数组、上传文件的总数,胜利数量,失败数量,未上传数量});
_ploadOssPlugin.upload();})
}
三、性能优化
性能优化是一个亘古不变的话题,总结来说优化方向:包下载阶段、js 注入阶段、申请阶段、渲染阶段。
以下次要介绍在下载阶段如何优化包体积,申请阶段如何进步申请效率。
(一)体积优化
置信应用过 taro3 的同学,都有个同样的领会,就是编译进去的产物过大,主包可能超 2M!
1. 主包是否开启
优化主包的体积大小:optimizeMainPackage。
像上面这样简略配置之后,能够防止主包没有引入的 module 不被提取到 commonChunks
中,该性能会在打包时剖析 module 和 chunk 的依赖关系,筛选出主包没有援用到的 module 把它提取到分包内。
module.exports = {
// ...
mini: {
// ...
optimizeMainPackage: {enable: true,},
},
}
2. 应用压缩插件 terser-webpack-plugin
// 应用压缩插件
webpackChain(chain, webpack) {
chain.merge({
plugin: {
install: {plugin: require('terser-webpack-plugin'),
args: [{
terserOptions: {
compress: true, // 默认应用 terser 压缩
keep_classnames: true, // 不扭转 class 名称
keep_fnames: true // 不扭转函数名称
}
}]
}
}
})
}
3. 把公共文件提取到分包。
mini.addChunkPages:
为某些页面独自指定须要援用的公共文件。
例如在应用小程序分包的时候,为了缩小主包大小,分包的页面心愿引入本人的公共文件,而不心愿间接放在主包内。那么咱们首先能够通过 webpackChain 配置 来独自抽离分包的公共文件,而后通过 mini.addChunkPages
为分包页面配置引入分包的公共文件,其应用形式如下:
mini.addChunkPages
配置为一个函数,承受两个参数
•pages
参数为 Map 类型,用于为页面增加公共文件
•pagesNames
参数为以后利用的所有页面标识列表,能够通过打印的形式进行查看页面的标识
例如,为 pages/index/index
页面增加 eating
和 morning
两个抽离的公共文件:
mini: {
// ...
addChunkPages(pages: Map<string, string[]>, pagesNames: string[]) {pages.set('pages/index/index', ['eating', 'morning'])
},
},
4. 代码剖析
如果以上形式,还达不到咱们想要的成果,那么咱们只能静下心来剖析下 taro 的打包逻辑。
能够执行 npm run dev 模式查看产物里的
`xxx` .LICENSE.txt 文件, 外面列举打包了哪些文件,须要自行剖析去除冗余。
以下以 vendors.LICENSE.txt 为例
•runtime.js
: webpack 运行时入口 , 只有 2k,没有优化空间。
•taro.js
: node\_modules 中 Taro 相干依赖,112k,能够魔改源码,否则没有优化空间。
•vendors.js
: node\_modules 除 Taro 外的公共依赖,查看 vendors.js.LICENSE.txt 文件剖析包含哪些文件
•common.js
: 我的项目中业务代码公共逻辑,查看 common
.js.LICENSE.txt 文件剖析包含哪些文件
•app.js app 生命周期中依赖的文件。查看 app .js.LICENSE.txt文件剖析包含哪些文件
•app.wxss 公共款式文件,看业务需要优化,去除非必要的全局款式。
•base.wxml 取决于应用组件的形式,可优化空间较小。
(二)网络申请优化:
置信大家的业务里有多种类型的申请,业务类、埋点类、行为剖析、监控、其余 sdk 封装的申请。然而在不同的宿主环境有不同的并发限度,比方,微信小程序申请并发限度 10 个,京东等小程序限度为 5 个。
如下图,以微信小程序为例,在申请过多时,业务与埋点类的申请争抢申请资源,造成业务申请排队,导致页面展现滞后,弱网状况甚至造成卡顿。
那么基于以上问题,如何均衡业务申请和非业务申请呢?
这里咱们有 2 个计划:
1. 动静调度计划 https://www.cnblogs.com/rsapaper/p/15047813.html
思路就即将申请分为高优和低优申请,当产生阻塞时,将高优申请放入申请队列,低优进入期待队列。
申请散发器 QueueRequest:对新的申请进行散发。
◦退出期待队列:正在进行的申请数超过设置的 threshold,且申请为低优先级时;
◦退出申请池:申请为高优先级,或并发数未达到 threshold。
期待队列 WaitingQueue:保护须要延时发送的申请期待队列。在申请池闲暇或申请超过最长等待时间时,补发期待申请。
申请池 RequestPool:发送申请并保护所有正在进行的申请的状态。对外裸露正在进行的申请数量,并在有申请实现时告诉期待队列尝试补发。
2. 虚构申请池计划
该思路是将微信的 10 个申请资源,分成 3 个申请池,业务申请:埋点类:其余申请的比例为 6:2:2。比例能够自行调整。
这样各类型申请都在本人的申请池,不存在争抢其余申请池资源,保障了业务不被其余申请阻塞。
实现形式
计划比照
优缺点 | 动静调度(计划一) | 虚构申请池(计划二) |
---|---|---|
拓展性 | 低 | 高 |
老本(开发、测试、保护) | 高 | 低 |
申请效率 | 低 | 高 |
2 个计划都能够实现申请资源的调配,但联合业务理论采纳的是虚构申请计划,经测试在弱网状况下,申请效率能够晋升15%.
四、总结和瞻望
将来肯定是一码多端的方向,所以咱们将来在根底建设上会投入更多的精力,包含框架层降级优化、根底库建设、组件库建设、工程化建设疾速部署多端。
在性能优化上咱们还能够摸索的方向有京东小程序分包预加载、分包异步化、京东容器 flutter 渲染、腾讯 skyLine 渲染引擎等。
在团队沟通合作上会与 Taro 团队、京东小程序容器团队、nut-ui、拼拼等团队进行学习沟通, 也心愿能与大家单干共建。
五、结束语
京东小程序开放平台是京东自研平台,提供丰盛的凋谢能力和底层的引擎反对,目前有开发者工具、转化工具、可视化拖拽等多种开发工具可供外部研发共事应用,晋升开发品质同时疾速实现业务性能的上线。外部已有京东领取、京东读书、京东居家等业务应用京东小程序作为技术框架发展其业务。
如您想深刻理解和体验京东小程序,可返回京东小程序官网(https://mp.jd.com/?entrance=shendeng)查看更多信息。
参考:
https://www.cnblogs.com/rsapaper/p/15047813.html
https://taro-docs.jd.com/docs/next/config-detail#minioptimize…
https://taro-docs.jd.com/docs/next/dynamic-import
https://zhuanlan.zhihu.com/p/396763942
作者:京东批发 邓树海、姜微
起源:京东云开发者社区