Vue上面的函数怎么来的

vue的打包路径

在web中使用的vue打包路径: npm run build 打包生成vue.js

下面是依次引入:

src/platforms/web/entry-runtime-with-compiler.js

src/platforms/web/runtime/index.js

src/core/index.js

src/core/instance/index.js

instance/index.js

这个js文件就是Vue本身了

首先这是一个构造函数, 然后在执行new的时候, 会执行一个this._init函数

导出这个Vue之前, 都会挂载一些函数, 我们就来看看, 分别挂载了什么

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

initMixin

主要就是挂载了_init方法, 这是最关键的.

stateMixin

$set

$delete

$watch

eventsMixin

$on

$once

$off

emit

lifecycleMixin

$_update

$forceUpdate

$destroy

renderMixin

$nextTick

_render

$mount

在src/platforms/web/runtime/index.js进行挂载.

这里引入了/src/core/instance/lifecycle.js中的mountComponent函数

new vue都干了什么

在这里就执行了一个_init函数

总览函数, 逐个分析

Vue.prototype._init = function (options?: Object) {

const vm: Component = this

// a uid

vm._uid = uid++

let startTag, endTag

/* istanbul ignore if */

if (process.env.NODE_ENV !== 'production' && config.performance && mark) {

startTag = `vue-perf-start:${vm._uid}`

endTag = `vue-perf-end:${vm._uid}`

mark(startTag)

}

// a flag to avoid this being observed

vm._isVue = true

// merge options

if (options && options._isComponent) {

// optimize internal component instantiation

// since dynamic options merging is pretty slow, and none of the

// internal component options needs special treatment.

initInternalComponent(vm, options)

} else {

vm.$options = mergeOptions(

resolveConstructorOptions(vm.constructor),

options || {},

vm

)

}

/* istanbul ignore else */

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

initProxy(vm)

} else {

vm._renderProxy = vm

}

// expose real self

vm._self = vm

initLifecycle(vm)

initEvents(vm)

initRender(vm)

callHook(vm, 'beforeCreate')

initInjections(vm) // resolve injections before data/props

initState(vm)

initProvide(vm) // resolve provide after data/props

callHook(vm, 'created')

/* istanbul ignore if */

if (process.env.NODE_ENV !== 'production' && config.performance && mark) {

vm._name = formatComponentName(vm, false)

mark(endTag)

measure(`vue ${vm._name} init`, startTag, endTag)

}

if (vm.$options.el) {

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

}

}

initLifecycle

初始化各种状态

export function initLifecycle (vm: Component) {

const options = vm.$options

// locate first non-abstract parent

let parent = options.parent

if (parent && !options.abstract) {

// 不断向下深复制所有的parent, 找到最终元素,

while (parent.$options.abstract && parent.$parent) {

parent = parent.$parent

}

parent.$children.push(vm)

}

// 确定该组件的状态, 初始化一些需要东西

vm.$parent = parent

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

}

initEvents

初始化事件, 尤其是events, 还有一个listenners.

export function initEvents (vm: Component) {

vm._events = Object.create(null)

vm._hasHookEvent = false

// init parent attached events

const listeners = vm.$options._parentListeners

// 更新监听函数什么鬼呢?

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

// bind the createElement fn to this instance

// so that we get proper render context inside it.

// args order: tag, data, children, normalizationType, alwaysNormalize

// internal version is used by render functions compiled from templates

// 这里的createElement关键, 确定了我们需要页面上需要渲染的函数

vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)

// normalization is always applied for the public version, used in

// user-written render functions.

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

// todo: 这里还没有搞太懂

// $attrs & $listeners are exposed for easier HOC creation.

// they need to be reactive so that HOCs using them are always updated

const parentData = parentVnode && parentVnode.data

/* istanbul ignore else */

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

defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => {

!isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)

}, true)

defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () => {

!isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)

}, true)

} else {

defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)

defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)

}

}

这个时候, 调用钩子函数beforeCreate钩子

initInjections

这是一个高级用法, 在初识话data和prop之前, 来设置一些应该设置的值

好吧, 还没有深入

initState

关键步骤:

props

methods

data

computed

watcher

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 /* asRootData */)

}

if (opts.computed) initComputed(vm, opts.computed)

if (opts.watch && opts.watch !== nativeWatch) {

initWatch(vm, opts.watch)

}

}

initProvide

在初识完一系列的data, 和prop之后执行调用, 需要深入

调用created函数

mount

如果存在options.el上面的el, 那么就会调用, mount函数

这个函数指的就是在mountComponent函数.

这时候, 调用beforeMount

在mount中

开始执行一系列的update和render函数

执行结束后, 执行mounted, 这个时候, 这个组件就算渲染完成了.

疑问

如果在一个父组件中有两个子组件, 那么这个两个组件生命周期的执行顺序是什么?

需要深入的还很多啊, 看了很多的文章, 杜宇Dep, Watcher, Observer的关系还是没这么清楚

还有就是vue在初识化的过程中做了很多不知道的操作

Logo

前往低代码交流专区

更多推荐