Preface: It is 2020, I feel it is time to learn a wave of Webpack, while I have time recently, I went to school, and so on the official website webpack structure and module about a look, I feel I can start to build a project combat, from 0, step by step to record
Version used: WebPack4.x
1. Plug-ins and Loaders are included
* html:
html-webpack-plugin clean-webpack-plugin
* css:
style-loader css-loader sass-loader node-sass postcss-loader autoprefixer
* js:
babel-loader @babel/preset-env @babel/core @babel/polyfill core-js@2 @babel/plugin-transform-runtime
@babel/runtime @babel/runtime-corejs2
* vue:
vue-loader vue-template-compiler vue-style-loader vue vue-router axios vuex
* webpack:
file-loader url-loader webpack-dev-server webpack-merge copy-webpack-plugin happypack HardSourceWebpackPlugin
webpack-bundle-analyzer optimize-css-assets-webpack-plugin portfinder FriendlyErrorsPlugin
Copy the code
2. Webpack function
-- generate HMTL template -- delete last dist file -- automatically add browser prefix -- use sass precompiler -- convert ES6,7,8,9 syntax to ES5 -- package files larger than 10k into dist, < 10K convert to Base64 -- VUE compatible -- copy static files -- hot update -- differentiate current environment -- multi-threaded packaging -- cache unchanged modules -- G-zip compression -- get native IP -- Package size analysis -- Compress CSS -- Check whether the port existsCopy the code
3. Implementation steps
1. The early experience
1. Create a new file named webpack-vue 2.cdwebpack-vue npm init -y npm i -D webpack webpack-cli 3. Create a new SRC /main.js file and say console.log('hello,webpack'Json - >scripts, add"build":"webpack src/main.js"5. NPM run build if there is an extra dist file, then the first package is successfulCopy the code
2. The configuration
- Create a new build folder and create webpack.config.js
- Write the following
const path=require('path')
module.exports = {
mode:'development',
entry:path.resolve(__dirname,'.. /src/main.js'// output:{filename:'[name].[hash:8].js', // The packaged name generates an 8-digit numberhash
path.resolve(__dirname,'.. /dist'}} Then modify package.json ->scripts to:"build":"webpack --config build/webpack.config.js"Then NPM runs buildCopy the code
3. Once we have generated main.js, it is impossible to manually import it in index.html every time, so we need this plugin to automatically import it for us
Install the plug-in first:
npm i -D html-webpack-pluginCopy the code
Create a new public/index.html for the root directory
Modify the webpack. Config. Js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'Module.exports = {mode:'development'// Development mode entry: path.resolve(__dirname,'.. /src/main.js'// output: {filename:'[name].[hash].js'Path: path.resolve(__dirname,'.. /dist'// Plugins :[new HtmlWebpackPlugin({template:path.resolve(__dirname,'.. /public/index.html')]}})Copy the code
Then the NPM run build will notice that there is more index.html in dist and that it has automatically introduced main.js for us
4. Since the hash is generated differently each time, the new main.js will be packed into the dist folder each time, so we need a plug-in to delete the dist file before packaging
Install the plug-in first:
npm i -D clean-webpack-pluginCopy the code
webpack.config.js
const {CleanWebpackPlugin} = require('clean-webpack-plugin'// Plugins :[new HtmlWebpackPlugin({template:path.resolve(__dirname,'.. /public/index.html')
}),
new CleanWebpackPlugin()
]
Copy the code
5. We usually put static files that do not need to be packaged in public, which will not be packaged in dist, so we need to use plug-ins to copy these files
Install the plug-in first:
npm i -D copy-webpack-pluginCopy the code
webpack.config.js
const CopyWebpackPlugin = require('copy-webpack-plugin'Plugins: [new CopyWebpackPlugin({patterns: [{from: path.resolve(__dirname,'.. /public'),
to: path.resolve(__dirname, '.. /dist'}]})]Copy the code
6. In order for WebPack to recognize CSS, we need to install loader and insert the parsed CSS into the style in index.html
First installation:
npm i -D style-loader css-loaderCopy the code
webpack.config.js
module.exports = {
module:{
rules:[{
test:/\.css$/,
use:['style-loader'.'css-loader'}}}Copy the code
7. We can use a precompiler to handle CSS better. I’m using Sass
First installation:
npm install -D sass-loader node-sass
webpack.config.js
module:{
rules: [{
test: /\.scss$/,
use: ['style-loader'.'css-loader'.'sass-loader'}}Copy the code
8. Automatically add the browser prefix
First installation:
npm i -D postcss-loader autoprefixer
webpakc.config.js
module: {
rules: [
{
test: /\.scss$/,
use: ['style-loader'.'css-loader'.'postcss-loader'.'sass-loader'}]} Configure compatible browsers in the root directory. Browserslistrc. Here is my default configuration. Last 2 versions not IE <= 8 postcss.config.js module.exports = {plugins: [require(postcss.config.js module.'autoprefixer'} this package will automatically prefix the browser for youCopy the code
9. The previous style-loader just packages CSS into the style-loader in index.html. If there are too many CSS, this method will not work, so we need to separate and extract CSS
First installation:
npm i -D mini-css-extract-plugin
webpack.config.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin"// Extract CSS module: {rules: [{test: /\.scss$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'.'postcss-loader'.'sass-loader'}} plugins: [new MiniCssExtractPlugin({filename:"css/[name].[hash:8].css",
chunkFilename: "[id].css"})] this will pack the CSS into the CSS fileCopy the code
10. In order to reduce the size of images, fonts, etc., we can use url-loader to convert files smaller than the specified size to Base64, and use file-loader to move files larger than the specified size to the specified location
First installation:
npm i -D file-loader url-loader
webpack.config.js
module:{
rules:[
{
test:/\.(jpe? g|png|gif|svg)(\? . *)? Use :[{loader:'url-loader',
options:{
limit:10240,
fallback:{
loader:'file-loader',
options:{
name:'img/[name].[hash:8].[ext]',
publicPath: '.. / '// to prevent image path error}}}}]},{test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\? . *)? $/, use: [{ loader:'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'media/[name].[hash:8].[ext]',
publicPath: '.. / '}}}}]},{test:/\.(woff2? |eot|ttf|otf)(\? .*)$/, loader:'url-loader',
options:{
limit:10240,
fallback:{
loader:'file-loader, options:{ name:'fonts/[name].[hash:8].[ext]', publicPath: '../'}}}}]} WebPack will only convert and move the images used in the project and find that the image fonts are also packedCopy the code
11. To be compatible with browsers, we need to convert ES6,7,8,9 to Es5
First installation:
npm install -D babel-loader @babel/preset-env @babel/coreCopy the code
webpack.config.js
module: {
rules: [{
test: /\.js$/,
use: ['babel-loader'[}]} root directory new.babelrc {"presets": [
"@babel/preset-env"} Some new apis like Promise,setMaps etc don't support conversion yet, so we need another plugin Babel/Polyfill, but this plugin packs all poly into mian.js, so we need another plugin core-js@2 to load 'NPM I on demand-s@babel/polyfill core-js@2 'modify. Babelrc {"presets": [["@babel/preset-env",
{
"modules": false."useBuiltIns": "usage"."corejs": 2."targets": {
"browsers": [
"last 2 versions"."ie >= 10"[}}]]} One more question, NPM I @babel/plugin-transform-runtime -d NPM I --save @babel/runtime NPM I --save @babel/runtime-corejs2 modified."presets": [
"@babel/preset-env"]."plugins": [["@babel/plugin-transform-runtime",
{
"helpers": true."regenerator": true."useESModules": false."corejs": 2}]]} This perfectly solves the compatibility problem of the new ES6 APICopy the code
12. Compatible vue
First installation:
npm i -D vue-loader vue-template-compiler vue-style-loader
npm i -S vue Copy the code
webpack.config.js
const vueLoaderPlugin=require('vue-loader/lib/plugin')
function resolve(dir) {
return path.join(__dirname, '.. ', dir)
}
module:{
rules:[{
test:/\.vue$/,
use:[vue-loader]
}]
},
resolve:{
alias: {'vue$':'vue/dist/vue.runtime.esm.js'.The '@':path.resolve(__dirname,'.. /src')
},
extensions: ['.js'.'.vue'.'.json'],}, plugins:[new vueLoaderPlugin()] So that webPack can recognize the vue fileCopy the code
13. Hot update
First installation:
npm i -D webpack-dev-serverCopy the code
wepack.config.js
const webpack = require('webpack')
devServer: {
compress: true,
port: 8989,
hot: true,
inline: true,
hotOnly: true// Overlay does not refresh when compilation fails:true// Display error publicPath on browser page when compiling error:'/'// Make sure to add open:true, watchOptions: {// Ignore: /node_modules/, // Wait 1s to listen to the changes before executing the action aggregateTimeout: Poll: 1000}}, plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin(), New webpack. NoEmitOnErrorsPlugin ()] in the package. The json inside configuration:"dev":"webpack-dev-server --config build/webpack.config.js"Import Vue from from main.js"vue"
import App from "./App"
new Vue({
render:h=>h(App)
}).$mount('#app'Create a new APP. Vue in the SRC folder, customize the content and the NPM run dev page will runCopy the code
14. Distinguish between development and production environments
Create webpack.dev.js webpack.prod.js in the build folderCopy the code
Development environment: 1. No need to compress code 2. Hot update 3. Production environment: 1. Compress code 2. Extract CSS files 3. Remove soureMap (according to personal needs)...Copy the code
Here we need to install Webpack-Merge to merge configuration items
First installation:
npm i -D webpack-merge Copy the code
webpack.dev.js
const webpackConfig = require('./webpack.config')
const merge = require('webpack-merge')
const webpack = require('webpack')
module.exports = merge(webpackConfig, {
mode: 'development',
devtool: 'cheap-module-eval-source-map',
devServer: {
compress: true,
port: 8989,
hot: true,
inline: true,
hotOnly: true// Overlay does not refresh when compilation fails:true// Display error publicPath on browser page when compiling error:'/'// Make sure to add open:true,
watchOptions: {
// 不监听的文件或文件夹,支持正则匹配
ignored: /node_modules/,
// 监听到变化后等1s再去执行动作
aggregateTimeout: 1000,
// 默认每秒询问1000次
poll: 1000
}
},
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader'] {},test: /\.css$/,
use: ['vue-style-loader'.'css-loader'.'postcss-loader'],}, {test: /\.scss$/,
use: ['vue-style-loader'.'css-loader'.'postcss-loader'.'sass-loader'],
exclude: /node_modules/
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
new webpack.NoEmitOnErrorsPlugin()
]
})Copy the code
webpack.prod.js
const webpackConfig = require('./webpack.config')
const merge = require('webpack-merge')
const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin'Dist const MiniCssExtractPlugin = require(dist const MiniCssExtractPlugin = require("mini-css-extract-plugin"// Extract CSSfunction resolve(dir) {
return path.join(__dirname, '.. ', dir)
}
module.exports = merge(webpackConfig, {
mode: "production",
devtool: 'none',
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
name: 'vendors'.test: /[\\\/]node_modules[\\\/]/,
priority: -10,
chunks: 'initial'
},
common: {
name: 'chunk-common',
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true
}
}
}
},
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader']
exclude: /node_modules/,
include: [resolve('src'), resolve('node_modules/webpack-dev-server/client')]}, {test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '.. / ',}},'css-loader'.'postcss-loader'],}, {test: /\.scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '.. / ',}},'css-loader'.'postcss-loader'.'sass-loader'],
exclude: /node_modules/
}
]
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: 'css/[name].[hash].css',
chunkFilename: 'css/[name].[hash].css',})]})Copy the code
webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin'// const vueLoaderPlugin = require('vue-loader/lib/plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin') // Copy the filefunction resolve(dir) {
return path.join(__dirname, '.. ', dir)
}
module.exports = {
mode: 'development',
entry: path.resolve(__dirname, '.. /src/main.js'),
output: {
filename: 'js/[name].[hash:8].js',
path: path.resolve(__dirname, '.. /dist'),
chunkFilename: 'js/[name].[hash:8].js'// Asynchronously load the module publicPath:'/'
},
externals: {},
module: {
noParse: /jquery/,
rules: [
{
test: /\.vue$/,
use: [{
loader: 'vue-loader',
options: {
compilerOptions: {
preserveWhitespace: false}}}]}, {test: /\.(jpe? G | PNG | GIF) $/ I / / picture file use: [{loader:'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]',
publicPath: '.. / '}}}}]}, {test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\? . *)? $/, // Media file use: [{loader:'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'media/[name].[hash:8].[ext]',
publicPath: '.. / '}}}}]}, {test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字体
use: [
{
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'font/[name].[hash:8].[ext]',
publicPath: '.. / '
}
}
}
}
]
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'.The '@': resolve('src'),
},
extensions: ['.js'.'.vue'.'.json'}, // plugins: [new HtmlWebpackPlugin({template: path.resolve(__dirname,'.. /public/index.html')
}),
new vueLoaderPlugin(),
new CopyWebpackPlugin({
patterns: [
{
from: path.resolve(__dirname, '.. /public'),
to: path.resolve(__dirname, '.. /dist'}]})]}Copy the code
3. Webpack configuration optimization
1. Set the mode
Default to Production, webpack4.x compresses code and removes useless code by default
Optional parameters: Production, development
Ps: BEFORE, I thought that the plug-in for compressing CSS and JS would not need to be used if the mode was set to production, but it turned out that I was wrong. After careful comparison, I still need to install it
Install the CSS package first:
npm i -D optimize-css-assets-webpack-pluginCopy the code
webpack.prod.js
const optimizeCss = require('optimize-css-assets-webpack-plugin');
plugins:[
new optimizeCss({
cssProcessor: require('cssnano'), // introduce the cssnano configuration compression option cssProcessorOptions: {discardComments: {removeAll:true }
},
canPrint: true// Whether to print plug-in information to console})]Copy the code
Compression JS and JS packaging multithreading has not been added, in the online search some said do not add, some said or to install plug-ins, such as the actual project AFTER I run out to add
2. Narrow your search
aliasWe can tell Webpack to specify folders to find, include exclude and filter noParse as much as possible when we use import JQ from in our code'jquery'Webpack resolves whether the JQ library has dependencies on other packages. This tells WebPack not to parse the extensions that are used frequently in the frontCopy the code
3. Switching from single thread to multi-thread
When webpack processes text, images, and CSS,HappyPack can split the task into multiple sub-threads, which will send the results to the main thread, thus speeding up the packaging speed
First installation:
npm i -D happypack
webpack.prod.js
const HappyPack = require('happypack'// const OS = require('os')
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })
module: {
rules: [{
test: /\.js$/,
use: ['happypack/loader? id=happyBabel'],
exclude: /node_modules/,
include: [resolve('src'), resolve('node_modules/webpack-dev-server/client')]
}]
}
plugins:[
new HappyPack({
id: 'happyBabel',
loaders: ['babel-loader? cacheDirectory'],
threadPool: happyThreadPool
})
]
Copy the code
4. Optimization of third-party modules
We can use DllPlugin DllReferencePlugin to remove the third-party dependencies that do not change much from the dependencies, so that each packaging does not need to package these files, speeding up the packaging speed;
However, the performance of Webpack 4.x is already very good, and the reference to vue-CLI does not use DLL extraction, so we will not use it here, instead we will use another plug-in: Hard-source-webpack-plugin: this plugin compares the configurations that have been changed and only packs the configurations that have been changed. The first time the package speed is normal and the second time the package speed is increased by 50%+
npm i -D hard-source-webpack-plugin
webpack.prod.js
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin'Plugins: [new HardSourceWebpackPlugin()]Copy the code
5.externals
Dependencies loaded via CDN can be set here and will not be compiled via Webpack
6. G – zip
G-zip compression can compress js and CSS again, reducing the package size, requiring nGINx configuration
npm i -D compression-webpack-plugin
webpack.prod.js
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const productionGzipExtensions = ["js"."css"];
plugins:[
new CompressionWebpackPlugin({
filename: '[path].gz[query]',
algorithm: 'gzip'.test: new RegExp("\ \. (" + productionGzipExtensions.join("|") + "$"), threshold: 10240, // Only resources larger than 10K will be processed minRatio: 0.6 // Compression ratio, 0 ~ 1})]Copy the code
7. Automatically obtains the local Ip address and starts the project using the local Ip address
webpack.dev.js
const os = require('os')
devServer:{
host:()=>{
var netWork = os.networkInterfaces()
var ip = ' '
for (var dev in netWork) {
netWork[dev].forEach(function (details) {
if (ip === ' ' && details.family === 'IPv4' && !details.internal) {
ip = details.address
return; }})}return ip || 'localhost'}}Copy the code
8. Remove some common configurations
The root directory of vue.config.js is created to configure some common configurations such as:
const os = require('os'Module. Exports = {dev: {host: getNetworkIp(), // port: 8999, autoOpen:true, / / automatically open}, build: {productionGzipExtensions:"js"."css"], // you need to enable g-zip file suffix productionGzip:false// Whether to enable G-zip compression}} // Obtain the local IP addressfunction getNetworkIp() {
var netWork = os.networkInterfaces()
var ip = ' '
for (var dev in netWork) {
netWork[dev].forEach(function (details) {
if (ip === ' ' && details.family === 'IPv4' && !details.internal) {
ip = details.address
return; }})}return ip || 'localhost'} then webpack.dev.js webpack.prod.js imports the file and gets the configurationCopy the code
9. Package size analysis
First installation:
npm i -D webpack-bundle-analyzer
webpack.prod.js
if (process.env.npm_config_report) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer'Push (new BundleAnalyzerPlugin())} Then NPM run build --report will pop up a page, Inside is the package size analysisCopy the code
10. Complete VUE project (VUE-Router AXIos Vuex, etc.)
First installation:
npm i -S vue-router axios vuex
Then in SRC, create a new folder -> Router folder -> create index.js
index.js
import Vue from "vue"
import Router from "vue-router"
Vue.use(Router)
export default new Router({
mode: 'hash',
routes: [
{
path: '/',
name: 'home',
component: () => import(/* webpackChunkName: "home"* /"@/views/home"),
},
]
})
main.js
import router from "./router"
new Vue({
router,
render: h => h(App)
}).$mount('#app'Create new views -> home.vue and write something, then NPM run dev will complete a routeCopy the code
At this point, webpack builds vue and the project is complete. We will use this Webpack-vue to write the project
To be continued…
The complete configuration has been uploaded to Github, you can see it yourself if you need it
Making: github.com/biubiubiu01…