vue

vue组件开发(组件之间的通信)

(优化代码)

Posted by YC on December 22, 2018

自制vue组件开发

vue组件的分类

vue 组件大致分为三类,vue-router产生的每个页面,基础组件,业务组件。

  • vue-router组件,也就是平时用cli开发,使用的vue文件,每个文件,和页面都是一个vue组件,处理一些常规文件,主要作用是用来渲染页面使用的,一般不会复用。

  • 基础组件,不包含业务,独立的,具有特定功能的组件,例如选择器,输入框,状态栏,按钮,像element-ui这些ui框架都是基础组件,这些组件往往是项目中复用率最高的组件,但这类组件的开发侧重在api的设计,兼容性,性能,考入的事项比其他组件都要的多。

  • 业务组件,不单单是一个简单功能的组件,他在业务中被多个页面服用,而他和基础组件区别在于他只在当前项目中广泛应用,业务组件里也会包含一些数据请求,这类组件的开发只要考虑到当前项目的兼容就好。

组件构成

  • 属性prop

prop 定义组件有哪些可配置的属性,也可以理解成,从父组件中继承下来的属性,prop是由父组件定义传值,再由子组件去继承,在子组件,props最好使用对象的写法去表明子组件可配置的属性,使用对象的写法,可以在props中也能申明默认属性,在父组件没有配置时,默认属性,也能够自定义校验属性的值是不是你所期望得到的值。 但是props都是单向数据流,只能通过父组件去配置,不能有子组件去修改配置,如果想要修改,只能将prop属性赋值给data里的数据,再修改data里的数据,但是这样更改,父组件还是原始的值。如果想要让子组件的属性通信父组件,则可以使用$emit来通信,或者使用vuex或者Bus来通信。但是这不试用与基础组件的开发。 在组件中,也能通过html的特性传入,例如id,class,这些特性都会被组件的根元素所继承,如果不期望继承,则可以在组件选项配置inheritAttrs: false就可以禁用组件继承特性了。

  • 插槽slot

slot插槽可以分发组件的内容,他能扩展组件内部的内容

例如,在定义一个按钮组件的时候,就可以在组件占位标签

<template>
  <button  :disabled="disabled">
    <slot></slot>
  </button>
</template>

在父类调用的时候就可以

<my-button>这是一个按钮</my-button>

他会将<my-button>标签内的 ‘这是一个按钮’ 传入给子组件,在<slot></slot>位置上插入该文本,当然不止能传入单单一个字符串,也能是其他的html内容。

<my-button>
  <strong>这是一个带标签的按钮</strong>
</my-button>

插槽也能够默认内容,例如

<template>
  <button  :disabled="disabled">
    <slot>默认按钮</slot>
  </button>
</template>
<my-button></my-button>

在父组件<my-button></my-button> 不填写内容的时候,默认会显示’默认按钮’

当需要试用多个插槽的时候,就会用到具名插槽,给子组件的slot设置name属性,在父组件调用的时候,给插入内容设置slot属性,让父子组件能够对应上。

<template>
  <button :disabled="disabled">
    <slot name="icon"></slot>
    <slot></slot>
  </button>
</template>

<my-button>
  <i slot="icon"></i>
  按钮 1
</my-button>
  • 自定义事件 $emit

像子组件向父组件传值的时候,可以通过$emit,触发自定义事件,然后父组件通过监听出发的事件,获取到子组件传入父组件的数据

<template>
  <button @click="handleClick">
    <slot></slot>
  </button>
</template>
<script>
  export default {
    methods: {
      handleClick (event) {
        //当点击按钮的时候 出发on-click事件,并且将dom对象当作数据传值给父组件
        this.$emit('on-click', event);
      }
    }
  }
</script>
// 父组件监听 on-click事件 触发handleClick 事件
<my-button @on-click="handleClick"></my-button>

组件之间的通信

组件之间的通信,有很多的方法,可以通过ref 给元素注册引用信息,$parent / $children:访问父 / 子实例。

这两种方法 可以调用组件信息,能够直接获取到方法或者访问数据, 用ref 来访问数据

export default {
  data () {
    return {
      title: 'Vue.js'
    }
  },
  methods: {
    sayHello () {
      window.alert('Hello');
    }
  }
}

<template>
  //注册 组件信息名字为comA
  <component-a ref="comA"></component-a>
</template>
<script>
  export default {
    mounted () {
      //调用vue下的refs 获取到注册组件的comA
      const comA = this.$refs.comA;
      console.log(comA.title);  // Vue.js
      comA.sayHello();  // 弹窗
    }
  }
</script>

$parent / $children:访问父 / 子实例 和 ref类似,都是通过vue实力属性来调用,但是缺点就是,无法跨级或者兄弟间通信,如果想实现跨级或者兄弟组件之间的通信,一般想到的都是试用vuex 或者Bus来解决,但是这不利于组件的开发。

provide / inject

provide / inject 组件通信,这个是vue2.2.0新增的API,在文档中,这样介绍

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

我个人认为的provide / inject 可以模拟vuex的效果, 对于。

  • 简单了解provide / inject

B组件是A组件的子组件 A->B

// A.vue
export default {
  provide: {
    name: 'Aresn'
  }
}

// B.vue
export default {
  inject: ['name'],
  mounted () {
    console.log(this.name);  // Aresn
  }
}

先是在A组件中设置provide:name,值为 ‘Aresn’ ,他的作用是将name这个变量提供给他的所有子组件,在B组件中,通过inject 来注入从A组件获取到的name变量,那么在组件B中,就可以直接this.name来获取到这个变量了,需要注意的是这样的通信的变量是不可响应的,如果A组件的name改变了,那么B组件还是之前的’Aresn’

  • 替代Vuex

之前了解到provide / inject类似于广播的效果,在父组件设置provide 然后该组件的所有子组件都能接受到这个广播,但是要在子组件中设置inject来接受这个广播。

那么vue是一个单页面应用,开发过程中,都是在app.vue作为根组件来进行开发的,如果我们在app.vue中设置总台发射站,设置provide,在其他需要的组件中设置inject。这是不是和vuex很像呢,在app.vue作为状态管理,那么我们provide只需要吧app.vue组件中的所有数据,方法都广播出去,这样就能实现一个类似Vuex的效果。 app.vue 是整个项目第一个被渲染的组件,而且只会渲染一次(即使切换路由,app.vue 也不会被再次渲染),利用这个特性,很适合做一次性全局的状态数据管理,例如,我们将用户的登录信息保存起来:

app.vue

<template>
  <div>
    <router-view></router-view>
  </div>
</template>
<script>
  export default {
    provide () {
      return {
        app: this
      }
    },
    data () {
      return {
        userInfo: null
      }
    },
    methods: {
      getUserInfo () {
        // 这里通过 ajax 获取用户信息后,赋值给 this.userInfo,以下为伪代码
        $.ajax('/user/info', (data) => {
          this.userInfo = data;
        });
      }
    },
    mounted () {
      this.getUserInfo();
    }
  }
</script>

template>
  <div>
    
  </div>
</template>
<script>
  export default {
    inject: ['app'],
    methods: {
      changeUserInfo () {
        // 这里修改完用户数据后,通知 app.vue 更新,以下为伪代码
        $.ajax('/user/update', () => {
          // 直接通过 this.app 就可以调用 app.vue 里的方法如果app中的userInfo更新了就可以通过调用app组件中的方法获取新的数据
          this.app.getUserInfo();
        })
      }
    }
  }
</script>

现在就能通过provide / inject解决了跨级组件的通信问题了解以上几点 就能简单的开发一个自定义的组件了