从小程序的双线程架构展开

浏览器中 GUI 渲染线程和 JavaScript 引擎线程是互斥的,JavaScript 在执行过程中会阻塞 UI 渲染。即使脚本执行时间过长,页面也会长时间无响应,然后崩溃。正是这种 GUI 渲染线程和 JavaScript 引擎线程之间互斥、阻塞的线程管理,让一些前端开发者认为浏览器是单线程的。

小程序双线程模型

逻辑层和渲染层拆分,每个 page 页面使用一个 webview 线程,逻辑层的 JsCore 是共享的。

运行环境

  • 在 iOS、iPadOS 和 Mac OS 上,小程序逻辑层的 JavaScript 代码运行在 JavaScriptCore 中,视图层是由 WKWebView 来渲染的,环境有 iOS 14、iPad OS 14、Mac OS 11.4 等;
  • 在 Android 上,小程序逻辑层的 JavaScript 代码运行在 V8 中,视图层是由基于 Mobile Chromium 内核的微信自研 XWeb 引擎来渲染的;
  • 在 Windows 上,小程序逻辑层 JavaScript 和视图层都是用 Chromium 内核;
  • 在 开发工具上,小程序逻辑层的 JavaScript 代码是运行在 nw.js,视图层是由 Chromium Webview 来渲染的。

优势:

  • 性能:避免 js 阻塞渲染
  • 安全:提供沙箱环境运行 JS 代码,限制了 DOM 和 BOM 的操作,禁用了 new Function 和 eval 函数,限制动态脚本执行,并对隐私数据进行了保护。

缺点:

  • 数据异步通信,渲染延迟,因此微信要发明 WXS ,让一部分 js 代码能在渲染层跑,部分解决通信和延迟的问题
  • 小程序的长列表、手势跟随交互、Canvas渲染都是问题多多

解读:

浏览器中JS并不会阻塞界面渲染,只是 JS 会阻塞 dom、style、layout 等等计算过程而已, css 动画绘制不会阻塞
好处是:

  • jscore 阻塞期间用户依然可以跟 webview 做非常有限的交互,比如页面上下滚动等等。但 jscore 阻塞的时候,你业务不能响应界面事件,不能更新显示内容,这种有限的交互也没什么太大意义。
  • 所有的页面、组件逻辑都在一个线程里,比较好做状态共享。在传统的浏览器网页开发时,如果涉及到跨页面通信,往往要用到 postMessage() 这种异步接口,一个页面的 js 不能同步调用另一个页面的逻辑。在小程序里是可以的,由于都在一个线程里,你完全可以在一个 Page 实例里找到另一个 Page 实例,页面间互操作。有的小程序用全局 Redux 管理所有页面的状态,就是这个原理

Flutter

Flutter 既不使用 WebView,也不使用原生控件,使用了跨平台的自渲染引擎。底层使用了 skia 作为 2D 引擎。

高性能

  • Flutter采用的开发语言是Dart,Dart在即时编译(JIT)模式下的执行速度跟JavaScript基本持平,但是Dart还支持提前编译(AOT),在提前编译模式下的执行速度远远快于JavaScript。对高帧率视图很有帮助。

  • Flutter的渲染是自己控制的,不是由客户端去渲染。布局的数据直接由Dart去控制,不需要像RN那样因为一些场景导致页面发生变化而需要JavaScript和Native去做一些数据同步,并且频繁的改变DOM。进而节省一些不必要的性能开销。

架构图

框架层

Flutter Framework,即框架层。这是一个纯 Dart实现的 SDK,它实现了一套基础库,自底向上

  • 底下两层(Foundation 和 Animation、Painting、Gestures)在 Google 的一些视频中被合并为一个dart UI层,对应的是Flutter中的dart:ui包,它是 Flutter Engine 暴露的底层UI库,提供动画、手势及绘制能力。
  • Rendering 层,即渲染层,这一层是一个抽象的布局层,它依赖于 Dart UI 层,渲染层会构建一棵由可渲染对象组成的渲染树,当动态更新这些对象时,渲染树会找出变化的部分,然后更新渲染。渲染层可以说是Flutter 框架层中最核心的部分,它除了确定每个渲染对象的位置、大小之外还要进行坐标变换、绘制(调用底层 dart:ui )。
  • Widgets 层是 Flutter 提供的一套基础组件库,在基础组件库之上,Flutter 还提供了 Material 和 Cupertino 两种视觉风格的组件库,它们分别实现了 Material 和 iOS 设计规范。

引擎层

Engine,即引擎层。毫无疑问是 Flutter 的核心, 该层主要是 C++ 实现,其中包括了 Skia 引擎、Dart 运行时(Dart runtime)、文字排版引擎等。在代码调用 dart:ui 库时,调用最终会走到引擎层,然后实现真正的绘制和显示。

未来自研的 Impeller 会替代 Skia

嵌入层

Embedder,即嵌入层。Flutter 最终渲染、交互是要依赖其所在平台的操作系统 API,嵌入层主要是将 Flutter 引擎 ”安装“ 到特定平台上。嵌入层采用了当前平台的语言编写,例如 Android 使用的是 Java 和 C++, iOS 和 macOS 使用的是 Objective-C 和 Objective-C++,Windows 和 Linux 使用的是 C++。 Flutter 代码可以通过嵌入层,以模块方式集成到现有的应用中,也可以作为应用的主体。Flutter 本身包含了各个常见平台的嵌入层,假如以后 Flutter 要支持新的平台,则需要针对该新的平台编写一个嵌入层。

小结

  • Flutter 基于自绘引擎+原生的方式去开发,因为自有引擎渲染抹平了不同平台的差异,可以保持UI的高度一致性。
  • Flutter 自绘引擎不需要在渲染时和Native频繁通信,在动画和一些拖拽等操作上性能优于RN,更接近于原生的体验。
  • Dart 作为一种新的开发语言,上手难度要高于RN。

React Native

老架构

微信小程序最早应该就是借鉴了 RN 的思路

新架构

使用 turbo module 让 js 直接通过 jsi 中介直接调用 java/obc 的代码,可以干掉异步开销

微信小程序 Skyline 渲染引擎

Skyline 创建了一条渲染线程来负责 Layout, Composite 和 Paint 等渲染任务,并在 AppService 中划出一个独立的上下文,来运行之前 WebView 承担的 JS 逻辑、DOM 树创建等逻辑。这种新的架构相比原有的 WebView 架构,有以下特点:

  • 界面更不容易被逻辑阻塞,进一步减少卡顿
  • 无需为每个页面新建一个 JS 引擎实例(WebView),减少了内存、时间开销
  • 框架可以在页面之间共享更多的资源,进一步减少运行时内存、时间开销
  • 框架的代码之间无需再通过 JSBridge 进行数据交换,减少了大量通信时间开销

以上是微信官方文档描述,网友通过抓包,确认是 Skyline 的渲染是 flutter 绘制方案 。

好处

  • 节约内存占用,渲染层从 1:1 改成 1:N ,即多个 Page() 实例复用一个渲染器
  • flutter 渲染流水线比较精简和可控,可以很好解决局部渲染、原生组件融合等问题。
  • 可以在页面间共享显示元素。
  • 可以灵活控制页面导航行为和动画
  • context 之间可以通过结构化克隆交换数据,性能比 jsbridge 高

缺点

  • 需要自己实现 CSS 子集,兼容性较差
  • lutter 控件与 Webview 控件的行为差异
  • 框架失控风险

Weex2.0

Weex 2.0 是在 Weex 1.0 基础上做了大幅架构升级的新版本,定位依然是面向 Web 标准和前端生态,通过自绘渲染引擎、高效字节码引擎等核心升级,在标准化程度、性能、开发体验方面相比 1.0 版本都有大幅提升

参考