mpvue開發小程式教程(四)

  • 2019 年 10 月 7 日
  • 筆記

在上一章節中,我們將 vue-cli命令行工具生成的程式碼骨架中的src目錄清理了一遍,然後從頭開始配置和編寫了一個可以運行的小程式頁面,算是正真走上了使用mpvue開發小程式的第一步。今天我們將進一步來了解和學習 mpvue/Vue的其他重要功能。

既然 mpvue是基於Vue的,那麼就沒有理由不進一步學習一下Vue最核心的東西:組件。組件系統是Vue應用開發中最具價值的特性之一,在前文中其實我們就已經有在使用組件了,比如 App.vue和首頁 index.vue就是兩個Vue組件。

組件是一種抽象,允許我們使用小型、獨立和通常可復用的組件構建大型應用。仔細想想,幾乎任意類型的應用介面都可以抽象為一個組件樹,若干的小組件可以聚合成一個完整的介面:

一個好的組件系統一定會有這些特點:封裝性、復用性、擴展性。對於Vue的組件來說,這幾點都算是實現的比較的優秀的。

組件的封裝性

Vue組件的寫法可以避免將屬於一個獨立邏輯單位的程式碼散落在各處,可以將介面(DOM)、樣式(CSS)、行為(JS)三部分的程式碼很好的組織在一起(推薦的實踐是使用 .vue文件)。在設計編寫一個組件時,我們要記住的原則就是:

避免向外部暴露過多的東西,只暴露必要的外部交互介面(組件屬性、事件、方法等)。

下面我們來在原先的程式碼基礎上,創建一個簡單的按鈕點擊計數器組件,它將實現的功能是:點擊按鈕並展示已點擊按鈕次數、點擊清零按鈕實現點擊次數的歸零。在 src/components目錄下,新建一個 click-counter.vue組件文件,並編寫如下程式碼:

