Vue2组件传参总结来了(建议收藏)


前言

对于Vue来说组件的数据通信非常重要,面试中也是频繁出现,为了更加深入了解组件的数据通信,本文专门总结了一下组件之间通信的场景和通信方式如何实现。

组件通信大致有以下场景:

  • 父子组件通信
  • 隔代组件通信
  • 兄弟组件通信
  • 跨路由组件通信

接下来本文将介绍以下通信方式

一、props/emit传参

使用介绍

props/emit 传参是最基础的组件通信方式,父组件通过props 可以向子组件进行通信,子组件通过emit 向父组件进行通信。

使用场景

  • 父子组件通信

使用方法

1.父组件向子组件传参

通过在子组件中定义 props 参数,父组件传入子组件中定义的参数属性来实现通信。

//父组件
<template>
    < msg :word="word"></ msg>
  </template>
  <script>
  import Msg from './Msg.vue'

  export default {
    components: {
    msg
    },
    data() {
      return {
        word: '该做核酸了'
      }
    }
  }
  </script>


//子组件
 <template>
    <div>
      <span>{{word}}</span>
    </div>
  </template>
  <script>
  export default {
    props: {
     word: {
        type: String,
        default: 'test'
      }
    }
  }
  </script>
2.自组件向父组件传参

Vue 通过 on/emit 的方式来实现子组件向父组件传参,在子组件中使用 $emit 绑定一个自定义事件,当执行语句时,就会将参数传递给父组件;父组件通过$on 来监听子组件自定义的事件获取子组件传递的参数。

//父组件
<template>
    <msg :num="num" @add='add'></ msg>
  </template>
  <script>
  import Msg from './Msg.vue'

  export default {
    components: {
    	msg
    },
    data() {
      return {
        num:10
      }
    },
    methods:{
    	add(res){
    		this.num = res
    	}
    }
  }
  </script>


//子组件
 <template>
    <div>
      <button ></button>
    </div>
  </template>
  <script>
  export default {
    props: {
     num: {
        type: Number,
        default: 100
      }
    },
    methods:{
    	add(){
    		this.$emit('add',this.num + 1)
    	}
   	 }	
  }
  </script>

使用总结

最常见的父子组件通信方式
props支持参数验证
emit 只会触发父组件的监听事件
不适合多层次组件参数传递,需要逐层传递参数

二、$parent/ $children传参

使用介绍

通过 $patent / $children 可以拿到父子组件的实例 ,从而调用实例里的数据方法,实现父子组件通信

使用方法

1.引用父组件

在这里插入图片描述

2.引用子组件 $children 和 $refs

在这里插入图片描述

三、事件bus

使用介绍

  1. 事件bus :eventBus ,相当于所有组件共用一个事件中心,这个事件中心用来管理事件,当我们需要的时候就向事件中心发送或者接收事件。通过共享一个vue实例,使用该实例的 $on 以及 $emit 实现数据传递。

  2. 跨层级访问数据

  3. 任意组件只要导入 bus (bus.js) 就可以随意,发送 与监听数据

使用场景(非父子关系)

  • 隔代组件通信
  • 兄弟组件通信

使用方法

1.新建 bus.js

在 utils 文件夹里新建bus.js
在这里插入图片描述

//导入vue
import Vue from "vue";

// 导出vue 创建的空实例
var bus = new Vue();
export default bus;
2.发送数据

在要发送数据的组件中导入bus.js ,并使用bus.$emit

在这里插入图片描述

<template>
  <div id="app">
  </div>
</template>

<script>
import bus from "@/utils/bus";
export default {
  //App.vue 作为数据的提供方
  provide: {
    a: "明天要放假了", // 只要子孙元素都可以接收
  },
  name: "app",
  components: { TabsCom },
  data() {
    return {
      msg: "来自app",
    };
  },
  //在mounted 里面发送
  mounted() {
    setTimeout(() => {
      this.changeIt();
    }, 5000);
  },
  //在任意组件中都可以发送和接收
  methods: {
    changeIt() {
      this.msg = "下楼做核酸了 ";
      bus.$emit("msgchange", this.msg);
    },
  },
};
</script>
3.接收数据

