前言:
随着微信小程序日渐发展和繁荣,相信不少的前端开发者写不过不少的微信小程序应用。那么,在应用开发之余,你是否也同样好奇小程序框架本身的一些实现原理。今天,让我们一起徜徉探索小程序的诞生和架构的原理。
一、微信小程序的诞生
小程序是期望的产物。H5 页面的推广方式比较多,但同时H5 也有自己的缺点,无法自己获取很多底层 APP 拥有的功能。随着微信推出微信公众号,并提供一系列的 JS-SDK 给 Web 开发使用, Web 开发者拥有可以使用到微信的原生能力后, WebView 的使用频率越来越高。而Webview 的加载体验相对比较糟糕。微信作为一个平台,具有优化用户体验的责任。那么平台如何优化这种用户体验?而作为一个平台,又如何保证平台不被滥用呢?小程序就是在这种期待中诞生的。一方面,小程序是基于 WebView 开发的,其目的是减少开发的成本,实现异步加载的方式,允许开发者在线的版本更新和 Bug 修复。而 前面说的 使用 Webview 容易导致加载体验不好,小程序使用了双线程的方式来使页面渲染和逻辑代码加载分开,降低页面卡壳的可能性。同时,小程序提供了基础能力,提供原生组件,让用户可以获得原生应用的体验。另一方面呢,微信平台通过平台发布、审核、下架等封禁能力,实现对 小程序的管控,保护了平台,防止平台被滥用。
二、小程序的架构原理
前面提到使用了双线程的方式来使页面渲染和逻辑代码分开加载,那么小程序具体是怎么实现的呢?下面我们来一起来探索小程序的架构设计。
微信小程序的框架包含两个部分,分别为视图渲染View 层 和代码逻辑 AppService层。视图渲染层用于渲染页面结构,代码逻辑层用于运行 JS 脚本。视图层和 逻辑代码层采用了双线程方式进行管理。视图层运行于一个 Webview 线程。在 Webview 线程中,会将 wxml 转化成 html, 将 wxss 转化为 css,最终展示成我们的视图。代码逻辑层运行在另一个 Webview 线程,即 JS 执行引擎线程(不同的环境中,实现的引擎不一样,在 IOS 中,使用 JavaScriptCore , 在 安卓中使用 V8 引擎或者 x5 JS 解析器,在 开发工具中,使用 nwjs Chrome 内核 ,我们统称为 JSCore)。在 Jscore 中,提供了 javaScript 的运行环境,在这个线程中执行小程序主要执行小程序的逻辑代码。而视图层和代码逻辑层是两个单独的执行线程,而这两个线程之间的通信则是通过我们的 JSBridge。
以上,我们从整体介绍了小程序的整个架构,下面根据不同部分再进行简单地剖析。
视图渲染View 层
视图层是运行于 Webview 线程中的。我们首先从开发者工作具调试-> 调试-> 调试开发者工具进入查看调试代码时可以发现这一点。如下图:
如上图可明显可知,小程序运行时是将整个视图转化成 我们平常熟悉的 html + css 格式的。而我们平常开发的视图展示则是用 一个 webview 中的 iframe 去承载。
我们通过下面这行代码可以打开对应的 webview 视图层的代码。
document.getElementsByTagName('webview')[0].showDevTools(true,null)
打开如下:
可以发现iframe 承载的内容实际是 一个 html ,他的标签是类似于 web component 的组件形式,而小程序识别这些标签则是通过小程序的基础库去进行识别解析。
我们知道我们平常开发微信小程序视图层代码是编写 WXML 和 WXSS。那么小程序是如何将 WXML 和 WXSS 转换成 hmtl 和 css 的呢?这也是通过小程序的基础库去进行的。我们通过开发者工具 -> 调试 -> 打开调试目录 -> 公共库目录 ,即可以打开我们的基础库目录。如下,其中2.04.wxvpkg 和 2.16.0.wxvkpg 为不同版本的基础库,其他的为一些其他的相关文件。
其中有两个运行脚本,wcc 和 wcsc。
解开基础库wxvpkg文件之后得到的如下文件。
我们重点关注
- WAWebview.js
- WAService.js
使用 wcc 可以将 wxml 文件编译为 js 文件,该js 文件有全局的 gwx 函数 最后会生成 Virtual dom 的对象 ,最后交给 WAWebview.js 去进行渲染生成html。
使用 wcsc 可以将 wxss 文件变为 js 文件,最后js 运行时渲染成 css。
以上,便是我们此次对于 视图 View 层渲染的一些简单解析。
逻辑层JSCore 层
逻辑层 JScore 层的部分则是 JS 在线程的运行,没有相对视图层的视图解析。在逻辑层,主要是将数据进行处理之后发给视图层,同时也接受视图层的事件反馈。在逻辑层,主要实现的功能是 初始化了应用程序,提供原生功能 api。上面基础库文件,WAService.js 是运行在 JScore 层的js 文件。在该文件中实现的模块主要是:
1、WeixinJSBridge 兼容模块
2、 Reporter 模块。
3、比 WAWebview.js 中 wx 功能更为丰富 wx 接口模块。(剩余部分 wx api 都在这里)
4、appServiceEngine 模块,提供 Page,App,GetApp 接口,
5、为 window 对象添加 AMD 接口 require define
在该文件中实现的只要功能则是:
1、App( ) 小程序的入口;Page( ) 页面的入口
2、wx API;
3、页面有的作用域,提供模块化能力
4、数据绑定、事件分发、生命周期管理、路由管理
以上,是对逻辑层的一些简单补充说明。
视图View层 和 逻辑JScore 层的通信
视图层和逻辑层之间的通信是通过 Native (微信客户端)做中转的。通过使用publish和subscribe机制来进行两个线程之间的通信。而具体的实现方式就是统一封装一个WeixinJSBridge。视图层封装了WeixinJSBridge ,逻辑层也同样也兼容了 WeixinJSBridge 的模块,而不同环境的封装的接口则不太一样。对于 windows 环境,主要是通过window.postMessage实现。对于 IOS ,则主要是通过 WKWebview的window.webkit.messageHandlers.NAME.postMessag。而对于 安卓,则是通过WeixinJSCore.invokeHanlder。当视图层有事件需要反馈给逻辑层的时候,会调用 WeixinJSBridge ,最终通知给逻辑层,而逻辑层有数据更新需要通知给视图层的时候,也一样会调用 WeixinJSBridge,将数据反馈给 视图层所以一次完整的用户事件可大致如下:
1、渲染层 -> Native (点击事件)
2、Native -> 逻辑层 (点击事件)
3、逻辑层 -> Native (setData)
4、Native -> 渲染层 (setData)
以上,是对两个线程之间的通信的一些简单补充。
总结:
以上,我们主要简单地分析了一下小程序的架构实现呢。小程序是由两个 webview 线程组成的,视图层和逻辑层。这种架构设计从本质上来讲依旧依赖于 webview,但是双线程的管理模式加速了首屏渲染模式,避免了单线程下,js 的运行阻塞页面的加载和渲染。关于小程序架构设计的内容,还有很多需要去剖析了解,包括架构设计的优缺点,架构设计还有哪些地方的可以优化的等等。这些需要我们一起继续探索。