ice-lab / icejs

仓库迁移至:https://github.com/alibaba/ice

Home Page:https://ice.work/docs/guide/intro

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[RFC] 通用场景的 Hooks 方案

chenbin92 opened this issue · comments

需求背景

React 的组件化开发特性给前端开发带来了新的开发体验,我们可以将一个页面分解成很多个组件,最后通过组件进行拼装组成完整的 UI 界面,以及组件可以很方便的在不同页面不同项目进行复用。

但是,随着业务功能复杂度的提高,组件大多数时候会和业务逻辑,生命周期函数等耦合到一起,导致很多重复的业务逻辑代码很难被抽离出来,为了快速开发很多时候我们经常是一把梭哈的复制粘贴,当业务代码逻辑发生变化时,我们又一把梭哈的同时修改多个地方,开发效率和可维护性可想而知,很多通用的逻辑也难以得到复用,如:

  • 状态及其逻辑的重复:比如 loading 状态,计数器等;
  • 副作用的逻辑重复:比如有同一个 ajax 请求、多个组件内对同一个浏览器事件的监听、同一类 dom 操作或者宿主 API 的调用等。

为了解决业务 逻辑复用的问题,React 官方也提供了相关方案如React.mixinHOCRender Props 等,但这些方案都各有利弊,如 Render Props 的嵌套问题,Hoc 的调试和 Ref 丢失问题,一直没有真正的解决好逻辑复用的问题。

类组件的不足:

  • 状态逻辑难复用
  • 趋向复杂难以维护
  • this 指向问题

直到 2019 年 React 最重磅的新特性 React Hooks 的出现,才得以真正解决逻辑复用的问题,在写法上更是颠覆。

Hooks 的优势:

  • 能在无需修改组件结构的情况下复用状态逻辑(自定义 Hooks )
  • 能将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据)
  • 副作用的关注点分离:副作用指那些没有发生在数据向视图转换过程中的逻辑,如 ajax 请求、访问原生dom 元素、本地持久化缓存、绑定/解绑事件、添加订阅、设置定时器、记录日志等。以往这些副作用都是写在类组件生命周期函数中的。

提案

定位

@ice/hooks:面向中后台业务场景的 Hooks 方案。

通过对通用的业务场景逻辑进行封装,无需在修改组件结构的情况下更优雅的进行逻辑复用,致力于提供一套覆盖中后台常用场景的 Hooks,让开发变得更简单,更友好。

使用

默认推荐从 ice 包进行导出

import { useTimeout, useInterval } from 'ice';
  • useTimeout
import { useTimeout } from 'ice';

const View = () => {
  const ready = useTimeout(2000);
  
  return <div>Ready: {ready ? 'Yes': 'No'}</div>
}
  • useInterval
import { useInterval } from 'ice';

const View = () => {
  const [count, setCount] = useState(0);

  useInterval(() => {
    setCount(count + 1);
  }, 1000);

  return <h1>{count}</h1>;
}

分类

一期主要分为如下几类:

  • State (状态)
    • useBoolean:boolean 值
    • useToggle:toogle 切换
    • useMap:管理 Map 类型状态
    • useSet:管理 Set 类型状态
    • useList:管理 Array 类型状态
    • useDefault:当 state 为 null 或 undefined 时返回默认值
    • useNetwork:判断网络状态
  • DOM
    • useEventListener:绑定 EventListener
    • useFullscreen:设置全屏
    • useHover:鼠标悬停
    • useMouse:鼠标位置
    • useInViewport:判断元素是否在可视范围之内
    • useScroll:获取元素的滚动位置
    • useKeyPress:键盘事件
  • Side-effects(副作用)
    • useFavicon: 设置页面的 favicon
    • useTitle: 设置页面的标题
    • useCookie:cookie 操作
    • useLocalStorage: 持久化到 localStorage
    • useDebounce:用来处理防抖
    • useDebounceFn:用来处理防抖(接收函数)
    • useThrottle:用来处理节流
    • useThrottleFn:用来处理节流(接收函数)
    • useTimeout: 在指定的毫秒数后返回 true
    • useInterval: 设置延迟
  • Lifecycles (生命周期)
    • useMount:类似 componentDidMount
    • useUnmount:类似 componentWillUnmount
    • useUpdate:对等 componentDidUpdate

