webpack 5

webpack@4与webpack@的区别

1.在webpack中5个重要的元素

  • entry
  • ouput
  • mode
  • loader
  • plugin

2.所有东西都要导出,通过

module.exports = {
  entry: './src/index.js', // 设置入口文件
  // 设置打包出口
  output: { 
    filename: 'dist.js',
    path: path.join(__dirname, '/dist') // 出口路径要求必须是绝对路径
  },
  module:{
    rules: [
      // loader
    ]
  },
  plugins: [],
  mode: '' 
}

3.图片的打包

  • 在webpack@4中

通过:url-loader file-loader html-loader

 rules: [
          ...,
          {
            test: /\.(gif|jpg|jpeg|png|webp)/,
            use: [
              {
                loader: 'url-loader',
                options: {
                  limit: 1024 * 100,
                  name: 'images/[hash].[ext]',
                  esModule: false
                }
              }
            ]
          }
        ]
  • 在webpack@5中

通过type='‘accect’

type 参数详解

  • asset/resource 发送一个单独的文件并导出 URL
  • asset/inline 导出一个资源的 data URI
  • asset/source 导出资源的源代码
  • asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现
{
  test: /\.(png|jpe?g|gif|webp|svg)$/,
  type: 'asset', 
  parser: {
    dataUrlCondition: {
      maxSize: 4 * 1024 // 4kb 以下打包base64
    }
   }
}

资源目录的自定义

// 未修改  
output: {
    filename: 'dist.js',
    path: path.join(__dirname, '/dist'),
    clean: true,
  },

// 修改后
    output: {
      filename: 'js/dist.js', // js入口文件的输出目录修改
      path: path.join(__dirname, '/dist'),
      clean: true,
      assetModuleFilename: 'assets/[hash:8][ext]' // 资源目录整体修改
    },

rules配置中增加 generator 配置项

{
  test: /\.(png|jpe?g|gif|webp|svg)$/,
  type: 'asset', // 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现。
  parser: { // 设置解析规则(主要是图片资源的base64的转化规则)
    dataUrlCondition: {
      maxSize: 4 * 1024 // 4kb 以下打包base64
    }
  },
  generator: { // 配置图片资源的输出目录
    filename: 'static/images/[hash:8][ext]'
  }
}

2.3 说明:两种修改方式同时出现时,以generator 为准

html文件中引入(html-loader处理) ,这样就能直接在html模板文件引入图片或者css

{
  test: /\.html$/i,
  loader: "html-loader",
},

3.加载css资源(loader的使用)

4… 单独提取css文件(需要配合 html-webpack-plugin 来使用) ,也就是单独打包css文件在html里面用

  • 安装插件

  • npm install --save-dev mini-css-extract-plugin
    
  • 使用(替代原本的style-loader)@4里面也是这个插件

  • const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    
    module.exports = {
      // 插件中引入
      plugins: [new MiniCssExtractPlugin({
        filename: 'static/css/main.css' // 指定输出目录
      })],
      module: {
        rules: [
          {
            test: /\.css$/i,
            // 在css-loader之后调用 MiniCssExtractPlugin.loader
            use: [MiniCssExtractPlugin.loader, "css-loader"],
          },
        ],
      },
    };
    

5.css 样式的兼容性处理

修改配置项(需要在css-loader之前处理兼容性)

{
  loader: 'postcss-loader',
    options: {
      postcssOptions: {
        plugins: [
          'postcss-preset-env', // 可以解决大多数样式兼容性问题
        ]
      }
    }
          }

创建.browserslistrc文件(取约束条件的交集)

last 20 version // 支持浏览器最近的哪些版本
> 5% // 支持市面上使用占比大于这个数值的浏览器
  1. css压缩
  • 依赖下载 在@4中是 optimize-css-assets-webpack-plugin

  • npm install css-minimizer-webpack-plugin --save-dev
    
    • 配置插件,方法1
  • const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
    optimization: {
      minimizer: [
        // 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
        new CssMinimizerPlugin(),
      ],
    },
    
    • 配置插件,方法2

    • const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
      
      ...
      plugins: [new CssMinimizerPlugin()],
      

    7.html-webpack-plugin

    ​ 作用

    • 基于模板html文件生成一个html文件,自动引入webpack打包生成的js文件
    • . 引入和配置
    // webpack.config.js
    // 引入
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    // 使用
    new HtmlWebpackPlugin({
      template: path.join(__dirname, './index.html'), // 模板文件目录(相对路径也可以)
      filename: 'webpack.html', // 生成的html文件的名字
      inject: 'body' // webpack打包生成的js文件插入的位置(默认插入head标签)
    })
    

