【源碼導讀 】在new Vue()以前,Vue是什麼樣子的?

前言

初步讀完vue(v2.6.10)的源碼,現將讀後感記錄以下,故事就是由此開始。vue

小提示:配合源碼食用更佳美味。node

混沌初開

開始的開始,vue就是一個簡單的函數。web

src/core/instance/index.js設計模式

function Vue (options) {
  this._init(options)
}
複製代碼

擴展功能

設計模式之混入模式,對Vue進行擴展,很值得學習的一種方式。api

initMixin()

定義了_init方法,是入口函數,在new Vue()時第一時間執行的方法。緩存

src/core/instance/init.jsbash

Vue.prototype._init = function (options?: Object){}
複製代碼

stateMixin()

數據相關的擴展。dom

src/core/instance/state.js函數

$data & $props

Vue實例觀察的數據對象和當前組件接收到的props對象。 其實是代理到_data_props工具

Object.defineProperty(Vue.prototype, '$data', {
      get(){
        return this._data
      }
  })
  Object.defineProperty(Vue.prototype, '$props', {
      get(){
        return this._props
      }
  })
複製代碼

$set & $delete

Vue.setVue.delete 的別名

Vue.prototype.$set = set
  Vue.prototype.$delete = del
複製代碼

$watch

實現了$watch方法,觀察Vue實例變化的一個表達式或計算屬性函數。

Vue.prototype.$watch = function () {}
複製代碼

eventsMixin()

事件相關的擴展

src/core/instance/events.js

$on

監聽當前實例上的自定義事件。

Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component{}
複製代碼

$once

監聽一個自定義事件,可是隻觸發一次。一旦觸發以後,監聽器就會被移除。

Vue.prototype.$once = function (event: string, fn: Function): Component{}
複製代碼

$off

移除自定義事件監聽器。

Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component{}
複製代碼

$emit

觸發當前實例上的事件。

Vue.prototype.$emit = function (event: string): Component {}
複製代碼

lifecycleMixin()

生命週期相關的擴展

src/core/instance/lifecycle.js

_update

私有方法,更新dom節點流程的重要函數

Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {}
複製代碼

$forceUpdate

迫使 Vue 實例從新渲染。簡單粗暴