在有接收数据的组件中导入 bus.js ,并使用bus.$on(注意this)

在这里插入图片描述

<template>
  <div class="nav" :style="{ backgroundColor: bg_color, color: text_color }">
    <div class="left" @click="$emit('left-click', $event)">
      <slot name="icon_left"></slot> {{ left_text }}
    </div>
    <div class="title">
      <slot name="title"></slot>
      {{ title }}
    </div>
    <div class="right" @click="$emit('right-click', $event)">
      <slot name="icon_right"></slot> {{ right_text }}
    </div>
  </div>
</template>

<script>
import bus from "@/utils/bus";
export default {
  //接收的地方要监听数据,$event 就是app页面发送的数据
  created() {
    bus.$on("msgchange", ($event) => {
      console.log("msgchange");
      this.myMsg = $event;
    });
  },

  data() {
    return {
      myColor: "#f30",
      myMsg: "",
    };
  },
  props: {
    text_color: {
      type: String,
      default: "#000",
    },
    bg_color: {
      type: String,
      default: "#fff",
    },
    title: {
      type: String,
      default: "",
    },
    left_text: {
      type: String,
      default: "返回",
    },
    right_text: {
      type: String,
      default: "",
    },
  },
};
</script>

  // 组件销毁时需要解绑监听
  beforeDestroy () {
    bus.$off('myMsg')
  }

使用总结

  1. 常用于多层嵌套组件场景下兄弟组件或任意两个组件之间通讯。
  2. $on 事件 是不会自动清除销毁的,需要手动销毁,可以在beforeDestroy中解绑监听,避免重复触发。
  3. 适合简单场景下使用,太过复杂的场景建议使用 vuex

四、provide/inject传参

使用介绍

  1. 常用于多层嵌套组件封装,当后代组件需要用到顶层组件的数据方法时可以使用这个。
  2. provide 提供数据,所有子孙都可以通过 inject注入数据,inject 接收父辈组件提供的数据。
  3. provideinject 依赖注入 ,跨层级访问(只读)

使用场景

  • 隔代组件通信

使用方法

在这里插入图片描述

使用总结

  1. 跨层级组件之间通信
  2. 传递的属性是非响应的
  3. 如果需要传递响应属性,采用函数的方式传入对象
  4. 复杂组件不建议使用此方式传参,任意层级都能访问导致数据追踪比较困难

五、Vuex全局数据共享

使用介绍

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态, 并以相应的规则保证状态以一种可预测的方式发生变化。

简单来说,Vuex 在大型,特大型,vue项目中做数据统一管理的;在vuex中存储的数据,每个组件都可以引用到,vuex中的数据发生变化,引用该数据的组件会自动更新。

Vuex包含以下几个部分:全局唯一的状态管理仓库(state),同步操作(mutations)、异步操作(actions)、缓存依赖(getters),模块(modules)

具体使用方法访问官网查看 ==> Vuex官网

什么数据可以存储在Vuex里面

  • 如果一个数据,需要在多个vuex的store中
  • 如:用户数据,购物车数据

为什么要把登录转换到Vuex中

  • 登录后成功的数可以全局共享
  • 为了重复利用,登录功能可能不止在登录页面
    • 首页点击弹框登录
    • 购物车 跳转前,弹框登录
    • 登录位置会有很多
  • 如果写在Vuex 只需要在登录地方,$store.dispatch('login',data) 方法就可以

使用场景

  • 隔代组件通信
  • 兄弟组件通信
  • 跨路由组件通信

使用方法

1.state
  • 存放状态
  • $store.state.xxx访问
    在这里插入图片描述
//定义vuex的数据地方
state:{
cartNum:10}

//在组件访问数据
​$store.state.cartNum
2.mutations
  • 定义修改数据的方法,改变状态的唯一方法
  • $store.commit(‘xxx’,data)
    在这里插入图片描述
mutations: {
    //修改state数据必须在mutations中的方法
    //方法名建议大写
     SET_CART_NUM(state, data) {
          //修改cartNum的值
           state.cartNum = data;
                }
        },
        
