组件
vue的基本语法已经差不多了,我们开始学习组件。
1、组件的概念
简单概括,组件就是自定义标签。
我们知道,vue框架的两大核心:数据驱动和组件化
组件封装的是完整的功能(包括:HTML、CSS、JS)
,而函数只封装JS(逻辑)
2、组件的使用(定义,注册,和使用)
2.1、定义组件
- 第一种
let 组件变量名= Vue.extend({
template:'<div class="header">{{msg}},我是header组件</div>'
data:function(){
return {
msg:”hi”
}
},
});
此处注意:
-
组件变量名不一定就是组件名;
-
template中引号的用法,使用单双引号时,只要换行就会报错,除非模板都写在一行,或者直接用es6模板字符串写法换行不报错,亲测有用哦;
-
data必须为函数,且有返回值。
-
自定义组件没有el项,切记。
-
第二种
let 组件变量名={
配置项
};
//定义组件
let myZujian = {
template:`
<div>
<p>我是自定义组件{{meg}}</p>
</div>
`,
data:function(){
return {
meg:'真的是',
}
}
}
2.2、注册组件
只有经过注册的组件才能够使用
- 全局注册
Vue.component('组件名',组件变量名);
全局注册的组件,在任何vue对象里都可以使用
- 局部注册(用的较多)
//在vue对象的components选项里进行注册
new Vue({
el:
components:{
组件名:组件变量名
}
});
局部注册的组件,只能在当前vue对象(组件)里使用
//局部注册,只能在该vue对象使用
let vm = new Vue({
el:'.box',
data:{
},
//在components里注册
components:{
myZujian:myZujian,
}
})
2.3、使用组件
组件定义完了,也注册了,那怎么使用呢?
很简单,就像我们使用官方html标签一样使用就好了!
<组件名></组件名>
<!-- 组件名就是你注册时的组件名,不一定就是定义时的变量名哦 -->
<div class="box">
<my-zujian></my-zujian>
</div>
2.4、组件编写方式与 Vue 实例的区别:
1、组件名不可和html官方的标签名同名,组件名如果是驼峰命名,那么使用时,用短横线连接,或者组件名首字母大写。
2、组件没有el选项,只有根实例存在el,组件里使用template定义模板
3、组件模板(html代码)只能有一个根标签
这里还有个问题:data为什么是个函数?
如果不是函数,那么,复用的组件的data共享同一块内存空间,数据会相互影响。
3、组件嵌套
把一个组件的标签写在另外一个组件的template里,就是组件嵌套。
//定义子组件1
let myCom1 = {
template:`
<p>我今年{{mes}}岁了</p>
`,
data:function(){
return {
mes:12
}
}
}
//定义子组件2 组件2嵌套组件1
let myCom2 = {
template:`
<div>
{{mee}}
<my-com1></my-com1>
</div>
`,
components:{
myCom1,
},
data:function(){
return {
mee:'真的真的'
}
}
}
4、组件的属性
使用props(property的简写)声明组件的属性。 props是外部给组件传入的数据,而组件的data是组件内部的数据。
4.1、使用 Prop 传递静态数据
- 声明属性
props是个数组,可以声明多个属性
//定义子组件
let myCom1 = {
//声明两个自定义属性
props:['name','sex'],
template:`
<div>
<p>年龄:{{age}}</p>
<p>姓名:{{name}}</p>
<p>性别:{{sex}}</p>
</div>
`,
data:function(){
return {
age:18
}
}
}
- 传入数据
<!-- 使用该自定义组件时,就可以传入数据了 -->
<div class="box">
{{mes}}
<my-com1 name="小芬" sex="23"></my-com1>
</div>
4.2、使用 Prop 传递动态数据
方法其实一样,就是传入数据的时候,将数据改成活的。
<!-- 使用v-bind -->
<div class="box">
{{mes}}
<my-com1 :name="name" :sex="sex"></my-com1>
</div>
let vm = new Vue({
el:'.box',
data:{
mes:'原vue对象中的mes',
name:'小芬',
sex:'女',
},
//局部注册组件
components:{
myCom1,
}
})
- 特殊情况:当传入的数据是一个对象的所有属性
<!-- 直接用v-bind="对象名" -->
<div class="box">
{{mes}}
<my-com1 v-bind="info"></my-com1>
</div>
let vm = new Vue({
el: '.box',
data: {
info: {
age: 21,
name: '小芬',
sex: '女',
}
},
//局部注册组件
components: {
myCom1,
}
})
- 还有特殊情况?
在 JavaScript 中对象和数组是通过引用传入的(传的是地址),所以对于一个数组或对象类型的 prop来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态(data),相当于函数的形参是引用类型。
4.3、Prop 验证
就是在声明属性的时候,对属性类型进行验证,注意:此时props是一个对象,而不是数组
//定义子组件
let myCom1 = {
//prop验证
props:{
"name":{
type:String,
required:true,
},
"sex":{
type:[String,Number]
},
"age":[Number]
},
template: `
<div>
<p>年龄:{{age}}</p>
<p>姓名:{{name}}</p>
<p>性别:{{sex}}</p>
</div>
`,
}
4.4、单向数据流
Prop 是单向绑定的:当父组件的属性(数据)变化时,将传导给子组件,但是反过来不会。
另外,每次父组件更新时,子组件的所有 prop 都会更新为最新值。这意味着你不应该在子组件内部改变 prop。
- 让我们再加深一下props的理解:
在组件内部用props声明的属性,相当于封装函数时声明的形参。
使用组件时,相当于调用函数,传递实参。
5、组件的事件
1、绑定事件:
HTML(标签)里的绑定方式:v-on
JS(Vue)里绑定方式: vue对象.$on(“事件名”)
2、触发事件 :
vue对象.$emit(“事件名”,参数);
我们在使用html官方标签绑定事件时,触发事件功能官方帮我们做好了,但是我们自定义组件(标签)时,需要自己去做触发事件的功能。
我们先看一段实例代码:
<div class="box">
<my-com1 @myclick="fn"></my-com1>
</div>
//定义子组件
let myCom1 = {
template: `
<input type="button" @click="add" value="点击加1"/>
`,
data:function(){
return {
mes:17
}
},
methods:{
add(){
//触发事件,自定义标签的触发事件,
// 也可以传递参数给父组件
this.$emit('myclick','我是子组件的人');
}
}
}
let vm = new Vue({
el: '.box',
data: {
},
//局部注册组件
components: {
myCom1,
},
methods:{
fn(str){
console.log('从子组件传来的数据:',str);
}
}
})
步骤分析:
- 子组件myCom1模板里调用触发事件函数add;
- 使用该组件时,调用事件处理函数fn
6、组件的内容(插槽)
组件的内容就是标签的innerHTML。vueJS里使用Slot(插槽)
分发内容。
将父组件的内容(DOM)放到子组件指定的位置叫作内容分发
。
props
用来处理标签的属性 ,slot
用来处理标签的内容。
6.1、单个插槽
子组件中插入一条父组件内容
<slot></slot>
<div class="box">
<my-com1>
<p>我在子组件里面</p>
</my-com1>
</div>
//定义子组件
let myCom1 = {
template: `
<div>
<div>我是子组件,并附带{{mes}}</div>
<slot></slot>
</div>
`,
data:function(){
return {
mes:17
}
},
}
let vm = new Vue({
el: '.box',
data: {
},
//局部注册组件
components: {
myCom1,
}
})
6.2、具名插槽
父级给子级传来了多个DOM(HTML元素),而且需要把不同的DOM放在子组件不同位置时,就需要给slot起名字,这就是具名插槽
。
slot元素可以用一个特殊的属性name 来配置如何分发内容。
<slot name="插槽名"></slot>
<div class="box">
<my-com1>
<p slot="p3">我在子组件里面1</p>
<p slot="p2">我在子组件里面2</p>
<p slot="p1">我在子组件里面3</p>
</my-com1>
</div>
//定义子组件
let myCom1 = {
template: `
<div>
<slot name="p1"></slot>
<div>我是子组件,并附带{{mes}}</div>
<slot name="p2"></slot>
<slot name="p3"></slot>
</div>
`,
data:function(){
return {
mes:17
}
},
}
6.3、编译作用域
即 父组件模板的内容在父组件作用域内(父组件对应的对象的作用域)编译;子组件模板的内容在子组件作用域内编译。
有兴趣可以点击插槽作用域
7、组件(间)的通信
vue组件之间的通信(传递数据)是必然的,根据vue组件之间的关系不同(父子,兄弟,或者无关组件),方法也不同。
7.1、父子组件间的通信
-
父—> 子: props, 子—> 父:事件
这个其实我们前面也用到过,组件事件里讲过子组件触发事件时可以带参传给父组件,达到父子组件的通信。
-
父---->子:refs
父传子也可以使用refs
什么是refs,可以理解为给组件一个id名,通过这个id值获取DOM,也就是标签,也即组件对象,从而操作组件对象里的属性。
refs—>dom----->组件对象----->操作组件对象的属性
-
如果某个元素使用ref属性,那么,在vue对象里,就能用this.$refs 访问。
//基本格式
<p ref = "pId"> {{msg}}</p>
methods:{
testf:function(){
this.$refs.pId.innerHTML = "hi";
}
}
<div class="box">
<input type="button" value="点击加1" @click="add">
<my-com1 ref="mcon">
<p slot="p2">{{mes}}</p>
</my-com1>
</div>
//定义子组件
let myCom1 = {
template: `
<div>
<div>我是子组件,并附带{{mes}}</div>
<slot name="p2"></slot>
</div>
`,
data:function(){
return {
mes:17
}
},
}
let vm = new Vue({
el: '.box',
data: {
mes:22
},
//局部注册组件
components: {
myCom1,
},
methods:{
add(){
this.mes++;
//获取子组件对象,改变子组件mes的值
this.$refs.mcon.mes ++;
}
}
})
7.2、兄弟组件/无关组件间的通信
-
事件总线(event-bus)
event-bus实质就是创建一个vue实例,通过一个空的vue实例作为桥梁实现vue组件间的通信。它是实现兄弟组件(或者任何无关组件,也可以实现父子)通信的一种解决方案。
基本步骤:
1、单独new一个Vue空对象: let bus= new Vue();
2、在组件A里,绑定一个自定义事件(相当于定义了一个自定义事件):
bus.$on('eclick',target => {
console.log(target)
})
3、在组件B里,触发事件
bus.$emit('eclick',"b传给a的");
实例代码:
<div class="box">
<my-com1></my-com1>
<my-com2></my-com2>
</div>
//定义子组件1
let myCom1 = {
template: `
<input type="button" value="点击触发" @click="add"/>
`,
data(){
return {
mes:40
}
},
methods:{
add(){
vm2.$emit('click',this.mes)
}
}
}
//子组件2
let myCom2 = {
template:`
<p>我是子组件二,附带{{mes2}}</p>
`,
data:function(){
return {
mes2:20
}
},
created(){
// created函数是,组件自动调用的
vm2.$on('click',(str)=>{
// console.log(str);
this.mes2 = str;
})
}
}
//单独new一个空的Vue对象 绑定事件一方为接收方 ,触发事件为发送方
let vm2 = new Vue({})
let vm = new Vue({
el: '.box',
data: {
mes:22
},
//局部注册子组件
components: {
myCom1,myCom2
},
})
-
集中管理($root)
把数据存到根实例的data选项,其他组件直接修改或者使用
基本格式:
//根组件内定义
new Vue({
data:{a:1}
})
//子组件内部使用
this // 子组件本身
this.$root // vm 根实例
this.xx //组件内部数据
this.$root.a //根实例数据
实例代码:
//定义子组件1
let myCom1 = {
template: `
<input type="button" value="点击触发1" @click="add"/>
`,
data(){
return {
mes:'我是子组件1数据'
}
},
methods:{
add(){
//修改根组件数据
this.$root.mes = this.mes;
}
}
}
//子组件2
let myCom2 = {
template:`
<input type="button" value="点击触发2" @click="add"/>
`,
data:function(){
return {
mes2:'我是子组件2数据'
}
},
methods:{
add(){
//修改根组件数据
this.$root.mes = this.mes2;
}
}
}
let vm = new Vue({
el: '.box',
data: {
mes:'我是根组件数据',
},
//局部注册子组件
components: {
myCom1,myCom2
},
})
8、动态组件
有的时候,在不同组件之间进行动态切换是非常有用的。即页面的某个位置要显示的组件是不确定的,是会变化的。
<component :is="组件名变量">
实例代码:选项卡
<div id="box">
<button @click="fn(0)">娱乐新闻</button>
<button @click="fn(1)">体育新闻</button>
<button @click="fn(2)">天文新闻</button>
<div>
<component :is="currCom"></component>
</div>
</div>
//选项卡功能
let news1 = {
template: "<div>娱乐新闻</div>"
}
let news2 = {
template: "<div>八卦新闻</div>"
}
let news3 = {
template: "<div>体育新闻</div>"
}
new Vue({
el: "#box",
data: {
currCom: "news1",
coms: ["news1", "news2", "news3"]
},
methods: {
fn(index) {
this.currCom = this.coms[index];
}
},
components: {
news1, news2, news3
}
});
花了一早上才写完,心塞!