vue 数据响应式

深入理解 options.data,响应式原理

const myData = {
  n:0
}
console.log(myData)
打印出{n:0}

const vm = new Vue({
  data:myData,
  template:`
  <div>{{n}}
  <button @click="add">+10</button>
  </div>
  `,
  methods:{
    add(){
      this.n+=10
    }
  }
}).$mount('#app')

//我在外面变更myData也是可以的
setTimeout(()=>{
  myData.n+=10
  console.log(myData)
  console.log(vm)
},5000)
三个console

ES6中的 getter 和 setter

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

1、我要得到他的姓名
let obj1 = {
  姓:"高",
  名:"圆圆",
  age:"18",
  姓名(){
    return this.姓+this.名
  }
}
console.log(obj1.姓名())

2、obj1.姓名()后面这个括号我要去掉,但是还得得到姓名

姓名不需要括号也可以得出值,+个get 他就知道这是一个属性,不是函数。
这种写法叫做getter,用于获取一个值
let obj2 = {
  姓:"高",
  名:"圆圆",
  age:"18",
  get 姓名(){
    return this.姓+this.名
  }
}
console.log(obj1.姓名)

3、我要姓名可以被写 set 

let obj3 = {
  姓:"高",
  名:"圆圆",
  age:"18",
  get 姓名(){
    return this.姓+this.名
  },
  set 姓名(xxx){
    this.姓 = xxx[0]
    this.名 = xxx.substring(1)
  },
}
obj3.姓名= ’刘亦菲‘ 触发set函数 = 右边就是xxx
console.log(`姓${obj3.姓},名${obj4.名}`)
写法叫setter

通过计算属性 改变原始属性。打印出来看看是啥console.log(obj3)


D9SL4SEMW{Z5_KZVZ4LD`KP.png

我们看到这个姓名:(...)和之前的n:(...)一样,说明我们之前得到的n:(...)也是一个get set

这个姓名不是真实的属性 因为我并没一个属性叫姓名 我只有 get姓名 和set姓名。
所以浏览器在显示这个姓名的时候是 姓名:(...)
说明你确实可以对姓名进行读和写但是姓名这个属性并不存在
是通过get姓名 和set姓名完成的
推出n:(...)并不存在叫n的属性 而是有一个get n set n模拟对n的操作

但是为什么把他变为get n set n呢?

Object.defineProperty()

我们一开始定义的时候直接把get set 写在对象里面, 如果我后面想加呢?
用Object.defineProperty()
定义一个对象之后你想在他身上额外添加新的get set

var z = 0定义一个东西存放它

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

第一个参数写在那个对象上添加
第二个参数你要定义个什么东西 xxx
然后就写get set
get xxx(){}
我们给object3添加一个虚拟属性 有一个get 和set
你定义的这个xxx是不存在的不能使用this.xxx(死循环 get xxx 就调用xxx就继续get)

代理和监控

//修改 myData  不让他成功
//obj 中介 data 房东 禁止租客和房东联系  所以我们要监听它

let myData5 = {n:0}
let data5 = proxy2({data:myData5})

function proxy2({data}){
    let value = data.n //首先拿到n的值
    //delete data.n //删除原生的n 不写也行 声明新的虚拟对象的时候 下面会直接覆盖的
    Object.defineProperty(data,'n',{ //声明虚拟的n放啊都data上
        get(){return value},
        set(v) {
            if (v<0){
                return
            }
            value = v
        }
    })
    //上面会监听data.n(在房东手机安装监听器) 下面是代理
    const obj = {}
    Object.defineProperty(obj,'n',{
        get(){return data.n},
        set(v) {
            if (v<0){
                return
            }
            data.n = v
        }
    })
    return obj
}obj是代理data5=obj 也就是代理

console.log(`data5${data5.n}`) //0
myData5.n = -1
console.log(`更改myData5为-1失败${data5.n}`)
myData5.n = 1
console.log(`更改myData5为1成功${data5.n}`)
//以上就做到了 防止你在我不知道的情况下 修改数据

//let data5 = proxy2({data:myData5}) 眼熟吗? 跟vue的原理一模一样
// const vm = new Vue({
//     data:{n:0}
// )

//回头我们看 vue 到底对data 做了哪两件事情
  • vm = new Vue({data:myData})做了什么
    1.会让vm称为myData的代理(proxy)
    2、会对myData 的所有属性进行监控(上面只是简化了 直接用的n其实应该遍历)
    为什么要监控》?防止 myData的属性变了 vm不知道
    vm知道了又如何?知道属性变了就可以render(data)了啊
    UI = render(data) UI更新

  • 监控就是把myData所有属性 移动到value上面
    然后添加虚拟属性 读写都会被vm监听到

  • 总结
    Object.defineProperty()
    可以给对象添加属性给他一个value
    可以给对象添加getter、setter
    getter、setter用于对属性的读写进行监控

  • 代理(设计模式)
    对myData对象的属性读写 由vm对象负责
    vm就是myData的代理
    myData.n不用 就要用vm.n/this.n来操作myData.n


    示意图

数据被new Vue进行改造,加监听
n:0 变为value 添加 get n set n 读写
然后有一个代理
如果data有多个属性 进行循环即可
有对应的 get n get m get k

同理vue 对methods computed也有处理

数据响应式

什么是响应式
我打你一拳你会疼,你就是响应式的
若一个物体对外界刺激做出反应就是响应式的

vue的data是响应式的
const vm = new Vue({
data:{n:0}
})
我修改vm.n 那么UI中的n就会响应我
Vue 通过 Object.defineProperty() 做到

响应式网页 改变窗口大小内容会响应式

Vue的bug

Object.defineProperty(obj,'n',{...}) 的问题
必须要给他传一个n ,才能监听&代理obj.n
如果开发者没有给n怎么办
1.vue发出警告


shili

属性或者方法n没有定义在实例上,但是你引用了它在render的时候。
2、它只会检查第一层


N0J2`YV75$F58DX)8}A_91S.png

