资讯专栏INFORMATION COLUMN

Vue组件通信

philadelphia / 3250人阅读

摘要:使,最主要的的是跨组件通信全局数据维护。这两种法旦发出事件后,任何组件都是可以接收到的,就近原则,且会在第次接收到后停冒泡,除返回。并且和也没有解决兄弟组件间的通信问题。

Vue的组件是其非常重要的系统,组件之间的通信也是开发中不可避免的需求

一般来说Vue组件是以下几种关系

A组件和B组件、B组件和C组件、B组件和D组件是父子关系,C组件和D组件是兄弟关系,A组件和C/D组件是隔代关系。

本文阐述了几种常用的通信方式和使用场景

props&&emit

父组件通过 props 传递数据给子组件,子组件通过 emit 发送事件传递数据给父组件

这种父子通信方式也就是典型的单向数据流,父组件通过 props 传递数据,子组件不能直接修改 props , 而是必须通过发送事件的方式告知父组件修改数据。

// component-a




// component-b


优点:易于使用,结构清晰

缺点:只能用于父子组件通信

ref&&$parent / $children

这两种都是直接得到组件实例,使⽤后可以直接调⽤组件的⽅法或访问数据

ref:给元素或组件注册引⽤信息

$parent/$children:访问父/子组件

ref
// component-a


$parent / $children

$parent$children都是基于当前上下文访问父组件和子组件

// component-a




// component-b


ref$parent/$children的优缺点和props&&emit相同,弊端都是无法在跨级和兄弟间通信

provide/inject

ref$parent/$children在跨级通信中有一定的弊端。
Vue.js 2.2.0 版本后新增 provide / inject API
vue文档
这对选项需要⼀起使⽤,以允许⼀个祖先组件向其所有⼦孙后代注⼊⼀个依赖,不论组件层次有多深,并在起上下游关系成⽴的时间⾥始终⽣效

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

inject 选项应该是:

一个字符串数组

一个对象,对象的 key 是本地的绑定名,value 是:

在可用的注入内容中搜索用的 key (字符串或 Symbol),或

一个对象,该对象的:

from 属性是在可用的注入内容中搜索用的 key (字符串或 Symbol)

default 属性是降级情况下使用的 value

provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。

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

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

在做 Vue ⼤型项⽬时,可以使⽤ Vuex 做状态管理。使用 provide / inject,可以模拟 达到 Vuex 的效果 。
使⽤ Vuex,最主要的⽬的是跨组件通信、全局数据维护。⽐如⽤户的登录信息维护、通知信息维护等全局的状态和数据

通常vue应用都有一个根根组件app.vue,可以⽤来存储所有需要的全局数据和状态,methods 等。项目中所有的组件其父组件都是app,通过provideapp实例暴露对外提供


接下来任何组件只要通过 inject 注⼊ app.vueapp 的话,都可以直接通过this.app.xxx 来访问 app.vuedata、computed、methods 等内容

例如通过这个特性保存登录信息

export default {
  provide() {
    return {
      app: this
    };
  },
  data() {
    return {
      userInfo: null
    };
  },
  methods: {
    getUserInfo() {
      // 这⾥通过 ajax 获取⽤户信息后,赋值给this.userInfo;
      $.ajax("/user", data => {
        this.userInfo = data;
      });
    }
  },
  mounted() {
    this.getUserInfo();
  }
};

之后在任何⻚⾯或组件,只要通过 inject 注⼊ app 后,就可以直接访问 userInfo 的数据了


优点:

跨级注入

所有子组件都可获取到注入的信息

缺点:

注入的数据非响应式

Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。当需要开发开发大型单页应用(SPA),就应该考虑使用Vuex了,它能把组件的共享状态抽取出来,当做一个全局单例模式进行管理。这样不管你在何处改变状态,都会通知使用该状态的组件做出相应修改。
Vuex官方文档已经给出了详细的使用方式

优点:

官方集成管理库,可以处理各种场景的通信和状态管理

缺点:

需要额外引入管理库

Bus

如果不是大型项目,状态管理不复杂,数据量不是很大,没有必要使用Vuex

可以使用一个空的vue实例作为事件总线中间件Bus处理组件间的通信

首先在全局定义bus

let bus = new Vue();

var eventBus = {
  install(Vue, options) {
    Vue.prototype.$bus = bus;
  }
};
Vue.use(eventBus);

然后就可以在组件中使用$on,$emit,off来监听,分发和销毁组件

分发组件

// component-c


监听组件

// component-d


最好在组件销毁之前清除监听事件

优点:

使用简单,不需要额外支持

可以实现跨级和兄弟间通信

缺点:

需要在组件销毁时,手动清除事件监听

事件过多时比较混乱

dispatch/broadcast

$dispatch$broadcast 是Vue1.x中提供的API,前者⽤于向上级派发事件,只要是它的⽗级(⼀级或多级以上),都可以在组件内通过 $on 监听到,后者相反,是由上级向下级⼴播事件

