最近在了解小程序的工作原理,我认为是非常有趣的领域,所以根据现有资料和自己的理解整理出此篇文章。这篇文章可能无法做到面面俱到,只求对整体架构有所了解。
小程序宿主环境
小程序和前端APP的开发体验是非常类似的,主要区别在于宿主环境的不同。小程序运行在微信、抖音这种超级APP中,而前端APP运行在浏览器中,这是最大的不同。
那应该如何理解这种不同呢?
回顾浏览器开发,纵使我们可以开发出非常精美且独一无二的页面,却始终无法突破浏览器自身能力的限制。比如我们没有办法在浏览器里执行nodejs运行时提供的fs库,导致我们没有办法直接读取用户本地的文件,这也是为什么很多js库无法在浏览器环境中执行的原因。所以我们需要了解小程序的宿主环境,就能知道有哪些能力以及能力的边界在哪里。
小程序架构
上面是小程序的架构图,分为逻辑层和视图层,这套架构需要客户端同学进行实现,在安卓和iOS的具体实现也不同,但对于开发者来说是透明的。
视图层
webview
视图层其实就是常规的浏览器环境,可以渲染html代码,也可以执行js代码。在客户端中,通过webview技术可以在native应用里加载网页。
webview和iframe有点类似,对于前端来说,第一次听到这个概念可能会觉得不就是iframe么?其实有点类似,对于我们而言,在我们自己自己的前端APP里可以通过嵌入iframe加载别的前端APP。而webview就是提供给客户端同学的iframe,在native app里加载前端app的技术。
双线程架构
有没有发现一个问题,既然webview可以执行js,那为什么还要做一个js逻辑层呢?
我们常规的前端APP开发,页面渲染和JS逻辑都是放在一起的,也没有什么问题。那这些微信官方给的意思是移动端设备性能比较弱,这样容易造成页面卡顿。对浏览器工作原理有了解的同学都会清楚,JS执行会导致页面渲染被阻塞。
但我觉得还有一个更重要的原因。
在国内所有的互联网服务,都是受到监管的。没有办法被监管的应用会面临下架风险。如果JS逻辑层也放在webview,那开发者就有办法绕过setData限制,直接使用JS进行DOM更新,导致内容管控出现风险。
而通过setData,所有的数据都是有办法被监管的。
视图层框架
开发者通过定义类似vue的语法以及css,来定义页面如何被渲染。小程序会将这个类似vue的语法最终编译成一串JS代码,有点类似一些AOT框架。最终在webview中,根据data计算成html将页面渲染出来。
逻辑层
逻辑层可以理解为一个植入了官方基础库的运行环境,在iOS内通过jscore执行js脚本。
通信实现
逻辑层与视图层的通信根据官方介绍是通过native层进行转发的,这里会用到JSBridge技术。
JSBridge是一种在native和js执行环境之间进行通信的技术。因为是跨语言跨进程通信,所有传输的数据都需要可以被序列化和反序列化。
相关的文章也比较多,大致原理就是:
webview发送信息给native,可以通过伪协议,按照规定拼接好URL发起请求即可。比如请求microapp://message?a=1。native对这串请求进行拦截,解析出{a:1}即可。
native发送信息给webview,可以通过类似jsonp的方式。回顾一下jsonp,可以通过执行一段js脚本,在浏览器的js上下文内定义数据。native也具备类似的能力,可以拼接好一串JavaScript代码,然后直接在在webview内执行。比如在jscore中使用evaluateScript这个方法,执行自己拼接好的代码。
当然还可以通过websocket进行传输,在native层启动一个服务端口。
小程序能力与基础库
为了吸引开发者参与小程序开发,以及丰富小程序的功能,各家的开放平台往往会提供非常多的基础能力。有些能力是客户端同学帮忙实现的,比如获取GPS定位,获取用户本地文件,访问用户的麦克风等。还有一些能力是服务端同学进行实现的,比如访问用户账户信息,调用支付等等。
而前端同学会对客户端同学和服务端同学提供的能力统一封装成jssdk,暴露给开发者使用。这个jssdk会抹平底层的实现差异,对开发者而言实现一次开发多端运行的效果。比如在pc端开发者工具中,也会模拟出移动设备的效果。
为了提升开发者体验,小程序技术团队还会封装一些组件给用户调用,比如scroll,rich-text之类的。你可以理解为就是你平时开发前端APP沉淀下来的组件,只不过在这里你不太能修改。
小程序的基础库会在开发者编写的逻辑执行之前先执行。
小程序安全
相比起前端APP的开发,小程序往往有非常严格的限制,会让开发者体验不舒服。但这也是确保这套商业模式能够延续的代价。
因为严格,所以更安全。用户会觉得自己隐私有保障,并且对超级APP的平台来说内容也更可控。
小程序分发与加载
我们如果要访问一个前端APP,直接在浏览器内输入URL即可,然后浏览器会帮我们做后续的事情。
在超级APP里,我们拉起一个小程序,背后其实也有一些复杂的逻辑。当我们点击一个小程序的icon,超级APP比如抖音,会去服务器拉取这个小程序的元数据,比如版本号,依赖的基础库版本号,分包的CDN地址等等。经过比对后判断直接通过本地缓存或去CDN拉取最新版本的小程序进行加载。
为了加速小程序的启动过程,小程序技术团队也煞费苦心了。最基础的方法是对分包进行压缩,并且限制分包的体积,比如分包最大为2MB,如果压缩率为30%,那用户只需要花费600KB流量就能完成首屏加载。以及预测用户的开启行为,提前预热等。
但最近又听说有流式加载这个技术,不需要将整个分包都下载完成,可以边下载边加载,感觉和unbundle的打包理念有点类似,按需加载(但是对网络性能要求可能更高)。
前端架构与自我提升
最近阴差阳错的开始了解起小程序架构,我感叹,第一个设计出这种架构的是天才级专家工程师。令我感叹的地方,具体如下:
- 混合应用的良好实践,性能与开发成本的完美权衡
- 开发的架构,但兼顾安全与合规
- 技术点宽泛还深入,涉及到前端的方方面面
如果想深入了解这小程序前端架构,其实有非常多内容是可以自我提升的:
- 代码编译原理,将模板代码通过AOT技术编译成JS脚本
- 前端工具链,IDE开发技术
- 性能优化实战,亿级用户高频访问,每一处优化都能带来巨大的收益,彰显前端价值