双向绑定
- 发布-订阅
- 脏检测
- 数据劫持
- Object.definePropery (es5)
- Proxy (es6)
- Object.observe(废弃)
- 数据模型
definePropery
1. 不能检测数组长度变化,准确的说是通过改变Length而增加的长度不能检测。
length 和数字下标之间的关系 —— JavaScript 数组的 length 属性和其数字下标之间有着紧密的联系。数组内置的几个方法(例如 join、slice、indexOf 等)都会考虑 length 的值。另外还有一些方法(例如 push、splice 等)还会改变 length 的值。为什么defineProperty不能检测到数组长度的“变化”
- 当你利用索引直接设置一个项时,vm.items[indexOfItem] = newValue
- 解决方法:Vue.set(vm.items, indexOfItem, newValue) / vm.$set(vm.items, indexOfItem, newValue) / vm.items.splice(indexOfItem, 1, newValue)
- 当你修改数组的长度时,vm.items.length = newLength
- 解决方法:vm.items.splice(newLength)
2. 为什么defineProperty不能检测到数组长度的“变化”
3. vue对数组方法的hack
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51const arrayProto = Array.prototype
// 复制了数组构造函数的原型
// 这里需要注意的是数组构造函数的原型也是个数组
// 实例中指向原型的指针__proto__也是个数组
// 数组并没有索引,因为length = 0
// 相反的拥有属性,属性名为数组方法,值为对应的函数
export const arrayMethods = Object.create(arrayProto)
// 对以下方法重写
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
methodsToPatch.forEach(function (method) {
// cache original method
const original = arrayProto[method]
// 这里的def很重要,其实也就是用object.defineProperty重新定义属性
// 但这里的arrayMethods是个数组,这就是为什么上面我们解释
// 数组构造函数原型是个空数组但是默认了属性方法
// 所以这里的定义是很巧妙的
def(arrayMethods, method, function mutator (...args) {
const result = original.apply(this, args)
// ob就是observe实例
const ob = this.__ob__
let inserted
switch (method) {
// 为什么对push和unshift单独处理?
// 我们在上看解释过,这2中方法会增加数组的索引,但是新增的索引位需要手动observe的
case 'push':
case 'unshift':
inserted = args
break
// 同理,splice的第三个参数,为新增的值,也需要手动observe
case 'splice':
inserted = args.slice(2)
break
}
// 其余的方法都是在原有的索引上更新,初始化的时候已经observe过了
if (inserted) ob.observeArray(inserted)
// notify change
// 然后通知所有的订阅者触发回调
ob.dep.notify()
return result
})
})
4. Vue 不能检测对象属性的添加或删除
对于已经创建的实例,Vue 不能动态添加根级别的响应式属性
1
2
3
4
5
6
7
8
9var vm = new Vue({
data: {
a: 1
}
})
// `vm.a` 现在是响应式的
vm.b = 2
// `vm.b` 不是响应式的解决方法:
- Vue.set(vm.userProfile, key, value)
- vm.$set(vm.userProfile, key, value)
- Object.assign()
5. definePropery缺点
- 无法监听数组变化
- 无法检测对象属性的添加和删除
- 性能差。只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历,如果属性值也是对象那么需要深度遍历
Proxy
Proxy可以直接监听对象而非属性
1 | const input = document.getElementById('input'); |
Proxy可以直接监听数组的变化
优缺点
- 优点:对数组和对象支持非常好,不需要hask;性能比definePropery好,初始化很多数据,当用到才会被监听。
- 缺点:兼容性不太好,而且无法用polyfill磨平