继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

从 Vue Lib 到 Weex 原生 UIKit 元素的完整流程

慕粉2128038
关注TA
已关注
手记 13
粉丝 0
获赞 0

一、核心步骤

从 Vue Lib 到 Weex 原生 UIKit 元素的完整流程。

首先,用户的需求是 “列举 Weex 做的事情” 并 “结合最新源码标位置”,所以思考过程要先拆解流程:从 Vue 编译产物处理、JS-Native 通信、Virtual DOM 解析、DOM 构建与布局计算、渲染指令生成、原生 View 创建与更新,这几个核心步骤。每个步骤都要对应 Weex 的具体工作,还要找到最新源码中的对应类和方法。

第一步,应该是 Vue 编译产物的适配。因为 Vue 写的代码要被 Weex 识别,Weex 需要处理 Vue 的模板编译结果(比如 render 函数生成的 Virtual DOM),还要注入 Weex 的 JS Runtime API。这里要找最新源码中 JS Runtime 的初始化类,比如 WXJSContext 或 WXBridgeCore,因为 JS 环境的搭建是第一步。

第二步,JS-Native 通信的建立。Vue 的 Virtual DOM 变化需要传递给 Native,所以 Weex 的 Bridge 核心要注册通信方法(比如 callNative),这里对应 WXBridgeCore 的 invokeNative 方法,之前讨论过 bridge 队列,这里要结合最新源码中通信任务的调度。

第三步,Virtual DOM 的解析与 DOM 树构建。Weex 需要把 Vue 的 Virtual DOM(JSON 结构)解析成 Native 侧的 DOM 节点(WXDOMNode),这部分现在在 WXDOMCore 中,比如 createNode 方法,处理节点创建、属性更新,还要维护 DOM 树结构。

第四步,样式解析与 Yoga 布局计算。Vue 的样式(比如 flex)要被 Weex 解析,转换为 Yoga 的布局属性,然后在 dom 线程计算布局,这部分在 WXDOMCore 的_scheduleLayout 方法,调用 Yoga 的 calculateLayout。

第五步,渲染指令生成与传递。DOM 和布局计算完成后,要把结果封装成渲染指令(节点 ID、frame、类型等),通过主线程传递给渲染核心,这里是 WXDOMCore 的_notifyRender 方法,切换到 main queue。

第六步,原生 UIKit 元素的创建与更新。渲染核心(WXRenderCore)接收指令,创建对应 UIKit 控件(UILabel、UIImageView 等),或复用已有 View,更新 frame 和样式,这部分在 WXRenderCore 的 render 方法,还有具体的 View 工厂类(比如 WXViewFactory)。

第七步,事件绑定与反向通信。原生 UI 的事件(比如点击)要传递回 Vue,Weex 需要注册 Native 事件,通过 Bridge 回调 JS,这里对应 WXEventDispatcher 和 WXBridgeCore 的 callbackJS 方法。

具体的逻辑不做深入探讨,可以查看源码研究。

二、Yoga

1. 支持"增量布局更新"

当组件样式变化时,Yoga 仅重新计算受影响的组件树分支,而非全量重算,大幅减少 RN 应用的布局耗时和卡顿』Yoga 是如何实现仅计算受影响的组件树分支的?类似有个打标记,标记为 dirty?

Yoga 实现增量布局的核心就是 「Dirty 标记机制」+「组件树依赖传播」—— 通过标记 “受影响的节点”,并仅处理这些节点及其关联分支,避免全量重算。

1. YogaNode 与 Dirty 状态标识

Yoga 中每个组件对应一个 YogaNode(布局计算的最小单元),每个节点都包含 3 个关键状态标记(用于判断是否需要重算):

  • dirtyFlags(核心标记):记录节点的 “脏状态类型”,主要分两类:

    • LAYOUT_DIRTY:节点自身样式(如 width、flex)或子节点布局变化,需要重新计算自身布局;

    • MEASURE_DIRTY:节点的测量相关属性(如 measureFunction 自定义测量逻辑)变化,需要先重新测量尺寸,再计算布局。

  • isLayoutClean:布尔值,快速判断节点是否 “干净”(无脏状态),避免重复检查 dirtyFlags;

  • childCount + children 指针:维护子节点列表,用于后续遍历依赖分支。

2. 脏状态触发与传播:从 “变化节点” 到 “根节点” 的冒泡

当组件样式变化时(如 RN 中修改 style={{ flex: 2 }}),Yoga 会触发以下流程:

  • 步骤 1:标记自身为 Dirty
    直接修改变化节点的 dirtyFlags |= LAYOUT_DIRTY(或 MEASURE_DIRTY),同时设置 isLayoutClean = false。

  • 步骤 2:向上冒泡通知父节点
    由于父节点的布局(如尺寸、位置)依赖子节点的布局结果(比如父节点是 flex:1,子节点尺寸变化会影响父节点的剩余空间分配),因此会递归向上遍历父节点,直到根节点,将所有 “依赖节点” 都标记为 LAYOUT_DIRTY。
    关键优化:父节点仅标记 “需要重算”,但不会立即计算,避免中途重复触发计算。

  • 步骤 3:跳过已标记的节点
    若某个节点已被标记为 Dirty,后续重复触发时会直接跳过(避免重复冒泡),提升效率。

