Webpack4从入门到入土

602 阅读10分钟

Webpack

一、Webpack简介

1.1 Webpack是什么

Webpack是一种前端资源构建工具,一个静态模块打包器。在Webpack看来,前端的所有资源文件都会作为模块处理,它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)。

1.2 Webpack五个核心概念

1.2.1 Entry

​ 入口(Entry)指示Webpack以哪个文件为入口起点开始打包,分析构建内部依赖图。

1.2.2 Output

​ 输出(Output)指示Webpack打包后的资源bundles输出到哪里去,以及如何命名。

1.2.3 Loader

​ Loader让Webpack能够去处理那些非JavaScript文件(webpack自身只理解JavaScript)

1.2.4 Plugins

​ 插件(Plugins)可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。

1.2.5 Mode

​ 模式(Mode)指示Webpack使用对应模式的配置。

选项描述特点
development会将process.env.NODE_ENV的值设为development。启用NamedChunksPluginNamedModulesPlugin能让代码本地调试运行的环境
production会将process.env.NODE_ENV的值设为production。启用FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPluginUglifyJsPlugin能让代码优化上线运行的环境

二、Webpack初体验

2.1 运行指令

​ 开发环境:webpack ./src/index.js -o ./build/build.js --mode=development

​ 生产环境:webpack ./src/index.js -o ./build/build.js --mode=production

​ webpack会以./src/index.js为入口文件开始打包,打包后输出到./build/build.js,整体打包环境是开发/生产环境

结论:

  1. webpack能处理js/json资源,不能处理css/img等其他资源
  2. 生成环境和开发环境将ES6模块编译成浏览器能识别的模块化
  3. 生成环境比开发环境多一个压缩js代码

2.2 webpack配置

webpack.config.js:webpack的配置文件。指示webpack干哪些活(当你运行webpack指令时,会加载里面的配置);
​ 所有构件工具都是基于nodejs平台运行的,模块化默认使用commonjs

/*
    loader:1. 下载	2. 使用(配置loader)
    plugins:1.下载	2. 引入		3. 使用
*/
// resolve用来拼接绝对路径的方法
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');

// 设置nodejs环境变量
process.env.NODE_ENV = 'development';

// 复用loader
const commonCssLoader = [
    // use数组中loader执行顺序:从右到左,从下到上,依次执行。
    // 创建style标签,将js中的样式资源插入进行,添加到head中生效
    // 'style-loader',
    // 这个loader取代style-loader。作用:提取js中的css成单独文件
    MiniCssExtractPlugin.loader,
    // 将css文件变成commonjs模块加载js中,里面内容时样式字符串
    'css-loader',
    /*
      css兼容性处理:postcss --> postcss-loaderpost-preset-env
      帮postcss找到packet.json中browerslist里面的配置,通过配置加载指定的css兼容性样式
          "browserslist": {
              // 开发环境 --> 设置node环境变量: process.env.NODE_ENV = 'development'
          "development": [
            "last 1 chrome version",
            "last 1 firefox version",
            "last 1 safari version",
           ],
           // 生产环境:默认是生产环境
           "production": [
                  ">0.2%",
                  "not dead",
                  "not op_mini all"
            ]
          }
        */
    // 使用loader的默认配置
    // 'post-loader',
    // 修改loader配置
    {
        loader: 'postcss-loader',
        options: {
            ident: 'postcss',
            plugins: () => [
                // postcss的插件
                require('postcss-preset-env')()
            ]
        }
    }
];

