一、自定义组件
组件是你能够拿来用的那些标签、方法等都可以叫做组件。
例如:h5标签可以说是系统自带的一个组件,html内置的组件。
那么如何用vue实现一个自定义组件呢?以下是实现过程:
1、首先定义一个组件:
其中:
(1)template表示当前组件显示的模板内容,
txt会在template这里模板html中显示
(2)
//定义一个组件
const Button = {
//组件的模板html
template:`<div class="btn">{{txt}}</div>`, //template表示当前组件显示的模板内容
//txt会在template这里模板html中显示
props:['txt'],//txt属性 调用组件的时候传递过来
};
2、注册组件:
const app = new Vue({
el:'#app',
//通过components注册组件
components:{
btn:Button,
//btn是在使用这个组件的时候的属性名
//Button是上面定义组件时定义的那个变量
}
})
3、使用这个组件
<div id="app">
<btn txt="按钮"></btn>
<btn txt="登录"></btn>
</div>
css样式:
.btn {
background-color: deeppink;
color: #fff;
padding: 0.5rem;
width: 80px;
text-align: center;
cursor: pointer;
border-radius: 0.3rem;
margin: 0.2rem;
}
.btn.danger {
background-color: red;
}
.btn.info {
background-color: greenyellow;
}
例子: 下面再定义一个计数器的组件,同上:
1、定义组件:
const Counter = {
template:`<div class="counter">
<button @click="count += 1">计数-{{count}}</button>
</div>`,
//组件中的data必须是一个function,其返回一个对象作为组件的数据
data(){
return {
count:1,
};
}
}
2、注册组件:
const app = new Vue({
el:'#app',
//注册组件
components:{
//组件在页面中使用的名字;组件对象
btn:Button,
//btn是在使用这个组件的时候的属性名
//Button是上面定义组件时定义的那个变量
counter:Counter,
}
})
3、使用组件:
<div id="app">
<btn txt="按钮" type="danger"></btn>
<btn txt="登录" ></btn>
<counter></counter>
</div>
结果如下:
若想再给计数换个刚刚自定义按钮,则可以写成如下形式:
其中还要在Counter里面再次进行注册
const Counter = {
template:`<div class="counter">
<button @click="count += 1">计数-{{count}}</button>
<btn txt="计数" @btn-click="clickHandle()"></btn>
</div>`,
//组件中的data必须是一个function,其返回一个对象作为组件的数据
data(){
return {
count:1,
};
},
//组件可以嵌套,在使用局部组件的时候需要进行注册
components:{
btn:Button,
}
}
总代码:
在定义的Button组件template中加了@click=“clickHandle”,并添加methods的clickHandle方法
//往外派发一个自定义事件btn-click,所有使用组件的父组件可以进行事件监听
this.$emit(‘btn-click’)
在定义的Counter组件template中加了@btn-click="clickHandle(),并添加methods的clickHandle方法
//此处的btn-click是属于组件的自定义事件,自定义事件需要使用$emit进行触发
总结:首先{<div @click=“clickHandle” class=“btn” :class=“type”>{{txt}}}定义了一个自定义button按钮,这个button其实就是一个div,div里有个文本内容,当点击这个div之后,类似于触发了一个click事件,这个click事件又向外派发了一个自定义事件,这个Counter组件监听一下这个自定义事件之后触发它的clickHandle函数。
</div>
<script src="./libs/vue.js"></script>
<script>
//定义一个组件
const Button = {
//组件的模板html
template:`<div @click="clickHandle" class="btn" :class="type">{{txt}}</div>`,
props:['txt','type'],//属性 调用组件的时候传递过来,父组件传递过来的参数
methods:{
clickHandle(){
// alert('111')
//往外派发一个自定义事件btn-click,所有使用组件的父组件可以进行事件监听
this.$emit('btn-click')
}
}
};
//此处的btn-click是属于组件的自定义事件,自定义事件需要使用$emit进行触发
const Counter = {
template:`<div class="counter">
<button @click="count += 1">计数-{{count}}</button>
<btn txt="计数" @btn-click="clickHandle()"></btn>
</div>`,
//组件中的data必须是一个function,其返回一个对象作为组件的数据
data(){
return {
count:1,
};
},
//组件可以嵌套,在使用局部组件的时候需要进行注册
components:{
btn:Button,
},
methods:{
clickHandle(){
alert('1111');
}
}
}
const app = new Vue({
el:'#app',
//注册组件
components:{
//组件在页面中使用的名字;组件对象
btn:Button,
//btn是在使用这个组件的时候的属性名
//Button是上面定义组件时定义的那个变量
counter:Counter,
},
})
</script>
二、局部组件和全局组件
我们在开发的时候遵循的原则:
(1)把经常复用的内容封装成一个组件,
(2)把逻辑复杂的一些功能封装成组件
在vue中定义组件有两种常见的形式
1、局部组件
const c = { template:`` }
局部组件在使用中需要注册
components属性 进行注册
2、全局组件
Vue.component(’’,{ template })
全局组件定义之后可以直接使用不需要注册
全局组件
<div id="app">
<count></count>
<hello></hello>
</div>
<hr>
<div id="app1">
<count></count>
<hello></hello>
</div>
、、
//定义一个全局组件
Vue.component('hello', {
template: `
<div>
<h1>Hello 小明</h1>
<p>小明是一个好孩子</p>
</div>
`
});
var app = new Vue({
el: '#app',
components: {
count: Counter
}
});
new Vue({
el: '#app1',
});
上面再复制一个app1,结果如下:控制台报错,下面那个局部组件没有注册,(我是一个计数器)不显示。全局的依然可用。
局部组件:
//定义局部组件,就是一个对象,此对象包含有一些特殊的属性
// template 模板内容
// 组件的数据,data必须是一个function,此function有一个对象作为返回值
// methods 方法
// computed 计算属性
// … 其他的等等等等,所有vue中可以使用的方法或者属性都可以在组件内部使用
定义全局组件用component属性
//定义一个全局组件
Vue.component('hello',{
template:`
<h1>Hello 小明</h1>
<p>小明是一个好孩子</p>
`
})
以上这样写会报错,需要在这两个同级的元素外面再加上一个根节点
因此组件定义的时候需要遵循一个原则:
定义的的组件必须只有一个根节点
定义一个计数器,
var Counter = {
template: `<div class="counter">
<h1>我是一个计数器:{{count}}</h1>
<p>步长:{{step}}</p>
<button @click="count+=step">点击</button>
</div>`,
props:['step'],
data(){
return {
count:0,
}
}
};
var app = new Vue({
el: '#app',
components: {
count: Counter
}
});
在页面中使用这个组件时,想要让它根据步长来增加,但是如果步长是字符串直接拼接到后面出现不了想要的效果,因此具体情况如下:
<div id="app">
<!-- step="2"中的2是一个字符串
:step="2"表示是一个js对象,此时的2是数字
如果想要它再变成字符串,:step="'2'" -->
<count :step="'2'"></count>
<hello></hello>
</div>
现在把props可以写成对象的形式:
//props以对象的形式设置属性 可以对传递的数据做验证
//如果不符合直接报错
props:{
step:Number,
},
三、组件之间的传值
1、父组件向子组件传值
父组件向子组件传值,使用props属性进行传值
var Counter = {
template: `<div class="counter">
<h1>我是一个计数器:{{count}}</h1>
<p>步长:{{step}}</p>
<button @click="count+=step">点击</button>
</div>`,
// props:['step'],
//props以对象的形式设置属性 可以对传递的数据做验证
//如果不符合直接报错
props:{
step:Number,//属性名:数据类型
},
data(){
return {
count:0;
}
}
};
2、子组件向父组件传值
子组件向父组件传值,使用事件派发
var Counter = {
template: `<div class="counter">
<h1>我是一个计数器:{{count}}</h1>
<p>步长:{{step}}</p>
<button @click="addHandle">点击</button>
</div>`,
// props:['step'],
//props以对象的形式设置属性 可以对传递的数据做验证
//如果不符合直接报错
props:{
step:Number,//属性名:数据类型
},
data(){
return {
count:0,
}
},
methods:{
addHandle(){
this.count += this.step;
//派发事件
this.$emit('countchanged',this.count);
}
}
};
、、父组件进行监听、接收
var app = new Vue({
el: '#app',
components: {
count: Counter,
},
data:{
stepVal:2,
fromCount:0,//为了动态的接收数据
},
methods:{
// 接收组件传递的数据 定义了一个第三方变量thisfromCount,
// 这个第三方变量等于我传递过来的数据进行显示
countChandle(v){
this.fromCount=v
}
}
});
四、vue实现跨组件传值
vue实现跨组件传值主要通过eventBus,即事件总线。
简单理解就是:小明在A城市,小红在B城市,小兰要从A城市去往B城市,小明委托给小兰给小红带去一封信,回来的时候小红又委托给小兰给小明带封回信,这里的小红就相当于事件总线。
即:
1、 事件总线 解决非相关组件之间传值
2、 我们通过定义一个空白的对象 所有的时间派发和监听都在这个对象上进行
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>非相关组件之间传值</title>
<style>
img{
width: 100px;
max-height: 100px;
}
.lists ul{
list-style: none;
display: flex;
align-items: center;
justify-content: center;
}
.lists ul li{padding:10px;color:green}
.product{min-height:150px;border:1px solid blanchedalmond}
</style>
</head>
<body>
<!-- 事件总线 解决非相关组件之间传值 -->
<!-- 我们通过定义一个空白的对象 所有的时间派发和监听都在这个对象上进行 -->
<div id="app">
<varbar></varbar>
<product v-for="(p,i) in list" :product="p" :key="i"></product>
</div>
<!-- 顶部导航组件 -->
<!-- 商品列表组件 -->
<script src="./libs/vue.js"></script>
<script>
const eventBus = new Vue();//定义一个空白的vue实例,用来做事件的转发
const varbar = {
template:`
<div class="lists">
<ul>
<li>首页</li>
<li>商品列表</li>
<li>购物车({{cartCount}})</li>
<li>其他</li>
</ul>
</div>
`,
created(){
//组件创建完成之后,监听一下这个buyHandle
eventBus.$on('buy-product',this.buyHandle)
},
methods:{
//然后接收一个参数product,这个参数由派发的p来
buyHandle(product){
console.log(product);
this.cartCount +=1
}
},
data(){
return {
cartCount:0,
}
}
};
const product = {
template:`
<div class="product">
<img src=""/>
<h5>{{product.name}}</h5>
<p>{{product.price}}</p>
<button @click="buyHandle(product)">购买</button>
</div>
`,
props:['product'],
methods:{
buyHandle(p){
eventBus.$emit('buy-product',p)//通过事件总线
}
}
};
const app = new Vue({
el:'#app',
data:{
list:[
{img:'',name:'MH',price:200},
{img:'',name:'IPhone',price:300},
{img:'',name:'HuaWei',price:400},
{img:'',name:'Nokie',price:500},
{img:'',name:'DiA',price:600},
]
},
components:{
varbar:varbar,
product:product,
},
})
</script>
</body>
</html>
五、插槽
1、slot 插槽 可以理解为占位符
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<hello>
<h5>我是什么呢?</h5>
<h5>你是什么呢?</h5>
</hello>
</div>
<script src="./libs/vue.js"></script>
<script>
//slot 插槽 可以理解为占位符
var hello = {
template:`
<div>
我是组件内部的东西
<slot></slot>
<p>我也是组件内部的p标签</p>
</div>`
};
var app = new Vue({
el:'#app',
components:{
hello,
}
})
</script>
</body>
</html>
运行结果:
2、具名插槽
具名插槽 通过name可以为插槽设置一个名字 在其中填充内容的时候使用一个template标签
使用方法:v-slot:插槽名字
<body>
<div id="app">
<hello>
<h5>我是什么呢?</h5>
<h5>你是什么呢?</h5>
<template v-slot:f>
<h3>我是f中的内容</h3>
</template>
</hello>
</div>
<script src="./libs/vue.js"></script>
<script>
//slot 插槽 可以理解为占位符
//具名插槽 通过name可以为插槽设置一个名字 在其中填充内容的时候使用一个template标签
//v-slot:插槽名字
var hello = {
template:`
<div>
我是组件内部的东西
<slot></slot>
<p>我也是组件内部的p标签</p>
<slot name="f"></slot>
</div>`
};
var app = new Vue({
el:'#app',
components:{
hello,
}
})
</script>
</body>
运行结果:
3、vue中template标签
未使用template时
<body>
<div id="app">
<!-- 在vue中template节点在html中生成的时候不会显示出来 -->
<div v-for="i in 10">
<h5>我是一个人</h5>
<p>我是一个人的详细信息</p>
</div>
<!-- <hello>
<h5>我是什么呢?</h5>
<h5>你是什么呢?</h5>
<template v-slot:f>
<h3>我是f中的内容</h3>
</template>
</hello> -->
</div>
<script src="./libs/vue.js"></script>
<script>
//slot 插槽 可以理解为占位符
//具名插槽 通过name可以为插槽设置一个名字 在其中填充内容的时候使用一个template标签
//v-slot:插槽名字
var hello = {
template:`
<div>
我是组件内部的东西
<slot></slot>
<p>我也是组件内部的p标签</p>
<slot name="f"></slot>
</div>`
};
var app = new Vue({
el:'#app',
components:{
hello,
}
})
</script>
生成的html节点中多了一个div标签
使用template时
<body>
<div id="app">
<!-- 在vue中template节点在html中生成的时候不会显示出来 -->
<template v-for="i in 10">
<h5>我是一个人</h5>
<p>我是一个人的详细信息</p>
</template>
<!-- <hello>
<h5>我是什么呢?</h5>
<h5>你是什么呢?</h5>
<template v-slot:f>
<h3>我是f中的内容</h3>
</template>
</hello> -->
</div>
<script src="./libs/vue.js"></script>
<script>
//slot 插槽 可以理解为占位符
//具名插槽 通过name可以为插槽设置一个名字 在其中填充内容的时候使用一个template标签
//v-slot:插槽名字
var hello = {
template:`
<div>
我是组件内部的东西
<slot></slot>
<p>我也是组件内部的p标签</p>
<slot name="f"></slot>
</div>`
};
var app = new Vue({
el:'#app',
components:{
hello,
}
})
</script>
六、动态组件
通过component实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.container{
padding:0.5rem;
border:0.02rem solid deeppink;
}
</style>
</head>
<body>
<div id="app">
<!-- <a href="tel:119">拨打电话:119</a> -->
<!-- Javascript:void(0) 不刷新 -->
<a href="Javascript:void(0)" @click="currentComponent='home'">【首页】</a>
<a href="Javascript:void(0)" @click="currentComponent='list'">【列表】</a>
<a href="Javascript:void(0)" @click="currentComponent='about'">【关于我们】</a>
<a href="Javascript:void(0)" @click="currentComponent='me'">【个人中心】</a>
<br>
<div class="container">
<!-- 动态组件 使用is设置当前显示的组件内容 -->
<component :is = "currentComponent"></component>
</div>
</div>
<script src="./libs/vue.js"></script>
<script>
const Home = {
template:`<h1>我是Home</h1>`
};
const List = {
template:`<h1>我是List</h1>`
};
const About = {
template:`<h1>我是About</h1>`
};
const Me = {
template:`<h1>我是Me</h1>`
};
var app = new Vue({
el:'#app',
components:{
home:Home,
list:List,
about:About,
me:Me,
},
data:{
currentComponent:'home',
}
})
</script>
</body>
</html>
运行结果: