在得物的小程序生态实践

2024-06-18 13:36
文章标签 实践 程序 生态 得物

本文主要是介绍在得物的小程序生态实践,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、前言

提起微信小程序,相信所有人都不陌生,下面这个典型使用场景你一定经历过:

餐馆落座——微信扫桌角小程序码——使用微信小程序点餐🍔

微信小程序(下文简称:小程序)作为一种在微信平台内运行的应用程序,用户无需前往应用商店下载安装包即可使用,可以在微信内被便捷地获取和传播,2017年一经推出便迅速成为热门技术关键词,得物也随即发布了得物App小程序,欢迎扫码体验:

七年时间过去了,小程序的周边配套已经十分成熟,微信官方对小程序生态进行了很多迭代——微信开发者工具从难用到可用、小程序API经历了多轮简化和拓展、数据分析能力逐渐完善,同时开源社区也贡献了非常多的开发框架、实践记录等。

得物内部也形成了自己的小程序开发生态,本文主要介绍一些得物App小程序版本迭代中形成的开发指南、实践经验等,给小程序新手开发者一些实践经验,如果你已经是个小程序开发高手了,那么欢迎交流指正我们做得好的/不好的部分。

事实上,“小程序”这种技术架构并非微信首创,但无疑微信小程序是目前生态建设最完整、用户量最大的小程序生态。

二、小程序运行机制

先简单介绍一下小程序的运行机制和架构设计。

小程序的架构设计初衷是“快”,同时又需要对展示内容做严格的安全管控,所以采用了Webview结合Native的Hybrid技术,设计了双线程模型架构:

渲染层:小程序界面由Webview渲染,一部分较复杂组件用客户端原生渲染,以提供更好的性能。

逻辑层:一个JavaScript沙箱环境(利用原生客户端系统的JavaScript的解释引擎创建),只执行有关小程序业务逻辑的代码,不能访问任何浏览器相关接口,以避免恶意代码运行:在运行时访问DOM树获取敏感数据、跳转到其他在线网页等。

上述渲染层和逻辑层只是上层的抽象概念,在不同运行环境下有各自技术实现,如下表:

小程序本质上是Web和Native的结合——成熟的Web渲染技术负责界面渲染、Native提供客户端原生API补充Web的短板;由于Web基于单线程模型且开放灵活,因此微信隔离了渲染层与逻辑层,以此作为对内容安全管控的有效控制手段。

目前小程序在渲染层上已经有了基于"Skyline渲染线程"的新型架构,减少了双线程架构之间的通信开销和内存占用,理论上拥有更好的性能,需要单独开启,可自行查阅相关文档。

三、开发阶段

从小程序的运行机制可以得知:小程序在开发、发布、运行等每个环节都依赖于微信自身的生态而非传统Web,所以有很多特有的设计。先介绍一些基础概念和知识:

用户端运行的小程序资源文件需要先上传到微信侧,然后才能推送到用户端,微信限制了资源体积大小:

  • 整个小程序所有分包大小不超过20M(开通虚拟支付后的小游戏不超过30M);
  • 单个分包/主包大小不能超过2M。

主包:小程序默认启动页面/TabBar页面的构建产物,以及一些所有分包都需用到公共资源/JS脚本;

分包:为了提高初始化加载速度,开发者可以划分以页面为维度的分包,分包只包含一些子级页面的代码。

跨平台框架选型

如果你的项目确实只需要运行在微信小程序平台,选用微信小程序原生开发方式或许是一个更好的选择——能随时接入微信小程序的最新特性和API,缺点就是不够灵活,如果日后还想要在别的平台开展相同的业务,需要额外开发工作。

更普遍的情况是,大多数开发者都选用跨端框架去开发小程序,毕竟程序员都喜欢 Write once, run anywhere,所以支持小程序端的跨平台框架在小程序刚推出的前几年如雨后春笋般层出不穷。

当然,截止到2024这个时间节点,其中很多框架都已经停止维护(比如remax、mpvue、wepy...),目前比较推荐的跨平台框架有:uni-app、Taro,两者异同比较:

一句话总结:喜欢写Vue的话用uni-app,喜欢写React用Taro,这两者在小程序平台上的表现来说都没有绝对的优势和劣势,社区活跃度都很高,时至今日都在持续更新维护中。

目前在得物内部,uni-app和Taro都有使用。

包体积优化

主包/分包配置

从前文中可以得知,微信限制了单个分包/主包大小不能超过2M,因此需要规划合理的目录,以uni-app的开发目录做示例:

├── ...
├── bin // 构建命令
├── loaders
├── src
│   ├── assets // 静态资源
│   ├── components // 通用组件
│   ├── pages // 主包目录
│   ├── product // product分包目录
│   ├── order // order分包目录
│   ├── ...
│   ├── sdk // 外部SDK
│   ├── store // 全局状态
│   ├── style // 样式文件
│   ├── utils // 通用方法
│   └── wxcomponents // 小程序原生组件
├── ...

