思想篇 - 通过 hooks 的出现,反思组件化开发存在的问题

4,082 阅读8分钟

前言

声明:此文不包含和 hooks 相关的所有的内容

因为在 react 项目当中使用了 hooksvue2.x 项目当中没有 hooks ,在现有的开发模式当中我们运用最多的还是 组件化开发 模式,但是在日渐复杂的业务逻辑中,好像 组件化开发 并不能完全满足我们的需求了,组件化开发 的优点深度越深,带来的维护 风险成本 越来越大,react 的官方尽早的发现了其存在的问题,所以,hooks 诞生了

当然,hooks 不只是解决了 组件化开发 所存在的问题,但是我认为这是最主要的原因之一

对于 hooks 诞生的相关原因请跳转 官方地址

hooks 的方案是最好的解决方案了吗

我认为不是的,看过我小册的朋友应该可以看到我所描述过的观点:

我从来不认为某一个框架或者思想是最好的,只不过在目前阶段它可能是最合适的

最好的框架永远都在未来,任何框架的诞生都是基于需求的,敬畏存在的一切,但也要保持怀疑

就是抱有这样的一个心态,我在各大论坛、博客、微信群等渠道,基于我现有的一种业务场景,做了将近半个月的探讨和调研,发现 hooks 并不能满足我们的需求,第一个硬伤就是直接 pass 掉它的方案,就是根本原因:框架的不统一

hooks 的存在可能更符合我之前的一个想法:干净,它可以让我们把所有的 行为布局展示 合理分离,使组件变的更 纯粹,但是复杂的业务场景,还是没办法完美的覆盖

下面是我自己通过调研和网友讨论的一个初步结果,属于雏形阶段,还希望大家多加讨论

项目场景案例

在多年的开发经验过程中,目前存在的已知的开发模式并不能满足我的所有需求,组件化开发代码的复用性 可以满足我大部分工作的模式,但是遇到很多繁琐而又复杂的需求,它们并不能为我提供我想达到的预期效果:

同一个功能(页面)中,基于部分功能是一致的,例如有一个弹框,里面有 2 个公共的弹窗,姓名 和 年龄,对于该用户信息的操作,对于不同的页面内,我们会有不同的数据处理,但是都是存在一个用户的数据表内,所以我们请求的接口也是一个的,这个时候我们把它封装起来,写成一个组件:

下面所有的片段代码都是伪代码,讲解大概的一个讲解一下案例,切勿当作实际代码跑demo

function Dialog(props) {
  function submit() {
    fetch({
      url: 'save',
      method: 'post',
      data: {
        // 所有的参数
      }
    })
  }
  const { children } = props
  return (
    <div>
      <div>
        <label>姓名</label>
        <input />
      </div>
      <div>
        <label>年龄</label>
        <input />
      </div>
      {children}
      <div>
        <button onclick="submit">确认</button>
      </div>
    </div>
  )
}

这样我们就可以在不同的页面中放置的去使用了,通过 chilren 把对应的其他类别的条件放进来,反正请求的是一个接口也是没有必要剥离的

这时我们可能有 3 个页面要用这个组件,其中一个页面需要添加一个 年龄 的文本框、一个需要添加 年龄职业 的文本框,还有一个是需要添加一个 籍贯 的文本框,我们发现有部分功能是重复的是可以复用的就是 年龄 文本框,我们想办法把它剥离出来写成单独的一个组件去调用

当然实际项目当中,不只是只有一个文本框这么简单,可能不同的下拉框都会有不同的逻辑判断,然而功能的目的地都是相同的,不剥离写n份,不易于维护,剥离写一份,嵌套地狱就是这样产生了

想法的诞生

基于上述的条件下我在考虑如何把这个事情给完美的解决掉,看问题要发现问题的本质才能更好的去解决问题,这种情况下我认为问题的根源是在过程,使用的页面或者输出的接口都是统一的,这里我们称之为 入口出口

结合我多年以来我了解的知识面并结合这样的业务场景我想到了一种解决办法,是不是可以通过中间件的模式去解决这种问题呢?

入口 -> 中间件(第一步处理、第二步处理....) -> 出口

问题定位到了,解决方法想出来了,接下来就是解决问题了

components-step 模式

这不是一个框架,也不是一个工具,只是一个提升开发效率、代码可读性、代码可易维护性的一种基于 组件化开发 的一种新的思路,我称之为 组件步骤化

git地址:github.com/nextdoorUnc…

欢迎 forkstarissues

安装

npm

npm install --save components-step

yarn

yarn add components-step

使用方式

