vue 数组 新增元素 响应式原理 7种方法
1、问题
思考一个问题,以下代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>vue 数组 响应式原理</title>
</head>
<body>
<div id="app">
<div v-for="item in list">
{{ item }}
</div>
</div>
<script src="https://cdn.bootcss.com/vue/2.5.17/vue.min.js"></script>
<script type="text/javascript">
var app = new Vue({
el: '#app',
data: {
list: [1, 2, 3]
}
})
</script>
</body>
</html>
当我们在控制台输入:app.list[0] = 100时,vue会监测到变化吗?
app.push(100)呢?
引申出的问题就是:
vue对数组新增的元素,包括push、unshift和splice(插入)的元素是怎么做到响应式的呢?
2、Vue对新增的数组元素响应式原理
(1)核心代码(observer/array.js)
/*
* not type checking this file because flow doesn't play well with
* dynamically accessing methods on Array prototype
*/
import { def } from '../util/index'
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
/**
* Intercept mutating methods and emit events
*/
methodsToPatch.forEach(function (method) {
// cache original method
const original = arrayProto[method]
def(arrayMethods, method, function mutator (...args) {
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// notify change
ob.dep.notify()
return result
})
})
在这个函数中使用到了def函数,def函数的定义是(util/lang.js):
export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
})
}
即对元素的属性重新定义,尤其是value的获取。
回到observer/array.js,除了正常返回push、unshift和splice(插入)函数执行的result结果外,还通知了变化!ob.dep.notify! 所以对新增的数组元素实现了响应式的变化。
留一个问题:
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
为什么push、unshift和splice处理的参数不一样?
查一下splice的参数有哪些吧。
作者:孟繁贵 Email:meng010387@126.com 期待共同进步!