In the past period of time, I was engaged in an official website project, which built a common HTML + SCSS + JS native Web front-end project, and incidentally practiced a wave of packaging process commonly used by Webpack.
The first is the project’s source code structure
Webpack configuration
Clear the dist folder
Before each packaging, the packaged output directory needs to be emptied. The method adopted here is to use nodeJs FS module to recursively traverse the packaged output directory and completely empty the contents inside.
// Clear the package output directory
function delDir(path){
let files = [];
if(fs.existsSync(path)){ // Check whether the path exists
files = fs.readdirSync(path); // Read the list of files in this path synchronously
files.forEach((file, index) = > { // Iterate over the list of files
let curPath = path + "/" + file; // Create an absolute path
if(fs.statSync(curPath).isDirectory()){ // Determine whether the file is a folder
delDir(curPath); // Delete folders recursively
} else {
fs.unlinkSync(curPath); // Delete files}}); fs.rmdirSync(path);// Delete the directory after clearing the contents under it
}
}
delDir(buildPath);
Copy the code
The reason for writing this section manually is that the project has a requirement to package files for output outside the project root directory. If this requirement is not available, the clean-webpack-plugin can be used to clear the package directory. NPM install clean-webpack-plugin –save-dev
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
plugin: [
new CleanWebpackPlugin(buildPath)
]
}
Copy the code
Multi-entry JS and HTML configuration
HTML packaging is processed using the HtmlWebpackPlugin. As the official website is a multi-page application, js of each page needs to be configured into entry, and HTML needs to be configured into a list of htmlPlugin instances. The project has a fixed page directory (each page has an HTML, SCSS, and JS. And placed under a directory of the same name), so you can use nodeJs to assemble these configurations.
let pagesEntry = {}; // Page js entry configuration
let pagesHtmlPlugin = []; // List of HtmlWebpackPlugin instances for page template HTML
const pagesRoot = path.resolve(__dirname, 'src/pages'); // Multi-page directory
// Read the page file directory
const pages = fs.readdirSync(pagesRoot)
pages.forEach(name= > {
// Page js entry configuration
const enterPath = path.join(pagesRoot, name)
pagesEntry[name] = path.join(enterPath, name+'.js')
// HtmlWebpackPlugin instance of the output page template HTML
pagesHtmlPlugin.push(new HtmlWebpackPlugin({
filename: `html/${name}.html`.// Output path
template: `${enterPath}/${name}.html`.// HTML source file path
inject: true.minify: process.env.NODE_ENV === "development" ? false : {
removeComments: true.// Remove comments from HTML
collapseWhitespace: true.// Folding white space is the same as compressing code
removeAttributeQuotes: true.// Remove attribute references
},
minify: true.hash: true.chunks: ['main', name] // Each page needs to import the chunk main}})))module.exports = {
entry: Object.assign({
main: './src/main.js' // Universal entrance
}, pagesEntry),
plugins: [].concat(pagesHtmlPlugin),
}
Copy the code
So here to complete the whole page of HTML template packaging and JS entry configuration, HtmlWebpackPlugin
Deal with js
In the previous step, I introduced the generic, unique JS entry file for each page. After that, we need to escape the JS file from ES5 to ES6. NPM install babel-core babel@babel /preset-env –save-dev
module.exports = {
module: {
rules: [{
test: /\.js$/.exclude: /node_modules/.// Do not process code that depends on the library
loader: 'babel-loader'}}}]Copy the code
Create a. Babelrc file to configure Babel:
{" presets ": [[" @ Babel/preset - env," {" targets ": {/ / configuration needs to support the browser version" chrome ", "67", "ie" : "10"}}]]}Copy the code
It is important to note that the above configuration is just to escape the es6 grammar, and no es6 add some API are introduced into the Promise, the Set, the Symbol, Array. The from, async and so on. To be compatible with these apis, you need to configure tools such as babel-Polyfill or babel-Transform-Runtime. The official website project does not require too much JS logic, so none of these apis are used, so I will not expand here for the time being.
Process SCSS style files
SCSS preprocessor is used in the project to write styles. The first step is to install the associated dependency libraries:
npm install node-sass sass-loader style-loader css-loader mini-css-extract-plugin autoprefixer --save-dev
Copy the code
Node-sass takes a long time to download because you need to download the code from Github, so there is no good solution but to wait. Handling CSS in Webpack:
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
module.exports = {
module: {
rules: [
{
test: / \. SCSS $/, use: [MiniCssExtractPlugin loader, / / the style out for file (rather than a base64 or the form of an inline style tag)'css-loader'// Handle modular syntax in CSS such as @import, URL ()'postcss-loader'// CSS post-processing'sass-loader'}]}, plugins: [new MiniCssExtractPlugin({filename:"css/[name].css"}})]Copy the code
Add postcss.config.js to configure postCSs-loader:
module.exports = {
plugins: [
require('autoprefixer') // Automatically add browser-compatible prefixes such as '-ms-' and '-webkit-' to some styles]}Copy the code
Process static resource files
Finally, it deals with the static resources used in the project, such as images, font files, etc., which do not need to be processed, but need to be moved from the source directory to the packaged output directory, hash references, etc., to refresh the browser cache. NPM install file-loader –save-dev
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
module: {
rules: [{
test: /\.(jpg|png|gif)$/,
use: {
loader: 'file-loader',
options: {
name: 'img/[name].[ext]? [hash:7]',
publicPath: '.. '}}}]}}Copy the code
NPM install copy-webpack-plugin –save-dev if there is no need to hash the file directly into the package directory
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
plugins: [
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, './src/static/img'// Move the images from staic/img to [buildPath]/img:'img')]}}]Copy the code
That’s all the packaging configuration practices a common official website project needs.
Finally, the full WebPack configuration
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const fs = require('fs');
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const buildPath = process.env.NODE_ENV === "development" ? path.resolve(__dirname, 'dist') : path.resolve(__dirname, '.. /official/src/main/resources/dist'); // Pack the input position const pagesRoot = path.resolve(__dirname,'src/pages'); // Multi-page directorylet pagesEntry = {};
letpagesHtmlPlugin = []; // Clear the package output directoryfunction delDir(path){
let files = [];
if(fs.existssync (path)){// Check whether the path exists. Files = fs.readdirsync (path); ForEach ((file, index) => {// Traverses the file listlet curPath = path + "/"+ file; // Create an absolute pathif(fs.statsync (curPath).isdirectory ()){// Check whether this file is folder delDir(curPath); // Recursively delete folder}else{ fs.unlinkSync(curPath); // Delete file}}); fs.rmdirSync(path); }} delDir(buildPath); // Const pages = fs.readdirsync (pagesRoot) pages. ForEach (name => {// path.join(pagesRoot, name) pagesEntry[name] = path.join(enterPath, name+'.js'Push (new HtmlWebpackPlugin({filename: 'HTML /${name}.html ', // output path template: '${enterPath}/${name}.html ', // HTML source file path inject:true,
minify: process.env.NODE_ENV === "development" ? false : {
removeComments: true// collapseWhitespace in HTMLtrue// Fold the blank area, which is the zip code removeAttributeQuotes:true// Remove attribute references}, minify:true.hash: true,
chunks: ['main'}))}) module.exports = {mode: exports () {mode: exports () {mode: exports ();'development',
devtool: process.env.NODE_ENV === "development" ? "cheap-module-eval-source-map" : "",
entry: Object.assign({
main: './src/main.js'
}, pagesEntry),
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'}, {test: /\.(jpg|png|gif)$/,
use: {
loader: 'file-loader',
options: {
name: 'img/[name].[ext]? [hash:7]',
publicPath: '.. '}}}, {test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'.'postcss-loader'.'sass-loader',
]
}]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new MiniCssExtractPlugin({filename: "css/[name].css? [hash:7]"}),
new webpack.ProvidePlugin({
$: "jquery",
jQuery: 'jquery'
}),
].concat(pagesHtmlPlugin),
optimization: {
usedExports: true
},
output: {
filename: 'js/[name].js',
path: buildPath,
chunkFilename: '[name].js? [hash:7]'}}Copy the code