module.exports = {
    // webpack配置
    // 入口起点
    entry: './src/index.js',

    // 输出
    output: {
        // 输出文件名
        filename: 'js/build.js',
        // 输出路径
        // __dirname nodejs的变量,代表当前文件的目录的绝对路径
        path: resolve(__dirname, 'build')
    },

    // loader的配置
    modules: {
        rules: [
            // 详细loader配置
            // 不同文件必须配置不同loader处理
            {
                // 匹配哪些文件
                test: /\.css$/,
                // 使用哪些loader进行处理
                use: [...commonCssLoader]
            },
            {
                test: /\.less$/,
                use: [
                    ...commonCssLoader,
                    // 将less文件编译成css文件
                    // 需要下载less-loader和less
                    'less-loader'
                ]
            },
            {
                // 处理样式中的图片资源 —— 默认处理不了html中的图片资源
                test: /\.(jpg|png|gif)$/,
                // 下载url-loader、file-loader
                loader: 'url-loader',
                options: {
                    // 图片大小小于8kb,就会被base64处理
                    // 优点:减少请求数量(减轻服务器压力)
                    // 缺点:图片提及会更大(文件请求速度更慢)
                    limit: 8 * 1024,
                    // 问题:因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs
                    // 解析时会出问题:【object Module】
                    // 解决: 关闭url-loader的es6模块化,使用commonjs解析
                    esModule: false,
                    // 给图片进行重命名
                    // [hash: 10]取图片的hash值前10位
                    // [ext]取文件原来扩展名
                    name: '[hash:10].[ext]',
                    outputPath: 'imgs'
                }
            },
            // 打包其他资源(除了html/js/css资源以外的资源)
            {
                test: /\.html$/,
                // 处理html文件中的img图片(负责引入img,从而能被url-loader进行处理)
                loader: 'html-loader'
            },
            {
                exclude: /\.(html|css|js|less|jpg|png|gif)$/,
                loader: 'file-loader',
                options: {
                    name: '[hash:10].[ext]',
                    outputPath: 'media'
                }
            },
            // 正常来讲,一个文件只能被一个loader处理,当被多个loader处理,一定要指定loader执行的先后顺序:先执行eslint再执行babel
            {
                /*
                    语法检查: eslint-loader eslint
                        注意:只检查自己写的源代码,第三方的库是不用检查的
                        设置检查规则:
                            package.json中eslintConfig中设置:
                            “eslintConfig”: {
                                "extend": "airbnb-base"
                            }
                    airbnb --> eslint-config-airbnb-base	eslint-plugin-import	eslint
                */
                test: /\.js$/,
                exclude: /node_modules/,
                // 优先执行
                enforce: 'pre',
                loader: 'eslint-loader',
                options: {
                    // 自动修复eslint的错误
                    fix: true
                }
            },
            /*
                  js兼容性处理:babel-loader @babel/core @babel/preset-env
                      1. 基本js兼容性处理	==》 @babel/preset-env
                          问题:只能转换基本语法,如promise不能转换
                2. 全部js兼容性处理	==》 @babel/polyfill
                      问题:我只要解决部分兼容性问题,但是所有兼容性代码全部引入,提及太大
                    3. 需要做兼容性处理的就做:按需加载	==》 core-js 
            */
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                options: {
                    // 预设:指示babel做怎么样的兼容性处理
                    presets: [
                        [
                            '@babel/preset-env',
                            {
                                // 按需加载
                                useBuiltIns: 'usage',
                                // 指定core-js版本
                                corejs: { version: 3 },
                                // 指定兼容性做到哪个版本的浏览器
                                targets: {
                                    chrome: '60',
                                    firefox: '60',
                                    ie: '9',
                                    safari: '10',
                                    edge: '17'
                                }
                            }
                        ]]
                }
            }
        ]
    },

    // plugins配置
    plugins: [
        // 详细plugins配置
        // html-webpack-plugin —— 默认会创建一个空的HTML,自动引入打包输出的所有资源(JS/CSS)
        new HtmlWebpackPlugin({
            // 复制'./src/index.html'文件,并自动引入打包输出的所有资源(JS/CSS)
            template: './src/index.html',
            // 压缩html
            minify: {
                // 移除空格
                collapseWhitspace: true,
                // 移除注释
                removeComments: true
            }
        }),
        new MiniCssExtractPlugin({
            // 对输出的css文件重命名
            filename: 'css/build.css'
        }),
        // 压缩css
        new OptimizeCssAssetsWebpackPlugin()
    ],

    // 模式
    mode: 'development',
    // mode: 'production',

    // 开发服务器devServer:用来自动化(自动编译、自动打开浏览器、自动刷新浏览器)
    // 特点:只会在内存中编译打包,不会有任何输出
    // 启动devServer指令为:`npx webpack-dev-server`
    devServer: {
        // 项目构建后路径
        contentBase: resolve(__dirname, 'build'),
        // 启动gzip压缩
        compress: true,
        // 端口号
        port: 3000,
        // 自动打开浏览器
        open: true
    }
  }

