使用 ES6 Proxy 实现极简响应式系统 -1

1. Proxy 基础概念

1.1 什么是 Proxy

  • ES6 新增的元编程特性

  • 用于创建对象的代理,拦截并自定义对象的基本操作

1.2 核心语法

const proxy = new Proxy(target, handler)
  • target: 要代理的目标对象

  • handler: 包含拦截器(traps)的对象

1.3 常用拦截器

  • get(target, property): 拦截属性读取

  • set(target, property, value): 拦截属性设置

  • has(target, property): 拦截 in 操作符

2. 实现目标

  • 创建响应式对象,当属性变化时自动触发回调

  • 支持功能:

    • 监听对象属性变化

    • 支持嵌套对象监听

    • 变化时触发回调函数

3. 实现步骤详解

3.1 定义响应式系统的基本结构

我们需要一个函数 reactive,它接受一个对象作为参数,并返回一个响应式代理对象。同时,我们需要一个机制来存储和触发回调函数。
function reactive(obj, callback) {
  const handler = {
    get(target, prop, receiver) {
      // 处理嵌套对象
      if (typeof target[prop] === 'object' && target[prop] !== null) {
        return reactive(target[prop], callback);
      }
      return Reflect.get(target, prop, receiver);
    },
    set(target, prop, value, receiver) {
      callback(prop, value); // 触发回调
      return Reflect.set(target, prop, value, receiver);
    }
  };
  return new Proxy(obj, handler);
}

3.2 依赖收集与触发

// 创建响应式对象
const data = reactive(
  { count: 0, user: { name: 'John' } },
  (key, value) => console.log(`[Update] ${key} => ${value}`)
)

// 测试
data.count = 1          // 输出: [Update] count => 1
data.user.name = 'Jane' // 输出: [Update] name => Jane

4. 完整代码示例

javascript
复制
const createReactive = (initialData, callback) => {
  const handler = {
    get(target, key) {
      const value = target[key]
      if (typeof value === 'object' && value !== null) {
        return new Proxy(value, handler)
      }
      return value
    },
    set(target, key, value) {
      const oldValue = target[key]
      target[key] = value
      if (oldValue !== value) {
        callback({
          target,
          key,
          oldValue,
          newValue: value
        })
      }
      return true
    }
  }

  return new Proxy(initialData, handler)
}

// 使用示例
const reactiveData = createReactive(
  { score: 100, items: [] },
  ({ key, newValue }) => console.log(`${key} changed to ${newValue}`)
)

reactiveData.score = 90    // 输出: score changed to 90
// 注意:数组方法需要特殊处理
reactiveData.items.push(1); // 输出: 0 changed to 1
reactiveData.items.push(2, 3); // 输出: 1 changed to 2  2 changed to 3

5. 优缺点与适用场景

优点:

  • 自动深度响应

  • 支持动态新增属性

  • 原生支持数组变化检测

缺点:

  • 无法检测直接操作数组索引的变化(需手动处理)

  • 兼容性问题(不支持 IE11)

  • 性能开销比 Object.defineProperty 大

适用场景:

  • 中小型应用的响应式需求

  • 需要动态新增属性的场景

  • 需要深度监听嵌套对象的场景


6. 注意事项

  1. 性能优化:避免创建过多 Proxy 实例

  2. 数组处理:需拦截数组方法(push/pop 等)

  3. 循环引用:注意避免对象循环引用导致内存泄漏

  4. 浏览器兼容:需要 Babel 转译或 polyfill


扩展思考

  1. 如何实现自动依赖收集?

  2. 如何与虚拟 DOM 结合实现视图更新?

  3. 如何优化深层嵌套对象的性能?

posted @ 2025-02-08 15:45  Yang9710  阅读(50)  评论(0)    收藏  举报