Webpack Dev Server
It integrates the functions of automatic compilation and automatic refresh of browser. The webpack dev server does not write to any output files after compilation, but keeps the bundle files in memory and then serves them to the server.
yarn add webpack-dev-server --dev
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
module.exports = {
mode: "development",
entry: "./src/main.js",
output: {
filename: "bundle.js",
path: path.join(__dirname, "dist"),
},
module: {
rules: [
{
test: /.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /.png$/,
use: {
loader: "url-loader",
options: {
limit: 10 * 1024,
},
},
},
],
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: "Ideal Webpack Develop Env",
meta: {
viewport: "width=device-width",
},
template: "./src/index.html",
}),
//It is better not to use this plug-in in the development stage and copy static resource files frequently
// new CopyWebpackPlugin({
// patterns: ["public"],
// }),
],
devServer: {
//Specify additional static resource file path
contentBase: ["./public"],
proxy: {
"/api": {
// http://localhost:8080/api/users -> https://api.github.com/api/users
target: "https://api.github.com",
// http://localhost:8080/api/users -> https://api.github.com/users
pathRewrite: {
"^/api": "",
},
},
},
},
};
yarn webpack serve --open
Source Map
A source map is an information file that stores location information. That is, each position of the converted code corresponds to the position before conversion.
With it, when an error occurs, the debugger will directly display the original code instead of the converted code. This undoubtedly brings great convenience to developers.
To enable source map, you only need to add at the end of the converted code//# sourceMappingURL=/path/to/file.js.map
Source map in webpack
const path = require("path");
module.exports = {
mode: "development",
entry: "./src/main.js",
output: {
filename: "bundle.js",
path: path.join(__dirname, "dist"),
},
devtool: "source-map",
};
Common mode:
eval
: only files can be located, and source map files are not generated. The construction speed is fast, and row and column information cannot be knowneval-source-map
: you can locate files and row and column informationeval-cheap-source-map
: only line information can be located, and the code is transformed by loadereval-cheap-module-source-map
: source code of development environment
HMR(Hot Module Replacement)
HMR allows all types of modules to be updated at run time without a full refresh
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
module.exports = {
mode: "development",
entry: "./src/main.js",
output: {
filename: "bundle.js",
path: path.join(__dirname, "dist"),
},
module: {
rules: [
{
test: /.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /.png$/,
use: {
loader: "url-loader",
options: {
limit: 10 * 1024,
},
},
},
],
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: "Ideal Webpack Develop Env",
meta: {
viewport: "width=device-width",
},
template: "./src/index.html",
}),
],
devServer: {
hot: true,
//Hotonly: true // only use HMR, not fallback to live reloading
},
devtool: "source-map",
};
Because CSS has uniform rules, you only need to override the replacement style, and style loader has implemented the hot replacement of HMR. JS files are not complex and diverse, and there are no unified rules, so we need to implement them according to the actual situation
import "./main.css";
import createEditor from "./editor";
const editor = createEditor();
document.body.appendChild(editor);
// ================================================================
//HMR manual processing module hot update
//Don't worry about the redundancy of these codes in the production environment, because after packaging through webpack,
//All the code will be removed, which is only used in the development phase
if (module.hot) {
let hotEditor = editor;
module.hot.accept("./editor.js", () => {
//When editor JS update to automatically execute this function
//Temporary record editor content
const value = hotEditor.innerHTML;
//Remove pre update elements
document.body.removeChild(hotEditor);
//Create a new editor
//At this point, createeditor is the updated function
hotEditor = createEditor();
//Restore editor content
hotEditor.innerHTML = value;
//Append to page
document.body.appendChild(hotEditor);
});
module.hot.accept("./better.png", () => {
//When better Execute after PNG update
//Overriding the setting SRC triggers the picture element to reload, thereby locally updating the picture
img.src = background;
});
//The style loader automatically processes the updated styles internally, so there is no need to manually process the style module
}
Configuration in different environments
Create different configurations for different environments, usingwebpack-merge
Merge the same configuration
webpack.common.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/main.js",
output: {
filename: "js/bundle.js",
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(png|jpe?g|gif)$/,
use: {
loader: "file-loader",
options: {
outputPath: "img",
name: "[name].[ext]",
},
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
title: "Webpack Tutorial",
template: "./src/index.html",
}),
],
};
webpack.dev.js
const webpack = require("webpack");
const { merge } = require("webpack-merge");
const common = require("./webpack.common");
module.exports = merge(common, {
mode: "development",
devtool: "source-map",
devServer: {
hot: true,
contentBase: "public",
},
plugins: [new webpack.HotModuleReplacementPlugin()],
});
webpack.prod.js
const { merge } = require("webpack-merge");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const common = require("./webpack.common");
module.exports = merge(common, {
mode: "production",
plugins: [new CleanWebpackPlugin(), new CopyWebpackPlugin(["public"])],
});
Execute package command
yarn webpack --config webpack.dev.js
DefinePlugin
DefinePlugin
Allows you to replace variables in your code with other values or expressions at compile time. This is very useful when different operations need to be carried out according to the development mode and production mode. For example, replace the domain name
new webpack.DefinePlugin({
//Value requires a snippet of code
API_BASE_URL: JSON.stringify("https://api.example.com"),
}),
Note that since this plug-in will directly replace the text, the provided value must contain an actual quotation mark in the string itself. In general, you can use similar'"production"'
Replace quotation marks like this, or use them directlyJSON.stringify('production')
。
Tree Shaking
Describes removing unreferenced code from a JavaScript context.production
Mode is enabled by default.
const path = require("path");
const webpack = require("webpack");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "none",
entry: "./src/main.js",
output: {
filename: "bundle.js",
path: path.join(__dirname, "dist"),
},
optimization: {
//The module exports only the used members
usedExports: true,
//Compressed output results
minimize: true,
},
};
sideEffects
adoptpackage.json
of"sideEffects"
Attribute is used as a tag to provide a prompt to the compiler to indicate which files in the project are “pure (pure es2015 module)”, so that unused parts of the file can be safely deleted.
{
"sideEffects": false
}
If your code does have some side effects, you can provide an array instead:
{
"sideEffects": ["./src/some-side-effectful-file.js"]
}
“Side effect” is defined as code that performs special behavior during import, rather than exposing only one or more exports. For example, Polyfill affects the global scope and usually does not provide export.
Code subcontracting
Multi entry packaging
It is applicable to multi page packaging. One page corresponds to one packaging entry to extract the public part
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "none",
entry: {
index: "./src/index.js",
album: "./src/album.js",
},
output: {
filename: "[name].bundle.js",
},
optimization: {
splitChunks: {
//Automatically extract all common modules to a separate bundle
chunks: "all",
},
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: "Multi Entry",
template: "./src/index.html",
filename: "index.html",
chunks: ["index"],
}),
new HtmlWebpackPlugin({
title: "Multi Entry",
template: "./src/album.html",
filename: "album.html",
chunks: ["album"],
}),
],
};
Dynamic import
Dynamically imported modules will be automatically subcontracted to realize on-demand loading
Magic note: define the name of the subcontracting bundle
// import posts from './posts/posts'
// import album from './album/album'
const render = () => {
const hash = window.location.hash || "#posts";
const mainElement = document.querySelector(".main");
mainElement.innerHTML = "";
if (hash === "#posts") {
// mainElement.appendChild(posts())
import(/* webpackChunkName: 'components' */ "./posts/posts").then(
({ default: posts }) => {
mainElement.appendChild(posts());
}
);
} else if (hash === "#album") {
// mainElement.appendChild(album())
import(/* webpackChunkName: 'components' */ "./album/album").then(
({ default: album }) => {
mainElement.appendChild(album());
}
);
}
};
render();
window.addEventListener("hashchange", render);
CSS file compression subcontracting
yarn add terser-webpack-plugin optimize-css-assets-webpack-plugin mini-css-extract-plugin --dev
terser-webpack-plugin
: this plug-in uses terser to compress JavaScriptoptimize-css-assets-webpack-plugin
: optimize and compress CSS using cssnanomini-css-extract-plugin
: this plug-in will extract CSS into a separate file, create a CSS file for each JS file containing CSS, and support on-demand loading of CSS and sourcemaps
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");
const TerserWebpackPlugin = require("terser-webpack-plugin");
module.exports = {
mode: "none",
entry: {
main: "./src/index.js",
},
output: {
filename: "[name].bundle.js",
},
optimization: {
minimizer: [
//Declaring an array will make webpack think that we need custom compression, so we need to declare the compression of JS files ourselves
new TerserWebpackPlugin(),
new OptimizeCssAssetsWebpackPlugin(),
],
},
module: {
rules: [
{
test: /\.css$/,
use: [
//'style loader ', // inject styles through style tags
MiniCssExtractPlugin.loader,
"css-loader",
],
},
],
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: "Dynamic import",
template: "./src/index.html",
filename: "index.html",
}),
new MiniCssExtractPlugin(),
],
};
Hash file name
File name dependent hash cache.
At the project level, any change to the project will change
output: {
filename: "[name]-[hash].js",
},
At the chunk level, changes to the same chunk will reference changes to the same chunk
output: {
filename: "[name]-[chunkhash].js",
},
At the file level, different files have different hash values, and the hash length is 8 bits
output: {
filename: "[name]-[contenthash:8].js",
},