Vue源码 --- 从入门到放弃
Vue 源码分析本文是根据源码 Vue.js v2.0.2 进行分析,内容主要针对options中最常见数据项props、data、computed、methods、watch的工作原理及流程进行详细说明。描述上,重于关键点的罗列,部分未能详细阐述。建议与这篇文章对比学习。
·
Vue 源码分析
本文是根据源码 Vue.js v2.0.2 进行分析,内容主要针对options中最常见数据项props、data、computed、methods、watch的工作原理及流程进行详细说明。描述上,重于关键点的罗列,部分未能详细阐述。建议与这篇文章对比学习。
function initState (vm) {
vm._watchers = [];
initProps(vm);
initData(vm);
initComputed(vm);
initMethods(vm);
initWatch(vm);
}
initState
- new Vue() 时,调用 _init 初始化,=> initState
_watchers
- 在每个vue实例上定义一个_watchers空数组
- 在该实例的数据属性发生 new Watcher() 时,将当前watcher放入数组中
- 当该实例销毁或主动调用unwatch时,将当前watcher从数组中移除
initProps
- 单独处理Boolean类型的值 (validateProp) ,三种写法:
<x-component :xxx="true"></x-component>
<x-component xxx></x-component>
<x-component xxx="xxx"></x-component>
- 校验prop值的合法性 assertProp 方法
- 核心方法 defineReactive$$1 来设置属性的getter和setter,但当props属性被setter时,触发回调来warn不可修改
initData
vm._data = data
将当前实例属性$options的data赋值给实例的_data属性- 判断data中是否有与props中相同的属性,warning
- proxy 代理处理,将data中的每个属性getter和setter到vm实例上,getter和setter直接转向了_data的getter和setter方法
- 观察_data到设置getter和setter,observe(data) => new Observe() => observe.walk => defineReactive$$1
插入Watcher 先看下方脚注
initComputed
initComputed 其实也是将computed中的每个属性通过
Object.defineProperty
定义到vm上,只不过每个属性的getter是在computed中自定义的,而默认setter为空。当属性为对象时,可显示声明set和get方法。
- isFunction => 设置getter和setter,setter默认为空。getter通过new Watcher过度一层到自定义的get方法
- isObject => 设置getter和setter,setter直接bind到自定义set方法,getter通过new Watcher过度一层到自定义的get方法
initMethods
将methods中的方法一一绑定到vm实例上
initWatch
- createWatcher => 判断handler类型
- isObject =>
handler = handler.handler
其他属性:deep, immediate, sync, lazy
deep: true
递归watchimmediate: true
在创建watcher时就执行一次handler方法sync: true
同步,直接执行watcher.run,否则要排队到queue数组中,等到nextTick异步到这里再依次执行lazy: true
在创建watcher时是否需要立即计算当前watcher的value值,通过computed属性创建的watcher默认lazy=true
不需要计算
- isString =>
handler = vm[method]
handler在vm的methods中查找对应的方法 - isFunction =>
handler = handler
- isObject =>
- vm.$watch => 执行实例的
$watch
方法,创建watcher,同时在这里判断immediate来决定是否执行一次handler
渲染流程
在页面挂载前所new的Watcher会被立即调用this.get(),来回调vm.update更新$el。而在更新前会先执行_render创建vnode。此时就会真正执行页面生成的render方法,获取页面所需的值,从而进入定义好的getter方法。
更新流程
当data中的属性进入到setter阶段,即进入_data属性的set方法时,会执行依赖修改
dep.notify()
。依赖修改的最终目的还是通过执行watcher.run来获取属性值,并回调handler,最终更新到vm的$el上。
流程:
- 改变属性值进入 set
- 依赖修改 dep.notify
- watcher更新 watcher.update
- 直接执行run或者排队进入queue后异步nextTick执行flushSchedulerQueue
- 根据watcher.id排序queue后,执行watcher.run
nextTick: 异步兼容
- Promise
- MutationObserver
- setTimeout
脚注
Watcher
var Watcher = function Watcher (
vm,
expOrFn,
cb,
options
) {
if ( options === void 0 ) options = {};
this.vm = vm;
vm._watchers.push(this);
// options
this.deep = !!options.deep;
this.user = !!options.user;
this.lazy = !!options.lazy;
this.sync = !!options.sync;
this.expression = expOrFn.toString();
this.cb = cb;
this.id = ++uid$1; // uid for batching
this.active = true;
this.dirty = this.lazy; // for lazy watchers
this.deps = [];
this.newDeps = [];
this.depIds = new _Set();
this.newDepIds = new _Set();
// parse expression for getter
if (typeof expOrFn === 'function') {
this.getter = expOrFn;
} else {
this.getter = parsePath(expOrFn);
if (!this.getter) {
this.getter = function () {};
"development" !== 'production' && warn(
"Failed watching path: \"" + expOrFn + "\" " +
'Watcher only accepts simple dot-delimited paths. ' +
'For full control, use a function instead.',
vm
);
}
}
this.value = this.lazy
? undefined
: this.get();
};
Watcher.prototype.run = function run () {
if (this.active) {
var value = this.get();
if (
value !== this.value ||
// Deep watchers and watchers on Object/Arrays should fire even
// when the value is the same, because the value may
// have mutated.
isObject(value) ||
this.deep
) {
// set new value
var oldValue = this.value;
this.value = value;
if (this.user) {
try {
this.cb.call(this.vm, value, oldValue);
} catch (e) {
"development" !== 'production' && warn(
("Error in watcher \"" + (this.expression) + "\""),
this.vm
);
/* istanbul ignore else */
if (config.errorHandler) {
config.errorHandler.call(null, e, this.vm);
} else {
throw e
}
}
} else {
this.cb.call(this.vm, value, oldValue);
}
}
}
};
更多推荐
已为社区贡献1条内容
所有评论(0)