Vue-数据响应式

275 阅读1分钟

我们先来看一下 Vue 到底对 data 做了什么

// index.html

<body>
  <div id="app"></div>
  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
</body>

// main.js

const Vue = window.Vue

const myData = {n:0}

console.log(myData) // 第一次打印myData {n:0}

new Vue({
  data: myData,
  template: `
    <div>{{n}}</div>
  `
}).$mount('#app')

setTimeout(() => {
  myData.n += 10
  console.log(myData)  // 第一次打印myData,此时myData已经传给了vue实列了,数据会悄悄发生改变如下图
}, 0)

控制台打印的两次n

  1. 第一次:{n:0}

  2. 第二次:

{__ob__: Observer}
    n: (...)  //看看此时的n,你究竟经历了什么哈哈哈哈哈哈哈
    __ob__: Observer {value: {...}, dep: ce, vmCount: 1}
    get n: f ()
    set n: f (t)
    __proto__: Object

这就是响应式,vue监听了这个数值,vue 会让 vm 成为 myData 的代理,vue 会对 myData 的所有属性进行监控,此时的n: (...)不再是一个单纯的n属性了,或者说根本就不是一个属性了,但是通过这个key(n)读取他,但是他的改变会通过get和set变化。当数值发生改变的时候,vue就重新渲染

vue中data是响应式的

  • const vm=new Vue({data:{n:0}) 创建了一个有data属性的vm实列

  • 如果我们修改了data的数据,那么UI就会响应数据的改变

    原理是Vue 将遍历此对象所有的属性,并使用Object.defineProperty 把这些属性全部转为 getter/setter对data里面的数据n进行监听

   function proxy({data}){
    const obj = {}
    Object.defineProperty(obj,'n',{
        get(){
            return data.n  //获取
        },
        set(newValue){
            data.n = newValue
        }
    })
    return obj   //返回一个代理对象
        }
        let dataProxy = proxy({data:{n:0})
        //便可以dataProxy.n来访问和修改原data中的数据,以便达到监听数据变化的作用
    

这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在属性被访问和修改时通知变更。这里需要注意的是不同浏览器在控制台打印数据对象时对 getter/setter 的格式化并不同,所以建议安装 vue-devtools 来获取对检查数据更加友好的用户界面。

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据属性记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

  • Object.defineProperty(obj,'n',{...}) 有一个n就能监听n
  • 如果页面出现了一个b并对其发生改变,如果Object.defineProperty(obj,'n',{...})没有监听b则不会发生响应

怎样在Vue中完完全全做到数据响应呢

1. 对象

  • 在data中提前申明好所有的key,不给属性就行

  • 使用Vue.set或者vm.$set (this.$set)

    • 自动新增key
    • 自动创建key的监听和代理(如果没有创建过)Object.defineProperty(obj,'n',{...})
    • 响应UI(但不会立刻更新)
var vm = new Vue({
  data:{
    a:1
  }
})

// `vm.a=33` 是响应式的
vm.b = 2    // `vm.b` 是非响应式的

做到b响应呢

可以使用 vm.$set 实例方法,这也是全局 Vue.set 方法的别名:

this.$set(this.someObject,'b',2)

2. 数组

  • 数组的长度是可以一直增加的,数组的下标就是key
  • 我们就没办法提前把数组的key都申明 因为不知道长度具体会是多少
  • 总不能每次数组新增一项就用一次Vue.set()吧?
so,vue中的做法是:变异方法 (mutation method)

Vue 将被侦听的数组的变异方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()