VueJS學(xué)習(xí)之旅 08

下面我就來看看VueJS中主要的組件選項(xiàng)英支,以及它們在Vue實(shí)例對象初始化過程中是如何完成屬性合并的。


選項(xiàng)options / 生命周期鉤子

首先,我們要看看VueJS默認(rèn)都提供了哪些生命周期鉤子。
前面我們曾經(jīng)學(xué)習(xí)過VueJS初始化Global Config的相關(guān)過程慢洋,涉及到一個(gè)文件 'src/core/config.js',其中給定了一些默認(rèn)的配置選項(xiàng)陆盘。
我們打開這個(gè)文件普筹,找到 _lifecycleHooks 的具體配置。

/**
  * List of lifecycle hooks.
  */
_lifecycleHooks: [
  'beforeCreate',
  'created',
  'beforeMount',
  'mounted',
  'beforeUpdate',
  'updated',
  'beforeDestroy',
  'destroyed',
  'activated',
  'deactivated'
],

這些就是默認(rèn)給出的生命周期鉤子的聲明礁遣,Vue實(shí)例對象在整個(gè)存活過程中斑芜,會(huì)根據(jù)當(dāng)前所處的狀態(tài),來觸發(fā)這些鉤子祟霍。
有了這些鉤子杏头,我們就可以以切面的方式,在不同的時(shí)間節(jié)點(diǎn)上進(jìn)行一些其他操作沸呐。而且實(shí)現(xiàn)方式非常簡單醇王,就是在初始化Vue實(shí)例對象時(shí),給定具體鉤子對應(yīng)的回調(diào)即可崭添。

這樣寓娩,接下來我們就有必要看看具體這些鉤子的回調(diào)時(shí)如何被調(diào)用的。
在之前我們學(xué)習(xí) VueJS 實(shí)例方法的源碼時(shí)呼渣,經(jīng)常會(huì)看到代碼之中會(huì)調(diào)用 callHook(vm, hook) 這樣的語句棘伴,很明顯這就是在調(diào)用相關(guān)的生命周期鉤子的回調(diào)函數(shù)。
來看看這個(gè) callhook 函數(shù)的實(shí)現(xiàn)細(xì)節(jié):

// src/core/instance/lifecycle.js

export function callHook (vm: Component, hook: string) {
  const handlers = vm.$options[hook]
  if (handlers) {
    for (let i = 0, j = handlers.length; i < j; i++) {
      handlers[i].call(vm)
    }
  }
  if (vm._hasHookEvent) {
    vm.$emit('hook:' + hook)
  }
}
  1. 函數(shù)接收兩個(gè)參數(shù)屁置,第一個(gè)是Vue實(shí)例對象焊夸,第二個(gè)是hook的名字
  2. 根據(jù) hookvm.$options 中找到相應(yīng)的回調(diào)handlers。(為何有多個(gè)handlers蓝角,可以參看'src/core/util/options.js'文件中mergeHook相關(guān)邏輯)
  3. 依次調(diào)用 handler.call(vm)阱穗,這樣就使得所有的生命周期鉤子自動(dòng)綁定 this 上下文到實(shí)例中
  4. vue實(shí)例根據(jù)條件調(diào)用 vm.$emit 觸發(fā)'hook:xxxx'事件

可以前往 這里 查看生命周期示例饭冬。

  • beforeCreate, created, beforeMount, mounted, beforeUpdate, updated, beforeDestroy, destroyed, activated, deactivated

那這些選項(xiàng)是如何初始化的呢。
我們之前提到過揪阶,VueJS預(yù)定義了一些屬性合并策略供內(nèi)部使用昌抠。其中有一項(xiàng)就是針對生命周期鉤子的,我們具體來看一下鲁僚。
打開 'src/core/util/options.js' 文件炊苫,找到以下代碼

/**
 * Hooks and param attributes are merged as arrays.
 */
function mergeHook (
  parentVal: ?Array<Function>,
  childVal: ?Function | ?Array<Function>
): ?Array<Function> {
  return childVal
    ? parentVal
      ? parentVal.concat(childVal)
      : Array.isArray(childVal)
        ? childVal
        : [childVal]
    : parentVal
}
config._lifecycleHooks.forEach(hook => {
  strats[hook] = mergeHook
})

