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的基本用法
组件不仅仅是对模板的内容进行复用,更重要的是要进行模板之间的通信
正向传递数据
父组件的模板包含子组件,父组件要想子组件正向的传递数据或参数,
子组件解收到后根据参数的不同来渲染不同的内容或执行操作.
props值的分类
1.数组
2.对象
props命名规则:在使用DOM模板时:使用CamelCase(驼峰命名)的props名称要使用kebab-case:
例:
<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作为需要被转变的原始值传入,在这种情况下使用计算属性.
例:
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')