We've learned a lot about Webpack, but it's not easy to become fully proficient at it. A great way to learn is by practicing with real projects. Once we have a good enough understanding of Webpack's configuration, we can try to refactor some projects. This time I chose aPure HTML/JS for PC projectThe project is located on GitHub, and many thanks to the contributors to the project.
Two pages were chosen for the refactoring case: the home page and the shopping cart page.
The project directory is clearly structured, with the root directory containing each HTML page, and CSS, JS and IMG folders at the same level. Each HTML page has its own CSS and business JS files.
Initialize the npm project
First, create a new empty folder and run thenpm init -y
command to initialize the project. Next, create the src folder in the root directory of the project and replace the Copy the file to the src directory and use it as a basis for refactoring. Open the
file, you can see that CSS, images and JS resources have been introduced to the page. Then move the CSS, IMG and JS folders to the src directory as well.
Subsequently, we observe in the file
<link>
cap (a poem)<script>
tags, which are used to load external CSS files and JavaScript files, respectively. In order to adapt your project to Webpack's modular packaging mechanism, you can add a new tag to the A location at the same directory level creates a new
Documentation. In this new
file, we will use a modularized approach to import the data that would have been imported via the
<link>
tag and the CSS files introduced by the<script>
JavaScript file loaded by the tag.
For those directly embedded in the<script>
The script code inside the tag, such as the one mentioned in the figureflexslider
function, we leave it as it is for now, without changes.
import "./css/";
import "./css/";
import './js/jquery-1.12.'
import './js/';
import './js/';
import './js/';
Initializing webpack
Using Commandsnpm install webpack --save
to installWebpack
and create file to define the basic configuration. This is a multi-entry project since the original project contains multiple HTML pages.
const path = require("path");
= {
mode: "development",
entry: {
index: "./src/",
},
output: {
filename: "[name].[hash:8].js",
path: (__dirname, "./dist"),
},
};
exist Add
"build": "webpack"
Command.
Handling css, images
Webpack
Handling CSS and image resources is not supported by default. To handle CSS resources, you can use thecss-loader
cap (a poem)style-loader
and image resources can be accessed through Webpack 5's built-in feature, theasset module
to deal with.
First, install the dependencies needed to process CSS:
npm install css-loader style-loader --save
Here we use thecss-loader
to parse the CSS file and pass thestyle-loader
Insert them into the DOM as inline styles. Initially, we can create inline styles this way, and then we can think about extracting the CSS resources for further optimization.
= {
module: {
rules: [
{ test: /\.css$/, use: ["style-loader", "css-loader"] },
{ test: /\.(jpg|jpeg|png|gif|svg)/i, type: "asset" },
],
},
}
Processing html
utilizationhtml-webpack-plugin
The plug-in is based on the Create compressed HTML files and bring in compiled JS files.
const HtmlWebpackPlugin = require("html-webpack-plugin");
= {
plugins: [
new HtmlWebpackPlugin({
template: "./src/",
filename: "",
}),
],
}
Photo Resources
asset module
It is mainly used to handle image resources that are introduced in CSS files through background images or other means. However, it can't do anything about resources introduced directly through tags in HTML pages.
As you can see, the paths of these images are all img/. Since the compiled files are located in the dist folder, there is no img directory in the dist folder. Therefore, we can use thecopy-webpack-plugin
Copy the img folder from the src directory to the dist directory.
const CopyPlugin = require("copy-webpack-plugin");
= {
plugins: [
new CopyPlugin({
patterns: [
{
from: "./src/img",
to: "./img",
},
],
}),
],
};
In this way, when we execute thenpm run build
When we try to create a new file from the However, when we try to retrieve them from the When you open the page, you find the page error message
$
is undefined and defined at the bottom of the pageflexslider
method did not take effect.
ProvidePlugin $ Symbol
We know.$
The symbols are actuallyjQuery
A global variable provided. The prompt cannot find the$
symbols, meaning that thejQuery
's global variables have not been exposed correctly. To fix this, we can follow the steps below:
First, by installing thejQuery
:
npm install jquery --save
Next, adjust the introduction method in the file:
// Before changes
import '. /js/jquery-1.12.'
// After
import 'jquery'.
Then, use theProvidePlugin
to define the $ mapping relationship:
const webpack = require("webpack");
= {
plugins: [
new ({
$: "jquery",
jQuery: "jquery",
}),
],
};
Finally, it will be The bottom of the file is passed through the
<script>
Tagged calls of theflexslider
The function code is moved to the business JS file where it needs to be introduced.
After completing the above steps, perform thenpm run build
The original The functionality then enables basic refactoring, and more optimization work can follow.
Automatically empties compiled folders
in implementingnpm run build
whenWebpack
will be based on The rules in the
dist
directory to generate compiled files. To avoiddist
The files generated in the folder are mixed together, and we usually need to manually clean up that directory before each compilation.
To save ourselves the trouble of doing this manually, we can use theclean-webpack-plugin
to automatically empty thedist
folder. This ensures that every time you build, thedist
The directories are clean, thus avoiding interference from old files.
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
= {
plugins: [
new CleanWebpackPlugin()
],
};
Extract css file
This results in JS files that are too large and a mix of JS and CSS code that is not clear enough. In a development environment this works because of the speed of compilation, but in a production environment we need to abstract the CSS resources into separate files.
To do this, we can use themini-css-extract-plugin
replacestyle-loader
to enable independent packaging of CSS resources.
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
= {
module: {
rules: [
{ test: /\.css$/, use: [, "css-loader"] },
],
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].[hash:8].css",
chunkFilename: "[name].[hash:8].css",
}),
],
};
In this way, the CSS resources are packaged into a separate file, making the final output more standardized and efficient. As you can see in the figure, the CSS file has been separated.
js and css compression
In the previous configuration, themode
be set todevelopment
, which facilitates debugging in development mode. However, when the code is released, we need to switch to theproduction
Mode. In this mode, theWebpack
Automatically compresses resource files to reduce file size.
In addition to changing the mode setting, we can utilize theterser-webpack-plugin
cap (a poem)css-minimizer-webpack-plugin
separate opinionJavaScript
cap (a poem)CSS
resources for further compression.
const TerserPlugin = require("terser-webpack-plugin");
const CssMinimizerWebpackPlugin = require("css-minimizer-webpack-plugin");
= {
mode: "production",
optimization: {
minimizer: [new TerserPlugin({}), new CssMinimizerWebpackPlugin()],
},
};
As you can see in the figure, we can see the changes in file size with different mode settings and after using the plugin to compress the code resources. Although the current project has only one page with a small number of HTML, CSS, and JS files, so the effect of code compression may not be particularly significant, but as the size of the project grows, the effect of this compression strategy will be more obvious.
Add development mode
All of the above code changes we made by executing thenpm run build
to observe the compiled product. However, when multiple files need to be migrated, using development mode makes it easier to see how all business scenarios are being used in real time.
To accomplish this, we can use thewebpack-dev-server
to start a development server. After the installation is complete, add the Add to the file
devServer
The configuration of the
= {
devServer: {
open: true,
compress: true,
port: 8000,
},
};
Next, in the file to configure a script command for starting the development server:
"scripts": {
"dev": "webpack serve",
},
In this way, by executing thenpm run dev
You can start the development server and automatically open a browser to view the The content of the page. This not only facilitates debugging, but also provides a real-time preview of the effect of code changes.
multi-entry
So far we have only migrated the resources on the home page. Now we will move on to migrate the shopping cart page. Similar to the home page migration, first copy the files into the src directory and look for the CSS and JS resources that were introduced there.
Next, create a file with the required JS and CSS files:
//
import "./css/";
import "./css/";
import 'jquery';
import './js/';
import './js/';
import './js/';
The next configuration is critical. We need to be in Define the multiple entries in the HTML file and generate the appropriate template HTML file for each page. It is important to note here that you must define the
chunks
attribute, otherwise the resulting HTML page will incorrectly introduce all CSS and JS files.
= {
entry: {
index: "./src/",
cart: "./src/",
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/",
filename: "",
chunks: ["index"],
}),
new HtmlWebpackPlugin({
template: "./src/",
filename: "",
chunks: ["cart"],
}),
],
};
After completing the above configuration, execute thenpm run build
If you want to compile the two pages, you can do so by clicking on the At this point, in the dist folder, click file, you can also access the content of the page without any problems.
Splitting up public resources
Although it is currently possible to compile twoHTML
page's resources, but if you view thedist
folder of the or
file, you will find that it still contains the
jQuery
of the code.
In order to optimize this, we would like to include a new class likejQuery
Duplicate resources like this are referenced as public modules, rather than having them compiled over and over again in different JS files. Here's a detailedsplitChunks
Configuration example, which sets thenode_modules
The resources in thejQuery
Compile into a separate file and compile other third-party libraries into another file.
= {
optimization: {
splitChunks: {
chunks: "all",
name: "common",
cacheGroups: {
jquery: {
// Test if the module contains the 'jquery' string.
test: /[\\/]node_modules[\\\/]jquery[\\\/]/,, // Set the filename.
// Set the filename
name: "jquery", // set the filename.
// The filename can be a function or a string.
filename: "",
// Ensure that only jQuery in asynchronously loaded chunks is included.
priority: 10, // You can set the priority to control the merge order.
enforce: true, // Force the creation of this chunk even if other rules might ignore it.
}, }
vendors: {
// This cache group is used to handle other third-party libraries
test: /[\\/]node_modules[\\/]/, name: "vendors", // "\/", "\/", "\/", "\/
name: "vendors", priority: -10, }
name: "vendors", priority: -10, filename: "",
chunks: "all", }
}, }
}, }, }, }, }, }, }, }, }, }, }, }
}, }, }
}, }
}.
Since jQuery is the only resource used in the current project, only thejQuery
be packaged separately. As the project grows and resources increase, the splitting rules can be further refined. As can be seen from the results, whenjQuery
After being split, the file sizes of both and have been significantly reduced.
Template file ejs
The navigation at the top of the page is usually fixed and identical across pages. In the current project, the same HTML sections are defined by copying. To improve code reusability and maintainability, we can use theEJS(Embedded JavaScript)
to pull this same part of the logic out.
First, in thesrc
folder to create aejs
folder and create a File. Find the definition of the
header
code, copy it to the file with the changes (e.g., page titles) passed through the
<%= title %>
The way it is defined.
Then, in the original HTML page, defineheader
The place where the code is introduced file and pass in dynamic variables:
<%=require('. /ejs/')({ title: 'Home' })%>
due toWebpack
Inherently not equipped to handleEJS
file capability, so we need to install theejs-loader
and configure the corresponding processing rules:
= {
module: {
rules: [
{ test: /\.ejs/, loader: "ejs-loader", options: { esModule: false } },
],
},
};
With this configuration, we achieve reuse of public code.
The above steps complete the process of migrating from a pure HTML/JS project to development using Webpack. By using Webpack, we have achieved code segmentation, on-demand loading of resources, and modular development. Plugins such as html-webpack-plugin and clean-webpack-plugin simplify the build process and ensure a clean and optimized output file for every build.
Abstracting repetitive code fragments such as public headers through EJS reduces redundancy, improves code reuse, and makes the code base more concise.
If you're interested in front-end, JavaScript, and engineering, come take a look at my other articles ~ I'll be sharing various learning tips and tricks from time to time. Poke my avatar to explore more fun content together!