vue 初始化请求例子_Vue 实例初始化过程

new Vue() 实例的初始化

Vue.js 是由 原型链 写法来实现的库,其构造函数在 src/core/instance/index.js

function Vue(options) {

if (process.env.NODE_ENV !== 'production' && !(this instanceof Vue)) {

warn(...)

// 必须是以 new Vue 方式来创建实例

}

this._init(options)

}

initMixin(Vue) // 定义了 _init 方法

stateMixin(Vue)

eventsMixin(Vue)

lifecycleMixin(Vue) // 定义了 _update 方法,$destroy 方法

renderMixin(Vue) // 定义了 _render 方法

function initMixin(Vue) {

Vue.prototype._init = function (options) {

const vm = this // 存储当前实例

...

vm._isVue = true // 确认自身为Vue实例

if (options && options._isComponent) {

initInternalComponent(vm, options) // 对于组件传入的options的合并

} else {

vm.$options = mergeOptions(

resolveConstructorOptions(vm.constructor),

options || {},

vm

) // mergeOptions 将传入的options和构造函数的options合并到实例本身的$options

}

... // 一些实例自身的初始化(包括beforeCreate/created钩子的触发)

if (vm.$options.el) {

// 如果合并之后的选项中有 el,则将其进行挂载

vm.$mount(vm.$options.el)

}

}

}

在附带 compiler(编译器)的版本中,$mount 的实现方式如下

位置:src/platform/web/entry-runtime/with-compiler.js

const mount = Vue.prototype.$mount

// hydrating == false

Vue.prototype.$mount = function (el, hydrating) {

const options = this.$options

if (!options.render) {

// 传入的options没有render函数,就通过template或者el,编译成render函数并赋值给 options.render

}

return mount.call(this, el, hydrating) // 调用原来的 $mount 方法, return vm

}

// src/platform/web/runtime/index.js 原$mount方法

Vue.prototype.$mount = function(el, hydrating) {

el = el && inBrowser ? query(el) : undefined // 通过传入的 el 来选择对应的容器元素

return mountComponent(this, el) // return vm

}

// src/core/instance/lifecycle.js (经简化)

function mountComponent(vm, el) {

vm.$el = el // 缓存 el

...

callHook(vm, 'beoforeMount')

let updateComponent = () => {

vm._update(vm._render())

}

// 创建 vm 对应的一个 渲染Watcher

new Watcher(vm, updateComponent, noop, {

before() {

if (vm._isMounted && !vm._isDestroyed) {

callHook(vm, 'beforeUpdate')

}

}

}, true /* 渲染watcher标志 */)

if (vm.$vnode == null) {

vm._isMounted = true

callHook(vm, 'mounted')

}

return vm

}

创建 渲染Watcher 的过程

// src/core/observer/watcher.js

class Watcher {

constructor(vm, expOrFn, cb, options, isRenderWatcher) {

this.vm = vm // Vue 实例

if (isRenderWatcher) {

vm._watcher = this

}

vm._watchers.push(this)

...

if (typeof expOrFn === 'function') {

this.getter = expOrFn // this.getter 其实就是定义Water时候传入的 updateComponent

} else {

...

}

this.value = this.lazy ? undefined : this.get() // this.lazy == false

// 所以在赋值 this.value 的过程中,this.get()执行过程中,传入的 getter 函数执行了一次

}

get() {

pushTarget(this)

let value

const vm = this.vm

try {

value = this.getter.call(vm, vm)

} catch (e) {

...

} finally {

if (this.deep) {

traverse(value)

}

popTarget()

this.cleanupDeps()

}

return value

}

}

分析 updateComponent 函数

// 定义

let updateComponent = () => {

vm._update(vm._render()) // 参数为 vnode

}

// 先分析 _render()

// src/core/instance/render.js

function renderMixin(Vue) {

Vue.prototype._render = function () {

const vm = this

const { render, _parentVnode } = vm.$options

if (_parentVnode) {

...

// 设置 vm 的插槽

}

vm.$vnode = _parentVnode // 存储 父vnode 节点

let vnode

try {

currentRenderingInstance = vm

vnode = render.call(vm._renderProxy, vm.$createElement)

// vm._renderProxy.render(vm.$createElement)

// vm._renderProxy == vm

} catch (e) {}

...

vnode.parent = _parentVnode

return vnode

// 返回一个由 编译/自带的 render 函数执行得到的 vnode

}

}

// render 函数的调用其实就是 $createElement 的执行

// 同一个文件中有这样的定义

// initRender 函数中

// 在 _init 方法初始化过程中,会调用initRender(vm)

vm.$createElement = (a,b,c,d) => createElement(vm,a,b,c,d,true)