小程序根目录下的app.json文件用来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多tab等。(选用不同的跨平台框架会有不同的配置方式和语法,但是殊途同归,编译到小程序平台的配置文件都会遵从小程序规范,下文将用小程序原生语法做说明。)

需要额外注意的是,考虑到小程序有主包和分包的设计,将关联性较强的页面放在同一目录里可以更合理地配置分包,示例如下

// app.json
{// 主包页面"pages": ["pages/index/index","pages/xxxxx/index",],// 分包配置页面"subPackages": [{"root": "product","pages": ["xxxxx/index",]},],"window": {// 全局相关配置},"tabBar": {// tabBar相关配置}
}

即便是合理地配置了分包且做好了目录规划,也依然可能超过2M的体积限制,这个时候我们的优化手段跟Web项目基本相同,这里不再赘述,主要手段:静态资源从cdn加载、代码压缩、依赖优化分析等。

分仓开发

小程序的构建发布机制决定了所有页面都要一起打包、上传,在开发阶段也是如此。

我们曾经真实经历的开发流程:

  1. 打开IDE,打开项目代码;
  2. 运行微信平台构建命令:npm run mp-weixin;
  3. 运行字节平台构建命令:npm run mp-bytedance;
  4. 运行QQ平台构建命令:npm run mp-qq;
  5. 运行Web构建命令:npm run h5;
  6. 打开微信开发者工具、字节开发者工具、QQ开发者工具、Chrome浏览器,在不同工具中预览页面。

上古时期的噩梦

我们的小程序业务代码已经非常庞大,面对这样复杂的多线程开发流程,开发阶段的编译构建给研发电脑极大的内存压力,卡顿属实家常便饭。

为了能更高效地进行开发工作,我们利用Webpack的虚拟模块能力,结合私有npm包管理,将业务代码收敛进了各业务域的仓库内,做到了:

将一个完整的小程序项目,分割成多个能独立进行开发、部署的子应用(基于独立git仓库),同时又可以将所有子应用作为一个完整小程序应用打包发布。

时至今日,分仓开发在得物成为了“过去式”——由于后续的一波研发电脑大升级(唯快不破M2 Pro+力大砖飞32GB RAM),同时得物在QQ平台&字节平台的小程序也停止了迭代。只运行Web构建和小程序构建,并不会卡顿影响开发效率,所以目前已经不再需要分仓开发模式。

限于本文主题和篇幅,这里我们不再过多赘述分仓开发相关实现,有兴趣的话记得关注我们,后续可以整理一波技术文档以供方案参考~

提效工具

在Web中切换API环境有天然的域名隔离,可以直接访问不同域名下部署的站点。而在小程序中,像原生App一样,页面路径在用户端是无感且不能修改的,而且小程序没有安装包的概念,同一时间只存在一个体验版。所以为了更方便去调试和测试,可以做一些提效小工具。

API环境切换功能

在TabBar页面可以开发一个环境切换功能,例如:

  <view v-if="!IS_PRODUCTION" class="changeServiceEnv"><view class="changeTitle">环境切换</view><radio-group @change="radioChange"><radiov-for="item in ENV_Array":key="item":value="item":checked="item === SERVICE_ENV"class="radio-info">{{ item }}</radio></radio-group></view>
radioChange(e) {let { value } = e.detailthis.$store.commit('SET_SERVICE_ENV', value)uni.showToast({ title: `当前环境是${this.SERVICE_ENV}` })
}

这样就可以做到在单一体验版中自由切换API环境,验证不同环境下的数据展现和业务逻辑了。

DEV面板

在测试场景下,尝试访问路径较深的页面会比较耗时繁琐,和很多原生App的测试包做法一样,可以开发一个全局的DEV面板。

但是受限于小程序的架构设计:小程序每跳转到一个页面,实际上都是打开一个新的Webview,在这种设计架构下,虽然可以注册全局组件,但是如果想要在每个页面都渲染出全局组件,还是需要在每个页面都写一遍全局组件的代码,这样并不cool,我们探索出来的方案:

在非生产环境的Webpack配置中使用注入全局组件的loader:

  • 分析小程序页面配置,计算并得到路由表,这一步具体怎么实现跟选用的框架相关;
  • loader的上下文环境this.resourcePath指向真实的文件路径,将它和上一步拿到路由表进行匹配,如果未命中,则不做任何操作;
  • 分析命中路由表的文件,从中找到模版代码,紧随第一个标签,在其后方插入全局组件代码:
    • 这一步可以用正则,也可以用AST;
    • 对于我们的项目来说,在.vue文件的<template/>标签中查找第一个标签用正则很方便,所以我们直接正则替换。

