美文网首页
基于 Decorator 的组件扩展实践

基于 Decorator 的组件扩展实践

作者: 南方帅 | 来源:发表于2018-12-28 11:22 被阅读0次

    原文链接
    在前端业务开发中,组件化已经成为我们的共识。沉淀和复用组件,是提高开发效率的利器。但在组件复用的过程中,我们往往会遇到这样的问题,组件相似,却在结构或交互上有些许差别,需要对组件进行改造方可满足需求。这个问题之前在

    @琼玖

    的文章 React实践 - Component Generator 就有所提及。

    之初,我们提出了组件配置式。在业务统一的情况下,仅仅修改组件用于配置的props就可以满足业务需求。但随着业务发生变化导致组件形态发生变化时,我们就必须不断增加配置去应对变化,便会出现配置泛滥,而在扩展过程中又必须保证组件向下兼容,只增不减,使组件可维护性的降低。

    最近的项目开发中,

    @Jason

    提出了组件组合式开发思想,有效地解决了配置式所存在的一些问题。下面我将详细阐述其思想与具体实现。

    组件再分离

    对于组件的 view 层,我们期望组件是没有冗余的,组件与组件间 view 重叠的部分应当被抽离出来,形成颗粒度更细的组件,使组件组合产生更多的可能。

    这种 view 细化的组合式思想早已在我们团队可视化库 Recharts 中有所体现。Recharts 避免了复杂的图表配置,而将图表进行有效拆分,通过声明式的标签进行组合,从而使图表更具扩展性。

    同样,我们在组件上也希望秉承这种思想,先来看一下在现有业务比较典型的三个公共组件:

    粒子化

    这三个组件无论在 UI 还是逻辑上均存在一定共性。在配置式中,我们会将这三个组件通过一个组件的配置变换来实现,但无疑会提高单个组件内部逻辑的复杂性。

    再做一次分离!它们可由 SelectInput、SearchInput 与 List 三个颗粒度更细的组件来组合。而对于颗粒度最细的组件,我们希望它是纯粹的,木偶式的组件。

    例如 SelectInput 组件,组件状态完全依赖传入的 props,包括 selectedItem (显示用户所选项)、isActive (当前下拉状态)、onClickHeader (反馈下拉状态)以及 placeholder (下拉框提示)。我们来看一下它的简要实现:

    class SelectInput extends Component {
      static displayName = 'SelectInput';
    
      render() {
        const { selectedItem, isActive, onClickHeader, placeholder } = this.props;
        const { text } = selectedItem || {};
        return (
          <div onClick={onClickHeader}>
            <Input 
              type="text"
              disabled
              value={text}
              placeholder={placeholder}
            />
            <Icon className={isActive} name="angle-down" />
          </div>
        );
      }
    }
    
    

    当组件被再次分离后,我们可以根据业务中的组件形态对其进行任意组合,形成统一层,摆脱在原有组件上扩展的模式,有效提高组件的灵活性。

    逻辑再抽象

    那么有了 view 细化再重组的公共组件后,是不是就可以愉快地开发了?

    是的,但组件层面的抽象不应该只停留在 view 层面,组件中的相同交互逻辑和业务逻辑也应该进行抽象。

    @诚身

    的文章 ReactEurope 2016 小记 - 上 中提到复用高阶函数的思想,编写 Higher-Order Components (高阶组件)来为基础组件增加新的功能。

    Higher-Order Components = Decorators + Components。在我们的组件中,也正是贯穿着这样函数式的思想,来完成组件逻辑上的抽象,例如:

    // 完成SearchInput与List的交互
    const SearchDecorator = Wrapper => {
      class WrapperComponent extends Component {
        handleSearch(keyword) {
          this.setState({
            data: this.props.data,
            keyword,
          });
          this.props.onSearch(keyword);
        }
    
        render() {
          const { data, keyword } = this.state;
          return (
            <Wrapper
              {...this.props}
              data={data}
              keyword={keyword}
              onSearch={this.handleSearch.bind(this)}
            />
          );
        }
      }
    
      return WrapperComponent;
    };
    
    // 完成List数据请求
    const AsyncSelectDecorator = Wrapper => {
      class WrapperComponent extends Component {
        componentDidMount() {
          const { url } = this.props;
    
          fetch(url)
          .then(response => response.json())
          .then(data => {
            this.setState({
              data,
            });
          });
        }
    
        render() {
          const { data } = this.state;
          return (
            <Wrapper
              {...this.props}
              data={data}
            />
          );
        }
      }
    
      return WrapperComponent;
    }
    
    

    拥有 Decorator 之后,我们就能赋予组件能力了,例如合成 Search 组件:

    @SearchDecorator
    class Search extends Component {
      render() {
        return (
          <Selector
            {...this.props}
          >
            <SearchInput />
            <List />
          </Selector>
        );
      }
    }
    
    

    那么当我们将逻辑抽象成为多个 Decorator 时,又该如何去组合呢?你是否还记得

    @流形

    的文章 React Mixin 的前世今生 中提到的方法?没错,就是compose!这里建议读者 review 这篇文章,顺便回顾一下Mixin与高阶组件的不同点。

    // SelectedItemDecorator为List与SelectInput的交互,读者可以自行尝试实现
    const FinalSelector = compose(AsyncSelectDecorator, SearchDecorator, SelectedItemDecorator)(Selector);
    
    class SearchSelect extends Component {
      render() {
        return (
          <FinalSelector
            {...this.props}
          >
            <SelectInput />
            <SearchInput />
            <List />
          </FinalSelector>
        );
      }
    }
    
    

    小结

    选择

    在配置式组件内部,组件与组件间以及组件与业务间是紧密关联的,而对于开发人员而言需要完成的仅仅是配置的工作。而组合式意图打破这种关联,寻求单元化,通过颗粒度更细的基础组件与抽象组件共有交互与业务逻辑的 Decorator,使组件更灵活,更易扩展,也使开发者能够完成对于基础组件的自由支配。

    虽然组合式确实能解决配置式所存在的一些问题,但多层 Decorator 带来的多层包裹,会对组件理解和调试造成一定困难,也"不能"使用外部公有的方法。同时组合式所基于的函数式编程的思想能否被整个团队所接受,也是我们需要考量的问题。

    总结

    也是实现HOC的一种方式, 通过 对于逻辑的抽象可以生产 公共的组件 通过 对于业务的抽象 可以生产业务的组件 对于组件解耦和分离还是有作用的

    相关文章

      网友评论

          本文标题:基于 Decorator 的组件扩展实践

          本文链接:https://www.haomeiwen.com/subject/vbrslqtx.html