1、Vue2 数据响应式 是什么
数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。 使用vue时,只需改变数据,视图层就会获取相应的更新。
vue对data做了什么:
- 数据劫持:当数据变化时,我们可以做一些特定的事情
- 依赖收集:我们要知道那些视图层的内容(
DOM)依赖了哪些数据(state) - 派发更新:数据变化后,如何通知依赖这些数据的
DOM
Object.definePropert用法
// 模拟 Vue 中的 data
const data = {}
// 对外不可见的内部变量
let _myName = 'Kobe' // _myName就是代理
// 响应式监听 data 中的 name
Object.defineProperty(data, "name", {
// 使用 data.name 时 get 方法被拦截调用,返回内部存储变量值
get(){ // 下文中该方法统称为getter
//在这里收集依赖
console.log('get')
return _myName
},
// 使用 data.name = xxx 修改变量时,set 方法被调用,设置内部存储变量值
set(newVal){ // 下文中该方法统称为setter
console.log('set')
_myName = newVal
//当数据变更时,通知依赖项变更UI
}
})
console.log(data.name) // 输出 Kobe get
data.name = 'Mr.Wu' // 输出 set (监听成功)
Vue使用Object.defineProperty来进行数据劫持
- 当我们把
options.data传给vue,data会被vue监听,通过Object.defineProperty给对象添加getter/setter,当我们访问data.name时,输出get,返回Kobe;当data.name = xxx修改变量时,我们自定义了data.name取值和赋值的行为,使用自定义的getter和setter来重写了原有的行为,这也就是数据劫持的含义。 - 为什么我们要用
let _myName = 'Kobe'代理,而不是直接return data.name和data.name = newVal?- 如果直接在get函数中使用
return data.name这里的data.name会再次调用get函数,这样就会陷入死循环;set也是同理;所以需要设置第三方变量_myName来阻止死循环。
- 如果直接在get函数中使用
但是如果我们需要代理更多的属性,不可能给每一个属性定义一个第三方的变量,可以通过封装函数和使用闭包来解决
// value使用了参数默认值
function defineReactive(data, key, value) {
Object.defineProperty(data, key, {
get: function reactiveGetter() {
return value
},
set: function reactiveSetter(newValue) {
if (newValue === value) return
value = newValue
}
})
}
defineReactive(obj, "a", 1)
2、Object.defineProperty缺点
Object.defineProperty(obj,'n',{...})dtate使用时必须传n- 只有监听的属性才能被劫持,在vue2中,数据开始就要写在data中。
3、解决
- 初始把date声明好,
- 监听新增的属性需要使用this.$set或者Vue.set
this.$set(this.object,'m',100)
4、Vue.set和this.$set的作用
- 新增key
- 自动创建代理和监听(如果没有创建过)
- 触发UI更新(不会立刻更新)
如果data中有数组怎么办?一要新增key
- 用set新增key,会更新ui,但不会创建监听和代理
- 不过vue中有变更方法,其中7个api方便对数组进行增删,但不会自动处理监听和代理 变更方法
push()pop()shift()unshift()splice()sort()reverse()