关键代码:

// 全局loader主要代码
function modifyTemplateString(inputString, customTag) {// 查找 <template> 标签内容let templateStartif (inputString.indexOf('<template>') > -1) {templateStart = inputString.indexOf('<template>') + '<template>'.length}const templateEnd = inputString.indexOf('</template>')const templateContent = inputString.substring(templateStart, templateEnd).trim()let modifiedTemplateContent = ''// 在 <template> 第一个元素开头插入自定义标签modifiedTemplateContent = templateContent.replace(/(^\s*<\w+(?:-\w+)*\b(?:\s+(?:"[^"]*"|'[^']*'|[^>"])+)?\s*>)/, `$1\n${customTag}`)// 替换原始字符串中的 <template> 和 <script> 内容return inputString.replace(templateContent, () => modifiedTemplateContent)
}module.exports = function loader(source) {const relPath = path.relative(path.join(__dirname, '../', './src'), this.resourcePath)// <devPanel>是已经在全局中注册过的全局组件return modifyTemplateString(source, '<devPanel></devPanel>')
}

这样就能自动在所有页面渲染出全局DEV按钮组件了,可以更加方便地进行各种快捷操作,实现效果如下

自动化发布

小程序开发完成之后,微信开发者工具提供了上传入口,构建产物上传之后,需要到小程序后台再设置为体验版,如下图所示:

每个开发者都这样操作会造成后台系统上有很多的开发者版本,而且不同系统环境下的构建产物可能不一样,这样的工作流低效且不稳定,所以通过专门的机器执行构建,并将构建产物上传到微信服务器是更可靠的选择。

目前我们的发布工作流如下

1. 开发人员将代码合入指定分支后,会触发发布平台的持续部署任务,打包机拉取代码后执行命令:

// 打包机执行的命令
#!/usr/bin/env node
const shell = require('shelljs')
const signale = require('signale')
const { Signale } = signale
const ci = require('miniprogram-ci')async function main() {const interactive = new Signale({ interactive: true })interactive.pending(task)// 执行构建命令const result = shell.exec(`npm run build`)if (result.code !== 0) {// 如果构建失败了 退出interactive.error(task)process.exit(1)} else {const project = new ci.Project({type: 'miniProgram',projectPath: '/dist/dev/mp-weixin',ignores: ['node_modules/**/*'],})// 将构建产物上传至微信服务器ci.upload({project,}).then(res => {console.log('上传成功')// 通过IM通知相关测试/研发验证体验版小程序}).catch(err => {console.log('上传失败')interactive.error(task)process.exit(1)})}
}
main()

2. 打包并上传成功后,通过IM通知相关测试/研发验证体验版小程序:

miniprogram-ci是从微信开发者工具中抽离的关于小程序/小游戏项目代码的编译模块。

使用前需要使用小程序管理员身份访问"微信公众平台-开发-开发设置"后下载代码上传密钥,并配置IP白名单,才能进行上传、预览操作。

miniprogram-ci从1.0.28开始支持第三方平台开发的上传和预览,调用方式与普通开发模式无异。

采用自动化发布的优势

  • 节省手动操作的时间和精力;
  • 集成到CI/CD流程:可以集成到持续集成/持续部署(CI/CD)流程中,实现自动化构建和部署;
  • 可编程性:可以通过脚本编程控制上传代码的过程,实现定制化的部署流程。

四、发布后的数据分析

想要对小程序产生的数据进行精确化分析,除了自建监控和埋点,还可以从微信官方提供的“We分析”数据分析平台入手,我们从研发比较关心的两点做介绍:稳定性监控、JS错误分析。

稳定性监控

  • “性能数据”面板,可以看到当前小程序性能综合表现,也可以看到更详细的“白屏分析”、“网络请求分析”等:

  • “实时日志”面板,可以根据关键词查找小程序运行中产生的日志,以此排查业务逻辑等:

JS错误分析

“JS分析”面板,这里可以通过版本、时间等多个维度筛选微信记录的JS错误,通过错误堆栈信息可以定位到代码中的bug进行修复。

同样可以配置微信告警群,群里会定时push错误量的趋势,能够让开发者及时观测到新发布版本的代码bug。

五、总结思考

在小程序发布之初,很多开发者都不看好小程序。从技术视角来看,小程序确实带着非常多的debuff:闭源、充满限制、原生IDE丑、bug多。然而,如今看来,不论开发者是否接受这种与Web精神相悖的技术理念,也不妨碍它已经成为一种新的技术标准——目前市场上很多App都已建设了自己的小程序生态,如支付宝小程序、飞书小程序等,它们无一例外都参考了微信的技术架构和API设计。

