React Fiber 架构中的原型模式:为什么这种设计如此重要

更新日期: 2025-10-23 阅读: 39 标签: 原型

如果你曾经好奇 react 如何高效地更新界面,或者想知道为什么 React 性能这么好,那么这篇文章会给你答案。今天我们要聊的是 React 核心架构中一个重要的设计模式——原型模式。


什么是原型模式?

先来看一个生活中的例子。假设你要制作一批相同的椅子,最直接的方法是每把椅子都从头开始制作。但更聪明的方法是先做一个完美的样品,然后基于这个样品快速复制。

在编程中,原型模式就是这个思路:创建一个模板对象,然后通过复制这个模板来创建新对象,而不是每次都重新构建。


原型模式解决了什么问题?

想象你在开发一个棋盘游戏。棋盘有 64 个格子,每个格子都有颜色、位置和棋子信息。

最直接的写法是这样的:

class BoardSquare {
  constructor(color, row, file, piece) {
    this.color = color;      // 颜色
    this.row = row;          // 行号
    this.file = file;        // 列号
    this.piece = piece;      // 棋子
  }

  occupySquare(piece) {
    this.piece = piece;
  }

  clearSquare() {
    this.piece = null;
  }
}

// 创建64个格子
const squares = [];
for (let i = 0; i < 8; i++) {
  for (let j = 0; j < 8; j++) {
    const color = (i + j) % 2 === 0 ? 'white' : 'black';
    squares.push(new BoardSquare(color, i, j, null));
  }
}

这种方法有个问题:如果后来需要给格子添加新属性,比如背景图片或者特殊样式,就需要修改所有创建格子的代码。这很容易出错,而且效率不高。


原型模式的解决方案

使用原型模式,我们可以这样做:

class BoardSquarePrototype {
  constructor(prototype) {
    this.prototype = prototype;
  }

  clone() {
    return Object.assign(new BoardSquare(), this.prototype);
  }
}

// 创建模板
const whiteSquareTemplate = new BoardSquare('white', null, null, null);
const prototype = new BoardSquarePrototype(whiteSquareTemplate);

// 快速复制
const square1 = prototype.clone();
const square2 = prototype.clone();

这样做的好处很明显:

  • 修改模板就能影响所有复制的对象

  • 避免重复的初始化代码

  • 代码更易于维护


JavaScript 中的原型机制

需要注意的是,JavaScript 语言本身就有原型机制,但这和我们讨论的设计模式不太一样。

JavaScript 的原型继承:

const squareTemplate = {
  color: 'white',
  row: 0,
  file: 0,
  piece: null,

  occupySquare(piece) {
    this.piece = piece;
  }
};

// 创建基于原型的对象
const square1 = Object.create(squareTemplate);
const square2 = Object.create(squareTemplate);

这里的区别很重要:

  • Object.create() 建立的是原型链关系,对象共享同一个原型

  • 原型模式的 clone() 是创建完全独立的新对象


React Fiber 中的原型模式

React 使用 Fiber 架构来管理组件更新。每次界面需要更新时,React 都会创建新的 Fiber 树。但如果每次都完全重新创建,性能会很差。

React 的解决方案就是使用原型模式的思想。我们来看看简化后的代码:

function createFiber(tag, pendingProps, key, mode) {
  return {
    tag,
    key,
    elementType: null,
    type: null,
    stateNode: null,
    pendingProps,
    memoizedProps: null,
    // ... 很多其他属性
    alternate: null,  // 关键指针
  };
}

function createWorkInProgressFiber(current) {
  let workInProgress = current.alternate;

  if (workInProgress === null) {
    // 首次创建,使用 Object.assign 快速复制
    workInProgress = Object.assign(new FiberNode(), current);
    workInProgress.alternate = current;
    current.alternate = workInProgress;
  } else {
    // 复用现有对象,只更新必要属性
    workInProgress.pendingProps = current.pendingProps;
    workInProgress.type = current.type;
    // ... 更新其他变化的属性
  }

  return workInProgress;
}

这个设计的巧妙之处在于:

  • 第一次更新时快速复制现有 Fiber

  • 后续更新时复用对象,只修改变化的属性

  • 通过 alternate 指针连接两棵树,实现双缓冲


其他库中的应用

Redux 的 immer 库也使用了类似的思想:

// 创建草案对象
const draft = Object.assign(Object.create(base), base);

// 修改草案
draft.user.name = '新名字';

// 最终生成新状态
const nextState = finalizeDraft(draft);

相比深拷贝,这种方法大大减少了内存使用。


实际应用场景

假设我们要管理应用配置:

传统方式(效率低):

const createConfig = () => ({
  apiUrl: 'https://api.example.com',
  timeout: 5000,
  retry: 3,
  headers: { 'Content-Type': 'application/json' }
});

// 每次都要重新初始化所有属性
const configs = Array(100).fill().map(() => createConfig());

使用原型模式:

class ConfigFactory {
  constructor(template) {
    this.template = template;
  }

  clone(overrides = {}) {
    return Object.assign({}, this.template, overrides);
  }
}

const baseConfig = {
  apiUrl: 'https://api.example.com',
  timeout: 5000
};

const configFactory = new ConfigFactory(baseConfig);

// 快速创建
const config1 = configFactory.clone();
const config2 = configFactory.clone();

// 创建变体
const devConfig = configFactory.clone({ apiUrl: 'http://localhost:3000' });

性能对比:

  • 传统方式:100 个配置 × 5 个属性 = 500 次赋值

  • 原型模式:使用 Object.assign 优化,性能提升明显


需要注意的问题

使用 Object.assign 进行浅拷贝时要注意:

const template = {
  user: { name: 'Alice' },
  tags: ['js', 'react']
};

const copy1 = Object.assign({}, template);
const copy2 = Object.assign({}, template);

// 问题:嵌套对象是共享的
copy1.user.name = 'Bob';
console.log(template.user.name);  // 也变成了 'Bob'

解决方案:

// 需要完全独立时使用深拷贝
import _ from 'lodash';

class ConfigFactory {
  constructor(template) {
    this.template = template;
  }

  // 浅拷贝(性能好)
  clone() {
    return Object.assign({}, this.template);
  }

  // 深拷贝(完全独立)
  deepClone() {
    return _.cloneDeep(this.template);
  }
}


实用建议

在实际项目中:

  • 大多数情况使用浅拷贝就够了

  • 只有在需要完全隔离时才使用深拷贝

  • 考虑使用现成的工具库,如 lodash 的 cloneDeep


完整的实用示例

这里是一个可以在项目中直接使用的对象工厂:

class ObjectFactory {
  constructor(template) {
    this.template = template;
  }

  // 创建新对象
  create(overrides = {}) {
    return Object.assign({}, this.template, overrides);
  }

  // 批量创建
  createBatch(count, overridesFn) {
    return Array(count)
      .fill()
      .map((_, i) => this.create(overridesFn?.(i) || {}));
  }

  // 更新模板
  updateTemplate(updates) {
    this.template = Object.assign({}, this.template, updates);
  }
}

// 使用示例
const userFactory = new ObjectFactory({
  id: null,
  name: '',
  role: 'user',
  permissions: []
});

// 批量创建用户
const users = userFactory.createBatch(1000, (i) => ({
  id: i,
  name: `用户${i}`
}));


总结

原型模式在现代前端框架中仍然非常重要:

  • React Fiber 使用它来高效更新界面

  • 状态管理库使用它来优化性能

  • 它让代码更易于维护和扩展

下次当你看到 React 源码中的 Object.assign 或 alternate 时,就知道这是经过深思熟虑的设计选择。这种看似简单的模式,正是支撑现代前端框架高性能的关键技术之一。

理解这些底层原理,不仅能帮助你更好地使用框架,还能在需要自己实现复杂功能时,做出更明智的技术选择。

本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!

链接: https://fly63.com/article/detial/13043

JS的原型链的理解。

JS是一个非常有魅力的语言也是一个比较烦人的语言,主要就是因为他的特殊性灵活性。JS的原型链,需要深刻的研究才能搞懂。不要纠结细节吧。实在不行就按这个死背住,慢慢就理解了。总之吧就是一句话万物皆对象。

JavaScript 原型的深入指南

不学会怎么处理对象,你在 JavaScript 道路就就走不了多远。它们几乎是 JavaScript 编程语言每个方面的基础。事实上,学习如何创建对象可能是你刚开始学习的第一件事。

JavaScript原型深入浅出

不学会怎么处理对象,你在 JavaScript 道路就就走不了多远。它们几乎是 JavaScript 编程语言每个方面的基础。事实上,学习如何创建对象可能是你刚开始学习的第一件事。

javascript中的prototype和__proto__的理解

在工作中有时候会看到prototype和__proto__这两个属性,对这两个属性我一直比较蒙圈,但是我通过查阅相关资料,决定做一下总结加深自己的理解

理解Js原型和原型链

在JS中,函数的本质就是对象,它与其他对象不同的是,创建它的构造函数与创建其他对象的构造函数不一样。那产生函数对象的构造函数是什么呢?是一个叫做Function的特殊函数,通过new Function 产生的对象就是一个函数。

JS原型解析

JS中原型是为了实现代码重用的一种仿类机制,不过它跟类又完全不同。它通过给对象添加原型关系(即给某个对象添加__proto__属性)实现一个关联。把共有的方法和属性放到这个关联上即实现了JS的继承。简单来说就是一种委托机制

js中__proto__与prototype的关系

了解JavaScript中原型以及原型链只需要记住以下2点即可:对象都有__proto__属性,指向构造函数的prototype;构造函数\\函数都有prototype属性,指向构造函数的原型

js原型链的看法

对象:1,函数对象:有function创造出来的函数2,普通对象:除开函数对象之外的对象,都是普通对象**即普通对象obj是构造函数Object的一个实例,因此:

Js原型到原型链

每个函数都有一个 prototype 属性,函数的 prototype 属性指向了一个对象,这个对象正是调用该构造函数而创建的实例的原型,也就是这个例子中的 cat 和dog 的原型。

Js原型对象

这里不介绍原型链。javascript 中有若干长得跟prototype,proto很想的属性/函数,这里简单总结一下他们都是啥,哪个是原型对象,哪个不是。[[Prototype]]这个对象的一个内置槽,对程序员是不可见

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!