




已阅读5页,还剩8页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Vue 源码解析:深入响应式原理(上)Vue.js 最显著的功能就是响应式系统,它是一个典型的 MVVM 框架,模型(Model)只是普通的 JavaScript 对象,修改它则视图(View)会自动更新。这种设计让状态管理变得非常简单而直观,不过理解它的原理也很重要,可以避免一些常见问题。下面让我们深挖 Vue.js 响应式系统的细节,来看一看 Vue.js 是如何把模型和视图建立起关联关系的。如何追踪变化我们先来看一个简单的例子。代码示例如下: count: times var vm = new Vue( el: #main, data: function () return times: 1 ; , created: function () var me = this; setInterval(function () me.times+; , 1000); );运行后,我们可以从页面中看到,count 后面的 times 每隔 1s 递增 1,视图一直在更新。在代码中仅仅是通过 setInterval 方法每隔 1s 来修改 vm.times 的值,并没有任何 DOM 操作。那么 Vue.js 是如何实现这个过程的呢?我们可以通过一张图来看一下,如下图所示:图中的模型(Model)就是 data 方法返回的times:1,视图(View)是最终在浏览器中显示的DOM。模型通过Observer、Dep、Watcher、Directive等一系列对象的关联,最终和视图建立起关系。归纳起来,Vue.js在这里主要做了三件事: 通过 Observer 对 data 做监听,并且提供了订阅某个数据项变化的能力。 把 template 编译成一段 document fragment,然后解析其中的 Directive,得到每一个 Directive 所依赖的数据项和update方法。 通过Watcher把上述两部分结合起来,即把Directive中的数据依赖通过Watcher订阅在对应数据的 Observer 的 Dep 上。当数据变化时,就会触发 Observer 的 Dep 上的 notify 方法通知对应的 Watcher 的 update,进而触发 Directive 的 update 方法来更新 DOM 视图,最后达到模型和视图关联起来。接下来我们就结合 Vue.js 的源码来详细介绍这三个过程。Observer首先来看一下 Vue.js 是如何给 data 对象添加 Observer 的。我们知道,Vue 实例创建的过程会有一个生命周期,其中有一个过程就是调用 vm.initData 方法处理 data 选项。initData 方法的源码定义如下:Vtotype._initData = function () var dataFn = this.$options.data var data = this._data = dataFn ? dataFn() : if (!isPlainObject(data) data = process.env.NODE_ENV != production & warn( data functions should return an object., this ) var props = this._props / proxy data on instance var keys = Object.keys(data) var i, key i = keys.length while (i-) key = keysi / there are two scenarios where we can proxy a data key: / 1. its not already defined as a prop / 2. its provided via a instantiation option AND there are no / template prop present if (!props | !hasOwn(props, key) this._proxy(key) else if (process.env.NODE_ENV != production) warn( Data field + key + is already defined + as a prop. To provide default value for a prop, use the default + prop option; if you want to pass prop values to an instantiation + call, use the propsData option., this ) / observe data observe(data, this) 在 initData 中我们要特别注意 proxy 方法,它的功能就是遍历 data 的 key,把 data 上的属性代理到 vm 实例上。_proxy 方法的源码定义如下:Vtotype._proxy = function (key) if (!isReserved(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._datakey , set: function proxySetter (val) self._datakey = val ) proxy 方法主要通过 Object.defineProperty 的 getter 和 setter 方法实现了代理。在前面的例子中,我们调用 vm.times 就相当于访问了 vm.data.times。在 _initData 方法的最后,我们调用了 observe(data, this) 方法来对 data 做监听。observe 方法的源码定义如下:export function observe (value, vm) if (!value | typeof value != object) return var ob if ( hasOwn(value, _ob_) & value._ob_ instanceof Observer ) ob = value._ob_ else if ( shouldConvert & (isArray(value) | isPlainObject(value) & Object.isExtensible(value) & !value._isVue ) ob = new Observer(value) if (ob & vm) ob.addVm(vm) return obobserve 方法首先判断 value 是否已经添加了 ob 属性,它是一个 Observer 对象的实例。如果是就直接用,否则在 value 满足一些条件(数组或对象、可扩展、非 vue 组件等)的情况下创建一个 Observer 对象。接下来我们看一下 Observer 这个类,它的源码定义如下:export function Observer (value) this.value = value this.dep = new Dep() def(value, _ob_, this) if (isArray(value) var augment = hasProto ? protoAugment : copyAugment augment(value, arrayMethods, arrayKeys) this.observeArray(value) else this.walk(value) Observer 类的构造函数主要做了这么几件事:首先创建了一个 Dep 对象实例(关于 Dep 对象我们稍后作介绍);然后把自身 this 添加到 value 的 ob 属性上;最后对 value 的类型进行判断,如果是数组则观察数组,否则观察单个元素。其实 observeArray 方法就是对数组进行遍历,递归调用 observe 方法,最终都会调用 walk 方法观察单个元素。接下来我们看一下 walk 方法,它的源码定义如下:Ototype.walk = function (obj) var keys = Object.keys(obj) for (var i = 0, l = keys.length; i l; i+) this.convert(keysi, objkeysi) walk 方法是对 obj 的 key 进行遍历,依次调用 convert 方法,对 obj 的每一个属性进行转换,让它们拥有 getter、setter 方法。只有当 obj 是一个对象时,这个方法才能被调用。接下来我们看一下 convert 方法,它的源码定义如下:Ototype.convert = function (key, val) defineReactive(this.value, key, val)convert 方法很简单,它调用了 defineReactive 方法。这里 this.value 就是要观察的 data 对象,key 是 data 对象的某个属性,val 则是这个属性的值。defineReactive 的功能是把要观察的 data 对象的每个属性都赋予 getter 和 setter 方法。这样一旦属性被访问或者更新,我们就可以追踪到这些变化。接下来我们看一下 defineReactive 方法,它的源码定义如下:export function defineReactive (obj, key, val) var dep = new Dep() var property = Object.getOwnPropertyDescriptor(obj, key) if (property & property.configurable = false) return / cater for pre-defined getter/setters var getter = property & property.get var setter = property & property.set var childOb = observe(val) Object.defineProperty(obj, key, enumerable: true, configurable: true, get: function reactiveGetter () var value = getter ? getter.call(obj) : val if (Dep.target) dep.depend() if (childOb) childOb.dep.depend() if (isArray(value) for (var e, i = 0, l = value.length; i l; i+) e = valuei e & e._ob_ & e._ob_.dep.depend() return value , set: function reactiveSetter (newVal) var value = getter ? getter.call(obj) : val if (newVal = value) return if (setter) setter.call(obj, newVal) else val = newVal childOb = observe(newVal) dep.notify() )defineReactive 方法最核心的部分就是通过调用 Object.defineProperty 给 data 的每个属性添加 getter 和setter 方法。当 data 的某个属性被访问时,则会调用 getter 方法,判断当 Dep.target 不为空时调用 dep.depend 和 childObj.dep.depend 方法做依赖收集。如果访问的属性是一个数组,则会遍历这个数组收集数组元素的依赖。当改变 data 的属性时,则会调用 setter 方法,这时调用 dep.notify 方法进行通知。这里我们提到了 dep,它是 Dep 对象的实例。接下来我们看一下 Dep 这个类,它的源码定义如下:export default function Dep () this.id = uid+ this.subs = / the current target watcher being evaluated./ this is globally unique because there could be only one/ watcher being evaluated at any time.Dep.target = nullDep 类是一个简单的观察者模式的实现。它的构造函数非常简单,初始化了 id 和 subs。其中 subs 用来存储所有订阅它的 Watche
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 灯具店转让合同范本
- 检验工作心得体会和感悟(汇编10篇)
- 金融科技支付服务消费金融模式用户消费方式提升创新性
- 2025年高考日语试卷及答案
- 需求工程试题及答案
- 2025年康复解剖大题题库及答案
- 汤沟酒厂招聘考试试题及答案
- 2025年山西特岗教师招聘考试试题(附答案)
- CN222961012U 一种欧式双梁桥式起重机 (河南力富特起重运输机械有限公司)
- 2025年船舶测速题库及答案
- 开学第一课 教学设计-2024-2025学年七年级上学期道德与法治部编版
- 有机肥采购合同书
- 团建活动申请书
- 压力疏导培训课件
- 肠造口回纳手术
- 篮球场改造工程施工组织设计方案
- 业务知识演讲稿:“三重一大”事项集体决策制度规范运用的思考
- 起搏器植入围手术期护理
- 《数学(第8版 上册)》 课件 第1章 运算与方程
- 中学生天文知识竞赛考试题库500题(含答案)
- 儿童英语小故事100篇englishforchildren
评论
0/150
提交评论