vue2源码浅读(二):vue初始化
入口文件
通过启动命令:"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev --sourcemap",
,找到scripts/config.js文件,在该文件中找到启动入口web-full-dev
: 找到resolve的定义处: 找到aliases定义处: 定位到src/platforms/web/entry-runtime-with-compiler.js文件。
vue初始化流程
找到初始化入口文件
可以通过在new Vue之前打debugger,然后单步调试找到vue初始化第一步。然后采用调试的方法依次观察初始化流程。 也可以在entry-runtime-with-compiler.js的文件里找到import Vue from './runtime/index'
,在/runtime/index.js里又可以找到import Vue from 'core/index'
,在core/index.js里又可以找到import Vue from './instance/index'
,自此找到了初始化的入口文件:
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'
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)
}
initMixin ( Vue)
stateMixin ( Vue)
eventsMixin ( Vue)
lifecycleMixin ( Vue)
renderMixin ( Vue)
export default Vue
_init
src\core\instance\init.js
export function initMixin ( Vue : Class< Component> ) {
Vue . prototype. _init = function ( options? : Object ) {
const vm : Component = this
vm. _uid = uid++
vm. _isVue = true
if ( options && options. _isComponent) {
initInternalComponent ( vm, options)
} else {
vm. $options = mergeOptions (
resolveConstructorOptions ( vm. constructor) ,
options || { } ,
vm
)
}
vm. _self = vm
initLifecycle ( vm)
initEvents ( vm)
initRender ( vm)
callHook ( vm, 'beforeCreate' )
initInjections ( vm)
initState ( vm)
initProvide ( vm)
callHook ( vm, 'created' )
if ( vm. $options. el) {
vm. $mount ( vm. $options. el)
}
}
}
initState
src\core\instance\state.js
export function initState ( vm : Component) {
vm. _watchers = [ ]
const 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 )
}
if ( opts. computed) initComputed ( vm, opts. computed)
if ( opts. watch && opts. watch !== nativeWatch) {
initWatch ( vm, opts. watch)
}
}
由上边可知,初始化的优先级为props > methods > data > computed > watch
(面试题),这些都是在created前完成的。 在此阶段,进行了大量的数据响应式处理,后边再做浅读。
$mount挂载
src/platforms/web/entry-runtime-with-compiler.js
Vue . prototype. $mount = function (
el? : string | Element,
hydrating? : boolean
) : Component {
el = el && query ( el)
const options = this . $options
if ( ! options. render) {
let template = options. template
if ( template) {
if ( typeof template === 'string' ) {
if ( template. charAt ( 0 ) === '#' ) {
template = idToTemplate ( template)
}
} else if ( template. nodeType) {
template = template. innerHTML
} else {
if ( process. env. NODE_ENV !== 'production' ) {
warn ( 'invalid template option:' + template, this )
}
return this
}
} else if ( el) {
template = getOuterHTML ( el)
}
if ( template) {
const { render, staticRenderFns } = compileToFunctions ( template, {
outputSourceRange : process. env. NODE_ENV !== 'production' ,
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters : options. delimiters,
comments : options. comments
} , this )
options. render = render
options. staticRenderFns = staticRenderFns
}
}
return mount . call ( this , el, hydrating)
}
由上边可知,vue的编译权重为:
先看有没有render函数,如果有直接用 如果没有render函数,再看有没有template模板 如果都没有就直接获取el的outerHTML作为渲染模板 如果用 template 进行编写HTML代码,vue 内部会把模板编译成 vue 可识别的 render 函数。 如果有写 render 则可以省去编译过程。
src/core/instance/lifecycle.js 简化一下
export function mountComponent ( vm, elhydrating ) {
vm. $el = el
if ( ! vm. $options. render) {
vm. $options. render = createEmptyVNode
}
callHook ( vm, 'beforeMount' )
let updateComponent
updateComponent = ( ) => {
vm. _update ( vm. _render ( ) , hydrating)
}
new Watcher ( vm, updateComponent, noop, {
before ( ) {
if ( vm. _isMounted && ! vm. _isDestroyed) {
callHook ( vm, 'beforeUpdate' )
}
}
} , true )
hydrating = false
if ( vm. $vnode == null ) {
vm. _isMounted = true
callHook ( vm, 'mounted' )
}
return vm
}
这个过程创建了渲染 watcher ,渲染 watcher 内部调用了 updateComponent 方法 关于watcher、虚拟dom与真实dom及diff后边再做浅读。
至此。vue2的初始化流程大概就是这样,至于里边涉及很多细节,可循着初始化顺序依次看下去