乐趣区

关于webpack:一文彻底读懂webpack常用配置

开发环境

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 来压缩 css
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
// 依据模板生产 html,并插入相应的 chunk,同时也能够压缩 html
const 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
  • 参考 webpack 视频解说:进入学习
// 将 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.js
new 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

退出移动版