本文的Demo和源代碼已放到GitHub,若是您以爲本篇內容不錯,請點個贊,或在GitHub上加個星星!javascript
https://github.com/zwl-jasmine95/Vue_testhtml
如下全部知識都是基於vue.js 2.0版本vue
<child-component> {{ message }}</child-component>
message
應該綁定到父組件的數據,組件做用域簡單地說是:java
父組件模板的內容在父組件做用域內編譯;子組件模板的內容在子組件做用域內編譯。node
組件的模板是在其做用域內編譯的,那麼組件選項對象中的數據也應該是在組件模板中使用的。
git
1 <div id="component-demo"> 2 <!-- #component-demo是Vue實例掛載的元素,應該在掛載元素範圍內使用組件--> 3 <hello-component></hello-component> 4 </div> 5 6 <script type="text/javascript"> 7 Vue.component('hello-component',{ 8 template:'<h1>hello component!</h1>' 9 }); 10 var vm = new Vue({ 11 el:'#component-demo' 12 }); 13 14 </script>
在建立一個Vue實例時,除了將它掛載到某個HTML元素下,還要編譯組件,將組件轉換爲HTML片斷。
除此以外,Vue實例還會識別其所掛載的元素下的<hello-component>標籤,而後將<hello-component>標籤替換爲HTML片斷。github
實際上瀏覽器仍然是不理解<hello-component>標籤的,web
組件在使用前,通過編譯已經被轉換爲HTML片斷了,組件是有一個做用域的,那麼組件的做用域能夠將它理解爲組件模板包含的HTML片斷,組件模板內容以外就不是組件的做用域了。算法
例如,hello-component組件的做用域只是下面這個小片斷:數組
通俗地講,在子組件中定義的數據,只能用在子組件的模板。在父組件中定義的數據,只能用在父組件的模板。若是父組件的數據要在子組件中使用,則須要子組件定義props。
在使用組件時,每每會這樣:
<app> <app-header></app-header> <app-footer></app-footer> </app>
注意兩點:
<app>
組件不知道它會收到什麼內容。這是由使用 <app>
的父組件決定的。
<app>
組件極可能有它本身的模版。
爲了讓組件能夠組合,咱們須要一種方式來混合父組件的內容與子組件本身的模板。這個過程被稱爲 內容分發。Vue.js 實現了一個內容分發 API,參照了當前 Web 組件規範草案,使用特殊的 <slot>
元素做爲原始內容的插槽。
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>單個slot</title> 6 <script type="text/javascript" src="../lib/js/vue.js"></script> 7 </head> 8 <body> 9 <div id="demo"> 10 <h1>我是父組件</h1> 11 <my-component> 12 <p>這是初始內容1</p> 13 <p>這是初始內容2</p> 14 </my-component> 15 </div> 16 17 <template id="myComponent"> 18 <div> 19 <h1>我是子組件的標題</h1> 20 <slot>沒有分發內容的時候纔會顯示</slot> 21 </div> 22 </template> 23 24 <script type="text/javascript"> 25 26 Vue.component('my-component',{ 27 template:'#myComponent' 28 }); 29 30 var vm = new Vue({ 31 el:'#demo' 32 }); 33 </script> 34 35 </body> 36 </html>
結果:
除非子組件模板包含至少一個
<slot>
插口,不然父組件的內容將會被丟棄(其餘狀況2)。當子組件模板只有一個沒有屬性的 slot 時,父組件整個內容片斷將插入到 slot 所在的 DOM 位置,並替換掉 slot 標籤自己。最初在
<slot>
標籤中的任何內容都被視爲備用內容。備用內容在子組件的做用域內編譯,而且只有在宿主元素爲空,且沒有要插入的內容時才顯示備用內容。
(固然,這裏有兩個匿名<slot>會有警告,應該用特殊的屬性 name
來配置如何分發內容。詳見第三節 具名slot)
<slot>
元素能夠用一個特殊的屬性 name
來配置如何分發內容。多個 slot 能夠有不一樣的名字。具名 slot 將匹配內容片斷中有對應 slot
特性的元素。
仍然能夠有一個匿名 slot,它是默認 slot,做爲找不到匹配的內容片斷的備用插槽。若是沒有默認的 slot,這些找不到匹配的內容片斷將被拋棄。
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>具名slot</title> 6 <script type="text/javascript" src="../lib/js/vue.js"></script> 7 </head> 8 <body> 9 <div id="demo"> 10 <h1>我是父組件</h1> 11 <my-component> 12 <h1 slot="header">這裏多是一個頁面標題</h1> 13 <p>主要內容的一個段落。</p> 14 <p>另外一個主要段落。</p> 15 <p slot="footer">這裏有一些聯繫信息</p> 16 </my-component> 17 </div> 18 19 <template id="myComponent"> 20 <div class="container"> 21 <header> 22 <slot name="header"></slot> 23 </header> 24 <main> 25 <slot>這裏是匿名slot</slot> 26 </main> 27 <footer> 28 <slot name="footer"></slot> 29 </footer> 30 </div> 31 </template> 32 33 <script type="text/javascript"> 34 35 Vue.component('my-component',{ 36 template:'#myComponent' 37 }); 38 39 var vm = new Vue({ 40 el:'#demo' 41 }); 42 </script> 43 </body> 44 </html>
2.1.0新增
做用域插槽是一種特殊類型的插槽,用做使用一個 (可以傳遞數據到) 可重用模板替換已渲染元素。
1 <div class="parent"> 2 <child> 3 <template scope="props"> 4 <span>hello from parent</span> 5 <span>{{ props.text }}</span> 6 </template> 7 </child> 8 </div> 9 10 <template id="myComponent"> 11 <div class="child"> 12 <slot text="hello from child">沒有分發內容的時候纔會顯示</slot> 13 </div> 14 </template> 15 16 <script type="text/javascript"> 17 Vue.component('child',{ 18 template:'#myComponent' 19 }); 20 21 var vm = new Vue({ 22 el:'.parent' 23 }); 24 </script>
在子組件中,只需將數據傳遞到插槽,就像你將 props 傳遞給組件同樣:
在父級中,具備特殊屬性
scope
的<template>
元素必須存在,表示它是做用域插槽的模板。scope
的值對應一個臨時變量名,此變量接收從子組件中傳遞的 props 對象:
做用域插槽更具表明性的用例是列表組件,容許組件自定義應該如何渲染列表每一項。做用域插槽也能夠是具名的。(線上demo)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>做用域插槽-列表</title> 6 <script type="text/javascript" src="../lib/js/vue.js"></script> 7 </head> 8 <body> 9 <div class="parent"> 10 <my-list :items="items"> 11 <template slot="item" scope="props"> 12 <li class="my-fancy-item">{{ props.text }}</li> 13 </template> 14 </my-list> 15 </div> 16 17 <template id="myComponent"> 18 <ul> 19 <slot name="item" v-for="item in items" :text="item.text"></slot> 20 </ul> 21 </template> 22 23 <script type="text/javascript"> 24 Vue.component('my-list',{ 25 template:'#myComponent', 26 data:function () { 27 return { 28 items:[ 29 {id:1,text:'列表1'}, 30 {id:2,text:'列表2'}, 31 {id:3,text:'列表3'}, 32 {id:4,text:'列表4'} 33 ] 34 } 35 } 36 }); 37 38 var vm = new Vue({ 39 el:'.parent', 40 data:{ 41 items:[] 42 } 43 }); 44 </script> 45 46 </body> 47 </html>
(這裏代碼中刪除和兩處對效果並無什麼影響)
經過使用保留的 <component>
元素,動態地綁定到它的 is
特性,咱們讓多個組件可使用同一個掛載點,並動態切換:
var vm = new Vue({ el: '#example', data: { currentView: 'component1' //默認選中的組件 }, components: { component1: { /* ... */ }, component2: { /* ... */ }, component13: { /* ... */ } } })
<component v-bind:is="currentView"> <!-- 組件在 vm.currentview 變化時改變! --> </component>
也能夠直接綁定到組件對象上:
var Home = { template: '<p>Welcome home!</p>' } var vm = new Vue({ el: '#example', data: { currentView: Home } })
經過具體實例來講明:demo
1 <div class="container"> 2 <!--導航欄--> 3 <ul class="nav nav-pills"> 4 <li><a href="javascript:void(0)" @click="toggleTab(0)">{{tabText1}}</a></li> 5 <li><a href="javascript:void(0)" @click="toggleTab(1)">{{tabText2}}</a></li> 6 <li><a href="javascript:void(0)" @click="toggleTab(2)">{{tabText3}}</a></li> 7 </ul> 8 <!-- 點擊導航後要切換的內容容器 --> 9 <div class="content"> 10 <!-- 若是把切換出去的組件保留在內存中,能夠保留它的狀態或避免從新渲染。爲此能夠添加一個 keep-alive 指令參數 --> 11 <keep-alive><component :is="currentView"></component></keep-alive> 12 </div> 13 </div> 14 15 <!-- 點擊導航後要切換的內容 --> 16 <template id="tab-content1"> 17 <div>這是第一個選項卡的內容!</div> 18 </template> 19 20 <template id="tab-content2"> 21 <div>這是第二個選項卡的內容!</div> 22 </template> 23 24 <template id="tab-content3"> 25 <div>這是第三個選項卡的內容!</div> 26 </template> 27 28 <script type="text/javascript"> 29 //局部註冊組件(選項卡內容) 30 var tab1 = { 31 template:'#tab-content1' 32 }; 33 var tab2 = { 34 template:'#tab-content2' 35 }; 36 var tab3 = { 37 template:'#tab-content3' 38 }; 39 40 var vm = new Vue({ 41 el:'.container', 42 data:{ 43 tabText1:'選項卡1', 44 tabText2:'選項卡2', 45 tabText3:'選項卡3', 46 currentView:tab1 47 }, 48 //註冊局部組件 49 components:{ 50 tabComponent1:tab1, 51 tabComponent2:tab2, 52 tabComponent3:tab3 53 }, 54 methods:{ 55 toggleTab:function (i) { 56 var arr = ['tabComponent1','tabComponent2','tabComponent3']; 57 this.currentView = arr[i]; 58 } 59 } 60 61 }) 62 </script>
若是把切換出去的組件保留在內存中,能夠保留它的狀態或避免從新渲染。爲此能夠添加一個
keep-alive
指令參數:<keep-alive> <component :is="currentView"> <!-- 非活動組件將被緩存! --> </component> </keep-alive>
儘管有 props 和 events,可是有時仍然須要在 JavaScript 中直接訪問子組件。爲此可使用 ref
爲子組件指定一個索引 ID。例如:
1 <div id="parent"> 2 <user-profile ref="profile"></user-profile> 3 </div> 4 5 <script type="text/javascript"> 6 Vue.component('user-profile',{ 7 template:'<p>{{message}}</p>', 8 data:function () { 9 return { 10 message:'這裏是子組件索引!' 11 } 12 } 13 }); 14 15 var parent = new Vue({ 16 el: '#parent' 17 }); 18 // 訪問子組件 19 var child = parent.$refs.profile; 20 console.log(child.$data); //打印子組件的數據
當 ref
和 v-for
一塊兒使用時,ref 是一個數組,包含相應的子組件。
$refs
只在組件渲染完成後才填充,而且它是非響應式的。它僅僅做爲一個直接訪問子組件的應急方案——應當避免在模版或計算屬性中使用 $refs
。
組件在它的模板內能夠遞歸地調用本身,不過,只有當它有 name 選項時才能夠
name: 'unique-name-of-my-component'
當你利用Vue.component
全局註冊了一個組件, 全局的ID做爲組件的 name
選項,被自動設置.
Vue.component('unique-name-of-my-component', { // ... })
若是你不謹慎, 遞歸組件可能致使死循環。要確保遞歸調用有終止條件 (好比遞歸調用時使用
v-if
並讓他最終返回false)
(1)定義一個組件模板,基本標籤爲<span>0</span>,而後調用該組件。而且將數值加1(若是加1以後不超過10)。注意這些操做必定要放在一個標籤內,以下代碼中的div,不然會報錯。
(2)定義父組件,而且傳入初始count值
(3)註冊組件,而且定義v-if的成立條件
效果圖:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>遞歸組件</title> 6 <script type="text/javascript" src="../lib/js/vue.js"></script> 7 </head> 8 <body> 9 <div id="parent"> 10 <counter-component :count="0"></counter-component> 11 </div> 12 13 <template id="counterComponent"> 14 <div> 15 <span>{{count}}</span> 16 <counter-component :count="count+1" v-if="countNum">{{count}}</counter-component> 17 </div> 18 </template> 19 20 <script type="text/javascript"> 21 Vue.component('counter-component',{ 22 name:'counter-component', 23 template:'#counterComponent', 24 props:['count'], 25 computed:{ 26 countNum:function () { 27 return this.count < 10; 28 } 29 } 30 }); 31 32 var parent = new Vue({ 33 el: '#parent' 34 }); 35 36 </script> 37 </body> 38 </html>
假設有兩個組件稱爲 A 和 B,模塊系統看到它須要 A,可是首先 A 須要 B,可是 B 須要 A,而 A 須要 B,陷入了一個無限循環,所以不知道到底應該先解決哪一個。以下:
當使用Vue.component
將這兩個組件註冊爲全局組件的時候,框架會自動爲你解決這個矛盾。
然而,若是使用諸如Webpack或者Browserify之類的模塊化管理工具來requiring/importing組件的話,就會報錯了。
要解決這個問題,咱們須要在其中一個組件中 (好比 A) 告訴模塊化管理系統,「A 雖然須要 B,可是不須要優先導入 B」
在咱們的例子中,咱們選擇在tree-folder
組件中來告訴模塊化管理系統循環引用的組件間的處理優先級,咱們知道引發矛盾的子組件是tree-folder-contents
,因此咱們在beforeCreate
生命週期鉤子中去註冊它:
beforeCreate: function () { this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue').default }
demo完成效果:
步驟:
一、首先在建立根實例時定義一組數據,在父組件上利用v-for指令循環數據,將每一項數據經過props傳遞給子組件(列表項li),並將數據的name值在li顯示。
HTML:
1 <div class="container"> 2 <ul class="list-group"> 3 <tree-node v-for="item in items" :parent_node="item" :key="item.name"></tree-node> 4 </ul> 5 </div> 6 <template id="treeNode"> 7 <li class="list-group-item"> 8 <div> 9 {{parent_node.name}} 10 </div> 11 </li> 12 </template>
JS:
1 Vue.component('tree-node',{ 2 name:'tree-node', 3 template:'#treeNode', 4 props:['parent_node'] 5 }); 6 7 var vm = new Vue({ 8 el:'.container', 9 data:{ 10 items:[ 11 { 12 name:'列表1' 13 }, 14 { 15 name:'列表2' 16 }, 17 { 18 name:'列表3' 19 } 20 ] 21 } 22 })
注意:
key
的特殊屬性主要用在 Vue的虛擬DOM算法,在新舊nodes對比時辨識VNodes。若是不使用key,Vue會使用一種最大限度減小動態元素而且儘量的嘗試修復/再利用相同類型元素的算法。使用key,它會基於key的變化從新排列元素順序,而且會移除key不存在的元素。有相同父元素的子元素必須有獨特的key。重複的key會形成渲染錯誤。最多見的用例是結合
v-for。
<ul> <li v-for="item in items" :key="item.id">...</li> </ul>
二、給數據加一項data,用來存放二級列表數據;循環使用組件tree-node,而且將傳遞給v-for的列表項改成對應二級列表的列表項。
1 var vm = new Vue({ 2 el:'.container', 3 data:{ 4 items:[ 5 { 6 name:'列表1', 7 data:[ 8 {name:'列表1-1'}, 9 {name:'列表1-2'}, 10 {name:'列表1-3'} 11 ] 12 }, 13 { 14 name:'列表2', 15 data:[ 16 {name:'列表2-1'}, 17 {name:'列表2-2'}, 18 {name:'列表2-3'} 19 ] 20 }, 21 { 22 name:'列表3', 23 data:[] 24 } 25 ] 26 } 27 })
1 <template id="treeNode"> 2 <li class="list-group-item"> 3 <div> 4 {{parent_node.name}} 5 </div> 6 <ul> 7 <tree-node v-for="child_node in parent_node.data" :parent_node="child_node" :key="child_node.name"></tree-node> 8 </ul> 9 </li> 10 </template>
此時效果以下:
三、給一級列表項數據添加open字段,用來顯示列表是否展開;
(我默認列表1展開,列表2和列表3不展開)
四、給一級列表添加兩個圖標(open和close圖標),用v-if來判斷哪個圖標顯示。兩個圖標的顯示條件都是列表項存在第二級列表數據-data。
1 <template id="treeNode"> 2 <li class="list-group-item"> 3 <div> 4 <span v-if="!parent_node.open && parent_node.data" class="glyphicon glyphicon-folder-close"></span> 5 <span v-if="parent_node.open && parent_node.data" class="glyphicon glyphicon-folder-open"></span> 6 {{parent_node.name}} 7 </div> 8 <ul> 9 <tree-node v-for="child_node in parent_node.data" :parent_node="child_node" :key="child_node.name"></tree-node> 10 </ul> 11 </li> 12 </template>
此時效果爲:
五、用v-show和v-if給二級列表加上顯示條件。當沒有data數據的時候,二級列表不存在;當open字段爲false的時候,二級列表不顯示;
1 <template id="treeNode"> 2 <li class="list-group-item"> 3 <div> 4 <span v-if="!parent_node.open && parent_node.data" class="glyphicon glyphicon-folder-close"></span> 5 <span v-if="parent_node.open && parent_node.data" class="glyphicon glyphicon-folder-open"></span> 6 {{parent_node.name}} 7 </div> 8 9 <ul v-if="parent_node.data" v-show="parent_node.open"> 10 <tree-node v-for="child_node in parent_node.data" :parent_node="child_node" :key="child_node.name"></tree-node> 11 </ul> 12 13 </li> 14 </template>
此時顯示效果爲:
至此demo算是已經完成了,最後一步則是給一級列表加上點擊事件。
六、給列表項中的div加上點擊事件
本文的Demo和源代碼已放到GitHub https://github.com/zwl-jasmine95/Vue_test
若是您以爲本篇內容不錯,請點個贊,或在GitHub上加個星星!