vue组件传值问题

vue组件通信

父子组件通讯原则

为了提高组件的独立性和重用性,父组件会通过props向下传数据给子组件,当子组件又事情要告诉父组件时用通过==$emit==事件告诉父组件,如此确保每个组件都是独立在相对隔离的环境中运行,可以大幅度提高组件的可维护性

兄弟组件通讯原则

EventBus又称事件总线,在Vue中可以使用EventBus来作为沟通桥梁的概念,就像是所有事件共用相同的事件中心,可以向该中心发送事件或接收事件,所有组件都可以上下平行的通知其他组件,但太方便若使用不慎,就会造成难以维护的灾难,因此才需要更完善的Vuex作为状态管理中心,将通知的概念上升到共享状态层次

如何使用EventBus

1.初始化

首先你需要做的是将事件总线并将其导出,以便其他模块可以使用或者监听它。我们可以用两种方式来处理:

第一种是,新建一个.js文件,比如:event-bus.js:

// event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()

第二种是,直接在项目的main.js种初始化Event Bus:

// main.js
Vue.prototype.$EventBus = new Vue()

这种方式初始化的 EventBus 是一个 全局的事件总线

注意,这种方式初始化的 EventBus 是一个 全局的事件总线 。稍后再来聊一聊全局的事件总线。

2.发送事件

假设你有两个Vue页面需要通信: A 和 B ,A页面 在按钮上面绑定了点击事件,发送一则消息,想=通知 B页面。

<!-- A.vue -->
<template>
    <button @click="sendMsg()">-</button>
</template>
 
<script> 
import { EventBus } from "../event-bus.js";
export default {
  methods: {
    sendMsg() {
      EventBus.$emit("aMsg", '来自A页面的消息');
    }
  }
}; 
</script>

接下来,我们需要在 B页面 中接收这则消息。

3.接收事件

<!-- B.vue -->
<template>
  <p>{{msg}}</p>
</template>
 
<script> 
import { EventBus } from "../event-bus.js";
export default {
  data(){
    return {
      msg: ''
    }
  },
  mounted() {
    EventBus.$on("aMsg", (msg) => {
      // A发送来的消息
      this.msg = msg;
    });
  }
};
</script>

同理我们也可以在 B页面 向 A页面 发送消息。这里主要用到的两个方法:

// 发送消息
EventBus.$emit(channel: string, callback(payload1,))
 
// 监听接收消息
EventBus.$on(channel: string, callback(payload1,))

​ 前面提到过,如果使用不善,EventBus 会是一种灾难,到底是什么样的“灾难”了?大家都知道vue是单页应用,如果你在某一个页面刷新了之后,与之相关的EventBus会被移除,这样就导致业务走不下去。还要就是如果业务有反复操作的页面,EventBus 在监听的时候就会触发很多次,也是一个非常大的隐患。这时候我们就需要好好处理 EventBus 在项目中的关系。通常会用到,在vue页面销毁时,同时移除EventBus 事件监听。

移除事件监听者

如果想移除事件的监听,可以像下面这样操作:

import { 
  eventBus 
} from './event-bus.js'
EventBus.$off('aMsg', {})

你也可以使用 EventBus.$off('aMsg') 来移除应用内所有对此某个事件的监听。或者直接调用 EventBus.$off() 来移除所有事件频道,不需要添加任何参数 。

上面就是 EventBus 的使用方法,是不是很简单。上面的示例中我们也看到了,每次使用 EventBus 时都需要在各组件中引入 event-bus.js 。事实上,我们还可以通过别的方式,让事情变得简单一些。那就是创建一个全局的 EventBus 。接下来的示例向大家演示如何在Vue项目中创建一个全局的 EventBus

<!-- B.vue -->

<template>

  <!-- 展示msgB -->
  <p>{{msgB}}</p>
  
</template>

<script> 
export default {
  data(){
    return {
      //初始化一个msgB
      msgB: ''
    }
  },
  mounted() {
    /*调用全局Vue实例中的$EventBus事件总线中的$on属性,监听A组件发送
    到事件总线中的aMsg事件*/
    this.$EventBus.$on("aMsg", (data) => {
      //将A组件传递过来的参数data赋值给msgB
      this.msgB = data;
    });
  },
  beforeDestroy(){
    //移除监听事件"aMsg"
  	this.$EventBus.$off("aMsg")
  }
};
</script>

vuex的使用详解

