手牵手学习vue之组件通信方案及案例

前言

组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。一般来说,组件可以有以下几种关系:  

                                                          image

如上图所示,A 和 B、B 和 C、B 和 D 都是父子关系,C 和 D 是兄弟关系,A 和 C 是隔代关系(可能隔多代)。

  • props/$emit

父组件A通过props的方式向子组件B传递,B to A 通过在 B 组件中 $emit, A 组件中 v-on 的方式实现。

//父组件
<template>
  <div>
    <button @click="fatherHandleClick">父向子传值</button>
    <Child
      :message="message"
      @onChatting="chatHandleClick"
      :callback="callback"
    />
  </div>
</template>

<script>
import Child from "./child";
export default {
  name: "HelloWorld",
  components: { Child },
  data() {
    return {
      message: "关于未来,您想对你的儿子说些什么?"
    };
  },
  methods: {
    fatherHandleClick() {
      this.message = "儿子 永远相信美好的事情会发生";
    },
    chatHandleClick(val) {
      this.message = val;
    },
    callback() {
      return true;
    }
  }
};
</script>
//子组件
<template>
  <div>
    <button @click="childHandleClick">子修改父值</button>
    {{ message }}
  </div>
</template>
<script>
export default {
  name: "",
  props: {
    message: {
      type: String
    },
    callback: {
      type: Function
    }
  },
  data() {
    return {};
  },
  methods: {
    childHandleClick() {
      console.log(this.callback());
      this.$emit(
        "onChatting",
        "谢谢爸爸,我会对未来充满期待,永远相信美好的事情正在到来!"
      );
    }
  }
};
</script>

总结:父组件通过props向下传递数据给子组件。注:组件中的数据共有三种形式:data、props、computed

 

  • 依赖注入provide&inject

解决的场景问题:

  根父组件A有一个方法getMap,该组件A下的所有子组件B,子组件C,子组件D,或者子组件B下的子组件E等层层嵌套情况下,在某种情况下,都需要访问父组件的getMap方法,那么常用的方法是子组件B:this.$parent.getMap(),组件E则是:this.$parent.$parent.getMap()以此类推;这种情况下,使用 $parent 属性无法很好的扩展到更深层级的嵌套组件上。

 

官方描述:这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。

provide 选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的 property。

inject 选项应该是一个字符串数组或者是一个对象,对象的 key 是本地的绑定名。

// 根组件
import father from "./components/father.vue";

export default {
  name: "App",
  components: {
    father,
  },
  data() {
    return {
      message: "我是被子节点需要的",
    };
  },
  //注入
  provide: function() {
    return {
      message: this.message, //变量
      modifyTheme: this.modifyTheme //方法
    };
  },
  methods: {
    modifyTheme(message) {
      console.log(`我现在的数据是由${msg}传给我的`);
    }
  }
};
</script>
//父组件
<template>
  <div>
    <button @click="fatherHandleClick">父向子传值</button>
    <div>父组件==={{ message }}</div>
    <Child />
  </div>
</template>
<script>
import Child from "./child";
export default {
  name: "",
  components: {
    Child,
  },
  inject: ["message" , "modifyTheme"], //接收根节点定义的变量、方法
  methods: {
    fatherHandleClick() {
      this.modifyTheme("父组件");
    },
  }
};
</script>
//子组件
<template>
  <button @click="fatherHandleClick">子向父传值</button>
  <div>子组件=={{message}}</div>
</template>
<script>
export default {
  name: "",
  inject: ["message" , "modifyTheme"],
  data() {
    return {};
  },
  methods: {
    childHandleClick() {
      this.modifyTheme("子组件");
    },
  },
};
</script>

总结:provide 和 inject 主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。 

 

  • vuex

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

什么情况下我应该使用 Vuex?

如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。

vuex核心概念

State共享状态(即变量)
Getter基于state的派生状态,可理解为组件中的计算属性
Mutation更改vuex的store中状态的唯一方法,通过提交mutation修改状态,同步操作(规则上是不允许异步操作的,虽然异步也可以执行,但是对devtool调试的状态跟踪或多个状态更改操作相互依赖是很不好的,所以不要觉得只要不报错我就可以这么用,还是尽量按照规则来比较好)
Action类似mutation,不同之处,1.通过提交mutation修改状态   2.支持异步操作
Module模块,在大型项目中为了方便状态的管理和协作开发将store拆分为多个子模块(modules),每个子模块拥有完整的state、mutation、action、getter

