Vue躬行記(4)——組件

  組件是可複用的Vue實例,擁有屬於本身的數據、模板、腳本和樣式,可避免繁重的重複性開發。因爲組件都是獨立的,所以其內部代碼不會影響其它組件,但能夠包含其它組件,而且相互之間還能通訊。html

1、註冊

  在使用組件以前,須要先將其註冊,Vue提供了兩種註冊方式:全局註冊和局部註冊。git

1)全局註冊數組

  經過Vue.component()方法可註冊全局的組件,它能接收兩個參數,第一個是組件名稱,第二個既能夠是擴展過的構造器(即Vue.extend()的返回值),也能夠是選項對象(會自動調用Vue.extend()),以下所示。瀏覽器

Vue.component("btn-custom", Vue.extend({ }));    //擴展過的構造器
Vue.component("btn-custom", { });                //選項對象

  組件的選項對象也會包含data、methods、計算屬性和生命週期鉤子等成員,但不包含掛載目標el選項,而且data選項也再也不是一個對象而是一個函數,由於只有這樣才能讓每一個實例維護各自的數據對象,互不影響。注意,只有在組件註冊以後才能將其應用於其它Vue根實例的模板中,以下所示。緩存

<div id="container">
  <btn-custom></btn-custom>
</div>
<script>
  Vue.component("btn-custom", {
    data: function() {
      return {
        txt: "提交"
      };
    },
    template: '<button>{{txt}}</button>'
  });
  var vm = new Vue({ 
    el: "#container"
  });
</script>

  渲染出的DOM結構以下所示。dom

<div id="container">
  <button>提交</button>
</div>

  組件的命名方式除了上面的連字符分隔式以外,還有另外一種大駝峯式(例如BtnCustom)。當把組件引用至字符串模板中時,兩種命名方式都是有效的;而當把組件直接應用到DOM模板中時(以下所示),就不能用大駝峯命名,由於標籤會被自動轉換成小寫(即<btncustom>),因而就找不到這個組件的定義,進而拋出錯誤。函數

<div id="container">
  <BtnCustom></BtnCustom>
</div>

2)局部註冊性能

  Vue的局部註冊須要分兩步,首先經過建立選項對象的方式來定義組件,以下所示。ui

var BtnCustom = {
  data: function() {
    return {
      txt: "提交"
    };
  },
  template: "<button>{{txt}}</button>"
};

  而後在Vue根實例的components選項中註冊要使用的組件(以下所示),其中屬性名就是模板中要使用的自定義元素名,屬性值就是組件。this

var vm = new Vue({
  el: "#container",
  components: {
    "btn-custom": BtnCustom
  }
});

  注意,當構建一個組件時,其模板中必須包含一個根元素,以前的示例都只有一個元素。若是有多個元素,那麼就得像下面這樣用一個元素(<div>)包裹其它元素(<span>和<button>)。

var BtnCustom = {
  template: `<div>
      <span>按鈕</span>
      <button>提交</button>
    </div>`
};

2、數據傳遞

  組件的props選項能接收從外部(能夠是父組件)傳遞進來的數據,其值是一個由HTML特性組成的數組或對象,以下所示。

<btn-custom in-html="提交"></btn-custom>
<script>
  Vue.component("btn-custom", {
    props: ["inHtml"],
    template: '<button>{{inHtml}}</button>'
  });
</script>

  因爲HTML特性的名稱大小寫不敏感,所以瀏覽器會將全部大寫字母自動轉換成小寫。這意味着若是在組件內爲props選項添加駝峯式的特性(例如inHtml),那麼在DOM模板中須要聲明成等價的連字符分隔式的特性(例如in-html),不然在組件內將讀取不到該特性。有一點要注意,在字符串模板中使用特性,兩種命名方式都是有效的。

1)動態傳值

  特性值的類型除了上文的字符串以外,還能夠經過v-bind指令動態的將任意類型傳遞給組件的props選項,例如傳入一個數字,以下所示。

<btn-custom :digit="1"></btn-custom>
<script>
  Vue.component("btn-custom", {
    props: ["digit"],
    created: function() {
      typeof this.digit;         //"number"
    }
  });
</script>

  在created鉤子中調用typeof運算符計算this.digit,獲得的值爲「number」,說明數字傳遞成功。

  若是要傳遞對象的全部屬性,那麼沒必要一個一個聲明,只須要不定義v-bind的參數便可,以下所示,兩個btn-custom組件是等價的。

<div id="container">
  <btn-custom v-bind="obj"></btn-custom>
  <!-- 至關於 -->
  <btn-custom :id="obj.id" :name="obj.name"></btn-custom>