三、Webpack性能优化

3.1 开发环境性能优化

3.1.1 优化打包构建速度

​ HMR:hot module replacement 热模块替换/模块热替换

​ 作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块),极大提升构建速度。

​ 样式文件:可以使用HMR功能:因为style-loader内部实现了

​ js文件:默认不能使用HMR功能。 --> 需要修改js代码,添加支持HMR功能的代码

​ 注意:HMR功能对js的处理,只能处理非入口文件的其他文件。

if(module.hot){module.hot.accept('文件路径', function(){...})}

​ html文件:默认不能使用HMR功能,同时会导致html文件不能热更新。(不用做HMR功能)

​ 解决:修改entry入口,将html文件引入

entry: ['./src/index.html']
devServer: {
	// 开启HMR功能
	hot: true,
}

3.1.2 优化代码调试

​ source-map:一种提供源代码到构建后代码映射的技术(如果构建后代码出错了,通过映射可以追踪源代码错误)

[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

source-map:外部

​ ----> 错误代码准确信息 和 源代码的错误位置

inline-source-map:内联;只生产一个内联source-map

​ ----> 错误代码准确信息 和 源代码的错误位置

hidden-source-map:外部;

​ ----> 错误代码错误原因,但是没有错误位置;不能跟踪源代码错误,只能提示到构建后代码的错误位置

eval-source-map:内联;每一个文件都生产对应的source-map,都在eval里

​ ----> 错误代码准确信息 和 源代码的错误位置

nosources-source-map: 外部

​ ----> 错误代码准确信息但是没有任何源代码

cheap-source-map:外部

​ ----> 错误代码准确信息 和 源代码的错误位置;只能精确到行

cheap-module-source-map:外部

​ ----> 错误代码准确信息 和 源代码的错误位置;只能精确到行

​ ----> module会将loader的source map加入

内联和外部的区别:1. 外部生成了文件,内联没有;2. 内联构建速度更快

开发环境:速度快,调试更友好

​ 速度快:eval>inline>cheap>...

​ eval-cheap-source-map

​ eval-source-map

​ 调试更友好

​ source-map

​ cheap-module-source-map

​ cheap-source-map

​ --> eval-source-map / eval-cheap-module-source-map

生产环境:源代码要不要隐藏?调试要不要更友好?

​ 内联会让代码体积变大,所以在生产环境不用内联

​ nosources-source-map 全部隐藏

​ hidden-source-map 只隐藏源代码,会提示构建后代码错误信息

--> source-map / cheap-module-source-map

module.exports = {
  ...,
	devtool: 'source-map';
}

3.2 生产环境性能优化

3.2.1 优化打包构建速度

3.2.1.1 oneof
const { resolve } = require('path');

const commonCssLoader = [
    MiniCssExtractPlugin.loader,
    'css-loader',

    {
        loader: 'postcss-loader',
        options: {
            ident: 'postcss',
            plugins: () => [
                // postcss的插件
                require('postcss-preset-env')()
            ]
        }
    }
];

module.exports = {
    entry: './src/index.js',

    output: {
        filename: 'js/build.js',
        path: resolve(__dirname, 'build')
    },

    modules: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                enforce: 'pre',
                loader: 'eslint-loader',
                options: {
                    fix: true
                }
            },
            {
                // 一个文件只会匹配一个loader,匹配到了不会向下继续匹配
                oneof: [
                    {
                        test: /\.css$/,
                        use: [...commonCssLoader]
                    },
                    {
                        test: /\.less$/,
                        use: [
                            ...commonCssLoader,
                            'less-loader'
                        ]
                    },
                    {
                        test: /\.(jpg|png|gif)$/,
                        loader: 'url-loader',
                        options: {
                            limit: 8 * 1024,
                            esModule: false,
                            name: '[hash:10].[ext]',
                            outputPath: 'imgs'
                        }
                    },
                    {
                        test: /\.html$/,
                        loader: 'html-loader'
                    },
                    {
                        exclude: /\.(html|css|js|less|jpg|png|gif)$/,
                        loader: 'file-loader',
                        options: {
                            name: '[hash:10].[ext]',
                            outputPath: 'media'
                        }
                    },

                    {
                        test: /\.js$/,
                        exclude: /node_modules/,
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                [
                                    '@babel/preset-env',
                                    {
                                        useBuiltIns: 'usage',
                                        corejs: { version: 3 },
                                        targets: {
                                            chrome: '60',
                                            firefox: '60',
                                            ie: '9',
                                            safari: '10',
                                            edge: '17'
                                        }
                                    }
                                ]]
                        }
                    }
                ]
            }
        ]
    },
    // mode: 'development',
    mode: 'production',

}
3.2.1.2 babel缓存
/*
    babel缓存 --> 第二次打包构建速度更快
        cacheDirectory: true
*/
module.exports = {
    entry: './src/index.js',

    output: {
        filename: 'js/build.js',
        path: resolve(__dirname, 'build')
    },

    modules: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                options: {
                    presets: [[
                        '@babel/preset-env',
                        {
                            useBuiltIns: 'usage',
                            corejs: { version: 3 },
                            targets: {
                                chrome: '60',
                                firefox: '60',
                                ie: '9',
                                safari: '10',
                                edge: '17'
                            }
                        }
                    ]],
                    // 开启babel缓存
                    // 第二次构建时,会读取之前的缓存
                    cacheDirectory: true
                }
            }
        ]
    }
}
3.2.1.3 多进程
module.exports = {
    entry: './src/index.js',

    output: {
        filename: 'js/build.[contenthash:10].js',
        path: resolve(__dirname, 'build')
    },

    modules: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                    /*
                        开启多进程打包
                        进程启动大概为600ms,进程通信也有开销
                        只有工作消耗时间比较长,才需要多进程打包
                    */
                    {
                        loader: 'thread-loader',
                        options: {
                            workers: 2	// 开启2个进程
                        }
                    },
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: [[
                                '@babel/preset-env',
                                {
                                    useBuiltIns: 'usage',
                                    corejs: { version: 3 },
                                    targets: {
                                        chrome: '60',
                                        firefox: '60',
                                        ie: '9',
                                        safari: '10',
                                        edge: '17'
                                    }
                                }
                            ]],
                            // 开启babel缓存
                            // 第二次构建时,会读取之前的缓存
                            cacheDirectory: true
                        }
                    }
                ]
            }
        ]
    }
}
3.2.1.4 externals
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/index.js',
    output: {
        // [name] 取文件名
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname, 'build')
    },
    ...
    mode: 'production',
    externals: {
        // 拒绝jQuery被打包 -- npm包名
        // 需要手动引入cdn - jQuery
        jquery: 'jQuery'
    }
}
3.2.1.5 dll
/*
    使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包
        当你运行webpack时,默认查找webpack.comfig.js配置文件
        需要运行:webpack.dll.js文件
        --> webpack --config webpack.dll.js
*/
const { resolve } = require('path');
const webpack = require('webpack');