由代碼可見,針對所有的生命周期鉤子蕴茴,都使用同一個(gè)屬性合并策略劝评。

  1. 子實(shí)例不存在,直接返回父實(shí)例
  2. 子實(shí)例存在倦淀,父實(shí)例不存在,返回子實(shí)例(子實(shí)例不是數(shù)組則包裝為數(shù)組)
  3. 父子實(shí)例都存在声畏,合并兩個(gè)實(shí)例

選項(xiàng)options / 資源

接下來撞叽,我們看看VueJS默認(rèn)都提供了資源類型的選項(xiàng)。
前面我們曾經(jīng)學(xué)習(xí)過VueJS初始化Global Config的相關(guān)過程插龄,涉及到一個(gè)文件 'src/core/config.js'愿棋,其中給定了一些默認(rèn)的配置選項(xiàng)。
我們打開這個(gè)文件均牢,找到 _assetTypes 的具體配置糠雨。

/**
   * List of asset types that a component can own.
   */
  _assetTypes: [
    'component',
    'directive',
    'filter'
  ],

以上這些就是對應(yīng)官方API中與資源相關(guān)的選項(xiàng)。

  • components, directives, filters

這些選項(xiàng)的初始化可以參考以下代碼:

/**
 * Assets
 *
 * When a vm is present (instance creation), we need to do
 * a three-way merge between constructor options, instance
 * options and parent options.
 */
function mergeAssets (parentVal: ?Object, childVal: ?Object): Object {
  const res = Object.create(parentVal || null)
  return childVal
    ? extend(res, childVal)
    : res
}

config._assetTypes.forEach(function (type) {
  strats[type + 's'] = mergeAssets
})

由代碼可見徘跪,針對上面3種資源選項(xiàng)甘邀,都使用同一個(gè)屬性合并策略。

  1. 代碼邏輯相對簡單垮庐,詳見上面的注釋

選項(xiàng)options / 其它

對于其它一些屬性松邪,代碼相對分散。這里哨查,我們根據(jù)預(yù)定義的屬性合并策略逗抑,來依次了解一下。

  • el, propsData

/**
 * Options with restrictions
 */
if (process.env.NODE_ENV !== 'production') {
  strats.el = strats.propsData = function (parent, child, vm, key) {
    if (!vm) {
      warn(
        `option "${key}" can only be used during instance ` +
        'creation with the `new` keyword.'
      )
    }
    return defaultStrat(parent, child)
  }
}
...
/**
 * Default strategy.
 */
const defaultStrat = function (parentVal: any, childVal: any): any {
  return childVal === undefined
    ? parentVal
    : childVal
}

對于這兩個(gè)選項(xiàng)的合并寒亥,使用默認(rèn)的合并策略邮府。(在非生產(chǎn)環(huán)境,若 vm 實(shí)例不存在溉奕,給出警告信息)

  • watch

/**
 * Watchers.
 *
 * Watchers hashes should not overwrite one
 * another, so we merge them as arrays.
 */
strats.watch = function (parentVal: ?Object, childVal: ?Object): ?Object {
  /* istanbul ignore if */
  if (!childVal) return parentVal
  if (!parentVal) return childVal
  const ret = {}
  extend(ret, parentVal)
  for (const key in childVal) {
    let parent = ret[key]
    const child = childVal[key]
    if (parent && !Array.isArray(parent)) {
      parent = [parent]
    }
    ret[key] = parent
      ? parent.concat(child)
      : [child]
  }
  return ret
}
  • props, methods, computed

/**
 * Other object hashes.
 */
strats.props =
strats.methods =
strats.computed = function (parentVal: ?Object, childVal: ?Object): ?Object {
   if (!childVal) return parentVal
   if (!parentVal) return childVal
   const ret = Object.create(null)
   extend(ret, parentVal)
   extend(ret, childVal)
   return ret
}
  • data

/**
 * Data
 */
