浅谈组件增强

来源:互联网 发布:房产中介端口推荐 编辑:程序博客网 时间:2024/06/14 21:33

11月的上海,褪去了一丝温暖,夹带着丝丝寒意。独自一人走在街上,望着路边的男男女女,不禁让我想起了那个前任的她。因为有她,我才学会了做饭,学会照顾人,学会怎么谦让,不过在生命的进程中,她不过是一个过客罢了,她的出现和离开,对我都没有本质的改变。一阵寒风吹过,我再次抬头,是啊,刚才的故事蛮好的,是时候来知乎分享刚编的故事了,显然,程序员并不会有前任。

细细想一想,其实生活中存在很多起修饰作用的东西,万物本通,在编程界,也有许多的起修饰作用的东东,而且基于这个概念还发展出了许许多多的名称。

装饰函数

来首先看第一个装饰函数,函数可以接受参数并且返回值。如果接受的值和返回的一样呢?如果我们有一个构造函数:

  1. function Person(name) {

  2.  this.name =  name

  3. }

  4. Person.prototype.greet = function() {

  5.  console.log('hello:' + this.name)

  6. }

现在由于 Person 函数是被封装的,不可以改变其源代码,而又想在先增加一个输出时间的方法,再调用原输出。就可以简单采用如下方式:

  1. const cloudic = new Person('cloudic')

  2. cloudic.greet() // => hello:cloudic

  3. // 函数直接包装

  4. function sayDate(f, ...args) {

  5. console.log('show date')

  6. typeOf f === 'funciton' && f(...args)

  7. }

  8. cloudic.dateGreet = sayDate(cloudic.greet)

  9. cloudic.dateGreet() // => show date hello:cloudic

  10. // 采用原型继承包装

  11. function DateDecorator(man) {

  12.  const newMan = Object.create(man)

  13.  newMan.greet = function() {

  14.     console.log('show date')

  15.     man.greet()

  16.  }

  17.  return newMan

  18. }

  19. const newCloudic = DateDecorator(cloudic)

  20. newCloudic.greet() // => show date hello:cloudic

这样子类的更改并不会影响其他子类,当然第一种看起来更加简单点,如果希望所做的改变能同步影响到子类,可以覆盖原型对象上同名的方法,当然也可以用 decorator 语法糖。

为什么想动态增加个功能这样繁琐呢,JS 是动态语言,在运行的时候,可以方便的修改对象的属性,但是由于函数存在作用域的限制,想要动态修改函数内部的变量很难实现。所以才绕着弯子,采用包装的方式来实现。换个角度想下,如果一个对象或者函数内部提供了一个方法,该方法可以动态的修改函数内部的变量,那我们只需要调用该方法即可,这样的模式暂且叫中间件模式吧,比如 redux 的中间件。

高阶组件

说回组件层面,如果把上面说的应用到组件上,那么就是传入一个组件返回另外一个组件。在 React 中,我们比较常用的叫 HOC 模式,常见的实现方式如下:

  1. function HOC(WrappedComponent) {

  2.  return class Wrap extends React.Component {

  3.    render() {

  4.      return <WrappedComponent {...this.props}/>

  5.    }

  6.  }

  7. }

在使用的时候,只需要传入需要包装的组件,调用函数后会返回一个包装过的组件。这样的好处是通过避免直接调用原组件,采用类似代理的方式来间接调用,从而可以方便后期替换原组件,可以做适配器,同时相比于 mixin 的实现方式,高阶组件更容易被调试,由于 mixin 是混入模式,导致在组件之间共享的代码很隐晦,如果方法比较多的话,还可能会覆盖现有的组件,所以 React 官方废弃了 mixin 的模式,推崇高阶组件来代替组件之间共享代码。

抽象组件

在 Vue 中,如果想对一个组件或者 DOM 进行相关功能的增强,我们可以用下面几种方式:

其一我们可以想到是采用指令的形式:

  1. Vue.directive('name', {

  2.  inserted: function (el) {

  3.    // do something...

  4.  }

  5. })

指令可以方便的增加功能和复用,但是指令不能使用回调函数,不能传 props,没有事件,导致使用起来的灵活度不高。指令更多的是做 DOM 层面的工作,封装一些方法来修饰元素或操作元素属性。

其二我们可以用函数式组件:

  1. Vue.component('name', {

  2.  functional: true,

  3.  render: function (createElement, context) {

  4.    // ...do something

  5.  },

  6.  // Props 可选

  7.  props: {

  8.    // ...

  9.  }

  10. })

函数式组件不会被实例化,也就是没有 data 和 this 上下文,也不能使用事件回调。可以简单的认为就是一个函数而已。不过函数式组件的优点也在于此,并不会有额外的性能开销,从而可以提高程序的性能。

其三就是采用抽象组件:

  1. Vue.component('name', {

  2.  abstract: true,

  3.  render: function (createElement) {

  4.    return this.$slots.default[0]

  5.  }

  6. })

抽象组件在 Vue 官网并没有文档介绍,因为这是一个内部组件定义的功能,不是很稳定,随时可能会更改,并且不会通知到你。Vue 内置的组件比如 keep-alive, transition, component, slot 都是抽象组件,抽象组件没有自己的 DOM 元素,只是简单增加功能然后返回子元素。和纯函数组件相比,它有自己的生命周期,会被实例化,内部有this,是一个真正的组件,所以可以用 emit 发放事件。

总结

在组件增强的各个方法中,每个都有优劣,至于到底选择那个,还是需要使用者根据业务场景来决定。


原文:https://zhuanlan.zhihu.com/p/30818429


原创粉丝点击