尽管小程序在技术上存在诸多限制和缺陷,但其所带来的便利和商业机会无疑是巨大的。未来,随着技术的不断进步和生态的逐渐完善,小程序有望成为移动应用开发的重要范式之一,为用户和开发者创造更多价值。

同时,小程序的成功也提醒我们开发者,要不断关注和学习新技术,不断调整自己的思维和方法,以适应技术和市场的变化。

引用:

https://developers.weixin.qq.com/miniprogram/introduction/

https://segmentfault.com/a/1190000019131399

https://juejin.cn/post/7087041847700226062

https://developers.weixin.qq.com/ebook?action=get_post_info&docid=0000286f908988db00866b85f5640a

https://developers.weixin.qq.com/miniprogram/dev/framework/runtime/skyline/introduction.html

https://uniapp.dcloud.net.cn/

https://docs.taro.zone/docs/

https://www.npmjs.com/package/miniprogram-ci

*文/Zyqy 航飞

本文属得物技术原创,更多精彩文章请看:得物技术

未经得物技术许可严禁转载,否则依法追究法律责任!

这篇关于在得物的小程序生态实践的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1072133

相关文章

Docker集成CI/CD的项目实践

《Docker集成CI/CD的项目实践》本文主要介绍了Docker集成CI/CD的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录一、引言1.1 什么是 CI/CD?1.2 docker 在 CI/CD 中的作用二、Docke

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

从去中心化到智能化:Web3如何与AI共同塑造数字生态

在数字时代的演进中,Web3和人工智能(AI)正成为塑造未来互联网的两大核心力量。Web3的去中心化理念与AI的智能化技术,正相互交织,共同推动数字生态的变革。本文将探讨Web3与AI的融合如何改变数字世界,并展望这一新兴组合如何重塑我们的在线体验。 Web3的去中心化愿景 Web3代表了互联网的第三代发展,它基于去中心化的区块链技术,旨在创建一个开放、透明且用户主导的数字生态。不同于传统

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

EMLOG程序单页友链和标签增加美化

单页友联效果图: 标签页面效果图: 源码介绍 EMLOG单页友情链接和TAG标签,友链单页文件代码main{width: 58%;是设置宽度 自己把设置成与您的网站宽度一样,如果自适应就填写100%,TAG文件不用修改 安装方法:把Links.php和tag.php上传到网站根目录即可,访问 域名/Links.php、域名/tag.php 所有模板适用,代码就不粘贴出来,已经打

跨系统环境下LabVIEW程序稳定运行

在LabVIEW开发中,不同电脑的配置和操作系统(如Win11与Win7)可能对程序的稳定运行产生影响。为了确保程序在不同平台上都能正常且稳定运行,需要从兼容性、驱动、以及性能优化等多个方面入手。本文将详细介绍如何在不同系统环境下,使LabVIEW开发的程序保持稳定运行的有效策略。 LabVIEW版本兼容性 LabVIEW各版本对不同操作系统的支持存在差异。因此,在开发程序时,尽量使用

CSP 2023 提高级第一轮 CSP-S 2023初试题 完善程序第二题解析 未完

一、题目阅读 (最大值之和)给定整数序列 a0,⋯,an−1,求该序列所有非空连续子序列的最大值之和。上述参数满足 1≤n≤105 和 1≤ai≤108。 一个序列的非空连续子序列可以用两个下标 ll 和 rr(其中0≤l≤r<n0≤l≤r<n)表示,对应的序列为 al,al+1,⋯,ar​。两个非空连续子序列不同,当且仅当下标不同。 例如,当原序列为 [1,2,1,2] 时,要计算子序列 [

这些心智程序你安装了吗?

原文题目:《为什么聪明人也会做蠢事(四)》 心智程序 大脑有两个特征导致人类不够理性,一个是处理信息方面的缺陷,一个是心智程序出了问题。前者可以称为“认知吝啬鬼”,前几篇文章已经讨论了。本期主要讲心智程序这个方面。 心智程序这一概念由哈佛大学认知科学家大卫•帕金斯提出,指个体可以从记忆中提取出的规则、知识、程序和策略,以辅助我们决策判断和解决问题。如果把人脑比喻成计算机,那心智程序就是人脑的

Prometheus与Grafana在DevOps中的应用与最佳实践

Prometheus 与 Grafana 在 DevOps 中的应用与最佳实践 随着 DevOps 文化和实践的普及,监控和可视化工具已成为 DevOps 工具链中不可或缺的部分。Prometheus 和 Grafana 是其中最受欢迎的开源监控解决方案之一,它们的结合能够为系统和应用程序提供全面的监控、告警和可视化展示。本篇文章将详细探讨 Prometheus 和 Grafana 在 DevO