webpack基本配置

const path = require('path');
const os = require('os');
const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserWbpackPlugin = require('terser-webpack-plugin');
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');
const WorkboxPlugin = require('workbox-webpack-plugin');

const threads = os.cpus().length; // cpu核数

// 封装样式loader函数,获取处理样式的loader
function getStyleLoader(pre) {
    return  [
        MiniCssExtractPlugin.loader, // 提取css成单独的文件
        'css-loader', // 将css资源编译成commonjs的模块到js中
        {
            loader: 'postcss-loader',
            options: {
                postcssOptions: {
                    plugins: [
                        'postcss-preset-env' // 能解决大多数样式兼容问题
                    ]
                }
            }
        },
        pre
    ].filter(Boolean);
}

module.exports = {
    // 单入口
    entry: 'app.js',
    // 多入口
    // entry: {
    //     app: './src/app.js',
    //     main: './src/main.js'
    // },
    // 输出
    output: {
        // 所有文件的输出路径
        // __dirname为node.js的变量,代表当前文件的文件夹目录
        path: path.resolve(__dirname, 'dist'), // 绝对路径
        // 入口文件打包输出的文件名
        filename: 'static/js/main.js',
        // 多入口文件打包
        // filename: '[name].js', // webpack命名方式,[name]以入口文件名的key命名打包后的文件名
        // 自动清空上次打包结果
        clean: true
    },
    // 加载器
    module: {
        // loader的配置
        rules: [
            {
                // 每个文件只能被其中一个loader配置处理
                oneOf: [
                    // 处理css资源
                    {
                        test: /\.css$/, // 只检测.css文件
                        // loader: '', // 只能使用一个loader
                        // 使用多个loader,使用那些loader处理,执行顺序为【倒序】执行
                        use: getStyleLoader()
                    },
                    // 处理less资源
                    {
                        test: /\.less$/, // 只检测.css文件
                        use: getStyleLoader('less-loader')
                    },
                    // 处理sass资源
                    {
                        test: /\.s[ac]ss$/, // 只检测.css文件
                        use: getStyleLoader('sass-loader')
                    },
                    // 处理stylus资源
                    {
                        test: /\.sty$/, // 只检测.css文件
                        use: getStyleLoader('stylus-loader')
                    },
                    // 处理图片资源
                    {
                        test: /\(png|jpe?g|gif|webp|svg)$/,
                        type: 'asset',
                        parser: {
                            dataUrlCondition: {
                                // 小于10kb的图片转成base64,优化请求数量,但是会导致打包体积变大
                                maxSize: 10 * 1024 // 10kb
                            }
                        },
                        generator: {
                            // 输出图片名称
                            // [hash:10] hash值取前十位,[hash]取全部hash值
                            filename: 'static/image/[hash:10][ext][query]'
                        }
                    },
                    // 处理字体图标和其他资源
                    {
                        test: /\(ttf|woff2?|mp3|mp4|avi)$/,
                        type: 'asset/resource',
                        generator: {
                            // 输出字体名称
                            filename: 'static/media/[hash:10][ext][query]'
                        }
                    },
                    // babel
                    {
                        test: /\.js$/,
                        // include和exclude只能任选其一
                        exclude: /node_modules/, // 排除node_modules文件夹下的js文件
                        // include: path.resolve(__dirname, 'src'), // 只处理src文件夹下的文件,其它不做处理
                        use: [
                            {
                                loader: 'thread-loader', // 开启多进程对babel进行处理
                                options: {
                                    works: threads // 进程数量
                                }
                            },
                            {
                                loader: 'babel-loader',
                                options: {
                                    presets: [
                                        '@babel/preset-env',
                                        // corejs解决js兼容性问题
                                        {
                                            useBuiltIns: 'usage',
                                            corejs: 3
                                        }
                                    ], // 使用智能预设
                                    cacheDirection: true, // 开启babel缓存,提升开发环境下的打包速度
                                    cacheCompression: false // 关闭缓存文件压缩,开启的话会占用内存空间,降低打包速度
                                }
                            }
                        ]
                    }
                ]
            }
        ]
    },
    // 插件
    plugins: [
        // eslint插件(必须先配置eslint文件 .eslintrc.js)
        new ESLintPlugin({
            // 检测哪些文件夹
            context: path.resolve(__dirname, 'src'),
            // 排除那些文件夹
            exclude: 'node_modules', // 默认值
            // 开启缓存
            cache: true,
            // 指定缓存文件的路径
            cacheLocation: path.resolve(__dirname, 'node_modules/.cache/eslintcache'),
            threads // 开启多进程和设置进程数量
        }),
        new HtmlWebpackPlugin({
            // 模板:以public/index.html文件创建html文件
            // 新的html文件特点:1、结构与原来一致 2、自动引入打包输出的资源
            template: path.resolve(__dirname, 'public/index.html')
        }),
        // 提取css成单独文件
        new MiniCssExtractPlugin({
            filename: 'static/css/main.css'
        }),
        // PWA离线技术,将文件缓存在本地,可以实现离线访问,但是兼容性教差
        new WorkboxPlugin({
            clientClaim: true,
            skipWaiting: true
        })
        // 若要使用PWA离线技术,需要将以下代码需要加到main.js当中
        // if ('serviceWorker' in navigator) {
        //     window.addEventListener('load', () => {
        //         navigator.serviceWorker.register('/service-worker.js').then(registration => {
        //             console.log('SW registration', registration)
        //         })
        //         .catah(registrationError => {
        //             console.log('SW registration failed', registrationError)
        //         })
        //     })
        // }
    ],
    optimization: {
        // 压缩的操作
        minimizer: [
            // css压缩
            new CssMinimizerPlugin(),
            // js压缩
            new TerserWbpackPlugin({
                parallel: threads // 开启多进程和设置进程数量
            }),
            // 图片压缩
            new ImageMinimizerPlugin()
        ],
        // 代码分割的操作
        splitChunks: {
            chunks: 'all'
        },
        runtimeChunk: {
            name: (entrypoint) => `runtime${entrypoint.name}.js`
        }
    },
    // 开发服务器(不会输出资源,在内存中编译打包)
    devServer: {
        host: 'loaclhost', // 域名
        port: '3000', // 端口
        open: true, // 是否自动打开浏览器
        hot: true // HMR(HotModuleReplacement),默认开启(true),只能用于开发环境,生产环境不再需要,旨在提升打包构建速度
    },
    // 模式
    mode: 'development', // 'production'
    // Source-Map,形成映射文件,方便查定位错误
    devtool: 'cheap-module-source-map' // 'source-map'
};