</div>
<script>
  Vue.component("btn-custom", {
    props: ["id"],
    template: '<button>{{id}}</button>'
  });
  var vm = new Vue({
    el: "#container",
    data: {
      obj: { id: 1, name: "strick" }
    }
  });
</script>

  注意,在props選項中聲明的是id或name,而不是obj。

2)數據流

  在Vue中,組件之間的數據是自頂向下單向流動的(即單向數據流),父組件經過props將數據傳遞給子組件。一旦父組件的數據有所更新,那麼子組件也會自動更新,若是在子組件中修改接收的props(例以下面的digit特性),那麼Vue會拋出錯誤警告,避免改變父組件的狀態。

Vue.component("btn-custom", {
  props: ["digit"],
  created: function() {
    this.digit = 2;
  }
});

  不少須要改變props的狀況,其實都能以另外一種更合理的方式解決,例如將其保存到組件的data屬性中或定義成一個計算屬性等。

3)校驗特性

  組件的props能以對象的形式指定值類型,其鍵是接收的特性名稱,值是類型構造函數。這樣既有助於閱讀,也能夠避免傳遞無效的值。在下面的示例中,指定了digit必須是數字,而number既能夠是數字也能夠是字符串。

Vue.component("btn-custom", {
  props: {
    digit: Number,
    number: [Number, String]
  }
});

  除了Number和String以外,內置的構造函數還有Boolean、Array、Object、Date、Function和Symbol。不只如此,還能夠自定義構造函數,經過instanceof運算符來檢查。在下面的示例中,驗證man特性是不是經過new Person()建立的。

Vue.component("btn-custom", {
  props: {
    man: People
  }
});
function People(name) {
  this.name = name;
}

  除了基礎的類型檢查以外,組件還容許自定義驗證函數、添加必填標記和附帶默認值,以下所示。

Vue.component("btn-custom", {
  props: {
    digit: {        
      type: Number,
      required: true    //必填
    },
    number: {        
      type: Number,
      default: 100      //數字默認值
    },
    people: {        
      type: Object,
      default: function() {           //對象默認值
        return { name: "strick" };
      }
    },
    name: {            
      validator: function(value) {    //驗證函數
        return value.length > 5;
      }
    }
  }
});

  在使用這些校驗規則時,有兩點須要注意:

  (1)當默認值是對象或數組時,須要從函數中獲取。

  (2)因爲props會在組件實例建立以前進行驗證,所以在default()和validator()函數中不能使用組件的屬性,例如data、computed、methods等。

4)未在props中的特性

  組件能夠聲明任意多個特性,而那些沒有在props中定義的特性不但會被保存到實例屬性$attrs中,還會被添加到根元素上。注意,class和style兩個特性未包含在$attrs屬性中,而且它們會與原特性進行合併,而不是替換。如下面的btn-custom組件爲例,根元素<button>會接收type和class兩個特性。

<btn-custom type="submit" class="size"></btn-custom>
<script>
  Vue.component("btn-custom", {
    props: ["digit"],
    created: function() {
      console.log(this.$attrs);         //{type: "submit"}
    },
    template: '<button type="button" class="warning">{{digit}}</button>'
  });
</script>

  渲染出的<button>元素以下所示,其中type的值被替換成了「submit」,而class的值變成了「warning size」。

<button type="submit" class="warning size"></button>

  若是不想讓根元素繼承特性,那麼能夠將組件的inheritAttrs選項設爲false,但要注意,inheritAttrs不會影響class和style的傳遞。仍是以btn-custom組件爲例,props和template兩個選項與以前相同。

<btn-custom type="submit" class="size"></btn-custom>
<script>
  Vue.component("btn-custom", {
    inheritAttrs: false
  });
</script>

  渲染出的<button>元素以下所示,其中type的值未被替換,而class的值仍然是「warning size」。

<button type="button" class="warning size"></button>

3、混入

  混入(mixin)是一種代碼複用技術,一個混入對象可包含任意組件選項,並能將其與普通組件混合在一塊兒。

1)選項合併策略

  當組件和混入對象包含同名選項時,這些選項將會經過2種策略進行合併。

  (1)當數據對象或值爲對象的選項(例如methods、components等)發生衝突時,同名的屬性將以組件的爲準。以下代碼所示,雖然混入對象Mixin的數據對象也包含name屬性,可是依然會被btn-custom組件中的name屬性所覆蓋,而且它的getName()也會被替換。

