logoAnt Design

⌘ K
  • 设计
  • 研发
  • 组件
  • 博客
  • 资源
  • 国内镜像
5.25.4
  • v6 的一些 CSS 琐事
  • 👀 视觉回归测试
  • 为什么禁用日期这么难?
  • 封装 Form.Item 实现数组转对象
  • 行省略计算
  • 📢 v4 维护周期截止
  • antd 里常用的 TypeScript 工具方法
  • 一个构建的幽灵
  • 当 Ant Design 遇上 CSS 变量
  • API 的历史债务
  • 灵动的 Notification
  • 色彩模型与颜色选择器
  • 主题拓展
  • 虚拟表格来了!
  • 快乐工作主题(一)
  • 动态样式去哪儿了?
  • Suspense 引发的样式丢失问题
  • 打包体积优化
  • 你好,GitHub Actions
  • 所见即所得
  • 静态方法之痛
  • SSR 静态样式导出
  • 依赖排查
  • 贡献者开发维护指南
  • 转载-如何提交无法解答的问题
  • 新的 Tooltip 对齐方式
  • 非必要的渲染
  • 如何成长为 Collaborator
  • Modal hook 的有趣 BUG
  • antd 测试库迁移的那些事儿
  • Tree 的勾选传导
  • getContainer 的一些变化
  • 组件级别的 CSS-in-JS

打包体积优化

2023-06-25
@zombieJ

文章被以下专栏收录:

antd

Ant Design

Juejin logoAnt Design 开源专栏
Juejin logo我有想法,去参与讨论
文档贡献者
  • Suspense 引发的样式丢失问题你好,GitHub Actions

    相关资源

    Ant Design X
    Ant Design Charts
    Ant Design Pro
    Pro Components
    Ant Design Mobile
    Ant Design Mini
    Ant Design Web3
    Ant Design Landing-首页模板集
    Scaffolds-脚手架市场
    Umi-React 应用开发框架
    dumi-组件/文档研发工具
    qiankun-微前端框架
    Ant Motion-设计动效
    国内镜像站点 🇨🇳

    社区

    Awesome Ant Design
    Medium
    Twitter
    yuque logoAnt Design 语雀专栏
    Ant Design 知乎专栏
    体验科技专栏
    seeconf logoSEE Conf-蚂蚁体验科技大会
    加入我们

    帮助

    GitHub
    更新日志
    常见问题
    报告 Bug
    议题
    讨论区
    StackOverflow
    SegmentFault

    Ant XTech logo更多产品

    yuque logo语雀-构建你的数字花园
    AntV logoAntV-数据可视化解决方案
    Egg logoEgg-企业级 Node.js 框架
    Kitchen logoKitchen-Sketch 工具集
    Galacean logoGalacean-互动图形解决方案
    xtech logo蚂蚁体验科技
    主题编辑器
    Made with ❤ by
    蚂蚁集团和 Ant Design 开源社区

    在现代 JS 应用中,通过模块化打包工具可以自动将一些未使用的模块代码去除,这个过程叫做 Tree Shaking。不过如果你已经对此非常熟悉了,你会发现现实中它并不是那么完美,我们还需要一些额外的操作才能达到最佳的体积优化效果。今天,我们就来讲讲一个 ConfigProvider 导致的 Tree Shaking 失效的问题。

    ConfigProvider 与 rc-field-form

    在日常维护中,我们遇到了一些关于使用 ConfigProvider 会导致 BundleSize 变大的问题:

    • https://github.com/ant-design/ant-design/issues/41607
    • https://github.com/ant-design/ant-design/issues/43019
    • https://github.com/ant-design/ant-design/issues/42499

    而社区在反馈的同时也把被错误打包进来的包找了出来 rc-field-form,这里我们就直接借用一下 issue 中的图示:

    bundle size

    ConfigProvider 提供了全局化配置能力,其中也包含了 Form 组件校验信息的自定义模板配置:

    tsx
    <ConfigProvider form={{ validateMessages }} />
    Customize

    由于该功能对表单的校验有依赖关系,因而它由底层的 rc-field-form 提供的 FormProvider 实现。在 antd 中会先和自身的本地化后的 validateMessages 做聚合:

    tsx
    // Sample only. Not real world code.
    import { FormProvider } from 'rc-field-form';
    const ConfigProvider = ({ validateMessages, children }) => {
    const mergedValidateMessages = React.useMemo(
    () => merge(antdDefaultValidateMessages, validateMessages),
    [validateMessages],
    );
    return (
    <FormProvider validateMessages={mergedValidateMessages}>
    <SomeOtherProvider>{children}</SomeOtherProvider>
    </FormProvider>
    );
    };

    而 FormProvider 本身又对 rc-field-form 的 FormContext 进行了封装,导致引入 FormProvider 后将 rc-field-form 的更多内容给打包进来了:

    Deps

    你或许会想到,我们是不是可以做一个优化,如果没有配置 validateMessages 我们就不调用这个 FormProvider?

    tsx
    // Sample only. Not real world code.
    import { FormProvider } from 'rc-field-form';
    const ConfigProvider = ({ validateMessages, children }) => {
    let node = children;
    if (validateMessages) {
    node = <FormProvider validateMessages={merge(...)}>{node}</FormProvider>;
    }
    return node;
    };

    很遗憾,这是不行的。Tree Shaking 是静态编译过程,而 validateMessages 是一个运行时的配置。所以在打包过程中,我们无法知道 validateMessages 是否存在,因而无法做到这样的优化。

    拆解依赖

    rc-field-form 自然可以调整依赖,让 FormProvider 解耦。但是很明显,我们不应该依赖于第三方库的调整(虽然 rc-field-form 也由我们来维护)。我们应该从根本上解决这个问题,让 ConfigProvider 不再依赖于 FormProvider。实现也非常简单,既然这是 rc-field-form 所独有的,那么我们直接抽一个 Context 出来,让 ConfigProvider 不再感知 FormProvider 即可:

    tsx
    // Sample only. Not real world code.
    import { ValidateMessageContext } from '../form/context.ts';
    const ConfigProvider = ({ validateMessages, children }) => {
    const mergedValidateMessages = ...
    return (
    // Just use the proxy context
    <ValidateMessageContext value={mergedValidateMessages}>
    <SomeOtherProvider>{children}</SomeOtherProvider>
    </ValidateMessageContext>
    );
    };

    而在 Form 中,同样消费代理的 Context:

    tsx
    // Sample only. Not real world code.
    import Form, { FormProvider } from 'rc-field-form';
    import { ValidateMessageContext } from './context';
    export default (props) => {
    const validateMessages = React.useContext(ValidateMessageContext);
    return (
    <FormProvider validateMessages={validateMessages}>
    <Form {...props} />
    </FormProvider>
    );
    };

    依赖就变成了这样,从而实现了解耦:

    New Deps

    总结

    Tree Shaking 提供了一种自动化的方式来优化打包体积,但是我们需要注意一些细节。否则可能不慎导致一些依赖被错误的引入。以上。