专栏首页女程序员的日常_LinVue.js的组件、组件间通信

Vue.js的组件、组件间通信

目录:

  1. 组件的种类:vue-router产生的每个页面、基础组件、业务组件
  2. Vue.js组件的三个API:prop、event、slot
  3. Vue.js组件的通信方式:
    • ref:给元素或组件注册引用信息;
    • parent/children:访问父 / 子实例。
    • provide/inject
    • 运用$emit实现dispatch和broadcast
    • 找到任意组件实例---findComponents 系列方法

组件的种类

  • 由vue-router产生的每个页面,本质上也是一个组件(.vue),主要承载当前页面的HTML结构,包括数据获取、整理……。一般不会有props选项和自定义事件,因为它作为路由的渲染、不会被复用,因此也不会对外提供接口。
  • 不包含业务,独立、具体功能的基本组件,比如日期选择器、模态框。
  • 业务组件。在业务中被多个页面复用,在当前项目中会用到,不具有通用性。

Vue.js组件的三个API:prop、event、slot

props

props定义了这个组件有哪些可配置的属性,props最好用对象的写法,这样可以针对每个属性设置类型、默认值或自定义校验属性的值。

下面封装一个组件

<template>
  <button :class="'i-button-size' + size" :disabled="disabled"></button>
</template>
<script>
  export default{
    props:{
      size:{
        default: 'default',
        validator(value){
          return oneOf(value,['small','large','default'])
        }
      },
      disabled:{
        type: Boolean,
        default: false
      }
    }
  }
</script>

组件里定义的props,都是单向数据流,只能通过父级修改,组件自己不能修改props的值,只能修改定义在data里的数据,非要修改需要通过自定义事件通知父级。

在使用组建时也可以传入一些标准的html特性,如id 、class

<i-button id="btn1" class="btn-submit"></i-button>

这些html特性,组件中的button元素会继承,不需要通过props属性传递。这个属性是默认支持的,如果不期望开启,在组件选项中设置 inheritAttrs : false。

slot插槽

如果要给上面的<i-button>添加文字,就要用到插槽slot,它可以分发组件的内容。

<template>
  <button :class="'i-button-size' + size" :disabled="disabled">
    <slot></slot>
  </button>
</template>
<i-button>按钮 1</i-button>
<i-button>
  <strong>按钮 2</strong>
</i-button>

当需要多个插槽时,可以设置具名插槽

<template>
  <button :class="'i-button-size' + size" :disabled="disabled">
    <slot name="icon"></slot>
    <slot></slot>
  </button>
</template>
<i-button>
  <i-icon slot="icon" type="checkmark"></i-icon>
  按钮 1
</i-button>

在slot中也可以设置默认内容,当父级没有任何slot时,会展示如下:

<slot>提交</slot>

Event事件

绑定事件:

<template>
  <button @click="clickBtn"></button>
</template>
<script>
  export default{
    methods:{
      clickBtn(){
        this.$emit('on-click','params')
      }
    }
  }
</script>

使用

<i-button @on-click="clickComponent"></i-button>

或者写成

<i-button @click.native="clickComponent"></i-button>

.native 修饰符,为了区别原生的click和自定义click事件。在组件内调用了on-click方法,如果不使用.native修饰符就不能调用原生click事件。

Vue.js组件的通信(基本)

Vue内置的通信手段一般有两种

  • ref:给元素或组件注册引用信息;
  • parent/children:访问父 / 子实例。

Vue.js组件的通信(其他)

一、provide/inject(主要解决子组件获取上级组件的状态,主动提供与依赖注入的关系)

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

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

用法:

// A.vue
export default {
  provide: {
    name: 'Aresn'  // 将name属性提供给所有子组件
  }
}

// B.vue
export default {
  inject: ['name'], // 注入从A组件中提供的name变量
  mounted () {
    console.log(this.name);  // Aresn
  }
}

provide、inject绑定并不是可响应的。

二、运用$emit实现dispatch和broadcast

