Vue2 点滴

(https://juejin.im/post/5c5c28cf518825627d37060a#heading-10)

组件间交互

这里所说的交互是父子组件以及父子孙组件之间的数据交互,这里有两个场景,

场景1:

组件 A 引用了组件 B ,组件 B 引用了组件 C ,而组件 C 需要接收组件 A 中传给组件 B 的所有 props 属性,这里我们就可以通过 B 组件实例的 $attrs 来获取组件 A 传给组件 B 的所有 props 属性,然后再把这个属性通过 v-bind='$attrs' 绑定到组件 C 上就可以了。组件 C 就可以获取到所有的 props 属性了(注意:C 组件中的 props 需要声明 A 中传过来的所有属性,并且如果组件 B 中有在 props 中定义一些同名的属性,那么这些属性都不会传给组件 C,相当于被拦截了)

场景2:

跟上面的正好相反,也就是组件 A 需要监听组件 C 中所有的事件,而它们之间还有一个组件 B,比如:组件 C 中有一个输入框,组件 A 想监听输入框的所有事件,此时我们就可以在 input 通过 v-on='$listeners' 把 input 所有的事件都发出,然后在组件 B 中作同样的操作,组件 A 就可以监听到 组件 C 中 input 的所有事件了。下面是一个精简后的示例

<!-- 组件A -->
<div class="component-a">
  <component-b :show="isShow" title="vue.js" @input="inputHandler"></component-b>
</div>
<!-- 组件B -->
<div class="component-b">
  <component-b v-bind="$attrs" v-on="$listeners"></component-b>
</div>
<!-- 组件C -->
<div class="component-c">
  <input v-on="$listeners" type="text">
</div>

如果组件 B 的 props 中声明了一个 show 属性那么传递到组件 C 的属性就只剩下 title 属性了。

可以把上面所说实例的 $attrs 和 $listeners 看作是批处理属性

遍历完执行指定函数

这个在一些场景会用得到,比如:需要等数据遍历渲染完之后,执行一些动画。这里就很必要了。

......
mounted () {
  // 更新数据
  this.updateData()
  this.$nextTick(() => {
    // 执行动画
    this.animateBar()
  })
}

自定义组件实现 v-model

https://segmentfault.com/a/1190000013783767

有时间我们需要对自己封装的组件实现双向绑定,比如自己封装的一个输入框。

vue 模板

<template>
  <input type="text" @input='change' v-model="innerValue">
</template>

vue Script

方式1:

export default {
  name: 'customInput',
  model: {
    prop: 'value',
    event: 'onChange'
  },
  props: {
    value: {
      type: String,
      default: ''
    }
  },
  methods:{
    change(){
      this.$emit('onChange', this.innerValue)
    }
  }
}

对于这各方式,需要给组件添加 model 属性对象,并且这个对象含有两个属性值(prop、evnet),prop 的值可以随意,但 props 中需要定义同名属性。然后当 输入框内容变化时触发 change 方法,把 onChange 事件传给父组件。

方式2:

这种方式会简单一些。

export default {
  name: 'customInput',
  props: {
    value: {
      type: String,
      default: ''
    }
  },
  methods:{
    change(){
      this.$emit('input', this.innerValue)
    }
  }
}

不过需要注意的是,props 属性中要定义 value 属性对象,并且事件名一定要是 input。

前面两种方式在父组件中可以像下面这样使用:

<custom-input v-model="content"></custom-input>

方式3:

这种方式有点不一样,不过实现的效果是一样的。

export default {
  name: 'customInput',
  props: {
    visible: {
      type: String,
      default: ''
    }
  },
  methods:{
    change(){
      this.$emit('update:visible', this.innerValue)
    }
  }
}

然后在父组件中这样调用

<custom-input :visible.sync="isVisible"></custom-input>

上面用到的 visible 只是一个变量名,你可以随意。


VUX 坑

第三方库 VUX 并不是很完美,比如 x-input 有一个天生的 bug ,清空按钮不生效。解决办法就是对其进行二次封装。不使用它提供的清空按钮,自已封装实现清空按钮。在自己的组件中引入 x-input 组件,隐藏它的清空按钮,并监听其部分事件,然后作相应的布局,添加清空按钮,给按钮添加事件

下面是监听 x-input 的 on-blur 事件的执行函数

......
clear () {
  this.innerValue = ''
  this.$emit('on-click-clear-icon')
  this.onChange()
},
onFocus ($event) {
  this.isFocus = true
  this.$emit('on-focus', this.innerValue, $event)
},
onBlur ($event) {
  setTimeout(() => {
    // 保证在下一次事件循环时才执行隐藏掉清空按钮,
    // 解决了点击清空按钮时, 由于 input 失去焦点先触发了失去焦点事件执行了隐藏按钮导致按钮上的事件不触发的问题
    this.isFocus = false
  })
  this.$emit('on-blur', this.innerValue, $event)
}
......

还有更简单的方式,只需要写几行代码,通过 extends 来扩展 x-input 组件,也就是重写 onBlur 方法。