美文网首页
Vue 的数据响应式

Vue 的数据响应式

作者: 我是Msorry | 来源:发表于2021-01-03 08:24 被阅读0次

    深入响应式原理文档

    从下面这个小例子带你走进Vue数据响应式

    const myData = {
      n: 0
    }
     console.log(myData) 
    
    new Vue({
      data: myData,
      template: `
        <div>{{n}}</div>
      `
    }).$mount("#app");
    
    setTimeout(()=>{
      myData.n += 10
      console.log(myData) 
    },3000)
    
    

    Object {n: 0}
    Object {n: (...)}

    一个对象作为数据传递给Vue后,变得不一样了,究竟发生了什么?

    const vm = new Vue({data:myData})做了什么?

    1.vm成为myData的代理(proxy),如果没有vmthis会代指这个对象
    2.对myData的所有属性进行监听,一旦myData的属性发生变化,vm立即知道
    3.属性变化后,调用render(data),UI = render(data)

    const data = {a:1,b:2}
    
    function proxy({data}) {
      const obj = {}
      for (let key in data) {
        //监听data
        let value = data[key]
        Object.defineProperty(data, key, {
            get() {
                return value
              },
              set(v) {
                value = v
              }
          })
          // obj 就是代理
        Object.defineProperty(obj, key, {
          get() {
              return data[key]
            },
            set(value) {
              data.n = value
            }
        })
    
      }
      return obj 
    }
    console.log(data)
    let vm = proxy({data:data})
    console.log(data)
    //在外部修改数据,看vm是否也跟着发生变化
    data.a = 2
    console.log(vm.a)
    

    Object {a: 1,b: 2}
    Object {a: (...),b: (...)}
    2

    同理,Vue对methods和computed也有处理

    数据响应式

    当数据发生改变后,Vue会通知到使用该数据的代码,自动视图更新

    Vue只监听第一层属性的变化,不监听嵌套属性,否则效率低

    因为obj.b没有在data中没有,所以不会被监听,undefined状态的数据Vue不会渲染到页面

    new Vue({
      data: {
        obj: {
          a: 0 // obj.a 会被 Vue 监听 & 代理
        }
      },
      template: `
        <div>
          {{obj.b}}
          <button @click="setB">set b</button>
        </div>
      `,
      methods: {
        setB() {
          this.obj.b = 1; //请问,页面中会显示 1 吗?
        }
      }
    }).$mount("#app");
    

    解决办法

    1.用.set方法

    Vue.set(this.obj,"b",1)
    this.$set(this.obj,"b",1)
    console.log(Vue.set===this.$set)//true,这两种方式一模一样
    

    Vue.set做了两件事:新增属性,并自动地对这个属性创建代理和监听
    Vue发现这个属性从undefined的状态变成1,触发render,更新页面

    2.提前在data中声明好,初始值设为undefined


    但是,当为数组时,无法提前声明,每次用Vue.set很麻烦

    new Vue({
      data: {
        array: ["a", "b", "c"]
      },
      template: `
        <div>
          {{array}}
          <button @click="setD">set d</button>
        </div>
      `,
      methods: {
        setD() {
          this.array[3] = "d"; //请问,页面中会显示 'd' 吗?
          // 等下,你为什么不用 this.array.push('d')
        }
      }
    }).$mount("#app");
    

    解决方法

    this.array.push(value)
    console.log(this.array)
    

    通过控制台可以看出,push方法变了,Vue篡改了数组的API
    变异方法文档

    Vue的变异方法实现思路(并不是源码)

    Vue会给这个数组加一层原型,对于数组的变化,自动Vue.set
    篡改push方法,对于Array的变化通过Vue.set通知给Vue

    class VueArray extends Array{
      push(...args){
          const oldLength = this.length //this 当前数组
          super.push(...args)//调用父类的push
          for(let i = oldLength;i < this.length;i++){
            Vue.set(this,i,this[i])
          }
      }
    }
    
    const vueArrayPrototype = { 
      push: function(){ 
            return Array.prototype.push.apply(this, arguments) 
      } 
    } 
    vueArrayPrototype.__proto__ = Array.prototype //不是标准属性
    
    const array = Object.create(vueArrayPrototype) array.push(1)
    

    总结

    1. const vm = new Vue({data:myData}) 这个语句中,this就是vmvmmyData的代理,所以this.n能够读取
    2. Vue没有办法监听和代理新的Key
    3. 要使用set来新增Key,创建代理和监听,更新UI
    4. 最好提前把属性都写出来,不要新增Key
    5. 但是数组做不到「不新增Key」
    6. 数组也可以用set来新增Key,创建代理和监听,更新UI
    7. Vue为解决这个困扰,篡改7个数组API
    8. 这7个API会自动处理监听和代理,并更新UI

    https://www.jianshu.com/p/4dff7c2cdaaa

    相关文章

      网友评论

          本文标题:Vue 的数据响应式

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