《Vue.js實戰》第七章.組件

7.1 組件做用:

提升代碼複用性,使項目易於維護

7.1 組件的使用

7.1.1 組件註冊-全局註冊

全局註冊後,任何vue的實例均可以使用該組件.
Vue.component('my-component',{

})

my-component就是自定義組件的名稱,推薦使用小寫加減號的方式來命名.
要在父元素中使用組件,必須在父元素實例初始化以前註冊組件

Vue.component('my-component', {
  template: '<div>這是my-component的內容!!</div>'
})
var app = new Vue({
  el: '#app',
  data: {

  }
})
=================================================
<div id="app">
  <my-component></my-component>
</div>

7.1.2 組價註冊-局部註冊

var Child = {
 template: '<div>這裏是組件內容!!</div>'
}
var app = new Vue({
  el: '#app',
  data: {

  },
  components: {
    'my-component': Child
  }
})
==============================================
<div id="app">
  <my-component></my-component>
</div>

7.2 使用props傳遞數據

7.2.1 props的基本用法

組件不單單是對模板的內容進行復用,更重要的是要進行模板之間的通訊vue

正向傳遞數據web

父組件的模板包含子組件,父組件要想子組件正向的傳遞數據或參數,
子組件解收到後根據參數的不一樣來渲染不一樣的內容或執行操做.數組

props值的分類app

1.數組
2.對象異步

props命名規則:在使用DOM模板時:使用CamelCase(駝峯命名)的props名稱要使用kebab-case:svg

例:函數

<div id="app">
		<my-component warning-text="提示信息!"></my-component>
</div>
========================================================================
<script>
	Vue.component('my-component',{
		props:['warningText'],
		template:'<div>{{warningText}}</div>'
	})
	var app = new Vue({
		el:'#app'
	})
</script>

props做爲須要被轉變的原始值傳入,在這種狀況下使用計算屬性.ui

例:this

Vue.component('my-component',{
	props:['width'],
	template:'<div :style="style">組件內容</div>',
	computed:{
		style:function(){
			return {
				width:this.width+'px'
			}
		}
	}
})

7.2.2 prop數據的驗證

例:	Vue.component('my-component',{
		props:{
			//必須是數字
			propA:Number,
			//必須是數字或字符串
			propB:[String,Number],
			//布爾值,若是沒有定義,默認值是true
			propC:{
				type:Boolean,
				default:true
			},
			//數字,而且必須是必傳
			propD:{
				type:Number,
				required:true
			},
			//若是是數組或對象,默認值必須是一個而函數來返回
			propE:{
				type:Array,
				default:function(){
					return []
				}
			},
			//自定義一個驗證函數
			propF:{
				validator:function(value){
					return value>10
				}
			}
			
		},
		template:'<div>{{propA}}:{{propD}}</div>'
	})

驗證的type類型設計

  • String
  • Number
  • Boolean
  • Object
  • Array
  • Function

7.3 組件通訊

* 7.3.1 父子組件通訊

**自定義事件**

	<div id="app">
		<p>總數:{{total}}</p>
		<my-component @increase="handleGetTotal" @reduce="handleGetTotal"></my-component>
	</div>
	======================================================================================
	Vue.component('my-component',{
		template:`
		<div>
			<button @click="handleIncrease">+1</button>
			<button @click="handleReduce">-1</button>
		</div>`,
		data:function(){
			return{
				counter:0
			}
		},
		methods:{
			handleIncrease:function(){
				this.counter++
				this.$emit('increase',this.counter)
			},
			handleReduce:function(){
				this.counter--
				this.$emit('reduce',this.counter)
			}
		}
	})
	var app = new Vue({
		el:'#app',
		data:{
			total:0
		},
		methods:{
			handleGetTotal:function(total){
				this.total=total
			}
		}
	})

使用v-model

<div id="app">
		<p>總數:{{total}}</p>
		<my-component v-model="total"></my-component>
	</div>
==========================================================
Vue.component('my-component',{
		template:'<button @click="handleClick">+1</button>',
		data:function(){
			return{
				counter:0
			}
		},
		methods:{
			handleClick:function(){
				this.counter++
				this.$emit('input',this.counter)
			}
		}
		
	})
	var app = new Vue({
		el:'#app',
		data:{
			total:0
		}
	})

使用v-model自定義雙向綁定組件

<div id="app">
		<p>總數:{{total}}</p>
		<my-component v-model="total"></my-component>
		<button type="button" @click="handleReduce">-1</button>
	</div>
