Vue组件通信

父子组件、兄弟组件间的数据传递、事件触发场景比较常见,总结出以下8种通信方法。
通信分类:A. 父子组件间的通信;B. 非父子组件间的通信

props 和$emit

适用于父子组件通信,父组件用props传参给子组件;子组件触发事件传递参数,父组件进行监听。

// 父组件
<template>
    <div class="parent">
        <comp-a :propA="parentValue" @eventA="handleEventA"></comp-a>
    </div>
</template>
<script>
import compA from '../../compA';
<script>
 export default {
  components: {
      compA,
  },   
  data() { 
      return { 
          parentValue: 'Hello World',
          message: ''
      } 
  },
  methods: {
      handleEventA(param) {
          this.message = param;
      }
  }
</script>

// 子组件
<template>
    <div id="compA">
        <p>{{propA}}</p>
        <button @click="changeText"></button>
    </div>
</template>
<script>
 export default {
  props: {
      propA: {
          type: String,
          default: '',
          required: true,
      },
  },   
  data() { 
      return {} 
  },
  methods: {
      changeText() {
          let paramA = 'ssss'
          this.$emit('eventA', paramA)
      }
  }
</script>
$children和 $parent

适用于父子组件通信。父组件有一个$children 的子组件数组,子组件有父组件对象 $parent。获取到的元素就是vue实例,推荐使用prop和 $emit通信。

provide 和inject

适用于父子、隔代组件传递数据。如字面意思,父组件“提供”数据,子组件“注入”数据。

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

注:provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。

// 父级组件提供 'foo'
var Provider = {
  provide: {
    foo: 'bar'
  },
  // ...
}

// 子组件注入 'foo'
var Child = {
  inject: ['foo'],
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}

// vue2.5.0+
const Child = {
  inject: {
    foo: {
      from: 'bar',  // 标注来源
      default: 'foo' // 默认值
    }
  }
}
ref 和refs

即通过refs获取对应定义ref值的组件实例。官网API示例:

<!-- `vm.$refs.p` will be the DOM node -->
<p ref="p">hello</p>

<!-- `vm.$refs.child` will be the child component instance -->
<child-component ref="child"></child-component>
EventBus

EventBus 又称为事件总线。在Vue中可以使用 EventBus
来作为沟通桥梁的概念,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件,所以组件都可以上下平行地通知其他组件,但也就是太方便所以若使用不慎,就会造成难以维护的灾难,因此才需要更完善的Vuex作为状态管理中心,将通知的概念上升到共享状态层次。
来源:https://www.jianshu.com/p/4fa3bf211785 作者:易水人去丶明月如霜

使用:

  1. 初始化
// 两种形式
// 1. 单独的js文件,应用时需要引入
// event-bus.js
import Vue from 'vue';
export const EventBus = new Vue()

// 2. 挂载到vue下
Vue.prototype.$eventBus = new Vue();

// 或者这么些
var EventBus = new Vue();
Object.defineProperties(Vue.prototype, {
    $eventBus: {
        get: function () {
            return EventBus
        }
    }
})

  1. 事件传递
    基于vue的$emit发布 和 $on监听模式,进行组件间的事件传递
var EventBus = new Vue();

this.$bus.$emit('nameOfEvent',{ ... pass some event data ...});

this.$bus.$on('nameOfEvent',($event) => {
    // ...
})
  1. 事件关闭
this.$bus.$off('nameOfEvent'); // 关闭单个
this.$bus.$off();  // 关闭所有
Vuex状态管理

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

Vuex是响应式的。相当于将公共状态抽离在单独的对象里,并运用固定的规则进行维护和管理。首先创建仓库store,预定义state用来存储状态,通过getters获取state中的状态,提交mutation修改state值,分发action执行逻辑操作。
逻辑梳理
官网示意图:
视图==>操作==>状态==>视图
图片转自https://vuex.vuejs.org/zh/
Vuex状态管理
图片转自https://vuex.vuejs.org/zh/

主要组成部分

  • store,状态管理器的仓库;
  • state,类似于实例中的data,不可在外部直接改写,用于数据存储驱动应用的数据源;
  • getters,类似于实例中的computed,提供格式化后的state数据;
  • mutations,类似于methods,定义更改state的入口,不支持异步逻辑;
  • actions,类似于methods,可以进行异步操作;
  • module,根据模块划分状态。

应用示例
在仓库文件store.js中定义对应的状态,并声明好获取、维护状态的操作

// ********** store.js********
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);

// 根据业务模块,可以分开定义状态模块
import draft from './draft';
import collaborator from './collaborator';

const state = {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
};

// 从 store 中的 state 中派生出一些状态
const getters = {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    },
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
};

// 更改 store 中的状态,通过 store.commit 调用, payload支持传参
const mutations = {
    increment (state,payload) {
      // 变更状态
      state.count++
    }
};

