preface
At present, the front-end system of the company is not very perfect. Generally, projects are written with a set of universal scaffolding built by create-React-app. The scaffolding is very generic and basic, but with tailwindcss and postcss, each compilation and packaging takes too long to be productive. Therefore, to avoid overtime, manual construction of a universal Webpack scaffolding is a current goal.
The body of the
Basic Project Configuration
Project initialization
mkdir webpack-demo-1
cd ./webpack-demo-1
yarn init -y
Copy the code
First you need to create the entry file for the project and the configuration file for Webpack. At this point the project directory is as follows:
Then, you need to install the WebPack dependency
yarn add --dev webpack webpack-cli
Copy the code
The related versions of Webpack used here are as follows:
"Webpack" : "^ 5.44.0", "webpack - cli" : "^ 4.7.2." "Copy the code
Write content
Write something random in index.js:
class Hello { constructor() { console.log('hello webpack! ') } } const test = new Hello()Copy the code
Then, let’s run WebPack to see what it looks like
npx webpack
Copy the code
As you can see, after this run, a dist directory is added to the root directory, and a main.js file will be added to that directory
Among them:
new class{constructor(){console.log('hello webpack! ')}};Copy the code
Babel is used to convert ES6 to ES5 code. Let’s install some of Babel’s dependencies.
Configure the Babel
Install dependencies:
yarn add --dev babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties @babel/plugin-proposal-private-methods
yarn add @babel/runtime @babel/runtime-corejs3
Copy the code
Modify the webpack.config.js file:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.[contenthash:8].js',
},
module: {
rules: [
{
test: /\.(jsx|js)$/,
use: 'babel-loader',
exclude: /node_modules/,
},
]
}
}
Copy the code
Create the.babelrc file in the root directory:
{
"presets": ["@babel/preset-env"],
"plugins": [
["@babel/plugin-transform-runtime", {"corejs": 3}],
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }],
["@babel/plugin-proposal-private-methods", { "loose": true }]
]
}
Copy the code
Let’s run NPX webpack again to see the output. The project directory at this time:
Take a look at the contents of the bundle.xxxx.js file in the dist directory:
(()=>{"use strict"; new function n(){! function(n,o){if(! (n instanceof o))throw new TypeError("Cannot call a class as a function")}(this,n),console.log("hello webpack!" )}}) ();Copy the code
That should be all right. Let’s get the project running in the browser.
Run in a browser
We use directly the html-webpack-plugin, a well-known plug-in in the WebPack ecosystem, which allows our build artifacts to use the HTML files we specify as templates.
yarn add --dev html-webpack-plugin
Copy the code
In the root directory, create a public folder, place an index. HTML file, and write basic HTML content to it
Use the html-webpack-plugin plugin in the wbepack.config.js file
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, './public/index.html'),
inject: 'body',
scriptLoading: 'blocking',
}),
]
}
Copy the code
At this point, run NPX webpack to see the packaging results
Go to the dist folder directory and open the index.html file directly in your browser
You can now see that we typed “Hello Webpack!” It’s already displayed in the console.
Current problems
The above is a basic process, but there are still some biggest problems:
- Hot updates are required. It is not possible to rebuild after every update and use a packaged file for debugging;
- You need to clear the contents of the last package before each package.
- Environment split
So next, to solve these two problems first!
Hot update
Here we use webpack-dev-server as suggested on the official website
yarn add --dev webpack-dev-server
Copy the code
Then you need to add the relevant configuration to webpack.config.js:
module.exports = { // ... DevServer: {port: '8080', // Enable the port number, usually 8080 hot: true, // enable the hot Module Replacement function of Webpack. 'errors-only', // terminal only prints error compress: true, // whether gzip compression is enabled},}Copy the code
Then we need to add a “scripts” command to package.json
{/ /... "scripts": { "start": "webpack serve --open" } }Copy the code
At this point, you can use the YARN start command to open the http://localhost:8080/ page in your browser and use hot updates for debugging. What a convenience!
Remove old packaging products
This problem can be easily solved by using the clean-webpack-plugin. Note, however, that after WebPack 5.20, the Output of Webpack now supports cleaning build artifacts before each packaging. Simply add clean to the output field in webpack.config.js: True achieves the same effect as the clean-webpack-plugin
// ...
module.exports = {
...
output: {
// ...
clean: true
}
}
Copy the code
Environment split
Typically, a project is divided into development, pre-release, and production environments, and in this case, mainly development and production environments
Update directory structure, create build folder in the root directory, delete wbepack.config.js from the original root directory, Create webpack.base.config.js (public part), webpack.dev.config.js (development part), and webpack.prod.config.js (production part) in the build directory.
Webpack. Base. Config. Js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const rootDir = process.cwd();
module.exports = {
entry: path.resolve(rootDir, 'src/index.js'),
output: {
path: path.resolve(rootDir, 'dist'),
filename: 'bundle.[contenthash:8].js',
},
module: {
rules: [
{
test: /\.(jsx|js)$/,
use: 'babel-loader',
include: path.resolve(rootDir, 'src'),
exclude: /node_modules/,
},
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(rootDir, 'public/index.html'),
inject: 'body',
scriptLoading: 'blocking',
}),
],
}
Copy the code
When configuring production and development environments, you need to install a plug-in for WebPack-Merge to merge configurations.
yarn add --dev webpack-merge
Copy the code
Webpack. Dev. Config. Js:
const { merge } = require('webpack-merge'); const baseConfig = require('./webpack.base.config'); Module. exports = merge(baseConfig, {mode: 'development', devServer: {port: '8080', // default: 8080, stats: 'errors-only', // terminal only prints error compress: true, // whether gzip compression is enabled},});Copy the code
Webpack. Prod. Config, js:
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.base.config');
module.exports = merge(baseConfig, {
mode: 'production',
});
Copy the code
Finally, be careful to update the scripts command in the package.json file:
"scripts": {
"start": "webpack serve --config build/webpack.dev.config.js --open",
"build": "npx webpack --config build/webpack.prod.config.js"
}
Copy the code
Continue to improve features
Supports SASS and CSS
First, add an index. SCSS file to the SRC directory and import it to the index.js file. In this case, after yarn start, you can find that webpack cannot be installed without the corresponding Loader. Identify the contents of SCSS and CSS files.
Next let’s install loader.
yarn add --dev sass dart-sass sass-loader css-loader style-loader
Copy the code
Note: If you have used creat-react-app to configure SASS, you will find that sASS can be installed in conjunction with Node-sass. If you have used creat-react-app to configure Sass, you will find that sass can be installed in conjunction with Node-sass. Create-react-app does not support Dart-Sass. However, if we manually configure it ourselves, dart-Sass should work fine.
Then, modify the webpack.base.config.js file
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.(s[ac]ss|css)$/i,
exclude: /node_modules/,
use: ['style-loader', 'css-loader', 'sass-loader']
},
]
},
// ...
}
Copy the code
At this point, run the project using YARN Start, and you can see that the CSS is ready to be displayed in the project.
Add postcss
Next, let’s add postCSS
yarn add --dev autoprefixer postcss postcss-loader
Copy the code
Update the webpack.base.config.js file
const autoprefixer = require('autoprefixer');
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.(s[ac]ss|css)$/i,
exclude: /node_modules/,
use: [
'style-loader',
'css-loader',
'sass-loader',
{
loader: "postcss-loader",
options: {
postcssOption: {
plugins: [
["autoprefixer"]
]
}
}
}
]
},
]
},
// ...
}
Copy the code
Pack and remove the CSS file
Install the mini-CSS-extract-plugin first
yarn add --dev mini-css-extract-plugin
Copy the code
Update webpack. Base. Config. Js
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { // ... module: { rules: [ // ... { test: /\.(s[ac]ss|css)$/i, exclude: /node_modules/, use: [ MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader', { loader: "postcss-loader", options: { postcssOptions: {plugins: [["autoprefixer"]]}}}]},]}, plugins: [// omit... new MiniCssExtractPlugin({filename: 'css/[name].css', }), ], }Copy the code
At this point, run YARN Build to see
As you can see, the CSS file has been removed to the specified directory.
Copy static resources to the package directory
Sometimes there may be a static file that needs to be manually downloaded and added to the project manually. Normally, we would put this resource in our public file directory and import it as script in index.html. But the reality is that the file cannot be found after being added to the public directory.
// index.html <! doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, User - scalable = no, initial - scale = 1.0, the maximum - scale = 1.0, Minimum -scale=1.0"> <meta http-equiv=" x-UA-compatible "content=" IE =edge"> <title>Document</title> </head> <body> <script src="./js/test.js"></script> </body> </html>Copy the code
Compile result:
This is where the copy-webpack-plugin plugin comes in, which copies the specified files into the packaged artifacts when the build is packaged.
yarn add --dev copy-webpack-plugin
Copy the code
Update webpack. Base. Js
const CopyWebpackPlugin = require('copy-webpack-plugin');
const rootDir = process.cwd();
module.exports = {
// ...
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: '*.js',
context: path.resolve(rootDir, "public/js"),
to: path.resolve(rootDir, 'dist/js'),
},
],
})
new MiniCssExtractPlugin({
filename: 'css/[name].css',
}),
new OptimizeCssPlugin(),
],
}
Copy the code
Run YARN start again
The static JS file is now ready to load successfully
Loading image resources
The front-end project will inevitably introduce images and other resources. At this point, we try to introduce resources into the project. Introducing images in index.js:
import './index.scss' import imgTets from './assets/1.png' class Hello { constructor() { console.log('hello webpack! ') } renderImg() { const img = document.createElement('img') img.src = imgTets document.body.appendChild(img) } } const test = new Hello() test.renderImg()Copy the code
Run the project and you’ll see a familiar error:
Yarn add XXx-loader: install raw-loader, URl-loader, file-loader and yarn add XXx-loader: install raw-loader, URL-loader, file-loader and yarn add XXx-loader.
Note, however, that in WebPack 5 you don’t need to install these dependencies anymore, just add the following configuration to webpack.base.config.js:
rules: [
// ...
{
test: /\.(png|jpg|gif|jpeg|webp|svg|eot|ttf|woff|woff2)$/,
type: 'asset',
},
]
Copy the code
Run yarn Start again, then run again, there is no problem!
Project optimization during construction
The cache
Webpack 5 has done a lot of things for us, including caching. Configuration:
// webpack.dev.config.js
module.exports = merge(baseConfig, {
mode: 'development',
//...
cache: {
type: 'memory'
},
});
Copy the code
// webpack.prod.config.js
module.exports = merge(baseConfig, {
mode: 'production',
// ...
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
},
},
});
Copy the code
Then we try to run YARN Build twice to see the time difference
You can see the difference, it’s still quite big
Resolution of the code
Split each module code, extract the same part of the code, the advantage is to reduce the frequency of repeated code. SplitChunks instead of CommonsChunksPlugin have been used to split code since WebPack 4. Configuration:
// webpack.base.config.js const webpack = require('webpack'); module.exports = { //... plugins: [ new webpack.optimize.SplitChunksPlugin(), ], optimization: { splitChunks: { chunks: 'all' // code split type: all all module, async asynchronous module, INITIAL entry module}},}Copy the code
Multithreaded packaging
The packaging speed of projects is a troublesome point in large projects. Here, we use thread-loader to carry out multi-threading packaging of projects.
Install dependencies:
yarn add --dev thread-loader
Copy the code
Update webpack. Base. Config. Js
module.exports = {
entry: path.resolve(rootDir, 'src/index.js'),
output: {
path: path.resolve(rootDir, 'dist'),
filename: 'bundle.[contenthash:8].js',
clean: true
},
module: {
rules: [
{
test: /\.(jsx|js)$/,
use: ['thread-loader', 'babel-loader'],
include: path.resolve(rootDir, 'src'),
exclude: /node_modules/,
},
{
test: /\.(s[ac]ss|css)$/i,
exclude: /node_modules/,
use: [
MiniCssExtractPlugin.loader,
'thread-loader',
'css-loader',
'sass-loader',
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
["autoprefixer"]
]
}
}
}
]
},
{
test: /\.(png|jpg|gif|jpeg|webp|svg|eot|ttf|woff|woff2)$/,
type: 'asset',
},
]
},
//...
}
Copy the code
Stage 1 Summary
So far, this is a fairly generic WebPack 5-based scaffolding. React or Vue is not yet installed and configured.
In phase 2, I need to build on this scaffolding and add react, typescript, tailwindCSS, etc. to meet my needs for daily development!