strats.data = function (
  parentVal: any,
  childVal: any,
  vm?: Component
): ?Function {
  if (!vm) {
    // in a Vue.extend merge, both should be functions
    if (!childVal) {
      return parentVal
    }
    if (typeof childVal !== 'function') {
      process.env.NODE_ENV !== 'production' && warn(
        'The "data" option should be a function ' +
        'that returns a per-instance value in component ' +
        'definitions.',
        vm
      )
      return parentVal
    }
    if (!parentVal) {
      return childVal
    }
    // when parentVal & childVal are both present,
    // we need to return a function that returns the
    // merged result of both functions... no need to
    // check if parentVal is a function here because
    // it has to be a function to pass previous merges.
    return function mergedDataFn () {
      return mergeData(
        childVal.call(this),
        parentVal.call(this)
      )
    }
  } else if (parentVal || childVal) {
    return function mergedInstanceDataFn () {
      // instance merge
      const instanceData = typeof childVal === 'function'
        ? childVal.call(vm)
        : childVal
      const defaultData = typeof parentVal === 'function'
        ? parentVal.call(vm)
        : undefined
      if (instanceData) {
        return mergeData(instanceData, defaultData)
      } else {
        return defaultData
      }
    }
  }
}
  1. 從上面的代碼可以看出褂傀,針對于data這個(gè)選項(xiàng)的合并策略,返回的是一個(gè)具體的合并屬性的函數(shù)腐宋。
  2. 為何要返回一個(gè)函數(shù)紊服,我沒看看官方的解釋檀轨。
當(dāng)一個(gè)組件被定義, data 必須聲明為返回一個(gè)初始數(shù)據(jù)對象的函數(shù)欺嗤,因?yàn)榻M件可能被用來創(chuàng)建多個(gè)實(shí)例参萄。
如果 data 仍然是一個(gè)純粹的對象,則所有的實(shí)例將共享引用同一個(gè)數(shù)據(jù)對象煎饼!
通過提供 data 函數(shù)讹挎,每次創(chuàng)建一個(gè)新實(shí)例后,我們能夠調(diào)用 data 函數(shù)吆玖,從而返回初始數(shù)據(jù)的一個(gè)全新副本數(shù)據(jù)對象筒溃。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市沾乘,隨后出現(xiàn)的幾起案子怜奖,更是在濱河造成了極大的恐慌,老刑警劉巖翅阵,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件歪玲,死亡現(xiàn)場離奇詭異,居然都是意外死亡掷匠,警方通過查閱死者的電腦和手機(jī)滥崩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來讹语,“玉大人钙皮,你說我怎么就攤上這事⊥缇觯” “怎么了短条?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長擎值。 經(jīng)常有香客問我慌烧,道長,這世上最難降的妖魔是什么鸠儿? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任屹蚊,我火速辦了婚禮,結(jié)果婚禮上进每,老公的妹妹穿的比我還像新娘汹粤。我一直安慰自己,他們只是感情好田晚,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布嘱兼。 她就那樣靜靜地躺著,像睡著了一般贤徒。 火紅的嫁衣襯著肌膚如雪芹壕。 梳的紋絲不亂的頭發(fā)上汇四,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音踢涌,去河邊找鬼通孽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛睁壁,可吹牛的內(nèi)容都是我干的背苦。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼潘明,長吁一口氣:“原來是場噩夢啊……” “哼行剂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起钳降,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬榮一對情侶失蹤厚宰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后遂填,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體固阁,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年城菊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碉克。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凌唬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出漏麦,到底是詐尸還是另有隱情客税,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布撕贞,位于F島的核電站更耻,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏捏膨。R本人自食惡果不足惜秧均,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望号涯。 院中可真熱鬧目胡,春花似錦、人聲如沸链快。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽域蜗。三九已至巨双,卻和暖如春噪猾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背筑累。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來泰國打工袱蜡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人疼阔。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓戒劫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親婆廊。 傳聞我的和親對象是個(gè)殘疾皇子迅细,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 單例模式 適用場景:可能會(huì)在場景中使用到對象,但只有一個(gè)實(shí)例淘邻,加載時(shí)并不主動(dòng)創(chuàng)建茵典,需要時(shí)才創(chuàng)建 最常見的單例模式,...
    Obeing閱讀 2,056評(píng)論 1 10
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理宾舅,服務(wù)發(fā)現(xiàn)统阿,斷路器,智...
    卡卡羅2017閱讀 134,629評(píng)論 18 139
  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內(nèi)容筹我,還有我對于 Vue 1.0 印象不深的內(nèi)容扶平。關(guān)于...
    云之外閱讀 5,046評(píng)論 0 29
  • 深入響應(yīng)式 追蹤變化: 把普通js對象傳給Vue實(shí)例的data選項(xiàng),Vue將使用Object.defineProp...
    冥冥2017閱讀 4,849評(píng)論 6 16
  • Vue 實(shí)例 屬性和方法 每個(gè) Vue 實(shí)例都會(huì)代理其 data 對象里所有的屬性:var data = { a:...
    云之外閱讀 2,204評(píng)論 0 6