实现

  • 新增 @ice/hooks 包,提供 Hooks 的实现(可单独使用)
  • 新增 build-plugin-ice-hooks 包,默认内置到 icejs 中,提供从 icejs 包导出的方式进行使用

FAQ

为什么不直接使用 react-use ?

react-use 提供了很多优秀的 Hooks,但是大部分在实际业务中可能使用不到,而 icejs 中提供的 Hooks 一方面是基于 react-use 的基础 Hooks,另一方面更多的是贴合业务的,由业务中衍生出来的 Hooks。

更新迭代机制是怎样的 ?

第一个问题其实已经回答,@ice/hooks 会基于社区已有的 react-use 进行合理刷选复用一部分基础的 Hooks,另外一部分是根据我们日常的业务进行抽象和沉淀到 @ice/hooks 中。

能不能单独使用 ?

可以单独使用

$ npm install @ice/hooks

打包策略是怎样的 ?

  • icejs 默认支持了按需加载,只会打包引用过的 Hooks

参考

其他

  • 整理业务场景的 Hooks
  1. 不用 react-use 的主要原因是 react-use hook 太多了,但我觉得多不是问题,用 ice/hooks 的话除了比 react-use 精简,还有其他优势吗?
  2. 「icejs 默认支持了按需加载,只会打包引用过的 Hooks」这个要看一下。。。

「更多的是贴合业务的,由业务中衍生出来的 Hooks」,这些可能要抽象出来看一下有哪些。

  1. 不用 react-use 的主要原因是 react-use hook 太多了,但我觉得多不是问题,用 ice/hooks 的话除了比 react-use 精简,还有其他优势吗?
  2. 「icejs 默认支持了按需加载,只会打包引用过的 Hooks」这个要看一下。。。
  1. 除了精简, 更多的是希望提供业务场景的 Hooks,设想的是 @ice/hooks = react-use 高频使用的 hooks + 业务场景的 hooks

  2. 这个是计划需要支持的能力

「更多的是贴合业务的,由业务中衍生出来的 Hooks」,这些可能要抽象出来看一下有哪些。

上面补充了,这块需要结合业务的场景具体抽象,目前想到的是中后台常见的比如表格,表单,列表,搜索这些。比如下面 Fusion Design Pro 模板的一个界面进行分析,可以抽象 useSearch、useProfile、useForm、useTable 之类的,本质上就是将业务逻辑(数据请求、loading 状态、分页、排序等)从组件里面抽象出来。

组件 = UI 视图+ Hooks 逻辑

image

如果提供 import { useXxx } from 'ice' 的方式,那物料里需要用这些 hooks 怎么搞?走 react-use ?

// 方案 1:项目和物料不一致
import { useInterval, helpers: { cookie } } from 'ice';
import { useInterval } from '@ice/hooks'; 
import { cookie } from '@ice/helpers';

// 方案 2:项目和物料一致
import { useInterval } from '@ice/hooks';
import { cookie } from '@ice/helpers';

// 方案 3:项目和物料一致
import { useInterval } from 'react-use';
import { cookie } from '@ice/helpers';

梳理一下目前理解的物料(主要指 Fusion 区块),如果使用 @ice/hooks、@ice/helpers、@ice/store、@ice/request 的可能性,以及结合项目使用的流程是怎样的

当下

物料只依赖 @alifd/next,看了下目前 36 个区块是没有一个有依赖第三方库的,都是 peerDependencies 依赖 @alifd/next。

  • 只依赖 @alife/next 的好处是依赖唯一,下载到项目之后不兼容性的概率小
  • 仅限于 UI + 本地 state,只是 UI 复用,二次开发改造成本大

