Vue3.0 响应式原理 (一)

396 阅读4分钟

前几天,回顾整理下关于vue2.0的响应式原理。温故而知新么,那么今天,整理了一下关于vue3.0的响应式原理,利用 JavaScript 来写的。本着尽可能的清晰易懂的原则,所以,可能会分几篇文章来发布。那现在开始上菜。

盘它!

敲黑板,面试必问,你觉得vue2.0 和vue3.0 的区别;或者说,你觉得vue3.0比vue2.0好在哪儿? 如果你只说 vue2.0 是基于Object.definePropery, vue3.0是基于Es6的proxy来架构的,仅此而已的话,那显然是不够的。

  • 首先我们说vue2.0 有哪些缺点或者说不足吧!
  1. 递归,大量的递归,如果数据嵌套层级过多,会特别的消耗内存资源,性能不好
  2. 更改数组,改变数组的长度是无效的
  3. 对象不存在的属性是不能被拦截的

Vue3.0 手撸版开撸

首先来说我们要知道,vue3.0的响应式数据,是怎么搞的

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vue3 原理</title>
</head>
<body>

  <script src="vue.global.js"></script>
  <script>  
    let proxy = Vue.reactive({name: 'bryant'});
    // 副作用 Vue.effect 默认会先执行一次,先打印出 'bryant',而后 proxy.name 的值发生改变,则会在执行一次 打印出 'wzy'
    Vue.effect(()=> {
      console.log(proxy.name)
    })
    proxy.name = 'wzy'
    proxy.name = 'yolin'

  </script>
</body>
</html>

是代码中的这个 Vue.reactive({name: 'bryant'})这个api 是吧!

那么我们创建一个js文件,定义一个这样的函数

// 1.响应式的核心方法
 function reactive(target) {
    // 创建响应式对象
    return createReactiveObject(target)
};
// 2. 创建响应式对象
function createReactiveObject (target) {
   if(!isObject(target)){
    return target; // 判断如果不是对象那就直接把他返回出去;
  };
  // 创建一个观察者, 此处是vue3的核心了,不在使用object.defineProperty 而是是用来自ES6的 proxy;
  // baseHandle 对象中就存放一些,拦截的函数了,比如get set方法等;
  let baseHandle = {
    // 形参中的target 值得是原对象,指的是下面定义的 let data = {name: 'wzy'};,key 指的是你要获取的某个属性
    // receiver 指的是下面的proxy, 也就是data 被代理后的对象
    get(target,key, receiver) {
      console.log('获取');
      // 理论上取值的话是这样的
      // return target[key];
      // 但是在vue3中 proxy 是和一个新的api结合使用的 reflect 反射
      let result = Reflect.get(target,key,receiver);
      return result;
    },
    set(target,key,value,receiver) {
      console.log('设置');
      let flag = Reflect.set(target,key,value,receiver); // 返回的是一个布尔类型,ture && false 告诉设置是否成功
      return flag;
    },
    deleteProperty(target,key) {
     console.log('删除');
     let res = Reflect.deleteProperty(target,key);
     return res;
    }
  };
  let observer = new Proxy(target, baseHandle); 
  return observer;
};
// 3. isObject(); 该函数就是判断是不是对象

function isObject(val) {
  return typeof val === 'object' && val !== null;
};

简单说一下: 首先reactive函数里面return的 createdReactiveObject() 函数,是创建响应式的主体,首先在c函数中先判断传进来的数据是否为对象,如果不是对象,就return出去,是对象的话,则声明一个变量,来接受Proxy的实例,并且把这个对象传给 Proxy();

如果对 Proxy 不是很了解的同学,可以去读一下阮一峰老师的Es6很详细。

那么简单的说下Proxy, 利用Proxy可以对,所要操作的对象,架设一层拦截,在这个拦截中,可以做一些我们想做的事情。那么,它所接受的第一个参数,则是要操作的目标对象数据,那么第二个参数,也是对象类型,那么在这个对象里面则是一些关于可以捕获到的拦截的方法。比如: get set delete 等等。所以第二个参数我们创建一个baseHandle对象用来存放一些方法。

如果上述说的不是很全面,欢迎大神抡锤!

let baseHandle = {
// 那么,在获取值的时候,走的是这个get方法,那么就涉及到传参的一个问题
// target 指的是原对象数据,也就是你要操作的那个对象
// key 指的是,你所要获取的这个对象的属性
// receiver 指的是被代理后的 对象
	get(target,key,receiver){
    	console.log('获取');
      // 理论上取值的话是这样的
      // return target[key];
      // 但是在vue3中 proxy 是和一个新的api结合使用的 reflect 反射
      let result = Reflect.get(target,key,receiver);
      return result;
    },
    set() {
    	console.log('设置');
      	let flag = Reflect.set(target,key,value,receiver); 
      	// 返回的是一个布尔类型,ture && false 告诉设置是否成功
        return flag;
    },
    deleteProperty() {
		console.log('删除');
     	let res = Reflect.deleteProperty(target,key);
     	return res;
    }
};
let observer = new Proxy(target,handle);

return observer; // 改造后的响应式数据得返回出去

那么下面则是一部分 测试数据

let data = {name: 'wzy'};
 // proxy 为经过 reactive();函数处理过后的 代理对象
let proxy = reactive(data);
proxy.name = 'yolin'
console.log(proxy.name)
// 删除
delete proxy.name;
console.log(proxy)

本篇文章说的不是全部vue3.0的响应式原理,只是数据是一层的情况下的。咱们拆开来一步步了解,会很清晰。

但是看得懂不如自己手撸一遍来的实际。撸吧骚年!