※このブログではサーバー運用、技術の検証等の費用のため広告をいれています。
記事が見づらいなどの問題がありましたらContactからお知らせください。


【webpack5】静的ファイルの公開設定にAsset Modulesを使う

web開発 javascript webpack フロント開発 静的ファイル TypeScript

投稿日:2021年8月1日

このエントリーをはてなブックマークに追加
この記事では様々な環境でwebpack5で静的ファイルの公開設定をする方法について詳しく解説していきます。

はじめに

この記事について

この記事ではwebpack5で静的ファイルの公開設定をする方法について解説していきます。

Asset Modules

まず軽くwebpackの歴史の話になります。

※興味がない方は飛ばしてください!

webpackの静的ファイルの扱いの設定方法はwebpack4までとwebpack5からで大きく異なります(後方互換あり)。

webpack4までは画像などの静的ファイルを管理するためにはurl-loaderraw-loaderfile-loaderなどのLoadersがのインストールと読み込み設定が必要になっていました。
しかし、webpack5からはAsset Modules という機能が追加され、デフォルトで静的ファイルを管理できるようになりました。

環境について

この記事は以下の環境を前提にしています。

  • node version 10.19.0
  • npm version 6.14.4
  • webpack version 5.47.0

【基本】JavaScriptから静的ファイルを読み込む

まずはもっとも基本となる、JavaScriptのコード上から静的ファイルを読み込む際の設定方法について解説します。

参考コード

プロジェクトについて

この節でのプロジェクトについて簡単に解説します。
プロジェクトの構成は下のようになっています。

プロジェクト構成
.
├── README.md
├── package-lock.json
├── package.json
├── src
│   ├── images
│   │   └── hearts.svg
│   └── index.js
└── webpack.config.js

ここで./src/index.jsは画像ファイルの./src/images/hearts.svgを読み込む以下のようなコードになっています。

./src/index.js
import mainImage from "./images/hearts.svg";

console.log(mainImage);

特になにも設定せずにwebpackのビルドをしてしまうと、上記コードimport文の"./images/hearts.svg"というパスがそのまま残ってしまいます。

このままでは./images/hearts.svgを手動でビルド後のjsファイルからの総体パスにコピーする必要があります。
静的ファイルはディレクトリを別名にしてまとめたり、CDNにデプロイしたりしたいのでこれはあまり好ましくありません。

Asset Modulesを使うとこれらの問題を簡単に解決できます。

webpackの設定

早速Asset Modulesの機能を使うためのwebpackの設定をしましょう。

// [...]

const config = {
    
    // [...]
    module: {
        rules: [
            {
                test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i,
                type: 'asset/resource',
            },
        ],
    },

// [...]
};

Asset Modulesの機能は、ModuleのRule.typeasset*の値にすることで利用できます。
今回は静的ファイルを出力ディレクトリに別ファイルで出力したいのでtypeasset/resourceにしました。

実際にビルドすると、以下のような出力を確認できます。

ビルド後の出力
./dist
├── 64ea56b6f03140695a9e.svg
└── main.js

./src/images/hearts.svgがハッシュ値を名前に含んだファイル名である64ea56b6f03140695a9e.svgに変換され出力されました。ファイル名に含まれるハッシュ値は元ファイルのデータの内容から生成されるため、内容が変わるとビルド後のファイル名も変わります。

また、./src/index.jsから生成されたmain.js内の"./images/hearts.svg"は画像のパスの変更に合わせて"./64ea56b6f03140695a9e.svg"に変換されました。

出力ファイル名の変換

先程の例では、静的ファイルは対象ディレクトリのルート直下に出力されていました。次に出力ディレクトリ、出力ファイル名を細かく設定してみましょう。出力パスはoutput.assetModuleFilenameで設定できます。

webpack.config.js
// [...]

const config = {

    // [...]
    output: {
        // [...]
        assetModuleFilename: 'assets/[hash][ext][query]'
    },

    // [...]

};

// [...]

ここではassetsというディレクトリに[hash][ext][query]というファイル名で出力するように設定しました。[hash][ext][query]は対象ファイルからそれぞれ特定の値が代入されます。

  • [hash]:元ファイルから計算したハッシュ値。
  • [ext]:元ファイルの拡張子で、.始まりの値。
  • [query]?始まりのクエリ値。

ビルドすると以下のように出力されます。

ビルド後の出力
./dist
├── assets
│   └── 64ea56b6f03140695a9e.svg
└── main.js

無事、assetsというディレクトリに出力できました。
main.js内のパスも自動的に変換されています。