使用前提:vuex是使用vue中必不可少的一部分,基于父子,兄弟组件,我们传值可能会比较方便,但是没有关联的组件之间使用同一组数据,就显得无能为力,那么vuex就解决了这个问题

相当于一个公共的仓库,保存着所有组件都能用的公用数据

一、适合初学者使用,保存数据以及获取数据
1.在store文件夹,新建一个index.js文件

在新建的js文件中写入一下代码

import Vue from "vue"
import Vuex from "vuex"
 
Vue.use(Vuex);
 
export default new Vuex.Store({
    state:{
        pathName: "",
        currDbSource: {},
        currJobData: {},
        DbSource: []
    },
    mutations:{
        // 保存当前菜单栏的路径
        savePath(state,pathName){
            state.pathName = pathName;
        },
        // 保存当前点击的数据源
        saveCurrDbSource(state,currDbSource){
            state.currDbSource = currDbSource;
        },
        // 保存当前点击的元数据
        saveCurrJobData(state,currJobData){
            state.currJobData = currJobData;
        },
        // 保存所有数据源
        saveDbSource(state,DbSource){
            state.DbSource = DbSource;
        }
    }
})

这里解释一下各个代码段的作用:state是自定义的一些变量,需要用来保存数据,mutations来触发事件,相当于方法,用户需要通过触发这个方法,借此来保存数据,参数的话,第二个参数就是用户传入的值,然后在方法中赋值给state中的变量

2、main.js引用:(注意路径即可)
// 引入vuex-store
import store from './store/index';
 
new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App)
});
3、保存数据:(场景举例:当我点击按钮后,我需要把当前的数据保存到vuex中,然后跳转到别的路由,然后使用这些数据)
methods:{
    click(){
        // 点击按钮进行一些操作,然后保存数据
        this.$store.commit('saveCurrDbSource',this.db)
    }
}

这里的第一个参数是要触发的方法,也就是上面mutations中的方法,第二个参数是你要传递的数据

4、获取变量:(当数据初始获取不到时,可以使用计算属性用来获取)
this.$store.state.变量名
 
// 例如
this.store.state.currDbSource

这样其他组件就可以共用这个保存起来的数据了,也能进行相应的修改

二,分模块化开发
1.模块结构如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1l2SEujG-1612086600969)(E:\Typora笔记\imgs\1612083409403.png)]

路由如下 :

import Vue from 'vue'
import Router from 'vue-router'
import componentsA from '@/components/componentsA'
import componentsB from '@/components/componentsB'
 
Vue.use(Router)
 
export default new Router({
   mode: 'history',
    routes: [
        {
        path: '/',
        name: 'componentsA',
        component: componentsA
        },
        {
            path: '/componentsA',
            name: 'componentsA',
            component: componentsA
        },
        {
            path: '/componentsB',
            name: 'componentsB',
            component: componentsB
        }
    ]
})
app.vue
<template>
  <div id="app">
    <router-view/>
  </div>
</template>
 
<script>
export default {
  name: 'App'
}
</script>
 
<style>
#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>
2.开始使用vuex,新建一个 sotre文件夹,分开维护 actions mutations getters
  • 在store/index.js文件中新建vuex 的store实例

    *as的意思是 导入这个文件里面的所有内容,就不用一个个实例来导入了。

    import Vue from 'vue'
    import Vuex from 'vuex'
    import * as getters from './getters' // 导入响应的模块,*相当于引入了这个组件下所有导出的事例
    import * as actions from './actions'
    import * as mutations from './mutations'
     
    Vue.use(Vuex)
    // 首先声明一个需要全局维护的状态 state,比如 我这里举例的resturantName
    const state = {
        resturantName: '天下第一客栈' // 默认值
        // id: xxx  如果还有全局状态也可以在这里添加
        // name:xxx
    }
     
    // 注册上面引入的各大模块
    const store = new Vuex.Store({
        state,    // 共同维护的一个状态,state里面可以是很多个全局状态
        getters,  // 获取数据并渲染
        actions,  // 数据的异步操作
        mutations  // 处理数据的唯一途径,state的改变或赋值只能在这里
    })
     
    export default store  // 导出store并在 main.js中引用注册。
    
  • actions
    // 给action注册事件处理函数。当这个函数被触发时候,将状态提交到mutations中处理
    export function modifyAName({commit}, name) { // commit 提交;name即为点击后传递过来的参数,此时是 'A餐馆'
        return commit ('modifyAName', name)
    }
    export function modifyBName({commit}, name) {
        return commit ('modifyBName', name)
    }
     
    // ES6精简写法
    // export const modifyAName = ({commit},name) => commit('modifyAName', name)
    
  • mutations
    // 提交 mutations是更改Vuex状态的唯一合法方法
    export const modifyAName = (state, name) => { // A组件点击更改餐馆名称为 A餐馆
        state.resturantName = name // 把方法传递过来的参数,赋值给state中的resturantName
    }
    export const modifyBName = (state, name) => { // B组件点击更改餐馆名称为 B餐馆
        state.resturantName = name
    }
    
  • getters
    // 获取最终的状态信息
    export const resturantName = state => state.resturantName
    