==============================================================
Vue.component('my-component',{
		props:['value'],
		template:'<input :value="value" @input="updateValue"></input>',
		methods:{
			updateValue:function(event){
				this.$emit('input',event.target.value)
			}
		}
	})
	var app = new Vue({
		el:'#app',
		data:{
			total:0
		},
		methods:{
			handleReduce:function(){
				this.total--
			}
		}
		
	})

實現一個具備雙向數據綁定的v-model組件要知足的要求

* 接收一個value屬性
 * 有新的value時觸發input事件

* 7.3.2 非父子間組件通訊(兄弟組件之間通訊,跨級組件之間通訊)

  • 1.中央事件總線(bus)
  • 2.父鏈
**定義:** 

在子組件中,使用this.$parent能夠直接訪問該組件的父實例或組件,父組件也能夠經過this.$children訪問他全部的子組件,並且能夠遞歸或向下無線訪問,知道根實例或最內層組件

**例:**  

Vue.component('my-component',{
		template:'<button @click="handleChange">經過父鏈修改父組件數據</b1utton>',
		methods:{
			handleChange:function(){
				this.$parent.message="來自子組件的數據"
			}
		}			
  })
  var app = new Vue({
    el:'#app',
    data:{
      message:''
    }
})
  • 3.子組件索引
>> **示例**    

  <div id="app">
    <button @click="handleRef">經過ref獲取子組件實例</button>
    <my-component ref="comA"></my-component>
  </div>
  ===========================================================
  Vue.component('my-component',{
		template:'<div>子組件</div>',
		data:function(){
			return{
				message:'自組件內容'
			}
		}
	})
	
	var app = new Vue({
		el:'#app',
		methods:{
			handleRef:function(){
			//經過ref來獲取指定ref實例
			var msg = this.$refs.comA.message
			console.log(msg)
		}}
	})

7.4 使用slot分發內容

7.4.1 什麼是slot

能夠在組件中插入內容
組件的3個API來源:props傳遞數據、event出發事件、slot分發內容
內容分發:當須要組件混合使用時,混合父組件的內容與子組件的模板時,就會用到slot

7.4.2 什麼是編譯做用域

7.4.3 做用域的用法

* 單個slot

示例

<div id="app">
		<child-component>
			<p>分發的內容</p>
		</child-component>
	</div>

======================================

Vue.component('child-component',{
		template:`
		<div>
			<slot>子組件默認顯示的內容</slot>
		</div>
		`
	})
	var  app = new Vue({
		el:'#app'
	})
* 具名插槽

示例

<div id="app">
		<child-component>
			<p slot="header">我是頭部</p>
			<p>默認插槽</p>
			<p slot="footer">我是尾部插槽</p>
		</child-component>
	</div>

================================================

Vue.component('child-component',{
		template:`
		<div>
			<div class="header">
				<slot name="header"></slot>
			</div>
			<div>
				<slot></slot>
			</div>
			<div class="footer">
				<slot name="footer"></slot>
			</div>
		</div>
		`
		
	})
	var app =  new Vue({
		el:'#app'
	})

7.4.4 做用域插槽

示例

<div id="app">
    <child-component>
      <template scope="props">
        <p>來自父組件的內容</p>
        <p>{{props.msg}}</p>
      </template>
    </child-component>
</div>

==========================================================

Vue.component('child-component',{
		template:`<div>
					<slot msg="來自子組件的內容"></slot>
				  </div>`
	})
	var app = new Vue({
		el:'#app'
	})

示例2

<div id="app">
		<my-list :books="books">
			<template slot="book" slot-scope="props">					
				<li>{{props.bookName}}</li>
			</template>
		</my-list>
		<ul>
			<li v-for="book in books">{{book.name}}</li>
		</ul>
	</div>

==========================================================

Vue.component('my-list',{
		props:{
			books:{
				type:Array,
				default:function(){
					return []
				}
			}
		},
		template:
		`
		<ul>
			<slot name="book" v-for="book in books" :book-name="book.name"></slot>
		</ul>
		`
	})
	var app = new Vue({
		el:'#app',
		data:{
			books:[
				{name:'《Vue.js實戰》'},
				{name:'《JavaScript語言精粹》'},
				{name:'《JavaScript高級程序設計》'}
			]
		}
	})

7.4.5 訪問slot

被分發的內容使用$slot來訪問

<div id="app">
		<my-component>
			<h2 slot="header">標題</h2>
			<p>主體內容</p>
			<p>更多主體內容</p>
			<div slot="footer">底部信息</div>
		</my-component>

	</div>

==========================================================================