// 子组件
vm.$dispatch(eventName,params)
// 父组件
vm.$on(eventName
, (params) => {
console.log(params); 
});

$broadcast 类似,只不过⽅向相反。这两种⽅法⼀旦发出事件后,任何组件都是可以接收到的,就近原则,⽽且会在第⼀次接收到后停⽌冒泡,除⾮返回 true

这2个方法在已经被弃用,Vue官方给出的解释是:

因为基于组件树结构的事件流方式实在是让人难以理解,并且在组件结构扩展的过程中会变得越来越脆弱。这种事件方式确实不太好,我们也不希望在以后让开发者们太痛苦。并且$dispatch 和 $broadcast 也没有解决兄弟组件间的通信问题。

虽然在开发中,没有Vuex这样的专业状态管理工具方便好用,但是在独立组件库和一些特殊场景中,也是非常好用的一种传递方式。

模拟dispatch/broadcast

自行模拟dispatch/broadcast无法达到与原方法一模一样的效果,但是基本功能都是可以实现的,解决组件之间的通信问题

方法有功能有向上/下找到对应的组件,触发指定事件并传递数据,其下/上级组件已经通过$on监听了该事件。

首先需要正确地向上或向下找到对应的组件实例,并在它上⾯触发⽅法。

function broadcast(componentName, eventName, params) {
  this.$children.forEach(child => {
    const name = child.$options.name;
    if (name === componentName) {
      child.$emit.apply(child, [eventName].concat(params));
    } else {
      broadcast.apply(child, [componentName, eventName].concat([params]));
    }
  });
}
export default {
  methods: {
    dispatch(componentName, eventName, params) {
      let parent = this.$parent || this.$root;
      let name = parent.$options.name;

      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent;
        if (parent) {
          name = parent.$options.name;
        }
      }

      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params));
      }
    },
    broadcast(componentName, eventName, params) {
      broadcast.call(this, componentName, eventName, params);
    }
  }
};

这两个⽅法都接收了三个参数,第⼀个是组件的 name 值,⽤于向上或向下递归遍历来寻找对应的组件,第⼆个和第三个就是上⽂分析的⾃定义事件名称和要传递的数据。

dispatch ⾥,通过 while 语句,不断向上遍历更新当前组件(即上下⽂为当前调⽤该⽅法的组件)的⽗组件实例(变量 parent 即为⽗组件实例),直到匹配到定义的 componentName 与某个上级组件的 name 选项⼀致时,结束循环,并在找到的组件实例上,调⽤ $emit ⽅法来触发⾃定义事件 eventNamebroadcast ⽅法与之类似,只不过是向下遍历寻找

优点:

使用简单

可以实现跨级通信

缺点:

原生支持已经废除,需要自行实现

findComponents系列

上述介绍的各种通信方法都有各自的局限性,我们可以实现一个 findComponents 系列的方法,可以实现

向上找到最近的指定组件

向上找到所有的指定组件

向下找到最近的指定组件

向下找到所有指定的组件

找到指定组件的兄弟组件

5个方法都是通过递归和遍历,通过组件name选项匹配到指定组件返回

向上找到最近的指定组件
function findComponentUpward(context, componentName) {
    let parent = context.$parent; // 获取父级组件
    let name = parent.$options.name; // 获取父级组件名称
    // 如果父级存在 且 父级组件 没有name 或 name与要寻找的组件名不一致,重置parent和name,再逐级寻找
    while (parent && (!name || [componentName].indexOf(name) < 0)) {
        parent = parent.$parent;
        if (parent) name = parent.$options.name;
    }
    // 逐级查找父级组件名和传参名是否一致,返回找到的parent
    return parent;
}

findComponentUpward 接收两个参数,第⼀个是当前上下⽂,即你要基于哪个组件来向上寻找,⼀般都是基于当前的组件,也就是传⼊ this;第⼆个参数是要找的组件的 name 。
dispatch是通过触发和监听事件来完成事件交互,findComponentUpward 会直接拿到组件的实例

向上找到所有的指定组件
function findComponentsUpward(context,
    componentName) {
    let parents = []; // 收集指定组件
    const parent = context.$parent;
    if (parent) {
        if (parent.$options.name === componentName)
            parents.push(parent);
        return parents.concat(findComponentsUpward(parent, // 递归逐级向上寻找
            componentName));
    } else {
        return [];
    }
}

findComponentsUpward会返回的是⼀个数组,包含了所有找到的组件实例
findComponentsUpward 的使⽤场景较少

向下找到最近的指定组件
function findComponentDownward(context,
    componentName) {
    const childrens = context.$children;
    let children = null;
    if (childrens.length) {
        for (const child of childrens) {
            const name = child.$options.name;
            if (name === componentName) {
                children = child;
                break;
            } else {
                children = findComponentDownward(child,
                    componentName);
                if (children) break;
            }
        }
    }
    return children;
}