3.在main.js中导入 store实例
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
 
Vue.config.productionTip = false
 
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,  // 这样就能全局使用vuex了
  components: { App },
  template: '<App/>'
})
4.在组件A中,定义点击事件,点击 修改 餐馆的名称,并把餐馆的名称在事件中用参数进行传递。

…mapactions 和 …mapgetters都是vuex提供的语法糖,在底层已经封装好了,拿来就能用,简化了很多操作。

其中…mapActions([‘clickAFn’]) 相当于this.$store.dispatch(‘clickAFn’,{参数}),mapActions中只需要指定方法名即可,参数省略。

…mapGetters([‘resturantName’])

相当于this.$store.getters.resturantName

<template>
  <div class="componentsA">
      <P class="title">组件A</P>
      <P class="titleName">餐馆名称:{{resturantName}}</P>
      <div>
            <!-- 点击修改 为 A 餐馆 -->
          <button class="btn" @click="modifyAName('A餐馆')">修改为A餐馆</button>
      </div>
      <div class="marTop">
          <button class="btn" @click="trunToB">跳转到B页面</button>
      </div>
  </div>
</template>
 
<script>
import {mapActions, mapGetters} from 'vuex'
export default {
  name: 'A',
  data () {
    return {
    }
  },
  methods:{
      ...mapActions( // 语法糖
          ['modifyAName'] // 相当于this.$store.dispatch('modifyName'),提交这个方法
      ),
      trunToB () {
          this.$router.push({path: '/componentsB'}) // 路由跳转到B
      }
  },
  computed: {
      ...mapGetters(['resturantName']) // 动态计算属性,相当于this.$store.getters.resturantName
  }
}
</script>
 
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
    .title,.titleName{
        color: blue;
        font-size: 20px;
    }
    .btn{
        width: 160px;
        height: 40px;
        background-color: blue;
        border: none;
        outline: none;
        color: #ffffff;
        border-radius: 4px;
    }
    .marTop{
        margin-top: 20px;
    }
</style>
B组件同理
<template>
  <div class="componentsB">
      <P class="title">组件B</P>
      <P class="titleName">餐馆名称:{{resturantName}}</P>
      <div>
          <!-- 点击修改 为 B 餐馆 -->
          <button class="btn" @click="modifyBName('B餐馆')">修改为B餐馆</button>
      </div>
      <div class="marTop">
          <button class="btn" @click="trunToA">跳转到A页面</button>
      </div>
  </div>
</template>
 
<script>
import {mapActions, mapGetters} from 'vuex'
export default {
  name: 'B',
  data () {
    return {
    }
  },
  methods:{
      ...mapActions( // 语法糖
          ['modifyBName'] // 相当于this.$store.dispatch('modifyName'),提交这个方法
      ),
      trunToA () {
          this.$router.push({path: '/componentsA'}) // 路由跳转到A
      }
  },
  computed: {
      ...mapGetters(['resturantName']) // 动态计算属性,相当于this.$store.getters.resturantName
  }
}
</script>
 
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
    .title,.titleName{
        color: red;
        font-size: 20px;
    }
    .btn{
        width: 160px;
        height: 40px;
        background-color: red;
        border: none;
        outline: none;
        color: #ffffff;
        border-radius: 4px;
    }
    .marTop{
        margin-top: 20px;
    }
</style>
</script>
 
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
    .title,.titleName{
        color: red;
        font-size: 20px;
    }
    .btn{
        width: 160px;
        height: 40px;
        background-color: red;
        border: none;
        outline: none;
        color: #ffffff;
        border-radius: 4px;
    }
    .marTop{
        margin-top: 20px;
    }
</style>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值