滴滴开源小程序框架 Mpx 新特性:局部运行时能力增强

滴滴技术 发表于 3月以前  | 总阅读数:179 次

Mpx 是滴滴开源的一款增强型跨端小程序框架,自 2018 年立项开源以来如今已经进入第六个年头,在这六年间,Mpx 根植于业务,与业务共同成长,针对小程序业务开发中遇到的各类痛点问题提出了解决方案,并在滴滴内部建设了完善的小程序跨端开发生态。目前,Mpx 已经覆盖支持了滴滴内部全量小程序业务开发,成为了滴滴小程序开发的统一技术标准。

本文主要探讨MPX局部运行时能力增强的方案设计。如需深入了解滴滴开源项目MPX,请参阅相关文章:

滴滴开源小程序框架Mpx,致力于提高小程序开发体验

滴滴出行小程序体积优化实践

滴滴小程序框架Mpx发布2.0:可直接转换已有微信小程序

小程序框架Mpx的下一代脚手架升级之路|滴滴开源

滴滴小程序开发标准 Mpx 推出新版本,聚焦性能与包体积优化

目前在小程序社区当中存在两种小程序上层框架技术方向:

  1. 以 Mpx 为代表的重编译框架,源码采用小程序平台的dsl,编译后直接产出满足小程序规范的代码
  2. 以 Taro@3.x 为代表的重运行时框架,源码可以使用 Vue、React 作为上层dsl,在运行时当中注入框架代码,同时 polyfill 掉小程序在逻辑线程当中并不能访问的 Dom、Bom 等相关 api

由于技术选型和设计思路的差异,小程序框架的发展及开发者实际开发过程中面临的问题侧重点也有所不同。

对于小程序框架自身的迭代:

  • 重运行时框架已具备现代Web框架开发的效率和体验,主要解决Web标准与上层框架和小程序平台规范的差异性和兼容性问题
  • 重编译框架遵循小程序平台的开发规范,核心在于增强小程序能力,提高开发效率和体验,尽量与现代Web框架能力对齐

Mpx 正是在小程序能力增强的设计思路下,努力与现代 Web 框架的开发体验和能力拉齐,以全面提升用户的开发效率、体验及小程序性能。例如,近两年发布了两个 minor 版本,分别支持了 webpack5 持久化缓存来大幅提高编译构建速度以及 composition-api 特性,让用户能够使用当下最热门且备受好评的开发范式。

在实际业务开发过程中,如果想要使用 props 透传、默认插槽、事件透传等现代 Web 框架提供的功能,但由于小程序自身框架设计未支持这些特性,开发者需要编写大量冗余代码才能达到相同的效果。因此,为了尽可能与现代 Web 框架能力拉齐,我们需要在运行时层面进行能力增强,以提高用户的开发效率和体验。这就是接下来要介绍的 mpx 局部运行时方案设计。

方案设计

在整体的技术方案设计上还是按照小程序能力增强的思路进行,以最小的组件粒度来按需开启运行时渲染。

在具体实现上分为运行时和编译两部分内容。

在编译阶段主要完成:

  1. Render Function 增强
  2. 基础模板按需生成
  3. 胶水代码注入

在运行时阶段主要完成:

  1. propertiesToComputed
  2. vdom tree 生成&视图渲染
  3. 组件实例上下维护
  4. 事件代理&分发

核心模块设计

render 函数增强

Mpx 在视图层处理上严格遵守小程序开发规范:一个Mpx SFC文件经过编译后,其处理完成的template将被输出为符合小程序语法规范的wxml静态文件。

在性能优化方面,Mpx 引入了 render 函数。目前,render 函数的主要功能是收集响应式数据。在组件初次渲染阶段,调用 render 函数完成响应式数据的收集。当响应式数据发生变化时,重新触发 render 函数的执行,完成变更后数据的收集,然后进行数据 diff,最后通过 setData 完成页面UI视图的更新。render 函数的形式如下:

global.currentInject = {
  moduleId: "m3f28ff60",
  render: function () {
    this._c("message", this.message);
    ({ tap: [["onViewTapBubble", 'b', "__mpx_event__"]] });
    this._i(this._c("navigatorList", this.navigatorList), function (item, index) {
      ({ tap: [["jumpTo", item]] });
      item.name;
    });
    this._r();
  }
}

在运行时增强方案中,Mpx SFC 文件经过编译后,对于 template 处理最大的不同在于经过编译后是处理为一个执行后会生成 vdom tree 的 render 函数。

