V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  ç™»å½•
› React
simonguo
V2EX  â€º  React

🎉 React Suite 5 发布了

  •  
  •   simonguo · 23 天前 · 2733 次点击

    距离 v4 的第一个版本已经有接近两年的时间了,在这段时间里 v4 一共迭代了 30 多个版本,新增了许多特性。有越来越多的开发者开始使用 React Suite,并且参与功能开发与改进。还有一直对我们提出宝贵建议的开发者们,我们再次表示感谢。希望 React Suite 能够陪伴您的产品持续成长,同时也希望能够服务到更多的开发者。

    React Suite 5 提升了组件可访问性以及提供了更丰富可定制化的组件。以下将详细介绍新增特性以及如何从 4.x 升级到 5.0。

    主要功能 ✨

    提升可访问(Accessibility)

    我们的希望可以让更多的用户在 React Suite 开发的产品上无障碍的使用。我们会在键盘操作、读屏设备等多个场景去改善 React Suite 提供的每一个组件。

    无障碍设计

    React Suite 遵循 WAI-ARIA 标准,对所有组件进行了重构,均具有开箱即用的适当属性和键盘交互功能。

    新增一套高对比度主题

    在 React Suite v4 我们参照 《 Web Content Accessibility Guidelines (WCAG) 》标准对颜色对比度的要求,对组件做了很大的改进,可以满足大多数用户。 我们还是希望在此基础上进行提升,并照顾到一小部分在视力上存在障碍的人群。 目前 React Suite 官方一共提供了 3 套主题 (light 、dark 、high-contrast)。

    采用 SVG Icon 代替 Icon font

    Icon font 存在存在一些渲染上问题,导致图标模糊,需要载入字体文件,内容区域闪烁等问题。 为了更好的可访问性(Accessibility),我们决定优先采用 SVG Icon 。 并且能够友好的兼容第三方的图标资源。

    import GearIcon from "@rsuite/icons/Gear";
    
    render(<GearIcon />);
    
    // output
    <svg>
      <path d="M11.967 ..."></path>
      <path d="M8 10a2 2 0 10.001-3.999A2 2 0 008 10zm0 1a3 3 0 110-6 3 3 0 010 6z"></path>
    </svg>;
    

    支持 CSS 变量

    当前主流的浏览器都已经支持 CSS 变量,我们计划提供一套组件 CSS 变量配置,可以更方便的做主题自定义,主题切换。

    采用函数组件重构

    我们采用函数组件重构大部分的组件,采用 React Hooks 所带来的新特性,提升开发体验。

    按需加载

    在 v4 中导入组件的时候需要区分是 cjs 还是 esm 。 在 v5 中这是自动的。

    // v4: cjs
    import Button from "rsuite/lib/Button";
    // v4: esm
    import Button from "rsuite/es/Button";
    
    // v5
    import Button from "rsuite/Button";
    

    对 Form 表单的改进

    • 改进 Form 在纯文本视图的效果。
    • 对组件进行重命名。
    `FormGroup` 重命名为 `Form.Group`
    `FormControl` 重命名为 `Form.Control`
    `ControlLabel` 重命名为 `Form.ControlLabel`
    `ErrorMessage` 重命名为 `Form.ErrorMessage`
    `HelpBlock` 重命名为 `Form.HelpText`
    
    • 表单校验支持对象结构。
    const model = SchemaModel({
      username: StringType().isRequired("Username required"),
      tags: ArrayType().of(StringType("The tag should be a string").isRequired()),
      role: ObjectType.shape({
        name: StringType().isRequired("Name required"),
        permissions: ArrayType().isRequired("Permissions required"),
      }),
    });
    
    const checkResult = model.check({
      username: "foobar",
      tags: ["Sports", "Games", 10],
      role: { name: "administrator" },
    });
    
    console.log(checkResult);
    

    输出的数据结构:

    {
      username: { hasError: false },
      tags: {
        hasError: true,
        array: [
          { hasError: false },
          { hasError: false },
          { hasError: true, errorMessage: 'The tag should be a string' }
        ]
      },
      role: {
        hasError: true,
        object: {
          name: { hasError: false },
          permissions: { hasError: true, errorMessage: 'Permissions required' }
        }
      }
    };
    

    详细的使用教程请阅读: 表单校验与Schema

    Avatar 新增支持 srcSet 、sizes 、imgProps 属性

    • srcSet: <img> 元素的 srcSet 属性。 使用此属性进行响应式图像显示。
    • sizes: <img> 元素的 sizes 属性。
    • imgProps: 如果该组件用于显示图像,则应用于 <img> 元素的属性。

    Slider 和 RangeSlider 支持 onChangeCommitted

    onChangeCommitted 和 onChange 不同的点在于,onChange 是每一次值的改变都会触发,而 onChangeCommitted 是在 mouseup 事件触发后并且值发生了改变而触发的的回调。

    DatePicker 和 DateRangePicker 功能改进

    • DatePicker 与 DateRangePicker 支持键盘输入。 C392D1BC-AD67-4692-BAC0-A273A74DC564

    • DateRangePicker 之前只能选择日期,在 v5 中可以选择时间。

    <DateRangePicker format="yyyy-MM-dd HH:mm:ss" />
    

    Badge 支持 color 属性

    color 属性设置徽标提示点样式

    <Badge color="red">Red</Badge>
    <Badge color="orange">Orange</Badge>
    <Badge color="yellow">Yellow</Badge>
    <Badge color="green">Green</Badge>
    <Badge color="cyan">Cyan</Badge>
    <Badge color="blue">Blue</Badge>
    <Badge color="violet">Violet</Badge>
    <Badge color="red" />
    <Badge color="orange" />
    <Badge color="yellow" />
    <Badge color="green" />
    <Badge color="cyan" />
    <Badge color="blue" />
    <Badge color="violet" />
    <Badge color="blue" content="99+" />
    <Badge color="violet" content="NEW" />
    
    895ED4F3-F258-4A98-B48A-857F964A0EA0

    对 Table 的改进

    • 重构 Table

    使用 react hooks 重构了 Table, 并改进了表格滚动时的性能。 废弃了 onDataUpdated 和 bodyRef 属性。

    对于一些要在表格内部渲染的组件,之前可以通过 bodyRef 获取表格的 body 容器。 现在我们可以通过 Table 的 ref 直接获取容器。

    // v4
    const bodyRef = useRef();
    return (
      <>
        <Table
          bodyRef={(body) => {
            bodyRef.current = body;
          }}
        />
        <CheckPicker container={() => bodyRef.current} />
      </>
    );
    
    // v5
    const ref = useRef();
    return (
      <>
        <Table ref={ref} />
        <CheckPicker container={() => ref.current.body} />
      </>
    );
    
    • 支持 rowSpan 合并行

    image

    const data = [
      {
        city: "New Gust",
        name: "Janis",
        rowspan: 2,
      },
      {
        city: "New Gust",
        name: "Ernest Schuppe Anderson",
      },
      {
        city: "Maria Junctions",
        name: "Alessandra",
        rowspan: 3,
      },
      {
        city: "Maria Junctions",
        name: "Margret",
      },
      {
        city: "Maria Junctions",
        name: "Emiliano",
      },
    ];
    return (
      <Table data={data}>
        <Column
          width={100}
          verticalAlign="middle"
          rowSpan={(rowData) => {
            return rowData.rowspan;
          }}
        >
          <HeaderCell>Name</HeaderCell>
          <Cell dataKey="city" />
        </Column>
        <Column width={100}>
          <HeaderCell />
          <Cell dataKey="name" />
        </Column>
      </Table>
    );
    

    新增 TagInput 组件

    对 Input 的增强,支持输入标签,管理标签。

    import TagInput from "rsuite/TagInput";
    
    return (
      <TagInput
        defaultValue={["HTML", "CSS"]}
        trigger={["Enter", "Space", "Comma"]}
      />
    );
    
    TagInput

    对 Carousel 的改进

    在 <Carousel> 组件上支持 onSelect, onSlideEnd, onSlideStart 属性。

    • onSelect: 活动项更改时触发的回调
    • onSlideEnd: 幻灯片过渡结束时触发的回调
    • onSlideStart: 幻灯片过渡开始时触发的回调

    从 v4 升级到 v5 🚀

    接下来将为您提供指导,从而能够迅速的从 v4 升级到 v5 。

    准备工作

    • React 升级到 16.8 以上版本。
    • 确保您当前的 rsuite 版本是 4.*, 否则请先迁移到 v4。

    运行 codemods

    对于大型项目来说升级组件的过程往往是痛苦的,我们准备了 codemods 来简化您的迁移体验 。

    使用说明

    npx rsuite-codemod <transform> <path> [...options]
    
    • transform - 转换的名称。
    • path - 要转换的文件或目录。
    • 使用 --dry 选项进行试运行,并使用 --print 打印输出以进行比较。

    不兼容的变更

    不再对 IE 10 进行兼容支持

    我们将在 v5 版本中不再支持 IE 10,如果您需要继续在 IE 10 浏览器上使用请继续使用 v4 版本。

    - last 2 versions or > 1% or ie >= 10
    + last 2 versions or > 1% and not ie <11
    

    采用 SVG Icon 代替 Icon font

    使用 SVG Icon,使用前您需要安装 @rsuite/icons。

    npm i @rsuite/icons
    
    // for rsuite v4
    import { Icon } from "rsuite";
    
    return <Icon icon="gear" />;
    
    // for rsuite v5
    import GearIcon from "@rsuite/icons/Gear";
    
    return <GearIcon />;
    

    移除了 size 属性,采用 fontSize 代替。

    // for rsuite v4
    return <Icon icon="gear" size="3x" />;
    
    // for rsuite v5
    return <GearIcon style={{ fontSize: "3em" }} />;
    

    size 属性值及其对应的 fontSize 值关系如下:

    • lg : 1.3333em
    • 2x : 2em
    • 3x : 3em
    • 4x : 4em
    • 5x : 5em

    date-fns 升级 v2

    在 React Suite 中使用了 date-fns 工具用于对日期格式、计算等等。基于 Unicode 标准,用于格式功能的新格式字符串有变更。

    // for rsuite v4
    
    return (
      <>
        <DatePicker format="YYYY-MM-DD" />
        <DateRangePicker format="YYYY-MM-DD" />
      </>
    );
    
    // for rsuite v5
    
    return (
      <>
        <DatePicker format="yyyy-MM-dd" />
        <DateRangePicker format="yyyy-MM-dd" />
      </>
    );
    

    废弃 Alert 组件,用 toaster.push(<Message>) 代替

    所有的弹出的通知消息,都使用新的 API toaster 进行管理。Alert 组件将会被废弃,替代的方式是通过 toaster 与 Message 组合使用。 例如:

    // for rsuite v4
    Alert.info("description");
    
    // for rsutie v5
    toaster.push(
      <Message type="info" closable>
        description
      </Message>
    );
    

    删除一个消息或者清空消息

    // Remove message
    const key = toaster.push(
      <Message type="info" closable>
        description
      </Message>
    );
    toaster.remove(key);
    
    // Clear all messages
    toaster.clear();
    

    修改 Notification 使用方式

    // for rsuite v4
    Notification.info({
      title: "info",
      description: "description",
      duration: 4500,
      placement: "topStart",
    });
    
    // for rsuite v5
    toaster.push(
      <Notification type="info" header="info" duration={4500}>
        description
      </Notification>,
      { placement: "topStart" }
    );
    

    Form 相关组件重命名

    • FormGroup 重命名为 Form.Group
    • FormControl 重命名为 Form.Control
    • ControlLabel 重命名为 Form.ControlLabel
    • ErrorMessage 重命名为 Form.ErrorMessage
    • HelpBlock 重命名为 Form.HelpText

    所有组件的 componentClass 属性重命名为 as

    // for rsuite v4
    return <Button componentClass="span" />;
    
    // for rsuite v5
    return <Button as="span" />;
    

    默认关闭所有 Picker 的 virtualized

    所有的 Picker 的 virtualized 的默认值为 false。如果您希望继续在项目中使用,需要设置为 true。

    <SelectPicker virtualized />
    

    改进异步更新子级的方式

    为了方便异步更新子节点,新增了一个 getChildren 属性。受影响的组件:

    • Cascader
    • MutilCascader
    • TreePicker
    • CheckTreePicker
    getChildren:(node: ItemDataType) => Promise<ItemDataType[]>
    
    function fetchNodes(id) {
      return new Promise((resolve) => {
        // fetch the child node data async
        resolve(childrenNodes);
      });
    }
    
    return (
      <>
        <Cascader getChildren={(node) => fetchNodes(node.id)} />
      </>
    );
    

    删除 Table.Pagination, 并增强 Pagination

    Table.Pagination 组件在本次更新中删除,请使用 Pagination 代替,新增了 layout 属性,用于自定义布局。

    // for rsuite v4
    return (
      <Table.Pagination
        lengthMenu={[
          { value: 50, label: 50 },
          { value: 100, label: 100 },
        ]}
        activePage={1}
        displayLength={20}
        total={100}
        onChangePage={handleChangePage}
        onChangeLength={handleChangeLength}
      />
    );
    
    // for rsuite v5
    return (
      <Pagination
        limit={50}
        limitOptions={[50, 100]}
        layout={["total", "-", "limit", "|", "pager", "skip"]}
        total={100}
        activePage={1}
        onChangePage={handleChangePage}
        onChangeLimit={handleChangeLimit}
      />
    );
    

    使用 CustomProvider 替换 IntlProvider

    // for rsuite v4
    import { IntlProvider } from "rsuite";
    import zhCN from "rsuite/lib/IntlProvider/locales/zh_CN";
    
    return (
      <IntlProvider locale={zhCN}>
        <App />
      </IntlProvider>
    );
    
    // for rsuite v5
    import { CustomProvider } from "rsuite";
    import zhCN from "rsuite/locales/zh_CN";
    
    return (
      <CustomProvider locale={zhCN}>
        <App />
      </CustomProvider>
    );
    

    废弃 <Sidenav> 组件的 activeKey 和 onSelect 属性

    <Sidenav> 组件总是和 <Nav> 组件配合使用。 您应当使用 <Nav> 组件的 activeKey 和 onSelect 属性。

    // for rsuite v4
    
    return (
      <Sidenav activeKey={activeKey} onSelect={setActiveKey}>
        <Sidenav.Body>
          <Nav>
            <Nav.Item>Nav item</Nav.Item>
            <Dropdown title="Dropdown">
              <Dropdown.Item>Dropdown item</Dropdown.Item>
            </Dropdown>
          </Nav>
        </Sidenav.Body>
      </Sidenav>
    );
    
    // for rsuite v5
    
    return (
      <Sidenav>
        <Sidenav.Body>
          <Nav activeKey={activeKey} onSelect={setActiveKey}>
            <Nav.Item>Nav item</Nav.Item>
            <Dropdown title="Dropdown">
              <Dropdown.Item>Dropdown item</Dropdown.Item>
            </Dropdown>
          </Nav>
        </Sidenav.Body>
      </Sidenav>
    );
    

    按需加载

    导入组件

    // v4
    import Button from "rsuite/lib/Button";
    import "rsuite/lib/Button/styles/index.less";
    
    // v5
    import Button from "rsuite/Button";
    import "rsuite/Button/styles/index.less";
    

    导入本地化语言包

    // v4
    import ruRU from "rsuite/lib/IntlProvider/locales/ru_RU";
    
    // v5
    import ruRU from "rsuite/locales/ru_RU";
    

    全局导入样式

    // v4
    import "rsuite/lib/styles/index.less"; // less
    import "rsuite/dist/styles/rsuite-default.css"; // css
    
    // v5
    import "rsuite/styles/index.less"; // less
    import "rsuite/dist/rsuite.min.css"; // or css
    import "rsuite/dist/rsuite-rtl.min.css"; // or rtl css
    

    废弃 <Dropdown> 组件的 renderTitle 属性

    废弃 renderTitle,取而代之的是 renderToggle。

    //v4
    return (
      <Dropdown
        renderTitle={() => (
          <IconButton appearance="primary" icon={<Icon icon="plus" />} circle />
        )}
      >
        ...
      </Dropdown>
    );
    
    //v5
    return (
      <Dropdown
        renderToggle={(props, ref) => (
          <IconButton
            {...props}
            ref={ref}
            icon={<PlusIcon />}
            circle
            appearance="primary"
          />
        )}
      >
        ...
      </Dropdown>
    );
    

    用 open/close 代替 show/hide。

    在 v4 版本中组件的属性命名同时存在 open/close 和 show/hide 的使用。在 v5 中将命名统一起来。

    // v4
    
    <Modal show="{true}" onShow="{...}" onHide="{...}" />
    <Drawer show="{true}" onShow="{...}" onHide="{...}" />
    <Whisper delayHide="{1000}" delayShow="{1000}" />
    
    // v5
    <Modal open="{true}" onOpen="{...}" onClose="{...}" />
    <Drawer open="{true}" onOpen="{...}" onClose="{...}" />
    <Whisper delayClose="{1000}" delayOpen="{1000}" />
    

    支持 React Suite

    如果您喜欢 React Suite,可以通过以下方式支持我们:

    • Star 这个项目。
    • 如果您在您的项目中使用了 React Suite,欢迎在这里留言
    • 在 OpenCollective 上赞助我们。
    10 条回复  â€¢  2021-10-13 14:26:51 +08:00
    niuroumian
        1
    niuroumian   23 天前
    请问 table 组件是否支持 header filter 功能?
    ragnaroks
        2
    ragnaroks   23 天前
    以前用过,总觉得顶部导航和边栏导航不太好看
    lrvinye
        3
    lrvinye   23 天前 via iPhone
    之前用过,风格比较舒服,很喜欢💯
    ericgui
        4
    ericgui   23 天前
    支持
    simonguo
        5
    simonguo   22 天前
    @ericgui @lrvinye 谢谢
    @ragnaroks 可以个性化定制
    @niuroumian 目前不支持,不过 table 的 API 都比较方便,通过和 CheckPicker 组合,实现一个 filter 不是什么麻烦的事情。
    yazoox
        6
    yazoox   21 天前
    有没有使用 theme 和 language 的具体例子?
    zikkeung
        7
    zikkeung   21 天前
    跟知乎的贴一模一样
    huai
        8
    huai   7 天前
    Modal 或者 drawer 隐藏后 默认销毁?能否加开关控制呢
    simonguo
        9
    simonguo   6 天前
    @yazoox examples 里有相关的例子,不过示例还没有升级到最新版本,可以先参考,最近有计划把示例全部更新一下 https://github.com/rsuite/rsuite/tree/main/examples

    @zikkeung 是的

    @huai 没有对应的开关,默认情况下不渲染的组件不会生成 DOM 。 不知道你这样的需求的应用场景是什么?
    huai
        10
    huai   6 天前
    @simonguo #9 无非是 modal 里用来做展示得,生成出来得代价比较昂贵。 比如 modal 里,展示某个组件,这个组件需要获取数据等等
    关于   ·   帮助文档   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1168 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 18:44 · PVG 02:44 · LAX 11:44 · JFK 14:44
    ♥ Do have faith in what you're doing.