// createElement 定义在 src/core/vdom/create-element.js

// src/core/vdom/create-element.js

function createElement(vm, tag, data, children, normalizationType, alwaysNormalize) {

if (Array.isArray(data) || isPrimitive(data)) {

// 参数重载

normalizationType = children

children = data

data = undefined

}

return _createElement(context, tag, data, normalizationType) // vnode

}

function _createElement(context, tag, data, children, normalizationType) {

...

// 将children 拍平成一维数组

if (normalizationType === ALWAYS_NORMALIZE) {

children = normalizeChildren(children)

} else if (normalizationType === SIMPLE_NORMALIZE) {

children = simpleNormalizeChildren(children)

}

let vnode, ns

if (typeof tag === 'string') {

// 适用于传入一个由 vue-loader 编译生成或者按格式编写 tag

if (config.isReservedTag(tag)) {

// 平台原有的 tag 标签,如H5的div等

vnode = new VNode(

config.parsePlatform(tag), data, children,

undefined, undefined, context

)

} else if (

(!data || !data.pre) &&

isDef(Ctor = resolveAssest(context.$options, 'components' ,tag))

) {

// 创建组件的vnode

vnode = createComponent(Ctor, data, context, children, tag) // return vnode

}

} else {

// 适用于直接传入一个导出的 .vue 文件到render函数

// e.g. render: h => { return h(App) }

// 此处 h 相当于 createElement

// tag == App

vnode = createComponent(tag, data, context, children) // return vnode

}

if (Array.isArray(vnode)) {

return vnode

} else if (isDef(vnode)) {

...

return vnode

} else {

return createEmptyVNode() // 空白vnode

}

}

// 因此 vm._render() 函数会生成 vm 对应的 vnode 并返回给 vm._update 函数

// 分析 createComponent 函数

// src/core/vdom/create-component.js

function createComponent (Ctor, data, context, children, tag) {

if (isUndef(Ctor)) { return } // 没有传入构造函数/信息

const baseCtor = context.$options._base // 其实就是Vue,在合并选项的时候会合并进去

if (isObject(Ctor)) {

Ctor = baseCtor.extend(Ctor) // 传入的构造信息通过 Vue.extend 转为构造函数

}

...

installComponentHooks(data)

// 安装组件钩子,合并到data对象

/*

data = {

on: {...},

hook: {...}

}

*/

const name = Ctor.options.name || tag

const vnode = new VNode(

`vue-component-${Ctor.c_id}${name ? `-${name}` : ''}`,

data ,undefined, undefined, undefined, context,

{ Ctor, propsData, listeners, tag, children }, // 该对象为componentOptions参数

asyncFactory

)

return vnode

}

再来看看 vm._update

// src/core/instance/lifecycle.js

function lifecycleMixin(Vue) {

Vue.prototype._update = function (vnode, hydrating) { // hydratng == false

const vm = this

const prevEl = vm.$el // 在 mountComponent 函数中缓存

const prevVnode = vm._vnode

vm._vnode = vnode // 缓存当前vnode 到实例的 _vnode属性

const restoreActiveInstance = setActiveInstance(vm)

// 存储当前的 vm 实例到 activeInstance

// 组件实际插入到 DOM 对象是在 __patch__ 过程中

if (!prevVnode) {

vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false)

} else {

vm.$el = vm.__patch__(prevVnode, vnode)

}

restoreActiveInstance() // 重新释放当前 vm 实例,activeInstance 回退到上一个 vm 实例

...

// 根组件直接将 $el 更新到 父实例 的$el

// e.g. render: h => h(App) 最后会将 App 实例 patch 得到的 $el 更新到根实例(new Vue) 上

if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {

vm.$parent.$el = vm.$el

}

}

}

// 至此实例的渲染 watcher 创建完成,实例挂载结束

下面是组件如何 实际插入到DOM对象 的过程分析,在 patch 函数中,

// src/platforms/web/runtime/index.js

Vue.prototype.__patch__ = patch

// 这个patch 函数就是实际上调用的函数

// 经分析最后 patch 函数在 src/core/vdom/patch.js 的 createPatchFunction 中返回

// 简化版 patch

