vue 構造函數---vue源碼解析(3)

介紹

使用vue時候,要先new操作符調用,說明Vue是一個構造函數,所以我們首先要把vue的構造函數搞清楚

Vue構造函數的原型

npm run dev

"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",

調用scripts/config.js

  // Runtime+compiler development build (Browser)
  'web-full-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.js'),
    format: 'umd',
    env: 'development',
    alias: { he: './entity-decoder' },
    banner
  }

由此可見入口文件是web/entry-runtime-with-compiler.js,輸出爲dist/vue.js,是一個umd模塊,但是在入口文件中web是哪個文件?
這其實是一個別名配置,script/alias.js文件。

const path = require('path')

const resolve = p => path.resolve(__dirname, '../', p)

module.exports = {
  vue: resolve('src/platforms/web/entry-runtime-with-compiler'),
  compiler: resolve('src/compiler'),
  core: resolve('src/core'),
  shared: resolve('src/shared'),
  web: resolve('src/platforms/web'),
  weex: resolve('src/platforms/weex'),
  server: resolve('src/server'),
  entries: resolve('src/entries'),
  sfc: resolve('src/sfc')
}

由此可見web爲 resolve(‘src/platforms/web’);
在src/platforms/web/entry-runtime-with-compiler.js文件中,看到這樣一句

import Vue from './runtime/index'

然後打開runtime文件看到

import Vue from 'core/index'

看到core來自於scripts/alias.js配置,core指向src/core,打開之後發現

import Vue from './instance/index'

打開./instance/index.js文件

// 從五個文件導入五個方法(不包括 warn)
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'

// 定義 Vue 構造函數
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)
}

// 將 Vue 作爲參數傳遞給導入的五個方法
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

// 導出 Vue
export default Vue

由此才發現這是vue的真正來源
首先重init等五個文件導入方法,然後再定義Vue的構造函數,其中還提醒你使用new來操作,最後導出Vue。

打開./init.js,找到initMixin方法,

export function initMixin(Vue: class<Component>){
    Vue.prototype._init = function(option?:Object){
        //..._init 方法內容
    }
}

發現在vue原型上添加了_init方法,在Vue的構造函數中也使用了這個方法,打開./state.js文件,stateMixin(數據)方法

export function stateMixin (Vue: Class<Component>) {
  const dataDef = {}
  dataDef.get = function () { return this._data }
  const propsDef = {}
  propsDef.get = function () { return this._props }
  if (process.env.NODE_ENV !== 'production') {
    dataDef.set = function () {
      warn(
        'Avoid replacing instance root $data. ' +
        'Use nested data properties instead.',
        this
      )
    }
    propsDef.set = function () {
      warn(`$props is readonly.`, this)
    }
  }
  Object.defineProperty(Vue.prototype, '$data', dataDef)
  Object.defineProperty(Vue.prototype, '$props', propsDef)

  Vue.prototype.$set = set
  Vue.prototype.$delete = del

  Vue.prototype.$watch = function (
    expOrFn: string | Function,
    cb: any,
    options?: Object
  ): Function {
    const vm: Component = this
    if (isPlainObject(cb)) {
      return createWatcher(vm, expOrFn, cb, options)
    }
    options = options || {}
    options.user = true
    const watcher = new Watcher(vm, expOrFn, cb, options)
    if (options.immediate) {
      cb.call(vm, watcher.value)
    }
    return function unwatchFn () {
      watcher.teardown()
    }
  }
}

在這基礎上分析,使用Obejct.defineProperty在Vue.prototype上定義兩個屬性,
這就是datadata和props,兩個屬性分別寫在dataDef和propsDef,仔細分析:

const dataDef = {}
dataDef.get = function () { return this._data }
const propsDef = {}
propsDef.get = function () { return this._props }

可以看出$data實際上是代理_data這個實例屬性,在不是生產環境中,設置set,使其只讀。
接着又在stateMixin在Vue原型上定義了三個方法:
set,set,delete,$watch
image

在分析eventsMixin(事件),eventsMixin在Vue原型上添加是四個個方法:

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

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

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

Vue.prototype.$emit = function(event:string):Compoent{};

下一個分析lifecycle(生命週期),lifecycle在Vue原型上添加三個方法:

Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {}
Vue.prototype.$forceUpdate = function () {}
Vue.prototype.$destroy = function () {}

最後一個renderMixin,在render.js中一開始就以Vue.prototype爲參數調用了installRenderHelpers函數

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
}

這個函數的作用就是爲Vue.prototype添加一些方法。之後又爲Vue.prototype添加兩個方法 $nextTick和_render,最後經過renderMixin之後,Vue.prototype添加如下方法

Vue.prototype._o = markOnce
Vue.prototype._n = toNumber
Vue.prototype._s = toString
Vue.prototype._l = renderList
Vue.prototype._t = renderSlot
Vue.prototype._q = looseEqual
Vue.prototype._i = looseIndexOf
Vue.prototype._m = renderStatic
Vue.prototype._f = resolveFilter
Vue.prototype._k = checkKeyCodes
Vue.prototype._b = bindObjectProps
Vue.prototype._v = createTextVNode
Vue.prototype._e = createEmptyVNode
Vue.prototype._u = resolveScopedSlots
Vue.prototype._g = bindObjectListeners

Vue.prototype.$nextTick = function (fn: Function) {}
Vue.prototype._render = function (): VNode {}

至此instance/index.js代碼運行完成,瞭解了*Mixin作用就是包裝Vue.prototype,

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章