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在初識化的過程中做了很多不知道的操作