正常来说,微信小程序是依赖于微信客户端上运行的,并且跟小程序基础库(包括了小程序的整个运行环境以及基础能力,跟具体小程序无关)版本有重大关联关系。
我们可以把微信客户端
以及小程序基础库
简称为宿主环境
(就算去开发类似于百度小程序、字节跳动小程序、京东小程序,它们的概念都是想通的)。
小程序可以调用宿主环境提供的微信客户端的能力,这就使得小程序比普通网页拥有更多的能力。小程序会运行在不同版本
(不同的微信客户端+不同基础库)的宿主环境下,因此针对各个版本的宿主环境做程序上的兼容也是在所难免的。
渲染层和逻辑层
小程序的运行环境分成渲染层
和逻辑层
,分工明确。
WXML
模板和WXSS
样式工作在渲染层,负责展示数据JS
脚本工作在逻辑层,负责产生、处理数据,通过Page 实例或者Component实例的 setData 方法传递数据到渲染层。
WXML模板可以使用 view等 标签,其节点用 {{ }} 的语法绑定一个 数据变量;
WXSS 样式负责整体展示效果,类似于位置、宽高、字体颜色等等;
JS 脚本使用 this.setData 方法来修改数据变量达到动态修改 WXML显示内容;
通信模型
小程序的渲染层和逻辑层分别由2个线程
管理:渲染层的界面使用了WebView
进行渲染;逻辑层采用JsCore
线程运行JS脚本。
一个小程序存在多个界面,所以渲染层存在多个WebView线程,这两个线程的通信会经由微信客户端(下文中也会采用Native来代指微信客户端)做中转
,逻辑层发送网络请求也经由Native转发,小程序的通信模型如图3-1所示。
从这个图,理解出以下内容:
- 整个图有点类似于Android
MVP
的设计概念,渲染层负责渲染绘制相关内容,也就是View层,逻辑层处理网络请求,产生处理数据,也就是Model层,Native类似于Presenter层,负责协调逻辑层(Model)和渲染层(View)之间的操作关系,逻辑层和渲染层不直接进行交互。三者职责分离。 - 逻辑层运行在一个单独线程,负责执行JsCore,也就是我们小程序代码中的那一堆JS代码,而渲染层运行于另一个UI单独线程,负责绘制内容,并且维护了一个页面栈(android开发工程师应该对页面栈很熟悉,特别是Activity生命周期),每一个Page对应一个Webview,由于有多个页面,也就意味着有多个Webview。
- 所有的网络请求都是通过Native去调用,也就是JsCore的网络请求最终都会经过Native去执行,也就是通过微信客户端本身的网络库去执行。
数据驱动
在开发UI界面过程中,程序需要维护很多变量状态,同时要操作对应的UI元素。随着界面越来越复杂,我们需要维护很多变量状态,同时要处理很多界面上的交互事件,整个程序变得越来越复杂。通常界面视图和变量状态是相关联的,如果有某种“方法”可以让状态和视图绑定在一起(状态变更时,视图也能自动变更,MVVM
),那我们就可以省去手动修改视图的工作。
这个方法就是“数据驱动
。
WXML结构实际上等价于一棵Dom树(但是小程序中没有支持直接操作DOM),通过一个JS对象也可以来表达Dom树的结构。
WXML可以先转成JS对象,然后再渲染出真正的Dom树。
通过setData把msg数据从“Hello World”变成“Goodbye”,产生的JS对象对应的节点就会发生变化,此时可以对比前后两个JS对象得到变化的部分,然后把这个差异应用到原来的Dom树上,从而达到更新UI的目的,这就是“数据驱动”的原理。
快速对比出修改点,再把修改点体现在Dom树上(那么,思考点,如果数据点很多或者多次调用setData会怎么样?)。
双线程下的界面渲染
小程序的逻辑层和渲染层是分开的两个线程。在渲染层,宿主环境会把WXML转化成对应的JS对象
,在逻辑层发生数据变更的时候,我们需要通过宿主环境提供的setData方法把数据从逻辑层传递到渲染层,再经过对比前后差异,把差异应用在原来的Dom树上,渲染出正确的UI界面。