写点什么

一文彻底读懂 webpack 常用配置

作者:Geek_02d948
  • 2023-03-15
    浙江
  • 本文字数:6346 字

    阅读完需:约 21 分钟

开发环境

const webpack = require("webpack");const path = require('path')module.exports = {    // entry: {        // a: './src/0706/a.js',        // c: './src/0706/c.js',    // },    entry: "./src/0707/reactDemo.js",    output: {        filename: '[name]_dist.js',        path: path.resolve(__dirname, 'dist3'),    },    mode: 'development',    devtool: 'source-map',    module: {        rules: [            {                test:/.js$/,                use: 'babel-loader',            },            {                test: /.css$/,                use: [                    'style-loader',                    'css-loader'                ]            },            {                test:/.less$/,                use: [                    'style-loader',                    'css-loader',                    'less-loader'                ]            },            {                test: /.(png|jpg|gif|jpeg)$/,                use: 'file-loader'            },            {                test: /.(png|jpg|jpeg|gif)$/,                use: {                    loader: 'url-loader',                    options: {                        limit: 10240 * 10                    }                }            },            {                test: /.(woff|woff2|eot|ttf|otf)$/,                use: 'file-loader'            }        ],    },    // plugins: [        // new webpack.HotModuleReplacementPlugin()    // ],    // 在使用devServer的时候,如果hot为true的话,会自动帮我们添加HotModuleReplacementPlugin    // 如果使用自己实现的服务器,就需要自己添加    devServer: {        contentBase: './dist3',        hot: true    }}
复制代码

生产环境

const webpack = require("webpack");const MiniCssExtractPlugin = require('mini-css-extract-plugin');// minicssextractplugin 推荐使用cssminimizerwebpackplugin来压缩cssconst CssMinimizerPlugin = require('css-minimizer-webpack-plugin');// 根据模板生产html,并插入相应的chunk,同时也可以压缩htmlconst HtmlWebpackPlugin = require('html-webpack-plugin');// 清除构建产物的插件,注意这里的引入方式const { CleanWebpackPlugin } = require('clean-webpack-plugin');const path = require('path');module.exports = {    // entry: {        // a: './src/0706/a.js',        // c: './src/0706/c.js',    // },    entry: "./src/0707/reactDemo.js",    output: {        // 文件指纹 chunkhash chunk改变就会重新生成        // hash 整个项目有文件改变就会重新生成        // contenthash 文件内容改变才会重新生成        filename: '[name]_[chunkhash:8].js',        path: path.resolve(__dirname, 'dist3'),    },    mode: 'production',    optimization: {        minimizer: [            // 压缩CSS            new CssMinimizerPlugin(),            // webpack5内置了terser-plugin,但是上面的插件会覆盖掉默认的terser-plugin,所以通过下面的一行来将默认的插件加回去            '...'        ]    },    module: {        rules: [            {                test:/.js$/,                use: 'babel-loader',            },            {                test: /.css$/,                use: [                    MiniCssExtractPlugin.loader,                    'css-loader'                ]            },            {                test:/.less$/,                use: [                    // 使用miniCssExtractPlugin提取css后,这里需要替换成它的loader                    MiniCssExtractPlugin.loader,                    'css-loader',                    'less-loader'                ]            },            {                test: /.(png|jpg|gif|jpeg)$/,                use: {                    loader: 'file-loader',                    options: {                        name: '[name]_[hash:8].[ext]'                    }                }            },            {                test: /.(png|jpg|jpeg|gif)$/,                use: {                    loader: 'url-loader',                    options: {                        limit: 10240 * 10                    }                }            },            {                test: /.(woff|woff2|eot|ttf|otf)$/,                use: 'file-loader'            }       ],    },     plugins: [        new MiniCssExtractPlugin({            // 使用contenthash 这样如果只改变了js的话css也无需重新生成            filename: '[name]_[contenthash:8].css'        }),        new HtmlWebpackPlugin({            // 模板所在路径            template: path.resolve(__dirname, 'src/index.html'),            // 生成的html的名字            filename: 'index2.html',            // 用到了哪个chunk            // chunks: ['a']            // 压缩选项            minify: {                html5: true,                collapseWhitespace: true,                preserveLineBreaks: false,                minifyCSS: true,                minifyJS: true,                removeComments: true            }        })    ]}
复制代码

自动添加 CSS 前缀

  • 使用 postcss-loader + autoprefixer

  • 添加 postcss.config.js 新版本直接在 webpack 配置文件里添加会报错,所以需要写到一个独立的配置文件里


module.exports = {    plugins: [        require('autoprefixer')({            overrideBrowserslist: ['last 2 version', '>1%', 'ios 7']        })    ]}
复制代码


  • 添加 loader


{    test: /.css$/,    use: [        MiniCssExtractPlugin.loader,        'css-loader',        'postcss-loader' // 这里为新加的loader    ]},
复制代码

移动端适配 css px 自动转 rem

  • 使用手淘 lib-flexible 动态计算 font-size


// 将lib-flexible静态内联到html上,因为要最先执行计算// 在头部加入如下代码// 使用了raw-loader,相当于在对应的位置是插入字符串// 需注意raw-loader新老版本引入的差异<script type="text/javascript"<%=require('raw-loader!babel-loader!./node_modules/lib-flexible/flexible.js')%></script>
复制代码


  • 使用 px2rem-loader 将 px 转成 rem


{    test: /.less$/    use: [        'style-loader',        'css-loader',        'less-loader',        {            loader: 'px2rem-loader',            options: {                // 以设计稿宽度750px为例,1rem = 75px                remUnit: 75,                // 转换后的小数点后保留位数                remPrecision: 8,            }        }    ]}
复制代码


  • 代码里面直接按设计稿一样写 px


// 下面的px最后会被转成em,如果有些特殊的地方不想转,可写成大写PX.box {    width: 100px;    height: 100px;    // 写成大写则不会被转换    border: 1PX;}
复制代码

代码分割

  • 利用 splitChunks plugin 将公共代码抽离


optimization: {    splitChunks: {        cacheGroups: {            vendors: {                chunks: 'all',                name: 'vendors',                // 将react和react-dom提取出一个包                test: /(react|react-dom)/            },            common: {                name: 'common',                chunks: 'all',                minSize: 0,                // 被引用两次以上的提取出一个包                minChunks: 2            }        }    }}
复制代码


参考 前端进阶面试题详细解答

动态 import 懒加载

  • 通过 ES6 的动态 import + babel 插件 @babel/plugin-syntax-dynamic-import


//babel配置里增加plugins: [    '@babel/plugin-syntax-dynamic-import']// 代码里按需引入import('xxx').then(res => res.default);
复制代码

webpack 结合 eslint

  • 以 react 为例,用到几个插件 eslint eslint-plugin-import eslint-plugin-react eslint-plugin-jsx-a11y

  • 安装解析器 babel-eslint

  • 用 airbnb 的规则,需安装 eslint-config-airbnb

  • 安装 eslint-loader

  • 增加 eslint 配置 eslintrc.js


module.exports = {    // 使用babel-eslint作为解析器    "parser": "babel-eslint",    // 继承airbnb的规则    "extends": ["airbnb"],    // 指定环境,这样使用全局变量的时候不会报错    "env": {        "browser": true,        "node": true    },    // 自定义规则覆盖默认规则    "rules": {        // 使用4个空格缩进,否则error        "indent": ["error", 4]    }}
复制代码

webpack 打包库

  • 代码写好后,webpack 配置如下


const path = require('path');module.exports = {    // 同时提供未压缩和压缩的版本    entry: {        'mylibrary': './src/entry.js',        'mylibrary.min': './src/entry.js'    },    output: {        path: path.resolve(__dirname, 'lib'),        // mylibrary.js mylibrary.min.js        filename: '[name].js',        // 对外暴露的库的名称        library: 'mylibrary',        // 支持cjs, ejs, script脚本等引入方式        libraryTarget: 'umd',        // 不加这个的话,使用的时候可能需要mylibrary.default        libraryExport: 'default'    }}
复制代码


  • 添加 terser-webpack-plugin 进行压缩


const TerserPlugin = require('terser-webpack-plugin');


optimization: {    minimize: true,    minimizer: [        new TerserPlugin({            // 只对min版本压缩            test: /.min.js/        })    ]}
复制代码


  • package.json 指定入口文件


"main": "index.js"
复制代码


  • index.js 里面做环境判断


if(process.env.NODE_EVN === 'production') {    module.exports = require('./lib/mylibrary.min.js');} else {    module.exports = require('./lib/mulibrary.js');}
复制代码

主动捕获异常

  • 通过插件主动捕获异常


plugins: [    function() {        this.hooks.done.tap('done', (stats) => {            if(stats.compilation && stats.compilation.errors.length > 1) {                console.log('error')            }        })    }]
复制代码

构建优化

速度优化:

  • speed-measure-webpack-plugin 分析构建速度


const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin');const spm = new SpeedMeasureWebpackPlugin();module.exports = spm.wrap({...});
复制代码


  • thread-loader 开启多进程,放在需要的 loader 上面


module: {    rules: [        {            test: /.js$/            use: [                {                    loader: 'thread-loader',                    options: {                        workers: 3                    }                }            ]        }    ]}
复制代码


  • include exclude 缩小构建目标

  • resolve 减少文件搜索范围


modules.exports = {    ...    resolve: {        // 指定node_modules的路径,减少模块搜索层级        modules: [path.resolve(__dirname, 'node_modules')],        // import react的时候直接从指定的路径去找        alias: {            react: path.resolve(__dirname, './node_modules/react/dist/react.min.js')        },        // import xx from 'a'的时候,只找.js后缀的        // 高频文件后缀名放前面        extensions: ['.js'],        // 指定入口,避免不必要的分析        mainFields: ['main']    }}
复制代码


  • 开启 babel-loader 缓存


// 仅需加个url参数module: {    rules: [        {            test: /.js$/,            use: ['babel-loader?cacheDirectory=true'        }    ]}
复制代码


  • terser-webpack-plugin 开启缓存


// webpack5之后不再用这种方式new TerserWebpackPlugin({    cache: true})
复制代码


  • cache-loader 缓存

  • hard-source-webpack-plugin 缓存,减少二次构建时间


plugins: [new HardSourceWebpackPlugin()]
复制代码


  • terser-webpack-plugin 默认开启了 JS 多进程压缩


optimization: {    minimizer: [        new TerserWebpackPlugin({            // 指定进程数量            parallel: 4        })    ]}
复制代码


  • 使用 DLLPlugin 进行分包


先构建出单独的包


// 单独的配置文件用于生成包module.exports = {    entry: {        // 将react react-dom抽离出单独的包        library: ['react', 'react-dom']    },    output: {        filename: '[name].dll.js',        path: path.resolve(__dirname, 'dist3/lib')        library: '[name]'    },    plugins: [        // 使用DLLPlugin抽离,生成manifest        new webpack.DllPlugin({            name: '[name]_2',            path: path.resolve(__dirname, 'dist3/lib/[name].json'),        }),        // new CleanWebpackPlugin(),    ]}
复制代码


再通过 manifest 关联抽离的包


// webpack.prod.config.jsnew webpack.DllReferencePlugin({    manifest: require('./dist3/lib/library.json')})
复制代码


最后将抽离的包插入 html 模板中


  • noParse 对完全不需要解析的库进行忽略 (不去解析但仍会打包到 bundle 中,注意被忽略掉的文件里不应该包含 import、require、define 等模块化语句)

体积优化

  • webpack-bundle-analyzer 分析体积


plugins: [    new WebpackBundleAnalyzer()]
复制代码


  • 图片压缩


使用 image-webpack-loader


rules: [{    test: /\.(gif|png|jpe?g|svg)$/i,    use: [        'file-loader',        {            loader: 'image-webpack-loader',            options: {                mozjpeg: {                    progressive: true,                },                // optipng.enabled: false will disable optipng                optipng: {                    enabled: false,                },                pngquant: {                    quality: [0.65, 0.90],                    speed: 4                },                gifsicle: {                    interlaced: false,                },                // the webp option will enable WEBP                webp: {                    quality: 75                }            }        },    ],}]
复制代码


  • 对 CSS 进行 tree shaking


使用 purgecss-webpack-plugin,要配合 mini-css-extract-plugin 一起使用


const purgecssPath = path.join(__dirname, 'src');const glob = require('glob');new PurgecssPlugin({    paths:    glob.sync(`${purgecssPath}/**/*`, { nodir: true }),}),
复制代码


  • 动态 polyfill


根据浏览器的 user agent 动态下发 polyfill


<script src="https://polyfill.io/v3/polyfill.min.js"></script>
复制代码


或者可以自建 CDN


用户头像

Geek_02d948

关注

还未添加个人签名 2022-09-08 加入

还未添加个人简介

评论

发布
暂无评论
一文彻底读懂webpack常用配置_JavaScript_Geek_02d948_InfoQ写作社区