1.通过is解决在使用组件中出现的bug(适用于<table>,<ul>,<ol>,<select>等标签)
场景:一个表格有多行,我想把行抽出来定义成一个自定义组件,然后在页面中显示出来.
<div id="app">
<table>
<row></row>
<row></row>
<row></row>
</table>
</div>
<script>
Vue.component('row',{
template:'<tr><td>this is a row</td></tr>'
});
var vm = new Vue({
el:'#app'
})
</script>
如果一切正常,<row>中的内容应该出现在<table></table>内,但实际却出现在了与<table>标签同级的位置:
这种情况下,我们可以采用is来解决,因为自定义标签破坏了HTML语法中<table>标签内必须是<tr>标签的标准语法.
<div id="app">
<table>
<tr is="row"></tr>
<tr is="row"></tr>
<tr is="row"></tr>
</table>
</div>
可以看出,使用is后完美解决该bug.
2.子组件的data必须是一个函数然后返回对象,不能像父组件里的data一样直接在其中定义对象.
<div id="app">
<table>
<tr is="row"></tr>
<tr is="row"></tr>
<tr is="row"></tr>
</table>
</div>
<script>
Vue.component('row',{
data:{
msg:'this is a row'
},
template:'<tr><td>{{msg}}</td></tr>'
});
var vm = new Vue({
el:'#app'
})
</script>
然后发现这样报错了,原因是每个子组件里的数据都应该是它独有的,各子组件之间的数据不应该被共享,所以子组件里定义的data要以函数的形式,返回数据.
修改后:
Vue.component('row',{
data:function(){
return{
msg:'this is a row'
}
},
template:'<tr><td>{{msg}}</td></tr>'
});
var vm = new Vue({
el:'#app'
})
数据正常显示且无任何报错.
3. 组件中的ref引用.
场景:尽管Vue不推荐操作DOM,但有些时候不得不去操作DOM,这时候我们可以通过this.$refs.xx来获取ref="xx"的元素.
<body>
<div id="app">
<div ref="test" @click="getRef">1</div>
</div>
<script>
var vm = new Vue({
el:'#app',
method:{
getRef:function(){
alert(this.$refs.test.innerHTML)
}
}
})
</script>
</body>
在点击时可以看到浏览器弹出提示框1
基于此,可以开发一个具有2个子组件,点击子组件后子组件数值递增,并触发父组件进行求和的demo:
<body>
<div id="app">
<num @change="handleChange" ref="one"></num>
<num @change="handleChange" ref="two"></num>
<div>{{total}}</div>
</div>
<script>
Vue.component('num',{
data:function () {
return{
num:0
}
},
template:'<div v-on:click="add">{{num}}</div>',
methods:{
add:function () {
this.num++
this.$emit('change')
}
}
});
var vm = new Vue({
el:'#app',
data:{
total:0
},
methods: {
handleChange:function () {
this.total = this.$refs.one.num + this.$refs.two.num;
}
}
})
4. 组件之间的值传递
4.1父组件向子组件传值
父组件向子组件传值比较简单,可以通过props进行传值,这里仅仅引出.
主要是为了铺垫接下来父组件向子组件传值后比较容易犯的一个错(子组件不可直接修改父组件传递过来的值,因为父组件传递来的值可能被多个子组件使用,这里有点类似于java中多线程访问数据时的数据不安全性.
<div id="app">
<counter :count="num1"></counter>
<counter :count="num2"></counter>
</div>
<script>
var counter = {
props:['count'],
template:'<div>{{count}}</div>'
}
vm = new Vue({
el:'#app',
data:{
num1:1,
num2:2
},
components:{
counter:counter
}
})
</script>
当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告.
父组件向子组件传值后,如果子组件想改变接收到的值之后,想改变父组件传过来的值,可以先对该数据进行一份拷贝,然后再操作拷贝后的值,这样就不会报错了,也就是官网所说的单向数据流.
<script>
var counter = {
props:['count'],
data:function(){
return{
add:this.count
}
},
methods:{
sum:function () {
this.add++
}
},
template:'<div @click="sum">{{add}}</div>'
}
vm = new Vue({
el:'#app',
data:{
num:1
},
components:{
counter:counter
}
})
</script>
4.2组件参数校验
<div id="app">
<my-prop></my-prop>
</div>
<script>
Vue.component('my-prop',{
template:'<div>{{content}}</div>',
props:{
content:{
//参数类型校验
type:[String,Number],
//参数是否必传校验
required:false,
//参数如果不传,默认值指定
default:'laohan',
//参数值校验,要符合某种规则,比如长度必须大于5
validator:function (value) {
return (value.length > 5)
}
}
}
});
var vm = new Vue({
el:'#app'
})
</script>
4.3非父子组件之间的传值(Bus/总线方式/观察者模式)
<div id="app">
<bus-msg content="DELL"></bus-msg>
<bus-msg content="LEE"></bus-msg>
</div>
<script>
//定义全局挂载的bus
Vue.prototype.bus = new Vue();
Vue.component('bus-msg', {
props: {
content: {
type: String
}
},
//创建content的副本
data:function(){
return {
myContent:this.content
}
},
template: '<div @click="handleClick">{{myContent}}</div>',
//页面加载完成后,点击会向外触发change事件
methods: {
handleClick:function () {
this.bus.$emit('change',this.myContent)
}
},
//通过bus监听该change事件,并更新副本中的值
mounted:function () {
var this_ = this;
this.bus.$on('change',function (msg) {
this_.myContent = msg;
})
}
});
var vm = new Vue({
el: '#app'
})
</script>
效果:
点击DELL页面上两个组件的值都变为DELL,点击LEE页面上两个组件的值都变为LEE
5.给组件绑定原生事件(.native)
如果想让子组件直接触发根组件的事件,我们可以给子组件绑定原生的事件,这样做可以节省很多代码量:
同样的功能,当点击子组件时触发父组件弹框,我们分别用非原生事件和原生事件实现一下:
//非原生方式:
<script>
Vue.component('native-event',{
template:'<div @click="sayHi">hello</div>',
methods: {
sayHi:function () {
this.$emit('parent')
}
}
});
var vm = new Vue({
el:'#app',
methods:{
sayHiParent:function () {
alert('hi-parent!')
}
}
})
</script>
//原生实现
<div id="app">
<native-event @click.native="sayHiParent"></native-event>
</div>
<script>
Vue.component('native-event',{
template:'<div>hello</div>',
});
var vm = new Vue({
el:'#app',
methods:{
sayHiParent:function () {
alert('hi-parent!')
}
}
})
</script>
6.插槽<slot>
在Vue中,可以使用插槽来动态地为子组件传递一些DOM,从而对组件做一些增强.
6.1普通插槽:普通插槽只允许有一个
<div id="app">
<my-slot>
<h1>普通插槽</h1>
</my-slot>
</div>
<script>
Vue.component('my-slot',{
template:'<div><p>hello</p><slot></slot></div>',
});
var vm = new Vue({
el:'#app'
})
</script>
6.2具名插槽:具名插槽可以有多个
<div id="app">
<my-slot>
<h1 slot="header">头部</h1>
<h2 slot="footer">尾部</h2>
</my-slot>
</div>
<script>
Vue.component('my-slot',{
template:'<div><slot name="header"></slot><p>hello</p><slot name="footer"></slot></div>',
});
var vm = new Vue({
el:'#app'
})
</script>
6.3作用域插槽
固定写法:
<template slot-scope="xx">
{{xx.item}}
</template>
普通的插槽一般是显示父组件向子组件传递的内容,但作用域插槽显示的是子组件向父组件传递的内容,这样就可以在父组件调用子组件时灵活的决定子组件显示的样式了.
这么说可能有点绕,直接上代码:
<div id="app">
<scope-slot>
<template slot-scope="props">
<h1>{{props.item}}</h1>
</template>
</scope-slot>
</div>
<script>
Vue.component('scope-slot',{
template:'<div><ul><slot v-for="item in list" :item="item"></slot></ul></div>',
data:function () {
return{
list:[1,2,3,4,5]
}
}
});
let vm = new Vue({
el:'#app'
})
</script>
7.动态组件和v-once
有时候需要在多个组件之间来回切换,除了可以使用v-if,还可以使用动态组件来解决.
固定语法是<component v-bind:is="组件名"></component>
<div id="app">
<!--普通方式实现-->
<!--<my-one v-if="type==='my-one'"></my-one>-->
<!--<my-two v-if="type==='my-two'"></my-two>-->
<!--动态组件实现-->
<component :is="type"></component>
<button @click="changeType">change</button>
</div>
<script>
Vue.component('my-one',{
template:'<div>one</div>'
});
Vue.component('my-two',{
template:'<div>two</div>'
});
let vm = new Vue({
el:'#app',
data:{
type:'my-one'
},
methods:{
changeType:function () {
this.type = (this.type==='my-one'? 'my-two' : 'my-one')
}
}
})
</script>
当我们在点击切换组件后,组件之间可以完成切换:
但是在组件切换过程中,每次点击change都会销毁旧组件和创建新组件,这样是比较耗性能的,我们可以通过添加v-once指令让组件仅在第一次加载时被渲染,然后存储到缓存中,之后可以直接从缓存中取,不需要重复创建.
Vue.component('my-one',{
template:'<div v-once>one</div>'
});
Vue.component('my-two',{
template:'<div v-once>two</div>'
});