八小时实现迷你版vuejs三:实现数据响应化
来源:互联网 发布:淘宝衣服店铺介绍 编辑:程序博客网 时间:2024/05/19 10:41
上一篇我们比较overview的讲了vuejs 的架构,从这一篇开始我们来自己动手实现一个vuejs。
首先我们实现数据响应化,数据响应化的意思就是我们要能监听到任何数据变动。比如执行了 this.name = “Yao Ming”
之后,我们要能监听到这个改动。那么怎么实现呢?我们需要借助 ES5 提供的新特性 getter/setter
。
构造函数 和 初始化
首先我们需要写一个 Vue 的构造函数,这里我们直接用ES6的Class语法实现一个Vue类:
class Vue { constructor (options) { this.init(options) } /** * 初始化的入口 */ init (options) { //... const el = document.querySelector(options.el) this._initState() //... }}
这里省略了不相关的代码,完整代码请参阅tiny-vue。在 init 函数里面我们会调用 this._initState() 来做state的初始化,为了方便起见,这里我们只处理 data,而跳过对 props 的处理。
为了更好的封装代码,我们把对state的处理放在了 state.js
中。
Proxy
当我们创建一个Vue实例的时候是这样的:
new Vue({ data: { //xxx}})
而访问data的时候是这样的 this.name=“xxx”
。显然vuejs会读取data中定义的数据,并把它“代理”到 this
上,这一步是也是通过getter/setter
来实现的。
思路就是:遍历 data
的所有 key
,然后在 this
上对每一个key也做一个 getter/setter
,在 getter/setter
内部依然去访问 data
上对应的key。
为了方便起见,首先给 data
做一个别名 this._data = options.data
。
这是我们在 state.js
中的主要代码:
Vue.prototype._initData = function () { var dataFn = this.$options.data var data = this._data = dataFn ? ( typeof dataFn == 'function' ? dataFn() : dataFn ) : {} var keys = Object.keys(data) var i, key i = keys.length while (i--) { key = keys[i] this._proxy(key) } // observe data observe(data, this) } Vue.prototype._proxy = function (key) { // need to store ref to self here // because these getter/setters might // be called by child scopes via // prototype inheritance. var self = this Object.defineProperty(self, key, { configurable: true, enumerable: true, get: function proxyGetter () { return self._data[key] }, set: function proxySetter (val) { self._data[key] = val } }) }
这样,当我们在 this
上做读写操作的时候,实际上是对 this._data
做的操作。
Observer
下一步,我们需要检测任何对 this._data
的读写操作,在 vuejs中,这是通过 Observer
来实现的。
Observer
的作用就是:对传入的数据设置 getter/setter
,当调用 getter
时收集依赖,当调用 setter
时通知依赖。
暂时我们不用太纠结这个依赖
是什么,在后面讲到 Watcher
的时候会仔细讲这部分。
所以 Observer 做的事情其实和上一步的 Proxy
很像。那么这里就有一个很有意思的问题,为什么不直接在 proxy
的getter和setter中去做Observe要做的事呢?这是因为vuejs还有一个 this.$data
的API,如果在proxy中做了,那么直接使用this.$data.name
就无法触发我们 getter/setter
,所以还是得在 this._data
本身上来实现。
我们需要创建一个 Observer 类:
import Dep from './dep.js'function Observer(value) { this.value = value this.dep = new Dep() // TODO: support Array this.walk(value)}// Instance methods/*** Walk through each property and convert them into* getter/setters. This method should only be called when* value type is Object.** @param {Object} obj*/Observer.prototype.walk = function (obj) { var keys = Object.keys(obj) for (var i = 0, l = keys.length; i < l; i++) { this.convert(keys[i], obj[keys[i]]) }}/*** Convert a property into getter/setter so we can emit* the events when the property is accessed/changed.** @param {String} key* @param {*} val*/Observer.prototype.convert = function (key, val) { defineReactive(this.value, key, val)}// …export function observe (value, vm) { const ob = new Observer(value) ob.addVm(vm) return ob}/*** Define a reactive property on an Object.** @param {Object} obj* @param {String} key* @param {*} val*/export function defineReactive (obj, key, val) { var dep = new Dep() //... Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { var value = getter ? getter.call(obj) : val if (Dep.target) { dep.depend() } return value }, set: function reactiveSetter (newVal) { var value = getter ? getter.call(obj) : val if (newVal === value) { return } val = newVal dep.notify() } })}
然后我们在 initData
中调用 observer(data)
就可以把 data
变成响应式的。
当执行 this.name =“xxx”
的时候,代码最终会进到 defineReactive
中的 reactiveSetter
中去。
这里的Dep其实是记录了 Observer
和 Watcher
的依赖关系,因为很可能存在多个 watcher
依赖同一个 observer
的情况,所以 Observer
中会创建一个 dep
用来记录到底哪些 watcher
依赖他。
单纯看数据响应化是比较简单的。下一步我们开始讲 Directive
的实现的时候就会比较复杂一些,也会涉及到 Observer
相关的内容。
注意:上面的代码并不是完整的,并且省略了一些比较简单的 (比如 lifecycle.js)代码,完整代码请参阅 tiny-vue
上一篇:Vuejs架构
- 八小时实现迷你版vuejs三:实现数据响应化
- 八小时实现迷你版vuejs之二:vuejs 架构
- 八小时实现迷你版vuejs五:实现Watcher
- 八小时实现迷你版vuejs六:实现常用指令
- 八小时实现迷你版vuejs: 一前言
- 八小时实现迷你版vuejs七:展望和结束语
- 八小时实现迷你版vuejs四:实现compile和Directive
- vuejs实现数据驱动视图原理
- QT5实现迷你版天气预报
- 自己动手开发音乐播放器《八》迷你界面的实现
- 基于vuejs菜单实现
- vueJs实现二级标题
- 自己实现迷你vector
- JS实现迷你日历
- VueJS实现用户管理系统
- vuejs实现iOS弹框 export
- Android版Web服务器实现(三)HTTP响应
- 三步实现响应式网页布局
- hdu4393 Throw nails【优先队列&&贪心&&模拟】
- android:text 与tools:text
- 【noi2001】方程的解数
- [Unity][Android]PC与Android设备传输的文章
- 【Android开发经验】移动设备的“声波通信/验证”的实现——SinVoice开源项目介绍(一)
- 八小时实现迷你版vuejs三:实现数据响应化
- Cannot change version of project facet Dynamic web module to 2.5
- 集成阿里百川的坑-【SDK初始化-iOS】读取身份图片AppKey失败
- 第六节、插件配置:配置js压缩
- Servlet基础:认识Servlet以及Tomcat容器等级
- opencv2 floodFill函数详解
- static,final用法总结
- bzoj1924 所驼门王的宝藏
- TVVJ 1473 校门外的树 线段树