dispatch和broadcast的功能:

  • 在子组件调用 dispatch 方法,向上级指定的组件实例(最近的)上触发自定义事件,并传递数据,且该上级组件已预先通过$on 监听了这个事件;
  • 相反,在父组件调用 broadcast 方法,向下级指定的组件实例(最近的)上触发自定义事件,并传递数据,且该下级组件已预先通过 $on 监听了这个事件。
// 部分代码省略
import Emitter from '../mixins/emitter.js'

export default {
  mixins: [ Emitter ],
  methods: {
    handleDispatch () {
      this.dispatch();  
    },
    handleBroadcast () {
      this.broadcast();  
    }
  }
}
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);
    }
  }
};

在 dispatch 里,通过 while 语句,不断向上遍历更新当前组件(即上下文为当前调用该方法的组件)的父组件实例(变量 parent 即为父组件实例),直到匹配到定义的 componentName 与某个上级组件的 name 选项一致时,结束循环,并在找到的组件实例上,调用 $emit 方法来触发自定义事件 eventName。broadcast 方法与之类似,只不过是向下遍历寻找。

二、找到任意组件实例---findComponents 系列方法

findComponents 系列方法最终都是返回组件的实例,进而读取或调用该组件的数据和方法。

适用场景

  • 由一个组件,向上找到最近的指定组件
  • 由一个组件,向上找到所有的指定组件
  • 由一个组件,向下找到最近的指定组件
  • 由一个组件,向下找到所有的指定组件
  • 由一个组件,找到指定组件的兄弟组件

5个函数的原理,都是通过递归、遍历,找到指定组件的name选项匹配的组件实例并返回。

向上找到最近的指定组件-findComponentUpward

// 由一个组件,向上找到最近的指定组件
// context 当前上下午,一般都是基于当前的组件,即传入this
// componentNam 是要找的组件的name
function findComponentUpward (context, componentName) {
  let parent = context.$parent;
  let name = parent.$options.name;
  // 判断组件的name与传入的componentName是否一致,直到最近的一个组件为止
  while (parent && (!name || [componentName].indexOf(name) < 0)) {
    parent = parent.$parent;
    if (parent) name = parent.$options.name;
  }
  // 与dispatch不同的是,findComponentUpward是直接拿到组件的实例,
  // 而非通过事件通知组件
  return parent;  
}
export { findComponentUpward };

其他函数与此方法大同小异

参考:https://juejin.im/book/5bc844166fb9a05cd676ebca/section/5bc844166fb9a05cf52af65f

本文分享自微信公众号 - 女程序员的日常(gh_df41d619fb70),作者:凛

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-05-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 关于Vue

    在 beforCreate 钩子函数调用的时候,是获取不到props或者data中的数据的,因为这些数据的初始化都在initState中。

    用户3258338
  • docker基础

    重点:volumes:将主机的数据卷或者文件挂载到容器里。(使得容器内目录与主机内目录完全同步)

    用户3258338
  • 读书笔记-《了不起的nodejs》-connect内置中间件

    给如果是true,connect就会托管那些以(.)开始的UNIX文件系统中被认为是隐藏的文件。

    用户3258338
  • 微信小程序组件化编程和实践(下)

    现在我们已经可以做到了两个组件之间的数据传递,那么如何在多个组件间传递数据呢?

    疯狂的小程序
  • Vue 在父(子)组件引用其子(父)组件方法和属性

    <button @click="callChildMethod()">父组件调用子组件方法</button>

    授客
  • Vue-cli父子组件之间传参

    小小咸鱼YwY
  • React + webpack 开发单页面应用简明中文文档教程(九)子组件给父组件传值

    版权声明:本文为 FengCms FungLeo 原创文章,允许转载,但转载必须注明出处并附带首发链接 ...

    FungLeo
  • Qampp支持腾讯云.

    Qampp支持win7以上系统. 快速搭建php环境包, 并且算带redis, pgsql等组件.

    Tuesday
  • React一种少用的组件通信方式,但是简单

    前朝楚水
  • 架构整洁之道导读(二)

    我是《架构整洁之道》(Clean Architecture) 中文版的技术审校者,在审校的过程当中略有感悟,所以希望通过撰写导读的方式分享给大家。

    lambeta

扫码关注云+社区

领取腾讯云代金券