webpack

140 阅读4分钟

loader

Loader 就是将 Webpack 不认识的内容转化为认识的内容

webpack 默认支持处理 JS 与 JSON 文件,其他类型都处理不了,这里必须借助 Loader 来对不同类型的文件的进行处理。

plugin

插件(Plugin)可以贯穿 Webpack 打包的生命周期,执行不同的任务

w问题一使用 postcss-loader,自动添加 CSS3 部分属性的浏览器前缀,但是单独使用会报错需要创建 postcss 配置文件 postcss.config.js

// postcss.config.js
module.exports = {
  plugins: [require('postcss-preset-env')]
}

创建 postcss-preset-env 配置文件 .browserslistrc

# 换行相当于 and
last 2 versions # 回退两个浏览器版本
> 0.5% # 全球超过0.5%人使用的浏览器,可以通过 caniuse.com 查看不同浏览器不同版本占有率
IE 10 # 兼容IE 10

还可以使用mini-css-extract-plugin这样就不用使用style-loader进而替换成MiniCssExtractPlugin.loader,并添加plugins

// ...
// 引入插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')


const config = {
  // ...
  module: { 
    rules: [
      // ...
      {
        test: /\.(s[ac]|c)ss$/i, //匹配所有的 sass/scss/css 文件
        use: [
          // 'style-loader',
          MiniCssExtractPlugin.loader, // 添加 loader
          'css-loader',
          'postcss-loader',
          'sass-loader', 
        ] 
      },
    ]
  },
  // ...
  plugins:[ // 配置插件
    // ...
    new MiniCssExtractPlugin({ // 添加插件
      filename: '[name].[hash:8].css'
    }),
    // ...
  ]
}

// ...

关于图片 webpack4是用 file-loader 和 url-loader

但是在 webpack5,内置了资源处理模块,file-loaderurl-loader 都可以不用安装

1.14 资源模块的使用

webpack5 新增资源模块(asset module),允许使用资源文件(字体,图标等)而无需配置额外的 loader。

资源模块支持以下四个配置:

  1. asset/resource 将资源分割为单独的文件,并导出 url,类似之前的 file-loader 的功能.
  2. asset/inline 将资源导出为 dataUrl 的形式,类似之前的 url-loader 的小于 limit 参数时功能.
  3. asset/source 将资源导出为源码(source code). 类似的 raw-loader 功能.
  4. asset 会根据文件大小来选择使用哪种类型,当文件小于 8 KB(默认) 的时候会使用 asset/inline,否则会使用 asset/resource

贴一下修改后的完整代码

js
复制代码
// ./src/index.js

const config = {
  // ...
  module: { 
    rules: [
      // ... 
      {
        test: /.(jpe?g|png|gif)$/i,
        type: 'asset',
        generator: {
          // 输出文件位置以及文件名
          // [ext] 自带 "." 这个与 url-loader 配置不同
          filename: "[name][hash:8][ext]"
        },
        parser: {
          dataUrlCondition: {
            maxSize: 50 * 1024 //超过50kb不转 base64
          }
        }
      },
      {
        test: /.(woff2?|eot|ttf|otf)(?.*)?$/i,
        type: 'asset',
        generator: {
          // 输出文件位置以及文件名
          filename: "[name][hash:8][ext]"
        },
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024 // 超过100kb不转 base64
          }
        }
      },
    ]
  },
  // ...
}

module.exports = (env, argv) => {
  console.log('argv.mode=',argv.mode) // 打印 mode(模式) 值
  // 这里可以通过不同的模式修改 config 配置
  return config;
}

执行打包,结果和之前一样

优化

1.alias 用的创建 import 或 require 的别名,用来简化模块引用,项目中基本都需要进行配置。
const config = { ... resolve:{ // 配置别名 alias: { '~': resolve('src'), '@': resolve('src'), 'components': resolve('src/components'), } } };

2 extensions
  1. webpack 默认配置
const config = {
  //...
  resolve: {
    extensions: ['.js', '.json', '.wasm'],
  },
};

如果用户引入模块时不带扩展名,例如

import file from '../path/to/file';

那么 webpack 就会按照 extensions 配置的数组从左到右的顺序去尝试解析模块

需要注意的是:

  1. 高频文件后缀名放前面;
  2. 手动配置后,默认配置会被覆盖

如果想保留默认配置,可以用 ... 扩展运算符代表默认配置,例如

js
复制代码
const config = {
  //...
  resolve: {
    extensions: ['.ts', '...'], 
  },
};
3 modules

告诉 webpack 解析模块时应该搜索的目录,常见配置如下

const path = require('path');

// 路径处理方法
function resolve(dir){
  return path.join(__dirname, dir);
}

const config = {
  //...
  resolve: {
     modules: [resolve('src'), 'node_modules'],
  },
};

告诉 webpack 优先 src 目录下查找需要解析的文件,会大大节省查找时间

4 resolveLoader

resolveLoader 与上面的 resolve 对象的属性集合相同, 但仅用于解析 webpack 的 loader 包。

一般情况下保持默认配置就可以了,但如果你有自定义的 Loader 就需要配置一下,不配可能会因为找不到 loader 报错

  • 例如:我们在 loader 文件夹下面,放着我们自己写的 loader

我们就可以怎么配置

const path = require('path');

// 路径处理方法
function resolve(dir){
  return path.join(__dirname, dir);
}

const config = {
  //...
  resolveLoader: {
    modules: ['node_modules',resolve('loader')]
  },
};
babel-loader 开启缓存
  • babel 在转译 js 过程中时间开销比价大,将 babel-loader 的执行结果缓存起来,重新打包的时候,直接读取缓存
  • 缓存位置: node_modules/.cache/babel-loader

具体配置如下:

js
复制代码
const config = {
 module: { 
    noParse: /jquery|lodash/,
    rules: [
      {
        test: /.js$/i,
        include: resolve('src'),
        exclude: /node_modules/,
        use: [
          // ...
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true // 启用缓存
            }
          },
        ]
      },
      // ...
    ]
  }
}

