您的位置:首页 > 产品设计 > UI/UE

Vue源码解析(一)

2017-07-23 18:25 387 查看
使用vue已经大半年时间了,写起来已经很顺手了。比起之前的jquery时代,vue配合webpack的使用,让前端代码整体性更强,代码量也比之前少了一些。用了一段时间后,也想了解Vue具体是如何实现这样的一个mvvm框架的,经过一段时间的挣扎,终于开始了源码解析。ps:本次解读的vue源码版本为2.3.3,本人水平有限,如果解析有误的地方,请批评指正。

首先,不管采用什么方式引入vue,直接script引入,requireJs引入等等等,vue都会进行相关的初始化工作。

function Vue$3 (options) {
if ("development" !== 'production' &&
!(this instanceof Vue$3)
) {
warn('Vue is a constructor and should be called with the `new` keyword');
}
this._init(options);
}

initMixin(Vue$3);
stateMixin(Vue$3);
eventsMixin(Vue$3);
lifecycleMixin(Vue$3);
renderMixin(Vue$3);
//全局Api的代码在比较靠下的位置
initGlobalAPI(Vue$3);


定义了Vue$3这个类,接着调用了initMixin、stateMixin、eventsMixin、lifecycleMixin、renderMixin、initGlobalAPI几个方法。

var uid$1 = 0;

function initMixin (Vue) {
Vue.prototype._init = function (options) {
var vm = this;
// a uid
vm._uid = uid$1++;

var startTag, endTag;
/* istanbul ignore if */
if ("development" !== 'production' && config.performance && mark) {
startTag = "vue-perf-init:" + (vm._uid);
endTag = "vue-perf-end:" + (vm._uid);
mark(startTag);
}

// a flag to avoid this being observed
vm._isVue = true;
// merge options
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options);
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
);
}
/* istanbul ignore else */
{
initProxy(vm);
}
// expose real self
vm._self = vm;
initLifecycle(vm);
initEvents(vm);
initRender(vm);
callHook(vm, 'beforeCreate');
initInjections(vm); // resolve injections before data/props
initState(vm);
initProvide(vm); // resolve provide after data/props
callHook(vm, 'created');

/* istanbul ignore if */
if ("development" !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false);
mark(endTag);
measure(((vm._name) + " init"), startTag, endTag);
}

if (vm.$options.el) {
vm.$mount(vm.$options.el);
}
};
}


initMixin方法,定义了Vue$3的_init的方法。startTag, endTag,mark为开启performance模式同时浏览器支持Performance.mark()方法时,在浏览器开发工具中对组件进行性能追踪。根据_isComponent是否为true调用initInternalComponent或mergeOptions进行options合并。

function initLifecycle (vm) {
var options = vm.$options;

// locate first non-abstract parent
var parent = options.parent;
if (parent && !options.abstract) {
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent;
}
parent.$children.push(vm);
}

vm.$parent = parent;
vm.$root = parent ? parent.$root : vm;

vm.$children = [];
vm.$refs = {};

vm._watcher = null;
vm._inactive = null;
vm._directInactive = false;
vm._isMounted = false;
vm._isDestroyed = false;
vm._isBeingDestroyed = false;
}


生命周期初始化,找到第一个非抽象parent,在$children数组中添加vm。
$children
$refs
等设置初始值。

function initEvents (vm) {
vm._events = Object.create(null);
vm._hasHookEvent = false;
// init parent attached events
var listeners = vm.$options._parentListeners;
if (listeners) {
updateComponentListeners(vm, listeners);
}
}


事件初始化,如果有父组件监听调用updateComponentListeners更新监听。

function initRender (vm) {
vm._vnode = null; // the root of the child tree
vm._staticTrees = null;
var parentVnode = vm.$vnode = vm.$options._parentVnode; // the placeholder node in parent tree
var renderContext = parentVnode && parentVnode.context;
vm.$slots = resolveSlots(vm.$options._renderChildren, renderContext);
vm.$scopedSlots = emptyObject;
// bind the createElement fn to this instance
// so that we get proper render context inside it.
// args order: tag, data, children, normalizationType, alwaysNormalize
// internal versi
cbc2
on is used by render functions compiled from templates
vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); };
// normalization is always applied for the public version, used in
// user-written render functions.
vm.$createElement = function (a, b, c, d) { return createElement(vm, a, b, c, d, true); };
}


渲染初始化,调用resolveSlots方法返回vm子组件slots。渲染初始化结束后callHook(vm, ‘beforeCreate’)方法下发beforeCreate事件。

function initInjections (vm) {
var result = resolveInject(vm.$options.inject, vm);
if (result) {
Object.keys(result).forEach(function (key) {
/* istanbul ignore else */
{
defineReactive$$1(vm, key, result[key], function () {
warn(
"Avoid mutating an injected value directly since the changes will be " +
"overwritten whenever the provided component re-renders. " +
"injection being mutated: \"" + key + "\"",
vm
);
});
}
});
}
}


注入初始化。

function initState (vm) {
vm._watchers = [];
var opts = vm.$options;
if (opts.props) { initProps(vm, opts.props); }
if (opts.methods) { initMethods(vm, opts.methods); }
if (opts.data) {
initData(vm);
} else {
observe(vm._data = {}, true /* asRootData */);
}
if (opts.computed) { initComputed(vm, opts.computed); }
if (opts.watch) { initWatch(vm, opts.watch); }
}


初始化props、methods、data、computed、watch。

function initProvide (vm) {
var provide = vm.$options.provide;
if (provide) {
vm._provided = typeof provide === 'function'
? provide.call(vm)
: provide;
}
}


初始化provide。provide初始化结束后,调用callHook(vm, ‘created’)方法下发created事件。

if (vm.$options.el) {
vm.$mount(vm.$options.el);
}


最后,如果option中el存在,挂载元素到对应的el上,之后进行组件的渲染。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  vue 源码 解析 前端