读vue-0.6-observer.js源码
2017-04-23 15:16
381 查看
实现监听数组方法
var ArrayProxy = Object.create(Array.prototype), methods = ['push','pop','shift','unshift','splice','sort','reverse']; function defProtected(obj, key, val, enumerable, configurable) { // 如果是用户添加的方法则不监听 if (obj.hasOwnProperty(key)) return Object.defineProperty(obj, key, { value : val, // 不可枚举 enumerable : !!enumerable, // 不可配置 configurable : !!configurable }) } // 监听原生数组方法 methods.forEach(function (method) { // ArrayProxy监听的对象 ,method监听的方法,第三个返回一个value defProtected(ArrayProxy, method, function () { // 这里面的this表示当前调用的数组 var result = Array.prototype[method].apply(this, arguments) // 调用数组的__observer__里面的emit方法,触发更新。 this.__observer__.emit('mutate', this.__observer__.path, this, { method: method, args: slice.call(arguments), result: result }) return result }, !hasProto) });
我们可以看到在这段代码中并没有对数组进行get和set监听,这也是为什么在vue中给数组直接赋值不会触发更新的主要原因。
数组remove和replace方法
var hasProto = ({}).__proto__; function def(obj, key, val, enumerable, configurable) { if (obj.hasOwnProperty(key)) return Object.defineProperty(obj, key, { value : val, enumerable : !!enumerable, configurable : !!configurable }) } var ArrayProxy = Object.create(Array.prototype); // 给数组添加remove和replace方法 var extensions = { remove: function (index) { /* 如果index是一个函数,则调用这个函数并且判断返回值,如果返回值为true则删除,false不删除 比如下面这个,删除index大于5的项 remove(function(index){ return index > 5; }); */ if (typeof index === 'function') { var i = this.length, removed = [] while (i--) { if (index(this[i])) { removed.push(this.splice(i, 1)[0]) } } // 将删除的项返回,返回后为新数组 return removed.reverse() } else { // 这个判断是为了实现如果数组项是字符串也能删除 if (typeof index !== 'number') { index = this.indexOf(index) } if (index > -1) { return this.splice(index, 1)[0] } } }, replace: function (index, data) { if (typeof index === 'function') { var i = this.length, replaced = [], replacer while (i--) { replacer = index(this[i]) /* 这里之所以不是直接判断if(replacer)是因为这里的目的就是实现替换功能,而如果值不是undefined说明用户有返回值而只要有返回值就应该给它替换。 */ if (replacer !== undefined) { replaced.push(this.splice(i, 1, replacer)[0]) } } return replaced.reverse() } else { if (typeof index !== 'number') { index = this.indexOf(index) } if (index > -1) { return this.splice(index, 1, data)[0] } } } } for (var method in extensions) { // 给ArrayProxy原型添加remove,replace方法,并且监听 def(ArrayProxy, method, extensions[method], !hasProto) }
实现监听对象方法
/** * 根据类型观察对象,入口 */ function watch (obj, path, observer) { var type = typeOf(obj) if (type === 'Object') { watchObject(obj, path, observer) } else if (type === 'Array') { watchArray(obj, path, observer) } } /** * 监听对象变化,但不监听对象中开头为$和_的属性和方法,入口 */ function watchObject (obj, path, observer) { for (var key in obj) { var keyPrefix = key.charAt(0) if (keyPrefix !== '$' && keyPrefix !== '_') { bind(obj, key, path, observer) } } } /** * 监听数组方法,并将其原型挂载到ArrayProxy上,入口 * ArrayProxy方法实现了一些变异的数组方法以及扩展,这是实现对数组方法监听的基础 */ function watchArray (arr, path, observer) { def(arr, '__observer__', observer) observer.path = path if (hasProto) { arr.__proto__ = ArrayProxy } else { for (var key in ArrayProxy) { def(arr, key, ArrayProxy[key]) } } } /* * 具体实现对象监听的方法 */ function bind (obj, key, path, observer) { var val = obj[key], watchable = isWatchable(val), values = observer.values, fullKey = (path ? path + '.' : '') + key values[fullKey] = val // 触发set事件 observer.emit('set', fullKey, val) Object.defineProperty(obj, key, { enumerable: true, get: function () { // only emit get on tip values if (depsOb.active && !watchable) { observer.emit('get', fullKey) } return values[fullKey] }, set: function (newVal) { values[fullKey] = newVal ensurePaths(key, newVal, values) observer.emit('set', fullKey, newVal) // 被赋值,监听新对象 watch(newVal, fullKey, observer) } }) watch(val, fullKey, observer) } /** * 只监听数组和对象 */ function isWatchable (obj) { var type = typeOf(obj) return type === 'Object' || type === 'Array' }
相关文章推荐
- 读vue-0.6-filters.js源码
- the super tiny vue.js(源码不超过200行)
- Vue.js 源码学习笔记 -- 分析前准备1 -- vue三大利器
- 从Vue.js源码看异步更新DOM策略及nextTick
- Vue学习之源码分析--Vue.js依赖收集(二)
- 从template到DOM(Vue.js源码角度看内部运行机制)
- vueJs源码解读0-2
- Vue学习之源码分析--Virtual DOM与diff(Vue.js实现)(六)
- jQuery+vue.js实现的九宫格拼图游戏完整实例【附源码下载】
- 从Vue.js源码角度再看数据绑定
- Vue学习之源码分析--Vue.js事件机制(四)
- Vue.js 源码学习笔记 - 细节
- 从vue.js的源码分析,input和textarea上的v-model指令到底做了什么
- vue.js实现含搜索的多种复选框(附源码)
- 从Vue.js源码看异步更新DOM策略及nextTick
- 源码分析Vue.js的监听实现教程
- Vue.js实现表格动态增加删除的方法(附源码下载)
- vue如何实现observer和watcher源码解析
- Vue.js 2.0源码解析之前端渲染篇
- 深入理解Vue.js源码之事件机制