如何安装vuex

yarn add vuex
//或者
npm install vuex -S

运用步骤

  • 新建store文件夹,暴露index.js
import Vue from "vue";
import Vuex from "vuex";
import common from "./common";
import shop from "./shop";

Vue.use(Vuex);

export default new Vuex.Store({
  modules: {
    common,
    shop
  }
});
  • 将store注入到入口文件main.js
import Vue from "vue";
import App from "./App";
import router from "./router";
import store from "./store";

Vue.config.productionTip = false;

new Vue({
  el: "#app",
  router,
  store,
  components: { App },
  template: "<App/>"
});
  • 分别在store目录新建common、shop文件夹并暴露住index.js
//common仓库

// 共享状态
const state = {
  age: 26,
  name: "吴亦凡"
};

const getters = {
  getNewName: state => `${state.name}喜欢用电音唱歌`
};

const mutations = {
  changeName(state, name) {
    state.name = name;
  }
};

const actions = {
  updateName({ commit }, payload) {
    commit("changeName", payload);
  }
};

export default {
  namespaced: true,
  state,
  getters,
  mutations,
  actions
};
//shop仓库

// 共享状态
const state = {
  name: "",
  numList: [1, 2, 3, 4, 5]
};

const getters = {
  getNumList: ({ numList }) => numList.map(item => item * 2)
};

const mutations = {
  asyncChangeName(state, name) {
    state.name = name;
  }
};

const actions = {
  asyncUpdateName({ commit, rootState }, payload) {
    console.log(rootState.common.age);
    commit("asyncChangeName", payload);
  }
};

export default {
  namespaced: true,
  state,
  getters,
  mutations,
  actions
};

注意:

  1. namespaced这是属性用于解决不同模块的命名冲突问题
  2. rootState可以帮助我们从其他模块中取值
  • 组件调用
/*
*    详解state、getters、mutations、actions的用法
*/


//state分模块
1、传统形式:this.$store.state.common.name
2、辅助函数:
             import { mapState } from "vuex";

             computed: {
                ...mapState("shop", ["name"]), //直接this.name使用
             }

//state不分模块
1、传统形式:this.$store.state.name
2、辅助函数:
             import { mapState } from "vuex";

             computed: {
                ...mapState(["name"]), //直接this.name使用
             }



========================================================================



//actions分模块
1、传统形式:this.$store.dispatch("shop/asyncUpdateName", "你好明天");
2、辅助函数:
            import { mapActions } from "vuex";
            
            methods: {
                ...mapActions("shop", ["asyncUpdateName"]),
                fatherHandleClick(){
                    this.asyncUpdateName('你好明天')
                }
            }

//actions不分模块
1、传统形式:this.$store.dispatch("asyncUpdateName", "你好明天");
2、辅助函数:
            import { mapActions } from "vuex";
            
            methods: {
                ...mapActions(["asyncUpdateName"]),
                fatherHandleClick(){
                    this.asyncUpdateName('你好明天')
                }
            }


========================================================================

//getters分模块
1、传统形式:this.$store.getters["common/getNewName"]
2、辅助函数:
             import { mapGetters } from "vuex";

             computed: {
                ...mapGetters("shop", ["getNumList"]), //直接this.getNumList使用
             }

//getters不分模块
1、传统形式:this.$store.state.getNewName
2、辅助函数:
             import { mapGetters } from "vuex";

             computed: {
                ...mapGetters(["getNumList"]), //直接this.name使用
             }


========================================================================



//mutations分模块
1、传统形式:this.$store.commit("shop/asyncChangeName", "你好明天");;
2、辅助函数:
            import { mapMutations } from "vuex";
            
            methods: {
                ...mapMutations("shop", ["asyncUpdateName"]),
                fatherHandleClick(){
                    this.asyncUpdateName('你好明天')
                }
            }

//mutations不分模块
1、传统形式:this.$store.commit("asyncChangeName", "明天你好");;
2、辅助函数:
            import { mapMutations } from "vuex";
            
            methods: {
                ...mapMutations(["asyncUpdateName"]),
                fatherHandleClick(){
                    this.asyncUpdateName('你好明天')
                }
            }

备注:组件通信方案还有很多,有需要可自行查阅资料。不足之处,欢迎批评指教!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值