前端框架vue:深入分析vue的响应式原理

本文深入浅出地解析了Vue.js作为渐进式框架的特点,解释了其数据绑定和DOM更新机制,探讨了数据响应式绑定原理及其实现细节,包括getter/setter的应用,动态属性的响应式处理,以及异步更新队列的工作机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

vue作为时下最流行的mvvm框架之一,相信不少前端的小伙伴们对vue绝不陌生。vue既小又快,渐进式项目功能,而且较为容易上手, 加上社区的大力开源支持,从一出生就站在巨人肩膀上的vue成为了潮流的明星。

但是,vue是怎么做到了数据的绑定,dom'的更新呢?这方面我想你一定想知道,但是又不愿意去看源码,所以这篇文章,事实上值得你读一读。

首先,我们来看看vue官方文档第一句:

Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的 渐进式框架。

 把渐进式这三个字圈起来,划重点,考试要考。那这时你可能就会有疑问了,什么叫渐进式?什么叫他妈的渐进式?他妈的什么叫他妈的渐进式?其实很简单,渐进式你可以理解为:我想要用vue,但我不必要使用它的全家桶,我想用多少就用多少,比如我有一个react项目,我想在里面用vue搞搞,我可以先用五分之一,觉得不错,再用五分之二,用到满意为止。

这就是渐进式,简而言之,就是可以选择不完全使用它,而是渐渐地使用它,渐渐地深入它。现在你明白了吧?是不是觉得,我靠,原来渐进式是个这么玩意,这跟jquery有毛区别,简直就是low B。事实上这么想虽然有点粗鲁,但是确实很正确的。我现在发现很多前端的新人对jquery嗤之以鼻,反而vue,react什么的奉如祖宗,面试动不动就来一句,“jquery已经过世了,这种mvm框架本来就应该被淘汰掉”,可是年轻人,你知不知道页面任何数据的改变都要操作dom来进行实现的,你之所以在vue里看不到任何有关dom的操作,只是因为vue已经帮你封装了。

那么,新的问题又来了,vue是如何实现数据的更新变化的呢?

来看文档里的这两句话:

把一个普通 Javascript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。

getter/setter 对于用户来说是不可见的,但是基于此内部机制,可以使 Vue 在属性被访问和修改时去触发相应的 getter 和 setter,以实现依赖追踪(dependency-track)和变更通知(change-notification)。

事实上,这两句话已经把vue框架的实现机制说得明明白白。但是非常有必要的,让我们再次来理解一下这两句话。

 

第一句话其实显得并不严谨,我用我自己的话翻译过来是这样的:在vue进行ceated之后,任何定义在data上的变量都将被转化成getter/setter,同时将产生一个监听器监听这些变量的行踪和变化,在数据unpdated之后,先执行setter去更新data里的属性,然后在用getter获取到data里的属性,进而去改变dom上的数据展示。

 

事实上你根本可以把vue当作一个类,只不过他的成员变量都是静态的,并且给他设置了一个监听器,用以监听这些数据的变化以及定义处理事件。有代码来表示他们大致是这样子的:

public class Vue{
    public static type vaiate1;
    public static type vaiate2;
    public static type vaiate3;
    public static type vaiate4;
    ……
    public static type vaiate5;
    private type setter1(){

    }
    private static getter1(){

    }
    private type setter2(){

    }
    private static getter2(){
        
    }
    private type setter3(){

    }
    private static getter3(){
        
    }
    private type setter14(){

    }
    private static getter4(){
        
    }
    ......
    private type settern(){

    }
    private static gettern{
        
    }
    public type Vue{
        
    }
}
Class Observer{
    //监听数据变化
}
class handle{
    //操作数据变化
}

这样看来,大家是不是更一目了然一些呢?

好的,现在大家理解了vue是如何实现数据的更新变化的,让我们进行下一步。在此之前我要声明,上述代码并非vue真正实现代码,我只是为了让我的逻辑更加清晰一点。如果你对此感兴趣,建议去查看vue的源代码。

因为vue基于data的数据响应式绑定的原理,所以当你动态地给vue添加新属性时,vue不能监听到它的变化。比如:

var vm = new Vue({
  data:{
  a:1
  }
})

// `vm.a` 是响应的,因为在created之前就在data里面进行定义了

vm.b = 2
// `vm.b` 是非响应的,因为它虽然是在data上添加的,但是是在mounted之后才进行添加的,没有生成getter和setter

所以在文档里我们可以看到这句话:

由于 Vue 不允许动态添加根级响应式属性,所以你必须在初始化实例前声明根级响应式属性,哪怕只是一个空值:

这时候可能有的小伙伴发脾气了,不行,我就是要在vue的根上加上一个属性,我还要让他是响应式的。言外之意就是想补一班车,让我定义的变量在vue实例化之后仍然可以拥有setter/getter的能力。vue也提供了这类办法,就是你可以在嵌套的对象里面加上一个响应式属性,卧槽,是不是感动得要哭了:

var vm = new Vue({
  data:{
      a:{//此处的a是一个嵌套的对象
            "message":“hello”
        }
  }
})
Vue.set(vm.a, 'messageLate', ",world");//前提,a是一个嵌套的对象

但是有的同学让然不满意,觉得我靠,我真的受不了做a的儿子,我一定要和它平起平坐。我王境泽就是死,也要实现这个东西:

var vm = new Vue({
  data:{
      a:1
  }
})
Vue.set(vm.a, 'b',1);
//然后data就变成了这样
data:{
      a:1,
      b:2
  }

对不起,vue没有给我们这样的权利。但是在项目里面,往往需要给一个数据对象加上一些新的属性,这时你可以新建一个变量,调用类似

this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })

给对象加上一个新的属性,但是缺点是它不是响应级的属性。

最后的最后,就是关于vue的异步更新队列的问题。vue并不是检测到数据的变化就去更新一个数据的,因为考虑到性能的问题,尽量避免不必要的数据更新。所以,数据变化时,vue不会马上更新,而是开启一个队列,把所有的更新都丢进去,在一段周期的去重之后,再对按照队列依次进行数据的更新操作。所以在实际的项目中会发现这样的问题,我修改了数据,但是数据没有发生改变,导致我取的值不正确,dom也没有发生变化。

这时,你很难去更改vue更新队列,只好使用函数Vue.nextTick(callback),这个函数不会影响到更新队列,但是在回调函数里你可以随心所欲地直接控制dom进行数据改变,而不必等到更新队列执行完毕。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值