在组件初次渲染过程中,执行这个 render 函数,不仅完成了响应式数据的收集,还生成了 vdom tree 。这个 vdom tree 描述了 template 的内容,并将它注入到运行时的 render 函数中。因此,在页面的初次渲染阶段,setData 设置的数据是整个 vdom tree ,它描述了整个模板视图的内容。然后通过自定义容器组件 mpx-custom-element 的递归渲染来完成整个组件的渲染。

global.currentInject = {
  moduleId: 'm3f28ff60',
  render: function () {
    var vnode = this.__c('block', [
      this.__c(
        'view',
        {
          class: this.__sc('root', undefined),
          mpxbindevents: {...},
          eventconfigs: {...}
        },
        [
          this._i(
            this._c('navigatorList', this.navigatorList),
            function (item, index) {
              return this.__c(
                'view',
                {...},
                [...]
              )
            }
          )
        ]
      )
    ])
    this._r(vnode)
  }
}

在组件初次渲染时,通过 setData 传递到渲染层的数据是全量的 vdom tree。当组件响应用户操作或接口请求导致组件实例上的响应式数据发生变化时,会触发 Render Function 的重执行,以生成一个新的 vdom tree。考虑到逻辑层与渲染层通信的成本和性能,这涉及到前后两次 vdom tree 的 diff。最终,生成具体的 vdom 路径数据更新内容,并通过 setData 传递到渲染层,完成视图的局部更新。

mpx-custom-element 容器组件mpx-custom-element.mpx 是 mpx 内置的一个自定义组件,主要用于运行时渲染。其主要功能有两个:

  1. 作为基础容器组件承载模板渲染:mpx-custom-element.mpx 主要作为一个渲染容器,接收组件传递的需动态渲染的 composed tree 数据,并在其上下文中完成真实组件的渲染。需要注意的是,mpx-custom-element.mpx 本身也是一个基于模板动态渲染的组件,其最终编译输出的内容包含 wxml(视图层)/wxss(样式)/js(逻辑层)/json(配置)。
  2. 跳脱微信小程序模板递归渲染的限制:由于微信小程序的 template 能力不支持模板的自引用递归渲染,因此需要借助一层自定义组件容器来绕开这一限制,从而提供新的渲染上下文。

基础模板

所有模板动态化组件的视图都是基于 mpx-custom-element.mpx 提供的基础模板 mpx-custom-element.wxml 进行动态渲染的。在组件的编译阶段,会按需收集使用到的组件和属性,并将其输出到基础模板中,以确保基础模板的包体积最小化。

组件的 Render Function 执行后生成的 vdom tree 描述了组件的视图层结构和样式。最终,通过组件实例的 setData 方法将 vdom tree 传递到视图层,并利用这些基础模板进行动态渲染。

<template is="tmpl_0_container" wx:if="{{r && r.nodeType}}" data="{{ i: r }}"></template>

<template name="mpx_tmpl">
  <element r="{{r}}" wx:if="{{r}}"></element>
</template>

<template name="tmpl_0_block">
  <block wx:for="{{i.children}}" wx:key="index">
    <template is="tmpl_1_container" data="{{i:item}}" />
  </block>
</template>

<template name="tmpl_0_view">
  <view class="{{i.data.class}}" bind:tap="__invoke" data-eventconfigs="{{i.data.dataEventconfigs}}" style="{{i.data.style}}"  data-mpxuid="{{i.data.uid}}">
    <block wx:for="{{i.children}}" wx:key="index">
      <template is="tmpl_1_container" data="{{i:item}}" />
    </block>
  </view>
</template>

...

事件系统小程序采用双线程架构(视图层+逻辑层),在其事件系统设计中:

  • 事件是视图层到逻辑层的通讯方式
  • 事件可以将用户的行为反馈到逻辑层进行处理
  • 事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数

同时,小程序的事件模型(事件冒泡、事件捕获)在视图层进行声明,而非在逻辑代码层通过 API 调用的形式实现,这也是小程序的事件系统和 Web 事件系统的最大差异点。

由于小程序没有真实的 DOM/BOM,其他运行时方案中的事件系统需要基于 vdom tree(vdom tree 上绑定了不同节点的事件处理类型&函数)按照浏览器的事件机制实现相关的事件流程(事件冒泡、事件捕获)。在整个事件系统处理的方案中,完全由运行时接管事件触发的生命周期。