context.$children 得到的是当前组件的全部⼦组件,所以需要遍历⼀遍,找到有没有匹配到的组件 name,如果没找到,继续递归找每个 $children$children,直到找到最近的⼀个为⽌

向下找到所有指定的组件
function findComponentsDownward(context,
    componentName) {
    return context.$children.reduce((components, child) => {
        if (child.$options.name === componentName)
            components.push(child);
        const foundChilds = findComponentsDownward(child, componentName);
        return components.concat(foundChilds);
    }, []);
}

使⽤ reduce 做累加器,并⽤递归将找到的组件合并为⼀个数组并返回

找到指定组件的兄弟组件
function findBrothersComponents(context,
    componentName, exceptMe = true) {
    let res = context.$parent.$children.filter(item
        => {
        return item.$options.name === componentName;
    });
    let index = res.findIndex(item => item._uid ===
        context._uid);
    if (exceptMe) res.splice(index, 1);
    return res;
}

findBrothersComponents 多了⼀个参数 exceptMe ,是否把本身除外,默认是 true 。寻找兄弟组件的⽅法,是先获取 context.$parent.$children,也就是⽗组件的全部⼦组件,这⾥⾯当前包含了本身,所有也会有第三个参数exceptMeVue.js 在渲染组件时,都会给每个组件加⼀个内置的属性 _uid,这个 _uid 是不会重复的,借此我们可以从⼀系列兄弟组件中把⾃⼰排除掉。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/103608.html

相关文章

  • Vue 组件通信方式

    摘要:本身提供哪几种通信方式首先灵感源于,支持双向绑定,本质还是单向数据流。跟一样,组件间最基本的数据流是通过向子组件传递数据。但是在却很少使用,因为组件可以自定义事件,即后面的组件间通信方式其实就是订阅发布模式。 例子是在 jsrun.net 平台编写,不支持移动端平台,所以本文建议在 PC 端进行阅读。 Vue 是数据驱动的视图框架,那么组件间的数据通信是必然的事情,那么组件间如何进行数...

    hss01248 评论0 收藏0
  • vue中8种组件通信方式, 值得收藏!

    摘要:一父组件通过的方式向子组件传递数据,而通过子组件可以向父组件通信。而且只读,不可被修改,所有修改都会失效并警告。 之前写了一篇关于vue面试总结的文章, 有不少网友提出组件之间通信方式还有很多, 这篇文章便是专门总结组件之间通信的 vue是数据驱动视图更新的框架, 所以对于vue来说组件间的数据通信非常重要,那么组件之间如何进行数据通信的呢?首先我们需要知道在vue中组件之间存在什么样...

    BicycleWarrior 评论0 收藏0
  • Vue组件基础与通信

    摘要:组件基础与通信一脚手架简介与安装之前安装的是模块,之后安装的是模块。如果是三级组件通信,该如何处理比如父组件与孙子组件通信。和,提供和注入实现祖先组件和后代组件之间通信。 Vue组件基础与通信 一、vue cli脚手架 ① vue cli 简介与安装 vue cli 3.0之前安装的是vue-cli模块,vue cli 3.0之后安装的是@vue/cli模块。如果已经全局安装了旧版本的...

    I_Am 评论0 收藏0
  • vue组件通信的几种方式

    摘要:虽然和都可以获取组件实例,但是它们无法在跨级或兄弟间通信,这是它们的缺点。也就是在父组件中提供一个值,并且在需要使用的子孙组件中注入改值,即不仅仅是,只要是的子组件,无论隔多少代,都可以通过这个的方式注入。通过混入组件,实现组件间的通信。 写在前面 vue 的组件化应该是其最核心的思想了,无论是一个大的页面还是一个小的按钮,都可以被称之为组件。基于 Vue 的开发,就是在写一个个组件,...

    vspiders 评论0 收藏0
  • 整理4种Vue组件通信方式

    摘要:整理种组件通信方式重点是梳理了前两个父子组件通信和通信我觉得文档里的说明还是有一些简易我自己第一遍是没看明白。第四种通信方式利用比较复杂可以单独写一篇 整理4种Vue组件通信方式 重点是梳理了前两个,父子组件通信和eventBus通信,我觉得Vue文档里的说明还是有一些简易,我自己第一遍是没看明白。 父子组件的通信 非父子组件的eventBus通信 利用本地缓存实现组件通信 Vuex...

    MingjunYang 评论0 收藏0
  • vue组件通信、数据传递(父子组件,同级组件

    总结一下对vue组件通信的理解和使用。 一、组件目录结构 父组件:app.vue 子组件:page1.vue 子组件:page2.vue 父组件 app.vue 请输入单价: import Page1 from ./components/page1; import Page2 from ./components/page2; export de...

    Harpsichord1207 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<