vue 不能响应set结构增加数据_Vue学习之路-vue数据响应式

vue 不能响应set结构增加数据_Vue学习之路-vue数据响应式_第1张图片

前言

我们在使用vue的时候,data属性几乎是必不可少的,vue的data属性也十分方便了为我们提供了暂存数据,数据修改,数据渲染的功能。

但是为什么在data处设置的数据,我们可以直接在模板处进行使用并且实时渲染呢

vue到底对data做了什么?


目录

  1. getter和setter
  2. Object.defineProperty
  3. 数据响应式

getter和setter

首先我们要了解getter和setter

我们先抛出一个需求

有这样的一个对象

let obj0={
  姓:‘高’,
  名:‘圆圆’,
  age:18
}

需求一,得到姓名

很简单,我们直接使用姓名函数返回姓名就好了

let obj1={
  姓:‘高’,
  名:‘圆圆’,
  age:18,
  姓名(){
    return this.姓+this.名  
  }
}
console.log("需求一",obj1.姓名())

需求二:这时候来了个杠精,说姓名后面的括号能去掉吗,我看的烦气

如何去掉括号呢,使用get

let obj2={
  姓:‘高’,
  名:‘圆圆’,
  age:18,
  get 姓名(){
    return this.姓+this.名  
  }
}
console.log("需求二",obj1.姓名)

get,说白了就是可以不加括号的函数

需求三:杠精又说,你这个只能查不能改啊,能不能改名字?

好办,有get就有set嘛

let obj3={
  姓:‘高’,
  名:‘圆圆’,
  age:18,
  get 姓名(){
    return this.姓+this.名  
  },
  set 姓名(xxx){
    this.姓=xxx[0]
    this.名=xxx.substring(1)
  }
}
obj3.姓名=‘刘诗诗’
console.log("需求三",姓${obj3.姓},名${obj3.名})

set就是这样使用的,用=xxx来触发set函数


Object.defineProperty

  • 说完了getter和setter,我们了解了,要直接获取和设置数据,可以使用getter和setter
  • 但是getter和setter的使用前提都是,对象上本来就存在这个属性,你才能读和改。要是对象上压根没有这个属性呢?

Object.defineProperty的作用就是在一个对象上定义一个新的属性,或者修改一个已存在的属性

代码演示,我们就在obj3这个对象上新增了属性xxx

let _xxx=0
Object.defineProperty(obj3,'xxx',{
  get(){
      return _xxx
  },
  set(value){
      _xxx=value
  }
})

需求一:用Object.defineProperty()定义n的值,且n不能小于0

//我们有对象
let data2={}
data2._n=0  //_n 用来偷偷存储n的值
Object.defineProperty(data2,'n',{
  get(){
    return this._n
  },
  set(value){
    if(value<0)return 
    this._n=value 
  }
})
console.log(`需求二:${data2.n}`)
data2.n=-1
console.log(`需求二:${data2.n}  设置为-1 失败`)
data2.n=1
console.log(`需求二:${data2.n}  设置为-1 成功`)

这时候上面的杠精又来了,他说,你这个可以直接对n进行修改,太不安全了吧

然后我找了一个中介,让它去负责对n的操作

需求二:使用代理

不暴露出n,必须通过中介proxy来修改n

let data3=proxy({data:{n:0}})
// 通过解构赋值拿到data
function proxy({data}){
  const obj={}
  Object.defineProperty(obj,'n',{
    get(){
      return data.n
    },
    set(value){
      if(value<0)return 
      data.n=value 
    }
  })
  return obj
}

这个函数返回的就是我们这个对象,这个对象里面的n不能小于0

同时杠精没办法对n直接进行操作了,要通过这个函数。

需求三,杠精还是不服

这个杠精还是访问到了n,因为proxy中的对象是匿名函数

let myData={n:0}
let data4=proxy({data:myData})
// 杠精通过修改myData里面的n,成功实现了直接对n的操作

就算用户擅自修改myData,我也要拦住,改代码

function proxy2({data}){
  let value=data.n
  delete data.n  //这个delete可以不写,因为会自动创建一个新的n的
  const obj={}
  Object.defineProperty(obj,'n',{
    get(){
      return value
    },
    set(newValue){
      if(newValue<0)return 
      value=newValue 
    }
  })
  return obj
}

现在杠精就没话可说了

现在我们看看这一句

let data5=proxy2({data:myData5})

是不是有点像

vm=new Vue({data:myData})

这下知道vue是如何处理data了吧

小结:

  • Object.defineProperty
  1. 可以给对象添加value
  2. 可以给对象添加getter和setter
  3. getter和setter用于对属性的读写进行监控
  • 代理
  1. 对myData对象的属性进行读写,全权由另一个对象vm负责
  2. 那么vm就是myData的代理
  • vm=new Vue({data:myData})
  1. 会让vm成为myData的代理
  2. 会对myData的所有属性进行监控,这样知道属性变了就可以实时的调用render来渲染视图

vue对data的处理示意图

vue 不能响应set结构增加数据_Vue学习之路-vue数据响应式_第2张图片

我们往vue对象中传入自己的myData,然后我们将data中的属性进行改造,监听,用value来篡改这个n。然后我们将这个包含了get和set的对象变为data。然后由vm进行代理

如果data有多个属性n,m,k ,那么就会有get n/get m/get k等等


数据响应式

什么是响应式?

我打你一拳,你会疼,这就是响应式

vue的data是响应式的

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

后续

关于vue中data的bug

Object.defineProperty的问题

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

必须要有一个n,才能监听obj.n

如果没有给n怎么办

来看两个例子

示例1

new Vue({
  data:{},
  template:`
    
{ {n}}
` }).$mount(‘#app’)

页面中的n不会显示,并且会报警告

n没有定义在实例上

示例2

new Vue({
  data:{
    obj:{
     a:0
    }
  },
  template:`
    
{ {obj.b}}
`, methods:{ setB(){ this.obj.b=1 } } }).$mount(‘#app’)

然后没警告了。。。。

  • 因为vue只会检测一层
  • 但是点击setb还是不会起到作用的
  • 如果我就是不想改data,又想要b怎么办呢
  • vue.set和this.$set可以帮我
  • vue.set(this.obj,'b') 或者this.$set(this.obj,'b')

vue.set和this.$set作用

  • 新增key
  • 自动创建代理和更新
  • 触发ui更新

关于vue中data的数组

尽管我们可以用set来添加data的属性,但是对于数组来说,没法提前声明所有的key值呀

vue官方修改了数组的7个api

vue 不能响应set结构增加数据_Vue学习之路-vue数据响应式_第3张图片

也就是说,我们调用这7个数组api的时候,不管是新增属性还是删除属性,vue都能监听到并且实时的去渲染

api是如何修改的? 用继承,在使用原生的push的时候可以调用set来进行数组的监听和代理

class VueArray extends Array{
  push(...args){
    const oldLength=this.length //this 是当前数组
    super.push(...args)  //调用原生的push
    this.length
    for(let i=oldLength;i

你可能感兴趣的:(vue,不能响应set结构增加数据)