結(jié)合源碼談?wù)剬ue組件化的理解

1.組件定義

image.png

全局組件和單文件組件兩種方式

2.1 全局組件

源碼分析1:src\core\global-api\assets.js

//是component,filter,directive三個的綜合方法
export function initAssetRegisters (Vue: GlobalAPI) {
  /**
   * Create asset registration methods.
   */
  //['component'陪汽,'filter','directive']循環(huán)遍歷這三個方法
  ASSET_TYPES.forEach(type => {
    //動態(tài)附加三個靜態(tài)方法展辞,然后分別去定義
    Vue[type] = function (
      id: string,
      definition: Function | Object
    ): Function | Object | void {
      if (!definition) {
        return this.options[type + 's'][id]
      } else {
        /* istanbul ignore if */
        if (process.env.NODE_ENV !== 'production' && type === 'component') {
          validateComponentName(id)
        }
        //如果def是對象,那么definition就是組件的配置對象
        if (type === 'component' && isPlainObject(definition)) {
          //定義組件name
          definition.name = definition.name || id
          //extend創(chuàng)建組件構(gòu)造函數(shù),def變成了構(gòu)造函數(shù)
          definition = this.options._base.extend(definition)
        }
        if (type === 'directive' && typeof definition === 'function') {
          definition = { bind: definition, update: definition }
        }
        //注冊 this.options[components][comp]=Ctor
        //在當(dāng)前vue的選項中加上components選項祖秒,這樣的話
        //定義全局組件痕囱,將來會被繼承在所有的子組件里
        this.options[type + 's'][id] = definition
        return definition
      }
    }
  })
}

再看看extend,js


image.png

1.2 單文件組件

其實寫的并不是一個組件而是組件的一個配置對象田轧。

vue-loader會編譯template為render函數(shù),最終導(dǎo)出的依然是組件配置對象鞍恢。
(style里面的代碼會用style-loader傻粘,css里的會用css-loader處理成css文件)
所以我們現(xiàn)在寫的.vue文件最終都會導(dǎo)出為js對象,這些對象都是組件的配置對象而不是組件的構(gòu)造函數(shù)帮掉。

2.組件化優(yōu)點(提高維護性弦悉,測試性,復(fù)用性)

源碼位置:lifecycle.js-mountComponent()
組件蟆炊、watcher稽莉、渲染函數(shù)和更新函數(shù)之間的關(guān)系

mountComponent()定義了組件和watcher一一對應(yīng)的關(guān)系。

//組件的實例在執(zhí)行$mount的時候會調(diào)用的方法
export function mountComponent (
//創(chuàng)建一個組件的時候也創(chuàng)建了一個與之對應(yīng)的watcher實例盅称,
  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted && !vm._isDestroyed) {
        callHook(vm, 'beforeUpdate')
      }
    }
...
 updateComponent = () => {
      vm._update(vm._render(), hydrating)
      //調(diào)用組件的更新函數(shù)和渲染函數(shù)
    }

創(chuàng)建一個組件的時候也創(chuàng)建了一個與之對應(yīng)的watcher實例肩祥,
如果這個組件中的數(shù)據(jù)發(fā)生了變化,其實只會調(diào)用該組件的渲染函數(shù)
如果在應(yīng)用程序中缩膝,合理切割組件的力度混狠,比如說將一個經(jīng)常發(fā)生數(shù)據(jù)變化的一塊內(nèi)容,切割成一個組件疾层,將來頻繁執(zhí)行的渲染函數(shù)和更新函數(shù)和打補丁的范圍就變得更小了.所以把數(shù)據(jù)變化頻繁的提取為組件将饺,可以有效地提升性能。

3.組件化的實現(xiàn)

構(gòu)造函數(shù)痛黎,src\core\global-api\extend.js
實例化及掛載予弧,src\core\vdom\patch.js-createElm()

組件的構(gòu)造函數(shù)通過extend來生成,extend執(zhí)行的時間點不完全相同湖饱,如果說是全局注冊組件掖蛤,是立刻執(zhí)行,甚至在當(dāng)前實例化之前就執(zhí)行了井厌。如果是一個局部組件蚓庭,聲明在一個單文件組件中致讥,可能在運行時的某個時刻才會去執(zhí)行extend方法,具體是什么時刻可以看一下patch的源碼器赞。
我們都知道dom是一個樹垢袱,那虛擬dom也是一個樹,那么這棵樹的起始點一定有一個根節(jié)點港柜,實際上根節(jié)點就是起始點请契,那么根節(jié)點一定不是一個組件,而是vue的根實例夏醉。那就不存在自定義組件這些東西爽锥。
當(dāng)vue實例化的時候,它執(zhí)行掛載之后授舟,會有一個初始化過程救恨,要執(zhí)行打補丁patch,在此過程中會執(zhí)行createElm方法释树,因為根節(jié)點需要創(chuàng)建相對應(yīng)的一個真實的dom,
看看createElm方法中有幾行代碼是和組件化息息相關(guān)的擎淤。

//如果要創(chuàng)建的是組件奢啥,走下面的流程,如果傳入的是vue實例,就不執(zhí)行
    if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
      return
    }

···javascript
//這里createComponent是把前面的那個執(zhí)行的結(jié)果vnode轉(zhuǎn)化為真實的dom
function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
//獲取管理鉤子函數(shù)
let i = vnode.data
if (isDef(i)) {
const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
//存在init鉤子嘴拢,則執(zhí)行之創(chuàng)建實例并掛載
//這個初始化函數(shù)是在create-component.js中定義的
if (isDef(i = i.hook) && isDef(i = i.init)) {
i(vnode, false /* hydrating */)
}
//如果組件實例存在
if (isDef(vnode.componentInstance)) {
//屬性初始化
initComponent(vnode, insertedVnodeQueue)
//dom插入操作
insert(parentElm, vnode.elm, refElm)
if (isTrue(isReactivated)) {
reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
}
return true
}
}
}

