Vue2.0 版本
注意: 我们这里说的版本,不是 Vue 发布的版本,而是 Vue 的打包编译之后的版本。我们都知道 Vue 2+ 是使用 rollup 打包的,Vue 是分为:
- 能编译模板的版本 entry-runtime-with-compiler
- 只有运行时的版本 entry-runtime
为什么选用 运行时版本
entry-runtime-with-compiler 内部重写挂载函数 $mount
, 原因是 compiler 版本是针对 Vueoptions
中含有 template
字段进行单独处理的。含有 template
字段就需要单独的用 Vue compiler 进行编译。我们一般在单文件内部写 template 不会使用 option 的 template 选项,所以直接使用运行时版本即可
暴露的 Vue 函数
从源码看 Vue 本身是一个 函数
, 它在 vue/src/core/instance/index.js
目录下
// code
function Vue(options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
// code
export default Vue
定义了 Vue构造函数(即:ES5 中的类) 之后,执行混入:
- 初始化
- 状态混入
- 事件混入
- 生命周期混入
- 渲染函数混入
导入 Vue
导入 Vue 后,JS 就开始创建 Vue 的运行时:
- 定义一个构造函数 Vue(类)
- 创建 Vue 总的运行时
运行时:initMixin
创建了 Vue.prototype._init
方法, 这方法将在 Vue 实例化后运行的,也就是说运行时是以 new Vue 实例为界限,我们写代码其实是 Vue 内部的,不是 Vue 外部的。Vue 外部的是 new Vue创建的实例。这是典型的面向对象编程思想。
initEvents 中 target.$on(event, fn)
为什么能访问 $on
?
initEvents 重要是初始化的是:listeners, 此时 实例上还没有 $on
,但也不会影响运行,因为这些时间不会被触发。
这里可以很好的理解 $listeners
api 的使用方法.
运行时:initRender
在 vm 实例上:
- _vnode
- _staticTrees
- $slots
- $scopedSlots
- _c
- $createElement
重要点就是创建以上的 vm 实例方法和属性, 其中 $createElement
是创建元素和组件,它的返回值是创建的 vnode 节点,到从位置,我们接触到的还都是 vnode
运行时:callHook, beforeCreate
vm._hasHookEvent = false, vm.$emit 没有定义,这个明显不会触发
pushTarget() 没有传入 target, 这里要补充 Class 的 target 属性
运行时:initInjections
defineReactive 的处理 inject 选项
运行时:initState
- initProps
- initMethods
- initData/ observe(data ={})
- initComputed
- initWatch
$mount
挂载函数 Vue.prototype.$mount
在,/vue@2.6.9/src/platforms/web/runtime/index.js
中定义
/* @flow */
import Vue from 'core/index'
import config from 'core/config'
import { extend, noop } from 'shared/util'
import { mountComponent } from 'core/instance/lifecycle'
import { devtools, inBrowser } from 'core/util/index'
import {
query,
mustUseProp,
isReservedTag,
isReservedAttr,
getTagNamespace,
isUnknownElement
} from 'web/util/index'
import { patch } from './patch'
import platformDirectives from './directives/index'
import platformComponents from './components/index'
// install platform specific utils
Vue.config.mustUseProp = mustUseProp
Vue.config.isReservedTag = isReservedTag
Vue.config.isReservedAttr = isReservedAttr
Vue.config.getTagNamespace = getTagNamespace
Vue.config.isUnknownElement = isUnknownElement
// install platform runtime directives & components
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)
// install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop
// public mount method
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
// devtools global hook
/* istanbul ignore next */
if (inBrowser) {
setTimeout(() => {
if (config.devtools) {
if (devtools) {
devtools.emit('init', Vue)
} else if (
process.env.NODE_ENV !== 'production' &&
process.env.NODE_ENV !== 'test'
) {
console[console.info ? 'info' : 'log'](
'Download the Vue Devtools extension for a better development experience:\n' +
'https://github.com/vuejs/vue-devtools'
)
}
}
if (process.env.NODE_ENV !== 'production' &&
process.env.NODE_ENV !== 'test' &&
config.productionTip !== false &&
typeof console !== 'undefined'
) {
console[console.info ? 'info' : 'log'](
`You are running Vue in development mode.\n` +
`Make sure to turn on production mode when deploying for production.\n` +
`See more tips at https://vuejs.org/guide/deployment.html`
)
}
}, 0)
}
export default Vue
- mountedComponent
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
vm.$el = el
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode
// coes
}
callHook(vm, 'beforeMount')
let updateComponent
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
// codes
} else {
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
}
// we set this to vm._watcher inside the watcher's constructor
// since the watcher's initial patch may call $forceUpdate (e.g. inside child
// component's mounted hook), which relies on vm._watcher being already defined
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
hydrating = false
// manually mounted instance, call mounted on self
// mounted is called for render-created child components in its inserted hook
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
return vm
}
_update
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
const vm: Component = this
const prevEl = vm.$el
const prevVnode = vm._vnode
const 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.
}
本质上是调用了 __patch__
方法
Vue.prototype.__patch__ = inBrowser ? patch : noop
// path
export const patch: Function = createPatchFunction({ nodeOps, modules })
- createPatchFunction 返回一个 patch 函数,createPatchFunction 本身是一个极为复杂的函数,设计到虚拟dom,diff 算法等等。。