微信小程序系列 -- 微信小程序双线程的设计及通信

544 阅读4分钟

转载自 微信小程序底层框架

双线程的小程序

双线程是什么?我们先来看个官方的图: image

小程序的渲染层和逻辑层分别由 2 个线程管理:渲染层的界面使用了 WebView 进行渲染,逻辑层采用 JsCore 线程运行 JS 脚本。

为什么要这么设计呢?这是为了解决管控和安全的问题,我们需要阻止开发者使用一些浏览器提供的接口,诸如跳转页面、操作 DOM、动态执行脚本的等开放性接口。

我们可以使用客户端系统的 JavaScript 引擎,iOS 下的 JavaScriptCore 框架,安卓下腾讯 x5 内核提供的 JsCore 环境。通过提供一个沙箱环境来运行开发者的 JavaScript 代码来解决。这个沙箱环境只提供纯 JavaScript 的解释执行环境,没有任何浏览器相关接口。

这就是小程序双线程模型的由来:

  • 逻辑层:创建一个单独的线程去执行 JavaScript,在这个环境下执行的都是有关小程序业务逻辑的代码
  • 渲染层:界面渲染相关的任务全都在 WebView 线程里执行,通过逻辑层代码去控制渲染哪些界面。一个小程序存在多个界面,所以渲染层存在多个 WebView 线程

双线程设计为什么能解决管控和安全问题

H5 的隐患

要知道,Web 技术是非常开放灵活的,开发者可以利用 JavaScript 脚本随意地操作 DOM,这是会带来以下的问题:

  1. 随意地跳转网页,改变界面上的任意内容

开发者可以利用 JavaScript 脚本随意地跳转网页,或是改变界面上的任意内容。当然,恶意攻击者也能利用这种便利。

  1. 获取页面数据

小程序也提供可一种可以展示敏感数据的组件,<open-data>能展示包括用户昵称、头像、性别、地理位置等信息(无需用户授权)。 如果开发者可以操作 DOM,意味着他们可以随意拿到用户的敏感信息。

  1. 常见的前端漏洞

开发者们普遍重视的安全漏洞,在前端常见的有 XSS 和 CSRF,XSS 是通过注入 JavaScript 脚本的方式来达到特定目的,而 CSRF 则是利用了 cookie。
XSS 在双线程的设计中就被过滤了,而 CSRF 会在后面讲到。

双线程的设计根除 XSS 攻击

由于双线程设计的缘故,小程序把开发者的 JS 逻辑代码放到单独的线程去运行,因为不在 Webview 线程里,所以这个环境没有 Webview 任何接口,自然的开发者就没法直接操作 DOM,也就没法动态去更改界面或者抓取页面数据,因此避免了 XSS 攻击

小程序防范 CSRF 攻击

跨站请求攻击(CSRF),简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。

这利用了 web 中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。通常的罪魁祸首则是浏览器的 cookie 登录态。

除了检查 Referer 字段来防范,更有效的一种方式是使用 token。小程序也是这么做的。

小程序登录详见 浏览器系列 -- cookie、session、Token、JWT及对应的登录态机制

双线程通信

把开发者的 JS 逻辑代码放到单独的线程去运行,但在 Webview 线程里,开发者就没法直接操作 DOM。那要怎么去实现动态更改界面呢?

前面我们知道,逻辑层和渲染层的通信会由 Native (微信客户端)做中转,逻辑层发送网络请求也经由 Native 转发。这是不是意味着,我们可以把 DOM 的更新通过简单的数据通信来实现呢?

Virtual DOM 相信大家都已有了解,大概是这么个过程:用 JS 对象模拟 DOM 树 -> 比较两棵虚拟 DOM 树的差异 -> 把差异应用到真正的 DOM 树上

在这里我们可以用上,如图: image

  1. 在渲染层把 WXML 转化成对应的 JS 对象。
  2. 在逻辑层发生数据变更的时候,通过宿主环境提供的 setData 方法把数据从逻辑层传递到 Native,再转发到渲染层。
  3. 经过对比前后差异,把差异应用在原来的 DOM 树上,更新界面。

我们通过把 WXML 转化为数据,通过 Native 进行转发,来实现逻辑层和渲染层的交互和通信。而不是让逻辑层直接去操作渲染层(操作 DOM)