关于 Vue.js 的原理一直以来都是一个话题。通过几天的源码学习和资料介绍,我将一些我的理解的经验给写下来,但愿可以与你们共勉。javascript
附上GITHUB源码地址, 若是有任何不解 能够在 文章下面提出或者写下issue, 方便你们回答和学习, 有兴趣能够Star.
最后附上 LIVE DEMOvue
应用建立时须要使用的构造函数对象, data
为咱们的数据模型java
const vm = new Vue({ data: { foo: 'hello world' } });
被观察对象,data属性里的值的变化,get
时检查是否有新的观察员须要加入观察员集合, set
时通知观察员集合里的观察员更新视图,被观察者有一个观察员的集合对象。react
一个观察员的收集器, depend()
负责将当前的 Dep.target
观察员加入观察员集合, data
中的每一项数据都会有对应的闭包dep
对象, 数据对象会有一个内置的dep
对象,用来通知嵌套的数据对象变化的状况。git
由模板解析指令建立的观察员, 负责模板中的更新视图操做。保留旧的数据,以及设置钩子函数 update()
, 等待被观察者数据通知更新,对比新的value与旧数据, 从而更新视图。github
咱们的关注点在与建立后的vm
, 其 options.data
, 被挂载至vm._data
, 同时被代理至 vm
上, 以致于 vm._data.foo
等价于 vm.foo
, 代理函数代码以下:缓存
const noop = function () {} const sharedPropertyDefinition = { enumerable: true, configurable: true, get: noop, set: noop } function proxy (target, sourceKey, key) { sharedPropertyDefinition.get = function proxyGetter () { return this[sourceKey][key] } sharedPropertyDefinition.set = function proxySetter (val) { this[sourceKey][key] = val } Object.defineProperty(target, key, sharedPropertyDefinition) } // initState 时执行 initData function initData () { const keys = Object.keys(data) let i = keys.length while (i--) { const key = keys[i] // key不以 $ 或 _开头 if (!isReserved(key)) { proxy(vm, `_data`, key) } } // do something }
/** * Define a reactive property on an Object. */ function defineReactive(obj, key, val) { // 观察员集合 const dep = new Dep(); // data 内属性描述 const property = Object.getOwnPropertyDescriptor(obj, key); // 属性不可再次修改 if (property && property.configurable === false) { return; } // 属性预约义 getter/setters const getter = property && property.get; const setter = property && property.set; // 若是val为对象, 获取val的 被观察数据对象 __ob__ let childOb = observe(val); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter() { // 被观察数据被使用时, 获取被观察员最新的数据 const value = getter ? getter.call(obj) : val // 观察员在new时或使用 get()时, 注入给被观察员对象集合 if (Dep.target) { // 将当前的 watcher 传递给 观察员 dep.depend(); if (childOb) { // 将当前的 watcher 传递给子对象的 观察员 childOb.dep.depend(); } } return val; }, set: function reactiveSetter(newVal) { const value = getter ? getter.call(obj) : val; if (newVal === value || (newVal !== newVal && value !== value)) { return; } if (setter) { setter.call(obj, newVal); } else { val = newVal; } // 新value设置被观察者对象 __ob__ childOb = observe(newVal); // 通知数据对象依赖的观察员, 更新 update() dep.notify(); } }); }
计算属性的定义和使用闭包
var vm = new Vue({ data: { firstname: 'li', lastname: 'yanlong' }, computed: { fullname () { return this.firstname + this.lastname; } } }); console.log(vm.fullname);
const computedWatcherOptions = {lazy: true}; function initComputed (vm, computedOptions) { // 建立计算属性对应的观察员对象 // 获取计算属性时收集 内置数据对象的 dep const watchers = vm._computedWatchers = Object.create(null) for (const key in computed) { const userDef = computed[key] const getter = typeof userDef === 'function' ? userDef : userDef.get // create internal watcher for the computed property. watchers[key] = new Watcher( vm, getter || noop, noop, computedWatcherOptions ); if (!(key in vm)) { defineComputed(vm, key, userDef) } } } function defineComputed (target, key, userDef) { // 若是不为服务端渲染,则使用缓存value const shouldCache = !isServerRendering() // sharedPropertyDefinition 共享属性配置 if (typeof userDef === 'function') { sharedPropertyDefinition.get = shouldCache ? createComputedGetter(key) : userDef sharedPropertyDefinition.set = noop } else { sharedPropertyDefinition.get = userDef.get ? shouldCache && userDef.cache !== false ? createComputedGetter(key) : userDef.get : noop sharedPropertyDefinition.set = userDef.set ? userDef.set : noop } // 给 vm对象定义计算属性 Object.defineProperty(target, key, sharedPropertyDefinition) } // 建立 function createComputedGetter (key) { return function computedGetter () { const watcher = this._computedWatchers && this._computedWatchers[key] if (watcher) { // 计算属性的 watcher 有数据更新过, 从新计算 if (watcher.dirty) { watcher.evaluate() } // 视图指令 使用了计算属性 // 将计算属性的watcher依赖传递给视图指令的 watcher if (Dep.target) { // 源码地址 // https://github.com/vuejs/vue/blob/master/src/core/observer/watcher.js#L210 watcher.depend() } return watcher.value } } }
1. 计算属性的 watcher
对象
计算属性函数在读取它自己的value
时, 使用一个watcher
观察员进行代理. 经过对原始数据的劫持, 将watcher
观察员添加到原始数据的dep
依赖集合中. mvvm
2. deps
的数据对象发生更新
举例,若是firstname
或者 lastname
任意一个更新,那么就会设置计算属性的watcher.dirty = true
, 而当其它指令或者函数使用,计算属性会从新计算值,若是是视图指令,还会从新该指令的watcher
的数据对象依赖。函数
目前了解状况来看, 主要分三类
watcher
watcher
watcher