// 提交 mutation,可包含异步操作,通过 store.dispatch 调用
const actions = {
	incrementAsync ({ commit }) {
	    setTimeout(() => {
	      commit('increment')
	    }, 1000)
	}
};

export default new Vuex.Store({
    state,
    actions,
    getters,
    mutations,
    modules: {
        draft,
        collaborator,
    },
});

在main.js中进行状态管理器的注册

// ************main.js*********
import store from './store/store.js';

// 热重载
if (module.hot) {
  // 使 action 和 mutation 成为可热重载模块
  module.hot.accept(['./mutations', './modules/a'], () => {
    // 获取更新后的模块
    // 因为 babel 6 的模块编译格式问题,这里需要加上 `.default`
    const newMutations = require('./mutations').default
    const newModuleA = require('./modules/a').default
    // 加载新模块
    store.hotUpdate({
      mutations: newMutations,
      modules: {
        a: newModuleA
      }
    })
  })
}

new Vue({
    el: '#app',
    router,
    store,
    components: { App },
    template: '<App/>'
});

在界面应用处进行状态的引用与维护,state、getters、mutations和actions都有两种方式调用,一种是直接调用挂载在$store下的对应模块(mutations\actions需要commit和dispatch调用),都可以由载荷的形式进行传递;另一种是直接通过mapState、mapGetters、mapMutations、mapActions引入状态作为this下的参数和方法,直接进行调用。


// *************App.vue********
import { mapState, mapGetters, mapActions, mapMutations } from 'vuex';

export default {
	data: {
		...
	},
	computed: {
		...mapState({
			'todos': state => state.todos
		}),
	    ...mapGetters([
	      'doneTodos',
	    ])
		
	},
	mounted() {
		// 直接取值
		console.log(this.$store.state.todos);
		let specialTarget = this.$store.getters.getTodoById(2);
		this.$store.commit('increment', {
 			 amount: 10
		}); // 直接提交,另一种方法是在methods中引入,直接类似函数调用
		
		// 以对象形式分发
		this.$store.dispatch({
		  type: 'incrementAsync',
		  amount: 10
		})
		// 以载荷形式分发
		this.$store.dispatch('incrementAsync', {
		  amount: 10
		})
	},
	methods: {
		...mapMutations([
			‘increment’
		]),
		...mapActions([
			‘incrementAsync’
		]),
	},
	submit() {
		this.increment();
		this.incrementAsync();
	}
}

对应module的状态应用只是在引入的时候添加路径参数。

如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。

const store = new Vuex.Store({
  modules: {
    account: {
      namespaced: true,

      // 模块内容(module assets)
      state: { ... }, // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响
      getters: {
        isAdmin () { ... } // -> getters['account/isAdmin']
      },
      actions: {
        login () { ... } // -> dispatch('account/login')
      },
      mutations: {
        login () { ... } // -> commit('account/login')
      },

      // 嵌套模块
      modules: {
        // 继承父模块的命名空间
        myPage: {
          state: { ... },
          getters: {
            profile () { ... } // -> getters['account/profile']
          }
        },

        // 进一步嵌套命名空间
        posts: {
          namespaced: true,

          state: { ... },
          getters: {
            popular () { ... } // -> getters['account/posts/popular']
          }
        }
      }
    }
  }
})


// **********app.vue***
	 ...mapActions([
	     'some/nested/module/foo', // -> this['some/nested/module/foo']()
	     'some/nested/module/bar' // -> this['some/nested/module/bar']()
	 ])

	// 或者
	...mapActions('some/nested/module', [
    	'foo', // -> this.foo()
    	'bar' // -> this.bar()
   ])

以上,官网讲解的很详细。

localStorage和sessionStorage

前者将数据缓存到电脑本地,后者混存数据到本tab页。数据存储比较简单,容易混乱,不易维护。两者都是只能存储string类型数据,复杂类型数据可以通过JSON.stringfy方法处理后进行存储。可以与vuex结合进行数据的本地存储。

$attrs和 $listeners

$attrs: { [key: string]: string }

包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。

inheritAttrs:默认值true,继承所有的父组件属性(除props的特定绑定)作为普通的HTML特性应用在子组件的根元素上,如果你不希望组件的根元素继承特性设置inheritAttrs: false,但是class属性会继承

$listeners: { [key: string]: Function | Array }

包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。

相当于隔代组件可以直接抛出父组件监听的事件,这里隔代组件之间传递参数时需要加上v-on=" $listeners"

总结

父子组件通信:

  • props和$emit
  • vuex
  • ref和$refs
  • $children和 $parent
  • provide和inject
  • eventbus
  • $attrs和 $liseners

兄弟组件通信

  • eventBus
  • vuex

隔代组件通信

  • eventBus
  • vuex
  • $attrs
  • provide和inject
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值