> create-component.js
···javascript
//默認(rèn)組件管理鉤子
const componentVNodeHooks = {
  init (vnode: VNodeWithData, hydrating: boolean): ?boolean {
    if (
      vnode.componentInstance &&
      !vnode.componentInstance._isDestroyed &&
      vnode.data.keepAlive
    ) {
      // kept-alive components, treat as a patch
      const mountedNode: any = vnode // work around flow
      componentVNodeHooks.prepatch(mountedNode, mountedNode)
    } else {
//不是keep-alive而是要重新創(chuàng)建實例的時候
      //創(chuàng)建組件實例
      const child = vnode.componentInstance = createComponentInstanceForVnode(
        vnode,
        activeInstance
      )
//子組件里面的子元素或者子組件也進行了初始化
      //創(chuàng)建完成并掛載(掛載后才能從虛擬dom變成真實dom)
      child.$mount(hydrating ? vnode.elm : undefined, hydrating)
    }
  },
···
實例化的過程是自上而下的桩盲,從根往葉子節(jié)點進行創(chuàng)建,但是由于子節(jié)點的創(chuàng)建會先執(zhí)行席吴,子節(jié)點創(chuàng)建后就立刻掛載了赌结,所以掛載是從下而上的。

# 4.總結(jié)
+ 基礎(chǔ)部分
1.組件是獨立和可復(fù)用的代碼組織單元孝冒。組件系統(tǒng)是Vue核心特性之一柬姚, 它使開發(fā)者使用小型、獨立和通匙校可
復(fù)用的組件構(gòu)建大型應(yīng)用;
2.組件化開發(fā)能大幅提高應(yīng)用開發(fā)效率量承、測試性、復(fù)用性等; 
+ 落地
3.組件使用按分類有:
頁面組件:用路由來導(dǎo)航一些頁面組件穴店,復(fù)用性不那么強撕捍,但是是組織頁面直接來回切換的必備組件
業(yè)務(wù)組件:登錄組件,購物車?yán)锏幕照陆M件泣洞,有很強的業(yè)務(wù)性和通用性
通用組件:按鈕忧风,表單,輸入框等等;
+ 特點
4. vue的組件是基于配置的球凰,我們通常編寫的組件是組件配置而非組件,框架后續(xù)會生成其構(gòu)造函數(shù)狮腿,它們基于
VueComponent,這個類擴展于Vue该窗,擴展的過程中,會繼承于vue中已經(jīng)有的一些選項蚤霞。
5. vue中常見組件化技術(shù)有:屬性prop,自定義事件,插槽等,它們主要用于組件通信酗失、擴展等;
+ 注意事項
6.合理的劃分組件,有助于提升應(yīng)用性能;
切割出經(jīng)常發(fā)生數(shù)據(jù)變化的組件昧绣,將來watcher去更新的時候规肴,只會重新渲染它對應(yīng)的組件,而不會影響其他的地方夜畴。

7.組件應(yīng)該是高內(nèi)聚拖刃、低耦合的;
高內(nèi)聚:組件本來就是一個獨立功能單元,功能應(yīng)該是單一的贪绘,獨立的兑牡,這樣組件才更容易復(fù)用。
低耦合:不應(yīng)該與其他組件有太多的耦合關(guān)系税灌。

8.遵循單向數(shù)據(jù)流的原則均函。
通過屬性把值傳進來或者通過vuex的方式直接把值注入進來。如果要改變值菱涤,通過事件派發(fā)出去苞也,然后讓父級接管這個事件,從而修改數(shù)據(jù)粘秆。如果使用vuex如迟,使用commit或者dispatch的方式,讓vuex去改變這個值攻走。而不是組件自己去改數(shù)據(jù)殷勘。讓組件功能更單一。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末昔搂,一起剝皮案震驚了整個濱河市玲销,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌巩趁,老刑警劉巖痒玩,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異议慰,居然都是意外死亡蠢古,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門别凹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來草讶,“玉大人,你說我怎么就攤上這事炉菲《檎剑” “怎么了坤溃?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長嘱丢。 經(jīng)常有香客問我薪介,道長,這世上最難降的妖魔是什么越驻? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任汁政,我火速辦了婚禮,結(jié)果婚禮上缀旁,老公的妹妹穿的比我還像新娘记劈。我一直安慰自己,他們只是感情好并巍,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布目木。 她就那樣靜靜地躺著,像睡著了一般懊渡。 火紅的嫁衣襯著肌膚如雪刽射。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天距贷,我揣著相機與錄音柄冲,去河邊找鬼。 笑死忠蝗,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的漓拾。 我是一名探鬼主播阁最,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼骇两!你這毒婦竟也來了速种?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤低千,失蹤者是張志新(化名)和其女友劉穎配阵,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體示血,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡棋傍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了难审。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瘫拣。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖告喊,靈堂內(nèi)的尸體忽然破棺而出麸拄,到底是詐尸還是另有隱情派昧,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布拢切,位于F島的核電站蒂萎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏淮椰。R本人自食惡果不足惜五慈,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望实苞。 院中可真熱鬧豺撑,春花似錦、人聲如沸黔牵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽猾浦。三九已至陆错,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間金赦,已是汗流浹背音瓷。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留夹抗,地道東北人绳慎。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像漠烧,于是被迫代替她去往敵國和親杏愤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

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