前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vue.js的组件、组件间通信

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

作者头像
用户3258338
发布2020-05-22 11:08:29
10.1K0
发布2020-05-22 11:08:29
举报

目录:

  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最好用对象的写法,这样可以针对每个属性设置类型、默认值或自定义校验属性的值。

下面封装一个组件

代码语言:javascript
复制
<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

代码语言:javascript
复制
<i-button id="btn1" class="btn-submit"></i-button>

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

slot插槽

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

代码语言:javascript
复制
<template>
  <button :class="'i-button-size' + size" :disabled="disabled">
    <slot></slot>
  </button>
</template>
代码语言:javascript
复制
<i-button>按钮 1</i-button>
<i-button>
  <strong>按钮 2</strong>
</i-button>

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

代码语言:javascript
复制
<template>
  <button :class="'i-button-size' + size" :disabled="disabled">
    <slot name="icon"></slot>
    <slot></slot>
  </button>
</template>
代码语言:javascript
复制
<i-button>
  <i-icon slot="icon" type="checkmark"></i-icon>
  按钮 1
</i-button>

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

代码语言:javascript
复制
<slot>提交</slot>

Event事件

绑定事件:

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

使用

代码语言:javascript
复制
<i-button @on-click="clickComponent"></i-button>

或者写成

代码语言:javascript
复制
<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主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。

用法:

代码语言:javascript
复制
// 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 监听了这个事件。
代码语言:javascript
复制
// 部分代码省略
import Emitter from '../mixins/emitter.js'

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

代码语言:javascript
复制
// 由一个组件,向上找到最近的指定组件
// 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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-05-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 女程序员的日常 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档