那其他的 loader 如何将结果缓存呢?

cache-loader 就可以帮我们完成这件事情

1.6.2 cache-loader
  • 缓存一些性能开销比较大的 loader 的处理结果
  • 缓存位置:node_modules/.cache/cache-loader
  1. 安装
bash
复制代码
$ npm i -D cache-loader
  1. 配置 cache-loader
js
复制代码
const config = {
 module: { 
    // ...
    rules: [
      {
        test: /.(s[ac]|c)ss$/i, //匹配所有的 sass/scss/css 文件
        use: [
          // 'style-loader',
          MiniCssExtractPlugin.loader,
          'cache-loader', // 获取前面 loader 转换的结果
          'css-loader',
          'postcss-loader',
          'sass-loader', 
        ]
      }, 
      // ...
    ]
  }
}

1 配置optimization时让高频出现的后缀名放前面

2 externals

让header 加上jquery的cdn

externals: { jquery: 'jQuery', },

noParse

  • 不需要解析依赖的第三方大型类库等,可以通过这个字段进行配置,以提高构建速度
  • 使用 noParse 进行忽略的模块文件中不会解析 importrequire 等语法
const config = {
  //...
  module: { 
    noParse: /jquery|lodash/,
    rules:[...]
  }

};

thread-loader

配置在 thread-loader 之后的 loader 都会在一个单独的 worker 池(worker pool)中运行

babel-loader 开启缓存

const config = {
 module: { 
    noParse: /jquery|lodash/,
    rules: [
      {
        test: /\.js$/i,
        include: resolve('src'),
        exclude: /node_modules/,
        use: [
          // ...
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true // 启用缓存
            }
          },
        ]
      },
      // ...
    ]
  }
}

cache-loader

const config = {
 module: { 
    // ...
    rules: [
      {
        test: /\.(s[ac]|c)ss$/i, //匹配所有的 sass/scss/css 文件
        use: [
          // 'style-loader',
          MiniCssExtractPlugin.loader,
          'cache-loader', // 获取前面 loader 转换的结果
          'css-loader',
          'postcss-loader',
          'sass-loader', 
        ]
      }, 
      // ...
    ]
  }
}

cache 持久化缓存

通过配置 cache 缓存生成的 webpack 模块和 chunk,来改善构建速度

const config = {
  cache: {
    type: 'filesystem',
  },
};

Tree-shaking

splitchunk

1 入口点分割

optimization.splitChunks 是基于 SplitChunksPlugin 插件实现的

默认情况下,它只会影响到按需加载的 chunks,因为修改 initial chunks 会影响到项目的 HTML 文件中的脚本标签。 webpack 将根据以下条件自动拆分 chunks:

  • 新的 chunk 可以被共享,或者模块来自于 node_modules 文件夹
  • 新的 chunk 体积大于 20kb(在进行 min+gz 之前的体积)
  • 当按需加载 chunks 时,并行请求的最大数量小于或等于 30
  • 当加载初始化页面时,并发请求的最大数量小于或等于 30
  1. 默认配置介绍
js
复制代码
module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async', // 有效值为 `all`,`async`  `initial`
      minSize: 20000, // 生成 chunk 的最小体积(≈ 20kb)
      minRemainingSize: 0, // 确保拆分后剩余的最小 chunk 体积超过限制来避免大小为零的模块
      minChunks: 1, // 拆分前必须共享模块的最小 chunks 数。
      maxAsyncRequests: 30, // 最大的按需(异步)加载次数
      maxInitialRequests: 30, // 打包后的入口文件加载时,还能同时加载js文件的数量(包括入口文件)
      enforceSizeThreshold: 50000,
      cacheGroups: { // 配置提取模块的方案
        defaultVendors: {
          test: /[/]node_modules[/]/,
          priority: -10,
          reuseExistingChunk: true,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
};
  1. 项目中的使用
js
复制代码
const config = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: { // 配置提取模块的方案
        default: false,
        styles: {
            name: 'styles',
            test: /.(s?css|less|sass)$/,
            chunks: 'all',
            enforce: true,
            priority: 10,
          },
          common: {
            name: 'chunk-common',
            chunks: 'all',
            minChunks: 2,
            maxInitialRequests: 5,
            minSize: 0,
            priority: 1,
            enforce: true,
            reuseExistingChunk: true,
          },
          vendors: {
            name: 'chunk-vendors',
            test: /[\/]node_modules[\/]/,
            chunks: 'all',
            priority: 2,
            enforce: true,
            reuseExistingChunk: true,
          },
         // ... 根据不同项目再细化拆分内容
      },
    },
  },
}

3 代码懒加载

// 按需加载 img.addEventListener('click', () => { import('./desc').then(({ default: element }) => { console.log(element) document.body.appendChild(element) }) })

  


作者:IT老班长  
链接:https://juejin.cn/post/7023242274876162084  
来源:稀土掘金  
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。