3. 布局计算阶段:只处理 Dirty 分支,跳过干净节点(DFS)

当 Yoga 触发布局计算(如 RN 渲染帧触发、组件挂载完成)时,会从根节点开始遍历组件树,但仅处理 “Dirty 节点及其子树”:

  • 步骤 1:根节点判断状态
    若根节点是干净的(isLayoutClean = true),直接终止计算(全量跳过);若为 Dirty,进入分支处理。

  • 步骤 2:递归处理 Dirty 分支
    对每个节点,先检查自身状态:

  • 若干净:直接复用上次缓存的布局结果(x/y/width/height),不重算;

  • 若 Dirty:

    • 先处理子节点:如果子节点是 Dirty,先递归计算子节点布局(保证父节点计算时依赖的子节点数据是最新的);

    • 再计算自身布局:根据 Flex 规则(如 flexDirection、justifyContent)和子节点布局结果,计算自身的最终尺寸和位置;

    • 清除 Dirty 标记:计算完成后,设置 dirtyFlags = 0、isLayoutClean = true,标记为干净。

  • 步骤 3:增量更新的核心效果
    比如修改一个列表项的 margin,只会标记该列表项 → 父列表容器 → 根节点为 Dirty,其他列表项、页面其他组件均为干净,会直接跳过计算,仅重算 “列表项→父容器” 这一小分支。

2. Flex 布局逻辑如何到 Native 系统

Flex 布局逻辑,或者说 DSL,是如何翻译为 iOS 的 AutoLayout 和 Android 的 LayoutParams 的?

Yoga 先将 Flex DSL 解析为统一的「布局计算结果」(节点的 x/y/width/height、间距、对齐方式等),再根据平台差异,将计算结果 “映射” 为对应平台的原生布局规则——iOS 映射为 AutoLayout 约束,Android 映射为 LayoutParams + 原生布局容器属性。

1. 第一步:通用前置流程(跨平台统一)

无论 iOS 还是 Android,Yoga 都会先完成以下步骤,屏蔽 Flex DSL 的解析差异:

  1. 解析 Flex 样式:将上层框架的 Flex 配置(如 RN 的 StyleSheet、Weex 的模板样式)解析为 YogaNode 的属性(如 flexDirection、justifyContent、margin、padding 等);

  2. 执行布局计算:通过 Flexbox 算法(基于 Web 标准),计算出每个 YogaNode 的最终布局数据:

  3. 固定属性:width/height(含 auto/flex 计算后的具体数值)、x/y(相对父节点的坐标);

  4. 间距属性:marginLeft/Top/Right/Bottom、paddingLeft/Top/Right/Bottom;

  5. 对齐属性:alignItems、justifyContent 对应的节点相对位置关系;

  6. 输出标准化布局数据:将上述结果封装为平台无关的结构体,供后续平台映射使用。

2. 第二步:iOS 端:映射为 AutoLayout 约束(NSLayoutConstraint)

AutoLayout 的核心是「基于约束的关系描述」(而非直接设置坐标),因此 Yoga 会将 “计算出的具体尺寸 / 位置” 转化为 UIView 的约束(NSLayoutConstraint),核心映射规则如下:一一翻译 css 规则到 iOS AutoLayout 写法:

Flex 核心属性对应的 AutoLayout 约束逻辑
width: 100映射为 view.widthAnchor.constraint(equalToConstant: 100)
height: auto先通过 Yoga 计算出具体高度(如文字高度、子节点包裹高度),再映射为 heightAnchor 约束;若为 flex:1,则映射为 heightAnchor.constraint(equalTo: superview.heightAnchor, multiplier: 1)(占满父容器剩余高度)
marginLeft: 20映射为 view.leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: 20)
marginTop: 15映射为 view.topAnchor.constraint(equalTo: superview.topAnchor, constant: 15)
justifyContent: center(父节点 flexDirection: row)父节点约束:view.centerXAnchor.constraint(equalTo: superview.centerXAnchor);若有多个子节点,通过调整子节点间的 spacing 约束实现均匀分布
alignItems: center(父节点 flexDirection: column)子节点约束:view.centerYAnchor.constraint(equalTo: superview.centerYAnchor)
flex: 1(子节点)映射为 view.widthAnchor.constraint(equalTo: superview.widthAnchor, multiplier: 1)(横向占满)+ 父节点的 distribution 约束(分配剩余空间)

补充信息:

  • Yoga 会为每个 UIView 关联一个 YogaNode,布局计算完成后,通过 YogaKit(或上层框架如 RN 的原生层)自动生成约束;

  • 支持 “约束优先级” 适配:比如 flex:1 对应的约束优先级会高于固定尺寸约束,确保 Flex 规则优先生效;

  • 混合布局兼容:若原生视图已有部分 AutoLayout 约束,Yoga 会生成 “补充约束”,避免冲突(通过 active属性控制约束启用 / 禁用)。


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP