webpack基础知识及性能优化

759 阅读9分钟

文件处理

webpack仅⽀持 .js .json ⽂件, 如果需要处理其他类型的文件,需要借助loader来进行解析

css

css对应的loader版本需要匹配,否则会报错: this.getOptions is not function等

npm install css-loader -D

  若需要对css进行模块化打包处理,webpack无法解析css/scss/less等文件类型,需要借助css相关的loader进行解析。

  首先需要安装css-loader,若是 .scss文件则还需要安装node-sasssass-loadersass-loader; .less文件需要安装 less-loader/less。如想对css进行进一步的优化, 则可使用到postcss-loaderpostcss-loader的功能比较齐全: 可以对css进行自动化补全浏览器(autoprefixer)、压缩css文件(cssnano)等;

loader&plugin

  mini-css-extract-pluginstyle-laoder不可一起使用。mini-css-extract-plugin将css打包成文件以外联src进行引入,而style-loader则将css内联方式插入dom中   

loader/plugin作用
css-loader会对 @import 和 url() 进行处理,就像 js 解析 import/require() 一样
style-loader把CSS以内联的方式插入到DOM中
sass-loader加载 Sass/SCSS 文件并将他们编译为 CSS,需要搭配node-sass/sass
less-loader将 Less 编译为 CSS 的 loader, 需搭配less
postcss-loader对css进行处理需搭配postcss,可安装postcss自身的插件去支持功能的实现
mini-css-extract-plugin将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载
purify-css删除未被使用的css,搭配purgecss-webpack-plugin

项目优化

分析工具

进度分析 progress-bar-webpack-plugin

 显示打包的进度

体积分析 webpack-bundle-analyzer

安装: npm install webpack-bundle-analyzer -D

// webpack.config.js
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); // 每个包的体积分析

plugins = [
   new BundleAnalyzerPlugin()
]

耗时分析 speed-measure-webpack-plugin

安装: npm install speed-measure-webpack-plugin -D

  speed-measure-webpack-plugin帮助我们分析整个打包过程中的总耗时,以及只管测量每一个loader 和每一个 plugins 构建所耗费的时间。

//webpack.config.js 
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin"); 
const SpeedMeasure = new SpeedMeasurePlugin(); 
const config = { 
    // webpack的所有配置配置 
} 
module.exports = SpeedMeasure.wrap(config);

image.png

common共用

文件清除 clean-webpack-plugin

webpack每次打包生产的文件都放在 dist 目录中,如果重新进行打包,webpack都会生成新的文件放入dist目录中,且dist目录中的旧文件不会被删除。这样会导致文件冗余。

image.png

clean-webpack-plugin就是用来帮助在每次打包时先清空dist目录,这样每次打包后dist中总是最新打包生成的文件。

加快带包速度

缓存 HardSourceWebpackPlugin

npm install hard-source-webpack-plugin -D

webpack4中需要自己进行配置,webpack5将内置此插件。HardSourceWebpackPlugin在第一次构建时写入缓存中,第二次构建可从缓存读取。加快打包速度--极速前进了

const HardSourceWebpackPlugin = require('hard-source-webpack-plugin'); 
module.exports = { 
    plugins: [ new HardSourceWebpackPlugin() ] 
}

1634525583(1).jpg image.png

CopyWebpackPlugin

安装: npm install copy-webpack-plugin -D

  CopyWebpackPlugin将单个文件或整个目录复制到构建目录

const CopyPlugin = require('copy-webpack-plugin');
// 复制文件  webpack4 匹配的是6.2.0版本,4以下及最新版报错
    new CopyPlugin({
      patterns: [
        {
          from: path.resolve(__dirname, "./src/static"),
          to: path.resolve(__dirname, "./dist/static"),
        },
      ],
    }),

css

删除无用代码 purifycss-webpack

安装: npm install purifycss-webpack purify-css -D 这个插件会删除你没有使用的css样式,需要注意的是 如果是多页面打包,指定paths时不要明确进行指定 避免其他页面的css被当做无效css擦除了

// webpack.config.js
const PurifyCssPlugin = require('purifycss-webpack');
const glob = require('glob');

// 删除未被使用的css  删除所有xx.html下多余css, 不需指定index.html  避免多页面打包时有效css未被打包
new PurifyCssPlugin({
  paths: glob.sync(path.join(__dirname, "/*.html")),
}),