module.exports = {
    entry: {
        // 最终打包生成的[name] --> jquery
        // ['jquery'] --> 要打包的库是jquery
        jquery: ['jquery']
    },
    output: {
        filename: '[name].js',
        path: resolve(__dirname, 'dll'),
        library: '[name]_[hash]',	// 打包库里面向外暴露出去的内容叫什么名字
    },
    plugins: [
        // 打包生成一个manifest.json --> 提供和jquery映射
        new webpack.DllPlugin({
            name: '[name]_[hash]',	// 映射库的暴露的内容名称
            path: resolver(__dirname, 'dll/manifest.json')	// 输出文件路径
        })
    ],
    mode: 'production'
}


// webpack.config.js

const webpack = require('webpack');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
plugins: [
    ...
    new webpack.DllReferencePlugin({
        //	告诉webpack哪些库不参与打包,同时使用时的名称也得变
        manifest: resolve(__dirname, 'dll/manifest.json')
    }),
    // 将某个文件打包输出去,并在html中自动引入该资源
    new AddAssetHtmlWebpackPlugin({
        filepath: resolve(__dirname, 'dll/jquery.js')
    })
]

3.2.2 优化代码运行的性能

3.2.2.1 文件缓存
/*
    文件资源缓存 --> 让代码上线运行缓存更好使用
        问题:强缓存命中,无法获取最新资源
        hash:每次webpack构建时会生产唯一的hash值
            问题:因为js和css同时使用一个hash值,如果重新打包,会导致所有缓存失效(无论改动几个文件)
        chunkhash:根据chunk生产的hash值,如果打包来源于同一个chunk,那么hash值就一样
            问题:js和css的hash值还是一样的,因为css时在js中被引入的,所以同属于一个chunk
        contenthash:根据文件内容生产hash值,不同文件hash值一定不一样
*/
module.exports = {
    entry: './src/index.js',

    output: {
        // filename: 'js/build.[hash:10].js',
        // filename: 'js/build.[chunkhash:10].js',
        filename: 'js/build.[contenthash:10].js',
        path: resolve(__dirname, 'build')
    },
    ...
}
3.2.2.2 tree shaking
/*
	tree shaking:去除无用代码
		前提:1. 必须使用ES6模块化
				 2. 开启production环境
		作用:减少代码体积
		
		在package.json中配置
			"sideEffects": false	所有代码都没有副作用(都可以进行tree shaking)
			问题:可能会把css、/@babel/polyfill文件干掉
			"sideEffects": ["*.css", "*.less"]
*/
3.2.2.3 code split
// demo1
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    // 单入口
    // entry: './src/index.js',
    entry: {
        // 多入口:输出多个bundle
        index: './src/index.js',
        test: './src/test.js'
    },
    output: {
        // [name] 取文件名
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname, 'build')
    },
    ...
}
// demo2
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    // 单入口
    //entry: './src/index.js',
    entry: {
        // 多入口:输出多个bundle
        index: './src/index.js',
        test: './src/test.js'
    },
    output: {
        // [name] 取文件名
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname, 'build')
    },
    /*
          1. 可以将node_modules中代码单独打包一个chunk最终输出
          2. 自动分析,多入口文件中有没有公共的文件,如果有会打包成一个单独的chunk
    */
    optimization: {
        splitChunks: {
            chunks: all
        }
    }
	...
}
// demo3
/*
    基于webpack配置optimization,通过js代码,让某个文件被单独打包成一个chunk
    import动态导入语法:能将某个文件单独打包
*/
// 限定名字
import(/*webpackChunkName: 'test'*/'./test')
    .then(result => { })
    .catch(() => { })
