webpack-Learning

127 阅读1分钟

webpack配置:

const path = require('path')   // 用来找到文件夹路径
const HtmlWebpackPlugin = require(`html-webpack-plugin`)  // 用来设置打包的html文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 引入css从style引入 变成 link引入。
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') // 优化打包css插件,对css进行压缩
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
    entry: './src/index.js',  // 入口文件
    output:{                  // 输出文件
        filename: 'bundle.js',
        path: path.resolve(__dirname,'./dist'),
        clean: true,          // 每次打包后,删除上次的文件
        assetModuleFilename: 'images/[contenthash][ext]', // 设置资源模块的文件夹和文件名
        publicPath: 'http://localhost:8080/', //配置公共路径
    },
    mode: 'production',     // 编译模式
    devtool: 'inline-source-map',  // 代码报错时,会在浏览器控制台显示多少代码多少行错误
    plugins: [
        new HtmlWebpackPlugin({   // 打包html文件,会自动在html文件中引入打包好后的js
            template: './index.html', // 要打包的文件
            filename: 'app.html',
            inject: 'body'    // script引入标签的地址
        }),
         new MiniCssExtractPlugin({
            filename: 'styles/[contenthash].css'  // 打包css文件设置目录和文件名
        })
    ],
    devServer:{
        static: './dist'  // 启动服务器
    },
    module: {    // 配置模块
        rules: [    // 文件解析规则
            {
                test: /\.png$/,  // 以png结尾的文件
                type: 'asset/resource',   // 获取文件的url地址
                generator: {
                    filename: 'images/[contenthash][ext]'  // 打包好后的文件路径和名字,contenthash-- 文件名会自动编译, ext -- 会打包对应的图片名字,优先级会比output高
                }
            },
            {
                test: /\.svg$/,  // 以svg结尾
                type: 'asset/inline' // 导出一个资源的data URI,不会打包出文件,64位字符
            },
            {
                test: /\.text$/,
                type: 'asset/source' // 导出资源的源代码,不会打包出文件
            },
            {
                 test: /\.jpg$/,
                 type: 'asset'  // 文件大小大于8k时生成'asset/resource', 小于8k时使用 'asset/inline'
                 parser: {
                     dataUrlCondition: {      // 转换成64位
                         maxSize: 4 * 1024 * 1024  // 表示超过4M为
                     }
                 }
            },
            {
               test: /\.(css|less)$/,
               use: ['MiniCssExtractPlugin.loader', 'css-loader', 'less-loader'] // style-loader会将css放到header标签里。注意顺序,webpack支持链式调用,顺序从后往前解析,
            },
            {
                test: /\.(csv|tsv)$/,   // 会将数据转换成数组
                use: 'csv-loader'
            },
            {
                test: /\.xml$/,       // 会将数据转换成对象
                use: 'xml-loader'
            },
                        {
                test: /\.toml$/,
                type: 'json',
                parser: {
                    parse: toml.parse
                }
            },
            {
                test: /\.yaml$/,
                type: 'json',
                parser: {
                    parse: yaml.parse
                }
            },
            {
                test: /\.json5$/,
                type: 'json',
                parser: {
                    parse: json5.parse
                }
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader', //   webpack里babel解析es6的桥梁
                    options: {
                        presets: ['@babel/preset-env'], // babel的预设,一组插件的集合
                        plugins: [
                            [
                                '@babel/plugin-transform-runtime' // @babel/preset-env
                            ]
                        ]
                    }
                }
            }
        ]                                                    
    },
     optimization: {  // 优化项
        minimizer: [
            new CssMinimizerPlugin(),  // 优化打包css插件,减少文件打包体积,使用插件需要将模式改成生产模式
            new TerserPlugin() //将生产环境代码进行压缩
        ],
        splitChunks: { 
                cacheGroups: {   // 缓存第三方库
                    vendor: {
                        test: /[\\/]node_modules[\\/]/,
                        name: 'vendors',
                        chunks: 'all'
                    }
                }
        }
    },
    performance: {
        hints: false  // 去掉打包文件体积性能提示
    }
}

一些插件:

