高级 Vue 组件模式 (7)

07 使用 State Initializers

目标

到目前为止,仅从 toggle 组件自身的角度来看,它已经能够知足大多数的业务场景了。但咱们会发现一个问题,就是当前 toggle 组件的状态对于调用者来讲,彻底是黑盒状态,即调用者没法初始化,也没法更改组件的开关状态,这在一些场景没法知足需求。vue

对于没法初始化开关状态的问题,却是很好解决,咱们能够在 toggle 组件声明一个 prop 属性 on 来表明组件的默认开关状态,同时在 mounted 生命周期函数中将这个默认值同步到组件 data 相应的属性中去。git

对于没法更改开关状态的问题,彷佛没法简单经过声明一个 prop 属性的方式来解决,而且若是咱们指望的更改逻辑是异步的话,一样没法知足。github

所以这篇文章着重来解决这两个问题:app

  • toggle 组件可以支持开关状态的初始化功能
  • toggle 组件可以提供一个 reset 方法以供重置开关状态
  • 重置开关状态能够以异步的方式进行

实现

初始化开关状态

为了使 toggle 组件可以支持默认状态的传入,咱们采用声明 prop 属性的方式,以下:框架

on: {
  type: Boolean,
  default: false
}

以后在其 mounted 生命周期对开关状态进行同步,以下:异步

mounted() {
    this.status.on = this.on;
  }

这样当咱们指望 toggle的状态进行渲染时,能够这样调用组件:async

<toggle :on="true" @toggle="onToggle">
  ...
</toggle>

重置开关状态

为了可以从外部更改 toggle 组件的开关状态,咱们能够在组件内部声明一个观测 on prop 属性的监听器,好比:函数

watch: {
  on(val){
    // do something...
  }
}

但若是这么作,会存在一个问题,即目标中关于开关状态的更改逻辑的编写者是组件调用者,而 watch 函数的编写者是组件实现者,因为实现者没法预知调用者更改状态的逻辑,因此使用 watch 是没法知足条件的。this

让咱们换一个角度来思考问题,既然实现者没法预知调用者的逻辑,何不把重置开关状态的逻辑所有交由调用者来实现?别忘了 Vue 组件也是能够传入 Function 类型的 prop 属性的,以下:spa

onReset: {
  type: Function,
  default: () => this.on
},

这样就将提供重置状态的逻辑暴露给了组件调用者,固然,若是调用者没有提供相关重置逻辑,组件内部会自动降级为使用 on 属性来做为重置的状态值。

组件内部额外声明一个 reset 方法,在其内部重置当前的开关状态,以下:

reset(){
  this.status.on = this.onReset(this.status.on)
  this.$emit("reset", this.status.on)
}

这里会首先以当前开关状态为参数,调用 onReset 方法,再将返回值赋值给当前状态,并触发一个 reset 事件以供父组件订阅。

以后在 app 组件中,能够按以下方式传入 onReset 函数,并编写具体的重置逻辑:

// template
<toggle :on="false" @toggle="onToggle" :on-reset="resetToTrue">
...
</toggle>

// script
...
resetToTrue(on) {
  return true;
},
...

运行效果以下:

clipboard.png

支持异步重置

在实现同步重置的基础上,实现异步重置十分简单,一般状况下,处理异步较好的方式是使用 Promise,使用 callback 也能够,使用 Observable 也是不错的选择,这里咱们选择 Promise。

因为要同时处理同步和异步两种状况,只需把同步状况视为异步状况便可,好比如下两种状况在效果上是等价的:

// sync
this.status.on = this.onReset(this.status.on)

// async
Promise.resolve(this.onReset(this.status.on))
    .then(on => {
      this.status.on = on
    })

onReset 函数若是返回的是一个 Promise 实例的话,Promise.resolve 也会正确解析到当它为 fullfill 状态的值,这样关于 reset 方法咱们改版以下:

reset(){
  Promise.resolve(this.onReset(this.status.on))
    .then(on => {
      this.status.on = on
      this.$emit("reset", this.status.on)
    })
}

在 app 组件中,能够传入一个异步的重置逻辑,这里就不贴代码了,直接上一个运行截图,组件会在点击重置按钮后 1 秒后,重置为状态:

clipboard.png

成果

你能够下面的连接来看看这个组件的实现代码以及演示:

总结

Function 类型的 prop 属性在一些状况下很是有用,好比文章中说起的状态初始化,这实际上是工厂模式的一种体现,在其余的框架中也有体现,好比 React 中,HOC 中说起的 render props 就是一种比较具体的应用,Angular 在声明具备循环依赖的 Module 时,能够经过 () => Module 的方式进行声明等等。

目录

github gist

关注公众号 全栈101,只谈技术,不谈人生

clipboard.png

相关文章
相关标签/搜索