《深入浅出Vue.js》-Object的变化侦测

什么是变化侦测

Vue.js会自动通过状态生成DOM,并输出到页面显示出来,这个过程叫渲染。在运行时应用内部的状态会不断发生变化,需要需要不停地重新渲染,这时如何确定状态中发生了什么变化?

变化侦测就是用来解决这个问题的,它分为两种类型:一种是"推"(push),一种是"拉"(pull)。Angular和React中的变化侦测都属于"拉",Vue.js的变化侦测属于"推",当状态发生变化时,Vue.js立刻就知道了,而且在一定程度上知道哪些状态变了。

如何追踪变化

在Javascript中,如何侦测一个对象的变化呢?有两种方法,Object.defineProperty和ES6的Proxy。

kotlin 复制代码
funtion defineReactive(data,key,val){
    Object.defineProperty(data,key,{
        enumerable:true,
        configurable:true,
        get:function(){
            return val
        },
        set:function(newVal){
            if(val===newVal){
                return
            }
            val=newVal
        }
    }
}

如何收集依赖

在getter中收集依赖,在setter中触发依赖

依赖收集在哪

收集到Dep中

scss 复制代码
export default class Dep{
    construcor(){
        this.subs=[]
    }
    addSub(sub){
        this.subs.push(sub)
    }
    removeSub(sub){
        remove(this.subs,sub)
    }
    depend(){
        if(window.target){
            this.addSub(window.target)
        }
    }
    notify(){
        const subs=this.subs.slice()
        for(let i=0,l=subs.length;i<l;i++){
            subs[i].update()
        }
    }
}
function remove(arr,item){
    if(arr.length){
        const index=arr.indexOf(item)
        if(index>-1){
            arr.splice(index,1)
        }
    }
}

依赖是谁,收集谁?

什么是Watcher

Watcher是一个中介的角色,数据发生变化时通知它,然后它再通知其他地方。

kotlin 复制代码
export default class Watcher{
    construcgtor(vm,expOrFn,cb){
        this.vm=vm
        this.getter=parsePath(expOrFn)
        this.cb=cb
        this.value=this.get()
    }
    get(){
        window.target=this
        let value=this.getter.call(this.vm,this.vm)
        window.target=undefined
        return value
    }
    update(){
        const oldValue=this.value
        this.value=this.get()
        this.cb.call(this.vm,this.value,oldValue)
    }
}

递归侦测所有key

前面介绍的代码只能侦测数据中的某一个属性,我们希望把数据中的所有属性都侦测到,所以要封装一个Observer类,来将一个数据内的所有属性都转换成getter/setter的形式,然后追踪它们的变化

javascript 复制代码
export class Observer{
    constructor(value){
        this.value=value
        
        if(!Array,isArray(value){
            this.walk(value)
        }
    }
    
    walck(obj){
        const keys=Object.keys(obj)
        for(let i=0;i<keys.length;i++){
            defineReactive(obj,keys[i],obj[keys[i]])
        }
    }
}


funtion defineReactive(data,key,val){
  **  新增 递归子属性**
    if(typeof val ==='objext'){
        new Observer(val)
    }
    let dep=new Dep()
    Object.defineProperty(data,key,{
        enumerable:true,
        configurable:true,
        get:function(){
            **新增 收集依赖**
            dep.depend()
            return val
        },
        set:function(newVal){
            if(val===newVal){
                return
            }
            val=newVal
            **新增 触发依赖**
            dep.notify()
        }
    }
}

Object.defineProperty将对象的key转换成getter/setter的形式来追踪是否被修改,无法追踪新增属性和删除属性,所以Vue.js无法侦测添加删除属性的变化,为了解决这个问题,提供了vm. <math xmlns="http://www.w3.org/1998/Math/MathML"> s e t 和 v m . set和vm. </math>set和vm.delete。

相关推荐
一名爱小惠的前端9 分钟前
🔥🔥🔥430+天,Naive UI Pro 跟大家见面
前端·vue.js·typescript
ZsTs11910 分钟前
还在死记 Vue 2 和 Vue 3 的区别?12个核心模块对比,让你彻底告别面试难题!
vue.js·面试·前端框架
兮漫天23 分钟前
bun + vite7 的结合,孕育的 Robot Admin 【靓仔出道】(十八)
前端·javascript·vue.js
秋天的一阵风1 小时前
用 vue3+ts+pdfjs 做 PDF 预览?这组件难道不值得夸?🚀
前端·javascript·vue.js
前端小巷子2 小时前
Vue渲染器解析
前端·vue.js·面试
做一位快乐的码农2 小时前
基于springboot的理商管理平台设计与实现、java/vue/mvc
java·vue.js·spring boot
大怪v11 小时前
超赞👍!优秀前端佬的电子布洛芬技术网站!
前端·javascript·vue.js
老华带你飞12 小时前
校园交友|基于SprinBoot+vue的校园交友网站(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·校园交友网站
陪我一起学编程15 小时前
创建Vue项目的不同方式及项目规范化配置
前端·javascript·vue.js·git·elementui·axios·企业规范
GISer_Jing15 小时前
Vue Teleport 原理解析与React Portal、 Fragment 组件
前端·vue.js·react.js