function patch (oldVnode, vnode, hydrating, removeOnly) {

if (isUndef(vnode)) {

// 只有传入了旧节点的信息

if (isDef(oldVnode)) invokeDestroy(oldVnode) // 销毁旧节点

}

let isInitialPatch = false // 是否为根节点

const insertedVnodeQueue = []

if (isUndef(oldVnode)) {

isInitialPatch = true

crateElm(vnode, insertedVnodeQueue)

} else {

const isRealElement = isDef(oldVnode.nodeType)

// h(App)是传入的 oldVnode 是 el

if (!isRealElement && sameVndoe(oldVnode, vnode)) {

patchVnode(oldVnode, vnode, insertedVnodeQueue)

} else {

if (isRealElement) {

...

oldVnode = emptyNodeAt(oldVnode) // 用 el 元素创建一个 oldVnode

}

const oldElm = oldVnode.elm // 就是传入的 el元素

const parentElm = nodeOps.parentNode(oldElm) // el的父元素

createElm(

vnode,

insertedVnodeQueue,

oldElm._leaveCB ? null : parentElm,

nodeOps.nextSibling(oldElm) // oldElm 的下一个兄弟节点

)

...

if (isDef(parentElm)) {

removeVnodes([oldVnode], 0, 0)

} else if (isDef(oldVnode.tag)) {

invokeDestroyHook(oldVnode)

}

}

}

invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)

// 通过insert钩子触发组件的mounted钩子

return vnode.elm // elm元素 返回给__patch__ ,赋值给vm.$el

}

重点分析 createElm 函数

function createElm (

vnode,

insertedVnodeQueue,

parentElm,

refElm,

nested,

ownerArray,

index

) {

// vnode直接创建一个组件

if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {

return

}

const data = vnode.data

const children = vnode.children

const tag = vnode.tag

if (isDef(tag)) {

...

// 利用 vnode 的 tag 属性直接生成一个原生元素(div)

vnode.elm = nodeOps.createElement(tag, vnode)

}

createChildren(vnode, children, insertedVnodeQueue)

// 创建子节点,实际上也是调用 createElm

if (isDef(data)) {

// 插入vnode序列

invokeCreateHooks(vnode, insertedVnodeQueue)

}

// 插入到父节点

insert(parentElm, vnode.elm, refElm)

} else if (isTrue(vnode.isComment)) {

// 注释节点创建

vnode.elm = nodeOps.createComment(vnode.text)

insert(parentElm, vnode.elm, refElm)

} else {

// 文本节点

vnode.elm = nodeOps.createTextNode(vnode.text)

insert(parentElm, vnode.elm, refElm)

}

// createChildren

function createChildren (vnode, children, insertedVnodeQueue) {

if (Array.isArray(children)) {

// 只接受数组类型

if (process.env.NODE_ENV !== 'production') {

checkDuplicateKeys(children)

}

for (let i = 0; i < children.length; ++i) {

// 实际上将父 vnode 的 elm 作为父元素传入

createElm(children[i], insertedVnodeQueue, vnode.elm, null, true, children, i)

}

} else if (isPrimitive(vnode.text)) {

nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(String(vnode.text)))

}

}

// createComponent

function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {

let i = vnode.data

if (isDef(i)) {

const isReactivated = isDef(vnode.componentInstance) && i.keepAlive

if (isDef(i = i.hook) && isDef(i = i.init)) {

i(vnode, false /* hydrating */)

// 调用hooks中的init钩子,在 create-component.js中componentVNodeHooks

}

if (isDef(vnode.componentInstance)) {

initComponent(vnode, insertedVnodeQueue)

insert(parentElm, vnode.elm, refElm) // 插入到父元素 DOM

if (isTrue(isReactivated)) {

reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)

}

return true

}

}

}

// src/core/vdom/create-component.js

// init钩子函数

function init (vnode, hydrating) {

if (

vnode.componentInstance &&

!vnode.componentInstance._isDestroy &&

vnode.data.keepAlive

) {

...

} else {

// createComponentInstanceForVnode 函数返回一个vm实例

// 实际上是调用了 vnode 的componentOptions.Ctor 来构造子组件

const child = vnode.componentInstance = createComponentInstanceForVnode(

vnode,

activeInstance

)

// 上面生成的实例挂载(el是undefined)

child.$mount(hydrating ? vnode.elm : undefined, htdrating) // hydrating == false

}

}

// createComponentInstanceForVnode

// parent为当前的父vm,就是需要创建的vm的父vm == activeInstance

function createComponentInstanceForVnode (vnode, parent) {

const options = {

_isComponent: true,

_parentVnode: vnode,

parent

}

...

return new vnode.componentOptions.Ctor(options)

// Sub 构造函数的实例化 return vm

// 重新走一次 _init 流程

}

new Vue - vm.mount - mountComponent(vm) - vm._render(vmVnode) - vm.componentOptions - vm._update(vmVnode) - patch - createElm(vmVnode) - createComponent(vmVnode) - vm.hook.init - new child - vm.componentInstance - child.mount- createComponent(vmVnode) - insert

new child - child.mount - mountComponent(child) - child._update(childVnode) - patch - createElm(childVnode) - createElement(childVnode) - insert(child)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值