六、组件
-
概念:
- 组件是可复用的 Vue 实例,且带有一个名字:我们可以把组件作为自定义元素来使用,
- 组件的出现就是为了拆分Vue实例的代码量
能够让我们以不同的组件来划分不同的功能模块
需要什么功能就去调用对应的模块即可 - 难点:为什么vue组件中的data要写成函数而不是对象?
- 问题:因为我们抽离出来的组件、是可以复用性的vue实例,在项目中会存在多个实例、如果data属性值是一个对象的时候。那么它所有的实例都会共享这些数据
- 解决:在js中函数具有独立的作用域块的特点、外部是无法访问其内部的变量,所以我们每个组件中的data属性是一个函数的时候、他们每个实例都会有自己的作用域空间、也就是独立的数据、每个实例之间不会互相影响,所以必须是一个函数
-
组件化和模块化的区别:
- 组件化:是从UI界面的角度进行划分的
根据封装的思想,把页面上可重用的 UI,结构封装为组件,从而方便项目的开发和维护。 - 模块化:是从代码的逻辑角度去划分的 方便代码分层开发保证每个功能模块的职能单一
- 组件化:是从UI界面的角度进行划分的
-
父传子的案例
- 通过自定义属性名如 :name=“fathernum”将数据传给子组件
- 再定义一个属性去接受父组件本身的数据
父组件
<template> <div> <h2>我是父组件</h2> <input type="text" v-model="content"> <button @click="fatherbtn">父之按钮</button> <!-- 给子组件绑定一个自定义属性 --> <son :name="new_content"></son> </div> </template> <script> import son from './son.vue'; export default{ components: { son }, data() { return { content: 1, new_content:0, }; }, methods: { fatherbtn() { this.content++ this.new_content = this.content }, }, } </script>
子组件
<template> <div> <h2>我是儿子组件</h2> <!-- 通过自定义属性名拿到数据 --> <p>接受值{{name}}</p> </div> </template> <script> export default{ // 接受子组件的自定义属性 props:['name'], } </script>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> <button @click="father">我是父元素{{fatherNum}}</button> <h2-children :name="fatherNum" :name2="fatherNum2" ></h2-children> </div> <script src="../../js/vue.js"></script> <script> Vue.component('h2-children',{ props:['name','name2'], template: `<div> <h2>我是子组件标题{{name}}</h2> <h2>我是子组件标题{{name2}}</h2> </div>`, data() { return { childrenNum: 0 }; }, }) new Vue({ el:'#app', data() { return { fatherNum: 0, fatherNum2:1 }; }, methods: { father() { this.fatherNum++ this.fatherNum2++ }, }, }) </script> </body> </html>
-
子传父案例
-
通过定义一个自定义事件
-
然后通过$emit(“自定义事件名”,子组件的值)
父组件的
<template> <div> <h2>我是父组件</h2> <p>接受子组件的值{{ fathermes }}</p> <!-- 自定义事件名 --> <Son2 @sendname="sendval"></Son2> </div> </template> <script> import Son2 from './son2.vue'; export default{ data() { return { fathermes: "value", }; }, components: { Son2 } ,methods: { // 自定义事件名来接受传值 sendval(message) { this.fathermes = message }, }, } </script>
子组件的
<template> <div> <h2>我子组件</h2> <input type="text" v-model="content"> <!-- 通过点击事件触发 --> <button @click="sonbtn">发送子组件的信息</button> </div> </template> <script> export default{ data() { return { content: 1, }; }, methods: { // 通过点击事件触发自定义事件 sonbtn() { // 通过自定义事件名来查找 this.$emit('sendname', this.content); }, }, } </script>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> <h2>我是父组件{{fatherNum}}</h2> <button-counter @on-counter="getCounter"></button-counter> </div> <script src="../../js/vue.js"></script> <script> // 子组件 Vue.component('button-counter',{ // 子组件内容,必须是一个元素 template: `<div> <button @click="children">我是子组件按钮{{childreNum}}</button> </div>`, data() { return { childreNum: 0, }; }, methods: { // 点击按钮的时候,触发这个事件函数 children() { // 子元素的值加一 this.childreNum++ // 通过emit,和自定义事件on-counter,把this.childreNum作为实参传给getCounter this.$emit('on-counter', this.childreNum); }, }, }) // 父组件 new Vue({ el:'#app', data() { // 父元素的值 return { fatherNum: 0, }; }, methods:{ getCounter(counter){ // 更改父元素的值,将形参的值传给它 this.fatherNum = counter } } }) </script> </body> </html>
-
-
兄弟组件的实现方式
-
在文件components里面创建连个文件夹
-
Brother1
-
Brother2
-
-
在文件main.js里面引入刚才的两个组件
原文件
import Vue from 'vue' import App from './App.vue' Vue.config.productionTip = false new Vue({ render: h => h(App) }).$mount('#app')
引入后文件
import Vue from 'vue' import App from './App.vue' // 引入 import Brother1 from './components/Brother1' import Brother2 from './components/Brother2' //全局注册 Vue.component('Brother1',Brother1) Vue.component('Brother2',Brother2) Vue.config.productionTip = false new Vue({ render: h => h(App) }).$mount('#app')
-
创建eventBus.js
-
目录结构(如下)
- src
- utils
- eventBus.js
- utils
- src
-
代码如下
import Vue from 'vue' export const vm = new Vue()
-
-
两个兄弟组件代码
- Brother1.vue
<template> <div> <!-- 第一个兄弟组件 --> <h2>兄弟接受者</h2> <p>接受值:{{message}}</p> </div> </template> <script> // 兄弟都引用这个vue实例 import {vm} from '../utils/eventBus' export default{ data() { return { message: '', }; }, created(){ // 订阅者:订阅on-send桥梁的信息 vm.$on('on-send',(content)=>{ // 形参为发布者的content数据 this.message = content }) } } </script>
2.Brother2.vue
<template> <div> <h2>兄弟发布者</h2> <input type="text" v-model="content"> <button @click="onSend">发送信息</button> </div> </template> <script> // 兄弟都要应用这个实例 import {vm} from '../utils/eventBus' export default{ data() { return { content: '', }; }, methods: { onSend() { //自定义事件,把input里面的数据发送出去 vm.$emit('on-send',this.content) }, }, } </script>
-
引用两个兄弟组件
<template> <div id="app"> <Brother1></Brother1> <Brother2></Brother2> </div> </template> <script> import HelloWorld from './components/HelloWorld.vue' import Brother1 from './components/Brother1.vue' export default { name: 'App', components: { HelloWorld, Brother1 } } </script> <style lang="scss"> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>