首頁
所有內容 組件 文章正文
網友投稿
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
15
這是根組件 16
17
18
19
20
21
22 //第一種使用extend()封裝Vue組件
23 //1、封裝組件的模板
24 let com1 = Vue.extend({
25 template: "
這是使用h3定義的組件 "
26 });
27 //2、使用Vue.component(組件的名字,組件的模板)方法注冊組件
28 Vue.component('hello', com1);
29 let vm = new Vue({
30 el: '#app',
31 });
32
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
15
這是根組件 16
17
18
19
20
21
22 //第一種的簡化版本,封裝一個p標簽和一個按鈕
23 //注意:如果封裝的模板中有兩個html元素,那么該封裝必須有個根標簽,一般我們使用div作根標簽
24 Vue.component("myCom", Vue.extend({
25 template: "
"
26 }));
27
28 let vm = new Vue({
29 el: '#app',
30 });
31
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
15
這是根組件 16
17
18
19
20
21
25
26
27
28
29
30 //注冊,Vue.component(組件的名字,組件的模板);
31 Vue.component("hellocom", { template: "#hellocom" });
32
33 let vm = new Vue({
34 el: '#app',
35 });
36
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
16
這是根組件 17
18
19
20
21
22
23
24
30
31
32 //全局方式注冊組件
33 Vue.component("myCom", Vue.extend({
34 template: "#myCom"
35 }));
36 let vm = new Vue({
37 el: '#app',
38 });
39
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
17
實例1的根組件 18
19
20
21
實例2 22
26
27
28
29
30
36
37
38 //全局方式注冊組件,多個實例共享
39 Vue.component("myCom", Vue.extend({
40 template: "#myCom"
41 }));
42 //下面是兩個Vue實例
43 let vm1 = new Vue({
44 el: '#app',
45 });
46 let vm2 = new Vue({
47 el: "#com"
48 });
49
50
程序的運行結果如下:
圖 5-05 這是全局注冊組件
通過例5-05,總結如下:
使用Vue.component()實現全局注冊,全局注冊的組件可以共享;一個頁面可以寫多個Vue實例,不同的實例控制不同的DOM元素。接下來,我們來看看局部組件:
例5-06 Demo0506.html,局部注冊組件。
1
2
3
4
5
6
7
全局注冊組件 8
9
16
實例1的根組件 17
18
19
20
實例2 21
25
26
27
33
34
35 //下面是兩個Vue實例
36 let vm1 = new Vue({
37 el: '#app',
38 components: {
39 'myCom': {
40 template: '#myCom'
41 }
42 },//components是component的復數形式
43 });
44 let vm2 = new Vue({
45 el: "#com"
46 });
47
48
49
程序的運行結果如下:
圖 5-06 局部注冊組件
通過demo0506結果我們發現,在兩個實例中都使用了新注冊的組件,但是只在實例1中顯示,這說明局部注冊的組件不具備共享性。同時發現,局部組件的注冊的時候,需要在某一個Vue實例中使用components屬性,那么在哪個Vue實例中注冊就只能在哪個Vue實例中共享。
想一想:全局注冊的組件換個頁面引入還可以使用嗎?
全局注冊的組件只在當前頁面的多個Vue實例中共享,并不能跨頁面。
5.2.3 開發組件
通過對組建的初步了解,上述案例都是在html頁面中定義組件,其實,真正在開發的時候,我們是把根據功能劃分不同的組件,每個組件都是以.vue結尾的文件,每個文件都是有三部分組成,分別是template,script,style部分,如下所示:
1
2
5
6
7
8 export default {
9 data() {
10 return {
11 //寫組件的數據
12 };
13 },
14 methods: {
15 //寫組件的方法
16 },
17 };
18
19
20
21 div {
22 width: 200px;
23 height: 100px;
24 background-color: pink;
25 }
26
以上的寫法,我們在學習了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
16
這是根組件 17
18
19
20
21
22
23
24
hello,Vue
25 {{hello}}
26
27
28
29 let vm = new Vue({
30 el: '#app',
31 components: {
32 'test': {
33 props: ['hello'],//使用props這個屬性來接受傳過來的值,props是個數組,里面寫的是自定義屬性的名字
34 template: '#myCom',
35 }
36 },
37 });
38
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
16
這是根組件 17
18
19
20
21
22
23
24
hello,Vue
25 {{hello}}
26
27
28
29 let vm = new Vue({
30 el: '#app',
31 data: {
32 msg: "我是父組件得數據"
33 },
34 components: {
35 'test': {
36 props: ['hello'],//使用props這個屬性來接受傳過來的值,props是個數組,里面寫的是自定義屬性的名字
37 template: '#myCom',
38 }
39 },
40 });
41
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
15
這是根組件 16
17
18
19
20
21
22
23
hello,Vue
24 {{hello}}
25
26
27
28 let vm = new Vue({
29 el: '#app',
30 data: {
31 msg: "我是父組件得數據"
32 },
33 components: {
34 'test': {
35 props: ['hello'],//使用props這個屬性來接受傳過來的值,props是個數組,里面寫的是自定義屬性的名字
36 template: '#myCom',
37 }
38 },
39 });
40
41
42
程序的運行結果如下:
圖 5-09 使用驗證方式接受傳值
如例5-09 顯示,我們使用的是驗證方式接受父組件傳過來的值。接下來我們對父組件穿過的數據及性能修改,控制臺警告如下:
圖 5- 10 通過子組件修改父組件的數據
5.3.2 組件通信
上一節我們研究了父組件將值傳遞給子組件,叫做正向傳值,子組件將值傳遞給父組件,叫做逆向傳值;需要借助自定義事件。
vue.js 中允許正向傳值,所以正向傳值不需要條件觸發,是主動的;逆向傳值,也是允許的,但是需要主動(手動)觸發,需要借助事件觸發器:
? $on():監聽事件
? $emit():把事件沿著作用域鏈向上傳遞
使用步驟:
第一步:在父組件中引用的子組件的標簽上,自定義事件;
這是根組件 顯示子組件傳過來的值:{{sonmsg}}
let vm = new Vue({
el: '#app',
data: {
msg: "我是父組件得數據",
sonmsg: "" //用來接受從子組件傳過來的值
},
//2、定義一個函數
methods: {
fn(val) {
this.sonmsg = val; //val 是子組件在觸發該函數的時候傳過來的參數,把該參數傳給父組件中的data
}
},
});