Vue的响应式原理

在理解Vue响应式原理之前,可以想想这几个问题,数据改变后,Vue是如何监听、修改、渲染页面的。

在此之前,我们先来说说Vue的双向数据绑定原理吧,或许对你有点启发

  • 实现一个监听器Observer:对数据对象进行遍历,包括子属性对象的属性,利用Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发setter,那么就监听到了数据变化。
  • 实现一个解析器 Compile:解析Vue模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。
  • 实现一个订阅者Watcher:Watcher 订阅者是 Observer 和 Compile之间通信的桥梁,主要的任务是订阅Observer中的属性值变化的消息,当收到属性值变化的消息时,触发解析器Compile中对应的更新函数
  • 实现一个订阅器 Dep:订阅器采用发布-订阅设计模式,用来收集订阅者Wahcher,对监听器Observer和订阅者Watcher进行统一管理

Vue的响应式 就是当数据变化后,可以监听到变化,并且将数据修改成新的值,重新渲染页面

一个简单的例子:

 <div id="app">
    {{ msg }}
  </div>
 const vm = new Vue({
    el: '#app',
    data: {
      msg: '风姿绰约、花枝招展'
    }
  });
  vm.msg = '手如柔荑、肤如凝脂'; // 见证奇迹的时刻,页面变化啦
  • 问:为什么data会直接出现在vm实例对象中咧?

  • 答:当创建vue实例时,vue会将data中的成员代理给vue实例,目的是为了实现响应式,监控数据变化,执行某个监听函数

那么如何实现响应式呢?

Vue响应式原理 的核心主要是使用Object.defineProperty()get()set()方法监听数据的变化,不需要手动操作dom元素
Object.defineProperty()是javascript的标准内置对象,它的作用就是直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象

思路:主要是监听数组和对象的的变化,如果是对象,就用defineProperty进行监听,
如果是数组就用数组的变异方法,并改写原型上的方法

监听对象

 // 监听对象
        function defineReactive(data, key, value) {
            observe(value)
            Object.defineProperty(data, key, {
                get(value) {
                    console.log('读')
                },
                set(newVal) {
                    console.log('写')
                    // 如果修改的值和原来的值相同,就不修改,可以提升性能
                    if (value == newVal) {
                        return
                    }
                    value = newVal
                    render()
                }
            })
        }  
        // 观察者
        function observe(data) {
        	// 观察数组
        	if (Array.isArray(data)) {
        		data.__proto__ = arrayMethods;
        	}
        	// 观察对象
        	if (typeof data === 'object') {
        		for (let key in data) {
        			defineReactive(data, key, data[key])
                }
            }
        }
        function render() {
            console.log('页面渲染了')
        } 
        observe(data)

监听数组
监听数组,用数组的变异方法,并改写原型上的方法,保存原来的原型
数组的变异方法:'push', 'pop', 'shift', 'unshift', 'sort', 'splice', 'reverse'
Array.prototype不是指单个数组,而是指Array()对象本身。prototype函数允许您向Array()对象添加新属性和方法

 		const arrayProto = Array.prototype;
        // 克隆一个新的原型
        const arrayMethods = Object.create(arrayProto)
        ['push', 'pop', 'shift', 'unshift', 'sort', 'splice', 'reverse'].forEach(method => {
            // 改变原型上的this指向,将this指向克隆的原型
            arrayMethods[method] = function () {
                arrayProto[method].call(this, ...arguments)
                render()
            }
        })

Vue的实例方法$set$delete的实现原理也是利用Object.defineProperty()实现的

function $set(data, key, value) {
    // 观察数组
    if (Array.isArray(data)) {
        data.splice(key, 1, value) //修改
        // data.splice(key, 0, value)  // 新增
    }
    // 观察对象
    defineReactive(data, key, value)
    render()
    return value
}

function $delete(data, key, value) {
    if (Array.isArray(data)) {
        data.splice(key, 1)
    }
    // 观察对象
    delete data[key]
    render()
}

利用Object.defineProperty()实现响应式的缺点

  • 天生就需要进行递归
  • 监听不到数组不存在的索引的改变
  • 监听不到数组长度的改变
  • 监听不到对象的增删

不用Object.defineProperty()监听数组的原因,数组中的数据量一般都比较大,Object.defineProperty()天生就需要递归进行遍历,会耗费性能

Vue3.0使用proxy代替Object.defineProperty()实现响应式,就不会存在这些问题了,之后会更新使用proxy的实现方法,并介绍其优缺点哈~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值