Vue2.0和3.0的响应式原理

vue3.0在数据的响应式上无疑做了很大的优化,性能得到大大提升。虽然vue3.0还没有正式发布,但是各大公众号已经闹得不亦说乎。

我们都知道vue2的响应式原理的实现依赖的是Object.defineProperty这个API,用它来为要实现响应式的数据设置getter和setter方法。
如果数据多时就要循环遍历。循环遍历就会降低性能。
所以vue3.0就摒弃了这个方法,改为使用ES6中的proxy来实现。而这个方法没有shim,不兼容低版本浏览器。所以vue3.0彻底摒弃了它们。

下面我就用Vue2.0和Vue3.0分别解释一下Vue的响应式原理
在这里插入图片描述

  • Vue 2.0使用的是Object.defineProperty
  1. 假设我们修改message的数据,Vue内部是如何监听message数据?
  2. 当数据发生改变,Vue是如何知道要通知哪些“人”,页面发生刷新?
<div id="app">
  <h2>{{message}}</h2>  <!--张三-->
  <h2>{{message}}</h2>  <!--李四-->
  <h2>{{message}}</h2>  <!--王五-->

  <h2>{{name}}</h2>
</div>


//这里的obj对象是data对象传到了Vue里面
const obj = {
    message: '你好啊',
    name: 'super'
  }
Object.keys(obj).forEach(key => {
    let value = obj[key]

    Object.defineProperty(obj, key, {
      set(newValue) {
        console.log('监听' + key + '改变');
        value = newValue
      },
      get() {
      //2.2要获取message的值,就要调用一下get,一旦发现newValue的值发生改变,
      //就要通知一下当前属性
        console.log('获取' + key + '对应的值');
        return value
      }
    })
  })

set里解析html代码,获取到哪些“人”使用了这个属性。在get里获取到message。要获取message的值,就要调用一次get,一旦发现newValue的值发生改变,就要通知一下(这里通知张三或者李四你该改变了)
那么我们就可以只用订阅者模式,让这里的张三/李四/王五订阅set里面属性的改变

 // 发布者订阅模式
 //订阅者
  class Dep {
    constructor() {
      this.subs = []
    }
    addSub(watcher){
      this.subs.push(watcher)
    }
    notify(){
      this.subs.forEach(item =>{
        item.update()
      })
    }
  }

//发布者
  class Watcher{
    constructor(name) {
      this.name = name
    }
    update(){
      console.log(this.name + '发生update');
    }
  }

  const dep = new Dep()
  const w1 = new Watcher('张三')
  dep.addSub(w1)
  dep.notify()

首先我们新建一个Dep的类,构造器里面的subs数组用来记录“谁”要记录我们的属性,此时dep对象就可以用subs数组就所有的订阅者。这里订阅者我起名字为张三 问题又来了,我们怎么知道所有的订阅者在哪里呢?
我们可以添加一个addSub方法,当通知到张三的时候,张三都会自己调用一下update,把界面更新一下。watcher就是订阅者,将watcher追加到addSub方法里面。
随后我们再一次创建一个叫做Watcher的类,此时张三调用了,就创建一个w1对象,w1也就是watcher,将它追击到addSub数组里面。
如果有一天newValue的值发生改变,我们需要新添加一个notify方法,循环所有的订阅者,让订阅者调用update。意味着我现在一旦调用dep.notify(),所有的订阅者发生改变的时候都能获取到改变。

在这里插入图片描述
我们用最简单的图解的方式再来解释一遍
在这里插入图片描述

  • Vue 3.0使用的是ES6原生的Proxy

proxy翻译过来的意思就是”代理“,ES6对Proxy的定位就是target对象(原对象)的基础上通过handler增加一层”拦截“,返回一个新的代理对象,之后所有在Proxy中被拦截的属性,都可以定制化一些新的流程在上面,先看一个最简单的例子。

const obj = {
    message: '你好啊',
    name: 'super'
  }

  let vm = new Proxy(obj, {
    get(target, key) {
      //第一个参数是obj对象,第二个参数是obj中的key
      console.log('获取' + key + '对应的值');
      return obj[key];
    },
    set(target, key, newValue) {
      //第一个参数是obj对象,第二个参数是obj中的key,第三个参数是要设置的值
      console.log('监听' + key + '改变');
      if (target[key] === newValue) {
        return
      }
      target[key] = newValue
      document.getElementById('app').textContent = target[key]
    }
  })
  //可以进行一个测试
  vm.name = '嘻嘻嘻'

猜你喜欢

转载自blog.csdn.net/qq_45814762/article/details/107446540