-Vue- 组件
一:组件化开发基础
1 组件是什么?有什么用?
组件就是:扩展 HTML 元素,封装可重用的代码,目的是复用,例如:有一个轮播图,可以在很多页面中使用,一个轮播有js,css,html放到一起,有逻辑,有样式,有html。
组件的分类:
- 全局组件:可以放在根中
- 局部组件:
工程化开发之后:
- 一个组件就是一个 xx.vue
2 组件的注册方式
2.1 定义全局组件,绑定时间,编写样式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>全局组件</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="box">
<div @click="handleClick">我是根部组件</div>
<global v-if="isShow"></global> <!--归vue实例管-->
<ul>
<li v-for="i in 4">
<global></global> <!--复用-->
</li>
</ul>
</div>
</body>
<script>
// 创建1个组件对象(全局组件)
Vue.component('global', {
template: `
<div>
<div style="background: rgba(255,104,104,0.7); padding: 5px;" @click="handleClick">我是头部组件</div>
<div v-if="isShow">显示消失</div>
</div>
`,
methods: {
handleClick() {
console.log('我被点击了')
this.isShow = !this.isShow
}
},
data() {
return {
isShow: true
}
}
})
// 组件与Vue实例是隔离的,所以点击事件绑定的是同一个变量名是没有关系的
// 一个是对根部部件的点击事件,一个是对头部部件的点击事件
var vm = new Vue({
el: '#box',
data: {
isShow: true
},
methods: {
handleClick() {
console.log('我被点击了 我是根组件')
}
}
})
</script>
</html>
2 定义局部组件
2.1 局部组件 放在Vue实例(根组件)中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../Vue/vue.js"></script>
</head>
<body>
<div id="box" style="max-width: 300px">
<local></local>
<global></global>
</div>
</body>
<script>
// 创建1个组件对象 (全局组件)
Vue.component('global',{
template:`
<div>
<div style="background: rgba(255,104,104,0.7); padding: 5px 10px; border-radius: 5px;margin: 5px 0;">
我是全局组件
</div>
</div>
`,
})
var vm = new Vue({
el: '#box',
data: {},
// 创建1个组件对象 (局部组件)
components: {
local: { // local 组件名
template:`
<div>
<div style="background: rgba(104,255,104,0.7); padding: 5px 10px; border-radius: 5px; margin: 3px 50px 3px 0;"
@click="handleClick">我是局部组件
</div>
</div>
`, // 局部组件模板
methods: {
handleClick() {
console.log('我被点击了')
}
}
}
}
})
</script>
</html>
2.2 局部组件 放在全局组件中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../Vue/vue.js"></script>
</head>
<body>
<div id="box" style="max-width: 300px">
<ul>
<li v-for="i in 3">
<global>
</global>
</li>
</ul>
</div>
<script>
// 创建全局组件
Vue.component('global',{
template:`
<div>
<div style="background: rgba(255,104,104,0.7); padding: 5px 10px; border-radius: 5px;margin: 5px 0;">
我是全局的组件
</div>
<local></local>
<local></local>
<br>
</div>`,
// 创建局部组件,<local> 为该局部组件的标识
components: {
local: {
template: `
<div>
<div style="background: rgba(104,255,104,0.7); padding: 5px 10px; border-radius: 5px; margin: 3px 50px 3px 0;">我是局部组件</div>
</div>
`,
}
}
})
let vm = new Vue({ // 全局组件依赖于Vue实例
el: '#box',
})
</script>
</body>
</html>
2.3 注意点
- 定义的组件(body中的位置)必须要放在Vue实例(这也是一个组件 根组件)中。
- 局部组件必须放在全局组件/根组件中,无法单独使用。
三:组件编写方式 与 Vue实例的区别
Vue实例
- 其实,它也是一个组件,是一个根组件,并且可以在其中布置局部组件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="box">
<ul>
<li>字符串:{{name}}</li>
<li>数值:{{age}}</li>
<li><button @click="handleClick()">Click Here</button></li>
<li><local></local></li>
</ul>
</div>
</body>
<script>
let vm = new Vue({
el: '#box',
data: {
name: 'Darker',
age: 18,
},
methods: {
handleClick() {
alert('按钮被点击')
}
},
components: {
local: {
template: `
<div @click="handleClick" v-if="isShow"> 我是根组件下的局部组件 </div>`,
methods: {
handleClick() {
alert('局部组件被点击了')
this.isShow=!this.isShow
}
},
data() {
return {
isShow: true
}
}
}
}
})
</script>
</html>
组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>局部组件</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="box" style="max-width: 300px">
<ul>
<li v-for="i in 3">
<global></global>
</li>
</ul>
</div>
</body>
<script>
// 创建1个组件对象(全局组件)
Vue.component('global', {
template: `
<div>
<div style="background: rgba(255,104,104,0.7); padding: 5px 10px; border-radius: 5px;margin: 5px 0;">
我是全局组件
</div>
<local></local>
<br>
</div>
`,
// 创建1个组件对象(局部组件)
components: {
local: {
template: `
<div>
<div style="background: rgba(104,255,104,0.7); padding: 5px 10px; border-radius: 5px; margin: 3px 50px 3px 0;">我是局部组件</div>
</div>
`,
}
}
})
let vm = new Vue({
el: '#box',
})
</script>
</html>
区别
-
自定义组件需要有1个
root element
,一般包裹在 1个div
中
-
父子组件的
data
是无法共享的-
这一点就像Docker的容器一样,是相互隔离的,只要是组件就隔离,不论是子组件与父组件,还是子组件与子组件,或父组件与父组件,即只要是组件就是隔离的。
-
就算父子的data数据相同,拥有相同的方法,也是互不影响的。
-
就像是在不同名称空间下的函数,并不会互相影响。
-
组件可以有data、methods、computed....,但是
data
必须是一个函数
-
Vue实例:data是1个键值对,用来存放属性的。
var vm = new Vue({ el: '#box', data: { isShow: true } })
- 组件:data是1个函数,需要有返回值(return)
Vue.component('global', { template: ` <div> <div style="background: rgba(255,104,104,0.7); padding: 5px;" @click="handleClick">我是头部组件</div> <div v-if="isShow">显示消失</div> </div> `, methods: { handleClick() { console.log('我被点击了') this.isShow = !this.isShow } }, data() { return { isShow: true } } })
-
四:组件通信
1 父传子
- 在全局组件中自定义属性:
<global :myname="name" :myage="19"></global>
- 在组件中获取:{{myname}} ——props: ['myname', 'myage']
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../Vue/vue.js"></script>
</head>
<body>
<div id="box">
<global myname="name" myage="18"></global> <!-- 这里 myname="name" 定义的name为字符串-->
<global :myname="name" :myage="19"></global> <!-- 这里是js语法,也就是js变量-->
<global :myname="'Ben'" :myage="20"></global><!-- 这里是js语法,不过定义的是字符串-->
</div>
</body>
<script>
Vue.component('global', {
template: `
<div>
<div style="background: rgba(255,104,104,0.7); padding: 5px;">全局组件/子组件</div>
{{myname}}
{{myage}}
</div>
`,
props: ['myname', 'myage'] // 写入父传子的数据的key值
})
let vm = new Vue({
el: '#box',
data :{
name: 'darker'
}
})
</script>
</html>
属性验证
-
限制父传子的变量类型
props: { myname: String, isshow: Boolean }
-
父传子的时候注意以下区别
<global myname="name" myage="18"></global> <!-- 这里 myname="name" myage="18"定义的name为html的字符串--> <global :myname="name" :myage="19"></global> <!-- 这里是js语法,也就是myname是js变量,myage是整形--> <global :myname="'Ben'" :myage="20"></global> <!-- 这里是js语法,不过myname定义的是字符串,myage定义的是整形-->
-
实例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="../Vue/vue.js"></script> </head> <body> <div id="box"> <global :myname="name" :myage="19" :myshow="isShow"></global> <!-- 这里是js语法,也就是js变量--> <global :myname="'Ben'" :myage="20" :myshow="false"></global><!-- 这里是js语法,不过定义的是字符串--> </div> </body> <script> Vue.component('global', { template: ` <div> <div style="background: rgba(255,104,104,0.7); padding: 5px;">全局组件/子组件</div> {{myname}} {{myshow}} </div> `, props: { myname: String, myshow: Boolean, } }) let vm = new Vue({ el: '#box', data :{ name: 'darker', isShow: true } }) </script> </html>
2 子传父 (控制子组件的显示和隐藏)
点击子组件,就会触发父组件的某个函数执行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../Vue/vue.js"></script>
</head>
<body>
<div id="box">
<global @myevent="handleClick"></global>
</div>
</body>
<script>
Vue.component('global', {
template: `
<div>
<div style="background: rgba(255,104,104,0.7); padding: 5px;">全局组件/子组件</div>
<button @click="handleNav">点我</button>
</div>
`,
data() {
return {
name: 'Darker'
}
},
methods: {
handleNav() {
console.log('我是子组件函数')
this.$emit('myevent',this.name,666,777)
}
}
})
let vm = new Vue({
el: '#box',
data: {},
methods: {
handleClick(a, b, c) {
console.log('我是父组件函数')
console.log(a)
console.log(b)
console.log(c)
}
}
})
</script>
</html>
小案例
- 子组件有一个按钮和一个输框,子组件输入完内容后,数据在父组件中展示。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../Vue/vue.js"></script>
</head>
<body>
<div id="box">
<global @myevent="handleClick"></global>
<br>
<div>父组件接收到:{{ myText }}</div>
</div>
</body>
<script>
Vue.component('global', {
template: `
<div>
<input type="text" v-model="myText">
<button @click="handleClick">点我传数据</button>
</div>
`,
data() {
return {
myText: ''
}
},
methods: {
handleClick() {
console.log('我是子组件函数')
this.$emit('myevent', this.myText)
}
}
})
let vm = new Vue({
el: '#box',
data: {
myText: ''
},
methods: {
handleClick(a) {
console.log('我是父组件函数')
console.log(a)
this.myText = a
}
}
})
</script>
</html>
因为是在两个不同的组件取同样的Js变量名,所以并不会冲突。
3 ref属性 (也可以实现组件间通信:子和父都可以实现通信)
- ref放在 标签 上,拿到的是 原生的DOM节点。
- ref放在 组件 上,拿到的是 组件对象。
- 通过这种方式实现子传父 (this.$refs.mychild.text)
- 通过这种方式实现父传子(调用子组件方法传参数)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../Vue/vue.js"></script>
</head>
<body>
<div id="box">
<!-- 通过ref,获取input的值-->
<input type="text" ref="mytext">
<button @click="handleClick">点我</button>
<child ref="mychild"></child>
<hr>
<h3>根部件获取子部件数据</h3>
<p>子部件的数据:{{ myText }}</p>
<hr>
<h3>ref 放在标签上获得的数据</h3>
<p>{{ sign }}</p>
</div>
</body>
<script>
Vue.component('child', {
template: `
<div>
<h3>子部件获取根部件数据</h3>
<p>根部件的数据:{{ myText }}</p> </div>`,
data() {
return {
text: '子组件数据',
myText: ''
}
},
methods: {
add(a) {
console.log(a) // 得到父组件传来的数据
this.myText = a
}
}
})
var vm = new Vue({
el: '#box',
data: {
text: '父组件数据',
myText: '',
sign:''
},
methods: {
handleClick() {
console.log(this)
//this.$refs.mytext 获取到input控件,取出value值
// 该节点的数据值
this.sign = this.$refs.mytext.value
// 通过点击事件直接获得该组件对象的text
this.myText = this.$refs.mychild.text
// this.$refs.mychild.add()
this.$refs.mychild.add(this.text) // 通过该函数给该组件传参,传递数据
}
}
})
</script>
</html>
4 事件总线 (不同层级的不同组件通信)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="box">
<global1></global1>
<hr>
<global2></global2>
</div>
</body>
<script>
// 定义1个时间总线
let bus = new Vue({})
// 组件1
Vue.component('global1', {
template: `
<div>
<h3>组件1</h3>
<input type="text" v-model="myText">
<button @click="handleClick1">点我传递数据到另一个组件</button>
</div>
`,
data() {
return {
myText: ''
}
},
methods: {
handleClick1() {
console.log(this.myText)
bus.$emit('any', this.myText) // 通过事件总线发送
}
}
})
// 组件2
Vue.component('global2', {
template: `
<div>
<h3>组件2</h3>
收到的消息是:{{recvText}}
</div>
`,
data() {
return {
recvText: ''
}
},
mounted() { // 组件的挂载(生命周期钩子函数中的1个),开始监听时间总线上的:any
bus.$on('any', (item) => {
console.log('收到了', item,)
this.recvText = item
})
},
methods: {}
})
// 父组件
let vm = new Vue({
el: '#box',
data: {},
})
</script>
</html>
五:动态组件
1 基本使用
1 <component> 元素,动态地绑定多个组件到它的 is 属性
2 <keep-alive> 保留状态,避免重新渲染
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../Vue/vue.js"></script>
</head>
<body>
<div id="box">
<ul>
<li><a @click="who='child1'">首页</a></li> <!--已经在此绑定了点击事件的函数-->
<li><a @click="who='child2'">商品</a></li>
<li><a @click="who='child3'">购物车</a></li>
<hr>
<component :is="who"></component>
</ul>
</div>
</body>
<script>
var bus = new Vue() //new一个vue的实例,就是中央事件总线
Vue.component('child1', {
template: `<div>
首页
<input type="text">
</div>`,
})
Vue.component('child2', {
template: `<div>
商品
<input type="text">
</div>`,
})
Vue.component('child3', {
template: `<div>
购物车
<input type="text">
</div>`,
})
var vm = new Vue({
el: '#box',
data: {
who: 'child1'
},
})
</script>
</html>
2 keep-alive 的使用
keep-alive
可以让输入框内有的内容一致保持,不会因为切换而重置。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../Vue/vue.js"></script>
</head>
<body>
<div id="box">
<ul>
<li>
<button @click="who='child1'">首页</button>
</li>
<li>
<button @click="who='child2'">订单</button>
</li>
<li>
<button @click="who='child3'">商品</button>
</li>
</ul>
<keep-alive>
<component :is="who"></component>
</keep-alive>
</div>
</body>
<script>
let vm = new Vue({
el: '#box',
data: {
who: 'child1'
},
components: {
child1: {
template: `
<div>
<span style="border-bottom: 5px solid rgba(255,104,104,0.7)">我是首页</span>
<input type="text">
</div>
`,
},
child2: {
template: `
<div>
<span style="border-bottom: 5px solid rgba(255,104,255,0.7)">我是订单</span>
<input type="text">
</div>
`,
},
child3: {
template: `
<div>
<span style="border-bottom: 5px solid rgba(104,255,104,0.7)">我是商品</span>
<input type="text">
</div>
`,
}
}
})
</script>
</html>
六:slot 插槽
1 基本使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>slot 插槽</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="box">
<child>
<h6>Hello World</h6>
</child>
</div>
</body>
<script>
let vm = new Vue({
el: '#box',
data: {
who: 'child1'
},
components: {
child: {
template: `
<div>
<slot></slot>
<span style="border-bottom: 5px solid rgba(255,104,104,0.7)">我是组件的原内容</span>
<slot></slot>
</div>
`,
},
}
})
</script>
</html>
<h6>Hello World</h6> 自动找到了 部件中 <slot></slot> 然后插入
2 小案例(通过插槽实现在1个组件中控制另1个组件的显示隐藏)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>slot 插槽</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="box">
<!--通过插槽实现在一个组件中控制另一个组件的显示隐藏-->
<child1>
<button @click="isShow=!isShow">显示/隐藏组件2</button>
</child1>
<child2 v-if="isShow"></child2>
</div>
</body>
<script>
Vue.component('child1', {
template: `<div>
组件1
<slot></slot>
</div>`,
})
Vue.component('child2', {
template: `<div>
<h3>组件2</h3>
</div>`,
})
var vm = new Vue({
el: '#box',
data: {
isShow: true
}
})
</script>
</html>
3 具名插槽
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>具名插槽</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="box">
<!-- 具名插槽,把p标签给a插槽,div标签给b插槽-->
<child>
<p slot="a">我是具名插槽a插入的内容</p>
<div slot="b">我是具名插槽b插入的内容</div>
</child>
</div>
</body>
<script>
Vue.component('child', {
template: `<div>
<slot name="a"></slot>
<hr>
<span style="border-bottom: 5px solid rgba(255,104,104,0.7)">我是组件的原内容</span>
<hr>
<slot name="b"></slot>
</div>`,
})
var vm = new Vue({
el: '#box',
data: {}
})
</script>
</html>