new Vue 发生了什么
new
关键字表明实例化一个对象, 而Vue
其实是一个类, 源码位置是/src/core/instance/index.js。html
在 new Vue()
以后。 Vue 会调用 _init
函数进行初始化,也就是这里的 init 过程,它会初始化生命周期、事件、 props、 methods、 data、 computed 与 watch 等vue
源码 -> _initnode
export function initMixin (Vue: Class<Component>) { Vue.prototype._init = function (options?: Object) { const vm: Component = this // a uid vm._uid = uid++ let startTag, endTag /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && 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 { vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ) } /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { initProxy(vm) } else { vm._renderProxy = 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 (process.env.NODE_ENV !== 'production' && 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) } } }
在初始化时,会调用以上_init
中代码,生命周期就是经过 callHook
调用的
它的定义在 src/core/instance/lifecycle 中:git
export function callHook (vm: Component, hook: string) { // #7573 disable dep collection when invoking lifecycle hooks pushTarget() const handlers = vm.$options[hook] const info = `${hook} hook` if (handlers) { for (let i = 0, j = handlers.length; i < j; i++) { invokeWithErrorHandling(handlers[i], vm, null, vm, info) } } if (vm._hasHookEvent) { vm.$emit('hook:' + hook) } popTarget() }
callHook
函数的逻辑很简单,根据传入的字符串 hook
,去拿到 vm.$options[hook]
对应的回调函数数组,而后遍历执行,执行的时候把 vm
做为函数执行的上下文。github
callhook
函数的功能就是调用某个生命周期钩子注册的全部回调函数。web
初始化最核心的逻辑是这段:vue-router
beforeCreate
和 created
函数都是在实例化 Vue
的阶段,在 _init
方法中执行的,它的定义在 src/core/instance/init.js 中:segmentfault
beforeCreate
调用的时候,是获取不到 props
或者 data
中的数据的,由于这些数据的初始化都在 initState
中。后端
能够看到 beforeCreate 和 created 的钩子调用是在 initState 的先后,initState 的做用是初始化 props、data、methods、watch、computed 等属性,以后咱们会详细分析。那么显然 beforeCreate 的钩子函数中就不能获取到 props、data 中定义的值,也不能调用 methods 中定义的函数。在这俩个钩子函数执行的时候,并无渲染 DOM,因此咱们也不可以访问 DOM,通常来讲,若是组件在加载的时候须要和后端有交互,放在这俩个钩子函数执行均可以,若是是须要访问 props、data 等数据的话,就须要使用 created 钩子函数。
--> 此段来自Vue.js 技术揭秘api
接下来会执行这里的挂载函数mountComponent
beforeMount
就是在挂载前执行的,而后开始建立 VDOM 并替换成真实 DOM,最后执行 mounted 钩子。
这里会有个判断逻辑,若是是外部 new Vue({})
的话,不会存在 $vnode
,因此直接执行 mounted 钩子了。若是有子组件的话,会递归挂载子组件,只有当全部子组件所有挂载完毕,才会执行根组件的挂载钩子。
对照生命周期图,咱们看看在beforeMount
钩子和mounted
钩子之间的 Create vm.$el and replace "el" width it
具体都有作了什么:
初始化以后调用 $mount 会挂载组件,若是是运行时编译,即不存在 render function 可是存在
template 的状况,须要进行「编译」步骤。
Vue 实例挂载如何实现
Vue 中咱们是经过$mount
实例方法去挂载vm
的,$mount
方法在多个文件中都有定义,如 src/platform/web/entry-runtime-with-compiler.js、 src/platform/web/runtime/index.js、 src/platform/weex/runtime/index.js。由于$mount
这个方法的实现是和平台、构建方式都相关的。
Virtual DOM
就是用一个原生的 JS 对象去描述一个 DOM 节点,因此它比建立一个 DOM 的代价要小不少。在 Vue.js 中,Virtual DOM 是用 VNode
这么一个 Class 去描述,它是定义在 src/core/vdom/vnode.js 中的。
Virtual DOM
其实就是一棵以 JavaScript 对象(VNode
节点)做为基础的树,用对象属性来描述节点,实际上它只是一层对真实 DOM 的抽象。最终能够经过一系列操做使这棵树映射到真实环境上。因为 Virtual DOM
是以 JavaScript 对象为基础而不依赖真实平台环境,因此使它具备了跨平台的能力,好比说浏览器平台
、Weex
、Node
等。
实现 Virtual DOM 下的一个 VNode 节点
// VNode 就是一个 JavaScript 对象,用 JavaScript 对象的属性来描述当前节点的一些状态, // 用 VNode 节点的形式来模拟一棵 Virtual DOM 树。 class VNode { constructor(tag, data, children, text, elm, context, componentOptions, asyncFactory) { // 当前节点的标签名 this.tag = tag // String // 当前节点的一些数据信息, 好比props,attrs等数据 this.data = data // VNodeData // 当前节点的子节点,是一个数组 this.children = children // Array<VNode> // 当前节点的文本 this.text = text // String // 当前虚拟节点对应的真实dom节点 this.elm = elm // Node this.ns = undefined // String | Void // rendered in this component's scope this.context = context // Component | Void // real context vm for functional nodes this.fnContext = undefined // Component | void // for SSR caching this.fnOptions = undefined // functional scope id support this.fnScopeId = undefined this.key = data && data.key // String | Number | Void this.componentOptions = componentOptions // VNodeComponentOptions | Void this.componentInstance = undefined // component instance // Component placeholder node this.parent = undefined // VNode | Void // strictly internal // contains raw HTML? (server only) this.raw = false // boolean // hoisted static node this.isStatic = false // boolean // necessary for enter transition check this.isRootInsert = true // boolean // empty comment placeholder this.isComment = false // boolean // is a cloned node ? this.isCloned = false // boolean // is a v-once node ? this.isOnce = false // boolean // aysync component factory function this.asyncFactory = asyncFactory // Function this.asyncMeta = undefined // Oject | void } get child() { return this.componentInstance } } // 对VNode进一步封装,实现一些产生经常使用VNode方法 // 建立空节点 const createEmptyVNode = (text) => { const node = new VNode() node.text = text node.isComment = true return node } // 建立文本节点 function createTextVNode (val) { return new VNode(undefined, undefined, undefined, String(val)) } // 克隆一个VNode节点 // optimized shallow clone // used for static nodes and slot nodes because they may be reused across // multiple renders, cloning them avoids errors when DOM manipulations rely // on their elm reference function cloneVNode(vnode) { const cloned = new VNode( vnode.tag, vnode.data, // clone children array to avoid mutating original in case of cloning a child vnode.children && vnode.children.slice() vnode.text, vnode.elm, vnode.context, vnode.componentOptions, vnode.asyncFactory ) cloned.ns = vnode.ns cloned.isStatic = vnode.isStatic cloned.key = vnode.key cloned.isComment = vnode.isComment cloned.fnContext = vnode.fnContext cloned.fnOptions = vnode.fnOptions cloned.fnScopeId = vnode.fnScopeId cloned.asyncMeta = vnode.asyncMeta cloned.isCloned = true return cloned }
若是咱们有这样一个vue组件
<template> <span class="demo" v-show="isShow"> This is a span. </span> </template>
转化成render函数描述的js代码形式就是这样的:
相关基础看这篇文章 -> Vue 路由知识点概括总结
Vue 通用的插件注册原理
Vue 从它的设计上就是一个渐进式 JavaScript 框架,它自己的核心是解决视图渲染的问题,其它的能力就经过插件的方式来解决。
如何注册插件 --> Vue.use
: Vue 提供了 Vue.use 的全局 API 来注册这些插件
vue/src/core/global-api/use.js
/* @flow */ import { toArray } from '../util/index' export function initUse (Vue: GlobalAPI) { // Vue.use 接受一个 plugin 参数 Vue.use = function (plugin: Function | Object) { // 而且维护了一个 _installedPlugins 数组,它存储全部注册过的 plugin const installedPlugins = (this._installedPlugins || (this._installedPlugins = [])) if (installedPlugins.indexOf(plugin) > -1) { return this } // additional parameters const args = toArray(arguments, 1) args.unshift(this) // 判断 plugin 有没有定义 install 方法 if (typeof plugin.install === 'function') { // 若是有的话则调用该方法,而且该方法执行的第一个参数是 Vue plugin.install.apply(plugin, args) } else if (typeof plugin === 'function') { plugin.apply(null, args) } // 最后把 plugin 存储到 installedPlugins 中 installedPlugins.push(plugin) return this } } // 能够看到 Vue 提供的插件注册机制很简单, // 每一个插件都须要实现一个静态的 install 方法, // 当咱们执行 Vue.use 注册插件的时候,就会执行这个 install 方法, // 而且在这个 install 方法的第一个参数咱们能够拿到 Vue 对象, // 这样的好处就是做为插件的编写方不须要再额外去import Vue 了
咱们知道, 当vue库文件加载完后,vue的初始化中已有这个东西:
Vue.options={ components:{ KeepAlive:Object, Transition:Object, TransitionGroup:Object }, directives:{ show:Object, model:Object }, filter:{}, _base:function Vue$3(options){...} }
这些都是vue库内置的组件和指令,当执行Vue.component、Vue.directive、Vue.filter时就是在对这些内置组件、指令、过滤器进行扩充,因此:
var child=Vue.component('child',{ template:'<div>child</div>', props:['name'] })
执行完后
Vue.options.components={ KeepAlive:Object, Transition:Object, TransitionGroup:Object, child:function VueComponent(options) }
https://yuchengkai.cn/docs/fr...
https://ustbhuangyi.github.io...
vue中SFC文件解析为SFCDescriptor的流程
Vue源码分析(11)--实例分析component,props,slot
vue变化侦测原理