Vue组件通信

六、组件

  • 概念:

    1. 组件是可复用的 Vue 实例,且带有一个名字:我们可以把组件作为自定义元素来使用,
    2. 组件的出现就是为了拆分Vue实例的代码量
      能够让我们以不同的组件来划分不同的功能模块
      需要什么功能就去调用对应的模块即可
    3. 难点:为什么vue组件中的data要写成函数而不是对象?
      1. 问题:因为我们抽离出来的组件、是可以复用性的vue实例,在项目中会存在多个实例、如果data属性值是一个对象的时候。那么它所有的实例都会共享这些数据
      2. 解决:在js中函数具有独立的作用域块的特点、外部是无法访问其内部的变量,所以我们每个组件中的data属性是一个函数的时候、他们每个实例都会有自己的作用域空间、也就是独立的数据、每个实例之间不会互相影响,所以必须是一个函数
  • 组件化和模块化的区别:

    • 组件化:是从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>
    
  • 兄弟组件的实现方式

在这里插入图片描述

  1. 在文件components里面创建连个文件夹

    • Brother1

    • Brother2

  2. 在文件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')
    
  3. 创建eventBus.js

    • 目录结构(如下)

      • src
        • utils
          • eventBus.js
    • 代码如下

      import Vue from 'vue'
      export const vm = new Vue()
      
  4. 两个兄弟组件代码

    1. 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>
    
  5. 引用两个兄弟组件

    <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>
    
    
在面试中回答 Vue 组件通信相关问题,可从组件通信背景、常见方式、最佳实践、常见问题等方面阐述,以下是详细要点及示例: ### 组件通信背景 Vue 组件通信在实际项目中非常重要,是开发者面试中的高频考点。掌握好组件通信技巧,能提升代码的可维护性和应用性能 [^1][^3]。 ### 常见通信方式 - **Props 和 Emit**:Props 用于父组件子组件传递数据,Emit 用于子组件触发父组件的自定义事件。示例代码如下: ```vue <!-- 父组件 --> <template> <div> <child-component :message="parentMessage" @childEvent="handleChildEvent"></child-component> </div> </template> <script> import ChildComponent from &#39;./ChildComponent.vue&#39;; export default { components: { ChildComponent }, data() { return { parentMessage: &#39;Hello from parent&#39; }; }, methods: { handleChildEvent(data) { console.log(&#39;Received data from child:&#39;, data); } } }; </script> <!-- 子组件 --> <template> <div> <p>{{ message }}</p> <button @click="sendDataToParent">Send data to parent</button> </div> </template> <script> export default { props: [&#39;message&#39;], methods: { sendDataToParent() { this.$emit(&#39;childEvent&#39;, &#39;Hello from child&#39;); } } }; </script> ``` - **Provide/Inject**:适用于跨层级组件通信,父组件通过 provide 提供数据,后代组件通过 inject 注入数据。示例代码如下: ```vue <!-- 祖先组件 --> <template> <div> <descendant-component></descendant-component> </div> </template> <script> import DescendantComponent from &#39;./DescendantComponent.vue&#39;; export default { components: { DescendantComponent }, provide() { return { sharedData: &#39;Shared data from ancestor&#39; }; } }; </script> <!-- 后代组件 --> <template> <div> <p>{{ sharedData }}</p> </div> </template> <script> export default { inject: [&#39;sharedData&#39;] }; </script> ``` - **Vuex/Pinia 状态管理**:用于管理应用的全局状态,多个组件可以共享和修改这些状态。以 Vuex 为例,示例代码如下: ```javascript // store/index.js import Vue from &#39;vue&#39;; import Vuex from &#39;vuex&#39;; Vue.use(Vuex); export default new Vuex.Store({ state: { count: 0 }, mutations: { increment(state) { state.count++; } }, actions: { incrementAsync({ commit }) { setTimeout(() => { commit(&#39;increment&#39;); }, 1000); } }, getters: { doubleCount(state) { return state.count * 2; } } }); // 组件中使用 <template> <div> <p>Count: {{ $store.state.count }}</p> <p>Double Count: {{ $store.getters.doubleCount }}</p> <button @click="$store.commit(&#39;increment&#39;)">Increment</button> <button @click="$store.dispatch(&#39;incrementAsync&#39;)">Increment Async</button> </div> </template> <script> export default { // ... }; </script> ``` - **EventBus(不推荐)**:创建一个全局的事件总线对象,组件可以通过它发布和订阅事件。示例代码如下: ```javascript // event-bus.js import Vue from &#39;vue&#39;; export const eventBus = new Vue(); // 发送事件的组件 import { eventBus } from &#39;./event-bus.js&#39;; export default { methods: { sendEvent() { eventBus.$emit(&#39;customEvent&#39;, &#39;Event data&#39;); } } }; // 接收事件的组件 import { eventBus } from &#39;./event-bus.js&#39;; export default { created() { eventBus.$on(&#39;customEvent&#39;, (data) => { console.log(&#39;Received event data:&#39;, data); }); } }; ``` ### 最佳实践 - **父子组件通信**:优先使用 Props 和 Emit 进行数据传递和事件触发 [^1][^3]。 - **跨组件通信**:对于跨层级组件,可使用 Provide/Inject;对于多个组件共享状态,使用 Vuex 或 Pinia [^1][^3]。 ### 常见问题 - **Props 单向数据流**:Props 是单向绑定的,子组件不能直接修改父组件传递的 Props 值,应通过 Emit 触发父组件的事件来修改 [^1]。 - **组件解耦**:使用状态管理库(如 Vuex、Pinia)可以实现组件之间的解耦,提高代码的可维护性 [^1][^3]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端达闻西

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值