<template>  <div class="click-counter">    <div class="counter-num">次數:{{num}}</div>    <button class="counter-btn" @click="handleClick">點我呀!</button>    <button class="counter-reset-btn" @click="handleResetClick">清零</button>  </div></template>  <script>export default {  data() {    return {      num: 0    };  },    methods: {    handleClick() {      this.num += 1;    },    handleResetClick() {      this.num = 0;    }  }};</script>  <style>.click-counter {  display: flex;  align-items: center;  justify-content: center;  border: 1px solid red;  background-color: #ffffff;  padding: 10px;}  .counter-num,.counter-btn,.counter-reset-btn {  flex: 1;  margin: 3px;}</style>

編寫完這個組件後,我們來嘗試在首頁組件 src/pages/index/index.vue文件中使用它:

<template>  <div class="container" @click="clickHandle">    <div class="message">{{msg}}</div>    <!-- 使用 click-counter 組件 -->    <click-counter />  </div></template>  <script>// 導入 click-counter 組件import ClickCounter from "@/components/click-counter";  export default {  // 聲明在當前組件下使用 counter-click 組件  components: { ClickCounter },    data() {    return {      msg: "Hello"    };  },    methods: {    clickHandle() {      this.msg = "Clicked!!!!!!";    }  }};</script>  <style scoped>.message {  color: red;  padding: 10px;  text-align: center;}</style>

完成上面兩個步驟後,記得重新運行一下命令行 npm run dev(注意點:新增文件必須重新運行該命令,編譯器不會自動檢測新加入的文件)。成功後通過微信開發者工具的模擬器查看,結果介面將會是這樣的:

點擊「點我呀!」按鈕,計數器就會累加點擊次數並更新介面上的數字;而點擊「清零」按鈕,則會將統計數字歸零。

回到程式碼上來看,對於 click-counter.vue的使用者 index.vue來說,它並不關心太多 click-counter.vue的實現細節,引入該組件文件並進行聲明,就可以通過標籤的形式來使用它了,非常簡單明了。而且,這樣一個 click-counter.vue組件也可以被拿到其他的Vue/mpvue程式碼中使用,其他使用者也並不需要關注它的實現細節,而只需要關心它能實現什麼功能就行了。這就是組件封裝帶來的好處。

不過,目前的這個click-counter組件還沒有跟它的父組件之間有什麼交互或通訊,沒有體現出「暴露介面」的特性,那讓我們來增加點程式碼,了解下這一特性。首先解釋一下我們要實現的功能:組件可以接收一個外部設置的初始點擊次數值,在點擊「點我呀!」按鈕的時候,從這個初始值開始進行累加;並且點擊按鈕後,可以通知組件的使用者(即父組件)當前的點擊統計值。

修改 click-counter.vue的程式碼:

<template>  <div class="click-counter">    <div class="counter-num">次數:{{num}}</div>    <button class="counter-btn" @click="handleClick">點我呀!</button>    <button class="counter-reset-btn" @click="handleResetClick">清零</button>  </div></template>  <script>export default {  // 增加一個可從外部傳入的屬性initNum  props: {    initNum: {      type: Number,      default: 0    }  },    data() {    return {      num: this.initNum //使用傳入的initNum值作為初始的點擊數    };  },    methods: {    handleClick() {      this.num += 1;      this.notifyNum();    },    handleResetClick() {      this.num = 0;      this.notifyNum();    },    notifyNum() {      //觸發自定義事件 clicknum      this.$emit("clicknum", {        num: this.num      });    }  }};</script>  <style scoped>.click-counter {  display: flex;  align-items: center;  justify-content: center;  border: 1px solid red;  background-color: #ffffff;  padding: 10px;}  .counter-num,.counter-btn,.counter-reset-btn {  flex: 1;  margin: 3px;}</style>

修改 index.vue的程式碼:

<template>  <div class="container" @click="clickHandle">    <div class="message">{{msg}}</div>    <!-- 使用 click-counter 組件 -->    <click-counter :init-num="10" @clicknum="handleClickNum" />  </div></template>  <script>// 導入 click-counter 組件import ClickCounter from "@/components/click-counter";  export default {  // 聲明在當前組件下使用 counter-click 組件  components: { ClickCounter },    data() {    return {      msg: "Hello"    };  },    methods: {    clickHandle() {      this.msg = "Clicked!!!!!!";    },    handleClickNum(data) {      console.log(">>>>>>", data.num);    }  }};</script>  <style scoped>.message {  color: red;  padding: 10px;  text-align: center;}</style>

觀察以上修改後的程式碼可以發現,在 click-couter.vue中的主要變化是:

  1. 使用 props定義了一個名為 initNum的數字型組件屬性(且初始值為0)。它可用於接收使用組件外部傳入的值。然後,這個 initNum值被賦值到 data中的屬性 num上作為它的初始值。
  2. 在兩個按鈕的click事件處理方法中,額外調用了一個 notifyNum()方法,它向組件觸發了一個自定義事件 clicknum並攜帶了當前點擊次數值。

而在 index.vue中的主要變化是實例化 click-counter組件的這行程式碼:

<click-counter :init-num="10" @clicknum="handleClickNum" />

實例化組件的時候,為組件傳入了 initNum屬性值 10;並且添加了一個對自定義事件 clicknum的監聽方法。

這樣一個結構實現了數據進入組件/數據傳出組件的機制,父子組件之間就能實現數據通訊。通過有限的通訊點進行數據互換,而不是直接進行函數調用,可以使得程式碼結構更優雅、更易維護。

組件的復用性

組件的復用性就好理解的多了,創建組件的目的,大多數時候就是希望這個組件可以被多個地方、多次使用,避免編寫重複的程式碼。比如我們前面的計數器組件,有可能一個項目中的多個頁面會用到,也可能一個頁面就會使用多次。

Vue組件的復用也是很容易的,比如我們要在前面例子中的 index.vue中復用計數器組件,創建3個計數器,那麼直接在模板部分編寫3個標籤就行了:

<template>  <div class="container" @click="clickHandle">    <div class="message">{{msg}}</div>    <!-- 創建 3個 click-counter 組件 -->    <click-counter :init-num="10" @clicknum="handleClickNum" />    <click-counter :init-num="20" @clicknum="handleClickNum" />    <click-counter :init-num="30" @clicknum="handleClickNum" />  </div></template>

運行後的效果如下圖所示,這三個計數器都能獨立統計各自的點擊數量:

組件的擴展性

談到擴展性,有面向對象編程經驗的開發者就會想到「繼承(extends)」。繼承是一種比較有效的擴展機制,不過隨著繼承的層次變深,程式碼也會變得難以理解。在Vue組件中,沒有採用繼承的機制,而是推薦使用「組合」的方式。

在組合理念下,我們盡量將想復用性高的組件設計到最小可拆分單位,比如按鈕、輸入框、單選框等等,然後再將這些低層組件放入更高層組件中,一層一層,慢慢拼裝出滿足需求的業務介面。

除了組合,Vue組件還提供了插槽(Slot)功能,相當於在一個組件中挖出了一個或多個坑,在具體使用這些具有插槽的組件時,可以選擇往坑裡面填什麼內容(其他組件)。

舉個例子,在計數器組件中,我們在清零按鈕後面用 <slot></slot>挖了一個坑:

<template>  <div class="click-counter">    <div class="counter-num">次數:{{num}}</div>    <button class="counter-btn" @click="handleClick">點我呀!</button>    <button class="counter-reset-btn" @click="handleResetClick">清零</button>    <slot></slot>  </div></template>

而後,在 index.vue中使用計數器組件時,在 <click-counter>標籤體中放入了額外的內容,會被傳入該組件中去用於填坑:

<template>  <div class="container" @click="clickHandle">    <div class="message">{{msg}}</div>    <!-- 使用 click-counter 組件 -->    <click-counter :init-num="10" @clicknum="handleClickNum">      <!-- 填坑用... -->      <input type="checkbox" /> 禁用    </click-counter>  </div></template>

從運行結果可以看到,清零按鈕後面已經多出了我們傳入的複選框和文字內容:

插槽其實可以理解為是另一種形式的組件屬性:普通組件屬性傳入的是比較簡單類型的數據;而插槽傳入的可以是更複雜的介面組件而已。

小結

本文我們初步學習了一下Vue組件的相關理念和特性,希望大家花點時間去熟悉和掌握這些比較核心的知識點,相信不管在之後使用Vue進行Web應用開發,還是mpvue小程式開發,都會更加得心應手、事半功倍的!