閱讀307 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Vue源碼筆記本(一)

(該文章對src/core/instance下的文件的代碼功能做了注解,便於大家在看源碼過程中快速理解)

入口文件 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

Vue的構造函數在此, 然後通過mixin的方式將

  • 數據方法等特性處理(state)
  • 事件處理(events)
  • 生命周期(lifecycle)
  • 渲染函數(render)

的相關的方法掛到Vue的原型上去。

1.init

掛上了_init 方法,沒錯就是 構造函數當中調用的_init方法。

這裏做了一些參數處理,其中作者在參數對象賦值的時候,用一個個賦值代替列舉賦值來提升性能。 這樣注釋的: // doing this because it's faster than dynamic enumeration.

然後就是依次執行以下方法,包括各個init函數和觸發鉤子

 /* 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')
    initState(vm)
    callHook(vm, 'created')
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }

2.state

initState一共做了如下操作

 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) initWatch(vm, opts.watch)

initProps和initData的處理類似:

initProps做了類型檢驗,做了defineReactive就是響應式處理;

initData做了函數處理(data是對象或者函數),然後檢查key是否與props重複,還檢查了key是否$ 或者 _ 開頭,這是vue內部使用, 如果你的key以此開頭,你就會拿不到你的key了(這個好像沒看到說明,感覺可以提示下)

然後做了個proxy,把用戶參數props和data分別代理到 _props 和 _data 這2個內部屬性,用戶訪問其實訪問到了_props和_data下。

initMethods 這個就是把methods內部的函數bind到實例然後掛到實例

initWatch 通過遍曆然後執行$watch方法來處理

initComputed 遍曆為創建一個 Watcher 實例,然後放到_computedWatchers

(響應式相關後續再展開)

stateMixin 混入方法掛了$data,$props,$set,$delete等讓數據操作指向_data 和 _props,然後掛了$watch 方法

3.events

掛載$on,$once,$off,$emit 四個方法

事件句柄都保存在 _events 這個屬性裏,在$on方法裏做了個處理,判斷該實例是否有使用生命周期的hook,如果沒有_hasHookEvent這個值是false,然後在lifecycle的callHook方法裏將不會$emit生命周期hook,是一個性能優化。

4.lifecycle

在原型上掛上 _mount 方法,這個比較重要, $mount 用的就是方法, ($mount在上麵的init函數被調用)這個方法運行的時候需要用到render函數裏了,沒有會報錯(關於這點,vue會把template或者el裏的html模版最終轉成render函數,一般如果我們用vue-loader開發,已經把template裏的內容轉成了render函數,所以最後打包出來的不會含有轉化template的功能代碼),render函數返回的是一個vnode,下麵貼個_mount部分代碼:

 callHook(vm, 'beforeMount')
    vm._watcher = new Watcher(vm, function updateComponent () {
      vm._update(vm._render(), hydrating)
    }, noop)
    hydrating = false
    // manually mounted instance, call mounted on self
    // mounted is called for render-created child components in its inserted hook
    if (vm.$vnode == null) {
      vm._isMounted = true
      callHook(vm, 'mounted')
    }

這裏可以看到2個生命周期了

_update 方法開始去做dom更新了,看_update方法

一開始用_isMounted判斷是否觸發beforeUpdate鉤子,因為第一次插入dom也是調用這個方法,但是不應該觸發beforeUpdate

然後就去調用 patch 方法去做diff 然後更新dom了。(vnode以後再看)

另外,還掛載了$forceUpdate和$destroy 這2個方法

5.render

先掛載了 $nextTick 方法.

然後在renderMixin函數內往原型掛載了_render 方法(在instancei 下有個render-helpers文件夾裏麵有處理render相關的工具函數供調用)這個方法做了slot相關的操作,然後調用render函數拿到vnode ( render 相關的需要具體展開)

這裏有個_renderProxy ,在非生產環境下做了proxy,看 proxy.js ,就是在訪問實例屬性的時候,訪問不存在的屬性的時候給予提示.

以上。

最後更新:2017-04-07 21:05:52

  上一篇:go (更新完結)阿裏珍貴技術資料免費下載
  下一篇:go 4月7日雲棲精選夜讀:給 Java 學習者的超全教程整理