vue總結系列--組件化

上一篇博文梳理了vue的數據驅動和相應式相干的特徵,這一篇博文就來梳理vue的一個很主要的特徵,組件化。
自定義組件之於vue,其意義不亞於函數之於C,java之類的編程言語。
函數是計算機科學中的一大主要的發明。
一方面,它代表着一種自頂向下,逐步求精的分而治之的頭腦,別的一方面,它能夠封裝龐雜完成的細節,供應更高籠統的接口,下降軟件工程的龐雜度。

在vue中,自定義組件也起着相似的作用。

<!–more–>
我們曉得,在組件化的GUI界面上,GUI能夠被視為一棵樹,瀏覽器的DOM就是一個最好的例子。
從規劃上來看,界面能夠算作大盒子套小盒子,小盒子再套更小的盒子。。。
反應到DOM上,DOM某節點的一切子節點,都是該組件的子組件,都是該組件內的元素。

在vue中也是云云,vue組件之間的關聯也是相似DOM一樣,是樹狀的。
在定義一個組件時,須要援用的一切組件,都成為了該組件的子組件。

組件通訊

組件作為一個模塊性子的東西,天然就有着它肯定的獨立性。而且,與別的模塊的耦合都理所應當的有着明白的接口商定。
在vue中,父子組件通訊經由過程組件屬性和事宜來舉行的。
个中,經由過程組件屬性,父組件的數據流向子組件;經由過程事宜,子組件的數據流向父組件。

從籠統的角度看,組件作為一個黑盒子,它有着特定的屬性用以吸收外部通報給它的數據,它也有着特定的事宜,當特定操縱發作時調用回調函數,以關照別的組件。

組件的屬性

自定義屬性

<div id="x-child">
    <span> {{ titleMessage }} <span>
</div>
<script>
    Vue.component('x-child', {
        template: '#x-child',
        props: ['titleMessage']
    });
</script>
<div id="x-parent">
    <x-child title-message="A"></x-child>
    <x-child title-message="B"></x-child>
    <x-child :title-message="message"></x-child>
</div>
<script>
    Vue.component('x-parent', {
        template: '#x-parent',
        data: function () {
            return {
                message: "C"
            };
        }
    });
</script>

上面的例子中,定義了x-child自定義組件,而且在x-parent組件中援用它。
之前在引見數據驅動的時刻,我們曉得,定義vue組件時,能夠經由過程data定義組件內部的狀況,它是組件數據的一部份。
除了data以外,prop(屬性)也是組件數據的泉源之一,父組件經由過程prop將本身的數據通報給子組件。

在定義組件時我們能夠看到:

  1. 經由過程props定義組件能夠吸收的屬性,以至還能指定屬性的默許值及範例,以至還能編寫恣意的函數考證屬性的合法性。明白的指定相似接口聲明,加強可讀性,下降debug難度。
  2. 屬性和內部狀況相似,都作為組件數據的一部份。辨別在於,在vue設想上,屬性是只讀的,能夠作為數據驅動視圖,然則沒法被轉變。(我不太清晰vue有無從語法強迫請求這點,然則優越實踐的vue組件是如許的)

在運用自定義組件時能夠看到:

  1. 組件屬性的靈活性迥殊強,你不僅能通報給它一個牢固的數據,還能夠運用vue的數據綁定語法把父組件內的數據經由過程prop通報給子組件。
    固然,這也是相應式的。當父組件中該數據變化時,天然的,通報給子組件的數據也會變化。那末,子組件中綁定了該數據的視圖部份也固然會被從新襯着以展如今瀏覽器上。
  2. 子組件中定義中的屬性是駝峰寫法,這是相符js編碼範例的。但是,在援用子組件的處所, 屬性應當要寫成短橫線支解式的寫法
    這是因為,html是不辨別大小寫的,vue對此也很無法。這是在現實編碼中很恣意犯的一個毛病,須要注重。

子組件的獨立性

