一.组件化开发
- 组件 (Component) 是 Vue.js 强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代 码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。
- 在vue中都是组件化开发的,组件化开发就是把一个完整的页面分割成一个一个的小组件
- 组件化开发 容易维护 可以复用
1.1.定义简单的组件
1.1.1语法:
Vue.component()里面有两个参数:
- 组件名称
- 是个对象 对组件的设置 包括html模板 数据侦听器等
Vue.component('first-component',{ template:'<h1>我爱我的祖国</h1>' })
1.1.2组件的使用:
<div id="app"> <first-component></first-component> </div>
组件不能写到#app的元素外面
1.1.3组件的重复使用
组件就是一个可以重复引用的Vue实例 可以在页面中引用多次
<div id="app"> <first-component></first-component> <first-component></first-component> <first-component></first-component> <first-component></first-component> <first-component></first-component> </div>
1.1.4定义的组件只能有一个根元素
案例:
下面定义组件中两个h1都输入根元素
Vue.component('aa',{ template:'<h1>爱我中华</h1><h1>建设我们的国家</h1>' })
可以改写成如下:
Vue.component('aa',{ template:' <div> <h1>爱我中华</h1> <h1>建设我们的国家</h1> </div> ' })
1.2.组件中的事件
组件就是可以重复使用的Vue实例 所以组件中也有Vue中的事件
data computed watch methods 以及生命周期钩子函数等 但是组件中没有el
组件中也可以为元素添加事件:
Vue.component('aa', { template: ` <div class="bottom"> <button @click="turnOn">我是测试按钮</button> </div> `, methods: { turnOn(){ console.log('我是组件中的事件') } } })
1.3.组件中的数据
组件中的data数据 与new Vue中的不同 为了保证组件中的数据是私有的所以组件中的data数据是一个返回函数 组件中的数据都在函数的作用域中 保证组件中的数据互不感染
data() { return { msg: '爱我中华' } }
二.组件通讯
因为组件对封闭的 但是在实际的开发中 组件之间的数据是相互依赖 需要相互传递的 具体来说组件通讯分为三大类:
- 父组件为子组件传递数据
- 子组件为父组件传递数据
- 非父子组件之间传递数据
2.1 父传子
父组件向子组件传递数据
- 在子组件中定义props属性 值为数组类似于data 但data中的数据来自本身 而props中的数据来自父组件
- 子组件使用模板中使用props中的属性和data中的用法相同
- 父组件通过props传值给子组件
输出结果为:
说明:
- 创建Vue实例 data中的数据msg为一个数组
- 创建组件 在整个项目中 2组件相对就是1的子组件
- 通过3方式前者msg为props值中的数据 后者msg为newVue中data中的数据
- 最后正是props中的属性也有data中的使用方法 将数据进行遍历在页面中
**注意:props负责获取父组件的传递过来的,props中的值是只读的,不允许修改 **
2.2 子传父
原理
- 父组件使用子组件时 在其中定义一个自定义事件 并且绑定父组件中的一个自定义函数 当事件被调用时执行自定义函数
- 子组件通过this$emit执行自定义事件
最终输出结果为3
- 在子组件中定义一个点击事件 触发时执行子组件中的dian函数 并且将参数传入函数中
- 在上面的函数中通过this.$emit(‘事件名称’,参数)调用3中的a自定义事件并且将参数传过去
- 当a事件被触发时 会执行4中的aaa自定义函数 同时获取参数 最终实现子组件向父组件传数据
实例改造
根据父子组件之间的数据传递实现产品列表的组件化开发
代码如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> div { width: 1000px; margin: 100px auto; text-align: center; } table { width: 900px; margin: 50px auto; border-collapse: collapse; } table, th, td { border: 1px solid rgb(218, 124, 17); } .color { background-color: rgb(26, 172, 152); } </style> </head> <body> <div id="app"> <atitle @tian="tian" :aatitle="aupda" @cha="cha"></atitle> <acontent :content="newcontent" @del="del" @upda="upda"></acontent> </div> <script src="./node_modules/vue/dist/vue.js"></script> <script> Vue.component('atitle', { props: ['aatitle'], data() { return { keyword: '' } }, template: `<div> 型号 <input type="text" v-model=aatitle.name> 价格 <input type="text" v-model=aatitle.may @keyup.enter="tian"> <button @click="tian">录入</button><br> <input type="text" v-model="keyword"> </div>`, methods: { tian() { this.$emit('tian', this.aatitle.name, this.aatitle.may) this.aatitle.name = '' this.aatitle.may = '' } }, watch: { keyword: { handler(newvalue) { this.$emit('cha', newvalue) }, immediate: true } } }) Vue.component('acontent', { props: ['content'], template: `<table> <tr> <th>编号</th> <th>机型</th> <th>价格</th> <th>时间</th> <th>操作</th> </tr> <tr v-show="this.content.length==0"> <td colspan="5">没有任何发布任何产品</td> </tr> <tr v-for="(item,index) in content"> <td>{{item.id}}</td> <td>{{item.name}}</td> <td>{{item.may}}</td> <td>{{item.time}}</td> <td> <a href="#"" @click.prevent="del(item.id)">删除</a> <a href="#"" @click.prevent='upda(item.id)'>编辑</a> </td> </tr> </table>`, methods: { del(id) { this.$emit('del', id) }, upda(id) { this.$emit('upda', id) } } }) const app = new Vue({ el: '#app', data: { newcontent: [], aupda: {}, isup: false, upid: '', content: [{ id: 1, name: '华为', may: 5000, time: Date.now() }, { id: 2, name: '小米', may: 6000, time: Date.now() }, { id: 3, name: '苹果', may: 4500, time: Date.now() }, { id: 4, name: '1+', may: 3000, time: Date.now() }, { id: 5, name: 'oppo', may: 2000, time: Date.now() }, { id: 6, name: '1+2', may: 8000, time: Date.now() }, { id: 7, name: '1+3', may: 12000, time: Date.now() } ] }, methods: { del(id) { let index = this.content.findIndex(item => { return item.id == id }) this.content.splice(index, 1) this.newcontent.splice(index, 1) }, tian(name, may) { if (this.isup) { let a = this.content.find(item => { return item.id == this.upid }) a.name = name a.may = may } else { let id = this.content.length - 1 < 0 ? 1 : this.content[this.content.length - 1].id + 1 let content = { id: id, name: name, may: may, time: Date.now() } this.content.push(content) this.newcontent.push(content) } }, cha(value) { this.newcontent = this.content.filter(item => { return item.name.includes(value) }) }, upda(id) { let a = this.content.find(item => { return item.id == id }) this.aupda = { name: a.name, may: a.may } this.isup = true this.upid = id } } }) </script> </body> </html>
2.3 非父子之间的组件通讯
原理:
通过一个空的Vue实例来传递数据
const bus =new Vue()
核心逻辑:
组件A给组件B传值:
- 组件A给bus注册一个事件,监听事件的处理程序
- 组件B触发bus上对应的事件,把 值当成参数来传递
- 组件A通过事件处理程序获取数据
最终点击h2控制台会输出2
- 创建1和2两个非父子组件以及3空vue实例bus
- 在1组件中 钩子函数created中通过bus.$on为bus自定义一个事件aa
- 在2组件中 当点击h2元素时触发dian函数 并且将值出过去
- 在2组件的dian函数中通过bus.$emit方触发1中的aa事件 并传参过去
- 当1中的aa事件被触发时会执行其中的函数并获取参数
实例:
通过非父子组件 实现开关灯案例
- 关闭状态:
开启状态:
代码如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> #app { width: 500px; height: 500px; margin: 100px auto; } .box { height: 200px; width: 200px; margin: 0 auto; background-color: black; border-radius: 50%; } .below { height: 200px; width: 400px; margin: 50px auto; } button { margin-left: 66px; width: 100px; height: 40px; } .on { background-color: rgb(160, 184, 25); } </style> </head> <body> <div id="app"> <zss></zss> <sgy></sgy> </div> <script src="./node_modules/vue/dist/vue.js"></script> <script> const bus = new Vue() Vue.component('zss', { data() { return { attribute: "on", state: false } }, created() { bus.$on('lamp', result => { this.state = result }) }, template: `<div class="box" :class="state?attribute:''"></div>` }) Vue.component('sgy', { template: `<div class="below"> <button @click="on">开灯</button> <button @click="off">关闭</button> </div>`, methods: { on() { bus.$emit('lamp', true) }, off() { bus.$emit('lamp', false) } } }) const app = new Vue({ el: '#app', data: { } }) </script> </body> </html>