01 | 双线程模型:为什么小程序不用浏览器的线程模型?

浏览器并不是单线程而是多进程的

GUI 渲染线程和 JavaScript 引擎线程是互斥的,JavaScript 在执行期间会阻塞 UI 的渲染,甚至如果脚本执行时间太长会由于页面长时间无响应然后崩溃,正是 GUI 渲染线程和 JavaScript 引擎线程之间的这种互斥、阻塞的线程管理方式
**

那为什么 JavaScript 被设计成单线程的呢?

在运行机制上,JavaScript 并没有像 Java 那样提供多线程能力,最主要就是为了避免多线程操作 DOM 造成 UI 冲突。比如存在多个线程同时操作同一个 DOM,浏览器该如何判断最终的 UI 效果是采用哪个线程的结果?这是经典的线程安全(也称为线程同步)问题,在多线程编程领域有很多解决方案,比如加入锁机制,但这样却又带来了更多的复杂性,与 JavaScript 简单易用的设计初衷相违背。
这同时也解释了为什么 GUI 渲染线程与 JavaScript 引擎线程是互斥的:JavaScript 代码有修改DOM 的权限。

为什么小程序不使用浏览器的线程模型

  • 安全:小程序依赖宿主环境微信,但是发布更新是独立的,不能影响微信
  • 性能:定位小而美,用完即走,不追求全部的web能力

从技术的角度上,平台最核心的一个考量点是为案例提供足够能力的前提下,保证案例的逻辑不会危及平台的安全
线程安全:

  • 一个是 Web Worker
    • Web Worker 是线程安全的,Worker 内的 JavaScript 代码无法获取 Window 和 Document 对象,也就无法操作 DOM。除此之外,由于 Worker 的线程安全特性,Worker 内的代码运行过程中不会阻塞外层的 GUI 渲染线程,两者可以并行。
  • 另一个是使用 Shadow DOM
    • Shadow DOM 是 Web Components 规范的一部分,将 ShadowRoot 的模式设置为 closed 就可以禁止获取到 ShadowRoot 节点,从而也无法操作其内部的 DOM
    • 兼容性更弱

image.png

安全高效的小程序双线程模型

  • 限制 UI 组件类型,只允许声明指定的几个组件
  • 保证逻辑线程安全,不允许直接操作 UI 组件
  • 能够在线更新,不依赖微信
    • 小程序采用了 Hybrid-混合的架构模式:使用 Webview 渲染 UI、使用类似Web Worker 的独立线程运行逻辑
  • 性能需尽量提升,保证用户体验

渲染线程和逻辑线程

小程序的双线程指的就是渲染线程和逻辑线程,这两个线程分别承担UI的渲染和执行 JavaScript 代码的工作
image.png
渲染线程使用 Webview 进行 UI 的渲染呈现。Webview 是一个完整的类浏览器运行环境,本身具备运行 JavaScript 的能力,但是小程序并不是将逻辑脚本放到 Webview 中运行,而是将逻辑层独立为一个与 Webview 平行的线程,使用客户端提供的 JavaScript 引擎运行代码,iOS 的JavaScriptCore安卓是腾讯 X5 内核提供的 JsCore 环境以及 IDE 工具的 nwjs

逻辑线程是一个只能够运行 JavaScript 的沙箱环境,不提供 DOM 操作相关的 API,所以不能直接操作 UI,只能够通过 setData 更新数据的方式异步更新 UI。

事件驱动的通信方式

小程序的渲染层与逻辑层之间的通信并不是在两者之间直接传递数据或事件,而是由 Native 作为中间媒介进行转发,整个过程是典型的事件驱动模式:

  • 渲染层(也可以称为视图层)通过与用户的交互触发特定的事件 event;
  • 然后 event 被传递给逻辑层;
  • 逻辑层继而通过一系列的逻辑处理、数据请求、接口调用等行为将加工好的数据 data 传递给渲染层;
  • 最后渲染层将 data 渲染为可视化的 UI。

02 | 授权模型: 小程序的用户体系与 OAuth 规范

如何搭建微信小程序的登录流程?

1.小程序使用微信登录的优势

  • 从生态系统的角度上
    • 拥有微信完善的生态系统,用户使用微信登录后可以使用微信提供给小程序的各种平台级能力,比如订阅消息、微信支付等
  • 从用户体验的角度上
    • 很大程度上降低登录的复杂程度
  • 从产品策略的角度上
    • 微信登录小程序能够根据用户的来源,制定特殊的产品策略

微信小程序完整的登录流程
image.png
image.png
2.登录流程里的三个角色
客户端

  • 作为整个流程的发起者,获取临时登录凭证 code;
  • 作为整个流程的终结者,存储登录态令牌 token。

开发者服务器

  • 使用code 获取 openid 和 session_key的请求,这个请求使用了微信提供的 auth.code2Session 接口

微信接口服务

  • 会验证网络请求的合法性,对于合法请求下发密钥 session_key 和用户 openid。

3.登录流程的六个术语 模块一:小程序原理 - 图5

03 | 自定义组件:怎么培养组件化思维?

自定义组件的目的

  • 提高代码可复用性
  • 降低代码维护难度
  • 降低系统重构难度

    自定义组件的资源管理

    创建微信小程序自定义组件需要使用 Component 构造器,这是微信小程序结构体系内最小粒度的构造器,外层是 Page 构造器,最外层的是 App 构造器,三者的关系如下图:
    image.png

    自定义组件的生命周期

    image.png
    组件创建流程

  • 创建 & 注册自定义元素

    1. // 创建自定义元素
    2. class MyCustomParagraphElement extends HTMLParagraphElement {
    3. //...
    4. }
    5. // 注册自定义元素
    6. customElements.define('custom-p', MyCustomParagraphElement);
  • 使用组件

    1. <custom-p></custom-p>

Web Components 规范对于自定义 HTML 元素的生命周期
image.png
image.png
差异点一:为什么小程序的自定义组件没有attributeChangedCallback函数?
微信小程序与 Vue/React 一样,同样不允许直接操作 DOM,从根本上就不可能发生 DOM 属性改变的情况
差异点二:Web Components 规范为何没有 created/ready/error 三个函数?
理论上的规范在实现的时候需要结合现实的客观条件

组件间的通信流程

  • 事件驱动模式

image.png

  • selectComponent

父组件通过selectComponent 方法直接获取某个子组件的实例对象,然后就可以访问这个子组件的任何属性和方法了。随后将这个子组件的某个属性通过 properties传递个另外一个子组件

04 | 性能优化:借助微信开发者工具提升小程序性能

小程序性能优化的具体维度

  • 避免过大的 WXML 节点数目
  • 避免执行脚本的耗时过长的情况
  • 避免首屏时间太长的情况
  • 避免渲染界面的耗时过长的情况
  • 对网络请求做必要的缓存以避免多余的请求
  • 所有请求的耗时不应太久
  • 避免 setData 的调用过于频繁
  • 避免 setData 的数据过大
  • 避免短时间内发起太多的图片请求
  • 避免短时间内发起太多的请求