最近从零开始配置了一个pc端项目,感觉本地服务启动很慢,还没写什么代码,就需要启动2s~3s,有点不可思议。

对比之前写H5项目时感觉启动速度快很多。于是决定优化一下。

怎么优化呢,先从分包开始吧。

目前启动慢,很大原因是所有代码和样式都被打包到同一个输出文件,导致这个文件非常大,应该是很多兆。所以最后上传、下载、加载的速度都很慢。

js分包加载

1. 修改输出文件名

output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist') 
  },

改成:

output: {
    filename: '[name].js', 
    chunkFilename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  },

2. 添加optimization配置

  optimization: {
    runtimeChunk: 'single', //会将Webpack在浏览器端运行时需要的代码单独抽离到一个文件
    splitChunks: {
      cacheGroups: {
        commons: {
          //产生一个Chunk
          chunks: 'initial',
          minChunks: 2,
          maxInitialRequests: 5, // The default limit is too small to showcase the effect
          minSize: 0 // This is example is too small to create commons chunks
        },
        vendor: {
          //产生一个Chunk
          test: /node_modules/,
          chunks: 'initial',
          name: 'vendor',
          priority: 10,
          enforce: true
        }
      }
    }
  }

总共会生成三个文件:main.js,runtime.js,vendor.js

这样以来,由原来的main.js分离出来三个js文件,不仅提升的加载速度,也提升了代码可读性,业务代码主要集中在runtime.js文件里。

3. webpack全量配置:

module.exports = {
  entry: './src/index.jsx', //入口,相对路径
  output: {
    // filename: 'main.js', //打包后的文件名
    filename: '[name].js', //打包后输出文件的文件名
    chunkFilename: '[name].js',
    path: path.resolve(__dirname, 'dist') //打包后的路径(文件夹路径),必须是绝对路径,"__dirname"指当前路径
  },
  //生成多个js文件
  optimization: {
    runtimeChunk: 'single', //会将Webpack在浏览器端运行时需要的代码单独抽离到一个文件
    splitChunks: {
      cacheGroups: {
        commons: {
          //产生一个Chunk
          chunks: 'initial',
          minChunks: 2,
          maxInitialRequests: 5, // The default limit is too small to showcase the effect
          minSize: 0 // This is example is too small to create commons chunks
        },
        vendor: {
          //产生一个Chunk
          test: /node_modules/,
          chunks: 'initial',
          name: 'vendor',
          priority: 10,
          enforce: true
        }
      }
    }
  },
}

css分包加载(抽离css文件)

上面抽离出多个js文件,如果想抽离css文件,该怎么做?

网上大多数教程,包括webpack官网给出的都是用mini-css-extract-plugin插件进行抽离。

方法没错,但是根据webpack版本不同,配置有所区别。

1. webpack5以下版本配置

webpack5以下版本,用下面这种方式加载应该没有问题:

let MiniCssExtractPlugin = require('mini-css-extract-plugin') //css单独分离文件加载

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css', //抽离css之后输出的文件名
      chunkFilename: '[id].css'
    })
  ],

  module: {
    //模块
    rules: [
      {
        test: /\.css$/, //匹配到css结尾的文件,加载css-loader,
        use: [
          {
          'style-loader',
          MiniCssExtractPlugin.loader,
          'css-loader'
        ]
      },
      {
        test: /\.less$/, //匹配到less结尾的文件
        use: [
          'style-loader',
          MiniCssExtractPlugin.loader,
          'css-loader',
          'less-loader'
        ]
      }
    ]
  }
}
注意 MiniCssExtractPlugin.loader需要放在'style-loader'和'css-loader'之间

2. webpack5以上版本配置

webpack5以上版本,需要在MiniCssExtractPlugin.loader配置里添加一个esModule: false属性。

use: [
    'style-loader',
    {
        loader: MiniCssExtractPlugin.loader,
        options: {
            esModule: false,
        },
    },
    'css-loader',
    'sass-loader'
]

完整webpack配置如下:

const path = require('path')
let MiniCssExtractPlugin = require('mini-css-extract-plugin') //css单独分离文件加载

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css', //抽离css之后输出的文件名
      chunkFilename: '[id].css'
    })
  ],
  module: {
    //模块
    rules: [
      {
        test: /\.css$/, //匹配到css结尾的文件,加载css-loader,
        use: [
          {
            loader: 'style-loader',
            options: {
              //将当前loader添加到<head>标签内容的最上面
              insert: function (element) {
                var parent = document.querySelector('head')
                var lastInsertedElement =
                  window._lastElementInsertedByStyleLoader
                if (!lastInsertedElement) {
                  parent.insertBefore(element, parent.firstChild)
                } else if (lastInsertedElement.nextSibling) {
                  parent.insertBefore(element, lastInsertedElement.nextSibling)
                } else {
                  parent.appendChild(element)
                }
              }
            }
          },
          {
            //css单独分离文件加载
            loader: MiniCssExtractPlugin.loader,
            options: {
              esModule: false
            }
          },
          'css-loader'
        ]
      },
      {
        test: /\.less$/, //匹配到less结尾的文件
        use: [
          {
            loader: 'style-loader',
            options: {
              //将当前loader添加到<head>标签内容的最上面
              insert: function (element) {
                var parent = document.querySelector('head')
                var lastInsertedElement =
                  window._lastElementInsertedByStyleLoader
                if (!lastInsertedElement) {
                  parent.insertBefore(element, parent.firstChild)
                } else if (lastInsertedElement.nextSibling) {
                  parent.insertBefore(element, lastInsertedElement.nextSibling)
                } else {
                  parent.appendChild(element)
                }
              }
            }
          },
          {
            //css单独分离文件加载
            loader: MiniCssExtractPlugin.loader,
            options: {
              esModule: false
            }
          },
          'css-loader',
          'less-loader'
        ]
      }
    ]
  }
}

运行结果发现多了两个css文件:main.css,vendor.css

main.css 里面主要是我们自己写的css的集合。
vendor.css一般都是第三方组件的样式集合。