Vue的組件是可復用的 Vue 實例,且帶有一個名字 。我們可以在一個通過 new Vue 創建的 Vue 根實例中,把這個組件作為自定義元素來使用。因為組件是可復用的 Vue 實例,所以它們與 new Vue 接收相同的選項,例如 data、computed、watch、methods 以及生命周... ...
Vue的組件是可復用的 Vue 實例,且帶有一個名字 。我們可以在一個通過 new Vue
創建的 Vue 根實例中,把這個組件作為自定義元素來使用。因為組件是可復用的 Vue 實例,所以它們與 new Vue
接收相同的選項,例如 data
、computed
、watch
、methods
以及生命周期鉤子等。僅有的例外是像 el
這樣根實例特有的選項。
一 創建組件
Vue提供了三種不同的方式來定義組件,分別是:全局組件,私有組件,單文件組件。接下來就讓我一一道來。
1,全局組件
註冊全局組件非常簡單,也是很常用的一種方式。
1 Vue.component('myCom',{
2 template:'<div><p>我是一個全局<span>組件</span></p></div>'
3 });
Vue.component()方法需要兩個參數:
第一個,組件名稱;
第二個,實例(初始化)對象,可以包含所有使用new方式創建Vue實例時提供的所有屬性,除了el。
註意:組件的實例對象必須提供一個template屬性,用作該組件的HTML代碼模板,且在該模板中有且只能有一個根元素。全局組件的註冊必須在創建Vue實例之前。
小技巧:由於在編寫JS時,一般沒有HTML代碼提示,創建組件模板代碼會很不方便,所有可以在HTML文件中使用<template>元素創建模板,然後在組件的template屬性中使用id選擇器引用該模板。
註意:<template>元素必須在new Vue實例接管的根元素外部。
1 <template id="tem">
2 <div>
3 <p>我是組件內的p</p>
4 <span>我是組件中的span</span>
5 </div>
6 </template>
7 <!-- 在HTML中 -->
1 Vue.component('myCom',{ 2 template:'#tem' 3 });
4 //在組件中
2,私有組件
全局創建的組件在所有Vue實例中均可以使用,有時候這並不符合我們的需求。你可以通過以下方式定義Vue實例的私有組件,這些組件只能在該Vue實例根元素內部使用。
1 var vm = new Vue({
2 el:'#app',
3 components:{
4 mycom:{
5 template:'#tem'
6 }
7 }
8 });
通過Vue實例的components屬性可以定義私有組件,該屬性綁定一個對象,對象的屬性名是組件名,屬性值是組件實例對象。
3,單文件組件
Vue的單文件組件是一個以.vue為尾碼名的文件。由於HTML和JavaScrip不能識別.vue文件,所以不能直接使用這種方式的組件,必須配合webpack或vue-cli工具才能正確解析.vue文件。這裡的重點是Vue單文件組件,所以有興趣的同學請移步webpack中文網。
1 <tempalte>
2 //HTML模板
3 </template>
4 <script>
5 //JS代碼
6 </script>
7 <style>
8 //CSS代碼
9 </style>
.vue文件的名稱就是組件的名稱,其結構非常簡單、清晰:
<template>標簽是組件的HTML模板;
<script>標簽是邏輯代碼;
<style>標簽中是樣式代碼。
二 組件的使用
不管以哪種方式創建Vue組件,我們最終的目的是在HTML頁面中展示出來。本節將詳細介紹Vue組件使用方式。
1,組件標簽
要把我們創建的Vue組件添加到頁面中去,只需要把組件名當做標簽來使用即可。
1 Vue.component('myCom',{
2 template:"#tem"
3 });
4 var vm = new Vue({
5 el:"#app"
6 });
7 //JS部分
1 <div id="app">
2 <my-com></my-com>
3 </div>
4 <!-- HTML部分 -->
小技巧:註冊組件時,建議使用全小寫形式命名,因為HTML標簽要求使用小寫字母。如果你一定要遵守小駝峰命名規則,那麼你應該在使用組件時用“-”短橫線把單詞分隔開。
2,組件復用
Vue的組件可以重覆使用。
1 <div id="app">
2 <my-com></my-com>
3 <my-com></my-com>
4 <my-com></my-com>
5 </div>
當然,全局組件可以在任何地方使用,而私有組件只能在實例接管元素內部使用。
組件不僅可以簡單的重覆使用,還可以嵌套。
1 var vm = new Vue({
2 el:'#app',
3 compontents:{
4 mycom1:{
5 template:'<div>組件一 <mycom2></mycom2></div>'
6 },
7 mycom2:{
8 template:'<div>組件二</div>'
9 }
10 }
11 });
3,另一種使用方式
1 var mycom = {
2 tempalte:'<div id="app2">hello</div>'
3 };
4 var vm = new Vue({
5 el:'#app',
6 render:function(createEl){
7 return createEl(mycom);
8 }
9 });
使用render方式渲染組件:給Vue實例添加render屬性,該屬性值是一個接收一個方法作為參數的函數。參數用於創建Vue組件,return該組件將替代Vue實例接管的#app元素。最終的表現是:頁面上將不再出現#app的div,取而代之的是#app2的div。
這種方式一般配合單文件組件使用,如果要渲染多個組件,只需要創建多個Vue實例即可。
三 數據傳遞(通信)
1,props傳遞數據(父組件 --> 子組件)
通過在子組件綁定props屬性,實現父組件向子組件傳遞數據。props屬性值是一個數組,數組元素被定義用來接收父組件傳遞來的數據,然後通過v-bind指令指定數組元素接收哪些數據。子組件通過訪問props數組元素就可以訪問到父組件傳遞過來的數據了,就如同訪問data裡面的值。
1 <div id="app">
2 <mycom :fromFatherMsg="toSonMsg"></mycom>
3 </div>
4 <!-- HTML部分 -->
1 Vue.component({
2 template:"<div>{{fromFatherMsg}}</div>",
3 props:["fromFatherMsg"]
4 });
5 var vm = new Vue({
6 el:'#app',
7 data:{
8 toSonMsg:'這是給子組件的數據'
9 }
10 });
11 //JS部分
通過上面的例子,我們可以將這個過程簡單的分為三步:
第一步,在子組件上添加一個數組屬性props,在數組中定義用來接收數據的變數(以字元串形式存儲);
第二步,使用子組件時通過v-bind指令,綁定預先定義的接收變數和父組件將要傳遞過來的值;
第三步,在子組件中,如同訪問data中的數據一樣,訪問props數組元素接收到的數據。
2,$emit傳遞方法(父組件 --> 子組件)
父組件向子組件傳遞方法,是通過自定義事件來實現的。子組件通過實例的$emit()方法調用,這裡的事件名將成為$emit()方法的參數。
1 <div id="app">
2 <mycom @fromFatherFun="toSonFun"></mycom>
3 </div>
4 <!-- HTML部分 -->
1 Vue.component({
2 template:"<div><button @click="myFun">點擊執行來自父組件的方法</button></div>",
3 methods:{
4 myFun:function(){
5 this.$emit('fromFatherFun');
6 }
7 }
8 });
9 var vm = new Vue({
10 el:'#app',
11 methods:{
12 toSonFun(){
13 console.log( "這是給子組件的方法");
14 },
15 });
16 //JS部分
註意:和傳遞數據一樣,子組件不能直接使用父組件的方法。子組件需要通過實例的$emit()方法間接執行來自父組件的方法。
這一過程也可以分為三步:
第一步,使用子組件時,通過v-on指令自定義一個事件,事件名用來接收父組件傳遞來的方法;
第二步,綁定自定義事件和父組件需要傳遞的方法;
第三步,通過子組件的$emit()方法(通過實參指定需要觸發的自定義事件)觸發父組件的方法執行;
3,子組件拋出值(子組件 --> 父組件)
子組件在通過$emit()觸發父組件的方法時,可以同時利用$emit()的第二個參數,來向父組件的方法(傳遞給子組件的那個方法)拋出一個值。父組件的方法需要定義一個形參來接收這個子組件拋來的值。
1 //接上面的例子
2 Vue.component({
3 template:"<div><button @click="myFun">點擊執行來自父組件的方法</button></div>",
4 data(){
5 return {name:'ren'};
6 },
7 methods:{
8 myFun:function(){
9 this.$emit('fromFatherFun',this.name);
10 }
11 }
12 });
13 var vm = new Vue({
14 el:'#app',
15 data:{
16 nameFromSon:null
17 }
18 methods:{
19 toSonFun(data){
20 this.nameFromSon = data;
21 },
22 });
子組件拋出一個值的原理和父組件給子組件傳遞方法原理是一樣的,只不過是不同的用法而已。雖然有點繞,但有用哦。
4,獲取子組件的引用
在使用子組件時,通過綁定ref屬性,父組件可以通過Vue實例的$refs屬性拿到子組件的引用,然後就可以直接訪問子組件的屬性或方法了。
1 <div id="app">
2 <mycom ref="sonCom"></mycom>
3 <button @click="printMsgFromSon">點擊列印子組件的信息</button>
4 </div>
5 <!-- HTML部分 -->
1 Vue.component('mycom',{
2 data:function(){return {name:'ren'}}
3 });
4
5 var vm = new Vue({
6 el:'#app',
7 methods:{
8 printMsgFromSon:function(){
9 console.log(this.$refs.sonCom.name);
10 }
11 }
12 });
13 //JS部分
小技巧:ref屬性不僅可以用在組件上,也可以用在其他標準HTML標簽上,這樣Vue實例就可以獲取到原生的DOM對象了。
註意:即使子組件是Vue實例的私有組件,實例也不能直接使用組件的相關數據,還是需要通過$refs等屬性來間接訪問。
四 其他事項
1,單獨的data
經過上面的學習,你可能已經發現了一個問題:組件中的data屬性是一個函數返回的對象。
1 Vue.component("mycom",{
2 template:"",
3 data(){
4 return { //some code };
5 }
6 });
7 //這是ES6的寫法,等同於data:function(){return {some code};}
由於data屬性綁定的是一個對象,而對象是一個引用類型,為了保證為每個組件維護一份獨立的數據,組件的data屬性必須是一個函數。
2,插槽<slot>
當你讀到這裡時,你可能會有一個疑問:既然我們可以用標簽形式使用Vue組件,那麼是否可以在開始標簽和結束標簽之間填些內容呢?如果可以的話,該如何做呢?Vue的答案是肯定的。
首先請看下麵的例子:
1 <div id="app">
2 <com>我是插槽內容</com>
3 </div>
4 <!-- HTML部分 -->
1 Vue.compenent('com',{
2 template:'<div><p>我是組件</p><slot>我是預設值<slot></div>'
3 });
4 var vm = new Vue({
5 el:'#app'
6 });
7 //JS部分
"我是插槽內容"將替換com組件中<slot>元素。
註意:如果在使用子組件時沒有提供插槽值,那麼<slot>元素中的預設值將會生效,前提是你已經定義了這些值。
上面的例子中,組件最終渲染的HTML結構如下:
1 <div>
2 <p>我是組件</p>
3 我是插槽內容
4 </div>
註意:插槽的內容不僅可以是文本內容,還可以是HTML代碼,甚至另一個組件。
如果你需要在一個組件中定義多個插槽,那麼你應該需要用到<slot>元素的name屬性,來指定每個插槽應該擁有怎麼樣的模板。
1 <div>
2 <com>
3 <template v-slot:"header">
4 <!-- 單獨的HTML模板 -->
5 </template>
6 <div><p>我是預設的模板</p></div>
7 <template v-slot:"footer">
8 <!-- 單獨的HTML模板 -->
9 </template>
10 </com>
11 </div>
12 <!-- HTML部分 -->
1 Vue.component('com',{
2 tempalte:'<div><slot name="header"></slot><slot></slot><slot name="footer"></slot></div>'
3 });
4 var vm = new Vue({
5 el:'#app'
6 });
具名的插槽需要在使用組件時,用<template>元素單獨定義模板,並通過v-slot指令以參數的形式指定:“我是xxx插槽的模板”。
其他所有沒有包裹在<template>元素內的模板,將自動歸為匿名的<slot>元素下麵。
3,特殊的嵌套元素
有些 HTML 元素,諸如 <ul>
、<ol>
、<table>
和 <select>
,對於哪些元素可以出現在其內部是有嚴格限制的。而有些元素,諸如 <li>
、<tr>
和 <option>
,只能出現在其它某些特定的元素內部。要怎樣才能在這些元素中正確的渲染組件呢?幸好,Vue提供了is特性:
1 <table>
2 <tr is="mycom"></tr>
3 </table>
註意:如果你使用字元串定義組件模板(例如:template: '...'
)、或者單文件組件(.vue)、或者<script>標簽(<script type="text/x-template">),那麼你完全可以忽略掉這個限制。