Multi-Page Application dengan Webpack 5
Published 16 November 2022
Pada sebuah project saya pernah menemukan kasus dimana saya harus memisahkan beberapa file css dan js untuk di gunakan di banyak halaman. Sedangkan hasil output nya di tempatkan di folder yang berbeda dari file utama (ex: app.css, app.js). Untuk folder output dari style saya tempatkan di folder ./pages/ sehingga akan mendapatkan struktur berikut ./pages/homepage.css, ./pages/about.css, dll.
Langsung saja sebelum kita mulai, install terlebih dahulu plugin yang di butuhkan seperti berikut
Webpack
Sekarang kita perlu menginstal Webpack untuk kebutuhan dasarnya. Versi webpack yang di gunakan adalah versi 5.
npm install webpack webpack-cli --save-dev
Dotenv
Nantinya kita akan menggunakan .env untuk setup mode build nya production atau development
npm install dotenv --save-dev
MiniCssExtractPlugin
Plugin ini mengekstrak CSS ke dalam file terpisah. Itu membuat file CSS per file JS yang berisi CSS. Mendukung On-Demand-Loading CSS dan SourceMaps.
npm install mini-css-extract-plugin --save-dev
TerserWebpackPlugin
Seperti css yang perlu di ekstrak dan di minimize file js juga perlu di lakukan agar ukurannya lebih kecil.
npm install terser-webpack-plugin --save-dev
Selain plugin di atas kita perlu juga menambahkan plugin berikut, karena kita akan menggunakan Bootstrap & jQuery. Selain itu juga kita menggunakan SCSS untuk compiler styling nya.
npm install sass-loader style-loader css-loader node-sass post-css babel-loader babel-core jquery url-loader bootstrap svg-url-loader lqip-loader img-loader --save-dev
Setelah semuanya berhasil di install kita buat satu file dengan nama webpack.config.js
dan copy code berikut
const path = require("path");
const Dotenv = require("dotenv-webpack");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
let appConf = (env) => {
return {
mode: env.NODE_ENV,
devtool: "nosources-source-map",
entry: {
"app.style": "@nstyle/app.scss",
"app.script": "@nscript/app.js",
},
output: {
path: path.resolve(__dirname, "public/assets"),
filename: "js/[name].js",
},
resolve: {
alias: {
"@nstyle": path.resolve(__dirname, "./resources/scss"),
"@nscript": path.resolve(__dirname, "./resources/js"),
},
},
devServer: {
port: 8000,
headers: {
"Access-Control-Allow-Origin": "*",
},
},
performance: {
hints: false,
},
stats: {
warnings: false,
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: "babel-loader",
},
},
{
test: /\.(sc|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: "css-loader",
options: {
url: false,
},
},
{
loader: "sass-loader",
},
{
loader: "postcss-loader",
},
],
},
{
test: /\.(?:ico|gif|png|jpg|jpeg|webp|svg)$/i,
type: "asset/resource",
use: [
"url-loader?limit=10000",
{
loader: "img-loader",
},
{
loader: "lqip-loader",
options: {
base64: true,
palette: false,
},
},
{
loader: "url-loader",
options: {
limit: 8000,
},
},
{
loader: "svg-url-loader",
options: {
encoding: "base64",
iesafe: true,
},
},
],
},
{
test: /\.(ttf|eot|woff|woff2)|(arunicon\.svg)$/,
type: "asset/inline",
},
],
},
optimization: {
minimize: true,
minimizer: [
new CssMinimizerPlugin({
minimizerOptions: {
preset: [
"default",
{
discardComments: {
removeAll: true,
},
},
],
},
}),
new TerserPlugin({
parallel: true,
terserOptions: {
format: {
comments: false,
},
},
extractComments: true,
}),
],
splitChunks: {
cacheGroups: {
styles: {
test: /\.css$/,
name: "styles",
chunks: "all",
enforce: true,
},
},
},
},
plugins: [
new Dotenv(),
new MiniCssExtractPlugin({
filename: "css/[name].css",
chunkFilename: "css/[name].[chunkhash:4].css",
}),
],
};
};
let pageConf = (env) => {
return {
mode: env.NODE_ENV,
devtool: "nosources-source-map",
entry: {
homepage: "@nstyle/pages/_homepage.scss",
about: "@nstyle/pages/_about.scss",
contact: "@nstyle/pages/_contact.scss",
},
output: {
path: path.resolve(__dirname, "public/assets"),
filename: "js/pages/[name].js",
},
resolve: {
alias: {
"@nstyle": path.resolve(__dirname, "./resources/scss"),
},
},
devServer: {
port: 8000,
headers: {
"Access-Control-Allow-Origin": "*",
},
},
performance: {
hints: false,
},
stats: {
warnings: false,
},
module: {
rules: [
{
test: /\.(sc|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: "css-loader",
options: {
url: false,
},
},
{
loader: "sass-loader",
},
{
loader: "postcss-loader",
},
],
},
{
test: /\.(?:ico|gif|png|jpg|jpeg|webp|svg)$/i,
type: "asset/resource",
use: [
"url-loader?limit=10000",
{
loader: "img-loader",
},
{
loader: "lqip-loader",
options: {
base64: true,
palette: false,
},
},
{
loader: "url-loader",
options: {
limit: 8000,
},
},
{
loader: "svg-url-loader",
options: {
encoding: "base64",
iesafe: true,
},
},
],
},
{
test: /\.(ttf|eot|woff|woff2)|(arunicon\.svg)$/,
type: "asset/inline",
},
],
},
optimization: {
minimize: true,
minimizer: [
new CssMinimizerPlugin({
parallel: true,
minimizerOptions: {
preset: [
"default",
{
discardComments: {
removeAll: true,
},
},
],
},
}),
],
splitChunks: {
cacheGroups: {
styles: {
test: /\.css$/,
name: "styles",
chunks: "all",
enforce: true,
},
},
},
},
plugins: [
new Dotenv(),
new MiniCssExtractPlugin({
filename: "css/[name].css",
chunkFilename: "css/[name].[chunkhash:4].css",
}),
],
};
};
module.exports = (env) => {
return [appConf(env), pageConf(env)];
};
NPM scripts
Tambahkan beberapa skrip ke package.json untuk menyederhanakan proses build:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "node_modules/webpack/bin/webpack.js --node-env=production --progress",
"watch": "node_modules/webpack/bin/webpack.js --node-env=development --watch --progress"
}
Jika semua proses sudah di lakukan dan berhasil maka kita akan mendapatkan file css dan js di dalam folder yang sudah kita tentukan sebelumnya.
Cukup sampai disini sedikit sharing pengalaman tentang penggunaan multi entry pada webpack. Jika ada masukan atau cara yang lebih baik lagi bisa share di komentar.
Terimakasih!