webpack配置4webpack.config.js

91 阅读8分钟
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const webpack = require('webpack')

// 更多webpack配置,访问 https://cloud.tencent.com/developer/section/1477556 , https://www.cnblogs.com/tugenhua0707/p/9384953.html

module.exports = {
    devServer: {
        port: 3000,
        progress: true
    },
    mode: "development",
    entry: {
        index: path.resolve(__dirname, 'src/index.js'),
        index1: path.resolve(__dirname, 'src/index1.js')
    },
    output: {
        filename: '[name].[hash:8].js',
        path: path.resolve(__dirname, 'dist'),
    },
    resolve: { // 解析第三方包
        // 解析第三方库时查找的位置
        modules: [
            path.resolve(__dirname, './node_modules')
        ],
        // 按照以下顺序解析文件后缀名
        extensions: ['.js', '.css', '.json'],
        // 引入第三方库时按照以下顺序查找第三方库里的package.json入口
        mainFields: ['style', 'main'],
        // 别名,第三方库配置别名后,引入第三方库时只需要引入别名即可
        alias: {
            bootstrap: 'bootstrap/dist/css/bootstrap.css'
        }
    },
    devtool: 'inline-source-map',
    // devtool: 'eval',
    // 不会产生单独的文件的情况,会集成到打包的js里
    // 1.会产生单独的文件,会显示报错的行和列
    // devtool: 'source-map',
    // 2.不会产生单独的文件,会显示报错的行和列
    // devtool: 'eval-source-map',
    // 3.会产生单独的文件,不会显示报错的列
    // devtool: 'cheap-module-source-map',
    // 4.不会产生单独的文件, 不会显示报错的列
    // devtool: 'cheap-module-eval-source-map',
    // watch: true,
    // watchOptions: { // 监控的选项
    //     poll: 1000, // 多少秒监控一次
    //     aggregateTimeout: 500, // 防抖,防止一直输入代码一直打包,单位毫秒
    //     ignored: /node_modules/ // 不需要监控哪个文件
    // },

    /**
     *
     * 配置项
     * 类型  含义
     * source-map  原始代码 最好的sourcemap质量有完整的结果,但是会很慢
     * eval-source-map 原始代码 同样道理,但是最高的质量和最低的性能
     * cheap-module-eval-source-map    原始代码(只有行内) 同样道理,但是更高的质量和更低的性能
     * cheap-eval-source-map   转换代码(行内) 每个模块被eval执行,并且sourcemap作为eval的一个dataurl
     * eval    生成代码 每个模块都被eval执行,并且存在@sourceURL,带eval的构建模式能cache SourceMap
     * cheap-source-map    转换代码(行内) 生成的sourcemap没有列映射,从loaders生成的sourcemap没有被使用
     * cheap-module-source-map 原始代码(只有行内) 与上面一样除了每行特点的从loader中进行映射
     *
     * 看似配置项很多, 其实只是五个关键字eval、source-map、cheap、module和inline的任意组合
     * 关键字可以任意组合,但是有顺序要求
     * 关键字 含义
     * eval    使用eval包裹模块代码
     * source-map  产生.map文件
     * cheap   不包含列信息(关于列信息的解释下面会有详细介绍)也不包含loader的sourcemap
     * module  包含loader的sourcemap(比如jsx to js ,babel的sourcemap),否则无法定义源文件
     * inline  将.map作为DataURI嵌入,不单独生成.map文件
     *
     * 组合规则
     * [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
     * source-map 单独在外部生成完整的sourcemap文件,并且在目标文件里建立关联,能提示错误代码的准确原始位置
     * inline-source-map 以base64格式内联在打包后的文件中,内联构建速度更快,也能提示错误代码的准确原始位置
     * hidden-source-map 会在外部生成sourcemap文件,但是在目标文件里没有建立关联,不能提示错误代码的准确原始位置
     * eval-source-map 会为每一个模块生成一个单独的sourcemap文件进行内联,并使用eval执行
     * nosources-source-map 也会在外部生成sourcemap文件,能找到源始代码位置,但源代码内容为空
     * cheap-source-map 外部生成sourcemap文件,不包含列和loader的map
     * cheap-module-source-map 外部生成sourcemap文件,不包含列的信息但包含loader的map
     */
    optimization: {
        /**
         chunks选项,决定要提取那些模块,详细 https://zhuanlan.zhihu.com/p/136443552
             async:默认值,只提取异步加载的模块出来打包到一个文件中,异步加载的模块:通过import('xxx')或require(['xxx'],() =>{})加载的模块。
                     1.只要是被异步加载的模块,单独放入新的chunks中
                     2.只要是被同步加载的模块,不管复用没复用,保留一份在之前的chunk中
             initial:提取同步加载和异步加载模块,如果xxx在项目中异步加载了,也同步加载了,那么xxx这个模块会被提取两次,分别打包到不同的文件中。
                     1.只要是被异步加载的模块,单独放入新的chunks中
                     2.对于复用的,每次都是被同步加载的模块,单独放入新的chunks中
                     3.对于复用的,一次被同步加载,这次保留一份在原来的chunks中,一次被异步加载,这次单独放入一份在新的chunks中
             同步加载的模块:通过 import xxx或require('xxx')加载的模块。
             all:不管异步加载还是同步加载的模块都提取出来,打包到一个文件中。
                     1.只要是被复用的,不管是异步加载还是同步加载的模块,单独放入新的chunks中
                     2.对于不复用的被同步加载的模块,保留在原来的chunks中
         minSize选项:规定被提取的模块在压缩前的大小最小值,单位为字节,默认为30000,只有超过了30000字节才会被提取。
         maxSize选项:把提取出来的模块打包生成的文件大小不能超过maxSize值,如果超过了,要对其进行分割并打包生成新的文件。单位为字节,默认为0,表示不限制大小。
         minChunks选项:表示要被提取的模块最小被引用次数,引用次数超过或等于minChunks值,才能被提取。
         maxAsyncRequests选项:最大的按需(异步)加载次数,默认为 6。
         maxInitialRequests选项:打包后的入口文件加载时,还能同时加载js文件的数量(包括入口文件),默认为4。
         优先级 maxInitialRequests / maxAsyncRequests < maxSize < minSize。
         automaticNameDelimiter选项:打包生成的js文件名的分割符,默认为~。
         name选项:打包生成js文件的名称。
         cacheGroups选项,核心重点,配置提取模块的方案。里面每一项代表一个提取模块的方案。下面是cacheGroups每项中特有的选项,其余选项和外面一致,若cacheGroups每项中有,就按配置的,没有就使用外面配置的。
            test选项:用来匹配要提取的模块的资源路径或名称。值是正则或函数。
            priority选项:方案的优先级,值越大表示提取模块时优先采用此方案。默认值为0。
            reuseExistingChunk选项:true/false。为true时,如果当前要提取的模块,在已经在打包生成的js文件中存在,则将重用该模块,而不是把当前要提取的模块打包生成新的js文件。
            enforce选项:true/false。为true时,忽略minSize,minChunks,maxAsyncRequests和maxInitialRequests外面选项
         */
        splitChunks: { // 代码分割
            cacheGroups: { // 缓存组
                common: { // 公共模块
                    chunks: "all",
                    minSize: 0, // 文件大小大于多少进行抽离,单位为B
                    minChunks: 2, // 文件被引用多少次进行抽离
                },
                vendor: {
                    priority: 1,
                    test: /node_modules/,
                    chunks: 'initial',
                    minSize: 0,
                    minChunks: 2
                }
            }
        },
        minimizer: [
            new UglifyjsWebpackPlugin({
                include: /.js$/, // 要包含的文件
                exclude: /.min.js$/, // 过滤掉.min.js结尾的文件,这个后缀本身就是已经压缩好的代码,没必要进行二次压缩
                cache: true, // 启用文件缓存,文件内容没变化不会重新压缩
                parallel: true, // 使用多进程并行运行来提高构建速度,并行压缩
                sourceMap: true, // 使用sourceMap将错误消息位置映射到模块,Type: Boolean Default: false
                extractComments: false, // 将注释提取到单独的文件中
                chunkFilter: (chunk) => { // 按照代码块名排除哪些需要被压缩,哪些不需要被压缩
                    // `vendor` 模块不压缩
                    if (chunk.name === 'vendor') {
                        return false;
                    }
                    return true;
                },
                uglifyOptions: {
                    output: {
                        beautify: false, // 是否输出可读性较强的代码,即会保留空格和制表符,默认为输出,为了达到更好的压缩效果,可以设置为false
                        comments: false // 是否保留代码中的注释,默认为保留,为了达到更好的压缩效果,可以设置为false
                    },
                    compress: {
                        drop_console: true, // 是否删除代码中所有的console语句,默认为不删除,开启后,会删除所有的console语句
                        collapse_vars: true, // 是否内嵌虽然已经定义了,但是只用到一次的变量,比如将 var x = 1; y = x, 转换成 y = 5, 默认为不转换,为了达到更好的压缩效果,可以设置为false
                        // 是否提取出现了多次但是没有定义成变量去引用的静态值,比如将 x = 'xxx'; y = 'xxx'  转换成
                        // var a = 'xxxx'; x = a; y = a; 默认为不转换,为了达到更好的压缩效果,可以设置为false
                        reduce_vars: true
                    },
                    warnings: false, // 是否在UglifyJS删除没有用到的代码时输出警告信息,默认为输出,可以设置为false关闭这些作用不大的警告
                }
            }),
            new OptimizeCssAssetsWebpackPlugin()
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './index.html',
            filename: 'index.html',
            minify: {
                removeComments: true, // 清理html中的注释
                removeEmptyElements: true, // 清理内容为空的元素
                caseSensitive: true, // 以区分大小写的方式处理自定义标签内的属性
                removeScriptTypeAttributes: true, // 去掉script标签的type属性,type="text/javascript"
                removeStyleLinkTypeAttributes: true, // 去掉style和link标签的type属性,type="text/css"
                minifyCSS: true, // 是否压缩html内的样式,构建后html内的样式,无论是写在style标签内还是直接写在元素上的样式,包含的空格、换行和最后一个分号都被清理
                minifyJS: true, // 是否压缩html内的js
                collapseWhitespace: true // 清除html中的空格、换行符
            },
            hash: true // 是否给生成的js添加hash值
        }),
        // 向打包文件里注入版本号注释
        new webpack.BannerPlugin('make 2021 by pa'),
        new webpack.DefinePlugin({
            'process.env': "'production'"
        }),
        // 复制插件,打包时将某个目录下的文件拷贝到打包目录里
        // new CopyWebpackPlugin([
        //     { from: './doc', to: './' }
        // ]),
        new CopyWebpackPlugin([
            {
                from: path.resolve(__dirname, 'public'),
                to: path.resolve(__dirname, 'dist/public')
            }
        ]),
        new MiniCssExtractPlugin({
            filename: 'css/main.css'
        }),
        new webpack.ProvidePlugin({
            $: 'jquery',
            '_b': 'bootstrap' // 此处的bootstrap,就是上面配置的别名
            // '_b': 'bootstrap/dist/css/bootstrap.css' // 如果上面没有配置别名,则这样处理
        }),
        new CleanWebpackPlugin()
    ],
    module: {
        rules: [
            {
                test: /.css$/,
                use: [
                    // 'style-loader',
                    { // 代替style-loader
                        loader: MiniCssExtractPlugin.loader
                    },
                    'css-loader',
                    'postcss-loader',
                ]
            },
            {
                test: /.scss$/,
                use: [
                    // 'style-loader',
                    {
                        loader: MiniCssExtractPlugin.loader
                    },
                    'css-loader',
                    'postcss-loader',
                    'sass-loader'
                ]
            },
            {
                test: /.js$/,
                use: {
                    loader: 'eslint-loader',
                    options: {
                        fix: true, // 如果发现不合法,自动修复
                        enforce: 'pre' // 设置该loader为前置loader,优先于babel-loader执行,意思就是在代码编译之前先进行格式校验
                    }
                }

            },
            /**
             * babel-loader调用babel核心@babel/core
             * 但是,@babel/core虽然能识别js代码,但不知道如何转换代码,插件知道
             * es6语法很多,转换es6代码的插件也就很多,把这些插件打成一个包,包被称为preset-env预设
             */
            {
                test: /.js$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [
                            '@babel/preset-env'
                        ],
                        plugins: [
                            // 当打包构建项目时,JavaScript 包会变得非常大,影响页面加载。
                            // 如果我们能把不同路由对应的组件分割成 不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
                            '@babel/plugin-syntax-dynamic-import' // 路由懒加载插件
                        ]
                    }
                },
                include: path.resolve(__dirname, 'src'),
                exclude: /node_modules/
            }
        ]
    }
}