代码压缩&添加前缀

  postcss实际做事的是它的插件。postcss 有自己的专属配置文件,可新建一个postcss.config.js文件配置postcss插件等信息。

  • autoprefixer: 添加了 vendor 浏览器前缀,它使用 Can I Use 上面的数据。

    autoprefixer 插件指定生效浏览器范围才能生效。可设置 overrideBrowserslist 属性,去指定需要生效的浏览器范围,若不设置该属性,也可自行定义一个 .browserslistrc文件或 在package.json文件中添加 browserslist属性来指定生效浏览器范围.

  • cssnano:cssnano 是一个模块化的 CSS 压缩器,需要注意的是当前插件的版本需要与postcss-loader一致

  • postcss-preset-env: 允许你使用未来的 CSS 特性    image.png

js

代码压缩 terser-webpack-plugin

安装: npm install terser-webpack-plugin -D

  terser-webpack-plugin压缩js,删除js中的 console.log和代码注释

const TerserPlugin = require("terser-webpack-plugin");

optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        minify: TerserPlugin.uglifyJsMinify,
        extractComments: true,
        // terserOptions的属性会被传递给TerserPlugin.uglifyJsMinify
        terserOptions: {
          // compress - 压缩的属性设置
          compress: {
            warnings: false,
            drop_console: true, // 移除console.log
            drop_debugger: true, // 移除debugger
            pure_funcs: ["console.log"], //移除console
          },
        },
      }),
    ],
  },

图片/字体等

文件抽离

我们可以使用url-loader来处理图片、第三方字体等静态资源。url-loader的工作原理与file-loader一致(url-loader依赖了file-loader),只是添加了一个limit属性,若文件小于该属性值则会返回一个dataUrl。如果文件大小大于限定的limit,则会输出图片

  url-loader可以自定义输出图片的名称,位置等信息

属性功能
name打包后资源文件的名称
outputPath打包后的存放位置
publiPath静态资源访问的基础路径

image.png

// webpack.config.js
rules:[{
   test: /\.(png|jpe?g|gif|svg)$/,
   loader: 'url-loader',
   options: {
     // 小于当前limit大小的文件将不会被打包,直接以base64方式引入
     limit: 1024 * 3,
     // 直接在name前面加一个目录名也会自动生成一个目录
     name: '/img/[name].[contenthash:8].[ext]'
   }
 },
 {
   test: /\.(eot|woff|ttf)$/,
   loader: 'url-loader',
   options: {
     limit: 1024 * 3,
     name: '/fonts/[name].[contenthash:8].[ext]'
   }
 }]

image-webpack-loader

图片压缩,但会影响打包的速度。可现在tiny压缩网站将图片先一步进行压缩,或者将图片放置在自己的cdn上。也可以将部分图片放在static中 直接使用copy-webpack-plugin复制一份

// webpack.config.js
rules:[{
  test: /\.(png|jpe?g|gif|svg)$/,
  use: [{
      loader: 'url-loader',
      options: {
        // 小于当前limit大小的文件将不会被打包,直接以base64方式引入
        limit: 1024 * 3,
        // 直接在name前面加一个目录名也会自动生成一个目录
        name: '/img/[name].[contenthash:8].[ext]'
      }
  }, {
    loader'image-webpack-loader',
    options: {
      // 压缩 jpeg 的配置
      mozjpeg: {
        progressivetrue,
        quality65
      },
      // 使用 imagemin**-optipng 压缩 png,enable: false 为关闭
      optipng: {
        enabledfalse,
      },
      // 使用 imagemin-pngquant 压缩 png
      pngquant: {
        quality'65-90',
        speed4
      },
      // 压缩 gif 的配置
      gifsicle: {
        interlacedfalse,
      },
      // 开启 webp,会把 jpg 和 png 图片压缩为 webp 格式
      webp: {
        quality75
      }
    }
  }
}]

抽离第三方模块

DllPlugin 和 DllReferencePlugin

  一些第三方库是不怎么变动的静态依赖文件,比如: element-uivue等。如果不抽离第三方模块,这些第三方都会打包到bundle入口文件中。这将会导致入口文件的主包格外大,减缓了打包速度,也增大了主文件的体积。

  我们可以使用webpack内置的DllPlugin 和 DllReferencePlugin插件,实现了拆分 bundles,同时大幅度提升了构建的速度。我们需要单独配置一个webpack.dll.js文件来处理抽离第三方模块

// webpack.dll.js
const path = require('path');
const webpack = require('webpack');

module.exports = {
  entry: {
    vendor: ['vue', 'vue-router', 'element-ui', 'moment', 'axios'],
  },
  output: {
    path: path.resolve(__dirname, 'static/js'), // 打包后文件输出的位置, 再由copy复制
    filename: '[name].dll.js',
    // 需要和webpack.DllPlugin中的name保持一致。
    library: '[name]_library' 
  },
  performance: {
    maxAssetSize: 1024 * 10000,
    maxEntrypointSize: 1024 * 10000,
    hints: false
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.resolve(__dirname, '[name]-manifest.json'),
      name: '[name]_library', 
      context: __dirname
    })
  ]
};

  webpack.dll.js文件配置完成后,我们需要在package.json文件中添加如下命令来生成[name]-manifest.json以供DllReferencePlugin插件使用

