二、React Reconciler 与 Renderer 分离架构详解

22 阅读17分钟

从零开始理解跨平台架构的核心设计思想

目录

  1. 用生活案例理解分离架构
  2. React 的跨平台挑战
  3. 分离架构的核心设计
  4. 深入 Reconciler 层
  5. 深入 Renderer 层
  6. Host Config 接口设计
  7. 架构设计思想提炼
  8. 手写一个简易跨平台框架
  9. 实际应用场景

用生活案例理解分离架构 {#生活案例}

案例:餐厅的厨房与服务系统

想象一个连锁餐厅品牌,它要在不同场景开店:

┌─────────────────────────────────────────────────────────────┐
│              同一个菜谱,不同的服务方式                      │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  场景 1:堂食餐厅                                            │
│    厨房制作 → 服务员端上桌 → 客人堂食                       │
│                                                              │
│  场景 2:外卖平台                                            │
│    厨房制作 → 打包装盒 → 骑手配送                           │
│                                                              │
│  场景 3:自助取餐                                            │
│    厨房制作 → 保温柜 → 客人自取                             │
│                                                              │
└─────────────────────────────────────────────────────────────┘

关键问题:如何设计系统架构?

❌ 糟糕的设计:耦合架构

// 每个场景写一套代码
class DineInRestaurant {
  prepareDish(order) {
    // 做菜逻辑
    const dish = this.cook(order);
    // 堂食服务逻辑(耦合!)
    this.waiter.serveToDiner(dish);
  }
}

class DeliveryRestaurant {
  prepareDish(order) {
    // 做菜逻辑(重复代码!)
    const dish = this.cook(order);
    // 外卖逻辑
    this.packDish(dish);
    this.deliveryService.send(dish);
  }
}

// 问题:
// 1. 做菜逻辑重复 → 难以维护
// 2. 菜谱更新需要改三个地方 → 容易出错
// 3. 新增场景(如自动贩卖机)→ 又要复制一套代码

✅ 优秀的设计:分离架构

// ============================================
// 核心层:厨房(Reconciler)
// 职责:只关心"做菜"的逻辑
// ============================================
class Kitchen {
  prepareDish(order) {
    // 1. 根据菜谱制作菜品
    const dish = this.cook(order);

    // 2. 通知服务层"菜做好了"
    this.serviceAdapter.deliverDish(dish);
  }

  cook(order) {
    // 核心做菜逻辑(所有场景共享)
    return { name: order.dishName, status: 'ready' };
  }
}

// ============================================
// 适配器接口(Host Config)
// 定义:服务层需要实现的标准接口
// ============================================
interface ServiceAdapter {
  deliverDish(dish): void;
  notifyCustomer(message): void;
  cleanup(): void;
}

// ============================================
// 服务层 1:堂食(Renderer for Dine-In)
// ============================================
class DineInService implements ServiceAdapter {
  deliverDish(dish) {
    this.waiter.serveToDiner(dish);
  }

  notifyCustomer(message) {
    this.waiter.announceToTable(message);
  }

  cleanup() {
    this.waiter.clearTable();
  }
}

// ============================================
// 服务层 2:外卖(Renderer for Delivery)
// ============================================
class DeliveryService implements ServiceAdapter {
  deliverDish(dish) {
    this.packDish(dish);
    this.deliveryPlatform.dispatchRider(dish);
  }

  notifyCustomer(message) {
    this.sms.send(message);
  }

  cleanup() {
    // 外卖无需清理
  }
}

// ============================================
// 使用:厨房不关心服务方式
// ============================================
const kitchen = new Kitchen();

// 堂食场景
kitchen.setServiceAdapter(new DineInService());
kitchen.prepareDish({ dishName: 'Pasta' });

// 外卖场景
kitchen.setServiceAdapter(new DeliveryService());
kitchen.prepareDish({ dishName: 'Pasta' });

// ✅ 厨房代码不变,只需切换服务层!

核心优势

┌─────────────────────────────────────────────────────────────┐
│               分离架构的三大优势                             │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. 代码复用(厨房逻辑只写一次)                            │
│     做菜流程、菜谱管理 → 所有场景共享                       │
│                                                              │
│  2. 易于扩展(新增场景不影响厨房)                          │
│     新开自动售卖机 → 只需实现 ServiceAdapter                │
│                                                              │
│  3. 职责清晰(关注点分离)                                  │
│     厨房:只管做菜                                           │
│     服务层:只管交付                                         │
│                                                              │
└─────────────────────────────────────────────────────────────┘

React 的跨平台挑战 {#跨平台挑战}

React 的目标

React 最初只为浏览器设计,但后来面临更多场景:

┌─────────────────────────────────────────────────────────────┐
│                   React 需要支持的平台                       │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  🌐 Web (react-dom)                                         │
│     渲染到:浏览器 DOM                                       │
│     API:document.createElement, appendChild                │
│                                                              │
│  📱 iOS/Android (react-native)                              │
│     渲染到:原生 UI 组件                                     │
│     API:UIView, TextView, etc.                             │
│                                                              │
│  🖥️ Desktop (react-native-windows)                          │
│     渲染到:桌面原生 UI                                      │
│     API:Win32 API, Cocoa                                   │
│                                                              │
│  🖨️ PDF (react-pdf)                                         │
│     渲染到:PDF 文档                                         │
│     API:PDFKit                                             │
│                                                              │
│  🧪 测试环境 (react-test-renderer)                          │
│     渲染到:纯 JS 对象(不涉及真实 UI)                     │
│                                                              │
└─────────────────────────────────────────────────────────────┘

核心矛盾

// React 的核心逻辑(所有平台通用)
// - 组件状态管理:useState, useReducer
// - 组件生命周期:useEffect, useLayoutEffect
// - 虚拟 DOM 树的计算:Diff 算法
// - 优先级调度:Scheduler

// React 的平台差异(每个平台不同)
// - 如何创建 UI 元素?
//   Web: document.createElement('div')
//   Native: new UIView()
//
// - 如何更新属性?
//   Web: element.className = 'active'
//   Native: view.backgroundColor = 'red'
//
// - 如何处理事件?
//   Web: element.addEventListener('click', handler)
//   Native: view.addGestureRecognizer(tapGesture)

// ❌ 如果不分离,React 代码会变成这样:
function commitUpdate(fiber) {
  if (platform === 'web') {
    updateDOMProperties(fiber.stateNode, fiber.props);
  } else if (platform === 'native') {
    updateNativeViewProps(fiber.stateNode, fiber.props);
  } else if (platform === 'pdf') {
    updatePDFNode(fiber.stateNode, fiber.props);
  }
  // 💥 每新增一个平台,核心代码就要改一次!
}

分离架构的核心设计 {#核心设计}

React 的三层架构

┌─────────────────────────────────────────────────────────────┐
│                    应用层(开发者代码)                      │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  function App() {                                   │   │
│  │    const [count, setCount] = useState(0);           │   │
│  │    return <div>{count}</div>;                       │   │
│  │  }                                                   │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                            ↓
                     使用 React API
                            ↓
┌─────────────────────────────────────────────────────────────┐
│              核心层:react 包(平台无关)                    │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  • React.createElement()                            │   │
│  │  • useState, useEffect, useContext                  │   │
│  │  • Component, PureComponent                         │   │
│  │  • Children, memo, lazy                             │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                            ↓
                    生成虚拟 DOMReact Element)
                            ↓
┌─────────────────────────────────────────────────────────────┐
│        协调层:react-reconciler(平台无关的核心逻辑)       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Reconciler 的职责:                                │   │
│  │  1. 维护 Fiber 树(虚拟树结构)                     │   │
│  │  2. 执行 Diff 算法(计算变化)                      │   │
│  │  3. 任务调度(时间切片、优先级)                    │   │
│  │  4. 调用 Hooks(useState, useEffect 的实现)        │   │
│  │  5. 标记副作用(Placement, Update, Deletion)       │   │
│  │                                                      │   │
│  │  ⚠️ 关键:Reconciler 不知道"具体平台"是什么         │   │
│  │          它只知道"需要执行什么操作"                  │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                            ↓
                通过 Host Config 接口调用
                            ↓
┌─────────────────────────────────────────────────────────────┐
│          渲染层:Renderer(平台特定的实现)                  │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐     │
│  │ react-dom    │  │ react-native │  │  react-pdf   │     │
│  │              │  │              │  │              │     │
│  │ 实现:        │  │ 实现:        │  │ 实现:        │     │
│  │ createInst.. │  │ createInst.. │  │ createInst.. │     │
│  │ appendChild  │  │ appendChild  │  │ appendChild  │     │
│  │ commitUpdate │  │ commitUpdate │  │ commitUpdate │     │
│  └──────────────┘  └──────────────┘  └──────────────┘     │
└─────────────────────────────────────────────────────────────┘
                            ↓
                            ↓
┌─────────────────────────────────────────────────────────────┐
│                     宿主环境                                 │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐     │
│  │  浏览器 DOM  │  │  Native UI   │  │  PDF 文档    │     │
│  └──────────────┘  └──────────────┘  └──────────────┘     │
└─────────────────────────────────────────────────────────────┘

依赖关系(依赖倒置)

传统依赖(高层依赖低层):
┌────────────┐
│   React    │
│ (核心逻辑)  │
└─────┬──────┘
      │ 依赖
      ↓
┌────────────┐
│  React DOM │
│ (具体实现)  │
└────────────┘

问题:核心代码依赖具体实现 → 无法扩展


依赖倒置(低层依赖高层的抽象):
┌────────────────────────────────┐
│        React Reconciler        │
│        (定义抽象接口)           │
│  ┌──────────────────────────┐  │
│  │  Host Config Interface   │  │
│  │  - createInstance()      │  │
│  │  - appendChild()         │  │
│  │  - commitUpdate()        │  │
│  └──────────────────────────┘  │
└────────────────────────────────┘
         ↑                ↑
         │ 实现           │ 实现
         │                │
┌────────┴──────┐  ┌─────┴────────┐
│  React DOM    │  │ React Native │
│ (实现接口)     │  │ (实现接口)    │
└───────────────┘  └──────────────┘

优势:
✅ 核心层不依赖具体平台
✅ 新增平台只需实现接口
✅ 核心层和平台层可独立演进

深入 Reconciler 层 {#reconciler层}

Reconciler 的核心职责

// ============================================
// react-reconciler 包的核心代码(简化版)
// ============================================

// Reconciler 只关心"算法逻辑",不关心"具体平台"
function performUnitOfWork(fiber) {
  // 1. beginWork:向下遍历,计算新的虚拟树
  const next = beginWork(fiber);

  if (next !== null) {
    return next; // 有子节点,继续向下
  }

  // 2. completeWork:向上回溯,构建真实节点
  let completedWork = fiber;
  while (completedWork !== null) {
    completeUnitOfWork(completedWork);

    if (completedWork.sibling !== null) {
      return completedWork.sibling; // 处理兄弟节点
    }

    completedWork = completedWork.return; // 返回父节点
  }

  return null; // 遍历完成
}

// ============================================
// completeWork:构建真实节点
// ============================================
function completeWork(fiber) {
  const { type, props } = fiber;

  switch (fiber.tag) {
    case HostComponent: // 宿主组件(如 div, View)
      if (fiber.stateNode === null) {
        // ✨ 关键:通过 Host Config 创建节点
        // Reconciler 不知道 hostConfig.createInstance 的具体实现
        // Web 平台:document.createElement('div')
        // Native 平台:new UIView()
        const instance = hostConfig.createInstance(
          type,      // 'div' or 'View'
          props,     // { className: 'container' }
          fiber
        );

        // 将子节点挂载到当前节点
        appendAllChildren(instance, fiber);

        // 保存到 Fiber 节点
        fiber.stateNode = instance;
      } else {
        // 节点已存在,需要更新
        // ✨ 通过 Host Config 准备更新
        const updatePayload = hostConfig.prepareUpdate(
          fiber.stateNode,
          type,
          fiber.alternate.memoizedProps, // 旧 props
          props                           // 新 props
        );

        if (updatePayload) {
          // 标记需要更新
          fiber.updateQueue = updatePayload;
          fiber.flags |= Update;
        }
      }
      break;

    case FunctionComponent:
      // 函数组件没有真实节点
      break;
  }
}

// ============================================
// Commit 阶段:执行副作用
// ============================================
function commitWork(fiber) {
  switch (fiber.flags) {
    case Placement: // 插入节点
      // ✨ 通过 Host Config 执行插入操作
      const parent = getHostParent(fiber);
      hostConfig.appendChild(parent, fiber.stateNode);
      break;

    case Update: // 更新节点
      // ✨ 通过 Host Config 执行更新操作
      const updatePayload = fiber.updateQueue;
      hostConfig.commitUpdate(
        fiber.stateNode,
        updatePayload,
        fiber.type,
        fiber.memoizedProps
      );
      break;

    case Deletion: // 删除节点
      // ✨ 通过 Host Config 执行删除操作
      hostConfig.removeChild(parent, fiber.stateNode);
      break;
  }
}

Reconciler 的"无知"是优势

┌─────────────────────────────────────────────────────────────┐
│          Reconciler 只知道"做什么",不知道"怎么做"           │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  Reconciler 说:                                             │
│    "我需要创建一个类型为 'div' 的节点,属性是 {...}"        │
│                                                              │
│  React DOM 说:                                              │
│    "好的,我用 document.createElement('div') 创建"          │
│                                                              │
│  React Native 说:                                           │
│    "好的,我用 new UIView() 创建"                           │
│                                                              │
│  React PDF 说:                                              │
│    "好的,我用 PDFKit.rect() 创建"                          │
│                                                              │
│  ✅ Reconciler 的代码完全不变!                              │
│                                                              │
└─────────────────────────────────────────────────────────────┘

深入 Renderer 层 {#renderer层}

React DOM 的实现

// ============================================
// react-dom 包的核心实现(简化版)
// ============================================

// React DOM 实现 Host Config 接口
const ReactDOMHostConfig = {
  // 1. 创建 DOM 节点
  createInstance(type, props) {
    const element = document.createElement(type);

    // 设置初始属性
    Object.keys(props).forEach(key => {
      if (key === 'children') return;

      if (key === 'className') {
        element.className = props[key];
      } else if (key.startsWith('on')) {
        // 事件监听
        const eventType = key.toLowerCase().slice(2);
        element.addEventListener(eventType, props[key]);
      } else {
        element.setAttribute(key, props[key]);
      }
    });

    return element; // 返回真实 DOM 节点
  },

  // 2. 创建文本节点
  createTextInstance(text) {
    return document.createTextNode(text);
  },

  // 3. 将子节点挂载到父节点
  appendChild(parent, child) {
    parent.appendChild(child);
  },

  // 4. 插入节点到指定位置
  insertBefore(parent, child, beforeChild) {
    parent.insertBefore(child, beforeChild);
  },

  // 5. 删除节点
  removeChild(parent, child) {
    parent.removeChild(child);
  },

  // 6. 计算需要更新的属性
  prepareUpdate(instance, type, oldProps, newProps) {
    // 对比新旧 props,返回差异
    const updatePayload = [];

    Object.keys(oldProps).forEach(key => {
      if (key === 'children' || newProps.hasOwnProperty(key)) return;
      // 旧属性在新 props 中不存在 → 删除
      updatePayload.push(key, null);
    });

    Object.keys(newProps).forEach(key => {
      if (key === 'children') return;
      if (oldProps[key] !== newProps[key]) {
        // 属性值变化 → 更新
        updatePayload.push(key, newProps[key]);
      }
    });

    return updatePayload.length > 0 ? updatePayload : null;
  },

  // 7. 执行更新
  commitUpdate(instance, updatePayload, type, oldProps, newProps) {
    // 应用 updatePayload
    for (let i = 0; i < updatePayload.length; i += 2) {
      const key = updatePayload[i];
      const value = updatePayload[i + 1];

      if (key === 'className') {
        instance.className = value;
      } else if (key.startsWith('on')) {
        // 事件更新(简化处理)
        const eventType = key.toLowerCase().slice(2);
        instance.removeEventListener(eventType, oldProps[key]);
        if (value) {
          instance.addEventListener(eventType, value);
        }
      } else if (value === null) {
        instance.removeAttribute(key);
      } else {
        instance.setAttribute(key, value);
      }
    }
  },

  // 8. 获取容器的根节点
  getRootHostContext(rootContainer) {
    return { namespace: 'html' };
  },

  // 9. 获取子节点的上下文
  getChildHostContext(parentContext, type) {
    if (type === 'svg') {
      return { namespace: 'svg' };
    }
    return parentContext;
  },

  // 10. 判断是否需要设置文本内容
  shouldSetTextContent(type, props) {
    return (
      typeof props.children === 'string' ||
      typeof props.children === 'number'
    );
  },

  // 其他方法...
};

// ============================================
// 创建 Reconciler 实例
// ============================================
import Reconciler from 'react-reconciler';

const ReactDOMReconciler = Reconciler(ReactDOMHostConfig);

// ============================================
// ReactDOM.render 的实现
// ============================================
const ReactDOM = {
  render(element, container) {
    // 创建根 Fiber
    const root = ReactDOMReconciler.createContainer(container);

    // 开始渲染
    ReactDOMReconciler.updateContainer(element, root, null);
  }
};

React Native 的实现

// ============================================
// react-native-renderer 的实现(简化版)
// ============================================

const ReactNativeHostConfig = {
  // 1. 创建原生 View
  createInstance(type, props) {
    // type: 'View', 'Text', 'Image', etc.

    // 调用原生模块创建 UI 组件
    const viewTag = UIManager.createView(
      type,          // 组件类型
      rootTag,       // 根容器
      props          // 初始属性
    );

    return {
      viewTag,       // 原生组件的唯一 ID
      type,
      props
    };
  },

  // 2. 创建文本节点
  createTextInstance(text) {
    const viewTag = UIManager.createView('RCTRawText', rootTag, {
      text: text
    });

    return { viewTag, text };
  },

  // 3. 将子节点挂载到父节点
  appendChild(parent, child) {
    UIManager.manageChildren(
      parent.viewTag,      // 父节点 ID
      null,                // 移动操作
      null,                // 移除操作
      [child.viewTag],     // 添加的子节点 ID
      [parent.children.length], // 插入位置
      null
    );

    parent.children.push(child);
  },

  // 4. 更新属性
  commitUpdate(instance, updatePayload, type, oldProps, newProps) {
    // 通知原生层更新属性
    UIManager.updateView(
      instance.viewTag,
      type,
      updatePayload  // 变化的属性
    );
  },

  // 5. 删除节点
  removeChild(parent, child) {
    UIManager.manageChildren(
      parent.viewTag,
      null,
      [parent.children.indexOf(child)], // 移除的索引
      null,
      null,
      null
    );

    parent.children.splice(parent.children.indexOf(child), 1);
  },

  // 其他方法实现...
};

// ✅ 同样是 Reconciler,不同的 Host Config!
const ReactNativeReconciler = Reconciler(ReactNativeHostConfig);

对比两个 Renderer

┌─────────────────────────────────────────────────────────────┐
│            React DOM vs React Native                        │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  相同的接口方法:                                            │
│    createInstance, appendChild, commitUpdate, ...           │
│                                                              │
│  不同的实现细节:                                            │
│                                                              │
│  React DOM:                                                 │
│    createInstance → document.createElement('div')           │
│    appendChild    → parent.appendChild(child)               │
│    commitUpdate   → element.className = 'active'            │
│                                                              │
│  React Native:                                              │
│    createInstance → UIManager.createView('View')            │
│    appendChild    → UIManager.manageChildren(...)           │
│    commitUpdate   → UIManager.updateView(...)               │
│                                                              │
│  ✅ Reconciler 调用的是相同的方法名                          │
│     但底层执行的是不同平台的 API                             │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Host Config 接口设计 {#hostconfig}

完整的 Host Config 接口

// ============================================
// Host Config 接口定义(官方文档)
// ============================================

interface HostConfig<
  Type,           // 节点类型(如 'div', 'View')
  Props,          // 节点属性
  Container,      // 根容器类型
  Instance,       // 节点实例
  TextInstance,   // 文本节点实例
  ...
> {
  // ==============================
  // 1. 节点创建与删除
  // ==============================

  /**
   * 创建节点实例
   * @param type - 节点类型(如 'div', 'View')
   * @param props - 节点属性
   * @param rootContainer - 根容器
   * @param hostContext - 宿主上下文
   * @returns 节点实例
   */
  createInstance(
    type: Type,
    props: Props,
    rootContainer: Container,
    hostContext: HostContext
  ): Instance;

  /**
   * 创建文本节点
   * @param text - 文本内容
   * @param rootContainer - 根容器
   * @param hostContext - 宿主上下文
   * @returns 文本节点实例
   */
  createTextInstance(
    text: string,
    rootContainer: Container,
    hostContext: HostContext
  ): TextInstance;

  // ==============================
  // 2. 树结构操作
  // ==============================

  /**
   * 将子节点添加到父节点
   */
  appendChild(parent: Instance, child: Instance | TextInstance): void;

  /**
   * 在指定节点前插入子节点
   */
  insertBefore(
    parent: Instance,
    child: Instance | TextInstance,
    beforeChild: Instance | TextInstance
  ): void;

  /**
   * 删除子节点
   */
  removeChild(parent: Instance, child: Instance | TextInstance): void;

  /**
   * 在初次渲染时,将所有子节点挂载到父节点
   * (在 completeWork 阶段调用)
   */
  appendInitialChild(parent: Instance, child: Instance | TextInstance): void;

  // ==============================
  // 3. 属性更新
  // ==============================

  /**
   * 准备更新:计算新旧 props 的差异
   * @returns 返回 updatePayload(差异对象)或 null
   */
  prepareUpdate(
    instance: Instance,
    type: Type,
    oldProps: Props,
    newProps: Props,
    rootContainer: Container,
    hostContext: HostContext
  ): UpdatePayload | null;

  /**
   * 提交更新:应用 updatePayload
   */
  commitUpdate(
    instance: Instance,
    updatePayload: UpdatePayload,
    type: Type,
    oldProps: Props,
    newProps: Props
  ): void;

  /**
   * 提交文本更新
   */
  commitTextUpdate(
    textInstance: TextInstance,
    oldText: string,
    newText: string
  ): void;

  // ==============================
  // 4. 上下文管理
  // ==============================

  /**
   * 获取根容器的上下文
   * 例如:HTML 中的 namespace(html/svg)
   */
  getRootHostContext(rootContainer: Container): HostContext;

  /**
   * 获取子节点的上下文
   * 例如:进入 <svg> 时切换 namespace
   */
  getChildHostContext(
    parentHostContext: HostContext,
    type: Type
  ): HostContext;

  // ==============================
  // 5. 特殊处理
  // ==============================

  /**
   * 判断是否应该直接设置文本内容
   * 优化:如果子节点只有纯文本,直接用 textContent 设置
   */
  shouldSetTextContent(type: Type, props: Props): boolean;

  /**
   * 重置文本内容
   * 在需要将文本节点替换为元素节点时调用
   */
  resetTextContent(instance: Instance): void;

  // ==============================
  // 6. 宿主环境相关
  // ==============================

  /**
   * 调度延迟回调(类似 requestIdleCallback)
   */
  scheduleTimeout(
    fn: () => void,
    delay: number
  ): TimeoutHandle;

  /**
   * 取消延迟回调
   */
  cancelTimeout(handle: TimeoutHandle): void;

  /**
   * 获取当前时间(用于调度器)
   */
  now(): number;

  /**
   * 判断是否支持 Mutation(修改 DOM)
   * 如果为 false,需要实现 Persistent 模式的接口
   */
  supportsMutation: boolean;

  /**
   * 判断是否支持 Hydration(SSR 水合)
   */
  supportsHydration: boolean;

  // ==============================
  // 7. Hydration 相关(SSR)
  // ==============================

  /**
   * 尝试复用服务端渲染的节点
   */
  canHydrateInstance?(
    instance: HydratableInstance,
    type: Type,
    props: Props
  ): boolean;

  /**
   * 水合节点
   */
  hydrateInstance?(
    instance: Instance,
    type: Type,
    props: Props,
    rootContainer: Container
  ): UpdatePayload | null;

  // 其他方法...
}

Host Config 的设计哲学

┌─────────────────────────────────────────────────────────────┐
│              Host Config 的设计原则                          │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. 最小化接口(只暴露必要的方法)                          │
│     不需要暴露平台的所有细节,只需实现渲染所需的操作        │
│                                                              │
│  2. 平台无关的命名                                           │
│     方法名不包含具体平台的概念                               │
│     ✅ createInstance(通用)                                │
│     ❌ createDOMElement(特定)                              │
│                                                              │
│  3. 分离关注点                                               │
│     创建、更新、删除 → 独立的方法                           │
│     不混杂业务逻辑                                           │
│                                                              │
│  4. 可选的扩展点                                             │
│     supportsHydration → 不是所有平台都需要 SSR               │
│     supportsMutation → 有些平台是不可变的(如 Figma)       │
│                                                              │
└─────────────────────────────────────────────────────────────┘

架构设计思想提炼 {#设计思想}

1. 依赖倒置原则(Dependency Inversion Principle)

传统依赖:高层模块依赖低层模块
┌──────────────┐
│  高层业务    │  ──depends on──┐
└──────────────┘                 ↓
                         ┌──────────────┐
                         │  具体实现    │
                         └──────────────┘

问题:业务逻辑与具体实现耦合,难以替换


依赖倒置:双方都依赖抽象
┌──────────────┐                 ┌──────────────┐
│  高层业务    │  ──依赖──→  │  抽象接口    │
└──────────────┘                 └──────────────┘
                                        ↑
                                        │ 实现
                         ┌──────────────┴─────┐
                         │     具体实现        │
                         └────────────────────┘

优势:
✅ 高层模块不依赖具体实现
✅ 具体实现可以随意替换
✅ 新增实现不影响高层

React 中的应用:
Reconciler(高层) ←依赖→ Host Config(抽象)
                              ↑
                              │ 实现
              ┌───────────────┼───────────────┐
              │               │               │
         React DOM      React Native    React PDF

2. 开闭原则(Open-Closed Principle)

定义:对扩展开放,对修改关闭

❌ 违反开闭原则:
function render(element) {
  if (platform === 'web') {
    renderToDOM(element);
  } else if (platform === 'native') {
    renderToNative(element);
  } else if (platform === 'pdf') {
    renderToPDF(element);
  }
  // 每次新增平台都要修改这个函数
}

✅ 遵循开闭原则:
// 定义抽象
interface Renderer {
  render(element): void;
}

// 核心逻辑不变
function performWork(element, renderer: Renderer) {
  const tree = buildTree(element);
  renderer.render(tree); // 多态
}

// 扩展:新增平台只需实现接口
class WebRenderer implements Renderer {
  render(tree) { /* DOM 渲染 */ }
}

class NativeRenderer implements Renderer {
  render(tree) { /* Native 渲染 */ }
}

class PDFRenderer implements Renderer {
  render(tree) { /* PDF 渲染 */ }
}

// ✅ 核心代码(performWork)不需要修改!

3. 适配器模式(Adapter Pattern)

定义:将一个类的接口转换成客户期望的另一个接口

React 中的体现:
┌─────────────────────────────────────────────────────────────┐
│                   Reconciler 期望的接口                      │
│  createInstance(type, props) → Instance                     │
│  appendChild(parent, child) → void                          │
└─────────────────────────────────────────────────────────────┘
                            ↑
                            │ 适配
                            │
┌─────────────────────────────────────────────────────────────┐
│                   Web 平台的原生 API                         │
│  document.createElement(tagName) → HTMLElement              │
│  parentNode.appendChild(childNode) → Node                   │
└─────────────────────────────────────────────────────────────┘

// Adapter 实现
const WebAdapter = {
  createInstance(type, props) {
    return document.createElement(type); // 适配
  },

  appendChild(parent, child) {
    parent.appendChild(child); // 直接映射
  }
};

// ✅ Reconciler 不关心底层是 document.createElement
//    它只知道调用 createInstance

4. 策略模式(Strategy Pattern)

定义:定义一系列算法,将每个算法封装起来,使它们可以互换

React 中的应用:
// 策略接口
interface RenderStrategy {
  render(element): void;
}

// 具体策略 1
class DOMRenderStrategy implements RenderStrategy {
  render(element) { /* DOM 渲染逻辑 */ }
}

// 具体策略 2
class NativeRenderStrategy implements RenderStrategy {
  render(element) { /* Native 渲染逻辑 */ }
}

// 上下文(使用策略)
class Reconciler {
  constructor(private strategy: RenderStrategy) {}

  performWork(element) {
    // 核心逻辑
    const tree = this.buildTree(element);

    // 使用策略渲染
    this.strategy.render(tree);
  }
}

// 运行时切换策略
const domReconciler = new Reconciler(new DOMRenderStrategy());
const nativeReconciler = new Reconciler(new NativeRenderStrategy());

5. 关注点分离(Separation of Concerns)

┌─────────────────────────────────────────────────────────────┐
                   关注点分离的三个层次                       
├─────────────────────────────────────────────────────────────┤
                                                              
  Layer 1: React 包(声明式 UI)                              
    关注点:让开发者用 JSX 描述 UI                           
    不关心:UI 如何计算、如何渲染                            
                                                              
  Layer 2: Reconciler(协调与调度)                          
    关注点:计算 UI 的变化、调度更新                         
    不关心:具体平台的渲染细节                               
                                                              
  Layer 3: Renderer(平台渲染)                              
    关注点:将变化渲染到具体平台                             
    不关心:UI 是如何计算出来的                              
                                                              
└─────────────────────────────────────────────────────────────┘

每一层只关心自己的职责,互不干扰

手写一个简易跨平台框架 {#实战案例}

让我们从零实现一个迷你版的跨平台框架,加深理解。

第一步:定义核心抽象(类似 React)

// ============================================
// 1. 定义虚拟节点(类似 React Element)
// ============================================
function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      children: children.flat()
    }
  };
}

// 使用示例
const vdom = createElement('div', { id: 'container' },
  createElement('h1', null, 'Hello'),
  createElement('p', null, 'World')
);

// 结果:
// {
//   type: 'div',
//   props: {
//     id: 'container',
//     children: [
//       { type: 'h1', props: { children: ['Hello'] } },
//       { type: 'p', props: { children: ['World'] } }
//     ]
//   }
// }

第二步:实现协调器(类似 Reconciler)

// ============================================
// 2. 实现 Reconciler(平台无关)
// ============================================
class Reconciler {
  constructor(hostConfig) {
    this.hostConfig = hostConfig; // 依赖注入
  }

  // 递归渲染虚拟节点
  render(vdom, container) {
    // 清空容器(简化处理)
    this.hostConfig.clearContainer(container);

    // 渲染根节点
    const instance = this.renderElement(vdom);

    // 挂载到容器
    this.hostConfig.appendChild(container, instance);
  }

  // 渲染单个元素
  renderElement(vdom) {
    // 处理文本节点
    if (typeof vdom === 'string' || typeof vdom === 'number') {
      return this.hostConfig.createTextInstance(String(vdom));
    }

    // 创建节点实例(通过 Host Config)
    const instance = this.hostConfig.createInstance(
      vdom.type,
      vdom.props
    );

    // 递归渲染子节点
    const children = vdom.props.children || [];
    children.forEach(child => {
      const childInstance = this.renderElement(child);
      this.hostConfig.appendChild(instance, childInstance);
    });

    return instance;
  }

  // 更新逻辑(简化版)
  update(oldVdom, newVdom, container) {
    // 类型不同 → 重新渲染
    if (oldVdom.type !== newVdom.type) {
      return this.render(newVdom, container);
    }

    // 更新节点(简化:只处理属性)
    const instance = container.firstChild;
    this.hostConfig.updateInstance(
      instance,
      oldVdom.props,
      newVdom.props
    );

    // 更新子节点(简化:假设子节点数量相同)
    const oldChildren = oldVdom.props.children || [];
    const newChildren = newVdom.props.children || [];

    oldChildren.forEach((oldChild, index) => {
      if (newChildren[index]) {
        this.update(oldChild, newChildren[index], instance);
      }
    });
  }
}

第三步:实现 Web 渲染器

// ============================================
// 3. 实现 Web Renderer(类似 react-dom)
// ============================================
const WebHostConfig = {
  // 创建元素
  createInstance(type, props) {
    const element = document.createElement(type);

    // 设置属性
    Object.keys(props).forEach(key => {
      if (key === 'children') return;

      if (key === 'className') {
        element.className = props[key];
      } else if (key.startsWith('on')) {
        const eventType = key.toLowerCase().substring(2);
        element.addEventListener(eventType, props[key]);
      } else {
        element.setAttribute(key, props[key]);
      }
    });

    return element;
  },

  // 创建文本节点
  createTextInstance(text) {
    return document.createTextNode(text);
  },

  // 添加子节点
  appendChild(parent, child) {
    parent.appendChild(child);
  },

  // 清空容器
  clearContainer(container) {
    container.innerHTML = '';
  },

  // 更新实例
  updateInstance(instance, oldProps, newProps) {
    // 简化:只处理 className
    if (oldProps.className !== newProps.className) {
      instance.className = newProps.className;
    }
  }
};

// 创建 Web Reconciler
const WebReconciler = new Reconciler(WebHostConfig);

// 暴露给开发者的 API
const MyReactDOM = {
  render(vdom, container) {
    WebReconciler.render(vdom, container);
  }
};

第四步:实现 Canvas 渲染器

// ============================================
// 4. 实现 Canvas Renderer(跨平台示例)
// ============================================
const CanvasHostConfig = {
  // 创建 Canvas 节点(用对象表示)
  createInstance(type, props) {
    return {
      type,
      props,
      children: [],
      x: props.x || 0,
      y: props.y || 0
    };
  },

  // 创建文本节点
  createTextInstance(text) {
    return { type: 'text', text, children: [] };
  },

  // 添加子节点
  appendChild(parent, child) {
    parent.children.push(child);
  },

  // 清空容器(重绘 Canvas)
  clearContainer(container) {
    const ctx = container.getContext('2d');
    ctx.clearRect(0, 0, container.width, container.height);
  },

  // 更新实例
  updateInstance(instance, oldProps, newProps) {
    instance.props = newProps;
    instance.x = newProps.x || instance.x;
    instance.y = newProps.y || instance.y;
  },

  // ✨ 额外方法:绘制到 Canvas
  paint(node, ctx, x = 0, y = 0) {
    if (node.type === 'rect') {
      ctx.fillStyle = node.props.color || 'black';
      ctx.fillRect(
        x + node.x,
        y + node.y,
        node.props.width,
        node.props.height
      );
    } else if (node.type === 'text') {
      ctx.fillStyle = node.props.color || 'black';
      ctx.fillText(node.text, x, y);
    }

    // 递归绘制子节点
    node.children.forEach(child => {
      this.paint(child, ctx, x + node.x, y + node.y);
    });
  }
};

// 创建 Canvas Reconciler
const CanvasReconciler = new Reconciler(CanvasHostConfig);

// 暴露给开发者的 API
const MyReactCanvas = {
  render(vdom, canvas) {
    CanvasReconciler.render(vdom, canvas);

    // 绘制到 Canvas
    const ctx = canvas.getContext('2d');
    const tree = canvas.firstChild; // 简化:直接取第一个子节点
    CanvasHostConfig.paint(tree, ctx);
  }
};

第五步:使用示例

// ============================================
// 5. 使用我们的框架(同一套代码,不同平台)
// ============================================

// 定义组件(平台无关)
const App = createElement('div', { className: 'app' },
  createElement('h1', null, 'Hello World'),
  createElement('p', null, 'This is a cross-platform framework!')
);

// 渲染到 Web
const webContainer = document.getElementById('root');
MyReactDOM.render(App, webContainer);

// 渲染到 Canvas
const App2 = createElement('rect', { x: 10, y: 10, width: 200, height: 100, color: 'blue' },
  createElement('text', { x: 20, y: 30, color: 'white' }, 'Hello Canvas!')
);

const canvasContainer = document.getElementById('canvas');
MyReactCanvas.render(App2, canvasContainer);

// ✅ 同样的 Reconciler 逻辑
// ✅ 不同的 Renderer 实现
// ✅ 一套代码,多端运行!

架构图总结

开发者代码(平台无关)
    createElement('div', ...)
            ↓
┌───────────────────────────────────────────┐
│     Reconciler(平台无关的协调逻辑)       │
│  • renderElement()                        │
│  • update()                               │
│  • 调用 hostConfig.createInstance()       │
└───────────────────────────────────────────┘
            ↓ 通过 Host Config 接口
┌───────────────────────────────────────────┐
│     Renderer(平台特定的实现)             │
│  ┌─────────────┐    ┌─────────────┐      │
│  │WebHostConfig│    │CanvasConfig │      │
│  │document.    │    │canvas.      │      │
│  │createElement│    │fillRect     │      │
│  └─────────────┘    └─────────────┘      │
└───────────────────────────────────────────┘
            ↓                  ↓
      ┌──────────┐      ┌──────────┐
      │   DOM    │      │  Canvas  │
      └──────────┘      └──────────┘

实际应用场景 {#应用场景}

1. 跨端框架(Taro / Uni-app)

┌─────────────────────────────────────────────────────────────┐
│            Taro 的跨平台架构                                 │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  开发者用 React 语法编写代码                                │
│    ↓                                                         │
│  Taro Runtime(类似 Reconciler)                            │
│    - 解析 React 组件                                         │
│    - 构建虚拟 DOM 树                                         │
│    - 计算差异                                                │
│    ↓ 通过适配层                                             │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐                 │
│  │ 微信小程序│  │ 支付宝小程│  │   H5     │                 │
│  │  Renderer │  │程Renderer│  │ Renderer │                 │
│  └──────────┘  └──────────┘  └──────────┘                 │
│       ↓              ↓              ↓                        │
│  setData()   setData()   DOM API                            │
│                                                              │
└─────────────────────────────────────────────────────────────┘

核心思想:
✅ 一套代码(React 组件)
✅ 多个 Renderer(小程序、H5、App)
✅ 通过适配层抹平差异

2. 测试框架(Jest / React Testing Library)

// ============================================
// react-test-renderer:不渲染真实 UI
// ============================================

const TestHostConfig = {
  createInstance(type, props) {
    // 返回纯 JS 对象,不创建真实 DOM
    return {
      type,
      props,
      children: []
    };
  },

  appendChild(parent, child) {
    parent.children.push(child);
  },

  // 其他方法返回空实现
};

// 使用
import { create } from 'react-test-renderer';

const App = () => <div>Hello</div>;
const tree = create(<App />).toJSON();

console.log(tree);
// {
//   type: 'div',
//   props: {},
//   children: ['Hello']
// }

// ✅ 不需要真实浏览器环境
// ✅ 测试速度快
// ✅ 可以断言 UI 结构

3. 自定义渲染器(Figma / Sketch)

// ============================================
// react-figma:渲染到 Figma 设计稿
// ============================================

const FigmaHostConfig = {
  createInstance(type, props) {
    let node;

    switch (type) {
      case 'frame':
        node = figma.createFrame();
        break;
      case 'text':
        node = figma.createText();
        break;
      case 'rectangle':
        node = figma.createRectangle();
        break;
    }

    // 设置属性
    if (props.width) node.resize(props.width, props.height);
    if (props.fill) node.fills = [{ type: 'SOLID', color: props.fill }];

    return node;
  },

  appendChild(parent, child) {
    parent.appendChild(child);
  },

  // ...
};

// 使用 React 编写 Figma 插件!
const DesignSystem = () => (
  <frame width={300} height={200}>
    <text fontSize={24}>Title</text>
    <rectangle width={100} height={50} fill={{ r: 1, g: 0, b: 0 }} />
  </frame>
);

4. 跨平台 UI 库(Ant Design / Material-UI)

// ============================================
// 组件库的跨平台架构
// ============================================

// 核心逻辑(平台无关)
class Button {
  constructor(props) {
    this.props = props;
  }

  handleClick = () => {
    this.props.onClick?.();
  };

  // 抽象方法(子类实现)
  render() {
    throw new Error('Must implement render()');
  }
}

// Web 平台实现
class WebButton extends Button {
  render() {
    return (
      <button onClick={this.handleClick}>
        {this.props.children}
      </button>
    );
  }
}

// Native 平台实现
class NativeButton extends Button {
  render() {
    return (
      <TouchableOpacity onPress={this.handleClick}>
        <Text>{this.props.children}</Text>
      </TouchableOpacity>
    );
  }
}

// 平台检测
const Button = Platform.select({
  web: WebButton,
  native: NativeButton
});

// ✅ 开发者使用统一 API
<Button onClick={() => console.log('clicked')}>
  Click me
</Button>

总结:核心理念再回顾

┌─────────────────────────────────────────────────────────────┐
│        Reconciler 与 Renderer 分离的核心价值                 │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  约束条件:                                                  │
│    • 多平台(Web、Native、Canvas、PDF...)                  │
│    • 每个平台 API 完全不同                                  │
│    • 核心逻辑需要复用                                        │
│                                                              │
│  架构设计:                                                  │
│    • 关注点分离(Reconciler 管算法,Renderer 管渲染)      │
│    • 依赖倒置(核心层定义接口,平台层实现)                 │
│    • 适配器模式(Host Config 抹平平台差异)                 │
│    • 策略模式(运行时切换渲染策略)                         │
│                                                              │
│  实现目标:                                                  │
│    • 一套核心代码                                            │
│    • 多端运行                                                │
│    • 易于扩展(新增平台不改核心)                           │
│    • 可测试(可以不依赖真实 UI)                            │
│                                                              │
└─────────────────────────────────────────────────────────────┘

核心理念:
在约束条件下(多平台、API 差异大),
通过合理的架构设计(分层、抽象、依赖倒置),
实现复杂目标(代码复用、易扩展、可维护)。

通过学习这个架构,你可以应用到:

  • 跨端框架设计:小程序、App、Web 统一开发
  • 通用 SDK 设计:一套代码,多平台运行
  • 插件系统设计:核心稳定,插件灵活扩展
  • 微服务架构:业务逻辑与基础设施解耦

关键思想:不要让核心逻辑依赖具体实现,而是让具体实现依赖抽象接口!