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的初始化流程大概就是这样,至于里边涉及很多细节,可循着初始化顺序依次看下去
|