webpack基本入门配置学习

48 阅读8分钟
const path = require('path')
const HtmlWebpackPlugin = require("html-webpack-plugin")  // 自动生成index.html文件
const MiniCssExtractPlugin = require("mini-css-extract-plugin")  //用MiniCssExtractPlugin.loader 替代style-loader 将css合并并成一个 并统一用一个link标签加载
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");  //压缩打包后的css
const TerserWebpackPlugin = require("terser-webpack-plugin");
const webpack = require("webpack")
// const { ModuleFederationPlugin } = webpack.container   // 模块联邦
// const WorkBoxPlugin = require("workbox-webpack-plugin");  //离线运行
// const { BundleAnalyzerPlugin } = require("webapck-bundle-analyzer")

const isDev = process.env.NODE_ENV === 'development';
module.exports = {

    //  常用代码分离
    // 入口起点:使用 entry 配置手动地分离代码。
    // 防止重复:使用 Entry dependencies 或者 SplitChunksPlugin 去重和分离 chunk。
    // 动态导入:通过模块的内联函数调用来分离代码。

    // 1. 单入口,直接是个字符串
    // entry:"./src/index.js",  
    //2. 配置入口节点 another,使用entry配置手动分离代码, 配合SplitChunksPlugin使用splitChunks自动all分隔
    //缺点:多入口的话,多入口的共享文件会重复在每个包里打包  推荐使用dependOn 
    // entry:{                  
    //     index:"./src/index.js",
    //     another: "./src/index-another.js" 
    // },

    // 3.防止重复分离
    //1、 配置入口节点 another,使用entry配置手动分离代码,缺点:多入口的话,多入口的共享文件会重复在每个包里打包
    //2.   防止重复分离: 使用entry dependence 或者optimization.splitChunks 去重和分离公共代码  
    //3.   动态导入  : 通过调用模块的内联函数 import() 分离代码  import(/* webpackChunkName: 'math', webpackPrefetch: true */
    //4  webpackPrefetch:true预获取,空闲时加载 ;webpackPreload:true 类似懒加载 使用时候加载

    //    import(/* webpackChunkName: 'math', webpackPrefetch: true */
    //     './math.js').then(({ add }) => {
    //         console.log(add(4, 5))
    //       })
    //     }) 



    // entry:{
    //     index:{
    //         import:"./src/index.js",
    //         dependOn:'shared'   //dependOn当前入口的依赖项,shared可以把共享的文件定义出来   防止重复分离: 使用entry dependOn 可以在多个chuck直接共享模块
    //     },
    //     another:{
    //         import:"./src/index-another.js",
    //         dependOn:'shared'
    //     },
    //     shared: 'lodash'  //  当上面两个模块有loadsh时 会抽离出来  并生成名为'shared'的chuck 
    // },
    // 4.多页应用,按功能模块划分, 多入口文件打包
    entry: {
        main1: {
            import: ["./src/app1.js", "./src/app2.js"],
            dependOn: "dependName",
            filename: "chanel1/[name].js"
        },
        main2: {
            import: "./src/app3.js",
            dependOn: "dependName",
            filename: "chanel2/[name].js"
        },
        dependName: {
            import: "lodash",
            filename: "common/[name].js"
        }

    },
    output: {
        filename: 'scripts/[name].[contenthash].js',  // [name] 可以拿到入口里面chuck的这个key的名字
        path: path.resolve(__dirname, './dist'),
        clean: true,
        publicPath: '/', //通常是CDN地址  例如,你最终编译出来的代码部署在 CDN 上,资源的地址为: 'https://AAA/BBB/YourProject/XXX',那么可以将生产的 publicPath 配置为: //AAA/BBB/。
        assetModuleFilename: 'image/[contenthash][ext]',  //导出文件的文件名 hash .后缀
        library: {     // 配置支持amd commonJS esMoudle 开发npm包时使用
            name: '[name]',
            type: "umd"
        },
        globalObject: "globalThis" //出来library报错
    },
    mode: isDev ? 'development' : 'production',     // mode 配置项,告知 webpack 使用相应模式的内置优化。
    devtool: isDev ? 'cheap-module-eval-source-map' : 'source-map',
    // devtool 中的一些设置,可以帮助我们将编译后的代码映射回原始源代码。不同的值会明显影响到构建和重新构建的速度。
    //"inline-source-map"
    // 能够定位到源码的行即可,因此,综合构建速度,在开发模式下,设置 devtool 的值是 cheap-module-eval-source-map。
    // 生产环境可以使用 none 或者是 source-map,使用 source-map 最终会单独打包出一个 .map 文件,我们可以根据报错信息和此 map 文件,进行错误解析,定位到源代码。
    // source-map 和 hidden-source-map 都会打包生成单独的 .map 文件,区别在于,source-map 会在打包出的js文件中增加一个引用注释,以便开发工具知道在哪里可以找到它。hidden-source-map 则不会在打包的js中增加引用注释。
    // 但是我们一般不会直接将 .map 文件部署到CDN,因为会直接映射到源码,更希望将.map 文件传到错误解析系统,然后根据上报的错误信息,直接解析到出错的源码位置。



    devServer: {
        static: path.resolve(__dirname, './dist'), //指向服务的物理路径
        port: '8888',     //默认是8080
        compress: true,  //是否启用 gzip 压缩
        hot: true,        //热更新HMR 默认开启
        liveReload: true, //热加载  默认开启
        proxy: {         //网络代理 处理本地请求跨域
            '/api': {
                target: "http://stvideo-test.kaixinkan.com.cn", // 后台接口域名
                ws: true, //如果要代理 websockets,配置这个参数
                secure: true, // 如果是https接口,需要配置这个参数
                changeOrigin: true, //是否跨域
            }
        },
        headers: {  //请求头信息
            "X-Access-Tonke": "abcde"
        },
        historyApiFallback: true  //单页面应用路由history模式  浏览器把资源当静态资源请求导致页面404报错 时使用
        // quiet: false, //默认不启用
        // inline: true, //默认开启 inline 模式,如果设置为false,开启 iframe 模式
        // stats: "errors-only", //终端仅打印 error
        // overlay: false, //默认不启用
        // clientLogLevel: "silent", //日志等级


    },
    // 启用 quiet 后,除了初始启动信息之外的任何内容都不会被打印到控制台。这也意味着来自 webpack 的错误或警告在控制台不可见 ,不建议开启
    // stats: "errors-only" ,终端中仅打印出 error,注意当启用了 quiet 或者是 noInfo 时,此属性不起作用。
    // 启用 overlay 后,当编译出错时,会在浏览器窗口全屏输出错误,默认是关闭的。



    // asset module 资源模块 处理非js文件 引入外部资源  是一种模块类型 
    // 优化:将loader应用于最少数量的应用模块 使用includes和excludes 
    //  每个loader和plugin都有启动时间,尽量少的使用工具

    module: {
        rules: [
            {
                test: /\.js$/,
                use: [{
                    loader: "babel-loader",   // 将js转译为低版本 es6 =>es5
                    options: {
                        presets: [
                            [
                                '@babel/preset-env',  //通过package.json的browserslist来转义浏览器不支持的特性   包含大多数程序转化 很多babel插件都在这里   如polyfill等等一些兼容es3等 低版本的插件
                                {
                                    targets: [    //约定 浏览器的最后1个版本 且 世界上大于 >1的 用户使用的浏览器
                                        "last 1 version",
                                        "> 1%"
                                    ],
                                    useBuiltIns: "usage",  //默认false会将babel-polyfill全局导入
                                    corejs: 3  // 添加依赖使用 v3版本的corejs 处理报错 
                                }
                            ]

                        ],
                        plugins: [
                            [
                                "@babel/plugin-transform-runtime",   // babel插件 ,处理regenertorRuntime报错。 兼容async/await
                                {
                                    "corejs": 3
                                }
                            ]
                        ]
                    },

                },
                    // {
                    //     loader:"thread-loader",  worker-pool worker池 针对比较大的loader 提升打包速度
                    //     options:{
                    //         workers:2
                    //     }
                    // }
                ],
                exclude: /node_modules/
            },
            {
                test: /\.png$/,  //识别出哪些文件
                type: 'asset/resource',
                // 资源模块类型   
                //asset/resource发送单独文件导出url   asset/inline导出资源的Data URL    asset/source 导出资源的源码   asset导出Data URL和发送单独一个文件间单独选择
                generator: {    //同output的assetModuleFilename ,但是比他优先级高
                    filename: 'image/[contenthash][ext]'
                }
            },
            {
                test: /\.png$/,
                type: 'asset',  //asset导出Data URL和发送单独一个文件间单独自动选择  默认临界值小于8k 走inline生成base64  否组导出文件url
                parser: {
                    dataUrlCondition: {  //修改自动选择临界值 由默认8k 修改为 4M
                        maxSize: 4 * 1024
                    }
                }
            },
            {
                test: /\.(css|less)$/,
                // use:["style-loader","css-loader","less-loader"] 
                // 逆序调用  先加载less loade解析less文件 在用css-loader转换成css  最后再运行style-loader 将css文件插入页面header标签中

                use: [
                    MiniCssExtractPlugin.loader,   // 用MiniCssExtractPlugin.loader 替代style-loader 将css合并并成一个 并统一用一个link标签加载
                    {
                        loader: "css-loader",
                        options: {
                            modules: true,     //css模块话 防止类名重复给类名转换为字符串,vue 中css模块使用该思想
                        }
                    },
                    "postcss-loader",       //css兼容浏览器 给css自动加浏览器前缀 需要配合autoprefixer插件使用
                    "less-loader",
                ],
                exclude: /node_modules/

            },
            {
                test: /\.ts$/,
                use: "ts-loader",  //处理ts
                exclude: /node_modules/
            },
            {
                test: /\.(woff|woff2|ttf|otf)$/i,  //处理字体font
                type: "asset/resource"
            }

        ]
    },
    // 加载webpack 插件
    plugins: [
        new HtmlWebpackPlugin({        //自动生成index.html文件
            title: "我是title",
            template: "./index.html",  //打包后使用的模板文件
            filename: "chanel1/app.html", // 打包生成的新文件名
            inject: 'body',        //script标签查入到body中
            chunks: ["mian1", "mian2"],  // 指定打包后 引入的entry模块
            publicPath: 'http://www.a.com',
        }),
        // 通过new多个HtmlWebpackPlugin分包
        new HtmlWebpackPlugin({        //自动生成index.html文件
            title: "我是title",
            template: "./index.html",  //打包后使用的模板文件
            filename: "chanel2/app2.html", // 打包生成的新文件名
            inject: 'body',        //script标签查入到body中
            chunks: ["dependName"],  // 指定打包后 引入的entry模块
            publicPath: 'http://www.b.com',
        }),
        new MiniCssExtractPlugin({  //将所有的css文件抽离合并成一个单独的文件main.css 用一个link标签加载
            filename: "styles/[contenthash].css"    //修改打包后的main.css的文件名
        }),
        new webpack.ProvidePlugin({
            _: "lodash"   //全局引入lodash 不需要再在业务代码中import  可直接使用_
        }),
        // new ModuleFederationPlugin({   // 模块联邦
        //     name:"nav",
        //     filename:"remoteEntry.js",
        //     remotes:{},  //使用 其他人的模块
        //     exposes:{     //暴露模块给其他人使用
        //         "./Header": './src/index.js'  //key为其他人的引用路径,value为本地项目路径
        //     }
        // }),
        // new WorkBoxPlugin.GenerateSW({   离线运行
        //     clientsClaim:true,
        //     skipWaiting:true
        // }),
        // new BundleAnalyzerPlugin(), // 打包分析工具
        require('autoprefixer'),  // 配合postcss-loade使用给css 添加浏览器前缀
        require('postcss-nested') //允许 css 样式嵌套 类似sass less
    ],
    // 减少resolve.modules ,resolve.extensions ,resolve.mainFiles 中条目数量,因为他们会增加文件系统的调用次数
    resolve: {
        extensions: ['.json', '.js', '.vue'], // 设置查找的拓展名的优先级,引用模块出现同名文件时候,调用的文件后缀优先顺序.json>.js>.vue
        alias: {
            "@": path.resolve(__dirname, './src')  //项目中可以用 @ 直接访问src根目录
        }
    },
    externalsType: "script",  // vue-router这种 value 为数组时候使用
    externals: {  //第三方资源库不打包 通过在html用cdn引入 减小打包体积
        "vue": 'Vue',           //key 为页面中from引入的值, value为第三库挂在在window上的对象
        'vue-router': [
            'https://h5.vivo.com.cn/shortVideo/vuerouter/vue-router.min.js',   //这种value为数组写法 可以直接在html中生成 script链接,需配合 externalsType:"script" 告知已script标签插入
            'VueRouter'
        ],
        'axios': 'axios',
        'vuex': 'Vuex',
        'lottie-web': 'lottie',
        'better-scroll': 'BetterScroll',
        'swiper/swiper-bundle.min.js': 'Swiper'
    },
    // 用于优化打包结果的对象。
    optimization: {
        minimizer: [
            new CssMinimizerPlugin(), //压缩打包后的css
            new TerserWebpackPlugin()  //压缩代码


        ],
        usedExports: true, //开启 Tree Shaking 功能,用于删除未被使用的代码    package.json中设置sideEffects: true或指定文件sideEffects:  ["*.css"]文件   智能删除
        splitChunks: {   //去重和分离公共代码  
            // async表示只从异步加载得模块(动态加载import())里面进行拆分
            // initial表示只从入口模块进行拆分
            // all表示以上两者都包括
            //  chunks:"all"
            cacheGroups: {   //缓存组 ,缓存一些不经常修改的第三方库
                vendor: {
                    test: /[//\node_modules[//\]]/,   //第三方组件库 都在node_modules里面
                    name: "vendor",
                    chunks: "all"
                }

            }
        }
    },
    //性能方面的一些配置
    performance: {
        hints: 'warning',
        //入口起点的最大体积
        maxEntrypointSize: 50000000,
        //生成文件的最大体积
        maxAssetSize: 30000000,
        //只给出 js 文件的性能提示
        assetFilter: function (assetFilename) {
            return assetFilename.endsWith('.js');
        }
    }
}