热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

providevue响应式_Vue数据响应式

Vue里的数据都存储在data里,然后对data里的数据进行更新,从而使页面的UI重新渲染1.首先先来看一下Vue到底对data做了什么呢?

Vue 里的数据都存储在 data 里,然后对 data 里的数据进行更新,从而使页面的 UI 重新渲染

1.首先先来看一下 Vue 到底对 data 做了什么呢?

//引用完整版 Vue
import Vue from "vue/dist/vue.js"; Vue.config.productionTip = false;//禁用警告const myData = {n: 0
}
console.log(myData) // 精髓new Vue({data: myData,template: `

{{n}}
`
}).$mount("#app");setTimeout(()=>{myData.n += 10
console.log(myData) // 精髓
},3000)

打出log如图

178ae7316904649ae210854012ea0276.png

说明myData 引用到 Vue 实例里,Vue 对 data 里的数据进行了一些处理

ES6的getter和setter

通过虚拟属性xxx来模拟对n的读写操作

需求一,得到姓名

let obj0 = {姓: "高",名: "圆圆",age: 18
};// 需求一,得到姓名let obj1 = {姓: "高",名: "圆圆",姓名() {return this.姓 + this.名;},age: 18
};console.log("需求一:" + obj1.姓名());


// 姓名后面的括号能删掉吗?不能,因为它是函数
// 怎么去掉括号?需求二,姓名不要括号也能得出值

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

get是一个属性只不过是用函数定义的
总结:这种写法就叫做 getter ,关键词为 get ,用于获取一个值。不加括号的函数,定义时为函数,但是使用时不用加括号。需求三:姓名可以被写

let obj3 = {姓: "高",名: "圆圆",get 姓名() {return this.姓 + this.名;},set 姓名(xxx){this.姓 = xxx[0]this.名 = xxx.slice(1)//或者this.名 = xxx.substring(1)},age: 18
};
obj3.姓名 = '刘亦菲'console.log(`需求三:姓 ${obj3.姓},名 ${obj3.名}`)

// 总结:setter 就是这样用的。接受一个新的值,obj.姓名 = 'xxx'触发 set 函数

确实可以对于'姓名'进行读和写,但是不存在叫'姓名'的属性,通过get和set来模拟对其读写操作

定义完一个对象后,想加在上面再加一个属性,使用Object.defineProperty

Object.defineProperty(obj, prop, descriptor)

obj:要在其上定义属性的对象;prop:要定义或修改的属性的名称;

descriptor:将被定义或修改的属性描述符。

var _xxx = 0
0bject.defineProperty(obj3,'xxx',{ //给obj3添加一个属性,属性为xxx,xxx的值为
get( ){
return _xxx
},
set(value){
_xxx=value//由于xxx是虚拟属性不存在,因此用_xxx来储存值
}
})
obj3.姓名='刘诗诗'console.log( `需求三:姓${obj3.姓},名${obj3.名}`)
console.log(obj3)

例子

需求一:用 Object.defineProperty 定义 n

let data0 = {n: 0
}let data1 = {}Object.defineProperty(data1, 'n', {value: 0 //给data1添加一个属性n,它的值是0
})

需求二:n 不能小于 0

即 data2.n = -1 应该无效,但 data2.n = 1 有效

let data2 &#61; {}data2._n &#61; 0 // _n 用来偷偷存储 n 的值Object.defineProperty(data2, &#39;n&#39;, {get(){return this._n},set(value){if(value <0) returnthis._n &#61; value}
})console.log(&#96;需求二&#xff1a;${data2.n}&#96;)
data2.n &#61; -1
console.log(&#96;需求二&#xff1a;${data2.n} 设置为 -1 失败&#96;)
data2.n &#61; 1
console.log(&#96;需求二&#xff1a;${data2.n} 设置为 1 成功&#96;)

如果对方直接使用 data2._n 呢&#xff1f;

需求三&#xff1a;使用代理

let data3 &#61; proxy({ data:{n:0} }) // 括号里是匿名对象&#xff0c;无法访问function proxy({data})// 解构赋值function proxy(options) {const{data}&#61;options}从options拿到dataconst obj &#61; {}// 这里的 &#39;n&#39; 写死了&#xff0c;理论上应该遍历 data 的所有 key&#xff0c;这里做了简化// 因为我怕你们看不懂Object.defineProperty(obj, &#39;n&#39;, { get(){return data.n //对obj.n做什么同时对data.n做什么},set(value){if(value<0)returndata.n &#61; value}})return obj // obj 就是代理
}// data3 就是 obj
console.log(&#96;需求三&#xff1a;${data3.n}&#96;)
data3.n &#61; -1
console.log(&#96;需求三&#xff1a;${data3.n}&#xff0c;设置为 -1 失败&#96;)
data3.n &#61; 1
console.log(&#96;需求三&#xff1a;${data3.n}&#xff0c;设置为 1 成功&#96;)

如果绕过代理&#xff0c;非要给对象一个名字myData&#xff0c;可以使用myData去修改n

let myData &#61; {n:0}
let data4 &#61; proxy({ data:myData }) // 括号里是匿名对象&#xff0c;无法访问// data3 就是 obj
console.log(&#96;杠精&#xff1a;${data4.n}&#96;)
myData.n &#61; -1
console.log(&#96;杠精&#xff1a;${data4.n}&#xff0c;设置为 -1 失败了吗&#xff01;&#xff1f;&#96;)

需求四、使用监听

let myData5 &#61; {n:0}
let data5 &#61; proxy2({ data:myData5 }) // 括号里是匿名对象&#xff0c;无法访问function proxy2({data}){let value &#61; data.n //用value存储原始的值Object.defineProperty(data, &#39;n&#39;, { //此处声明新的虚拟对象n就会删掉原始的nget(){return value},set(newValue){if(newValue<0)returnvalue &#61; newValue}})// 就加了上面几句&#xff0c;这几句话会监听 dataconst obj &#61; {}Object.defineProperty(obj, &#39;n&#39;, {get(){return data.n},set(value){if(value<0)returndata.n &#61; value}})return obj // obj 就是代理
}// data3 就是 obj
console.log(&#96;需求五&#xff1a;${data5.n}&#96;)
myData5.n &#61; -1
console.log(&#96;需求五&#xff1a;${data5.n}&#xff0c;设置为 -1 失败了&#96;)
myData5.n &#61; 1
console.log(&#96;需求五&#xff1a;${data5.n}&#xff0c;设置为 1 成功了&#96;)

可以看出let data5 &#61; proxy2({ data:myData5 })类似于

let vm &#61; new Vue({data: myData})

小结&#xff1a;

vue 对 data 做了什么&#xff1f;

vm &#61; new Vue({data: myData})

1、会让vm成为myData的代理(proxy)&#xff0c;可以通过this来访问vm(this.n来访问myData.n&#xff0c;因为vm是代理&#xff09;

2、会对myData的所有属性进行监控&#xff0c;为了防止myData的属性变了&#xff0c;vm不知道。

vm知道属性变了就可以调用render(data)来更新 UI 和渲染页面&#xff0c;UI &#61; render(data)

3、 const vm &#61; new Vue({data: {n:0}})整个流程&#xff1a;vue会对内部数据data进行改造和监听&#xff0c;let value&#61;0并且对其get和set形成新的被篡改后的对象,然后通过代理对篡改后的对象进行读和写&#xff0c;再用vm接纳新的对象&#xff0c;data 中有多个变量/属性时&#xff0c;可以用闭包和循环来实现这个过程。

6ee81b220eeed5e50e7d43707ab1e812.png

数据响应式

若一个物体能对外界的刺激做出反应&#xff0c;它就是响应式的

Vue的data是响应式&#xff0c;const vm &#61; new Vue({data: {n: 0}}}&#xff0c;如果修改vm.n,那么UI中的n就会响应我

Vue 2通过0bject.defineProperty来实现数据响应式

Vue有一个bug

Object.defineProperty的问题

Object.defineProperty(obj, &#39;n&#39; ,{..})必须要有一个&#39;n&#39;,才能监听&代理obj.n

示例1、如果没有给n

import Vue from "vue/dist/vue.js";Vue.config.productionTip &#61; false;new Vue({data: {},template: &#96;

{{n}}
&#96;
}).$mount("#app");

Vue会给一个警告n没有被定义

77d2e5fa84e668ff547e593d269e8a3e.png

示例2、Vue只会检查第一层

import Vue from "vue/dist/vue.js";Vue.config.productionTip &#61; false;new Vue({data: {obj: {a: 0 // obj.a 会被 Vue 监听 & 代理}},template: &#96;

{{obj.b}}
&#96;,methods: {setB() {this.obj.b &#61; 1; //请问&#xff0c;页面中会显示 1 吗&#xff1f;}}
}).$mount("#app");

页面中不会显示1&#xff0c;因为b一开始没有被监听

解决方法有2个

1、一开始声明好所有的key

new Vue({data: {obj: {a: 0 &#xff1b;b:undefined}},

再点击setb就会显示1

2、使用Vue.set 和 this.$set

methods: {setB() {写法1&#xff1a;Vue.set(this.obj,&#39;b&#39;,1) 或者写法2&#xff1a;this.$set(this.obj,&#39;b&#39;,1)}}

作用:

a.新增key

b.自动创建代理和监听(如果没有创建过)

c.触发UI更新(但并不会立刻更新)

当data有数组时

import Vue from "vue/dist/vue.js";Vue.config.productionTip &#61; false;new Vue({data: {array: ["a", "b", "c"]},template: &#96;

{{array}}
&#96;,methods: {setD() {this.array[3] &#61; "d"; //请问&#xff0c;页面中会显示 &#39;d&#39; 吗&#xff1f;// 等下&#xff0c;你为什么不用 this.array.push(&#39;d&#39;)}}
}).$mount("#app");

不会显示d是因为一开始数组的下标只有0,1,2&#xff1b;数组的长度无法预测无法使用 undefined 去为每一项占位&#xff0c;或一直使用 Vue.set( ) 方法。

因此可以用 Array.push 方法来添加

this.array.push(&#39;value&#39;)

Array.push不是原生的API,而是当你将数组对象传递给vue后&#xff0c;它会篡改数组&#xff0c;在中间增加一层原型&#xff0c;该原型有七个方法&#xff1a;

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

这些API会自动处理对数组的监听和代理&#xff0c;并触发视图更新。

大概原理&#xff1a;

c1ass VueArray extends Array {push(...args) {const oldLength &#61; this.length // this 就是当前的数组super.push(...args)//原理会先调用父级的push&#xff0c;但是会执行后面的监听代码console.1og(&#39;你push 了&#39;)for(let i &#61; oldLength; i}
const a &#61; new VueArray(1,2,3,4)
a. push(5)

总结

对象中新增的key

  • Vue没有办法事先监听和代理
  • 要使用set 来新增key&#xff0c;创建监听和代理&#xff0c;更新Ul
  • 最好提前把属性都写出来&#xff0c;不要新增key
  • 但数组做不到「不新增key」

数组中新增的key

  • 也可用set来新增key&#xff0c;但set对于数组无法创建监听和代理&#xff0c;更新UI
  • 不过尤玉溪篡改了7个API方便你对数组进行增删
  • 这7个API会自动处理监听和代理&#xff0c;并更新U
  • 结论:数组新增key最好通过7个API



推荐阅读
author-avatar
啦啦郭嘿
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有