8.webpack-dev-server

帮助我们在开发时,自动检测代码的变化重新打包文件,打包的文件放入内存中反应更迅速

配置( )

devServer: {
  static: './dist' // 服务器根目录地址
     port:7000
      host:'0.0.0.0'//host设置的是服务器的主机号
    publicPath: "/assets/
      proxy: {
    '/proxy': {
        target: 'http://your_api_server.com',
        changeOrigin: true,
        pathRewrite: {
            '^/proxy': ''
        }
    }
}
  • webpack-dev-server支持热模块更新,相较于(开启本地服务(live-server)+ --watch监听文件变化重新打包(webpack -w))的模式虽然表现上没有区别,但效率更高,打包的文件放入内存中反应更迅速

9.js文件的处理

eslint格式化js文件

  • 创建eslint配置文件(三种方法都可以)

  • .eslintrc

  • .eslintrc.js(推荐,注释方便,符合js习惯)

  • .eslintrc.json

  • 基础的配置文件

  • module.exports = {
      // 解析器选项
      parserOptions: {
        ecmaVersion: 6, //支持的es语法版本
        sourceType: 'module', // es模块化
        ecmaFeatures: { // 支持的其他特性
          jsx: true, // 如果是react项目打开这个
        }
      },
      // eslint.bootcss.com/docs/rules/
      rules: {
        // key: value
        /**
         * key: 规则名
         * value: 规则的控制
         *  off/0 关闭该规则
         *  warn/1 警告级别
         *  error/2 错误级别
         */
        'no-var': 2,
        'no-unused-vars': 0
      },
      // 继承规则,比如: eslint 官方推荐规则
      extends: ['eslint:recommended'],
      env: {
        node: true, // node的全局内置api变量可用
        browser: true // bom模型的内置api变量可用
        es6: true // es6 新特性,比如:promise
      }
    }
    
  • 安装和使用插件(之前的版本使用 loader 处理)

  • npm install eslint-webpack-plugin eslint --save-dev
    
  • 创建.eslintignore文件

    • vscode 的 eslint 插件也会扫描eslint配置文件,但它无法读取我们在webpack.config.js中设置的扫描范围,所以我们需要创建.eslintignore文件

    • // .eslintignore 文件
      dist
      

9.2babel 处理js兼容性

  • .babelrc

  • .babelrc.js( 推荐 )

  • .babelrc.json

  • module.exports = {
      // 预设
      // @babel/preset-env: 一个智能预设,允许使用最新的javascript语法(比如箭头函数, ...)
      presets: ['@babel/preset-env'],
    }
    
  • 安装依赖

  • npm install -D babel-loader @babel/core @babel/preset-env
    
  • 使用

  • {
      test: /\.js$/,
      exclude: /(node_modules)/, // 设置哪些目录不需要扫描
      use: [{
        loader: 'babel-loader?cacheDirectory', //开启缓存,可以设置缓存目录
      },
      ...]
    },
    
  • babel存在的一些问题:

    • Babel 在每个文件都插入了辅助代码,使代码体积过大!

      • 解决方案:

      • 安装依赖

      • npm install -D @babel/plugin-transform-runtime
        npm install @babel/runtime
        
      • module.exports = {
          // 禁用 Babel 自动对每个文件的 runtime 注入,改为引入 @babel/plugin-transform-runtime 并且使所有辅助代码从这里引用。
          plugins: ['@babel/plugin-transform-runtime']
        }
        
      • babel-loader 很慢!

        • 开启缓存: 'babel-loader?cacheDirectory'
        • 排除不需要检测的目录: exclude: /(node_modules)/, // 设置哪些目录不需要扫描

      9.3 使用 core-js 解决 babel 无法解决的兼容性问题

      • babel 的 preset-env 可以解决一些 es6 语法的兼容性问题(箭头函数、…语法等),但比如 async、promise、Array.includes()等preset-env是处理不了的,所以我们要用到 core-js来处理 es6以及 es6+的 polyfill

      • 安装依赖

      • npm install core-js
        
      • 使用方式 1: 完全引入

      • import 'core-js'
        
      • 自动按需引入,修改 babel 的 preset-env 设置( 不需要再按照方式1 引入 core-js了 ),在.babelrc文件下写的

      • module.exports = {
          // 预设
          // @babel/preset-env: 一个智能预设,允许使用最新的javascript语法(比如箭头函数, ...)
          presets: [
            // 预设的配置是需要使用数组实现的,配置对象作为数组的第二个元素出现
            ['@babel/preset-env', {
              useBuiltIns: 'usage', // core-js 按需引入
              corejs: 3 // core-js 版本
            }]
          ],
        }
        

​ 9.4 js代码的压缩

  • 在webpack5中

    • 自动对js(terser)和html文件进行压缩,terser-webpack-plugin
    • 自动开启 tree shaking
  • 在webpack@4中

    • uglifyjs-webpack-plugin 用这个插件

webpack优化

source-map的设置(错误提示)

  • devtool

1.1 开发模式使用:cheap-module-source-map

@eval-source-map(精确到开发环境的行)

  • 打包编译速度更快,只映射到行,

1.2 生产环境使用:source-map/

@4 nosource-source-map (是为了报错的时候并不显示源码,只显示报错的哪一行)

  • 因为生产环境的代码会压缩,所以必须映射到行和列的信息

2. HMR(只能在开发环境下使用)

  • webpack的默认行为会在一个模块发生变化的时候把所有模块都重新打包
  • 开启HMR功能后,只重新打包变化的模块,其它模块使用缓存
  • 样式文件天然支持这个功能(devserver 的hot配置默认为true)
  • js的支持:(vue-loader和react-loader都默认支持了这样的功能,不需要我们单独实现)

3. oneOf

  • webpack的默认行为是当一个类型的检查是否使用某一个loader进行处理时,即使匹配到了也会在接下来的所有规则中进行再次匹配(走完所有的rules规则匹配)
  • 使用oneOf解决上面的问题
rules: [
  {
    oneOf:[
      ...rules
    ]
  }
]

4. 所有文件处理范围(include和exclude)

  • 两个配置只能同时是用一个,可以设置包含(include)或者设置排除(exclude)

  • 一般对js处理(babel和eslint比较耗时),因为代码量比较大

  • // loader 的配置
    {
      test: /\.js$/,
      exclude: /(node_modules)/, // 使用这种
      include: path.join(__dirname, 'src'), // 或者使用这种 
      use: [...]
    }
    // 插件的配置
    new ESLintPlugin({
      context: path.join(__dirname, 'src'), // 配置eslint检测范围
      exclude: 'node_modules' // 这是一个默认值
    }),
    

5. webpack缓存的使用

  • 前面提到js文件的编译需要进行babel和eslint的处理,比较耗时,所有这里说的缓存也只针对这两个过程

  • bebel-loader 配置

  • {
      loader: 'babel-loader?',
      options: {
        cacheDirectory: true, // 开启babel缓存
        cacheCompression: false // 关闭缓存文件压缩(耗时无意义,线上用不到)
      }
    }
    
  • eslint 配置

  • new ESLintPlugin({
      context: path.join(__dirname, 'src'), // 配置eslint检测范围
      exclude: 'node_modules', // 这是一个默认值
      cache: true, // 开启缓存
      cacheLocation: path.resolve(__dirname, './node_modules/.cache/eslint') // 设置缓存目录
    }),
    

6. 多进程打包(大数据量的项目才去使用)

  • 注意: 把注意写在前面,每个进程打开大概需要 600ms 的时间,建议在文件量很大的情况下使用

  • 一样是针对最耗时的任务 js 处理 babel 和 eslint 检查使用

  • 安装依赖(用于babel编译开启多进程)

  • npm install thread-loader -D
    
  • 获取 cpu 核心数量

  • const os = require('os')
    const threads = os.cpus().length
    
  • 增加多进程配置添加到 babel-loader 前面

  • {
      test: /\.js$/,
      exclude: /(node_modules)/, // 设置哪些目录不需要扫描
      use: [
        {
          loader: 'thread-loader', // 开启多线程处理 babel
          options: {
            works: threads // 设置线程数量
          }
        },
        {
          loader: 'babel-loader?cacheDirectory', //开启缓存,可以设置缓存目录
        }
      ]
    }
    
  • 引入 webpack5 内置压缩 js 代码的插件

  • const TerserWebpackPlugin = require('terser-webpack-plugin')
    
  • js代码压缩开启多进程

  • plugins: [
      new TerserWebpackPlugin({
        parallel: threads // js 代码压缩开启多进程,threads:cpu 核心数量
      })
    ]
    
    // 或者放入optimization中,和之前的 css 压缩一样
    optimization: {
      minimizer: [
        new CssMinimizerPlugin(),
        new TerserWebpackPlugin({
          parallel: threads // js 代码压缩开启多进程,threads:cpu 核心数量
        })
      ]
    }
    
  • eslint 开启多进程

  • new ESLintPlugin({
      context: path.join(__dirname, 'src'), // 配置eslint检测范围
      exclude: 'node_modules', // 这是一个默认值
      cache: true, // 开启缓存
      cacheLocation: path.resolve(__dirname, './node_modules/.cache/eslint') // 设置缓存目录
    + threads, // +++ eslint 开启多进程
    }),
    

7. tree-shaking

  • webpack 内置了这个功能,生产模式下自动打开
  • tree shaking 依赖于 esm实现,commonjs 语法不支持

8. 图片压缩

  • 依赖安装1

  • npm install image-minimizer-webpack-plugin -D
    npm install @squoosh/lib --save-dev
    
  • 有损压缩:

  • // 引入依赖
    const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
    // 使用插件(放入plugins)
    new ImageMinimizerPlugin({
      minimizer: {
        implementation: ImageMinimizerPlugin.squooshMinify,
        options: {
          // Your options for `squoosh`
        },
      },
    }),
    // 再说一次还可以在 optimization 中使用
    optimization: {
        minimizer: [
          new ImageMinimizerPlugin({
            minimizer: {
              implementation: ImageMinimizerPlugin.squooshMinify,
              options: {
                // Your options for `squoosh`
              },
            },
          }),
        ],
      },
    
    
  • 无损压缩

  • optimization: {
        minimizer: [
          new ImageMinimizerPlugin({
            minimizer: {
              implementation: ImageMinimizerPlugin.squooshMinify,
              options: {
                encodeOptions: {
                  mozjpeg: {
                    // That setting might be close to lossless, but it’s not guaranteed
                    // https://github.com/GoogleChromeLabs/squoosh/issues/85
                    quality: 100,
                  },
                  webp: {
                    lossless: 1,
                  },
                  avif: {              
               // https://github.com/GoogleChromeLabs/squoosh/blob/dev/codecs/avif/enc/README.md
                    cqLevel: 0,
                  },
                },
              },
            },
          }),
        ],
      },
    

splitchunk

  1. 多入口的配置文件

    module.exports = {
      // entry: './src/main.js', // 单入口
      // 多入口
      entry: { 
        app: './src/app.js',
        main: './src/main.js'
      },
      output: {
        path: path.join(__dirname, 'dist'),
        filename: [name].js
      }
    }
    
    • 多入口存在的问题:
      • 如果入口 chunk 之间包含一些重复的模块,那些重复模块都会被引入到各个 bundle 中

    2. splitchunks

    • 多个入口引用相同模块的 splitchunk 配置
    optimization: {
        splitChunks: {
          chunks: 'all', // 设置哪些文件会被检测
          cacheGroups: {
            // defaultVendors: {}, 这里主要是给 node_modules 里面第三方依赖合使用,通常使用默认配置就好
            default: { // 这个配置项给我们自己写的模块代码使用
              minSize: 0, // 分割代码最小值(实际开发的时候我们就使用默认值(20000)就好了,为了演示改为 0)
              // 下面的都是默认值(用就好)
              minChunks: 2, // 最少被引用次数大于等于这个数值才会合并
              priority: -20, // 权重(设置优先级,权重越大优先级越高)
              reuseExistingChunk: true // 重用从主文件中分离出来的模块
            },
              
            // 这种自定义的选项主要用于当项目过大,导致defaultVendors的包太大需要拆分的时候使用
            anyname: {
              test: /[\\/]node_modules[\\/]react(.*)?[\\/]/, // 通过正则匹配要单独打包的第三方依赖包
              name: 'chunk-any', // 单独打包的第三方依赖
              priority: 10, // 权重要大于 node_modules的默认权重
            }
          }
        }
      }
    
  • splitchunk 结合 import 动态导入实现按需加载
  • webpackChunkName: '[chunkname]' 可以设置动态打包的包名
  • 之前还需要配置 output.chunkFilename: '[name].js'(加上肯定不会出错)
const button = document.createElement('button')
button.innerHTML = 'click'
button.addEventListener('click', () => {
  // import 动态导入的文件会被 splitchunk 代码分割成单独的模块打包,在 import 调用的时候引入
  import(/* webpackChunkName: '[chunkname]' */ './js/sum').then((res) => {
    console.log(res)
  }).catch()
})

document.body.appendChild(button)
  • eslint 如果动态导入报错,在 eslintrc 中添加如下代码
plugins: ['import']

3. 打包输出文件命名规则整理

// 添加 contenthash 当内容发生变化的时候加载新的资源
output: {
    filename: '[name].[contenthash:8]js', // 正常 js 打包文件
    chunkFilename: '[name].chunk.[contenthash:8].js', // splitchunk 单独打包的文件增加一个.chunk标识
    assetModuleFilename: 'static/[hash:8][ext]', // 通过 'type: asset' 这种方式打包的资源文件
    path: path.join(__dirname, 'dist'),
    clean: true,
},
  
plugins: [new MiniCssExtractPlugin({
    filename: 'static/css/[name].[contenthash:8].css', // 指定输出目录
  	// 多次引入同一个 css 文件也可能单独打包 css 文件
  	chunkFilename: 'static/css/[name].chunk.[contenthash:8].css', // splitchunk 单独打包的文件增加一个.chunk标识
})],

4. preload 和 prefetch 结合 splitchunk

  • 当我们想利用浏览器空闲时间,加载后续使用的资源,我们就会用到 preload 和 prefetch(偷偷加载未来要用到的资源),比如我们的按需加载的使用场景(点击按钮触发的按需引入早就下载好了等待使用多么美好),通过埋点决定先显示什么

  • 共同点:

    • 加载未来要用的资源

    • 只加载不执行

    • 加载完毕后缓存起来等待调用

    • 兼容性有一点问题(preload: 92%(ie 完全不支持), prefetch: 79%)

  • 不同点:

    • preload 相较于 prefetch 有更高的优先级
    • preload 只能加载当前页面要用的资源(怎么理解:页面跳转缓存的资源就失效了),prefetch 可以加载当前以及后续页面要用到的资源
    • preload 会立刻下载,prefetch 会在浏览器空闲时下载
  • 如何使用:

    • 安装依赖:

    • npm install --save-dev @vue/preload-webpack-plugin
      
    • 引入

    • const PreloadWebpackPlugin = require('@vue/preload-webpack-plugin');
      
    • 配置插件(它是结合 htmlwebpackplugin 使用的)

    • plugins: [
        new HtmlWebpackPlugin(), // 它是结合 htmlwebpackplugin 使用的
        new PreloadWebpackPlugin({
          rel: 'preload',
          as: 'script'
        })
      ]
      

5. runtimeChunk解决重复打包的问题

  • 当 a 文件依赖一个单独打包的 b 文件时,如果 b 文件发生变化,重新打包的时候发现 a 文件也重新打包了,这是因为 a 直接引用了 b 打包后的文件(它的文件名包含了 hash 值),而它的 hash 值发生了变化,导致了 a 文件的缓存失效,重新打包,为了解决这个问题,引入一个 runtime 的概念, runtime类型一个对象,key 表示模块(比如:b),对应的value值存储 b 文件打包后的文件名(比如:b.chunk.490905b5.js),所以当 b 文件变化,重新打包时,只有 b 文件和 runtime 对象发生变化,不会导致 a 文件缓存失效,这在大型项目的负载文件依赖关系种非常有效

  • 实现

  • optimization: {
        runtimeChunk: {
          name: (entry) => `runtime-${entry.name}`
        }
    }
    

    resolve

  • webpack 解析模块的时候用到的选项

  • resolve: {
      // 用于自动补全文件扩展名
      extensions: ['.js', '.vue', '.json']// 配置别名
      alias: {
        @: path.join(__dirname, 'src'),
          
      }
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值