import ComponentsStep from 'components-step'

ComponentsStep([mid1,mid2])(container, attr)

这种模式我之所以称之为 组件步骤化 是我觉得它做事情是一步一步来的,下一步的动作永远都是基于上一步的,保证了输入输出的稳定性,前面称呼为中间件的原因是通过中间件这个概念来去了解 组件步骤化 可能会更快和更方便一些,毕竟,这是一种开发模式,而不是一个功能

基于上面我所描述的需求,我们通过 组件步骤化 做一下处理:

components-step 作用

解决冗余代码

首先,我们肯定要保证的问题就是,如何让我们的代码不重复的,解决重复的代码,是我们要做的第一步:

function Mid1(props) {
  console.log(props, this)
  return () => {
    console.log(this)
    return (
      <div>mid1 {props ? 'true' : 'false'}</div>
    )
  }
}

function Mid2(props) {
  return () => {
    return (
      <div>mid2 {!props ? 'true' : 'false'}</div>
    )
  }
}

假设上面的方法就是我们那些在不同的条件下,重复的逻辑我们把他剥离出来,这里的写法和我们平时有一点的不同是多了一层的 return 这里我还没有构思好,但是我留出来的目的是为了可能后期这里会做中间件处理

这样不同的可以剥离出来,那么我们相同的代码就可以不用在写两遍了,这是我们要做的第一步

基于不同条件下组件渲染的灵活性

组件步骤化 虽然和 中间件(middleware) 的概念有相似之处,只不过 中间件(middleware) 在开发过程中起到的是一个辅助作用,而 组件步骤化 的每个步骤都很重要,它会直接决定我们未来的容器内,需要放置怎么样的内容呈现给用户:

import ComponentsStep from 'components-step'

const step = ComponentsStep([
  {
    label: 'Step1',
    stepItemMiddle: Mid1
  },
  {
    label: 'Step2',
    stepItemMiddle: Mid2
  }
])

ComponentsStep 就是我们 组件步骤化 的入口,用来收集所有的步骤化的组件:

  • label: 每一个标示,当要在父容器内渲染时,它就是该步骤的容器
  • stepItemMiddle: 用做所需渲染组件的方法,返回值需要是一个 function component

ComponentsStep 返回值也是一个 function ,该 function 接收两个值,一个是 容器(container),一个是 参数(attr),只有调用了 ComponentsStep 返回的函数渲染才会执行,因为组件的渲染可能需要依赖某些条件才可以完成,容器就是我们步骤化的组件需要放置于的位置,由于组件渲染的位置是不可控的,所以我们返回的不能是一个单一的队列,这样对于我们步骤化组件的渲染并不便利

容器渲染的灵活性

基于上面组件化开发的需求,我们可能有相同的功能在不同的页面内做渲染,所以要把不同的页面,也就是我们的容器入口给打开,保证功能的单一性及灵活性:

function HighFn(atom, attr) {
  return class extends Component {
    constructor() {
      super()
    }
    render() {
      return (
        <Dialog {...atom} />
      )
    }
  }
}
const arg = 1
step(HighFn, arg)

容器的灵活性直接影响到我们功能的实用性,频繁的函数调用也会影响渲染的速度,在为了保持该两个条件的情况下,我把他们放在了 ComponentsStep 的返回函数的参数内

highFn 就是所谓的 容器(container)arg 就是参数, ComponentsStep 的返回函数 step 调用后,highFn 的函数内会接收到两个参数:步骤化组件集合(atom)参数(attr)

数据支撑

components-step 目前可以非常好的满足我的业务需求,而且整体的数据对比也是比较可观的:

下方指标是基于目前我的业务代码去做的数据统计,误差区间和所统计数据不大
指标使用前使用后
代码量500 lines300 lines
代码阅读时长60s30s
维护成本所有关联函数都需要进行测试独立性更强,关联强度降低

结束语

我的想法和我的输出大概暂时就是这么多了,我希望大家可以各抒己见,一起努力让前端行业变得更美好

可能有很多大佬会有更好的方案,我也希望有机会学习,知识在讨论过程中才会变的更有价值嘛

最后在挂一下git地址,欢迎 forkstarissuesgithub.com/nextdoorUnc…

广告时间

前段时间写了本小册:《微前端 - 从概念到开发》,也希望大家多多支持,你少喝一杯瑞幸,我就早买一天奔驰,写的不好不要喷,憋着,下次见面直接揍,OK?

~~开个玩笑,还是希望有更多好的建议让我可以更好的输出有价值有意义的内容到社区,谢谢大家