美文网首页
Vue原理「十一」-- 响应式原理 *****

Vue原理「十一」-- 响应式原理 *****

作者: loushumei | 来源:发表于2020-11-17 21:20 被阅读0次

    Vue响应式:

    组件data数据一旦变化,立刻触发视图的更新
    实现数据驱动视图的第一步

    1. 核心API - Object.defineProperty

    基本用法

    const data={}
    var name='zhangsan'
    Object.defineProperty(data,"name",{
        get:function(){
            console.log('get')
            return name
        },
        set:function(newVal){
            console.log('set')
            name = newVal
        }
    })
    
    console.log(data.name)
    data.name="lisi"
    console.log(data.name)
    // get zhangsan
    // set
    // get lisi
    

    在基本使用中,不能监听复杂对象和数组

    2. 响应式需要监听对象(深度监听)、监听数组

    实现思路:
    • 定义触发视图更新的方法:updateView()
    • 重新定义数组原型 -> 实现调用数组方法时触发视图更新

    创建新新对象,原型指向oldArrayProperty,再扩展新新的方法,不会影响原型,不污染全局变量.
    在新定义的数组原型用添加 updateView()

    • 深度监听对象方法 observer()

    判断是否是数组或对象类型,不是的话,直接返回
    若是数组类型,指向自定义的数组原型
    重新定义各个数据

    • 自定义监听方法 defineReactive()

    在 Object.defineProperty 中的set方法中添加 updateView()方法触发视图更新;
    在调用set方法前,定义深度监听方法 observer() 实现

    • 监听

    准备一个对象数据
    调用 深度监听对象方法 observer()
    遍历对象每个属性,调用自定义监听方法 defineReactive()实现监听

    实现代码:

    // 触发更新视图
    function updateView() {
        console.log("视图更新")
    }
    
    //重新定义数组原型
    const oldArrayProperty=Array.prototype;
    // 创建新新对象,原型指向oldArrayProperty,再扩展新新的方法,不会影响原型
    const arrProto = Object.create(oldArrayProperty);
    ['push','pop','shift','unshift','splice'].forEach(methodName=>{
        arrProto[methodName] = function () {
            updateView()//触发视图更新
            oldArrayProperty[methodName].call(this,...arguments)//
            // Array.prototype.push.call(this,...arguments) 真正数组原型的处理
        }
    });
    
    
    // 定义自定义属性,监听起来
    function defineReactive(target, key, value) {
        //深度监听
        observer(value)
    
        Object.defineProperty(target, key,{
            get(){
                return value;
            },
            set(newValue){
                if (value != newValue) {
                    // 设置新值时-监听
                    observer(value) 
                    // 注意,value一直在闭包中,此处设置完之后,再get时也是会获取最新的值。
                    value = newValue
                    // 触发更新视图
                    updateView()
                }
            }
        })
    }
    
    
    
    // 监听对象属性
    function observer(target) {
        if (typeof target != 'object' || target === null) {
            // 不是对象或者数组
            return target
        }
        if (Array.isArray(target)) {
            // 将数组类型数据 指向 自定义的数组原型 
            target.__proto__ = arrProto
        }
        // 重新定义各个属性
        for (const key in target) {
            defineReactive(target, key, target[key])
        }
    }
    
    // 准备数据
    const data = {
        name: "张三",
        age: 20,
        info:{
            address:"北京"  //深度监听
        },
        nums:[10,20,30]
    }
    
    // 监听数据
    observer(data)
    
    // 测试
    // data.name = '李四'
    // data.age = 22
    // data.info.address = "上海" //深度监听
    
    // data.x = '100' //新增属性 监听不到--所以有vue.set
    // delete data.name //删除属性,监听不到 --所以有vue.delete
    
    
    data.nums.push(4) //监听数组
    
    

    3. Object.defineProperty的一些缺点

    • 深度监听,需要递归到底,一次性计算量大
    • 无法监听新增属性/删除属性【解决办法:Vue.set Vue delete,新增和删除后通知视图更新】
    • 无法原生监听数组,需要特殊处理

    vue 3.0 启用 Proxy

    • Proxy有兼容性问题
    • Proxy兼容性问题,无法polyfill

    相关文章

      网友评论

          本文标题:Vue原理「十一」-- 响应式原理 *****

          本文链接:https://www.haomeiwen.com/subject/bnmfiktx.html