一:组件化开发基础
1.组件是什么?有什么用?
组件就是:扩展 HTML 元素,封装可重用的代码
,目的是复用
例如:有一个轮播图,可以在很多页面中使用,一个轮播有js,css,html
组件把js,css,html放到一起,有逻辑,有样式,有html
组件的分类:
- 全局组件:可以放在根中
- 局部组件:
工程化开发之后:
1个组件 就是1个xx.vue
二:组件的注册方式
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></global> <ul> <li v-for="i in 4"> <global></global> </li> </ul> </div> </body> <script> // 创建1个组件对象(全局组件) Vue.component('global', {
// global是组件名
// template 是组件的html样式
// methods 是组件的方法
// data()函数 是存放组件的变量名,在组件中data函数中的变量名要以retun的方式定义
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 } } })
let vm = new Vue({ el: '#box', data: { isShow: true },
methods: { handleClick() { console.log('我被点击了 我是根组件') } } }) </script>
</html>
3. 定义局部组件
① 局部组件 放在 Vue实例(根组件) 中
<!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- 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> `, }) let 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>
② 局部组件 放在 全局组件 中
<!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- 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> <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>
注意点:
- 定义的组件(body中的位置)必须要放在Vue实例(这也是一个组件 根组件)中
- 局部组件 必须放在 全局组件/根组件 中,无法单独使用
二:组件编写方式 与 Vue实例的区别
Vue实例(其实,它也是1个组件,是1个根组件)
<!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> </ul> </div> </body> <script> let vm = new Vue({ el: '#box', data: { name: 'Darker', age: 18, }, methods: { handleClick() { alert('按钮被点击') } } }) </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- 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.自定义组件需要有1个 root element
,一般包裹在 1个div
中
2.父子组件的data
是无法共享的
- 这一点就像Docker的容器一样,是相互隔离的
- 就算父子的data中数据相同,拥有相同的方法,也是互不影响的
3.组件可以有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}}
<!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"> <!-- myName是自定义属性 --> <global myname="name" myage="18"></global> <global :myname="name" :myage="19"></global> <global :myname="'Ben'" :myage="20"></global> </div> </body> <script> // 创建1个组件对象(全局组件/子组件) Vue.component('global', { template: ` <div> <div style="background: rgba(255,104,104,0.7); padding: 5px;">全局组件/子组件</div> {{myname}} {{myage}} </div> `, props: ['myname', 'myage'] }) // 父组件 let vm = new Vue({ el: '#box', data: { name: 'darker' }, }) </script> </html>
属性验证
- 限制父传子的变量类型
props: {
myname: String,
isshow: Boolean
}
父传子时候注意以下区别
<global :myname="name" :myage="19" :isshow="'false'"></global> <global :myname="name" :myage="19" :isshow="false"></global> <global :myname="name" :myage="19" :isshow="isshow"></global>
实例
<!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"> <!-- myName是自定义属性 --> <!-- <global :myname="name" :myage="19" :isshow="'false'"></global>--> <global :myname="name" :myage="19" :isshow="false"></global> <global :myname="name" :myage="19" :isshow="isshow"></global> </div> </body> <script> // 创建1个组件对象(全局组件/子组件) Vue.component('global', { template: ` <div> <div style="background: rgba(255,104,104,0.7); padding: 5px;">全局组件/子组件</div> <button @click="handleClick">点我显示/隐藏</button> {{myname}} {{isshow}} </div> `, props: { myname: String, isshow: Boolean } }) // 父组件 let vm = new Vue({ el: '#box', data: { name: 'darker', 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"> <global @my_event="handleClick($event)"></global> </div> </body> <script> // 创建1个组件对象(全局组件/子组件) 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('my_event', 666, 777, this.name) } } }) // 父组件 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>
小案例
- 子组件有1个按钮和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"> <global @my_event="handleShow($event)"></global> <br> <div>父组件接收到的数据:{{name}}</div> </div> </body> <script> // 创建1个组件对象(全局组件/子组件) Vue.component('global', { template: ` <div> <input type="text" v-model="myText"> <button @click="handleClick">点我传数据</button> </div> `, data() { return { myText: '' } }, methods: { handleClick() { this.$emit('my_event', this.myText) } } }) // 父组件 let vm = new Vue({ el: '#box', data: { name: '' }, methods: { handleShow(a) { this.name = a } } }) </script> </html>
4.ref属性(也可以实现组件间通信:子和父都可以实现通信)
- ref放在
标签
上,拿到的是原生的DOM节点
- ref放在
组件
上,拿到的是组件对象
- 通过这种方式实现子传父(this.$refs.mychild.text)
- 通过这种方式实现父传子(调用子组件方法传参数)
<!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"> <input type="text" ref="myRef"> <button @click="handleButton">点我</button> </div> </body> <script> // 创建1个组件对象(全局组件/子组件) Vue.component('global', { template: ` <div> <input type="text" v-model="myText"> </div> `, data() { return { myText: '' } }, methods: { handleClick() { this.$emit('my_event', this.myText) this.$emit('my_event', this.innerHTML) } } }) // 父组件 let vm = new Vue({ el: '#box', data: { name: '' }, methods: { handleShow(a) { this.name = a }, handleButton() { console.log(this.$refs) console.log(this.$refs.myRef) console.log(this.$refs.myRef.value) } } }) </script> </html>
5.事件总线(不同层级的不通组件通信)
<!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.基本使用
<!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"> <ul> <li> <button @click="who='child1'">首页</button> </li> <li> <button @click="who='child2'">订单</button> </li> <li> <button @click="who='child3'">商品</button> </li> </ul> <component :is="who"></component> </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> </div> `, }, child2: { template: ` <div> <span style="border-bottom: 5px solid rgba(255,104,255,0.7)">我是订单</span> </div> `, }, child3: { template: ` <div> <span style="border-bottom: 5px solid rgba(104,255,104,0.7)">我是商品</span> </div> `, } } }) </script> </html>
1.首先什么是keep-alive?
keep-alive
是一个抽象组件:它自身不会渲染一个DOM元素
,也不会出现在
父组件链中;使用keep-alive包裹动态组件
时,会缓存不活动的组件
实例,而不是销毁
它们。简单理解就是:keep-alive用来缓存组件,避免多次加载相应的组件,减少性能消耗。
2.keep-alive的作用
通过设置了
keep-alive
,可以简单理解为从页面1跳转到页面2后,然后后退到页面1,只会加载缓存中之前已经渲染好的页面1,而不会再次重新加载页面1,以及不会再触发页面中的created等类似的钩子函数,除非自己重新刷新该页面1。
3.keep-alive的参数
Keep-alive
组件提供了 include 和 exclude 两个属性,允许组件有条件的进行缓存。
include: 字符串或正则表达式。只有匹配的组件会被缓存。
exclude: 字符串或正则表达式。任何匹配的组件都不会被缓存。
4.什么时候用到keep-alive,并且怎么使用
- 什么时候用到 keep-alive ?
如果需要
频繁切换路由
,这个时候就可以考虑用keep-alive
了,来达到避免数据的重复请求
的目的。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>keep-alive</title> <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.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> </div> `, }, child2: { template: ` <div> <span style="border-bottom: 5px solid rgba(255,104,255,0.7)">我是订单</span> </div> `, }, child3: { template: ` <div> <span style="border-bottom: 5px solid rgba(104,255,104,0.7)">我是商品</span> </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>
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>