资讯专栏INFORMATION COLUMN

基于Decorator的组件扩展实践

魏明 / 2328人阅读

摘要:在业务统一的情况下,仅仅修改组件用于配置的就可以满足业务需求。避免了复杂的图表配置,而将图表进行有效拆分,通过声明式的标签进行组合,从而使图表更具扩展性。而对于颗粒度最细的组件,我们希望它是纯粹的,木偶式的组件。

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

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

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

组件再分离

对于组件的 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 (
      
); } }

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

逻辑再抽象

那么有了 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 (
        
      );
    }
  }
  
  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 (
        
      );
    }
  }

  return WrapperComponent;
}

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

@SearchDecorator
class Search extends Component {
  render() {
    return (
      
        
        
      
    );
  }
}

那么当我们将逻辑抽象成为多个 Decorator 时,又该如何去组合呢?你是否还记得 React Mixin 的前世今生 中提到的方法?没错,就是compose!这里建议读者 review 这篇文章,顺便回顾一下Mixin与高阶组件的不同点。

// SelectedItemDecorator为List与SelectInput的交互,读者可以自行尝试实现
const FinalSelector = compose(AsyncSelectDecorator, SearchDecorator, SelectedItemDecorator)(Selector);

class SearchSelect extends Component {
  render() {
    return (
      
        
        
        
      
    );
  }
}
小结

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

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/80088.html

相关文章

  • 基于DecoratorReact高阶组件思路分析与实现

    摘要:在深入技术栈一书中,提到了基于的。书里对基于的没有给出完整的实现,在这里实现并记录一下实现的思路。在这里最小的组件就是。对比范式与父组件的范式,如果完全利用来实现的,将操作与分离,也未尝不可,但却不优雅。 在深入react 技术栈一书中,提到了基于Decorator的HOC。而不是直接通过父组件来逐层传递props,因为当业务逻辑越来越复杂的时候,props的传递和维护也将变得困难且冗...

    LinkedME2016 评论0 收藏0
  • 装饰模式(Decorator Pattern)

    摘要:通常有两种方式可以实现给一个类或对象增加行为继承机制,使用继承机制是给现有类添加功能的一种有效途径,通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。 装饰模式 (Decorator Pattern) 装饰模式能够实现动态的为对象添加功能,是从一个对象外部来给对象添加功能。通常有两种方式可以实现给一个类或对象增加行为: 继承机制,使用继承机制是给现有类添加功能的一种...

    gityuan 评论0 收藏0
  • 设计模式之装饰者模式

    摘要:相关设计模式装饰者模式和代理模式装饰者模式关注再一个对象上动态添加方法代理模式关注再对代理对象的控制访问,可以对客户隐藏被代理类的信息装饰着模式和适配器模式都叫包装模式关于新职责适配器也可以在转换时增加新的职责,但主要目的不在此。 0x01.定义与类型 定义:装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的...

    chuyao 评论0 收藏0
  • Vue with typescript

    摘要:想要使用语法的话,配合,这个插件,体验更佳,这个插件在语法中实现了。这种方式最接近的单文件组件的写法,如果一个完善项目从改成,用这种方法很快,只要加上和一些必要的变量类型就好了,然后用包裹就好。不推荐混入用这种方式写,无法实现多继承。 最近尝试了一下 TypeScript,试着把一个 Vue 项目改成了 TypeScript 的,感觉还不错 目前 Vue 和 TypeScript 的配...

    JerryWangSAP 评论0 收藏0
  • 前端每周清单半年盘点之 Node.js 篇

    摘要:前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点分为新闻热点开发教程工程实践深度阅读开源项目巅峰人生等栏目。对该漏洞的综合评级为高危。目前,相关利用方式已经在互联网上公开,近期出现攻击尝试爆发的可能。 前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点;分为新闻热点、开发教程、工程实践、深度阅读、开源项目、巅峰人生等栏目。欢...

    kid143 评论0 收藏0

发表评论

0条评论

魏明

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<