简单总结流程如下:

  • 由小程序的视图层接收事件的触发
  • 视图层与逻辑层通信,将用户行为反馈到逻辑层处理
  • 运行时代码模拟整个事件系统生命周期
<view
  bindtap="__invoke"
  bindtouchstart="__invoke"
  bindtouchmove="__invoke"
  bindtouchend="__invoke"
  bindtouchcancel="__invoke"
  bindlongpress="__invoke"></view>

可以看到在基础模板上,我们只对普通的事件如 tap、touchstart 等进行了绑定(在模板上没有绑定其他特殊的事件类型),同时每个事件使用__invoke 代理方法统一响应微信小程序触发的事件。在其他运行时方案(以 Kbone 为例)中,运行时代码实现了一整套事件系统,在__invoke 代理方法内部根据实际的事件类型触发相应的事件,并根据 vdom tree 递归寻找事件触发的上层节点,从而模拟出事件冒泡的流程,事件捕获的流程同理。

然而,在 mpx 运行时能力增强方案中,整个方案设计的出发点是按组件粒度使用。如果事件系统也采用这种运行时模拟事件生命周期的方式,将无法很好地与微信小程序的事件机制贴合在一起:主要体现在事件的生命周期无法统一(一个是视图层的声明式,另一个是逻辑代码层的 API 形式)。因此,在 mpx 运行时能力增强方案中,针对事件系统的设计仍然遵循微信小程序的事件系统设计:视图层的声明式。我们不单独实现一套基于 vdom tree 的事件系统。

为了实现这一目标,在运行时组件的模板编译过程中,我们会收集那些使用了 catch、capture-bind、capture-catch 特殊事件生命周期的节点。这些节点最终会被注入到 mpx-custom-element.wxml 基础模板中,同时会根据节点的属性生成对应的 template 模板内容。这样,最终运行时组件的事件生命周期也与微信小程序保持一致。虽然使用了特殊事件生命周期的节点会被注入到基础模板中,导致 mpx-custom-element.wxml 基础模板的最终体积有所增加,但在我们实际使用的场景中,例如乘客主小程序里使用特殊场景事件的情况少之又少,所以这部分的基础模板体积增加是完全可接受的。

性能相关

对于运行时方案来说,小程序的渲染性能是需要特别关注的地方。相较于编译型框架,随着项目复杂度的提升,运行时方案的整体渲染性能表现可能会较差。

有几个比较典型的场景:

  • 页面初次渲染:对于 mpx 项目,最终页面的模板存在于视图层,渲染只需将模板中使用到的数据传递到视图层。而在运行时方案中,逻辑层需要将全量的 vdom tree 数据传递到渲染层驱动基础模板的渲染,因此整体的数据量会大得多。
  • 页面二次更新渲染:对于 mpx 项目,基于 Render Function 的数据 diff 最终只会收集发生变化的 key 以及 value。然而,对于运行时方案,例如使用 Vue 的 Taro 项目,如果响应式数据发生变化,重新触发 Render Function 执行及后续的 diff 操作,需要准确找到数据发生变更的 vdom 节点信息,因此传输的数据量相较于编译型框架而言也会更多。

在 mpx 运行时渲染中,同样会遇到这些性能问题,但在具体的实现过程中,我们会尽量将一些对性能有影响的点降到最低:

  • 首先,在运行时方案的整体策略上,mpx 运行时的能力是按需的,做到局部开启组件的运行时特性。因此,对于一个页面而言,可以部分走原有的编译方案,部分走运行时的方案。
  • 其次,相较于纯运行时方案,它们的基础模板基本要做到全量的输出,而在 mpx 局部方案中,我们是按需完成基础模板的收集和输出,同时对于模板节点的属性、事件绑定也能做到按需。

小程序基于运行时的能力拓展

对于运行时方案来说,它提供了一种新的逻辑层控制视图层渲染的方式。从某种程度上说,小程序的运行时渲染可以看作是对原生小程序能力的增强,这为小程序的能力拓展带来了更多的可能性。

例如,React 提出的 Server Components,通过调试官方提供的 demo,我们可以大致了解其工作流程。简单来说,前端通过接口(props数据)驱动后端 Server Components 的执行,生成描述前端页面结构的 ast json,然后这部分数据再驱动前端完成最终的视图渲染。