npm i webpack-dev-server -D  // 用来热更新,启动后npm webpack-dev-server,进入对应打包好后文件夹,会热更新
npm i css-loader -D   // 解析引入css
npm i style-loader -D  // 让css渲染到界面
npm i less-loader less -D 
npm i mini-css-extract-plugin -D    // webpack5环境才有,合并style标签代码,生成一个css文件,变成link标签引入
npm i css-minimizer-webpack-plugin -D // 优化css打包插件,对css进行压缩
npm i csv-loader xml-loader -D // 解析csv、xml的loader
npm i toml yaml json5 -D   // 解析toml、yaml、json文件
npm i babel-loader @babel/core @babel/preset-env -D 
        // babel-loader       webpack里babel解析es6的桥梁
        // @babel/core        babel的核心模块
        // @babel/preset-env  babel的预设,一组插件的集合
regeneratorRuntime  // 
npm i @babel/runtime -D   // regenerator运行时需要的内容
npm i @babel/plugin-transform-runtime -D // 会在需要regenerator时候自动导包
npm install terser-webpack-plugin -D // webpack开箱即用的打包压缩插件,但在optimization中配置了minimizer:  new CssMinimizerPlugin() 插件时候,terser这个插件就会失效,因此需要重新下载。
npm i webpack-merge -D // 合并webpack配置文件,

一些命令:

npm webpack --watch  // 主动修改代码后,自动重新打包
npx webpack-dev-server --open 
npx webpack --env production // 编译在生产环境
npx webpack -c ./config/webpack.config.dev.js // 指定webpack调用打包配置文件
npx webpack serve -c ./config/webpack.config.dev.js // 指定打包配置文件启动。

打包时代码分离方法:

  1. 入口起点:使用entry配置手动分离代码。缺点:公共代码会分别在每个包里边去重复打包。
  2. 防止重复:使用Entry dependencies 或者 SplitChunksPlugin 去重和分离代码。
  3. 动态导入:通过模块的内联函数调用来分离代码。

入口起点配置:

entry: {index: './src/index.js',another: './src/another-module.js'},
output:{
        filename: '[name].bundle.js', // 会更具文件名自动生成打包好的文件
        path: path.resolve(__dirname,'./dist'),
        clean: true,
        assetModuleFilename: 'images/[contenthash][ext]'
 },

防止重复: 这样配置公共代码会重复打包,入口文件可以修改成下面代码解决问题: 第一种方式:

entry: {
        index: {
            import: './src/index.js',
            dependOn: 'shared' 
        },
        another: {
            import: './src/another-module.js',
            dependOn: 'shared'
        },
        shared: 'lodash'  // 将的lodash提成公共的
    },

第二种方式:在optimization中使用splitChunks(webpack内置),对公共文件进行抽离成单独的chunks打包。

 entry: {index: './src/index.js',another: './src/another-module.js'},
 optimization: { 
         splitChunks: {
            chunks: 'all'
        }
 }

动态导入:动态导入公共文件会自动分离,但是如果动态导入和静态导入一起时会失效,需要splitChunks进行分离。

动态加载文件

function getComponent(){
   return import('lodash').then(({default: _})=>{
        const ele = document.createElement('div')
        ele.innerHTML= _.join(['Hello','webpack'],' ')
        return ele
    })
}
getComponent().then((ele)=>{
    document.body.appendChild(ele)
})
import './async-module'

懒加载文件: 在js文件中通过 点击事件 实现懒加载。

const button = document.createElement('button')
button.textContent = '点击执行加法'
button.addEventListener('click',()=>{
    import(/* webpackChunkName: 'math' */'./math').then(({add})=>{ // 可以使用魔法注释修改打包后的文件名
        console.log(add(4,5));
    })
})
document.body.appendChild(button)

预获取、预加载模块

webpack在4.6.0+增加了对预获取和预加载的支持。 在声明import时,使用下面这些内置指令,可以让webpack输出“resoure hint(资源提示)”,来告知浏览器:

  • prefetch(预获取):将来某些导航下可能需要的资源
  • preload (预加载): 当前导航下可能需要的资源

prefetch: 魔法注释里 webpackPrefetch: true ,浏览器会通过link标签prefetch形式,网络空闲时候去加载打包文件

const button = document.createElement('button')
button.textContent = '点击执行加法'
button.addEventListener('click',()=>{
    import(/* webpackChunkName: 'math', webpackPrefetch: true */'./math').then(({add})=>{
        console.log(add(4,5));
    })
})
document.body.appendChild(button)

preload: 魔法注释里 webpackPreload: true ,和上面点击懒加载类似

