捨棄 create-react-app 之餘還架了個 astro blog 昭告天下:webpack 5 與圖片資源
今天會先分享個人慣用的靜態資源設定,接著會介紹我滿喜歡的 svg loader @svgr/webpack。這個套件能讓 .svg
直接以「元件」的形式被引用:
import EditIcon from './edit_icon.svg';
function EditButton() {
return (
<button>
<EditIcon /> edit profile
</button>
);
}
那麽開始吧 (`・ω・´)
webpack 設定
以路徑形式使用圖片
首先在 webpack 設定中加上以下內容:
const webpackDevelopmentConfig: WebpackConfiguration = {
module: {
rules: [
{
test: /\.(png|svg|jpg)$/i,
type: 'asset/resource',
resourceQuery: /url/,
},
],
},
};
沒錯,在 webpack 5 的時代裡,取用靜態資源不強制要求安裝套件 raw-loader
/ url-loader
/ file-loader
了。
webpack: Asset Modules types replace all of these loaders by adding 4 new module types.
以上這段規則在做的事情是:當偵測到檔案路徑符合 .png?url
/ .svg?url
/ .jpg?url
時,webpack 會提供該檔案在瀏覽器環境中可以使用的路徑。
webpack:
asset/resource
emits a separate file and exports the URL. Previously achievable by usingfile-loader
.
所以現在你可以在專案內使用以下方式運用 ./src/asset
資料夾中的圖片檔案了:
import React from 'react';
import foxImageUrl from '@Asset/alexander-andrews-unsplash.jpg?url';
function Image() {
return <img src={foxImageUrl} alt="fox image" />;
}
export default Image;
記得 import 路徑結尾要加上我們在 webpack 規則中設定好的 resourceQuery url
。
以元件形式使用 svg
先安裝 @svgr/webpack
,然後追加以下設定:
const webpackDevelopmentConfig: WebpackConfiguration = {
module: {
rules: [
{
test: /\.(png|svg|jpg)$/i,
type: 'asset/resource',
resourceQuery: /url/,
},
{
test: /\.svg$/i,
issuer: /\.tsx?$/,
resourceQuery: { not: [/url/] },
use: [
{
loader: '@svgr/webpack',
options: {
svgo: false,
},
},
],
},
],
},
};
透過 test: /\.svg$/i
、resourceQuery: { not: [/url/] }
與 issuer: /\.tsx?$/
來處理「所有 .tsx
中結尾不為 .svg?url
的內容」。遇到符合前述條件的檔案時,使用 @svgr/webpack
來處理之。
這邊比較弔詭的地方是設定 svgo: false
來讓 css 針對 svg 的尺寸設定可以順利被套用上去。只寫成 use: ['@svgr/webpack']
或是使用 svgo 官方建議的解決方式在個人的實驗結果中都是失敗的(svg 無法透過 css 調整尺寸)。
🤷
更新 src/env.d.ts
請將以下內容加到 ./src/env.d.ts
中。這能讓 TypeScript 知道「路徑結尾為 .(jpg|png|svg)?url
的檔案會是文字型別」,而「路徑結尾為 .svg
的檔案會是一個 React 元件且擁有 html svg element 的特性(props)」。
declare module '*.jpg?url' {
const src: string;
export default src;
}
declare module '*.png?url' {
const src: string;
export default src;
}
declare module '*.svg?url' {
const src: string;
export default src;
}
declare module '*.svg' {
import React from 'react';
const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
export default ReactComponent;
}
完成!現在你的專案能夠正常顯示圖片內容了 (`・∀・)b
補充:如何選擇圖片格式
綜合平常的開發經驗以及參考 web.dev: Choose the right image format 一文,個人通常會以以下流程來判斷最終要使用哪一種格式來顯示圖片:
- 需要支援透明度:沒什麼選擇,基本上就是
png
- 照片、不是色塊分明的圖面:使用
jpg
- 色塊分明的圖片(如網站 logo 等):使用
svg
或png
確定格式後,我會再根據圖片實際的展示位置決定是否調整檔案尺寸。除了 landing page 這類大面積區塊外,基本上圖片都可以縮小成「畫面上實際展示的大小」即可。
雖然現在是不需要特別斤斤計較流量的時代,但無意義地使用大圖片也會造成 lighthouse 評分低落。開發時若還有餘裕的話,上圖時稍微注意一下也沒有損失 😈