淺析Vue源碼(一)—— 造物創世

聲明:英文註解爲尤雨溪大神原著,中文爲本人理解翻譯。水平有限若理解有誤請以原著爲準,望指正,見諒哈~vue

要是以爲還不錯,快給我個star,快點這裏githubnode

Vue 項目的起源,實際上是源於對Vue進行實例化:ios

new Vue({
  el: ...,
  data: ...,
  ....
})
複製代碼

那麼在此次實例化的過程當中,究竟發生了哪些行爲?讓咱們來一探究竟。打開Vue的源碼文件,其核心代碼在src/core目錄下。下面咱們從入口文件index.js開始進入:git

// 這個應該是實例化的引入
import Vue from './instance/index'
//這個應該是初始化一些全局API
import { initGlobalAPI } from './global-api/index'
// 這個應該是從判斷執行環境中的引入是不是ssr環境,是一個Boolea類型
import { isServerRendering } from 'core/util/env'
// 這個應該是virtualDom編譯成renderContext的方法
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'
//這裏開始執行初始化全局變量
initGlobalAPI(Vue)
//爲Vue原型定義屬性$isServer
Object.defineProperty(Vue.prototype, '$isServer', {
  get: isServerRendering
})
// 爲Vue原型定義屬性$ssrContext
Object.defineProperty(Vue.prototype, '$ssrContext', {
  get () {
    /* istanbul ignore next */
    return this.$vnode && this.$vnode.ssrContext
  }
})
// 爲vue原型定義當爲ssr環境運行時去加載FunctionalRenderContext方法
// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
  value: FunctionalRenderContext
})

Vue.version = '__VERSION__'
// 導出Vue
export default Vue
複製代碼

接下來咱們來看一下各個加載文件:github

import Vue from './instance/index'
複製代碼

內容以下:api

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
複製代碼

這裏簡單粗暴的定義了一個 Vue Class,而後又調用了一系列init、mixin這樣的方法來初始化一些功能,具體的咱們後面在分析,不過經過代碼咱們能夠確認的是:沒錯!這裏確實是導出了一個 Vue 功能類。瀏覽器

import { initGlobalAPI } from './global-api/index'
複製代碼

initGlobalAPI這個東西,其實在Vue官網上,就已經爲咱們說明了Vue的全局屬性:bash

import config from '../config'
import { initUse } from './use'
import { initMixin } from './mixin'
import { initExtend } from './extend'
import { initAssetRegisters } from './assets'
import { set, del } from '../observer/index'
import { ASSET_TYPES } from 'shared/constants'
import builtInComponents from '../components/index'

import {
  warn,
  extend,
  nextTick,
  mergeOptions,
  defineReactive
} from '../util/index'

export function initGlobalAPI (Vue: GlobalAPI) {
  // config
  const configDef = {}
  configDef.get = () => config
  if (process.env.NODE_ENV !== 'production') {
    configDef.set = () => {
      warn(
        'Do not replace the Vue.config object, set individual fields instead.'
      )
    }
  }
  Object.defineProperty(Vue, 'config', configDef)
  // 這些工具方法不視做全局API的一部分,除非你已經意識到某些風險,不然不要去依賴他們
  // exposed util methods.
  // NOTE: these are not considered part of the public API - avoid relying on
  // them unless you are aware of the risk.
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }
  // 這裏定義全局屬性
  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  Vue.options = Object.create(null)
  ASSET_TYPES.forEach(type => {
    Vue.options[type + 's'] = Object.create(null)
  })

  // this is used to identify the "base" constructor to extend all plain-object
  // components with in Weex's multi-instance scenarios. Vue.options._base = Vue extend(Vue.options.components, builtInComponents) // 定義全局方法 initUse(Vue) initMixin(Vue) initExtend(Vue) initAssetRegisters(Vue) } 複製代碼

♦【Vue.config】 各類全局配置項less

♦【Vue.util】 各類工具函數,還有一些兼容性的標誌位(哇,不用本身判斷瀏覽器了,Vue已經判斷好了) ♦【Vue.set/delete】 這個你文檔應該見過dom

♦【Vue.nextTick】 這個是下一次更新前合併處理data變化作的一次優化

♦【Vue.options】 這個options和咱們上面用來構造實例的options不同。這個是Vue默認提供的資源(組件指令過濾器)。

♦【Vue.use】 經過initUse方法定義

♦【Vue.mixin】 經過initMixin方法定義

♦【Vue.extend】經過initExtend方法定義

import { isServerRendering } from 'core/util/env'
複製代碼
// 這個須要用懶加載在vue渲染前
// this needs to be lazy-evaled because vue may be required before
// ssr使用的時候要把VUE_ENV(vue環境)設置成'server'
// vue-server-renderer can set VUE_ENV
let _isServer
export const isServerRendering = () => {
  if (_isServer === undefined) {
    /* istanbul ignore if */
    if (!inBrowser && !inWeex && typeof global !== 'undefined') {
      // detect presence of vue-server-renderer and avoid
      // Webpack shimming the process
      _isServer = global['process'].env.VUE_ENV === 'server'
    } else {
      _isServer = false
    }
  }
  return _isServer
}
複製代碼
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'
複製代碼
export function FunctionalRenderContext (
  data: VNodeData,
  props: Object,
  children: ?Array<VNode>,
  parent: Component,
  Ctor: Class<Component>
) {
  const options = Ctor.options
  // 確保createElement方法在components方法中
  // ensure the createElement function in functional components
  // 獲得一個惟一的context上下文-主要是爲了檢查是否有重複命名確保惟一性
  // gets a unique context - this is necessary for correct named slot check
  let contextVm
  if (hasOwn(parent, '_uid')) {
  // 表示不存在建立
    contextVm = Object.create(parent)
    // $flow-disable-line
    contextVm._original = parent
  } else {
    // the context vm passed in is a functional context as well.
    // in this case we want to make sure we are able to get a hold to the
    // real context instance.
    contextVm = parent
    // $flow-disable-line
    parent = parent._original
  }
  const isCompiled = isTrue(options._compiled)
  const needNormalization = !isCompiled

  this.data = data
  this.props = props
  this.children = children
  this.parent = parent
  this.listeners = data.on || emptyObject
  this.injections = resolveInject(options.inject, parent)
  this.slots = () => resolveSlots(children, parent)
  // 支持把template編譯的方法
  // support for compiled functional template
  if (isCompiled) {
    // exposing $options for renderStatic()
    this.$options = options
    // pre-resolve slots for renderSlot()
    this.$slots = this.slots()
    this.$scopedSlots = data.scopedSlots || emptyObject
  }

  if (options._scopeId) {
    this._c = (a, b, c, d) => {
      const vnode = createElement(contextVm, a, b, c, d, needNormalization)
      if (vnode && !Array.isArray(vnode)) {
        vnode.fnScopeId = options._scopeId
        vnode.fnContext = parent
      }
      return vnode
    }
  } else {
    this._c = (a, b, c, d) => createElement(contextVm, a, b, c, d, needNormalization)
  }
}

installRenderHelpers(FunctionalRenderContext.prototype)
複製代碼

到這裏,咱們的入口文件差很少就瞭解清楚了,接下來,咱們開始去了解一下 Vue class 的具體實現,其中咱們會了解到Vue的相關生命週期的知識。

感謝muwoo提供的素材

相關文章
相關標籤/搜索