Vue2中响应式更新原理

35 阅读2分钟

前言

首先知道得知道什么叫数据响应式更新,那就是当我一个数据变动时,页面跟这个数据相关的也会随之变化。

如果用原生代码怎么实现页面更新

<!DOCTYPE html>
<html lang="en"> 
<head>
	<meta charset="utf-8">
	<title>vue2的响应式更新原理</title>

</head>
<body>
	<div class="lastName">     
	</div>
	<div class="firstName">
	</div>
    <script src="index.js"></script>
</body>
</html>
const data = {
  name: "周杰伦",
};

const getLastName = () => {
  const dom = document.querySelector(".lastName");
  if (dom) {
    dom.innerHTML = "姓:" + data.name.substring(0, 1);
  }
};

const getFirsttName = () => {
  const dom = document.querySelector(".firstName");
  if (dom) {
    dom.innerHTML = "名:" + data.name.substring(1, data.name.length);
  }
};

getLastName();
getFirsttName();


image.png

从上面的代码片段可以看出,当我们更改数据想要更新页面时要去调用使用到这个数据的函数,如果我们能够做到更新数据时,自动去调用这些依赖于这个数据的函数,那么我们就能够做到数据的响应式更新,而vue2则是用Object.defineProperty去实现数据响应式更新。

vue2中使用Object.defineProperty实现数据响应式更新的探讨

根据属性描述符的功能,我们可以知道当给一个属性赋值时,会触发属性描述符的set中的回调,而当用到i这个属性的时候会触发属性描述符的get中的回调,我们只要在get中收集依赖这个属性的函数,如何在set中自动调用这些方法,就可以做到数据响应式更新。

let nameVal = data.name;

Object.defineProperty(data, "name", {
  get: function () {
    //收集用到这个属性的函数
    return nameVal;
  },
  set: function (val) {
    //自动调用依赖于这个属性的函数
    nameVal = val;
  }
})

这里我们采用一个数组来存储这些函数,但是有可能存在一个函数中多次使用这个属性,也就是多次触发get,那么这个数组要做去重,然后在set中遍历数组,调用函数即可,代码如下:

let nameVal = data.name;
const funcArr = []

Object.defineProperty(data, "name", {
  get: function () {
    //收集用到这个属性的函数
    if (!funcArr.includes(???)) {
        funcArr.push(???)
    }

    return nameVal;
  },
  set: function (val) {
    nameVal = val;
    //自动调用依赖于这个属性的函数
    for (let i = 0; i < funcArr.length; i++) {
      funcArr[i]() 
    }

  }
})

做到这一步,我们还是不知道到底这个funcArr应该push什么,那么怎么获得呢?其实我们初始化页面的时候,第一次去调这些方法的时候用应该全局变量把它存起来,再调函数,这时候函数会用到属性,触发get就可以拿到函数,调用完后再将全局变量等于null,代码如下

  let nameVal = data.name;
  const funcArr = [];
  Object.defineProperty(data, "name", {
    get: function () {
      // 收集用到这个属性的函数
      if (window.___func &&!funcArr.includes(window.___func)) {
        funcArr.push(window.___func);
      }
      return nameVal;
    },
    set: function (val) {
      nameVal = val;
      // 自动调用依赖于这个属性的函数
      for (let i = 0; i < funcArr.length; i++) {
        funcArr[i]();
      }
    },
  });
  
  const runFunc = (fn) => {
    window.___func = fn;
    fn();
    window.___func = null;
  };

  runFunc(getLastName);
  runFunc(getFirsttName);

结尾

这样就实现了数据响应式更新,让我们来测试一下

屏幕录制-2025-03-10-175001.gif