js实现vue watch效果

67 阅读2分钟

此处使用defineProperty封装一个方法,该方法将实现对对象的监听效果

function disposehData(obj) {
    let reactiveObject = {};
    let handlers = {};
  
    Object.keys(obj).forEach(key => {
      Object.defineProperty(reactiveObject, key, {
        get() {
          if (handlers[key] && handlers[key].mode === 'all') {
            handlers[key].callbacks.forEach(cb => cb(obj[key], obj[key], 'all'));
          }
          return obj[key];
        },
        set(value) {
          let oldVal = obj[key];
          obj[key] = value;
          if (handlers[key] && handlers[key].mode === 'all') {
            handlers[key].callbacks.forEach(cb => cb(oldVal, value,'all'));
          }
          if (handlers[key] && handlers[key].mode === undefined) {
            handlers[key].callbacks.forEach(cb => cb(oldVal, value));
          }
        }
      });
    });
  
    reactiveObject.watch = function(key, cb, mode) {
      if (mode !== undefined && mode !== 'all') {
        throw new Error(`Invalid mode: ${mode}. The mode parameter must be 'all'.`);
      }
      if (!handlers[key]) {
        handlers[key] = { callbacks: [] };
      }
      handlers[key].callbacks.push(cb);
      handlers[key].mode = mode;
    };
  
    return reactiveObject;
  }

调用方法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./2.js"></script>
</head>
<body>
    <script>
        let data = {
            name: '你好',
            age: 18,
            arr:['1','2','3']
        };
        let reactiveData = disposehData(data);
        reactiveData.watch('arr', (oldVal,newVal)=>{
            console.log('变量被改变了', oldVal, newVal);
        });
        reactiveData.arr = 'hello';
    </script>
</body>
</html>

值得注意的是,这里实现的是对整个对象的监听,我们需要把要监听的变量方法到一个对象中,这个vue是类似的。之后我们使用封装好的disposehData方法,将对象进行处理,处理后会增加一个watch方法,只要当监听的数据被修改,就会触发该方法。并且可以接收两个参数,oldVal, newVal,分别对应修改前的值和修改后的值

你也可以传入三个参数,第三个参数为可选参数。没有传入第三个参数,则只有在修改时才触发watch监听。如果传入了第三个参数,那第三个参数必须为‘all’,否则报错。如果第三个参数为all,那么在读取和修改属性值时,都会触发对应的 watch 回调函数,并且都会返回三个参数,第三个参数为字符串all(注意:修改时返回的前两个参数值oldVal和newVal,读取时返回的前两个参数值是一样的)。如果仅仅只是想在读取时触发监听,便可以传入第三参数‘all’,再在你自己的代码体中加上if判断即可

let data = {
            name: '你好',
            age: 18,
            arr:['1','2','3']
        };
        let reactiveData = disposehData(data);
        reactiveData.watch('arr', (oldVal,newVal,mode)=>{
            console.log('变量被改变了', oldVal, newVal,mode);
       },'all');
        // console.log(reactiveObj.age);
        reactiveObj.arr = 30