【小程序八股文】系列之篇章二 | 小程序的核心机制
- 前言
- 三、微信小程序原理与运行机制
- 简述一下微信小程序的原理
- 微信小程序的双线程的理解
- 为什么不采用浏览器多线程模式?为什么是双线程?`(出发点:安全,快速,方便管控,迭代独立)`
- 双线程模式的流程
- 为什么要多个WebView
- 微信小程序的通信机制的理解(双线程机制下的通信原理)
- 微信小程序的界面渲染的整体流程
- 微信小程序的数据驱动的理解
- 微信小程序如何进行双向绑定?
- 微信小程序,为什么使用js闭包?
- 下篇笔记链接
前言
上一章中我们主要介绍两个部分的内容:一个是小程序的基础/背景,另外一个是小程序与其他平台的区别。
上一章笔记链接:
【小程序八股文】系列之篇章一 | 小程序基础及与其他产品区别
那么在这一篇章中,我们主要介绍小程序的核心机制,因为核心机制比较重要,所以我把它单独作为一章来写。
三、微信小程序原理与运行机制
这里主要是介绍小程序的原理及运行机制
简述一下微信小程序的原理
-
微信小程序采用
JavaScript、WXML、WXSS
三种技术进行开发,本质就是一个单页面应用,所有的页面渲染和事件处理,都在一个页面内进行,但又可以通过微信客户端调用原生的各种接口 -
微信小程序基于数据驱动的架构模式,也就是它的
UI
和数据是分离的,所有的页面更新,都需要通过对数据的更改来实现。 -
微信小程序是一个双线程的框架,也就是说渲染进程
WebView
和脚本进程JSCore
是互相独立的。这两个进程分别管理微信小程序框架的两个组成部分视图层(也叫做渲染层)和逻辑层。其中,视图层View
用来渲染页面结构,展现UI,逻辑层appService
用来处理业务逻辑、数据及接口调用。它们分别在渲染线程和脚本进程中运行,并通过系统层JSBridge
实现通信,实现 UI 的渲染、事件的处理。(从上面可以看除微信小程序具有三个层次:视图层(View),逻辑层(APP Service),系统层(Native);两个进程:渲染进程和脚本进程)。
微信小程序的双线程的理解
为什么不采用浏览器多线程模式?为什么是双线程?(出发点:安全,快速,方便管控,迭代独立)
上面两个问题都是小程序双线程框架的背景。
-
小程序双线程框架出发点是在保证微信安全的前提下提供一种轻量化,快速便捷的应用程序。这就需要同时兼顾安全和性能。而浏览器采用的是多线程的模式,会为每个标签页添加一个独立的渲染进程,每个进程之间的资源和行为互不干扰,虽然这样可以保证网页独立稳定的运行,但同时也消耗了较多的资源。网站是依赖于浏览器的,有关Web相关的技术比较全面,可以承载一些庞大的应用程序,但微信小程序的宿主是微信,定位也是轻量化,并不需要太过丰富的Web能力,因此,多线程的设计不能够很多好的满足微信小程序设计的初衷。
-
跟网站于浏览器的关系相比,我认为小程序与微信的关系更加接近CodePen这类(一个前端编程神器)在线编程平台中每个程序案例与平台的关系。小程序的版本迭代是独立的,不依赖于微信。
-
为了实现版本迭代的独立性,同时兼顾安全性(禁止程序操作DOM),一般有两种方法,一种是采用Web Worker, 另外一种是采用Shadow DOM。但Shadow DOM 的兼容性较差,不适合推广,因此,采用类似Web Worker的方案更加现实。但Web Worker 非常耗费资源,无法兼顾小程序轻量化的要求,因此,小程序采用一种类似Web Worker的新方案,也就是双线程模式。
【拓展】
- Web Worker 和Shadow DOM
- Web Worker 是线程安全的,Worker 内的 JavaScript 代码无法获取 Window 和 Document 对象,也就无法操作 DOM。除此之外,由于 Worker 的线程安全特性,Worker 内的代码运行过程中不会阻塞外层的 GUI 渲染线程,两者可以并行。
- 而 Shadow DOM 是 Web Components 规范的一部分,将 ShadowRoot 的模式设置为 closed 就可以禁止获取到 ShadowRoot 节点,从而也无法操作其内部的 DOM。
- Web Worker
从名字上就可以看出,web worker就是在web应用程序中使用的worker。这个worker是独立于web主线程的,是一个运行在后台的JS线程,不会影响页面的响应。如果将 docs 表格中复杂计算放到主线程中,页面不仅可能卡顿,甚至可能崩溃掉。将计算挪入 web worker 中,将计算结果事件回调的方式返回,可以让用户使用更加流畅。
- Web Worker 和Shadow DOM
-
采用双线程模式也可以更加方便对小程序的管控。跟Web Worker类似,小程序将业务及数据的处理独立于页面的渲染进程,即使用类似Web Worker 的独立线程运行逻辑,使用 Webview 渲染 UI。但不同的是,小程序并没有使用Web Worker 子线程,而是一个独立的“主线程”,这样子可以保证更好的性能,其实本质上也是做沙盒的思想。
渲染线程使用 Webview 进行 UI 的渲染呈现。Webview 是一个完整的类浏览器运行环境,本身具备运行 JavaScript 的能力,但是小程序并不是将逻辑脚本放到 Webview 中运行,而是将逻辑层独立为一个与 Webview 平行的线程,使用客户端提供的 JavaScript 引擎运行代码,iOS 的JavaScriptCore、安卓是腾讯 X5 内核提供的 JsCore 环境以及 IDE 工具的 nwjs 。- 使用客户端系统的
JavaScript 引擎
提供一个沙箱环境来运行开发者的JavaScript
代码(安全管控需求) - 并且
逻辑线程是一个只能够运行 JavaScript 的沙箱环境
(这个沙箱环境只提供纯 JavaScript 的解释执行环境,没有任何浏览器相关接口)。不提供 DOM 操作相关的 API,所以不能直接操作 UI,只能够通过 setData 更新数据的方式异步更新 UI。
- 使用客户端系统的
双线程模式的流程
- 在小程序中,选择了
Hybrid
的渲染方式,将视图层和逻辑层是分开的,双线程同时运行,视图层的界面使用WebView
进程进行渲染,逻辑层运行在JSCore
进程中。 - WebView 主要处理 WXML 和 WXSS,负责渲染等工作,多个页面会创建多个 WebView 线程。
WebView
直译是网页视图
,就是原生应用中的浏览器引擎
。简单来说webview
是手机中内置了一款高性能 webkit 内核浏览器
,不过没有提供UI(地址栏、导航栏),只是单纯的展示一个网页界面。 - JsCore 主要执行业务逻辑代码。
- 渲染层和逻辑层通过 Native 来进行通信。例如请求数据WebSocket 都是通过 Native 来执行后给 JsCore 处理或者给 WebView 来渲染。
- 小程序中为了达到多页面体验的功能,需要使用到多个WebView,每个WebView对应的就是一个小程序的页面。WebView和JsCore这两个线程的通信会经由微信客户端(下文中也会采用Native来代指微信客户端)做中转,线程之间的通讯、数据的传递以及逻辑层发送网络请求也都是经由Native转发
为什么要多个WebView
多个WebView
可以理解为多页面应用
,其实是有区别于单页面应用SPA
的,SPA
渲染页面是通过路由识别以后去动态地将页面挂载到root节点
里,如果SPA
打开一个新页面,是需要卸载掉当前的页面,然后重新渲染。很显然,只有通过多个WebView
才能更加接近原生应用APP的用户体验。
💡 既然一个页面一个WebView线程,那每一page线程都有对应的js线程吗?还是js线程是处理所有page线程的逻辑呢?
逻辑线程是跟页面无关的,只负责执行js代码,而且小程序同一时间只能显示一个页面,所以同一个逻辑线程可以支持所有的渲染线程。
微信小程序的通信机制的理解(双线程机制下的通信原理)
小程序的通信机制——WeixinJSBridge
- 在小程序中,选择了
Hybrid
的渲染方式,将视图层和逻辑层是分开的,双线程同时运行,视图层的界面使用WebView
进程进行渲染,逻辑层运行在JSCore
进程中。 - 线程的通信是通过
Native
(理论上是微信客户端)做中转来处理或者转发信息,具体来说,就是Native
分别在渲染层
和逻辑层
注入WeixinJSBridge
(微信内置浏览器私有接口),渲染层
和逻辑层
可以与Native
通信。 - 微信内置浏览器私有接口WeixinJSBridge是微信浏览器的一个内置的JavaScript库,它提供了一系列的API,可以让网页调用微信客户端的功能。
WeixinJSBridge
提供了渲染层
与Native
、Native
与逻辑层
之间消息通讯的机制。- 逻辑层和渲染层的通信会由 Native (微信客户端)做中转,逻辑层发送网络请求也经由 Native 转发。
为了保持与真实环境的一致,微信开发者工具没有新增或者删除WeixinJSBridge
的方法,只是在WeixinJSBridge
的基础上进行了方法的重构。
window.WeixinJSBridge = {
on: c,
invoke: e,
publish: t,
subscribe: o,
}
- on:主要用来收集小程序开发者工具触发的事件回调。
- invoke:以api的方式调用开发者工具提供的基础能力,并提供对应api执行后的回调
- publish:主要用来向
逻辑层
发送信息,也就是说要调用逻辑层
的事件方法 - subscribe:主要用来收集
逻辑层
触发的事件回调,和publish
配套,就像javascript中的发布订阅模式。
微信小程序的界面渲染的整体流程
小程序的逻辑层和渲染层是分开的两个线程。在渲染层,宿主环境会把WXML转化成对应的JS对象,在逻辑层发生数据变更的时候,我们需要通过宿主环境提供的setData方法把数据从逻辑层传递到渲染层,再经过对比前后差异,把差异应用在原来的Dom树上,渲染出正确的UI界面。
小程序更新视图数据的通信流程
- 在渲染层把 WXML 转化成对应的 JS 对象。
- 在逻辑层发生数据变更的时候,通过宿主环境提供的 setData 方法把数据从逻辑层传递到 Native,再转发到渲染层。
- 经过对比前后差异,把差异应用在原来的 DOM 树上,更新界面。
界面渲染整体流
wxml 可以等价于一棵 DOM 树,另外也可以使用一个 js 对象来模拟 DOM 树,简称虚拟 DOM
1.在渲染层,宿主环境会把 WXML 转化成对应的j s 对象;
2.将 js 对象再次转成真实 DOM 树,交由渲染层线程渲染;
3.数据变化时,逻辑层提供最新的变化数据,js 对象发生变化比较进行 diff 算法对比;
4.将最新变化的内容反映到真实的 DOM 树中,更新界面;
会调用 WeixinJSBridge,将数据反馈给 视图层所以一次完整的用户事件可大致如下:
1、渲染层 -> Native (点击事件)
2、Native -> 逻辑层 (点击事件)
3、逻辑层 -> Native (setData)
4、Native -> 渲染层 (setData)
微信小程序的数据驱动的理解
在JS文件中的data中动态操控数据:使用 this.setData方法修改视图层data中的数据;
在wxml文件通过{{}}将数据绑定,即可在页面中显示
注意:
直接修改this.data,而不调用this.setData(),是无法改变当前页面的状态,会导致数据不一致
仅支持可以JSON化的数据
单次设置的数据不能超过1024KB,尽量避免一次设置过多的数据
不要把data中的任何一项的value设为undefined,否则这一项将不能被设置,可能会有潜在的问题
微信小程序如何进行双向绑定?
- 通过bind-tap点击事件 向app.js 定义的方法中获取回执
- 设置data的值 实现双向绑定
微信小程序,为什么使用js闭包?
因为微信小程序处理函数是异步执行的,异步执行造成的结果可能和预期的不合,如果函数中有循环,最后的结果都一样,所以使用js闭包可以解决这个问题。
下篇笔记链接
【小程序八股文】系列之篇章三 | 小程序的事件及生命周期
下篇笔记链接:【待更新】
下篇笔记内容:【待更新】
原创笔记,未经同意请勿转载
码字不易