此时点击按钮b并没有出现在视图中
因为Vue一开始 监听obj 和 obj.a
没办法监听一开始i不存在的obj.b

如何解决
1、 直接声明好 就算写b:undefined一开始也会被监听的
2、使用Vue.set 或this.$set

setB(){
  Vue.set(this.obj,'b',1)
  this.$set(this.obj,'b',1)
}

之后就可以直接用了 不要set了
这个代码做了什么
新增属性b
自动创建代理和监听(如果没有创建过)
触发UI更新(不会立即)

data中有数组怎么办?

没有办法事先声明好,数组的长度可以一直增加 下标就是key.
难道每次修改数组都要Vue.set?

点击按钮 在数组加一个D
点击之后发现无反应。

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

下面这样可以

 this.$set(array,3,'d')

难道我 数组操作都要set?
为啥不用push,push竟然可以

 this.array.push("d")

因为此时的push不是以前的push
console.log(this,array)


1

以前的数组不止着七个方法啊
为啥还有个啥mutator


2

再点开一层原型 发现才是真正的数组原型

也就是说
这个数组对象,传给vue之后。vue就会篡改数组,调用后会更新UI。中间加原型有七个方法 和以前的七个方法同名 但是代码被改了 应该就加了一个set
所以说这个push做做两个事情 1、调用以前的push 加个set

大概原理如下

class VueArray extends Array{  首先继承数组原有的属性,再声明一个push,接收一个数组
      push(...args){
        const oldLength = this.length//this就是当前数组 先看看本来长度是多少
        super.push(...args)  调用父元素上的push,你如果调我这个push方法 我就先调用我下一层的 push
        for(let i = oldLength;i<this.length;i++){
          Vue.set(this,i,this[i]) 将新增的key告诉vue
        }
      }
}
aw(~)$08{fzk@h7f}zkafq3.png

总结
对象中新增的key
Vue没有办法事先监听和代理
要使用set来新增key创建代理和监听更新UI
最好提前把属性写出来
但是数组做不到

数组新增key
也可以使用set来新增
Vue篡改了7个API来方便你对数组的操作

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Vue到底对 data 做了什么? 我们有如上的代码,其中 data 中的的数据引用外面的变量 myData; 我...
    MrTon_1965阅读 1,697评论 0 0
  • 数据响应式 主要原理:深入响应式原理[https://cn.vuejs.org/v2/guide/reactivi...
    珍惜时间小李阅读 2,870评论 0 0
  • 一个物体能对外界的刺激作出反应,那他就是响应式的 const vm = new Vue({data:{n:0}})...
    Shigure_Rain阅读 2,425评论 0 0
  • 写在前面 我相信很多同学对Vue的数据响应式是通过Vue.js文档[https://link.zhihu.com/...
    luci_dity阅读 2,695评论 0 0
  • Vue数据响应式主要研究的是 Vue 构造选项中 data 属性的特性 深入响应式 官方文档 网址: http...
    雨溪滩阅读 3,754评论 0 0

友情链接更多精彩内容