Vue組件基礎

      網友投稿 915 2025-03-31

      組件是Vue.js最推崇的,也是最強大的功能之一,核心目標是為了代碼開發的重用性。我們可以把組件代碼按照 template、style、script 的拆分方式,放置到對應的.vue文件中。本章節我們就來學習有關Vue組件的基礎知識。


      5.1 組件簡介

      組件的本質就是為了拆分Vue實例的代碼量。我們也可以把所有的組件代碼寫到Vue實例中,那樣的話,實例代碼則顯的臃腫不堪,冗長無比,這并不是我們想要的,所以,我們根據組件的功能性,以不同的的功能來劃分不同的組件,將來我們需要什么樣的功能,就可以取調用對應的組件即可,從而達到組件的可重用性和靈活性。

      5.1.1 組件的定義

      組件(Component)是Vue.js最強大的功能之一,組件可以擴展HTML元素,封裝可重用代碼,在較高層面上,組件是自定義元素,Vue.js的編譯器為他添加特殊功能。有些情況下,組件也可以表現用 is 特性進行了擴展的原生的HTML元素,所有的Vue組件同時也都是Vue實例,所以可以接受相同的選項對象(除了一些根級特有的選項),并提供相同的生命周期鉤子。

      vue組件的功能:

      ? 能夠把頁面抽象成多個相對獨立的模塊;

      ? 實現代碼重用,提高開發效率和代碼質量,使得代碼易于維護。

      5.1.2 組件的類型

      ? 組件按照注冊方式不同,分為全局組件和局部組件。

      什么情況下需要將組件注冊為全局組件?一般是一些基礎組件,頻繁(3 次以上)需要用到的,需要全局注冊。例如常用的 dialog(對話框)組件,search(搜索框) 組件,toast(彈出框) 組件,message(消息框)組件等。

      一般情況下的組件應該是局部組件,這樣會極大的減少構建應用后的代碼體積,但是對于頻繁使用的組件就顯得麻煩了,所以建議,組件使用頻率低,組件比較大的時候注冊為局部組件。比如 table 組件,chart 組件等。

      ? 組件按照有無自己的狀態,可以分為函數式(無狀態)組件和普通(有狀態)組件。

      什么情況下需要將組件寫為函數式組件?一般是無狀態 (沒有響應式數據)的組件可以注冊成函數式組件,好像不用函數式組件也可以呀,為啥要注冊成函數式組件?當一個組件是一個函數式組件的時候,它沒有管理任何狀態,也沒有監聽任何傳遞給它的狀態,也沒有生命周期方法。實際上,它只是一個接受一些 prop 的函數,所以渲染開銷也低很多。

      ? 組件按照是否動態分類:可以分為動態組件和普通(非動態)組件。

      什么情況下需要將組件寫為動態組件?一般是組件之間需要切換的情況下。但是不用動態組件也可以,那為啥要動態組件?當你導入 Dynamic Component1(動態組件) 從 1 寫到 10 的時候,然后 template 再寫 DynamicComponent 10 次的時候,它的好處就出來了。

      ? 組件按照是否異步分類:可以分為異步組件和普通(非異步)組件。

      在大型應用中,我們可能需要將應用分割成小一些的代碼塊,并且只在需要的時候才從服務器加載一個模塊。為了簡化,Vue 允許你以一個工廠函數的方式定義你的組件,這個工廠函數會異步解析你的組件定義。Vue 只有在這個組件需要被渲染的時候才會觸發該工廠函數,且會把結果緩存起來供未來重渲染。

      什么情況下需要將組件寫為異步組件?一般是需要從服務器加載數據的組件,且需要多個地方使用的,因為它會把結果緩存起來供未來重渲染。還有就是大家使用最多的是在 Vue Router 里使用,異步組件結合 Webpack 的代碼分割功能,可以輕松實現路由組件的懶加載。

      ? 組件按照是否循環引用分類:可以分為遞歸組件和普通(非遞歸)組件。

      組件是可以在它們自己的模板中調用自身的。不過它們只能通過 name 選項來做這件事。一般是組件需要調用自身的時候,比如樹組件,側邊欄路由組件等,才會使用遞歸組件。

      表5.1 組件的分類

      分類標準

      分類1

      分類2

      注冊方式

      全局組件

      局部組件

      有無自己的狀態

      無狀態組件(函數式組件)

      有狀態組件(普通組件)

      是否動態

      動態組件

      非動態組件

      是否異步

      異步組件

      非異步組件

      是否循環使用

      遞歸組件

      普通組件

      以上是我們組件的分類,大家現在只需要了解組件的分類,后面我們會一一詳解。

      5.1.3 組件的構成要素

      Vue.js的組件可以理解為預先定義好行為的ViewModel類。一個組件就像一個Vue實例一樣,也可以預定義很多選項,但最核心的是以下幾個:

      ? 模板(template)——模板聲明了數據和最終展現給用戶的DOM之間的映射關系。

      ? 初始數據(data)——一個組件的初始數據狀態。對于可復用的組件來說,通常是私有數據。

      ? 接受的外部參數(props)——組件之間通過參數來進行數據的傳遞和共享。參數默認是單向綁(由上至下),但也可以顯式聲明為雙向綁定。

      ? 方法( methods)——對數據的改動操作一般都在組件的方法內進行。可以通過 v-on指令將用戶輸入事件和組件方法進行綁定。

      ? 生命周期鉤子函數(lifecycle hooks)——一個組件會觸發多個生命周期鉤子函數,比如created、attached、destroyed 等。在這些鉤子函數中,我們可以封裝一些自定義的邏輯。

      在組件的構成要素中,我們發現data,methods,以及生命周期函數這些都是跟我們的Vue實例相似的,其實在功能上的初衷也是一樣的,但是在使用上還是有差別的,接下來我們就來學習組件的封裝和使用。

      5.2組件封裝和使用

      5.2.1 封裝組件

      封裝一個組件有以下三種方式:

      表5.2封裝組件的方法

      方法

      方法描述

      Vue.extend()

      使用extend()方法封裝一個組件

      Template

      使用template標簽來封裝一個組件

      Script

      使用script標簽來封裝一個組件

      封裝好組件就可以直接使用組件了嗎,我們仍然需要一系列的步驟,來看Vue組件的使用步驟:

      1. 使用任意的封裝方法進行封裝組件的內容;

      2. 使用Vue.component()方法進行組件的注冊;

      3. 根據業務需要進行數據的通信;

      了解完步驟后,我們知道組件根據注冊方式的不同,分為全局和局部,接下來我們使用全局注冊來演示Vue組件的封裝的三種方式,現在先來看第一種方式。

      例5-01 Demo0501.html

      1

      2

      3

      4

      5

      6

      7 Vue組件的第一種封裝方式

      8

      9

      33

      程序的運行結果如下:

      圖 5- 01 使用Vue.extend()方法封裝的組件模板

      根據例題Demo0501,我們來總結以下注意事項:

      1、 使用Vue.extend()方法來封裝組件模板,模板內容必須使用{}括起來。根據編程常識,我們知道在{}中可以有多個選項,不止template一個選項。在5.1.3中我們學習了模板的構成要素,那么這些構成要素就可以放在{}中;

      2、 使用Vue.component("組件的名字",模板) 注冊成為組件,在給組件起名字的時候是有注意事項的。在注冊時,注冊的名字是駝峰命名法,則在使用的時候,必須把大駝峰變為小寫,中間加-,例如注冊的名字為myCom,則在根容器中引入該名字標簽的時候要使用。如果注冊時沒有使用駝峰命名,則不需要進行此轉化,例如注冊時hellocom,則在根標簽使用的時候就是

      接下來我們來看第一種封裝方式的簡化版本:

      例5-02 Demo0502.html

      1

      2

      3

      4

      5

      6

      7 Vue組件的第一種封裝方式

      8

      9

      32

      程序的運行結果如下:

      圖 5- 02 這是使用extend()封裝的簡化版本

      通過例5-02,進行如下總結:

      我們發現組件封裝的模板是有html和css組成的;同時,也發現,在雙引號中寫html元素是一件很不方便的事情,為了解決這個問題,接下來我們使用template標簽來實現第二種方式的封裝;

      例5-03 Demo0503.html,使用template標簽進行封裝;

      1

      2

      3

      4

      5

      6

      7 Vue組件的第二種封裝方式

      8

      9

      37

      38

      程序的運行結果如下:

      圖 5- 03 template標簽封裝模板

      根據例5-03我們總結以下:

      template標簽必須寫到body中,給template標簽一個id屬性,方便注冊的時候通過id引入;

      無論是使用哪種方式封裝模板,如果模板中有多個html元素,都必須使用根標簽包裹;

      接下來我們來看第三種方式封裝組件:

      例5-04 Demo0504.html,使用script標簽進行封裝;

      1

      2

      3

      4

      5

      6

      7

      8 Vue組件的第三種封裝方式

      9

      10

      31

      40

      41

      程序的運行結果如下:

      圖 5-04 這是使用script標簽封裝的組件

      通過demo0504,總結如下:

      使用script標簽封裝組件模板,最好在body標簽外部定義,這樣能很好的識別;必須指定type='text/x-template',聲明id方便注冊的時候通過id引入。

      以上就是我們封裝組件的三種使用方式,同時我們也學習了組件的使用步驟,第一,要封裝內容,第二,注冊組件,目前我們使用的是全局注冊,第三,根據業務進行邏輯處理,這個后續講解。

      5.2.2 注冊組件

      在上一節中,我們使用三種封裝方式以及全局注冊對組件的使用有了清晰的了解,接下來,詳細講解組件的注冊。

      組件的注冊有兩種,全局注冊和局部注冊。

      全局注冊使用Vue.component(組件的注冊名字,組件),使用該方法注冊的組件可以在多個Vue實例中共享,我們來看實例:

      例5-05 Demo0505.html,全局注冊組件。

      1

      2

      3

      4

      5

      6

      7

      8 全局注冊組件

      9

      10

      37

      50

      程序的運行結果如下:

      圖 5-05 這是全局注冊組件

      通過例5-05,總結如下:

      使用Vue.component()實現全局注冊,全局注冊的組件可以共享;一個頁面可以寫多個Vue實例,不同的實例控制不同的DOM元素。接下來,我們來看看局部組件:

      例5-06 Demo0506.html,局部注冊組件。

      1

      2

      3

      4

      5

      6

      7 全局注冊組件

      8

      9

      34

      48

      49

      程序的運行結果如下:

      圖 5-06 局部注冊組件

      通過demo0506結果我們發現,在兩個實例中都使用了新注冊的組件,但是只在實例1中顯示,這說明局部注冊的組件不具備共享性。同時發現,局部組件的注冊的時候,需要在某一個Vue實例中使用components屬性,那么在哪個Vue實例中注冊就只能在哪個Vue實例中共享。

      想一想:全局注冊的組件換個頁面引入還可以使用嗎?

      全局注冊的組件只在當前頁面的多個Vue實例中共享,并不能跨頁面。

      5.2.3 開發組件

      通過對組建的初步了解,上述案例都是在html頁面中定義組件,其實,真正在開發的時候,我們是把根據功能劃分不同的組件,每個組件都是以.vue結尾的文件,每個文件都是有三部分組成,分別是template,script,style部分,如下所示:

      1

      6

      7

      19

      20

      以上的寫法,我們在學習了vue與webpack結合后,就會大量使用,現在大家先做了解。

      5.3組件數據通信

      在實際開發過程中,Vue.js中的組件會與所在的環境進行通信,總結起來,組件通信有三種數據傳遞方式:

      ? props

      ? 組件通信

      ? slot

      接下來我們分別對每一種數據通信進行詳細的闡述。

      5.3.1 props

      "props”是組件數據的一個字段,期望從父組件傳下來數據。因為組件實例的作用域是孤立的,這意味著不能并且不應該在子組件的模板內直接引用父組件的數據,所以子組件需要顯式地用props選項來獲取父組件的數據。props選項可以是字面量,也可以是表達式,還可以綁定修飾符。下面我們詳細看一下它是如何使用的。

      1.字面量語法

      我們可以給子組件傳一個常量值,也就是字面量,來看看子組件如何接受:

      接下來演示普通常量值的傳遞,如例9-15所示。

      例5-07 Demo0507.html

      1

      2

      3

      4

      5

      6

      7

      8 props字面量的傳值

      9

      10

      28

      39

      40

      程序的運行結果如下:

      圖 5-07 傳遞字面常量

      例5-07中,通過在子組件上自定義了一個屬性“hello”,然后給這個屬性一個字面常量,在子組件的內部通過props屬性來接受。接受到后,可以正常使用插值表達式進行顯示。

      注意:props這個數組里寫的是自定義屬性的名字,所以是字符串。

      自定義屬性的名字,如果是kebab-case命名法,則在props接受得時候轉為駝峰命名,例如

      ,則在子組件使用props接受得時候必須寫成props:[‘myHello’]

      2.動態語法

      類似于用v-bind將HTML 特性綁定到一個表達式,我們也可以用v-bind將動態將父組件得數據綁定到自定義屬性中。每當父組件的數據變化時,該變化也會傳導給子組件,代碼示例如下:

      接下來演示動態綁定,如例5-08所示。

      例5-08 Demo0508.html

      1

      2

      3

      4

      5

      6

      7

      8 props字面量的傳值

      9

      10

      28

      42

      43

      程序的運行結果如下:

      圖 5-08 動態傳遞父組件的數據

      例5-08中,如果自定義屬性要綁定父組件data中的數據,則需要使用v-bind,v-bind簡寫為冒號。

      3.props類型

      通常,我們只看到了以字符串數組形式列出的 props,例如,props: ['title', 'likes', 'isPublished', 'commentIds', 'author']。但是,如果你希望每個 prop 都有指定的值類型。這時,你可以以對象形式列出 prop,這些 property 的名稱和值分別是 prop 各自的名稱和類型:

      1 props: {

      2 title: String,

      3 likes: Number,

      4 isPublished: Boolean,

      5 commentIds: Array,

      6 author: Object,

      7 callback: Function,

      8 contactsPromise: Promise // or any other constructor

      9 }

      type類型有如下:

      ? String

      ? Number

      ? Boolean

      ? Object

      ? Function

      ? Array

      4.props驗證

      我們可以為組件的 prop 指定驗證要求,如果有一個需求沒有被滿足,則 Vue 會在瀏覽器控制臺中警告你。為了定制 prop 的驗證方式,你可以為 props 中的值提供一個帶有驗證需求的對象,而不是一個字符串數組。例如:

      1 let vm = new Vue({

      2 el: '#app',

      3 data: {

      4 msg: "我是父組件得數據"

      5 },

      6 components: {

      7 'test': {

      8 props: {

      9 // 基礎的類型檢查 (`null` 和 `undefined` 會通過任何類型驗證)

      10 propA: Number,

      11 // 多個可能的類型

      12 propB: [String, Number],

      13 // 必填的字符串

      14 propC: {

      15 type: String, //數據類型

      16 required: true //必填項

      17 },

      18 // 帶有默認值的數字

      19 propD: {

      20 type: Number,

      21 default: 100 //默認值

      22 },

      23 // 帶有默認值的對象

      24 propE: {

      25 type: Object,

      26 // 對象或數組默認值必須從一個工廠函數獲取

      27 default: function () {

      28 return { message: 'hello' }

      29 }

      30 },

      31 // 自定義驗證函數

      32 propF: {

      33 validator: function (value) {

      34 // 這個值必須匹配下列字符串中的一個

      35 return ['success', 'warning', 'danger'].indexOf(value) !== -1

      36 }

      37 }

      38 },

      39 template: '#myCom',

      40 data() {//這個是組件自己的數據

      41 return {

      42 hi: 'hello'

      43 }

      44 },

      45 }

      46 },

      47 });

      48

      49

      注意:props的數據來自父級,data中數據是組件自己的數據。

      子組件中的data和Vue實例中的data是不一樣的,子組件的data是個函數,而且數據必須定義在return的對象中。

      5.單項數據流

      所有的 prop 都使得父子 prop 之間形成了一個單向向下的數據流:父級 prop 的更新會向下流動到子組件中,但是反過來則不行。這樣會防止從子組件意外變更父級組件的狀態,從而導致你的應用的數據流向難以理解。

      父組件===>子組件:vue允許的,會主動觸發的,也叫正向傳遞。

      子組件===>父組件:vue允許的,不會主動觸發,需要手動(被動)觸發,叫做逆向傳遞。

      另外,每次父級組件發生變更時,子組件中所有的 prop 都將會刷新為最新的值。這意味著你不應該在一個子組件內部改變 通過prop傳遞的值,如果你這樣做了,Vue 會在瀏覽器的控制臺中發出警告。

      例5-09 Demo0509.html,通過

      44

      1

      2

      3

      4

      5

      6

      7 props字面量的傳值

      8

      9

      27

      41

      42

      程序的運行結果如下:

      圖 5-09 使用驗證方式接受傳值

      如例5-09 顯示,我們使用的是驗證方式接受父組件傳過來的值。接下來我們對父組件穿過的數據及性能修改,控制臺警告如下:

      圖 5- 10 通過子組件修改父組件的數據

      5.3.2 組件通信

      上一節我們研究了父組件將值傳遞給子組件,叫做正向傳值,子組件將值傳遞給父組件,叫做逆向傳值;需要借助自定義事件。

      vue.js 中允許正向傳值,所以正向傳值不需要條件觸發,是主動的;逆向傳值,也是允許的,但是需要主動(手動)觸發,需要借助事件觸發器:

      ? $on():監聽事件

      ? $emit():把事件沿著作用域鏈向上傳遞

      使用步驟:

      第一步:在父組件中引用的子組件的標簽上,自定義事件;

      這是根組件

      顯示子組件傳過來的值:{{sonmsg}}


      第二步:在子組件的模板中,定義事件函數;

      第三步:在第二步驟的函數中,觸發事件;

      components: {

      'test': {

      template: '#myCom',

      data() {//用來存放局部組件數據

      return {

      name: ""

      }

      },

      methods: {//用來放局部組件的方法

      sonfn() {

      this.$emit("getmsg", this.name);

      }

      },

      }

      }

      接下來演示子組件向父組件傳值完整案例,如例5-10所示。

      例5-10 Demo0510.html

      Vue組件基礎

      1

      2

      3

      4

      5

      6

      7

      8 子組件向父組件傳值

      9

      10

      29

      61

      62

      程序的運行結果如下:

      圖 5- 11 子組件向父組件傳值

      通過例5-10,總結如下:

      模板對象的構成要素中,我們使用了template,data,methods這些選項;template指的是組建的模板內容,data里面放的是局部組件內部的數據,methods放的是組件內部的方法;data是個函數,存放的數據必須放在return的對象中;子組件傳值給父組件,必須在子組件中通過點擊事件對應的函數來觸發父組件中自定義的事件。

      5.3.3 slot使用

      我們在構建頁面過程中一般會把用的比較多的公共的部分抽取出來作為一個單獨的組件,但是在實際使用這個組件的時候卻又不能完全的滿足需求,我們希望在這個組件中添加一點東西,這時候就需要用到插槽來分發內容。插槽(slot)是對組件的擴展,通過slot插槽向組件內部指定位置傳遞內容,通過slot可以父子傳參。slot又可以分為三類,分別是匿名插槽,具名插槽以及作用域插槽。

      1.匿名插槽

      匿名插槽,顧名思義,就是沒有名字的插槽。

      插槽的具體使用步驟:

      第一:在子組件中定義內容,但是對于不確定的內容使用slot占位置;

      第二:注冊子組件;

      第三:在父組件中調用子組件,同時把不確定的內容補充上。

      具體使用我們來看例5-11,如下:

      例5-11 Demo0511.html

      1

      2

      3

      4

      5

      6

      7

      8 子組件向父組件傳值

      9

      10

      45

      46

      47

      程序的運行結果如下:

      圖 5- 12 匿名插槽

      例5-11中,跟普通組件定義的區別,就是在定義子組件模板的時候多了個slot標簽,而這個slot標簽的作用就是為子組件中不確定的html內容占位置。在父組件中調用子組件的時候,在子組件內部寫的span標簽就會自動插入到slot標簽所占的位置。

      2.具名插槽

      假設我們的電腦主板上的各種插槽,有插CPU的,有插顯卡的,有插內存的,有插硬盤的,所以假設有個組件是computer,我們不可能把顯卡插到內存的位置上,具名slot也就是每個slot都有名字,不能隨意替換,要對應插入,接下來我們演示具名插槽的使用。

      例5-12 Demo0512.html

      1

      2

      3

      4

      5

      6

      7

      8 子組件向父組件傳值

      9

      10

      76

      77

      78

      程序的運行結果如下:

      圖 5- 13 具名插槽

      案例5-12中,跟5-11的區別是,在子組件模板定義的時候,每個slot都有name值。在父組件引入子組件的時候,如何給對應的slot內容呢?每個slot插入的內容使用template標簽,該標簽使用v-slot:插槽名字,從而能夠把對應的內容插入到對應的插槽中。注意:?v-slot?只能添加在?