一、组件中的细节点
is的使用
在html
语法中,table
标签内就必须是tr
标签,tr
标签下就必须是td
标签,ul
标签下就必须是li标签 等等 标签间的嵌套规则
在运用组件的时候,会出现想在这个标签内使用组件,但这个标签内并不支持的情况,如下
<div id="app">
<table>
<tbody>
<tr>
<td>this is a row</td>
</tr>
<tr>
<td>this is a row</td>
</tr>
<tr>
<td>this is a row</td>
</tr>
</tbody>
</table>
</div>
我们看到tr
标签这一部分重复了几次,我们可以试着用组件的方式,写出它,如下
<div id="app">
<table>
<tbody>
<row></row>
<row></row>
<row></row>
</tbody>
</table>
</div>
<script>
Vue.component("row", {
template:"<tr><td>this is a row</td></tr>"
});
var app = new Vue({
el: "#app"
});
</script>
然后,我们打开浏览器测试下,
虽然展示结果一样,但我们可以看待tr
这部分标签出现在了table
标签外部,这就是table
标签不支持内部嵌套不是tr
标签的情况
这时候,我们就可以使用is了
<table>
<tbody>
<tr is="row"></tr>//采用table内支持的标签,is后面接组件的名字
</tbody>
</table>
这里就不贴图了,这时,table标签的嵌套就正常了
组件中data的使用
在子组件中,data必须是一个函数,并且该函数要返回一个对象
这是因为子组件会重复使用,而他们之间需要有独立数据存储内存,这样数据才不会互相影响,如果在组件中data是一个对象,那么,所有组件的数据都指向同一块内存。
需要操作dom实例时,$refs出现了
如果你需要在父组件中,直接访问子组件的实例
可以通过ref
给子组件加个id
ref="one"
然后父组件中,这样访问
this.$refs.one
现在我们用ref
来做个小例子
当我们点击上面的两个小数,会自增,最下的数字是两个小数是总回
子组件代码
Vue.component("count",{
data:function(){
return {
number:0,
}
},
template:"<div @click='add'>{{number}}</div>",
methods:{
add:function(){
this.number++;
this.$emit("change")//当被点击时,会触发父组件的change事件
}
}
})
html代码
<div id="app">
<count ref="one" @change="allNum"></count>//用ref给子组件设置一个id,one
<count ref="two" @change="allNum"></count>
<div>{{all}}</div>
</div>
父组件代码
var app = new Vue({
el:"#app",
data:{
all:0
},
methods:{
allNum:function(){
this.all=this.$refs.one.number+this.$refs.two.number;
//在父组件中,可以通过this.$refs.ref来获取子组件的实例,
}
}
})
二、父子组件间的传值
父组件想要给子组件传值,必须在调用子组件的html上写上要传值的属性名和值,如下
<count :count="3"></count>
这里要注意:属性名
后面接的都是一个js表达式,所以这里的3是一个数值类型
而在子组件中,要接受父组件的传值,则必须在propps:[]
中加一个同样的属性名,props:["count"]
重点,在这里要注意vue的单向数据流概念,就是父组件可以给子组件传值,但子组件不能随意直接修改父组件传过来的值,必须先将父组件传过来的值复制一份,放在子组件的data中后,然后对data中的值进行修改
如下
Vue.component("count",{
props:['count'],
data:function(){
return {
number:this.count
}
}
})
如上,只能对父组件传过来的值的副本进行操作,这是因为,传进的是基础数据类型还好,如果父组件传递的是一个引用型的数据,那么子组件直接改动这个数据,就有可能对其他引用该数据的组件造成干扰
子组件给父组件传值,要借助$emit去触发父组件自定义的事件,
this.$emit("change",1)
;change是父组件自定义的事件,而1,就是我们要传递的值了,
现在我们来利用父子组件的传值,做一个简单的计数器
<div id="app">
<count :count="one" @change="allNum"></count>
//@change是父组件自定义的事件,触发了change//就会去执行allNum函数
<count :count="two" @change="allNum"></count>
//:count 是要传递的属性名,two是要传递的值,在data中,two对应0;
{{all}}
</div>
<script>
var count = {
props:['count'],//用count来接受父组件传递过来的值
data:function(){
return {
number:this.count,//记得单项数据流,要将父组件的传值,复制一份,放在子组件data中
}
},
template:'<div @click="add">{{number}}</div>',
methods:{
add:function(){
this.number++;
this.$emit("change",1);//触发change事件,传值是1;
}
}
}
var app = new Vue({
el:"#app",
data:{
all:0,
one:0,
two:0
},
components:{
count:count,
},
methods:{
allNum:function(value){//value来接受$emit的传值
this.all+=value;
}
}
})
效果如下:
三、组件参数校验与非Props特性
组件参数校验
当父组件给子组件传值时,我们希望对该值进行约束,如下
props:{
content:{
type:String,//限制传过来的值必须是字符串,
required:false,//是否必须传递该值
default:'default value',//默认值
validator:function(value){//
return (value.length>5);//该值的长度必须大于5
}
}
}
props特性
父组件给子组件传值时,切好子组件的props中有对应的prop进行接收,那么这就是props特性,,父组件给子组件传递的值,不会显示在html中,
向相反,非props特性,则会显示在html中
四、给组件绑定原生事件
父组件在子组件上绑定了一个事件,想要触发该事件,只能通过触发自定义事件的方式去触发,或者在加上事件修饰符.native
先看第一种,不加修饰符,不触发自定义事件
<div id="app">
<child @click="onClick"></child>
</div>
<script>
Vue.component('child',{
template:'<div>child</div>',
})
var app = new Vue({
el:"#app",
methods:{
onClick:function(){
alert('click');
}
}
})
</script>
在浏览器中,我们发现,点击child并没有任何效果,
这就是想要触发父组件在子组件上绑定的事件,只能通过触发自定义事件的方式去触发,现在改代码如下:
<div id="app">
<child @click="onClick"></child>
</div>
<script>
Vue.component('child',{
template:'<div @click="childClick">child</div>',
methods:{
childClick:function(){
this.$emit("click");//触发父组件的自定义事件,click
}
}
})
var app = new Vue({
el:"#app",
methods:{
onClick:function(){
alert('click');
}
}
})
</script>
或者给加上一个事件修饰符.native也可以达到相同的效果
<child @click.native="onClikc"></child>
五、非父子组件间的传值
这里先介绍一种总线的模式。
<div id="app">
<child content="胡"></child>
<child content="志武"></child>
</div>
<script>
Vue.prototype.bus = new Vue();
Vue.component("child",{
props:['content'],
data:function(){
return {
name:this.content,
}
},
template:'<div @click="exchange">{{name}}</div>',
methods:{
exchange:function(){
this.bus.$emit("change",this.name)
}
},
//组件被挂载时,
mounted:function(){
var that = this;
this.bus.$on("change",function(msg){
that.name=msg;
})
}
})
var app = new Vue({
el:"#app",
})
</script>
我们在Vue
的prototype
上挂载了一个bus
,这个bus是一个vue
的实例,子组件要互相传值时,就可以用这个共同的祖先bus来监听和触发相应的事件,并借助这个共同的祖先来传值
六、使用插槽
slot可以让我们在组件的某个位置插入想要的内容
Vue.component("child",{
template:`<div>
<slot name="header">默认内容</slot>
<div>content</div>
<slot name="footer">默认内容</slot>
</div>`
})V
<div id="app">
<child>
<div slot="header">我是头部</div>
<div slot="footer">我是尾部</div>
</child>
</div>
如果在插槽位置没有插入内容,则会显示默认内容
作用域插槽
当子组件在做遍历,而希望这个遍历的dom结构由外部决定时,可以使用作用域插槽
<div id="app">
<child >
<template slot-scope="props">//template是必须写的
<h1>{{props.item}}</h1>//props可以任意写,而item必须和template中的`:item`一致
</template>
</child>
</div>
<script>
Vue.component("child",{
data:function(){
return {
list:[1,2,3,4,5]
}
},
template:`
<ul>
<slot v-for="item of list"
:item=item>//这里的:item是要传值给html实际插槽部分的
</slot>
</ul>
`
})
new Vue({
el:"#app",
})
</script>
动态组件和v-once
<component>
是vue自带标签,
<component :is="组件名">
component会根据is后面组件名的不同而动态加载不同的组件
v-once可以把组件放在内存中,可以有效提高性能
template:`<div v-once>child ONE</div>`
结语
因为本人水平有限,如果有错漏的地方,还请看官多多指正
本文作者胡志武,写于2019/5/13,如果要转载,请注明出处,
如果觉得写的不错, 请点个赞吧