3.2.2.4 lazy loading
// 基于3.2.1.4代码分割实现懒加载
document.getElementById('btn').onclick = function(){
  // 懒加载:当文件需要使用时才加载
  // 预加载 prefetch:会在使用之前,提前加载js文件
  // 正常加载可以认为是并行加载(同一时间加载多个文件)
  // 预加载会等其他资源加载完毕,浏览器空闲了,再偷偷加载资源
  import(/*webpackChunkName: 'test', webpackPrefetch: true*/'./test')
    .then(({mul}) => {
      console.log(mul(4,5))
    })
}
3.2.2.5 PWA
/*
    PWA:渐进式网络开发应用程序(离线可访问)
        workbox -->	workbox-webpack-plugin
*/

const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
module.exports = {
    ...
    plugins: [
		...
    /*
        1. 帮助serviceworker快速启动
        2. 删除旧的serviceworker
    	
        生成一个serviceworker配置文件
    */
    new WorkboxWebpackPlugin.GenerateSW({
        clientsClaim: true,
        skipWaiting: true
    })
  ]
}

/*
    1. eslint不认识window、navigator全局变量
        解决:需要修改package.json中eslintConfig配置
            "env": {
                "brower": true	// 支持浏览器端全局变量
            }
   2. sw代码必须运行在服务器上
          --> nodejs
          --> npm i serve -g
                    serve -s build	启动服务器,将build目录下的所有资源作为静态资源暴露出去
*/
// index.js 入口文件
if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
        navigator.serviceWorker
            .register('/service-worker.js')
            .then(() => { })
            .catch(() => { })
    })
}