var Mixin = {
  data: function() {
    return { name: "strick" };
  },
  methods: {
    getName: function() {
      console.log("mixin");
    }
  }
};
Vue.component("btn-custom", {
  mixins: [Mixin],
  data: function() {
    return { name: "freedom" };
  },
  methods: {
    getName: function() {
      console.log("component");
    }
  }
});

  (2)當生命週期鉤子發生衝突時,同名的鉤子將合併成一個數組,混入對象的鉤子在前,組件的鉤子在後,以下所示,先輸出「mixin」,再輸出「component」。

var Mixin = {
  created: function() {
    console.log("mixin");
  }
};
Vue.component("btn-custom", {
  mixins: [Mixin],
  created: function() {
    console.log("component");
  }
});

2)全局混入

  經過Vue.mixin()方法可註冊全局的混入對象,以下所示。

Vue.mixin({
  created: function () {
    console.log("global");
  }
});

  全局混入會影響全部的Vue實例,包括自定義的組件或第三方組件,所以要謹慎使用。大部分狀況下它只適合自定義的選項,在官方的代碼風格指南中,爲混入中的這些選項制訂了專門的命名規範,即以「$_」和自定義的命名空間爲前綴(例如$_namespace_),從而避免與其它實例中的選項相沖突,下面是一個簡單的示例。

Vue.mixin({
  $_namespace_getAge: function () {
    return 28;
  }
});

3)自定義選項合併策略

  除了預約義的合併策略以外,Vue還容許自定義合併策略,只需在Vue.config.optionMergeStrategies中添加一個包含合併邏輯的函數便可。

  下面是一個示例,首先在混入對象和組件中都聲明瞭一個自定義的age選項;而後在Vue.config.optionMergeStrategies中添加一個同名的age()函數,而且須要在組件以前聲明合併函數;最後在created鉤子中調用實例屬性$options,讀取到的age值爲28,符合age()函數中的合併規則。

var Mixin = {
  age: 28
};
Vue.config.optionMergeStrategies.age = function(toVal, fromVal) {
  return fromVal > toVal ? toVal : fromVal;
};
Vue.component("btn-custom", {
  mixins: [Mixin],
  created: function() {
    this.$options.age;        //28
  },
  age: 30
});

4、動態組件

  Vue內置的<component>元素可渲染一個元組件爲動態組件,經過它的is特性來決定使用哪一個組件。下面用一個例子來演示<component>元素的用法,首先全局註冊兩個組件tab1和tab2;而後將它們合併成數組賦給vm實例的tabs屬性,而另外一個current屬性記錄了當前要渲染的組件,默認值爲tab1;最後將該屬性值傳遞給is特性,並在DOM模板中建立兩個按鈕,每一個按鈕都註冊了點擊事件,可更改要渲染的組件。

<div id="container">
  <button v-for="tab in tabs" @click="current = tab">{{ tab }}</button>
  <component :is="current"></component>
</div>
<script>
  Vue.component("tab1", {
    template: '<input type="text"/>'
  });
  Vue.component("tab2", {
    template: '<input type="text"/>'
  });
  var vm = new Vue({
    el: "#container",
    data: {
      current: "tab1",
      tabs: ["tab1", "tab2"]
    }
  });
</script>

1)<keep-alive>

  雖然能夠動態切換組件,可是組件的狀態沒法保持,例如在tab1組件的文本框中輸入字符,來回切換後,這些字符就消失了。若是要緩存組件的狀態,那麼能夠用Vue提供的另外一個內置的<keep-alive>元素,以下所示,用它來包裹<component>元素,就不會銷燬失活的組件,從而提高渲染性能。

<keep-alive>
  <component :is="current"></component>
</keep-alive>

  注意,<keep-alive>元素自身不會渲染成一個DOM元素,而且其可與任意元素配合,但子元素只能渲染一個。由此可知,<keep-alive>元素內可包含條件指令(以下所示),但不能包含v-for指令。

<keep-alive>
  <tab1 v-if="current == 'tab1'"></tab1>
  <tab2 v-else></tab2>
<keep-alive>

  有兩個與<keep-alive>元素相關的生命週期鉤子:activated和deactivated。以以前的tab1組件爲例,爲其添加這兩個鉤子(以下代碼所示),它被包裹在<keep-alive>元素中。當激活tab1組件時,會觸發activated鉤子;而當停用tab1組件時,會觸發deactivated鉤子。

Vue.component("tab1", {
  template: '<input type="text"/>',
  activated: function() {
    console.log("activated");
  },
  deactivated: function() {
    console.log("deactivated");
  }
});
相關文章
相關標籤/搜索