深入理解 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)
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)
我们看到这个姓名:(...)和之前的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发出警告
属性或者方法n没有定义在实例上,但是你引用了它在render的时候。
2、它只会检查第一层
此时点击按钮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)
以前的数组不止着七个方法啊
为啥还有个啥mutator
再点开一层原型 发现才是真正的数组原型
也就是说
这个数组对象,传给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
}
}
}
总结
对象中新增的key
Vue没有办法事先监听和代理
要使用set来新增key创建代理和监听更新UI
最好提前把属性写出来
但是数组做不到
数组新增key
也可以使用set来新增
Vue篡改了7个API来方便你对数组的操作