四、webpack配置详解

4.1 entry

/*
    entry: 入口起点
        1. string --> './src/index.js'
            打包形成一个chunk,输出一个bundle文件。
            此时chunk的默认名称是main
    2. array	-->	['./src/js', './src/add.js']
        多入口
        所有入口文件最终只会形成一个chunk,输出一个bundle文件
            --> 一般用于HMR功能中让html热更新生效
    3. object
        多入口
        有几个入口文件就形成几个chunk,输出几个bundle文件
        此时chunk的名称是key
        --> 特殊用法	3里面包含1+2两种形式
*/
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    // entry: './src/index.js',
    // entry: ['./src/index.js', './src/add.js'],
    entry: {
        // index: './src/index.js',
        index: ['./src/index.js', './src/add.js'],
        add: './src/add.js'
    },
    output: {
        filename: '[name].js'
    path: resolve(__dirname, 'build')
    },
    plugins: [new HtmlWebpackPlugin()],
    mode: 'development'
}

4.2 output

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    entry: './src/index.js',
    output: {
        // 文件名称(指定名称+目录)
        filename: 'js/[name].js'
    // 输出文件目录(将来所有资源输出的公共目录)
    path: resolve(__dirname, 'build')
  	// 所有资源引入公共路径前缀:	'imgs/a.jpg' --> '/imgs/a.jpg'
  	publicPath: '/',
        chunkFilename: '[name]_chunk.js',	// 非入口chunk的名称
        // library: '[name]',	// 整个库向外暴露的变量名 + 一般结合dll一起使用
        // libraryTarget: 'window'	// 变量名添加到哪个上 browser
        // libraryTarget: 'global'	// 变量名添加到哪个上 node
        // libraryTarget: 'commonjs'	// 模块化语法

    },
    plugins: [new HtmlWebpackPlugin()],
    mode: 'development'
}

4.3 module


const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'js/[name].js'
    path: resolve(__dirname, 'build')
    },
    module: {
        rules: [
            // loader的配置
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.js$/,
                // 排除node_modules下的js文件
                exclude: /node_modules/,
                // 只检查src下的js文件
                include: resolve(__dirname, 'src'),
                // 优先执行
                enfore: 'pre',
                // 延后执行
                // enfore: 'post',
                // 单个loader使用loader
                loader: 'eslint-loader',
                options: {}
            },
            {
                // 一下配置只会生效一个
                oneof: []
            }

        ]
    },
    plugins: [new HtmlWebpackPlugin()],
    mode: 'development'
}

4.4 resolve