vue中,屬性被設想用於父組件通報數據給子組件的,假如子組件轉變了屬性,那末父組件不會遭到任何影響。這在vue中被稱為 單向數據流
然則,假如屬性用來通報數組或對象等複合的數據結構,那末能夠會出題目。斟酌以下的場景:

  1. 父組件把數據中的對象通報給了子組件的屬性。
  2. 因為能夠修正該屬性,子組件把該屬性直接賦值給內部狀況,作為內部狀況修正。
  3. 在某些操縱觸發后,該內部狀況被修正。

題目在於,因為js是援用範例言語,簡樸的賦值僅僅是通報援用,那末,以上場景中,父組件中的數據,子組件中的屬性,另有子組件中的狀況, 指向的都是一致份對象
這會形成一個題目,假如子組件修正了該對象的屬性,那末父組件的數據也會遭到影響,這破壞了單向數據流,會形成許多詭異的bug。

處理要領也很顯然:

  1. 計劃一是父組件通報數組或對象給子組件時,運用深拷貝拷貝一份過去,或許子組件將屬性賦值給內部狀況時,深拷貝一份過去,如許就能夠互不滋擾。
  2. 計劃二是運用不可變數據結構,每次修正都是發生新的拷貝,因而也能處理題目。

組件的事宜

自定義事宜

<div id="x-child">
  <button @click="onClick">click</button>
</div>

<script>
Vue.component('x-child', {
  template: '#x-child',
  data: function() {
      return {
          counter: 0
      };
  },
  methods: {
      onClick: function() {
          this.counter++;
          this.$emit("on-counter-add", this.counter);
      }
  }
});
</script>

以上自定義了x-child組件,而且自定義了組件事宜。我們能夠看到:

  1. 運用vue組件實例的$emit要領,用以觸發一個自定義事宜。觸發事宜能夠照顧數據,這些數據被用於通報給綁定了事宜的別的組件的回調函數上,進而被通報給別的組件。
  2. 不像屬性,自定義事宜沒有一個一致聲明的處所,至於為何我也不清晰。。。得問vue作者去。
  3. 該自定義組件內部包含了一個按鈕,按鈕被點擊事宜觸發了自定義組件的回調函數,進而觸發了該自定義組件的自定義事宜。
    從一個角度來看,該自定義組件像是轉發了原生組件的事宜罷了。然則從別的一個角度來看,該自定義組件封裝了這些細節,對外展示的是一個點下按鈕觸發計數器增添的事宜的如許一個計數器。
  4. 事宜稱號的定義用的短橫線支解的寫法,緣由和屬性相似。
<div id="x-parent">
    <x-child @on-counter-add="onCounterAdd"></x-child>
    <span> { { counter }} </span>
</div>
<script>
    Vue.component('x-parent', {
        template: '#x-parent',
        data: function () {
            return {
                counter: 0
            };
        },
        methods: {
            onCounterAdd: function (counter) {
                this.counter = counter;
            }
        }
    });
</script>

以上定義了x-parent組件,而且援用了上面定義的子組件。能夠看出:

  1. 子組件事宜觸發了父組件的回調函數,而且將數據從回調函數中傳入。父組件能夠在回調函數里做任何事情,很有靈活性。
  2. 平常狀況下,父組件會在回調函數中更新本身的狀況數據。數據更新后觸發新的視圖襯着,用戶即可在界面上看到了反應。如許,經由過程事宜,子組件的數據通報到了父組件中。

事宜綁定的表達式寫法

在監聽事宜的處所,上面的寫法是運用了一個回調函數,不過,也能夠運用js表達式,比方:

<x-child @on-counter-add="counter = arguments[0]"></x-child>

上面代碼的重點在於arguments[0],假如是js表達式寫法,運用arguments援用事宜的參數,就好像這段js表達式被放入了一個vue供應的匿名函數,然後運用匿名函數監聽這個事宜一樣。
那它有什麼用呢?在上面的場景里如許寫固然是不好的,因為減弱了可讀性。

