for里面调用方法 vue_Vue源码阅读连载之Vue实例
我們學習Vue都是從下面這個例子開始的
new Vue({render: h => h(App), }).$mount('#app')事實上,所有的Vue項目的組成組件都是一個Vue的實例,最后由根部的Vue實例去掛載到DOM上,當然這個"掛載"的操作可以針對不同的平臺而有不同的行為,比如掛載到移動設備上就成了weex,掛載到小程序上就成了mpvue。所以首先我們要知道Vue實例里面含有哪些東西。
實際上,這個$mount就等同于React里的ReactDOM.render。
Vue構造函數
我是全局搜索關鍵詞"function Vue"來定位到Vue類的定義的,位于/src/core/instance/index.js里。
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代碼很簡單,就是用function的方式定義了Vue類,之后只要new Vue一下就可以生成Vue的實例了。在構造函數里我們看到調用了_init( )方法,但是上下文中并沒有看到_init( )方法的定義,往下看,有一堆的mixin方法,這些方法其實就是在Vue的prototype上增加各種方法,_init( )也就是在這些mixin調用過后添加的。
列舉一下Vue里約定俗成前綴
_ 表示私有,這個和一般的約定一致。
$ 表示實例上的屬性或方法,比如$mount。
另外還有一些方法是定義在全局的,也就是Vue構造函數上的,比如Vue.component。
initMixin
剛才看到的_init( )方法就是在這里定義出來的,抽掉一些不重要的代碼后
Vue.prototype._init = function (options?: Object) {const vm: Component = thisvm._uid = uid++vm._isVue = trueif (options && options._isComponent) {initInternalComponent(vm, options)} else {vm.$options = mergeOptions(resolveConstructorOptions(vm.constructor),options || {},vm)}// expose real selfvm._self = vminitLifecycle(vm)initEvents(vm)initRender(vm)callHook(vm, 'beforeCreate')initInjections(vm) // resolve injections before data/propsinitState(vm)initProvide(vm) // resolve provide after data/propscallHook(vm, 'created')if (vm.$options.el) {vm.$mount(vm.$options.el)}} }這一段執行完成后,相當于生命周期的
大致干了這些事情
對于每一個生成的Vue實例在內部都用_uid來跟蹤
合并option對象
initLifecycle方法里定義了許多和生命周期有關的變量,有些是內部的,有些是暴露在實例上的
export function initLifecycle (vm: Component) {const options = vm.$options// locate first non-abstract parentlet parent = options.parentif (parent && !options.abstract) {while (parent.$options.abstract && parent.$parent) {parent = parent.$parent}parent.$children.push(vm)}vm.$parent = parentvm.$root = parent ? parent.$root : vmvm.$children = []vm.$refs = {}vm._watcher = nullvm._inactive = nullvm._directInactive = falsevm._isMounted = falsevm._isDestroyed = falsevm._isBeingDestroyed = false }initEvents針對配置里傳進來事件,做了一些操作。
initRender里初始化了一些跟虛擬DOM渲染有關的屬性和方法,比如后面會登場的赫赫有名的_c方法和實例上的$createElement方法,而把方法連接到實例上其實就是為了在渲染的時候能獲得實例的上下文
export function initRender (vm: Component) {vm._vnode = null // the root of the child treevm._staticTrees = null // v-once cached treesconst options = vm.$optionsconst parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent treeconst renderContext = parentVnode && parentVnode.contextvm.$slots = resolveSlots(options._renderChildren, renderContext)vm.$scopedSlots = emptyObjectvm._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)}接著callHook方法第一次登場,從名字就可以看出來callHook就是用來調用鉤子函數。
export function callHook (vm: Component, hook: string) {// #7573 disable dep collection when invoking lifecycle hookspushTarget()const handlers = vm.$options[hook]const info = `${hook} hook`if (handlers) {for (let i = 0, j = handlers.length; i < j; i++) {invokeWithErrorHandling(handlers[i], vm, null, vm, info)}}if (vm._hasHookEvent) {vm.$emit('hook:' + hook)}popTarget() }它還是通過$emit來調用的,所以在后面的章節要睜大眼睛看一下Vue里事件的實現。
下面的Injection和Provider有點陌生,查了下官網上說
provide和inject主要為高階插件/組件庫提供用例。并不推薦直接用于應用程序代碼中。而夾在中間的initState( )又是重中之重,初始化了Vue的幾大響應式組成部分,計劃下一篇文章就是探究這方面的原理的。
最后如果找到配置項里有el,就執行掛載的方法。當然也可以如之前的代碼所示,調用Vue實例上的$mount來執行掛載,對應到生命周期圖里的這個部分。
stateMixin
這里面定義了一個響應式里很重要的方法——$watch,也會放到后文來詳述。
eventsMixin
實例上事件的四大方法
- vm.$on
- vm.$once
- vm.$off
- vm.$emit
先看$on
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {const vm: Component = thisif (Array.isArray(event)) {for (let i = 0, l = event.length; i < l; i++) {vm.$on(event[i], fn)}} else {(vm._events[event] || (vm._events[event] = [])).push(fn)if (hookRE.test(event)) {vm._hasHookEvent = true}}return vm}其實就是把傳入的方法回調,放在Vue的實例上,而事件的名字就作為鍵,對應的值是一個數組,表明同一個事件可以調用多個回調方法,傳入的event也能是一個數組,不過這種不同的事件上調用同一個回調的事情現實中應該不多吧。
可以做一下實驗
有$on就必有$off
Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {const vm: Component = this// allif (!arguments.length) {vm._events = Object.create(null)return vm}// array of eventsif (Array.isArray(event)) {for (let i = 0, l = event.length; i < l; i++) {vm.$off(event[i], fn)}return vm}// specific eventconst cbs = vm._events[event]if (!cbs) {return vm}if (!fn) {vm._events[event] = nullreturn vm}// specific handlerlet cblet i = cbs.lengthwhile (i--) {cb = cbs[i]if (cb === fn || cb.fn === fn) {cbs.splice(i, 1)break}}return vm}原理也很簡單,就是尋找——刪除。
而$once本質就是在這個事件回調上做了替換,先調用一下這個傳入的回調方法,執行完成后調用一下$off把它刪除,達到只調用一次的目的。
Vue.prototype.$once = function (event: string, fn: Function): Component {const vm: Component = thisfunction on () {vm.$off(event, on)fn.apply(vm, arguments)}on.fn = fnvm.$on(event, on)return vm}$emit的話就是尋找——調用。
Vue.prototype.$emit = function (event: string): Component {const vm: Component = thislet cbs = vm._events[event]if (cbs) {cbs = cbs.length > 1 ? toArray(cbs) : cbsconst args = toArray(arguments, 1)const info = `event handler for "${event}"`for (let i = 0, l = cbs.length; i < l; i++) {invokeWithErrorHandling(cbs[i], vm, args, vm, info)}}return vm}注意一下這幾個方法調用返回的都是Vue實例本身,說明實際中可以寫成鏈式調用的形式。
lifecycleMixin
定義了$forceUpdate,強行在Vue實例上的所有Watcher對象都調用一把更新,后面講響應式原理的時候會談到什么是Watcher對象。
Vue.prototype.$forceUpdate = function () {const vm: Component = thisif (vm._watcher) {vm._watcher.update()}}又定義了$destroy
Vue.prototype.$destroy = function () {const vm: Component = thisif (vm._isBeingDestroyed) {return}callHook(vm, 'beforeDestroy')vm._isBeingDestroyed = true// remove self from parentconst parent = vm.$parentif (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {remove(parent.$children, vm)}// teardown watchersif (vm._watcher) {vm._watcher.teardown()}let i = vm._watchers.lengthwhile (i--) {vm._watchers[i].teardown()}// remove reference from data ob// frozen object may not have observer.if (vm._data.__ob__) {vm._data.__ob__.vmCount--}// call the last hook...vm._isDestroyed = true// invoke destroy hooks on current rendered treevm.__patch__(vm._vnode, null)// fire destroyed hookcallHook(vm, 'destroyed')// turn off all instance listeners.vm.$off()// remove __vue__ referenceif (vm.$el) {vm.$el.__vue__ = null}// release circular reference (#6759)if (vm.$vnode) {vm.$vnode.parent = null}}對應了生命周期圖上的這個部分
清理工作還是挺多的
renderMixin
主要是定義了跟虛擬DOM渲染有關的屬性($vnode)和私有方法(_render),在后面講到虛擬DOM的時候再來講。
下一篇文章要講Vue響應式原理了,想想都激動。
總結
以上是生活随笔為你收集整理的for里面调用方法 vue_Vue源码阅读连载之Vue实例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java xml 节点路径,Select
- 下一篇: git 拉取远程其他分支代码_git切换