//在组件中访问mutations的方法
$store.commit('SET_CART_NUM',100)
3.actions
  • 定义异步延迟方法
  • $store.dispatch(‘xxx’,data)
    在这里插入图片描述
    actions: {
       //定义异步,网络延迟等方法
      //只能调用mutations,不能直接修改state
         getCartNum(context,data){
              //可以执行网络请求,等待延迟
             setTimeout(()=>{
        //等待4秒后执行mutations 的SET_CART_NUM方法
                              context.commit('SET_CART_NUM',data)
                        },4000)
                }
        },

//在组件中调用
$store.dispatch('getCartNum',33)
4.getters
  • 从现有state 数据计算出新的数据
  • $store.getters.xxx
    在这里插入图片描述
  getters: {
                //从现有数据计算新的数据,每个商品佣金是0.5元
                //fee 佣金会随着cartNum变化而变化
                fee:function(state){
                        return state.cartNum*0.5
                }
        },

//在组件中调用
$store.getters.fee

小案例

module 把vuex的数据分成多个子模块(添加子组件)

1.user.js
import { Login, Reg } from "@/api/user";
import $router from "@/router/index";
//导入vue
import Vue from "vue";
//导入插件
import Notify from "@/plugin/Notify";
//使用插件
Vue.use(Notify);
export default {
    state: {
        //用户信息
        userInfo: {
            name: "",
            score: 0,
        },
        //token 标识
        token: "",
    },
    mutations: {
        //负责修改用户信息
        SET_USERINFO(state, data) {
            //更新state 的userInfo
            state.userInfo = data;
            //本地存储用户信息
            localStorage.setItem("user", JSON.stringify(data));
        },
        //修改token
        SET_TOKEN(state, data) {
            state.token = data;
            //token 本来就是字符串,不需要
            localStorage.setItem("token", data);
        },
    },
    actions: {
        //页面退出
        logout(context) {
            context.commit("SET_USERINFO", {});
            context.commit("SET_TOKEN", "");
        },

        //负责登录  (登录是异步的需要放在actions中)
        login(context, data) {
            Login(data)
                .then((res) => {
                    //200 代表成功其他代表失败
                    if (res.data.code === 200) {
                        Notify.success(res.data.msg || "登录成功");
                        //登录成功弹出设置用户信息与设置token
                        context.commit("SET_USERINFO", res.data.user);
                        context.commit("SET_TOKEN", res.data.token);
                        //跳转到redirect 对应页面
                        //获取当前留言信息
                        var $route = $router.history.current;
                        //获取查询参数
                        var redirect = $route.query.redirect || "/";
                        //实现跳转
                        $router.replace(redirect);
                    } else {
                        //登录不成功清空用户信息
                        Notify.danger(res.data.msg || "登录失败");
                        context.commit("SET_USERINFO", {});
                        context.commit("SET_TOKEN", "");
                    }
                })
                //失败显示网络失败
                .catch((err) => {
                    console.log(err);
                    Notify.danger("网络失败");
                });
        },
};

2.使用
<template>
  <div>
    <h1>我的</h1>
    <p v-if="$store.state.user.userInfo.name">
      <label>
        <input type="file" name="file" ref="myfile" @change="fileChange()" />
        <img :src="user.avatar" width="100" class="avatar" />
      </label>
      {{ $store.state.user.userInfo.name }},积分:{{
        $store.state.user.userInfo.score
      }}
      <a href="" @click.prevent="$store.dispatch('logout')">退出</a>
    </p>
    <p v-else>
      <router-link to="/login?redirect=/user">登录</router-link> <br />
      <router-link to="/reg">注册</router-link>
    </p>
  </div>
</template>

使用总结

  1. 任意组件之间通信,多组件之间通信
  2. 跨路由组件之间通信
  3. 刷新浏览器,vuex数据会丢失,可以采用vuex-persistedstate插件解决此问题
  4. 适合场景复杂的大型项目,简单的业务场景不建议使用

往期传送门

【Vue】Vue2生命周期详解


【Vue】axios的二次封装和使用(附详细代码)


【Vue】vue组件和vue插件的创建和使用(底部栏组件、Toast 和 Notify通知插件)


【Vue】 vue2路由搭建和搭建vue2脚手架(入门级)


Logo

前往低代码交流专区

更多推荐