不要这样使用 React 导入,改用包装模式
https://dev.to/perisicnikola37/dont-use-react-imports-like-this-use-wrapper-pattern-instead-124p
在实际项目中,我遇到了一个低效的 React.js 导入策略问题。在这篇文章中,我将带你了解我遇到的问题。看看我是如何通过使用包装模式来改进设计,使其更加灵活。
问题来了在我的一个项目中,我看到像这样导入Framer Motion 和 lodash(或者其他任何库):
像这样导入:
引入 '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);
这种方法确保了只有必要的工具被包含在软件包中,从而大大减少了不必要的导入。
结论:
为什么你总是要给你的核心库写个包装层呢?
- 避免冗余导入 - 如果每个组件都手动从
framer-motion
导入特定的内容,不同组件可能会导入库的不同部分。 - 维护简单 - 如果库更新其导出内容或引入更优化的导入方式,只需更新包装文件即可,而无需修改每个组件。
- 代码库中的开发者知道该使用什么 - 使用包装器,开发者无需每次思考如何导入库的最佳方式。他们只需使用预定义的优化包装。
在选择一个库时,应该考虑它如何处理导入问题。例如,在 Recharts
和 Chart.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 更适合对性能有要求的应用程序。
备注:
- 本节并不是为了批评 Recharts,而是为了实际演示。
- Recharts 在即将发布的
v3
中将会解决这个问题。你可以查看我在这个仓库里打开的问题。
你可以在这里查看仓库 here。
希望这个解决方案对你有帮助!谢谢你的阅读。😊