vue源碼(1):Vue 初始化過程

問題:new Vue(options) 發(fā)生了什么?

1.處理組件配置項

  • initInternalComponent(性能優(yōu)化挂签,減少原型鏈的動態(tài)查找)疤祭,初始化每個子組件時做了一些性能優(yōu)化,將組件配置對象上的一些深層次屬性放到 vm.$options 選項中饵婆,以提高代碼的執(zhí)行效率勺馆。
  • mergeOptions (選項合并),根組件初始化時進行了選項合并侨核,將全局配置的選項合并到跟組建的局部配置上草穆。
  • 源碼文件路徑 :/src/core/instance/init.js
export function initMixin(Vue: Class<Component>) {
  // 初始化Vue的過程
  Vue.prototype._init = function (options?: Object) {
    //vue實例
    const vm: Component = this;
    // 每個vue實例都有一個遞增uid
    vm._uid = uid++;
    vm._isVue = true;
    //配置組件配置項
    if (options && options._isComponent) {
      /*性能優(yōu)化,減少原型鏈的動態(tài)查找搓译,提高執(zhí)行效率
       *每個子組件初始化時走這里悲柱,這里做了一些性能優(yōu)化
       *將組件一些深層次的屬性放入vm.$option中,提高代碼效率
       */
      initInternalComponent(vm, options);
    } else {
      /**
       *初始化根組件時走這里些己,合并 Vue 的全局配置到根組件的局部配置豌鸡,比如 Vue.component 注冊的全局組件會合并到 根實例的 components 選項中
       *至于每個子組件的選項合并則發(fā)生在兩個地方:
       *  1、Vue.component 方法注冊的全局組件在注冊時做了選項合并
       *  2轴总、{ components: { xx } } 方式注冊的局部組件在執(zhí)行編譯器生成的 render 函數(shù)時做了選項合并直颅,包括根組件中的 components 配置
       * */
      // 根組件選項合并,將全局配置合并到根組件的局部配置上
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      );
    }
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== "production") {
      // 設置代理怀樟,將 vm 實例上的屬性代理到 vm.renderProxy
      initProxy(vm);
    } else {
      vm._renderProxy = vm;
    }
    // expose real self
    vm._self = vm;
    //初始化組件實例關系屬性功偿,比如 $parent、$children往堡、$root械荷、$refs 等
    initLifecycle(vm);
    /**
     * 初始化自定義事件,這里需要注意一點虑灰,<comp @click="handelClick"> 上注冊的事件吨瞎,監(jiān)聽者不是父組件,
     * 而是子組件本身,也就是說事件的派發(fā)和監(jiān)聽這都是子組件本身穆咐,和父組件無關
     *
     **/
    initEvents(vm);
    //解析組件的插槽信息颤诀,得到vm.$slot處理渲染函數(shù),得到vm.$ ceraterElement方法
    initRender(vm);
    //
    callHook(vm, "beforeCreate");
    initInjections(vm); // resolve injections before data/props
    initState(vm);
    initProvide(vm); // resolve provide after data/props
    callHook(vm, "created");

    if (vm.$options.el) {
      vm.$mount(vm.$options.el);
    }
  };
}

2.初始化實例關系,比如 parent对湃、children崖叫、root、refs 等

  • initLifecycle()
  • 源碼文件路徑:src\core\instance\lifecycle.js
export function initLifecycle (vm: Component) {
  const options = vm.$options

  // locate first non-abstract parent
  let parent = options.parent
  if (parent && !options.abstract) {
    while (parent.$options.abstract && parent.$parent) {
      parent = parent.$parent
    }
    parent.$children.push(vm)
  }

  vm.$parent = parent
  vm.$root = parent ? parent.$root : vm

  vm.$children = []
  vm.$refs = {}

  vm._watcher = null
  vm._inactive = null
  vm._directInactive = false
  vm._isMounted = false
  vm._isDestroyed = false
  vm._isBeingDestroyed = false
}

3.處理自定義事件

  • initEvents
  • 源碼文件路徑 :src\core\instance\events.js
export function initEvents (vm: Component) {
  vm._events = Object.create(null)
  vm._hasHookEvent = false
  // init parent attached events
  const listeners = vm.$options._parentListeners
  if (listeners) {
    updateComponentListeners(vm, listeners)
  }
}
  • 問題思考:父組件調用子組件時子組件調用的事件是誰在監(jiān)聽
 <comp @click="handleClick" /> 
//通過vue的初始化 this.$emit('click')會被轉為this.$on('click',function handleClick(){})
//這里的this指的是子組件拍柒,所以子組件事件調用以后是子組件自己在監(jiān)聽自己

4.調用 beforeCreate 鉤子函數(shù)

  • callHook(vm, "beforeCreate")
  • 源碼文件路徑:src\core\instance\lifecycle.js
export function callHook (vm: Component, hook: string) {
  // #7573 disable dep collection when invoking lifecycle hooks
  pushTarget()
  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()
}

5.初始化組件的 inject 配置項(inject 為一個高階函數(shù)可在vue.js文檔中查看具體的使用方法)

  • initInjections
  • 源碼文件路徑 :/src/core/instance/inject.js
/**
 * 解析 inject 配置項心傀,從祖代組件的 provide 配置中找到 key 對應的值,否則用 默認值拆讯,最后得到 result[key] = val
 * inject 對象肯定是以下這個結構脂男,因為在 合并 選項時對組件配置對象做了標準化處理
 * @param {*} inject = {
 *  key: {
 *    from: provideKey,
 *    default: xx
 *  }
 * }
 */
export function resolveInject (inject: any, vm: Component): ?Object {
  if (inject) {
    // inject is :any because flow is not smart enough to figure out cached
    const result = Object.create(null)
    // inject 配置項的所有的 key
    const keys = hasSymbol
      ? Reflect.ownKeys(inject)
      : Object.keys(inject)

    // 遍歷 key
    for (let i = 0; i < keys.length; i++) {
      const key = keys[i]
      // 跳過 __ob__ 對象
      // #6574 in case the inject object is observed...
      if (key === '__ob__') continue
      // 拿到 provide 中對應的 key
      const provideKey = inject[key].from
      let source = vm
      // 遍歷所有的祖代組件养叛,直到 根組件,找到 provide 中對應 key 的值宰翅,最后得到 result[key] = provide[provideKey]
      while (source) {
        if (source._provided && hasOwn(source._provided, provideKey)) {
          result[key] = source._provided[provideKey]
          break
        }
        source = source.$parent
      }
      // 如果上一個循環(huán)未找到弃甥,則采用 inject[key].default,如果沒有設置 default 值堕油,則拋出錯誤
      if (!source) {
        if ('default' in inject[key]) {
          const provideDefault = inject[key].default
          result[key] = typeof provideDefault === 'function'
            ? provideDefault.call(vm)
            : provideDefault
        } else if (process.env.NODE_ENV !== 'production') {
          warn(`Injection "${key}" not found`, vm)
        }
      }
    }
    return result
  }
}

6. 數(shù)據響應式的處理 props潘飘、methods、data掉缺、computed、watch

  • initState
  • 源碼文件路徑 :src\core\instance\state.js
export function initState (vm: Component) {
  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 && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch)
  }
}

7.解析組件配置項上的 provide 對象戈擒,將其掛載到 vm._provided 屬性上

  • initProvide()
  • 源碼文件路徑 :\src\core\instance\inject.js
  • 注:inject()和 provide()是組合使用的高階方法 provide負責提供inject則負責子組件注入
export function initProvide (vm: Component) {
  const provide = vm.$options.provide
  if (provide) {
    vm._provided = typeof provide === 'function'
      ? provide.call(vm)
      : provide
  }
}

8.調用 created 鉤子函數(shù)

  • callHook(vm, 'created')
  • 源碼文件路徑:src\core\instance\lifecycle.js

9.如果發(fā)現(xiàn)配置項上有 el 選項眶明,則自動調用 mount 方法,也就是說有了 el 選項筐高,就不需要再手動調用mount 方法搜囱,反之,沒提供 el 選項則必須調用 $mount

  • 源碼文件路徑:src\core\instance\init.js
 if (vm.$options.el) {
      vm.$mount(vm.$options.el);
    }

10.接下來進入掛載階段

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末柑土,一起剝皮案震驚了整個濱河市蜀肘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌稽屏,老刑警劉巖扮宠,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昆著,死亡現(xiàn)場離奇詭異滞详,居然都是意外死亡,警方通過查閱死者的電腦和手機雕欺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門薄腻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來收捣,“玉大人,你說我怎么就攤上這事庵楷“瞻” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵尽纽,是天一觀的道長咐蚯。 經常有香客問我,道長蜓斧,這世上最難降的妖魔是什么仓蛆? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮挎春,結果婚禮上看疙,老公的妹妹穿的比我還像新娘豆拨。我一直安慰自己,他們只是感情好能庆,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布施禾。 她就那樣靜靜地躺著,像睡著了一般搁胆。 火紅的嫁衣襯著肌膚如雪弥搞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天渠旁,我揣著相機與錄音攀例,去河邊找鬼。 笑死顾腊,一個胖子當著我的面吹牛粤铭,可吹牛的內容都是我干的。 我是一名探鬼主播杂靶,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼梆惯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吗垮?” 一聲冷哼從身側響起垛吗,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎烁登,沒想到半個月后怯屉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡防泵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年蚀之,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捷泞。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡足删,死狀恐怖,靈堂內的尸體忽然破棺而出锁右,到底是詐尸還是另有隱情失受,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布咏瑟,位于F島的核電站拂到,受9級特大地震影響,放射性物質發(fā)生泄漏码泞。R本人自食惡果不足惜兄旬,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧领铐,春花似錦悯森、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至音诈,卻和暖如春幻碱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背细溅。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工褥傍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人喇聊。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓摔桦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親承疲。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容