参考:https://segmentfault.com/a/1190000019208626?utm_source=tag-newest
vue.js最强大的功能之一就是组件,不同的组件之间的作用域是相互独立的:
而组件之间常用的几种通信方式包括:
props/$emit$emit/$onVuex$attrs/$listenersprovide/inject$parent/$children和$ref父组件通过props的方式向子组件传递值,子组件通过向父组件发送$emit。
在父组件中定义要传递的值:
export default { data(){ return { users:["Wang","Zhang","Li"], nameType:"nameType" } } }父组件调用子组件时使用props方式传值:
<Child :users="users" :nameType="nameType"></Child>子组件通过props获取属性:
export default { props:{ users:{ type:Array, // 数据类型 default: [],// 默认值 required: false//是否必须 }, nameType:{ type:String, // 数据类型 default: '',// 默认值 required: false//是否必须 } } }也可以这样写:
export default { props:['users','nameType'] }子组件使用$emit注册事件:
export default { name: "Child", methods:{ changeName(){ this.$emit("changeName","wangzhibin") } }, mounted(){ window.setTimeout(this.changeName,1000); } }父组件调用子组件时绑定事件,并用$event 获取参数:
<Child @changeName="changeName($event)" />父组件事件:
export default { data() { return { msg: "lisi" }; }, methods: { changeName(newValue) { this.msg = newValue; } } };通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级,实现方式如下:
var Event=new Vue(); Event.$emit(事件名,数据); Event.$on(事件名,data => {});比如一个组件发送一个$emit:
<script> var Event = new Vue() export default { data() { return { name: "tom" }; }, methods: { send() { Event.$emit('data-a', this.name); } } }; </script>另一个组件使用$on接收:
<script> var Event = new Vue() export default { data() { return { name: "Jack" }; }, mounted() { Event.$on('data-a',(name) => { this.name = name; }) }; </script>Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。Vuex有五个属性:State、Getter、Mutation、Action、Module。
State里放入的是仓库的数据,类似于js里的data:
//获取state this.$store.state.count //vuex的辅助方法 import { mapState } from 'vuex' computed:mapState([ 'count' ])Getters里放入State里数据的扩展数据,可以把它当作计算属性。比如State里有一个数组,就可以从Getters里获取到数组的长度,或是取到对象里面的属性,用法和计算属性类似:
//直接使用 this.$store.getters.doneTodosCount //辅助方法 import { mapGetters } from 'vuex' computed:mapGetters({ doneCount: 'doneTodosCount' })Mutations里是保存在仓库的方法,这里面的方法只能用来改变state里面的数据,而且想要改变state里的数据只能用mutations里的方法,并且它里面的方法只能是同步的:
//触发mutations this.$store.commit('xxx') //辅助函数 import { mapMutations } from 'vuex' methods:mapMutations(['increment' ])对于Action的官方解释如下。 类似于 mutation,不同在于: Action 提交的是 mutation,而不是直接变更状态。 Action 可以包含任意异步操作。
//触发action store.dispatch('increment') //辅助函数 import { mapActions } from 'vuex' methods:mapActions(['increment' ])module可以把vuex分为不同的模块,使业务逻辑变得更清晰,更准确:
const moduleA = { state: { ... }, mutations: { ... }, actions: { ... }, getters: { ... } } const moduleB = { state: { ... }, mutations: { ... }, actions: { ... } } const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB } }) store.state.a // -> moduleA 的状态 store.state.b // -> moduleB 的状态多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。为此Vue2.4 版本提供了另一种方法----$attrs/$listeners
$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用。 $listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
//App.vue <ComponentOne :appName="appName" :appId="appId" :appColor="appColor" :appStyle="appStyle" /> data() { return { appName: "父类app", appId: "Number-99", appColor: "#0099FF", appStyle: "AppStyle" }; }, //ComponentOne.vue <div> 我是Component11111组件! <br /> 父类传来的appName是:{{appName}} <br /> ComponentOne$attrs: {{ $attrs }} <br /> <ComponentTwo v-bind="$attrs" /> </div> inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性 props: { appName: String // foo作为props属性绑定 }, //ComponentTwo .vue <div> 我是Component2222组件! <br /> 父类传来的appId是:{{appId}} <br /> ComponentTwo$attrs: {{ $attrs }} </div> inheritAttrs: false, props: { appId: String, }如上图所示$attrs表示没有继承数据的对象,格式为{属性名:属性值}。Vue2.4提供了$attrs , $listeners 来传递数据与事件,跨级组件之间的通讯变得更简单。
简单来说:$attrs与$listeners 是两个对象,$attrs 里存放的是父组件中绑定的非 Props 属性,$listeners里存放的是父组件中绑定的非原生事件。
以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。一言而蔽之:祖先组件中通过provider来提供变量,然后在子孙组件中通过inject来注入变量。
provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
// A.vue是B.vue的父组件 // A.vue export default { provide: { name: '浪里行舟' } } // B.vue export default { inject: ['name'], mounted () { console.log(this.name); // 浪里行舟 } }需要注意的是:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的----vue官方文档 所以,上面 A.vue 的 name 如果改变了,B.vue 的 this.name 是不会改变的,仍然是 浪里行舟。
ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例;
$parent / $children:访问父 / 子实例;
需要注意的是:这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据。
// component-a 子组件 export default { data () { return { title: 'Vue.js' } }, methods: { sayHello () { window.alert('Hello'); } } } // 父组件 <template> <component-a ref="comA"></component-a> </template> <script> export default { mounted () { const comA = this.$refs.comA; console.log(comA.title); // Vue.js comA.sayHello(); // 弹窗 } } </script>这两种方法的弊端是,无法在跨级或兄弟间通信。
父子通信:
父向子传递数据是通过 props,子向父是通过 events($emit);通过父链 / 子链也可以通信($parent / $children);ref 也可以访问组件实例;provide / inject ;$attrs/$listeners
兄弟通信:
Bus;Vuex
跨级通信:
Bus;Vuex;provide / inject API、$attrs/$listeners