【応用】HTMLから静的ファイルを読み込む

次にHTMLから静的ファイルを読み込む方法について解説します。

参考コード

プロジェクトについて

プロジェクトの構成
.
├── README.md
├── package-lock.json
├── package.json
├── src
│   ├── images
│   │   └── hearts.svg
│   ├── index.html
│   └── index.js
└── webpack.config.js

./src/index.htmlは下ののように./src/images/hearts.svgの相対パスを<img>タグのsrcとして指定しています。

./src/index.html
<!DOCTYPE html>
<html>
<head>
    <title>Sample Page</title>
</head>
<body>
    <img src="./images/hearts.svg" />
</body>
</html>

先程の例と同じように、このパスもwebpackのAsset Modulesビルドでうまく変換できます!

必要パッケージのインストール

ここでの設定は、追加のパッケージが必要になります。まずは必要なパッケージをインストールしましょう。

ターミナル
$ npm install --save-dev html-loader html-webpack-plugin

新しく登場したパッケージに対しての解説を簡単に載せておきます。

  • html-loader:HTMLのソースコードをwebpackで処理できる形式に変換するloader
  • html-webpack-plugin:HTMLのソースコードをビルド後のディレクトリに出力するためのパッケージ

webpackの設定

./webpack.config.js
// [...]

const HtmlWebpackPlugin = require('html-webpack-plugin');

// [...]

const config = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        assetModuleFilename: 'assets/[hash][ext][query]'
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./src/index.html"
        })
    ],
    module: {
        rules: [
            {
                test: /\.html$/i,
                loader: "html-loader",
            },
            {
                test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i,
                type: 'asset/resource',
            },
        ],
    },

// [...]

};

// [...]

これでビルドすると以下のように出力されます。

出力
./dist
├── assets
│   └── 64ea56b6f03140695a9e.svg
├── index.html
└── main.js

./dist/index.html内のパスもoutputの設定に合わせて変換されています。


【応用】TypeScriptから静的ファイルを読み込む

最後に生のTypeScriptコードから静的ファイルを読み込む方法について解説します。

参考コード

プロジェクトについて

プロジェクト構成
.
├── README.md
├── package-lock.json
├── package.json
├── src
│   ├── custom.d.ts
│   ├── images
│   │   ├── close.png
│   │   └── hearts.svg
│   ├── index.html
│   └── index.ts
├── tsconfig.json
└── webpack.config.js

./src/index.tsは以下のように./images/close.pngを読み込んでいます。

./src/index.ts
import CloseImg from "./images/close.png";

const imgElem = document.querySelector("#img-main") as HTMLImageElement;
imgElem.src = CloseImg;

このままだとTypeScriptのコンパイラは存在しないモジュール./images/close.pngimportをエラーとして認識してしまいます。

静的ファイル用の型宣言を作成

TypeScriptのコンパイルエラーを防ぐためには、静的ファイルに対応する型宣言が必要になります。

※create-react-appなどで作成したアプリケーションにはこの型宣言がデフォルトで用意されています。

静的ファイルとしてよくある拡張子に対しての型宣言を作成してみましょう。

./src/custom.d.ts
declare module '*.avif' {
    const src: string;
    export default src;
}

declare module '*.bmp' {
    const src: string;
    export default src;
}

declare module '*.gif' {
    const src: string;
    export default src;
}

declare module '*.jpg' {
    const src: string;
    export default src;
}

declare module '*.jpeg' {
    const src: string;
    export default src;
}

declare module '*.png' {
    const src: string;
    export default src;
}

declare module '*.webp' {
    const src: string;
    export default src;
}

declare module '*.svg' {
    const src: string;
    export default src;
}

これでOKです!

ここからwebpackでTypeScriptを処理するための設定をしていきます。

必要パッケージのインストール

まずはwebpackがTypeScriptのトランスパイルをするために必要なパッケージをインストールしましょう。

ターミナル
$ npm install --save-dev typescript ts-loader file-loader

webpackの設定

./webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');

// [...]

const config = {
    entry: './src/index.ts',
    
    // [...]

    module: {
        rules: [
            {
                test: /\.tsx?$/,
                use: 'ts-loader',
                exclude: /node_modules/,
            },
            {
                test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i,
                type: 'asset/resource',
            },
        ],
    },
    resolve: {
        extensions: ['.tsx', '.ts', '.js'],
    },
};

// [...]

これでビルドするとJavaScriptと同じように静的ファイルを公開できます。

このエントリーをはてなブックマークに追加


関連記事

記事へのコメント
    
検索