高阶函数在组件的接口开发上的应用

写这篇文章的想法,其实来自于本身的一次React组件开发实践(传送门)。当时是作一个markdown编辑器,原理是用户编辑事后的string,通过其余开源的markdown语法解析器解析后输出html格式的文档,这样就能够实时预览markdown了。开发初版时,我将解析器markdown-it内置在组件里,虽然可以知足了需求,可是也带来了一些问题:解析器会打包到本身的组件里致使代码体积增大,用户不能自定义解析器(ps:就想用那谁谁家的,咋地)。后来在项目贡献者sylingd的改造下,将组件的renderHtml接口改形成了一个高阶函数,完美的解决了这个问题(在此表示衷心的感谢!)html

高阶函数

  1. 高阶函数是一个函数,它接收函数做为参数或将函数做为输出返回。常运用于AOP,函数柯里化,节流或防抖等。
  2. 函数式编程的思想在不少的鼎鼎大名的开源库里运用很普遍,好比redux、koa等。特别是redux,里面有不少中间件本质其实就是高阶函数。

组件开发

  1. 组件开发的目的其实就是暴露出对外使用的接口。使用者只须要定义好订阅的事件,至于组件内部如何去实现的过程,对使用者来讲,这并非他所关心的。使用者只须要关心接口的入参和出参,是否异步等这些关键信息便可。
  2. 如何写出一个扩展性和稳定性都很好的接口,确实会考验开发者的设计水平。每每当用户有新的需求,然而目前的项目却不能支持,不得不面临着升级(万一开发者不能及时或者不升级呢?)。因此,优秀的开发者在定义接口时,会考虑好各类状况,而且将高度的自定义权利移交给使用者,虽然这样会给开发带来复杂度,可是带给用户的好处倒是成倍的。

举个vue的🌰

<div id="app" class='container'>
    <div style='margin: 50px'>{{title}}</div>
    <div style='margin: 20px; font-size: 16px'>{{message}}</div>
    <my-button :tap='tapString' type='我是字符串'></my-button>
    <my-button :tap='tapFunction' type='我是函数'></my-button>
    <my-button :tap='tapPromise' type='我是promise'></my-button>
  </div>
  
复制代码
<script>
  // 子组件
  Vue.component('my-button', {
    props: ['type'],
    methods: {
      handleClickMe: function () {
        // 普通返回
        var value = '我是一个普通数据类型,好比字符串,数字'
        var middleware = this.$attrs.tap(value)
        if (!middleware) {
          console.log('返回undefined,直接终止')
          return
        }
        if (typeof middleware === 'function') {
          middleware('我是一个函数')
        } else if (typeof middleware === 'object' && typeof middleware.then === 'function') {
          middleware.then(({ payload, callback }) => {
            typeof callback === 'function' && callback(payload + ', world!')
          }).catch(({ payload, callback }) => {
            typeof callback === 'function' && callback(payload + ', 不要紧!')
          })
        }
        // ...其余类型
      }
    },
    template: '<button v-on:click="handleClickMe">点我, {{type}} </button>'
  })
  // 父组件
  var app = new Vue({
    el: '#app',
    data: {
      title: '父组件',
      message: ''
    },
    methods: {
      tapString: function (res) {
        this.message = res
        // alert(res)
      },
      tapFunction: function (res) {
        var _this = this
        return function (data) {
          _this.message = data
          // alert(data)
        }
      },
      tapPromise: function (res) {
        var _this = this
        return new Promise((resolve, reject) => {
          _this.message = ''
          var number = Math.random()
          setTimeout(() => {
            if (number > 0.5) {
              resolve({
                payload: 'hello',
                callback: function (res) {
                  _this.message = res
                  // alert(res)
                }
              })
            } else {
              reject({
                payload: '发生错误了',
                callback: function (res) {
                  _this.message = res
                  // alert(res)
                }
              })
            }
          }, 1000)
        })
      }
    }
  })
</script>
复制代码
  1. 子组件有个tap属性的接口,当子组件触发click事件,会触发(发布)父组件订阅的tap方法。一般咱们写的组件的属性方法,相似tapString这样,经过函数参数的形式,获取自组件传递过来的结果便可。
  2. 若是换成高阶组件,能够返回一个函数,或者Promise甚至其余类型,这样自组件第一步执行tap属性函数,会获取一个函数执行的返回结果 var middleware = this.$attrs.tap(value),而后经过判断这个结果的类型,若是是函数或者是promise,再进行相应的处理便可。
if (!middleware) {
  return
}
if (typeof middleware === 'function') {
  middleware('我是一个函数')
} else if (typeof middleware === 'object' && typeof middleware.then === 'function') {
  middleware.then(({ payload, callback }) => {
    typeof callback === 'function' && callback(payload + ', world!')
  }).catch(({ payload, callback }) => {
    typeof callback === 'function' && callback(payload + ', 不要紧!')
  })
}
复制代码
  1. 经过这种高阶函数的一个接口,就能知足用户在不一样的使用场景下,都能获取满意的结果。好比异步获取的信息传递给组件,最后又从组件那里获取传递过来的结果信息。
  2. 这个🌰一样适用于react组件,只不过其中的语法不一样而已。
相关文章
相关标签/搜索