本文vue 版本为 2.5.17, 分析的是 Runtime + Compiler 构建出来的 Vue.js。 在 Vue.js 2.0 中,最终都是经过 render 函数渲染,若是含有 template 属性,则须要将 template 编译成 render 函数,那么这个编译过程会发⽣运⾏时,因此须要带有Compiler编译器的版本。本文为vue源码介绍系列的第一篇,主要概括整合vue实例化,将render函数转为vnode到生成挂载真实dom主要流程,具体细节请查看源码。第二篇将介绍组件化过程。vue
具体调试能够下载vue.js,而后具体作断点debugger调试。node
<script src="./vue.js"></script> <div id="app"></div> <script> var vm = new Vue({ el: '#app', render(h) { return h( 'div', { attr: { class: 'classname' } }, [ 'first item', h('h2', { style: {color: 'orange' } }, 'second item') ] ) }, data: { message: 'Hello', } }) </script>
先上图分析流程浏览器
function Vue (options) { if ("development" !== 'production' && !(this instanceof Vue) ) { warn('Vue is a constructor and should be called with the `new` keyword'); } this._init(options); } initMixin(Vue); // 定义 _init stateMixin(Vue); // 定义 $set $get $delete $watch 等 eventsMixin(Vue); // 定义事件 $on $once $off $emit lifecycleMixin(Vue); // 定义 _update $forceUpdate $destroy renderMixin(Vue); // 定义 _render 返回虚拟dom
实例化Vue时,执行 _init, _init 定义在 initMixin 中app
Vue.prototype._init = function (options) { // 合并 options if (options && options._isComponent) { initInternalComponent(vm, options); // 组件合并 } else { // 非组件合并 vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ); } initLifecycle(vm); // 定义 vm.$parent vm.$root vm.$children vm.$refs 等 initEvents(vm); // 定义 vm._events vm._hasHookEvent 等 initRender(vm); // 定义 $createElement $c callHook(vm, 'beforeCreate'); // 挂载 beforeCreate 钩子函数 initInjections(vm); // resolve injections before data/props initState(vm); // 初始化 props methods data computed watch 等方法 initProvide(vm); // resolve provide after data/props callHook(vm, 'created'); // 挂载 created 钩子函数 if (vm.$options.el) { vm.$mount(vm.$options.el); // 实例挂载渲染dom } };
vue最终都是经过render函数将dom编译为虚拟domdom
// 构建render函数 if (!options.render) { // 若是没有render属性,那么将template模版编译转为render } // 最后调用 mount return mount.call(this, el, hydrating) // mount 调用 mountComponent return mountComponent(this, el, hydrating)
经过 new Watcher 调用执行 updateComponent, vm._render获取虚拟dom, vm._update将虚拟dom转为真实的dom并挂载到页面ide
// hydrating 表明服务端渲染 hydrating => false updateComponent = function () { vm._update(vm._render(), hydrating); // 关键点 };
_render执行render函数 返回vnoe函数
Vue.prototype._render = function () { // 此处的 vm._renderProxy 等价于 vm vnode = render.call(vm._renderProxy, vm.$createElement); }
$createElement 主要是参数重载,整合为统一格式后调用 _createElement函数oop
function createElement ( context, tag, data, children, normalizationType, alwaysNormalize) { // 参数重载 if (Array.isArray(data) || isPrimitive(data)) { normalizationType = children; children = data; data = undefined; } if (isTrue(alwaysNormalize)) { normalizationType = ALWAYS_NORMALIZE; } return _createElement(context, tag, data, children, normalizationType) }
_createElement 主要是根据 tag 标签判断是组件仍是普通node标签,返回对应的vnode虚拟dom组件化
function _createElement ( context, tag, data, children, normalizationType) { if (typeof tag === 'string') { // platform built-in elements vnode = new VNode( config.parsePlatformTagName(tag), data, children, undefined, undefined, context ); }else{ // direct component options / constructor vnode = createComponent(tag, data, context, children); } }
_update 主要实现 vnode 转化为实际的dom, 注入到页面的同时并销毁页面模版。ui
定义 _update
// _update => __patch__ Vue.prototype._update = function (vnode, hydrating) { if (!prevVnode) { // 初始化时 vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */); } else { // 更新时 vm.$el = vm.__patch__(prevVnode, vnode); } }
定义 __patch__
// __patch__ => patch Vue.prototype.__patch__ = inBrowser ? patch : noop;
定义 patch,
// 利用函数柯里化,将服务端和浏览器的差别集成到modules, nodeOps为dom元素操做方法集合 var modules = platformModules.concat(baseModules); var patch = createPatchFunction({ nodeOps: nodeOps, modules: modules });
定义 createPatchFunction
function createPatchFunction (backend) { return function patch (oldVnode, vnode, hydrating, removeOnly) { // 建立新节点 createElm( vnode, insertedVnodeQueue, oldElm._leaveCb ? null : parentElm, nodeOps.nextSibling(oldElm) ); // 销毁节点 if (isDef(parentElm)) { removeVnodes(parentElm, [oldVnode], 0, 0); } else if (isDef(oldVnode.tag)) { invokeDestroyHook(oldVnode); } } }
定义 createElm, 根据vnode建立真实dom
function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested, ownerArray, index) { // createChildren函数由子到父,深序递归调用createElm createChildren(vnode, children, insertedVnodeQueue); if (isDef(data)) { invokeCreateHooks(vnode, insertedVnodeQueue); } // 子节点插入到父节点 insert(parentElm, vnode.elm, refElm); }
以上就是vue从实例化,到调用render函数生成vnode,vnode经过patch转为真实dom节点,并挂载到页面的流程状况。接下来将第二篇将接扫vue组件化和生命周期过程。