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的参数有哪些吧。