Vue.component('my-component',{
		template:
		`
		<div class="container">
			<div class="header">
				<slot name="header"></slot>
			</div>
			<div class = "main">
				<slot></slot>
			</div>
			<div class = "footer">
				<slot name = "footer"></slot>
			</div>
		</div>
		`,			
		mounted:function(){
			var header = this.$slots.header
			var main = this.$slots.default
			var footer = this.$slots.footer
			console.log(main)
			console.log(header)
			console.log(footer)
			console.log(footer[0].elm.innerHTML)
		}			
	})
	var app = new Vue({
		el:'#app',
		
	})

7.5 組件的高級用法

7.5.1 遞歸組件 (組件能夠在它的模板內遞歸的調用本身)

示例

<div id="app">
		<child-component :count="1"></child-component>
	</div>

===================================================================

Vue.component('child-component',{
		name:'child-component',
		props:{
			count:{
				type:Number,
				default:1
			}
		},
		template:
		`
		<div class="child">
			<child-component :count="count+1" v-if="count<3"></child-component>
		</div>
		`
	})
	var app = new Vue({
		el:'#app',
		data:{
			
		}
	})

使用場景:級聯菜單,樹形控件

7.5.2 內聯模板

示例

<div id="app">
		<child-component inline-template>
			<div>
				<h2>在父組件中定義子組件的模板</h2>
				<p>{{m}}</p>
				<p>{{msg}}</p>
			</div>
		</child-component>			
	</div>

====================================================================

Vue.component('child-component',{
		data:function(){
			return{
				msg:'在子組件中聲明的數據'					
			}
		}
	})
	var app = new Vue({
		el:'#app',
		data:{
			m:'在父組件中聲明的數據'
		}
	})

子組件和父組件中聲明的數據均可以渲染,我的環境中只能渲染子組件的數據而不能渲染父組件的數據

7.5.3 動態組件

使用<component>元素來動態的掛載不一樣的組件

示例

<div id="app">
		<component :is="currentView"></component>
		<button @click="handleChangeView('A')">切換到A</button>
		<button @click="handleChangeView('B')">切換到B</button>
		<button @click="handleChangeView('C')">切換到C</button>
	</div>

==========================================================================================

var app = new Vue({
		el:'#app',
		components:{
			comA:{
				template:'<div>組件A</div>'
			},
			comB:{
				template:'<div>組件B</div>'
			},
			comC:{
				template:'<div>組件C</div>'
			}
		},
		data:{
			currentView:'comA'
		},
		methods:{
			handleChangeView:function(component){
				this.currentView='com'+component
				
			}
		}
	})

7.5.4 異步組件

vue觀察到數據變化時並非直接更新DOM,而是開啓一個隊列,並緩衝在同一事件循環中發生的全部數據改變.在緩衝時會去除重複的數據,從而避免沒必要要的計算和DOM操做,而後在下一個事件循環tick中,Vue刷新隊列並執行實際(已經去重)工做,

示例

<div id="app">
		<child-component></child-component>
	</div>

=======================================================================

Vue.component('child-component',function(resolve,reject){
		window.setTimeout(function(){
			resolve({
				template:'<div>我是異步加載的</div>'
			})
		},2000)
	})
	var app = new Vue({
		el:'#app'
	})

7.6 其餘

7.6.1 $nextTick

需求場景

有一個div默認爲用v-if將其隱藏,當點擊按鈕時,將v-if的值修改成true顯示div,同時獲取div中的文本內容

模板代碼

<div id="app">
		<div id="test" v-if="showDiv">這是一段文本</div>
		<button @click="getText">獲取文本</button>
	</div>

錯誤js代碼

var app = new Vue({
		el:'#app',
		data:{
			showDiv:false
		},
		methods:{
			getText:function(){
				this.showDiv=true
				var text = document.getElementById('test').innerHTML
				console.log(text)       
			}
		}			
	})

正確JS代碼

var app = new Vue({
		el:'#app',
		data:{
			showDiv:false
		},
		methods:{
			getText:function(){
				this.showDiv=true				
				this.$nextTick(function(){
					var text = document.getElementById('test').innerHTML
					console.log(text)
				})
			}
		}			
	})

7.6.2 x-tamplates

示例

<div id="app">
		<my-component></my-component>
		<script type="text/x-template" id="my-component">
			<div>這是組件的內容</div>
		</script>
	</div>

===========================================================================

Vue.component('my-component',{
		template:'#my-component'
	})
	var app = new Vue({
		el:'#app',
		
	})

7.6.3 手動掛載實例

示例

<div id="mount-div"></div>

========================================

var MyComponent = Vue.extend({
		template:'<div>Hello:{{name}}</div>',
		data:function(){
			return{
				name:'Aresn'
			}
		}
	});
	
	new MyComponent().$mount('#mount-div')