之前在我同事遇到的一個場景里,是一個涉及到插槽分發作用域的場景,假如寫成回調函數的情勢,那末在回調函數中沒法訪問插槽作用域的變量。
因而,必需運用js表達式的寫法,將插槽作用域中的變量顯式的帶到回調函數中,代碼相似這類,懶得組織詳細的例子了

<x-child @on-counter-add="onCountAdd(arguments[0], scope.id)"></x-child>

雙向綁定

因為vue設想的父子組件通訊是單向數據流,然則因為一些需求的須要,假如能供應雙向數據流,會使運用起來更輕易。
便利性和設想的一致性爭執,怎麼辦?固然是用語法糖處理了。

現實上,vue供應的兩種好像是雙向數據流的機制,.syncv-model ,都是語法糖。

.sync修飾符

<comp :foo.sync="bar"></comp>

這類寫法只是下面的語法糖:

<comp :foo="bar" @update:foo="val => bar = val"></comp>

子組件內,假如修正了foo時,須要觸發update:foo事宜。

v-model

v-model常用於相似表單如許的自定義控件:

<my-checkbox v-model="foo"></my-checkbox>

它也是以下語法的語法糖:

<my-checkbox
  :value="foo"
  @input="val => foo = val" >
</my-checkbox>

插槽

細緻思索適才的自定義組件的定義,不難發明,上面的自定義組件只能對DOM中的一棵子樹做籠統和封裝。

那末,斟酌如許一種狀況,我們封裝了一個card組件,card的內容能夠運用恣意的vue組件添補。
這類場景,就須要在自定義組件時,能夠在組件的DOM樹里 挖個洞 ,這個洞能夠讓該組件的調用者添補。
vue供應的這類相似的機制,被稱為插槽。

定義插槽

<div id="x-my-card">
    <h2>我是子組件的題目
        <slot name="title"></slot>
    </h2>
    <slot>
    </slot>
</div>

<script>
Vue.component('x-my-card', {
  template: '#x-my-card'
});
</script>
<div id="x-component">
    <x-my-card>
        <p>這是一些初始內容</p>
        <p>這是更多的初始內容</p>
    </x-my-card>
    <x-my-card>
          <h2 slot="title">題目</h2>
          <p>這是一些初始內容</p>
          <p>這是更多的初始內容</p>
    </x-my-card>
</div>

<script>
Vue.component('x-component', {
  template: '#x-component'
});
</script>

從上面的示例中能夠看到:

  1. 在自定義組件時,運用slot標籤給自定義組件留了一個“洞”。
  2. 在援用該自定義組件時,自定義組件標籤內部的子元素會填補上這個洞,被襯着出來。
  3. 默許的插槽只能有一個。能夠運用slot標籤的name屬性定義插槽稱號以辨別差別的插槽,如許能夠在自定義組件上挖多個”洞”。

數據通報

vue供應的插槽機制,在給自定義組件挖”洞”的同時,還能使自定義組件給洞里添補的組件通報數據。以下:

<div id="x-my-card">
  <slot text="hello from child"></slot>
</div>
<div id="x-component">
    <x-my-card>
        <template slot-scope="scope">
            <span>{{ scope.text }}</span>
        </template>
    </x-my-card>
</div>

從上面能夠看出:

  1. 在定義slot時,能夠經由過程屬性將數據通報給它。在援用自定義組件的處所,將插槽內容放入template標籤內,經由過程slot-scope指定變量名,即可在template標籤內援用該變量從而運用插槽通報過來的數據。
  2. 在現實運用中,一個典範的例子是,表格組件供應插槽自定義表格行的款式和規劃,同時經由過程插槽將該表格行的數據通報給插槽內容。

末了

本篇博文梳理了vue的自定義組件機制,經由過程自定義組件,就能夠在vue項目中很好的將項目組件化。
一方面,能夠提取配合的組件舉行復用,下降代碼冗餘;別的一方面,也能夠供應一種壯大的籠統機制,進步vue的表達能力。

注:該文於2018-04-10撰寫於我的github靜態頁博客,現同步到我的segmentfault來。

    原文作者:frapples
    原文地址: https://segmentfault.com/a/1190000015453897
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