在前端通常会导入一些方便的 utility 函式库,以 lodash
来说,它是一个够帮我们处理各种资料的函式库,可以减少写一些比较琐碎的程序码,至今每周都将近 4000 万次下载。但是因为 lodash
的历史较为久远,在 2012 年就已经被开源,到现在已经接近快 10 年,可能因为各种历史因素,导致目前 lodash
并不是使用 es module。
不是使用 es module 的套件就会面临一个问题「webpack 没有办法发挥 tree shaking 的功能」,因为 webpack 的 tree shaking 只能使用在符合 es module 规范的程序码。所以,在没有 tree shaking 的情况下,就有可能会把很多没用到的函式库一起打包进 bundle 中。而在使用 lodash
时就要特别注意 import 的方式,不同的方式就会导致不一样的结果。
接下来,我们就来看看, 用不同的方式 import lodash 会有什麽不一样的结果,在 Next.js 里面要怎麽优化呢?
我们需要一个乾净的 Next.js 环境测试 用不同的方式 import lodash 会有什麽不一样的结果,使用以下指定建立一个新的专案:
yarn create next-app analyze-import-lodash
建立完後进入专案资料夹,安装我们需要的 lodash
:
yarn add lodash
在分析之前,先来介绍一款分析打包结果的视觉化套件 — webpack bundle analyzer,有了这套工具我们就可以清楚地从视觉化的图案知道每次打包後的档案大小,可以用来比较不同的 import
方式对 bundle size 会有什麽样的影响。
在 Next.js 中使用的不是原始的 webpack-bundle-analyzer
,而是官方包装过一层的 @next/bundle-analyzer
,因为在 Next.js 中设定 webpack 的方式不太一样,所以为了减少设定的流程,官方另外开源了这个套件,专门在 Next.js 中使用。我们使用以下指令安装这个套件:
yarn add -D @next/bundle-analyzer
接着,我们修改 next.config.js
的设定:
const withBundleAnalyzer = require("@next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true",
});
module.exports = withBundleAnalyzer({
reactStrictMode: true,
});
如果想要分析打包後的结果,可以运行以下指令,在执行完後就会打开像是上图的两个网页,分别为 客户端与服务器端的打包後的程序码:
ANALYZE=true yarn build
不指定 function 路径也就像我们平常使用 named exports 的 module 一样,我们稍微修改 pages/index.js
的程序码,在这个页面中使用 isEmpty
判断 title
是否为空的程序码:
import { isEmpty } from "lodash";
const Home = ({ title }) => (
<div>{isEmpty(title) ? "Title is Empty!!" : title}</div>
);
export default Home;
在各位读者还没看到结果之前,心里想的只有几行程序码的应用,bundle size 应该不会太大才对。如果是这样想的话,接下来看到的应该会让你大吃一惊。
接着,我们用 webpack bundle analyzer 看看使用这种 import 方式的 bundle size 为多少。
![lodash 原始 bundle size]](https://i.imgur.com/TQ1sjoI.png)
很夸张的是,明明只有用到 isEmpty
这个 function,结果 lodash 打包 Next.js 後的档案大小却足足有 531KB, 不禁让人怀疑 [isEmpty](https://github.com/lodash/lodash/blob/master/isEmpty.js)
是多麽伟大的 function ?,做了包山包海的事情。
isEmpty 的原始码: lodash/isEmpty.js
接下来,我们换一种 import
的方式,看看对 bundle size 会有什麽影响:
import isEmpty from "lodash/isEmpty";
const Home = ({ title }) => (
<div>{isEmpty(title) ? "Title is Empty!!" : title}</div>
);
export default Home;
修改完後,再次执行 ANALYZE=true yarn build
,让 webpack-bundle-analyzer
分析打包後的结果。天哪,换一种方式结果让 lodash
打包後的大小足足少了 22 倍,这是什麽魔法?
我们首先要知道 lodash
是一个使用 UMD (Universal Module Definition) 的套件,这意味着 lodash
并不满足在 webpack 中的 tree-shaking 必须是 es module 的条件。所以第一种方法实际上会载入完整的 lodash
,最终导致 bundle size 莫名的巨大;而第二种方法就是只载入一个档案,再从档案中拿出我们需要的 isEmpty
,如此一来就不用担心载入整包 lodash
的问题。
可是如果都要像第二种方法这样写 code 实际上有点麻烦,而且团队可能一开始没考虑到这个问题,程序码有很多地方都使用第一种方法 import lodash,改起来十分麻烦。
以下提供两种我认为比较简易的解法,可以用最少量的配置,达到降低 bundle size 的方法。
这也是 lodash 的 GitHub 提到的作法,lodash 的 GitHub 中写道:「Looking for Lodash modules written in ES6 or smaller bundle sizes? Check out lodash-es.」,所以第一种解法便是改用 lodash-es
:
// 下载 lodash-es
yarn add lodash-es
// 修改程序码 lodash 的引用,变成使用 lodash-es
import { isEmpty } from 'lodash-es';
你可以看到 bundle size 顺利地从 531KB 降低到 24KB 左右,与上面提到的第二种 import 的方法有异曲同工之妙。
如果你不想动到大量的程序码,上面使用 lodash-es
意味者必须全域取代 lodash
的引用,其实有另一个解法是使用 babel 的插件,让 babel 帮我们从第一种 import
的方式改成第二种。
首先,安装 babel-plugin-import 这个插件:
yarn add -D babel-plugin-import
然後修改 .babelrc
中的设定:
再用 webpack bundle analyzer 看看打包後的档案大小,可以看到档案打小与第二种 import
的方式一样都是 24.31KB,同样成功地降低 bundle size。
在这篇文章中我们了解了如何透过 webpack bundle analyzer 分析打包後的档案,并且透过这个工具看到不同 import lodash
方式对於 bundle size 的影响。
针对如何降低 lodash
被打包後的档案大小,本文提供两种方式,分别是使用 lodash-es
全域取代原本 lodash
的引用,或使用 babel-plugin-import 非侵入式的改动大量的程序码,而是在打包时处理,这两种方式都可以达到不把完整的 lodash 都打包进 bundle 的结果,就看各位如何选择罗!
自己的事件自己决定。 网页最重要的两件事,资讯显示与使用者交互,而使用者交互在页面中所代表的行为就...
昨天已经把 Memory Write 的功能做完了, 今天稍微轻松一点,就来完成 Memory St...
台湾游戏公司最新营收 | 第三季游戏公司营运「冻能」 | 蛤! 神逆转! 老大换人了 老二雄风不再...
如何实现一套基础开发流程? 在前面的章节里谈到了 GCP、Docker、Cloud Run 等技术,...
前言 使用float可以让元素浮起来~原本block会撑满父元素的宽,现在不会发生了,变得有点像in...