小程序的双线程模型解释 & 为什么小程序不提供 DOM 接口

浏览器的线程模型

我们知道浏览器内核是多线程的

  • GUI 渲染线程
  • JavaScript引擎线程
  • 定时触发器线程
  • 事件触发线程
  • 异步http请求线程

其中 GUI 渲染线程和 JavaScript 引擎线程是交替执行的,JavaScript 也可以通过 DOM 接口来对视图进行控制。

这么样的结构还导致了 JS 就被设计成为了单线程的语言。因为若是多线程的话,那么操作 DOM,就会遇到麻烦。比如说一个修改节点,一个删除节点,DOM 视图先听谁的呢?解决这个问题就得上锁,但 JS 创立之初结构是比较简单的,为了避免语言变得复杂,而天然的使用了单线程。

小程序的双线程模型

回到小程序,小程序不同,它使用的是两个线程并行执行的模式,叫做双线程模型。

渲染和 JS 引擎这两个线程同时运行,并通过微信客户端来交换数据。

在小程序运行的时候,JS 层执行我们编写的逻辑,将数据通过 setData 发送到渲染层;而渲染层解析我们的 WXML 和 WXSS,并结合数据渲染出页面。

一方面,每个页面对应一个 WebView 渲染层,对于用户来说更加有页面的感觉,体验更好,而且也可以避免单个 WebView 的负担太重;另一方面,将小程序代码运行在独立的线程中的模式有更好的安全表现,允许有像 open-data 这样的组件可以在确保用户隐私的前提下让我们展示用户数据。

所以这就是为什么和页面有关的改动都只能通过 setData 来完成,它天生不能操作真实的 DOM 结构。

小程序视图层和逻辑层是怎么通信的?

逻辑层调用 setState 设置数据,再通过微信客户端(Native)做中转,通知视图层。

数据传输的本质,是通过逻辑层和视图层两边提供的 evaluateJavascript 所实现的,这就是一个 JSBridge 。即用户传输的数据,需要将其转换为字符串形式传递,同时把转换后的数据内容拼接成一份 JS 脚本,再通过执行 JS 脚本的形式传递到两边独立环境。

补充一点,逻辑层发送网络请求经由客户端 Native 转发。

通信过程是否存在性能问题

每次两个线程传递数据的过程,是用 JSBridge 经过客户端中转的,频繁触发 setState 的时候是否会有性能问题?

会有的,所以小程序官方推荐减少触发频率,必要的时候可以使用防抖、截流函数。

页面多的时候是否性能会下降?

每一个页面有一个对应的渲染层,那页面变多的时候,岂不是会有很大的开销?

在小程序的运行过程中,逻辑层需要常驻,但渲染层可以回收。实际上,当页面栈的层数比较高的时候,栈底页面的渲染层是会被慢慢回收的。

所以为什么不可代理 DOM 处理逻辑

小程序这样的双线程结构,若真能允许使用者操作 DOM 结构,那么每次对 DOM 增删改查的操作,都会引发线程通信问题,只能让调用过程非常缓慢,这个设计就失去了意义。

在实际测试中,如果每次 DOM 调用都进行一次线程通信,耗时大约是同等节点规模直接在渲染层调用的百倍以上;

参考

微信开放社区-小程序底层框架

解剖小程序的 setData

posted @ 2020-04-07 15:25  Ever-Lose  阅读(687)  评论(0编辑  收藏  举报