Vue学习笔记 - 数据响应式

166 阅读2分钟

深入理解 options.data

前置1:ES 6的getter和setter

getter、setter用于对属性的读写进行监控

let obj = {
    姓:'高',
    名: '圆圆',
    get 姓名(){
        return this.姓 + this.名
    },
    set 姓名(name){
        this.姓 = name[0]
        this.名 = name.slice(1)
    }
}


obj.姓名  //'高圆圆'

obj.姓名 = '高媛媛'
obj.姓名 //'高媛媛'

姓名属性实际是不存在的(可视为虚拟对象),它是计算出来的属性

  • obj.姓名 --> 获取get 姓名方法的返回值
  • obj.姓名 = 'xxx' --> 调用obj.set 姓名('xxx')

前置2:Object.defineProperty

作用:给对象新增属性,也可以添加getter、setter属性

let obj = {}

Object.defineProperty(obj, 'x', {value: 1}) //添加属性'x'

Object.defineProperty(obj, 'y', {
    get(){...},
    set(value){...}
})

// 添加虚拟的属性'y'

案例:

设计模式--代理,用新对象newData代理原对象oldData,以实现用newData.n来操作oldData.n

let oldData = {n:0}
let newData = proxy({data: oldData})

function proxy({data}){ //解构赋值
  let value = data.n
  Object.defineProperty(data, 'n', {
    get(){
      return value
    },
    set(newValue){
      value = newValue
    }
  })
  //监听 oldData,防止 oldData 直接被篡改

  let obj = {}
  Object.defineProperty(obj, 'n', {
    get(){
      return data.n 
    },
    set(newValue){
      data.n = newValue
    }
  })
  //令 newData 代理 oldData
  return obj
}

newData.n ----操作----> oldData.n ----操作----> value

无论直接操作oldData.n还是操作newData.nnewData都可以收到通知

Vue 对 data 做了什么

Vue 的 data 是响应式的

const vm = new Vue({data: myData})后,myData 被改造:

  • 令 vm 成为 myData 的代理
  • 对 myData 的所有属性进行监控(即变为getter/setter)

目的: 无论直接修改myData.n还是修改vm.n,vm 都会收到通知,然后调用触发重新渲染

Vue 对 methods 和 computed 也有类似的处理

data 的 bug

Vue 通过Object.defineProperty(obj, 'n', {...})实现数据的响应式,data有属性'n'才可以监听&代理data.n,若无属性'n',则会产生问题

data.n

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

data.n(或有但为undefined)但被引用,不会显示,且报警告

data.obj,但无data.obj.n

const vm = new Vue({
  data: {
    obj: {
      a: 0 // obj.a 会被 Vue 监听 & 代理
    }
  },
  template: `
    <div>
      {{obj.n}}
      <button @click="setN">set n</button>
    </div>
  `,
  methods: {
    setN() {
      this.obj.n = 1;
    }
  }
}).$mount("#app");

data.obj中一开始没有'n',此后给vm添加属性vm.obj.n = 1并不会出现在页面中,因为vm没有监听&代理data.obj.n

解决:

1.在一开始定义好data.obj.n = undefined推荐

2.若后续添加属性,只能用Vue.setthis.$set(两者等价),它可以为新增的属性添加代理&监听

methods: {
    setN(){
        Vue.set(this.obj, 'n', 1)
        //或
        this.$set(this.obj, 'n', 1)
    }
}

给 data 中的数组添加元素

直接为数组添加元素,也不会渲染到页面

new Vue({
  data: {
    array: ["a", "b", "c"]
  },
  template: `
    <div>
      {{array}}
      <button @click="setD">set d</button>
    </div>
  `,
  methods: {
    setD() {
      this.array[3] = "d"; 
    }
  }
}).$mount("#app");

同对象一样,用Vue.setvm.$set新增元素,也可以触发视图更新

this.array.push('d'),也能更新

经过 Vue 的数组,其方法会被篡改,称为变异方法,这些方法会自动为新增元素添加代理&监听,更新视图

数组变异方法有7个:push pop unshift shift splice sort reverse

总是推荐使用变异方法对数组进行增删