<div id="app">
<div class="home">
<h1>{{title}}</h1>
</div>
</div>
new Vue({
el: '#app',
data: () => {
return {
title: 'Home Page'
}
}
})
更多详细内容,请微信搜索“前端爱好者
“, 戳我 查看 。
在Vue.js 2.x的源码中,new Vue()
的主要作用是创建一个Vue实例。当我们调用new Vue()
时,会经过以下几个步骤:
beforeCreate
、created
、beforeMount
、mounted
等。template
选项,Vue会将模板解析成渲染函数,以便后续的渲染过程中使用。在这个过程中,Vue还会做很多其他的工作,例如处理指令、组件注册、依赖收集等,以确保整个应用的正常运行和响应式更新。
以上只是对new Vue()
的简单概述,具体的实现细节涉及到很多源码内容,在Vue.js的源码中可以详细了解其中的实现原理。
vue源码版本vue2.5.2
new Vue()会执行_init方法,而_init方法在initMixin函数中定义。
src/core/instance/index.js文件中定义了Vue
function Vue (options) {
this._init(options)
}
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
定义的 Vue.prototype._init
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// a uid,自增ID
vm._uid = uid++
vm._isVue = true
// merge options 合并options
if (options && options._isComponent) {
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
vm._renderProxy = vm
// expose real self
vm._self = vm
// 初始化生命周期相关实例属性
initLifecycle(vm)
//初始化事件
initEvents(vm)
//定义$createElement() createElement的执行过程
initRender(vm)
// 执行beforeCreate生命周期钩子
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
//将data代理至Vue._data data的代理
initState(vm)
// 初始化provide
initProvide(vm) // resolve provide after data/props
// 执行created生命周期钩子
callHook(vm, 'created')
// 当options中存在el属性,则执行挂载
if (vm.$options.el) {
//挂载#app $mount执行过程
vm.$mount(vm.$options.el)
}
}
}
initLifecycle 初始化生命周期相关变量即在vue实例上挂载一些属性并设置默认值,如
export function initLifecycle (vm: Component) {
const options = vm.$options
// locate first non-abstract parent
let parent = options.parent
//当前组件存在父级并且当前组件不是抽象组件
if (parent && !options.abstract) {
//通过while循环来向上循环,如果当前组件的父级是抽象组件并且也存在父级,那就继续向上查找当前组件父级的父级
while (parent.$options.abstract && parent.$parent) {
//更新parent
parent = parent.$parent
}
//把该实例自身添加进找到的父级的$children属性中
parent.$children.push(vm)
}
//直到找到第一个不是抽象类型的父级时,将其赋值vm.$parent
vm.$parent = parent
//设置实例根元素。判断如果当前实例存在父级,那么当前实例的根实例$root属性就是其父级的根实例$root属性,如果不存在,那么根实例$root属性就是它自己
vm.$root = parent ? parent.$root : vm
vm.$children = []
vm.$refs = {}
vm._watcher = null
vm._inactive = null
vm._directInactive = false
//实例是否已挂载
vm._isMounted = false
//实例是否已销毁
vm._isDestroyed = false
//实例是否正准备销毁
vm._isBeingDestroyed = false
}
合并options之后
initEvents 初始化事件。 初始化的是父组件在模板中使用v-on或@注册的监听子组件内触发的事件。
export function initEvents (vm: Component) {
//在vm上新增_events属性并将其赋值为空对象,用来存储事件。
vm._events = Object.create(null)
vm._hasHookEvent = false
// init parent attached events
//获取父组件注册的事件赋给listeners,
const listeners = vm.$options._parentListeners
//如果listeners不为空,则调用updateComponentListeners函数,将父组件向子组件注册的事件注册到子组件的实例中
if (listeners) {
updateComponentListeners(vm, listeners)
}
}
initRender函数 初始化渲染.。
export function initRender (vm: Component) {
vm._vnode = null // the root of the child tree
vm._staticTrees = null // v-once cached trees
const options = vm.$options
const parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent tree
const renderContext = parentVnode && parentVnode.context
//实例上插槽
vm.$slots = resolveSlots(options._renderChildren, renderContext)
vm.$scopedSlots = emptyObject
//在实例上定义_c函数和$_createElement函数
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
const parentData = parentVnode && parentVnode.data
defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)
}
inject 选项中的每一个数据key都是由其上游父级组件提供的,所以我们应该把每一个数据key从当前组件起,不断的向上游父级组件中查找该数据key对应的值,直到找到为止。
如果在上游所有父级组件中没找到,那么就看在inject 选项是否为该数据key设置了默认值,如果设置了就使用默认值,如果没有设置,则抛出异常。
export function initInjections (vm: Component) {
//调用resolveInject把inject选项中的数据转化成键值对的形式赋给result
const result = resolveInject(vm.$options.inject, vm)
if (result) {
toggleObserving(false)
//遍历result中的每一对键值
Object.keys(result).forEach(key => {
//调用defineReactive函数将其添加当前实例上
defineReactive(vm, key, result[key])
})
toggleObserving(true)
}
}
注意,在把result中的键值添加到当前实例上之前,会先调用toggleObserving(false),而这个函数内部是把shouldObserve = false,这是为了告诉defineReactive函数仅仅是把键值添加到当前实例上而不需要将其转换成响应式
resolveInject函数内部是如何把inject 选项中数据转换成键值对的。
export function resolveInject (inject: any, vm: Component): ?Object {
if (inject) {
// inject is :any because flow is not smart enough to figure out cached
//创建一个空对象result,用来存储inject 选项中的数据key及其对应的值,作为最后的返回结果。
const result = Object.create(null)
const keys = hasSymbol
? Reflect.ownKeys(inject).filter(key => {
/* istanbul ignore next */
return Object.getOwnPropertyDescriptor(inject, key).enumerable
})
: Object.keys(inject)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
//provideKey就是上游父级组件提供的源属性
const provideKey = inject[key].from
let source = vm
//while循环,从当前组件起,不断的向上游父级组件的_provided属性中(父级组件使用provide选项注入数据时会将注入的数据存入自己的实例的_provided属性中)查找,直到查找到源属性的对应的值,将其存入result中
while (source) {
if (source._provided && hasOwn(source._provided, provideKey)) {
result[key] = source._provided[provideKey]
break
}
source = source.$parent
}
if (!source) {
//是否有default属性,如果有的话,则拿到这个默认值,官方文档示例中说了,默认值可以为一个工厂函数,所以当默认值是函数的时候,就去该函数的返回值,否则就取默认值本身。如果没有设置默认值,则抛出异常。
if ('default' in inject[key]) {
const provideDefault = inject[key].default
result[key] = typeof provideDefault === 'function'
? provideDefault.call(vm)
: provideDefault
} else if (process.env.NODE_ENV !== 'production') {
warn(`Injection "${key}" not found`, vm)
}
}
}
return result
}
}
官方文档中说inject 选项可以是一个字符串数组,也可以是一个对象,在上面的代码中只看见了处理当为对象的情况,那如果是字符串数组呢?怎么没有处理呢?
其实在初始化阶段_init函数在合并属性的时候还调用了一个将inject 选项数据规范化的函数normalizeInject
参考文档:
https://blog.csdn.net/weixin_40119412/article/details/128880336