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
使用介绍
事件bus :eventBus
,相当于所有组件共用一个事件中心,这个事件中心用来管理事件,当我们需要的时候就向事件中心发送或者接收事件。通过共享一个vue实例,使用该实例的 $on
以及 $emit
实现数据传递。
跨层级访问数据
任意组件只要导入 bus
(bus.js) 就可以随意,发送 与监听数据
使用场景(非父子关系)
隔代组件通信兄弟组件通信使用方法
1.新建 bus.js
在 utils 文件夹里新建bus.js
//导入vueimport 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') }
使用总结
常用于多层嵌套组件场景下兄弟组件或任意两个组件之间通讯。$on
事件 是不会自动清除销毁的,需要手动销毁,可以在beforeDestroy
中解绑监听,避免重复触发。适合简单场景下使用,太过复杂的场景建议使用 vuex
。 四、provide/inject传参
使用介绍
常用于多层嵌套组件封装,当后代组件需要用到顶层组件的数据方法时可以使用这个。provide
提供数据,所有子孙都可以通过 inject
注入数据,inject
接收父辈组件提供的数据。provide
和inject
依赖注入 ,跨层级访问(只读) 使用场景
隔代组件通信使用方法
使用总结
跨层级组件之间通信传递的属性是非响应的如果需要传递响应属性,采用函数的方式传入对象复杂组件不建议使用此方式传参,任意层级都能访问导致数据追踪比较困难五、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";//导入vueimport 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>
使用总结
任意组件之间通信,多组件之间通信跨路由组件之间通信刷新浏览器,vuex数据会丢失,可以采用vuex-persistedstate插件解决此问题适合场景复杂的大型项目,简单的业务场景不建议使用往期传送门
【Vue】Vue2生命周期详解
【Vue】axios的二次封装和使用(附详细代码)
【Vue】vue组件和vue插件的创建和使用(底部栏组件、Toast 和 Notify通知插件)
【Vue】 vue2路由搭建和搭建vue2脚手架(入门级)