Vue 数据响应式

响应式就是一个物体能对外界的刺激做出反应

Vue 的 data 就是响应式

  • const vm = new Vue ({data:{n:0}})
  • 如果修改 vm.n,那么 UI 的 n 就会响应
  • Vue 是通过 Object.defineProperty 来实现数据响应式

Vue 的数据响应式原理依赖 Object.defineProperty,Vue 通过设定对象属性的 setter/getter 方法来监听数据的变化,通过 getter 进行依赖收集,而每个 setter 方法就是一个观察者,在数据变更的时候通知订阅者更新视图。Vue 不能检测到对象属性的添加或删除,解决方法是手动调用 Vue.set 或者 this.$set。

何为响应式网页?

改变窗口大小,网页内容会做出响应,就是响应式网页

getter / setter

get 语法将对象属性绑定到查询该属性时将被调用的函数

当尝试设置属性时,set 语法将对象属性绑定到要调用的函数

let obj = {
  姓: "徐",
  名: "冬冬",
  get 姓名() {
    return this.姓 + this.名;
  },
  set 姓名(xxx){
    this.姓 = xxx[0]
    this.名 = xxx.substring(1)
  },
  age: 18
};

obj3.姓名 = '徐冬冬'

Object.defineProperty

可以给对象添加属性 value

可以给对象添加 getter / setter

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

var _xxx = 0  //用来放 xxx 的值

Object.defineProperty(obj,'xxx',{ // 定义的属性 xxx 是不存在的,如果在后面 return 会死循环
  get(){
    return _xxx
  },
  set(value){
    _xxx = value
  }
})

需求一:用 Object.defineProperty 定义 n

let data = {}

Object.defineProperty(data1, 'n', {
  value: 0
})

需求二:n 不能小于 0 (即 data2.n = -1 应该无效,但 data2.n = 1 有效)

结论:可以给 set 做判断,不满足就不 set

问题:无法阻止别人修改 _n 的值

let data = {}

data._n = 0 // _n 用来存储 n 的值

Object.defineProperty(data, 'n', {
  get(){ // 读属性
    return this._n // 防止没有data,所以推荐使用this隐式传参
  },
  set(value){ // 写属性
    if(value < 0) return
    this._n = value
  }
})

需求三:使用代理

结论:因为只暴露了代理对象,别人无法接触真实的对象

let data = proxy({ data:{n:0} }) // 括号里是匿名对象,无法访问

function proxy({data}){  // 解构赋值,原本应该是(options){const {data} = options}
  const obj = {}
  // 这里的 'n' 写死了,理论上应该遍历 data 的所有 key,这里做了简化
  Object.defineProperty(obj, 'n', { 
    get(){
      return data.n
    },
    set(value){
      if(value<0)return
      data.n = value
    }
  })
  return obj // obj 就是代理
}

 需求四:绕过代理,直接在初始化时申明引用

let myData = {n:0}
let data = proxy({ data:myData }) // 括号里是匿名对象,无法访问

myData.n = -1

需求五:就算用户擅自修改 myData,也要拦截他

let myData = {n:0}
let data = proxy({ data:myData }) // 括号里是匿名对象,无法访问

function proxy({data}){
  let value = data.n
  Object.defineProperty(data, 'n', { // 申明的 n 和之前的 n 重名,会直接用新的覆盖之前的
    get(){
      return value
    },
    set(newValue){
      if(newValue<0)return
      value = newValue
    }
  })
  // 上面几句会监听 data

  const obj = {}
  Object.defineProperty(obj, 'n', {
    get(){
      return data.n
    },
    set(value){
      if(value<0)return//这句话多余了
      data.n = value
    }
  })
  
  return obj // obj 就是代理
}

vm = new Vue({data: myData})

一、会让 vm 成为 myData 的代理 (proxy)
二、会对 myData 的所有属性进行监控
监控是为了防止 myData 的属性变了,vm 不知道
vm 知道属性变了就可以调用 render(data) 
Ul= render(data) UI 就可以自动刷新

什么是代理?

代理是一种设计模式,对 mydata 对象的属性的读写,全权由另一个对象 vm 负责

那么 vm 就是 mydata 的代理

Object.defineProperty 的问题

Object.defineProperty(obj,'n',{...})

必须要有 n 才能监听&代理

如果没有给 n 会有如下问题:

  • Vue 会给出一个警告 
new Vue({
  data: {},
  template: `
    <div>{{n}}</div>
  `
}).$mount("#app");

[Vue warn]: Property or method "n" is not defined on theinstance
but referenced during render. 
  • Vue 只会检查第一层属性,会绕过警告,点击 set b 视图不会显示1,因为 Vue 没法监听一开始不存在的 obj.b
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
    }
  }
}).$mount("#app")

解决方法:

  1. 将 key 都申明好,后面不再加属性
  2. 使用 Vue.set 或 this.$set

Vue.set 和 this.$set 的作用

  • 新增 key
  • 自动创建代理和监听
  • 触发 UI 更新(但不会立刻更新)
this.$set(this.object,'m',100)

数组中新增的 key

  • 可用 set 来新增 key,更新 Ul 
  • Vue 篡改了7个 API 方便对数组进行增删
  • 这7个 API 会自动处理监听和代理,并更新 Ul
  • 数组新增 key 最好通过7个 API
push()
pop()
shift()
unshift()
splice()
sort()
reverse()

  *本文为鲲游北冥的原创文章,著作权归本人和饥人谷所有,转载务必注明来源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值