创建Vue实例前,先了解下Vue的构造函数:
function Vue (options) {
if (!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword');
}
this._init(options);
}
当通过new关键字创建Vue实例时,会调用_init方法,此方法是initMixin函数内部定义在Vue原型链上的方法;
function initMixin (Vue) {
Vue.prototype._init = function (options) {
var vm = this;
// a uid Vue实例的一个唯一性标识
vm._uid = uid$3++;
var startTag, endTag;
/* istanbul ignore if */
if (config.performance && mark) {
startTag = "vue-perf-start:" + (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 {
//合并创建Vue实例的option和Vue构造函数选项
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);
//Vue的beforeCreate的钩子函数,此时访问数据数据和计算数据,不会产生监听;给属性赋新值不会生效;
callHook(vm, 'beforeCreate');
initInjections(vm); // resolve injections before data/props
initState(vm);
initProvide(vm); // resolve provide after data/props
//Vue的beforeCreate的钩子函数,此时访问数据数据和计算数据,不会产生监听;但是,可以给属性赋新值
callHook(vm, 'created');
/* istanbul ignore if */
if (config.performance && mark) {
vm._name = formatComponentName(vm, false);
mark(endTag);
measure(("vue " + (vm._name) + " init"), startTag, endTag);
}
if (vm.$options.el) {
//挂载实例
vm.$mount(vm.$options.el);
}
};
}
对performance感兴趣的同学,可以查看文章:window.performance(监控网页与程序性能)
vm.$options值为Vue构造函数选项和Vue实例选项的合并值,实例选项的值会覆盖构造函数选项的值;想详细了解可以直接查看mergeOptions函数;
initProxy是初始化Vue实例的一个代理(Proxy);
Proxy链接的文章需要注意hander.has会拦截的操作:
1 with 检查: with(proxy) { (foo); }
2 属性查询: foo in proxy
3 继承属性查询: foo in Object.create(proxy)
4 Reflect.has()
hander.get会拦截的操作:
1 访问属性: proxy[foo]和 proxy.bar;
2 访问原型链上的属性: Object.create(proxy)[foo]
3 Reflect.get();
initState函数中会调用如下初始化函数:
1 initProps 初始化子组件访问的父组件属性;
2 initMethods 初始化函数方法;
3 initData 初始化数据属性;
4 initComputed 初始化计算属性;
5 initWatch 初始化监听属性;
函数中定义了两个钩子函数:
var mount = Vue.prototype.$mount;
Vue.prototype.$mount = function (
el,
hydrating
) {
el = el && query(el);
/* istanbul ignore if */
if (el === document.body || el === document.documentElement) {
warn(
"Do not mount Vue to or - mount to normal elements instead."
);
return this
}
var options = this.$options;
// resolve template/el and convert to render function
if (!options.render) {
var template = options.template;
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template);
/* istanbul ignore if */
if (!template) {
warn(
("Template element not found or is empty: " + (options.template)),
this
);
}
}
} else if (template.nodeType) {
template = template.innerHTML;
} else {
{
warn('invalid template option:' + template, this);
}
return this
}
} else if (el) {
template = getOuterHTML(el);
}
if (template) {
/* istanbul ignore if */
if (config.performance && mark) {
mark('compile');
}
var ref = compileToFunctions(template, {
outputSourceRange: "development" !== 'production',
shouldDecodeNewlines: shouldDecodeNewlines,
shouldDecodeNewlinesForHref: shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this);
var render = ref.render;
var staticRenderFns = ref.staticRenderFns;
options.render = render;
options.staticRenderFns = staticRenderFns;
/* istanbul ignore if */
if (config.performance && mark) {
mark('compile end');
measure(("vue " + (this._name) + " compile"), 'compile', 'compile end');
}
}
}
return mount.call(this, el, hydrating)
};
1 $mount函数中compileToFunctions函数编译模板为AST的模型树;
2 调用mount.call(this, el, hydrating);源码如下:
// public mount method,vue实例和vue组件的公用挂载函数
Vue.prototype.$mount = function (
el,
hydrating
) {
el = el && inBrowser ? query(el) : undefined;
return mountComponent(this, el, hydrating)
};
注意点:
1 var mount = Vue.prototype.$mount;这行代码保存了Vue原型链中之前保存的函数;
2 compileToFunctions函数把template编译成AST的模型树,并生成了render的函数表达式;
里面定义了updateComponent函数:
updateComponent = function () {
vm._update(vm._render(), hydrating);
};
_render是在renderMixin 函数调用时定义的Vue原型链中的函数
function renderMixin (Vue) {
// install runtime convenience helpers
//渲染的一些函数放入Vue.prototype
installRenderHelpers(Vue.prototype);
Vue.prototype.$nextTick = function (fn) {
return nextTick(fn, this)
};
Vue.prototype._render = function () {
var vm = this;
var ref = vm.$options;
var render = ref.render;
var _parentVnode = ref._parentVnode;
if (_parentVnode) {
vm.$scopedSlots = normalizeScopedSlots(
_parentVnode.data.scopedSlots,
vm.$slots,
vm.$scopedSlots
);
}
// set parent vnode. this allows render functions to have access
// to the data on the placeholder node.
vm.$vnode = _parentVnode;
// render self
var vnode;
try {
// There's no need to maintain a stack becaues all render fns are called
// separately from one another. Nested component's render fns are called
// when parent component is patched.
currentRenderingInstance = vm;
//渲染视图的入口;参考关键字:installRenderHelpers
vnode = render.call(vm._renderProxy, vm.$createElement);
} catch (e) {
handleError(e, vm, "render");
// return error render result,
// or previous vnode to prevent render error causing blank component
/* istanbul ignore else */
if (vm.$options.renderError) {
try {
vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e);
} catch (e) {
handleError(e, vm, "renderError");
vnode = vm._vnode;
}
} else {
vnode = vm._vnode;
}
} finally {
currentRenderingInstance = null;
}
// if the returned array contains only a single node, allow it
if (Array.isArray(vnode) && vnode.length === 1) {
vnode = vnode[0];
}
// return empty vnode in case the render function errored out
if (!(vnode instanceof VNode)) {
if (Array.isArray(vnode)) {
warn(
'Multiple root nodes returned from render function. Render function ' +
'should return a single root node.',
vm
);
}
vnode = createEmptyVNode();
}
// set parent
vnode.parent = _parentVnode;
return vnode
};
}
_render 函数会执行render的函数表达式,创建出vnode节点;
_update是在lifecycleMixin 函数中定义的Vue原型链中的函数
function lifecycleMixin (Vue) {
//更新DOM进行展示
Vue.prototype._update = function (vnode, hydrating) {
var vm = this;
var prevEl = vm.$el;
var prevVnode = vm._vnode;
var restoreActiveInstance = setActiveInstance(vm);
vm._vnode = vnode;
// Vue.prototype.__patch__ is injected in entry points
// based on the rendering backend used.
if (!prevVnode) {
// initial render
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */);
} else {
// updates
vm.$el = vm.__patch__(prevVnode, vnode);
}
restoreActiveInstance();
// update __vue__ reference
if (prevEl) {
prevEl.__vue__ = null;
}
if (vm.$el) {
vm.$el.__vue__ = vm;
}
// if parent is an HOC, update its $el as well
if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
vm.$parent.$el = vm.$el;
}
// updated hook is called by the scheduler to ensure that children are
// updated in a parent's updated hook.
};
_update会根据patch(diff原理)函数,对比渲染前后的vnode的差异后,更新具体的视图(View);
函数中定义了两个钩子函数:
下一篇:实例学习Vue源码第三篇-Vue的响应式原理