Vue.prototype.$forceUpdate = function (){ // 觸發更新}
複製代碼

$destroy

徹底銷燬一個實例。

Vue.prototype.$destroy = function (){ // 移除事件 指令等 }
複製代碼

installRenderHelpers

render相關的擴展

src/core/instance/render.js

installRenderHelpers

定義render函數有關的方法,在執行render時使用

src/core/instance/render-helpers/index.js

export function installRenderHelpers (target: any) {
  target._o = markOnce
  target._n = toNumber
  target._s = toString
  target._l = renderList
  target._t = renderSlot
  target._q = looseEqual
  target._i = looseIndexOf
  target._m = renderStatic
  target._f = resolveFilter
  target._k = checkKeyCodes
  target._b = bindObjectProps
  target._v = createTextVNode
  target._e = createEmptyVNode
  target._u = resolveScopedSlots
  target._g = bindObjectListeners
  target._d = bindDynamicKeys
  target._p = prependModifier
}
複製代碼

$nextTick

將回調延遲到下次 DOM 更新循環以後執行。跟Vue.nextTick同樣,可是綁定了實例的this

Vue.prototype.$nextTick = function (fn: Function) {
  return nextTick(fn, this)
}
複製代碼

_render

私有方法,建立vnode流程的重要函數

Vue.prototype._render = function (): VNode{}
複製代碼

核心代碼

src/core/index.js

initGlobalAPI()

全局api

src/core/global-api/index.js

config

vue 全局配置 代理到vue的config,在new Vue()以前能夠修改

全局配置 src/core/config.js

import config from '../config'
Object.defineProperty(Vue, 'config', {
    get() {
        return config
    }
})
複製代碼

util

vue的工具函數

Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }
複製代碼

經常使用方法

Vue.set = set // 向響應式對象中添加一個屬性
Vue.delete = del // 刪除對象的屬性。
Vue.nextTick = nextTick // 在下次 DOM 更新循環結束以後執行延遲迴調。
複製代碼

observable

讓一個對象可響應。

Vue.observable = <T>(obj: T): T => {
  observe(obj)
  return obj
}
複製代碼

options

默認的options

Vue.options = Object.create(null)
複製代碼

組件、指令、過濾器的存放屬性

組件、指令、過濾器實際上就是在options中建立了三個屬性。

ASSET_TYPES.forEach(type => {
    Vue.options[type + 's'] = Object.create(null)
  })
// Vue.options.components
// Vue.options.directives
// Vue.options.filters
複製代碼

options._base

私有屬性,緩存Vue

Vue.options._base = Vue
複製代碼

內部組件

定義了內部組件KeepAlive

extend(Vue.options.components, builtInComponents)
複製代碼

initUse(Vue)

定義了Vue.use方法, 安裝 Vue.js 插件。

src/core/global-api/use.js

Vue.use = function (plugin: Function | Object) {}
複製代碼

initMixin(Vue)

定義了Vue.mixin方法, 全局註冊一個混入

src/core/global-api/mixin.js

Vue.mixin = function (mixin: Object) {}
複製代碼

initExtend(Vue)

定義了Vue.extend方法, Vue 構造器,建立一個「子類」。

src/core/global-api/extend.js

Vue.extend = function (extendOptions: Object): Function
複製代碼

initAssetRegisters(Vue)

用於實現組件、指令、過濾器方法

Vue.directive 註冊或獲取全局指令。

Vue.filter 註冊或獲取全局過濾器。

Vue.component 註冊或獲取全局組件。

src/core/global-api/assets.js

ASSET_TYPES.forEach(type => {
    Vue[type] = function (
        id: string,
        definition: Function | Object
    ): Function | Object | void {
        if (!definition) {
          return this.options[type + 's'][id]
        } else {
            if (type === 'component' && isPlainObject(definition)) {
                definition.name = definition.name || id
                definition = this.options._base.extend(definition)
            }
            if (type === 'directive' && typeof definition === 'function') {
                definition = { bind: definition, update: definition }
            }
            this.options[type + 's'][id] = definition
            return definition
        }
    }
})
複製代碼

version

版本號

Vue.version = '__VERSION__'
複製代碼

web運行時

再次之上是vue的核心代碼,與平臺無關。如下是web平臺有關代碼。 咱們一般用cli寫的代碼是不須要編譯器的,由於vue-loader有一個編譯過程,這個版本一般較小,可是不帶編譯器。

僅運行時的版本src/platforms/web/entry-runtime.js 實際上就是 src/platforms/web/runtime/index.js

定義配置

Vue.config.mustUseProp = mustUseProp
Vue.config.isReservedTag = isReservedTag
Vue.config.isReservedAttr = isReservedAttr
Vue.config.getTagNamespace = getTagNamespace
Vue.config.isUnknownElement = isUnknownElement
複製代碼

內部指令

平臺相關 定義內部指令v-modelv-show

extend(Vue.options.directives, platformDirectives)
複製代碼

內部組件

平臺相關 定義內部組件TransitionTransitionGroup,上面提到過平臺無關的內部組件KeepAlive

extend(Vue.options.components, platformComponents)
複製代碼

patch

打補丁方法,平臺不同打補丁的方法也不同,設計很巧妙。

Vue.prototype.__patch__ = inBrowser ? patch : noop
複製代碼

$mount

若是 Vue 實例在實例化時沒有收到 el 選項,則它處於「未掛載」狀態,沒有關聯的 DOM 元素。可使用 vm.$mount()手動地掛載一個未掛載的實例。

最後執行了實際上是src/core/instance/lifecycle.js中的mountComponent方法。也就是對mountComponent的一次封裝

Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}
複製代碼

帶編譯的運行時

這個版本比較大,由於帶有編譯器。

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

$mount 二次封裝

const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && query(el)
  const options = this.$options
  
  // 其實所謂的編譯就是講模板編譯成render函數
  
  // 存在render將直接使用render函數,則不須要編譯
  // 不存在render則讀取template template有害幾種配置方法也是在此到處理

    if (template) {
      const { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: process.env.NODE_ENV !== 'production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this)
      options.render = render
      options.staticRenderFns = staticRenderFns

    }

  // 實際仍是調用了$mount
  return mount.call(this, el, hydrating)
}
複製代碼

compile

在 render 函數中編譯模板字符串 編譯器版本特有。

Vue.compile = compileToFunctions
複製代碼

系列

結尾

感謝各位的閱讀,錯誤是在所不免的,如有錯誤,或者有更好的理解,請在評論區留言,再次感謝。但願你們相互學習,共同進步。

相關文章
相關標籤/搜索