// package.json
script: {
    "dll": "webpack --config build/webpack.dll.config.js"
}

  最后还需要在webpack.base.js文件中进行配置DllReferencePluginCopyWebpackPlugin两个插件。将第三方文件引入index.html中

// index.html
<script src="static/js/vendor.dll.js"></script>

// webpack.base.js
const CopyWebpackPlugin = require('copy-webpack-plugin');

new webpack.DllReferencePlugin({
  context: __dirname,
  manifest: require('./vendor-manifest.json')
}),
new CopyWebpackPlugin([{ from: 'static', to: 'static' }])

使用: 先执行npm run dll生成对用的manifest.json文件 再去进行npm run dev或者npm run build。整个使用费过程中 只需要执行一次npm run dll即可,执行完后dllPlugin已经生成了第三方模块

替代方案

autodll-webpack-plugin综合了DllPlugin 和 DllReferencePlugin的功能,精简配置。但是需要和htmlWebpackPlugin综合使用,两者的inject属性需要为true,这样生成的第三方库 vendor.js 会自动被引入html中

const path = require('path');
const AutoDllPlugin = require('autodll-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
 
module.exports = {
  entry'./src/index.js',
 
  output: {
    filename'[name].bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath'/'
  },
 
  plugins: [
    new HtmlWebpackPlugin({
      injecttrue// will inject the main bundle to index.html
      template'./src/index.html',
    }),
    new AutoDllPlugin({
      injecttrue// will inject the DLL bundle to index.html
      debugtrue,
      filename'[name]_[hash].js',
      path'./dll',
      entry: {
        vendor: [
          'react',
          'react-dom'
        ]
      }
    })
  ]
};

开发环境

// webpack.de.config
const webpack = require('webpack');

plugins: [
    new webpack.HotModuleReplacementPlugin()
]

生产环境

css

文件抽离 mini-css-extract-plugin

  这个插件应该只在生产环境构建中使用!! 本插件会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载,但是并不会压缩css。此插件有匹配的一个处理css的loader(MiniCssExtractPlugin.loader)。

  如果想将css打包放置目标目录下的同一个文件夹中,可将mini-css-extract-plugin插件可设置属性filename时在css的名称前添加一个目录名。

miniCssExtractPlugin.png 怎么为每一个js文件生一个css文件???

2.4.2版本运行报错?? "webpack": "^4.43.0",重新安装 0.9.0版本无报错

指纹策略

  webpack中有三种指纹策略,使用指纹策略的情景:

  项目发布的时候,用户的电脑访问没有任何变化,还是旧的样式。或者js修改完后但是无法生效的根本原因在于浏览器依然在使用缓存。浏览器的工作流程: 若是第一次访问 则先去询问服务器获取文件等数据。但是如果非第一次,浏览器将会在本地缓存中读取文件,如果服务器更新之后的文件名与上一次的文件名相同,浏览器则会优先读取本地缓存的数据。这样会网站导致更新之后但是看不到效果。为了解决这个问题,我们需要用到webpack的指纹策略,在文件名后加上 hashchunkhashcontenthash 中的其中一种

指纹策略解析
hash和整个项目相关,只要项目文件改变,整个项目构建的hash值都会改变
chunkhash和webpack打包的chunk有关,不同的entry会生成不同的chunkhash
contenthash根据文件内容来定义hash,文件内容不变,则contenthash不变

优化策略

构建速度

优化webpack构建的方式有很多,主要可以从优化搜索时间、缩小文件搜索范围、减少不必要的编译、多线程执行、缓存等方面入手

  • 优化 loader 配置
  • 合理使用 resolve.extensions
  • 优化 resolve.modules
  • 优化 resolve.alias
  • 使用 DLLPlugin 插件
  • 使用 cache-loader
  • terser 启动多线程
  • 合理使用 sourceMap
  • hardSourceWebpackPlugin 缓存
  • CopyWebpackPlugin 静态文件复制

性能优化

关于webpack对前端性能的优化,可以通过各种文件体积大小入手,其次还可通过分包的形式、减少http请求次数等方式,实现对前端性能的优化

  • JS代码压缩
  • CSS代码压缩
  • Html文件代码压缩
  • 文件大小压缩
  • 图片压缩
  • Tree Shaking
  • 代码分离
  • 内联 chunk