Vue源码解析(一)
2017-07-23 18:25
387 查看
使用vue已经大半年时间了,写起来已经很顺手了。比起之前的jquery时代,vue配合webpack的使用,让前端代码整体性更强,代码量也比之前少了一些。用了一段时间后,也想了解Vue具体是如何实现这样的一个mvvm框架的,经过一段时间的挣扎,终于开始了源码解析。ps:本次解读的vue源码版本为2.3.3,本人水平有限,如果解析有误的地方,请批评指正。
首先,不管采用什么方式引入vue,直接script引入,requireJs引入等等等,vue都会进行相关的初始化工作。
定义了Vue$3这个类,接着调用了initMixin、stateMixin、eventsMixin、lifecycleMixin、renderMixin、initGlobalAPI几个方法。
initMixin方法,定义了Vue$3的_init的方法。startTag, endTag,mark为开启performance模式同时浏览器支持Performance.mark()方法时,在浏览器开发工具中对组件进行性能追踪。根据_isComponent是否为true调用initInternalComponent或mergeOptions进行options合并。
生命周期初始化,找到第一个非抽象parent,在$children数组中添加vm。
事件初始化,如果有父组件监听调用updateComponentListeners更新监听。
渲染初始化,调用resolveSlots方法返回vm子组件slots。渲染初始化结束后callHook(vm, ‘beforeCreate’)方法下发beforeCreate事件。
注入初始化。
初始化props、methods、data、computed、watch。
初始化provide。provide初始化结束后,调用callHook(vm, ‘created’)方法下发created事件。
最后,如果option中el存在,挂载元素到对应的el上,之后进行组件的渲染。
首先,不管采用什么方式引入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上,之后进行组件的渲染。
相关文章推荐
- 自己实现MVVM(Vue源码解析)
- Vue.js解析(三)【从Vue.js源码角度再看数据绑定】
- Vue源码解析(二)
- Vue如何实现组件的源码解析
- Vue源码解析(五)
- Vue 源码解析:深入响应式原理
- Vue之Watcher源码解析(2)
- vue数据控制视图源码解析
- vue源码解析之--数据双向绑定
- Vue.js 源码学习七 —— template 解析过程学习
- vue源码解析之插件入侵机制
- vue源码解析之事件机制原理
- Vue源码学习之初始化模块init.js解析
- Vue.js 源码学习八 —— HTML解析细节学习
- Vue之Watcher源码解析(1)
- Vue.js 2.0源码解析之前端渲染篇
- Vue-lazyload原理详解之源码解析
- Vue.js源码解析(七)【聊聊Vue.js的template编译】
- Vue源码解析(三)
- Vue.js源码解析(八)【Vue.js异步更新DOM策略及nextTick】