使用 Server Components 会带来以下好处:

  • Zero-Bundle-Size Components(在 server 端运行的组件及其纯 js 依赖最终并不会被下载到浏览器当中运行)
  • loading performance(Server Components 可以直接访问后端的服务or database,对于一些场景可以减少网络请求数量,同时把一些纯计算的工作可以放到 server)

那么,在小程序场景下,借助运行时的能力,我们是否可以尝试实现类似 Server Components 的功能呢?暂时抛开工程方面的问题,针对目前小程序的技术架构,可能会遇到以下的问题。

视图渲染

在小程序场景下,视图层渲染有两种方案:一种是原生小程序的渲染流程,另一种是运行时渲染流程。以 mpx 运行时能力增强为例,编译环节将 template 转换为可执行的 Render Function,运行后生成描述视图的 vdom tree,然后驱动视图层的基础模板进行渲染。在这个过程中,Render Function 的执行实际上是由 props/data 的变化驱动的,与平台和环境(例如不依赖小程序提供的API)无关。因此,Render Function 也可以在 server 端执行,生成描述视图的 vdom tree。

既然在 server 端可以实现与原本在小程序环境下相同的效果,那么小程序通过接口获取到这部分数据后,后续的流程就可以交给运行时方案接管,完成最终的渲染。

样式规则对于 wxss 样式规则而言,需要注意的是:在小程序的技术架构中,开发者工具在编译构建阶段会将 wxss 和 wxml 提前处理为可执行的函数,这些代码最终在小程序运行时被注入到视图层执行。

然而,在 Server Components 中,样式规则不再是静态数据,而是由业务后端接口下发。这些数据最初存在于逻辑线程中。为了使逻辑线程中的样式规则能够在视图层生效,有且仅有一个方案就是在 Render Function 执行完生成 vdom tree 后将样式规则通过 inline 的方式合并到最终需要被渲染的 composed tree 当中。

组件逻辑

对于一个组件来说,通常包括由外部传入的 props、自身的状态、生命周期、事件处理(用户交互)等方面。首先,我们可以明确的是,小程序组件的生命周期脱离了小程序平台是无法正常运行的,同时事件也需要在用户发生交互行为后才能触发。因此,对于组件本身而言,这部分内容无法像视图层渲染那样将js代码存放在远端并通过接口/cdn 的形式下发,而只能随着小程序的上线发布存在于逻辑线程中。这样,组件的生命周期和事件交互等方法可以与原有的执行策略保持一致,确保其正常工作。对于事件的处理,与运行时方案一致:通过事件代理的形式来分发真实的用户操作。

实际上,这在 server components demo 中也可以清晰地看到不同类型的组件的职责划分非常明确:

  • 对于 x.server 组件而言:由 props 驱动(props 来源可以是 client 的请求,也可能是服务端访问其他服务或 database),不涉及组件自身状态的维护、生命周期、用户事件交互,只做纯渲染工作;
  • 对于 x.client 组件而言:在浏览器中执行的代码与普通组件无异,可以进行任何操作。

server components 和 client components 混合渲染

在 RFC 中也提到了 server components 和 client components 相互引用的场景。其中,server components 最终在服务器端运行并生成 vdom tree,而 client components 则仅通过一个组件标识下发,其实例的创建在浏览器端执行。

在小程序运行时方案中,也存在类似的场景:部分组件采用运行时渲染,这些组件可能会引用原生的小程序组件,这意味着组件内部存在两种不同的组件渲染方式。对此,我们有相应的解决方案:将原生的小程序组件作为依赖注册到基础模板容器组件中,基础模板最终也需要生成原生组件。结语

Mpx 作为一款兼具优秀开发体验和深度性能优化的增强型跨端小程序框架,我们一直致力于在此领域持续前进,同时也诚邀大家加入 Mpx 用户群,共同参与和交流。

本文由微信公众号滴滴技术原创,哈喽比特收录。
文章来源:https://mp.weixin.qq.com/s/cjA68-C4PDU02VB66blTng

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:8月以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:8月以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:8月以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:8月以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:8月以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:8月以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:8月以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:8月以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:8月以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:8月以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:8月以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:8月以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:8月以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:8月以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:8月以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:8月以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:8月以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:8月以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:8月以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:8月以前  |  398次阅读  |  详细内容 »
 相关文章
为Electron程序添加运行时日志 4年以前  |  19600次阅读
Node.js下通过配置host访问URL 5年以前  |  5632次阅读
用 esbuild 让你的构建压缩性能翻倍 4年以前  |  5367次阅读
 目录