const button = document.createElement('button')
button.textContent = '点击执行加法'
button.addEventListener('click',()=>{
    import(/* webpackChunkName: 'math', webpackPreload: true */'./math').then(({add})=>{
        console.log(add(4,5));
    })
})
document.body.appendChild(button)

prefetch和preload区别:

  • preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
  • preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
  • preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。

输出文件名并将js放在一个文件夹:

部署时候,浏览器会在文件名没更新时会使用缓存版本,因此部署时候需要对输出文件名进行更改。

 output:{
        filename: 'scripts/[name].[contenthash].js', // contenthash会根据内容动态生成hash文件名
        path: path.resolve(__dirname,'./dist'), 
        clean: true,
        assetModuleFilename: 'images/[contenthash][ext]'
    },

缓存第三方库

将第三方库(library)提取到单独的vendor chunk文件中,他们很少像本地的源代码频繁修改,因此通过实现以上步骤,client的长效缓存机制,浏览器命中缓存来消除请求,并减少向server获取资源,同时还能保证client代码和server代码版本一致(将代码打包缓存到浏览器里,这样只有我们改变的代码会更新,第三方库始终使用浏览器缓存的内容)

 optimization: {
        minimizer: [
            new CssMinimizerPlugin()
        ],
        splitChunks: {
            cacheGroups: {
                vendor:{
                    test: /[\\/]node_modules[\\/]/,
                    name: 'vendors',
                    chunks: 'all'
                }
            }
        }
    }

环境变量

暴露方式变成函数,传入形参env,用户在使用下面打包命令时候,就可以识别打包环境

npx webpack --env production // 编译在生产环境运行
npx webpack --env production --env goal=local // 编译在生产环境运行,并切携带参数{goal:local}
module.exports = (env) => {
    console.log(env.goal);
    return {
          mode: env.production ? 'production' : 'development', // 可以根据用户输入命令来识别打包环境
    }
}

生产环境和开发环境配置分离,提取公共环境配置

webpack配置文件分离后,注意需要把output中打包路径修改一下,提取公共环境配置文件时候需要使用merge插件

const path = require('path')
const HtmlWebpackPlugin = require(`html-webpack-plugin`)
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
const toml = require('toml')
const yaml = require('yaml')
const json5 = require('json5')

module.exports = {
    entry: {
        index: './src/index.js',
        another: './src/another-module.js'
    },
    output: {
        filename: 'scripts/[name].[contenthash].js',
        path: path.resolve(__dirname, '../dist'),
        clean: true,
        assetModuleFilename: 'images/[contenthash][ext]',
        publicPath: 'http://localhost:8080/'
    },
    devtool: 'inline-source-map',
    plugins: [
        new HtmlWebpackPlugin({
            template: './index.html',
            filename: 'app.html',
            inject: 'body'
        }),
        new MiniCssExtractPlugin({
            filename: 'styles/[contenthash].css'
        })
    ],
    devServer: {
        static: './dist'
    },
    module: {
        rules: [
            {
                test: /\.png$/,
                type: 'asset/resource',
                generator: {
                    filename: 'images/[contenthash][ext]'
                }
            },
            {
                test: /\.svg$/,
                type: 'asset/inline'
            },
            {
                test: /\.(css|less)$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
            },
            {
                test: /\.(woff|woff2|eot|ttf|otf)$/,
                type: 'asset/resource'
            },
            {
                test: /\.(csv|tsv)$/,
                use: 'csv-loader'
            },
            {
                test: /\.xml$/,
                use: 'xml-loader'
            },
            {
                test: /\.toml$/,
                type: 'json',
                parser: {
                    parse: toml.parse
                }
            },
            {
                test: /\.yaml$/,
                type: 'json',
                parser: {
                    parse: yaml.parse
                }
            },
            {
                test: /\.json5$/,
                type: 'json',
                parser: {
                    parse: json5.parse
                }
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env'],
                        plugins: [
                            [
                                '@babel/plugin-transform-runtime'
                            ]
                        ]
                    }
                }
            }
        ]
    },
    optimization: {
        minimizer: [
            new CssMinimizerPlugin(),
            new TerserPlugin()
        ],
        splitChunks: {
            cacheGroups: {
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name: 'vendors',
                    chunks: 'all'
                }
            }
        }
    }
}