const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'js/[name].js'
    path: resolve(__dirname, 'build')
    },
    module: {
        rules: [
            // loader的配置
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
        ]
    },
    plugins: [new HtmlWebpackPlugin()],
    mode: 'development',
    // 解析模块的规则
    resolve: {
        // 配置解析模块路径别名: 简写路径
        alias: {
            $css: resovle(__dirname, 'src/css')
        },
        // 配置省略文件路径的后缀名
        extensions: ['js', 'json', 'css', 'jsx'],
        // 告诉webpack解析模块是去找哪个目录
        modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
    }
}

4.5 devServer


const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'js/[name].js'
    path: resolve(__dirname, 'build')
    },
    module: {
        rules: [
            // loader的配置
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
        ]
    },
    plugins: [new HtmlWebpackPlugin()],
    mode: 'development',
    devServer: {
        // 运行代码的目录
        contentBase: resolve(__dirname, 'build'),
        // 见识contenBase目录下的所有文件,一旦文件变化就会reload
        watchContentBase: true,
        watchOptions: {
            // 忽略文件
            ignored: /node_modules/
        },
        // 启动gzip压缩
        compress: true,
        // 端口号
        port: 5000,
        // 域名
        host: 'localhost',
        // 自动打开浏览器
        open: true,
        // 开启HMR功能
        hot: true,
        // 不要显示启动服务器日志信息
        clientLogLevel: 'none',
        // 除了一些基本启动信息以外,其他内容都不要显示  
        quiet: true,
        // 如果出错了,不要全屏提示
        overlay: false// 服务器代理 --> 解决开发环境跨域问题
        proxy: {
            // 一旦devServer(5000)服务器接收到/api/xxx的请求,就会把请求转发到另一个服务器
            '/api': {
                target: 'http://localhost:3000',
                // 发送请求时,请求路径重写:将/api/xx	-->	/xxx;(去掉/api)
                pathRewrite: {
                    '^/api': ''
                }
            }
        }
    }
}

4.6 optimization


const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'js/[name].js'
    path: resolve(__dirname, 'build')
    },
    module: {
        rules: [
            // loader的配置
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
        ]
    },
    plugins: [new HtmlWebpackPlugin()],
    mode: 'production',
    optimization: {
        splitChunks: {
            chunks: 'all',
            // 以下为默认值
            /*
             minSize: 30 * 1024,	// 分割的chunk最小为30kb
             maxSize: 0,	// 最大没有限制
             minChunks: 1,	// 要提取的chunk最少被引用1次
             maxAsyncRequests: 5,	// 按需加载时并行加载的文件最大数量
             maxInitialRequests: 3,	// 入口js文件最大并行请求数量
             automaticNameDelimiter: '~',	// 名称链接符
             name: true,	// 可以使用命名规则
             cacheGroups: {
               // 分割chunk的组
               // node_modules文件会被打包到wendors组的chunk中。	--> vendors~xxx.js
               // 满足上面的公共规则,如:大小超过30kb,至少被引用一次
               vendors: {
                 test: /[\\/]node_modules[\\/]/,
                 // 优先级
                 priority: -10
               },
               default: {
                   // 要提取的chunk最少被引用2次
                 minChunks: 2,
                 // 优先级
                 priority: -20,
                 // 如果当前要打包的模块,和之前已经被提取的模块是同一个,就会复用
                 reuseExistingChunk: true
               }
             }
             */
        },
        // 将当前模块的记录其他模块的hash单独打包为一个文件runtime
        // 解决:修改a文件导致b文件的contenthash变化
        runtimeChunk: {
            name: entrypoint => `runtime-${entrypoint.name}`
        },
        minimizer: {
            // 配置生成环境的压缩方案:js和css
            new TerserWebpackPlugin({
                // 开启缓存
                cache: true,
                // 开启多线程打包
                parallel: true
        // 启动source-map
        sourceMap: true
            })
        }
    }
}