03 - Vue源码解读-响应式
响应式原理
- 我们在使用 Vue 的时候,复制属性获得属性都是直接使用Vue实例
我们在设计属性值的时候,页面数据更新
Object.defineProperty(obj, key, {})
1
2
3
4
5
6
7
8
9
10
11
12// 注意 value 和 writable 不可以和 get set 连用
Object.defineProperty(obj, key, {
enumerable: !!enumerable,
configurable: true,
set (newValue){
value = newValue
},
get(){
console.log(value)
return value
}
})使用
Object.defineProperty()
实现数据监控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
42const data = {
name: '优·库里伍德·海尔赛兹',
behavior: '吃橘子',
like: [
{juice: 'juice'}
],
other: {
name: '相川步',
behavior: {
name: '约的人',
time: '12m'
}
}
}
function setPerperties(obj, key, value, enumerable) {
console.log(obj, key, value, !!enumerable)
Object.defineProperty(obj, key, {
enumerable: !!enumerable,
configurable: true,
set (newValue){
value = newValue
},
get(){
console.log(value)
return value
}
})
}
function setReactive(o) {
Object.keys(o).map(key => {
if(Object.prototype.toString.call(o[key]) === '[object Object]') {
setReactive(o[key])
} else if(Object.prototype.toString.call(o[key]) === '[object Array]') {
o[key].map(item => {
if(Object.prototype.toString.call(item) === '[object Object]' || Object.prototype.toString.call(item) === '[object Array]') setReactive(item)
})
} else {
setPerperties(o, key, o[key], true)
}
})
}
setReactive(data)针对数组中的特殊方法进行响应式化处理
- push 向数组的最后添加元素,并返回新的数组长度
- unshift 向数组的开始添加元素,并返回新的数组长度
- pop 删除数组的最后一个元素,并返回被删除的元素
- shift 删除数组的第一个元素,并返回被删除的元素
- reverse 翻转数组,并返回翻转之后的数组
- sort 数组排序
splice 向/从数组中添加/删除项目,然后返回被删除的项目,rrayObject.splice(index,howmany,item1,…..,itemX) (删除的位置, 删除的个数, 插入的新元素)
vue2 中数组发生变化,设置 length 没法通知(Vue3 中使用Proxy语法解决了这个问题)
- 加进来的元素也应该是响应式的
技巧:如果一个函数已经定义了,但是我们需要扩展其功能,我们一般的处理方法
- 使用一个临时的函数名存储函数
- 重新定义原来的函数
- 定义扩展的功能
- 调用临时的那个函数
1
2
3
4
5
6
7
8
9
10
11
12
13function func() {
console.log('old property')
}
// 第一步
let _tempFn = func
// 第二步
func = function() {
// 第四步
_tempFn()
// 第三步
console.log('new property')
}
func()
如何修改数组的扩展函数
- 不能直接修改数组的 prototype
- 修改要进行响应式化的数组的原型(proto)
- 原型式继承:修改原型链结构
- 继承关系: arr -> Array.prototype -> Object.prototype -> …
- 修改之后: arr -> 改写的方法 -> Array.prototype -> Object.prototype -> …
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16let arr = []
let ARRAY_METHOD = ['pop', 'unshift', 'push', 'shift', 'reverse', 'sort', 'splice']
let arrayMethods = Object.create(Array.prototype)
ARRAY_METHOD.map(item => {
arrayMethods[item] = function() {
Array.from(arguments).map(item => {
setReactive(item)
})
// 在这里对数据进行响应式化
let res = Array.prototype[item].apply(this, arguments)
return res
}
})
arr.__proto__ = arrayMethods - vue做了兼容,如果浏览器兼容proto,就使用 原型式继承
不兼容就使用 混入法 将方法一个个的加入到 arr 中
将方法整合到设置响应式方法中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function setReactive(o) {
Object.keys(o).map(key => {
if(Object.prototype.toString.call(o[key]) === '[object Object]') {
setReactive(o[key])
} else if(Object.prototype.toString.call(o[key]) === '[object Array]') {
console.log(o[key])
o[key].__proto__ = arrayMethods // 拦截数组,进行数组响应式
o[key].map(item => {
if(Object.prototype.toString.call(item) === '[object Object]' || Object.prototype.toString.call(item) === '[object Array]') setReactive(item)
})
} else {
setPerperties(o, key, o[key], true)
}
})
}对 对象 或者 数组 重新赋值 如何设置响应式
setPerperties() 函数的set方法中 重新调用一次 setReactive()
变更页面
在 JGVue 函数中调用 setReactive 方法 把 this._data 设置成响应式类型,同时传递 vue 实例到 Object.defineProperty
1
2
3
4
5
6function JGVue(options) {
this._data = options.data
setReactive(this._data, this)
this._template = document.querySelector(options.el) // vue 中这里是DOM
this.mount() // 挂载
}
修改 setReactive 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14function setReactive(o, vm) {
Object.keys(o).map(key => {
if(Object.prototype.toString.call(o[key]) === '[object Object]') {
setReactive(o[key], vm)
} else if(Object.prototype.toString.call(o[key]) === '[object Array]') {
o[key].__proto__ = arrayMethods // 拦截数组,进行数组响应式
o[key].map(item => {
if(Object.prototype.toString.call(item) === '[object Object]' || Object.prototype.toString.call(item) === '[object Array]') setReactive(item, vm)
})
} else {
setPerperties.call(vm, o, key, o[key], true)
}
})
}
修改 setPerperties 函数
1 | function setPerperties(obj, key, value, enumerable) { |