推演

假设物料除了依赖 @alifd/next,还有可能依赖 @ice/hooks、@ice/helpers,@ice/request,@ice/store。

  • 物料依赖越多,下载到项目之后不兼容问题存在的概率越大
  • 物料会依赖到 @ice/hooks 里的什么能力,从目前 Fusion 区块和 react-use 的 hooks 列表来看,非常之少
  • 物料会依赖到 @ice/helpers 里的什么能力,目前只有 cookie 和 urlParse,区块依赖几乎不会依赖

问题一:以此类推,在物料里会使用到 @ice/hooks、@ice/helpers、@ice/request、@ice/store 吗

从目前的区块来设计来看,是基本不会用到的,但是不会用到不代表设计时可以不考虑。

问题二:那在什么场景下可以在物料里使用到上述库或者其他依赖,界限是什么?

一个合格或者好的区块,首先需要定义清楚这个「区块使用的宿主环境是什么」,因为区块本质是代码片段,是需要下载到宿主环境里然后还能正常工作的,并不是所有的区块在任何地方都能使用。

从目前的区块设计来看,宿主环境是只要能正常运行 @alifd/next 的即可。因此目前的区块只是 peerDependencies 依赖 @alifd/next。

反之推演,如果一个区块需要依赖 @ice/hooks、@ice/helpers、@ice/request、@ice/stote 那是否意味者当前区块的宿主环境就是 icejs,那么这个界限就很清楚了。

综上

考虑「物料 + 项目」组合的场景,除了目前的 helpers 和 hooks,可能还存在的问题有 request 和 store。

基于此,是否能提供一套「基于 icejs 宿主环境提供配套的物料」。

与 icejs 配套,不考虑大而全的通用场景,可提供 「UI + State」 和 「UI + Data Model」两种方式的区块

蚂蚁这边沉淀了一些 hooks:https://github.com/umijs/hooks

目前已经在蚂蚁中台推广开来。希望可以共建 hooks 库,这样阿里集团共用一份,对开发者和用户都比较方便。

当然,共建具体事宜可以再行商量~

期待😘~~~~~

蚂蚁这边沉淀了一些 hooks:https://github.com/umijs/hooks

目前已经在蚂蚁中台推广开来。希望可以共建 hooks 库,这样阿里集团共用一份,对开发者和用户都比较方便。

当然,共建具体事宜可以再行商量~

@brickspert

之前也确实有考虑直接使用 umi/hooks,对社区用户会比较友好以及避免不必要的重复建设,主要是看到其中有一些 hooks 耦合了 antd 组件,所以就放弃了。

如果有机会可以共建我个人是非常乐意的,之前设想的大致思路如下,具体怎么共建可以约个时间对下。

  • 基础通用的 hooks :类似 react-use 的定位,提供通用的 hooks(不耦合组件库)。
  • 业务场景的 hooks :基于 antd 组件或者 Fusion 组件进行业务场景的封装(可耦合组件库)。

蚂蚁这边沉淀了一些 hooks:https://github.com/umijs/hooks
目前已经在蚂蚁中台推广开来。希望可以共建 hooks 库,这样阿里集团共用一份,对开发者和用户都比较方便。
当然,共建具体事宜可以再行商量~

@brickspert

之前也确实有考虑直接使用 umi/hooks,对社区用户会比较友好以及避免不必要的重复建设,主要是看到其中有一些 hooks 耦合了 antd 组件,所以就放弃了。

如果有机会可以共建我个人是非常乐意的,之前设想的大致思路如下,具体怎么共建可以约个时间对下。

  • 基础通用的 hooks :类似 react-use 的定位,提供通用的 hooks(不耦合组件库)。
  • 业务场景的 hooks :基于 antd 组件或者 Fusion 组件进行业务场景的封装(可耦合组件库)。

嗯嗯,共建是最好的。我先出个初步的共建方案。❤️

结论:共建 ahooks 库