iT邦幫忙

2021 iThome 鐵人賽

DAY 25
1
Modern Web

從零開始學習 Next.js系列 第 25

Day25 - 如何在 Next.js 中正確地使用 lodash,使用 babel-plugin-import

前言

在前端通常會導入一些方便的 utility 函式庫,以 lodash 來說,它是一個夠幫我們處理各種資料的函式庫,可以減少寫一些比較瑣碎的程式碼,至今每週都將近 4000 萬次下載。但是因為 lodash 的歷史較為久遠,在 2012 年就已經被開源,到現在已經接近快 10 年,可能因為各種歷史因素,導致目前 lodash 並不是使用 es module。

lodash 下載次數

不是使用 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 (@next/bundle-analyzer**)**

在分析之前,先來介紹一款分析打包結果的視覺化套件 — webpack bundle analyzer,有了這套工具我們就可以清楚地從視覺化的圖案知道每次打包後的檔案大小,可以用來比較不同的  import  方式對 bundle size 會有什麼樣的影響。

https://miro.medium.com/max/1400/0*UFykTIFSbESy4hsC.gif

在 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

分析用不同的方式 import lodash 的結果

第一種方式:如一般使用函式庫的方式 named import lodash

不指定 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

第二種方式:指定 function 的路徑

接下來,我們換一種  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/isEmpty

為什麼兩種 import 的方式會導致 bundle size 不一樣

我們首先要知道  lodash  是一個使用  UMD (Universal Module Definition)  的套件,這意味著  lodash  並不滿足在 webpack 中的  tree-shaking  必須是 es module 的條件。所以第一種方法實際上會載入完整的  lodash ,最終導致 bundle size 莫名的巨大;而第二種方法就是只載入一個檔案,再從檔案中拿出我們需要的  isEmpty,如此一來就不用擔心載入整包  lodash  的問題。

可是如果都要像第二種方法這樣寫 code 實際上有點麻煩,而且團隊可能一開始沒考慮到這個問題,程式碼有很多地方都使用第一種方法 import lodash,改起來十分麻煩。

以下提供兩種我認為比較簡易的解法,可以用最少量的配置,達到降低 bundle size 的方法。

使用 lodash-es 而不是 lodash

這也是 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

使用 babel-plugin-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。

babel-plugin-import

結論

在這篇文章中我們了解了如何透過 webpack bundle analyzer 分析打包後的檔案,並且透過這個工具看到不同  import lodash  方式對於 bundle size 的影響。

針對如何降低  lodash  被打包後的檔案大小,本文提供兩種方式,分別是使用  lodash-es  全域取代原本  lodash  的引用,或使用 babel-plugin-import 非侵入式的改動大量的程式碼,而是在打包時處理,這兩種方式都可以達到不把完整的 lodash 都打包進 bundle 的結果,就看各位如何選擇囉!

Reference


上一篇
Day24 - 遇到 404 或 500 怎麼辦,客製化錯誤頁面
下一篇
Day26 - 移除沒用到的 CSS,使用 Purge CSS (feat. Ant Design, Tailwind)
系列文
從零開始學習 Next.js30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
AndrewYEE
iT邦新手 3 級 ‧ 2023-02-17 19:59:02

然後修改 .babelrc 中的設定:
這邊設定內容不見了

我要留言

立即登入留言