vue2的响应式数据原理

简介: `Object.defineProperty` 是 Vue 2 实现响应式数据的核心方法,通过定义 getter 和 setter 来追踪属性的变化。当访问或修改属性时,会触发相应的函数,从而实现数据的动态更新。本文介绍了该方法的基本用法、响应式原理及其简单实现,展示了如何通过监听属性变化来自动更新视图,体现了前端框架设计中的巧妙之处。

1.介绍Object.defineProperty

    vue2的响应式数据是依靠Object.defineProperty这个方法,用法如下:

const obj = {
username: "zz",
age: "zz",
};

// Object.defineProperty(对象名,对象键名,配置对象)
Object.defineProperty(obj, "username", {
  get() {
    // 调用obj.username 会运行这个函数
  },
  set() {
    // 给obj.username赋值会运行这个函数
  },
});

其中配置对象参数有多个可选值:

writable: 是否可以重写该属性

value: 当前值

get: 读取该值时会运行的函数

set: 给该值赋值时会运行的函数

enumerable: 是否可被迭代

configurable: 是否可以再次修改配置项

2.响应式原理

    Object.defineProperty方法的配置对象里面有一个函数get,每次我们访问某个变量时就会通过get函数来获得。set函数在我们给某个变量赋值时也会触发这个函数。基于这套逻辑,我们就可以想到在get方法里去保存访问过这个变量的方法,在set方法里去触发访问这个变量的方法实现数据的更新,思路有了,接下来就是实现。

3.响应式原理的实现

    假设页面上只有一个p元素,代码如下:

const pEl = document.querySelector("p");

const data = {
  username: "zr",
};
pEl.innerHTML = data.username;

    页面显示:



    接下来我们基于上面的逻辑改造一下代码。

// 创建响应式数据对象
function observer(targetObj, key) {
// 保存值,不然get方法会无限递归
const val = targetObj[key];
Object.defineProperty(targetObj, key, {
get() {
return val;
},
set(newVal) {
val = newVal;
},
});
}

const data = {
  username: "zr",
};

observer(data, "username");

function setUserNameVal() {
  pEl.innerHTML = data.username;
}
setUserNameVal();

现在基本的逻辑框架已经出来了,现在的问题是我们无法知道当前是哪个方法在访问变量。解决办法就是创建一个全局变量,通过一个函数来控制这个变量,这个变量就是当前调用变量的方法。再在get方法里面保存这个方法到数组,在set方法里触发这个数组里所有的函数。下面是代码:

let currentFn = null;

// 更新数据函数
function update(fn) {
  // 把当前运行的函数保存到全局变量中
  currentFn = fn;
  fn();
  // 收集完依赖后清空
  currentFn = null;
}
// 创建响应式数据对象
function observer(targetObj, key) {
  // 保存值,不然get方法会无限递归
  let val = targetObj[key];
  const fnList = new Set();
  Object.defineProperty(targetObj, key, {
    get() {
      // 如果是update函数访问则添加到依赖列表中,否则就是更新数据所触发的。
      currentFn && fnList.add(currentFn);
      return val;
    },
    set(newVal) {
      val = newVal;
      // 运行所有依赖该数据的函数 更新数据
      fnList.forEach((fn) => {
        fn();
      });
    },
  });
}

const data = {
  username: "zr",
};

observer(data, "username");

function setUserNameVal() {
  pEl.innerHTML = data.username;
}
update(setUserNameVal);

4.结语

    尤大大还得是尤大大,光一个响应式就蕴含了这么多开发技巧和思想在里面,更别说整个响应式系统和别的功能了,尤大大牛逼!

目录
相关文章
|
缓存 JavaScript 算法
vue2和vue3之间diff算法的差异
vue2和vue3之间diff算法的差异
|
前端开发 Serverless UED
iconfont渐变色实现方案总结
iconfont渐变色实现方案总结
931 0
|
JavaScript
在 Vue 中处理组件选项与 Mixin 选项冲突的详细解决方案
【10月更文挑战第18天】通过以上的分析和探讨,相信你对在 Vue 中使用 Mixin 时遇到组件选项与 Mixin 选项冲突的解决方法有了更深入的理解。在实际开发中,要根据具体情况灵活选择合适的解决方案,以确保代码的质量和可维护性。
462 7
|
JavaScript
在vue中,在哪个生命周期内调用异步请求?
在vue中,在哪个生命周期内调用异步请求?
588 0
|
6月前
|
调度 开发工具 Android开发
【HarmonyOS Next】鸿蒙应用进程和线程详解
进程的定义: 进程是系统进行资源分配的基本单位,是操作系统结构的基础。 在鸿蒙系统中,一个应用下会有三类进程:
232 0
|
6月前
|
前端开发 JavaScript 索引
前端性能优化:虚拟滚动技术原理与实战
前端性能优化:虚拟滚动技术原理与实战
839 80
|
8月前
|
Web App开发 前端开发 Ubuntu
WebRTC项目中的janus安装和编译教程
但是请记住,这只是开始。Janus只是一个服务器,你还需要为其配置前端界面,通过Janus与前端进行通信。你可能需要看一些关于WebRTC,JavaScript,HTTP,WebSocket等知识,去完善你的WebRTC项目。像搭积木一样,一块块的知识是你的乐高,用心去搭建,你会创造出美妙的作品。
367 12
|
设计模式 Go
Go语言事件系统设计解析:发布-订阅模式实战
Go语言事件系统设计解析:发布-订阅模式实战
839 0
|
前端开发 UED
用CSS3实现惊艳的过渡动画(建议收藏)
用CSS3实现惊艳的过渡动画(建议收藏)
|
JavaScript
vue 代码高亮 highlight.js vue-highlightjs
vue 代码高亮 highlight.js vue-highlightjs
327 0