继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

不要这样导入 React 组件。试试使用包装器模式吧

汪汪一只猫
关注TA
已关注
手记 617
粉丝 130
获赞 719

不要这样使用 React 导入,改用包装模式
https://dev.to/perisicnikola37/dont-use-react-imports-like-this-use-wrapper-pattern-instead-124p

介绍

在实际项目中,我遇到了一个低效的 React.js 导入策略问题。在这篇文章中,我将带你了解我遇到的问题。看看我是如何通过使用包装模式来改进设计,使其更加灵活。

问题来了

在我的一个项目中,我看到像这样导入Framer Motionlodash(或者其他任何库):

像这样导入:
引入 'framer-motion' 的 motion, -> 110.63KB  
引入 'lodash' 的 _, -> 71.78KB
这种方法在某些类型的项目中会出现的问题

现代打包工具如 Vite 会将这些存放在共享块(树摇)中,不过在某些类型的项目中,这可能是个问题,你需要注意这一点。

让我来解释一下框架动效示例的情况。乍一看,110.63KB 看起来不多。然而,如果在多个组件文件中使用这个导入,打包大小会迅速增加。例如,如果你在 7 个组件中导入它,打包大小将会是:

7乘110.63KB等于774.41KB

如果你的应用总大小约为 1.5MB ,Framer Motion 可能单独就占到 51.3% !这么高的占比对动画来说是一个不小的负担。

这什么时候会发生的?

  • 当捆绑器没有将 framer-motion 打包为一个共享块时(例如,由于单仓库结构、不同版本、糟糕的代码分割配置等原因等)
  • 混合使用 CommonJS 和 ES Modules 导入
  • 单仓库中由于依赖提升不当的问题
解决方法

新建一个 MotionWrapper.tsx 文件:

    // 为了更小的包大小,我们从'framer-motion'导入'm',并将其赋值给一个常量'MotionWrapper',最后导出这个常量。
    import { m } from 'framer-motion'

    const MotionWrapper = m;

    export default MotionWrapper

请在上面提到的文件中加入{m},它仅包含必需的内容。

比如说现在你有这种情况:

// App.tsx
import { motion } from 'framer-motion'

// 这是一个简单的React组件
const App = () => {
    return (
        // 动画div
        <motion.div {smth} />
    )
}

改成这样

    // App.tsx
    // 导入MotionWrapper组件,用于包裹具有动画效果的元素
    import { MotionWrapper } from '@/components/MotionWrapper'

    const App = () => {
        // 返回一个带有动画效果的元素
        return (
         <MotionWrapper.div {smth} />
        )
    }

视觉呈现

现在我们的版本是这样的:

使用了vite-bundle-visualizer工具来生成。

另一个例子:Lodash,就像之前说的。

另一个低效的使用方法是像这样使用 Lodash

    import _ from 'lodash' // 功能齐全,增大包体积

更好的办法是创建一个这样的 LodashWrapper

    // LodashWrapper.tsx
    import debounce from 'lodash/debounce';
    import throttle from 'lodash/throttle';

    export const LodashWrapper = { debounce, throttle };

现在,不要导入整个库,只需要导入你需要的那部分。

    import { LodashWrapper } from '@/utils/LodashWrapper';  

    const handleInput = LodashWrapper.debounce((value) => {  
        console.log('防抖处理:', value);  
    }, 300);

这种方法确保了只有必要的工具被包含在软件包中,从而大大减少了不必要的导入。

结论:

为什么你总是要给你的核心库写个包装层呢?

  1. 避免冗余导入 - 如果每个组件都手动从 framer-motion 导入特定的内容,不同组件可能会导入库的不同部分。
  2. 维护简单 - 如果库更新其导出内容或引入更优化的导入方式,只需更新包装文件即可,而无需修改每个组件。
  3. 代码库中的开发者知道该使用什么 - 使用包装器,开发者无需每次思考如何导入库的最佳方式。他们只需使用预定义的优化包装。
如何选择正确的库呢?

在选择一个库时,应该考虑它如何处理导入问题。例如,在 RechartsChart.js 之间做选择时,一个重要的因素是它们如何管理导入。

Recharts 目前还不支持单独导入各个组件(component),因此你必须导入整个库,这会增加包体积。

Chart.js 而是支持按需导入,意味着你可以只导入所需的 特定图表的组件,从而减少最终打包的体积。

以下是 Recharts 强制进行大量导入的一个例子:

import { LineChart } from 'recharts'; // 注释:引入 recharts 的 LineChart 组件

通过使用 Chart.js,你可以优化你的导入操作:

    import { Chart, LineElement, CategoryScale } from 'chart.js';  
    // 注册图表组件
    Chart.register(LineElement, CategoryScale);

这种灵活性让 Chart.js 更适合对性能有要求的应用程序。

备注:

  1. 本节并不是为了批评 Recharts,而是为了实际演示。
  2. Recharts 在即将发布的 v3 中将会解决这个问题。你可以查看我在这个仓库里打开的问题
装饰者模式在实际项目中的应用

你可以在这里查看仓库 here

希望这个解决方案对你有帮助!谢谢你的阅读。😊

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP