webpack性能优化之代码分离

246 阅读4分钟

介绍

代码分离:主要是将代码分离到不同的bundle中,之后我们可以按需加载,或者并行加载这些文件。 如果没有进行代码分离,一般打包成一个js文件后,在首页进行加载时,就会加载这一个js文件,如果这个js文件过大,就会导致影响首页的加载速度。

webpack常用的代码分离有三种:

  1. 多入口起点:配置entry的多个打包入口。
  2. 动态导入:通过import函数来分离代码。
  3. 防止重复:入口的多份相同依赖进行抽取或者使用splitchunkPlugin分离代码。

多入口起点

配置很简单,假如src目录下面有index和main文件,配置这两个文件为打包入口

image.png

webpack.config.js配置

const path = require("path");
module.exports = {
  mode: "development",
  entry: {
    index: "./src/index.js",
    main: "./src/main.js",
  },
  output: {
    path: path.resolve(__dirname, "./build"),
    filename: "[name]bundle.js",
    clean: true,
  },
};

最后的打包结果,此时就会打包成两个文件

image.png

动态导入

主要使用ECMAScript中的 import() 语法来完成。 例如:当有一个foo.js的模块需要在代码运行过程中来决定是否导入它,这个代码不一定用到,所以最好将他拆分成一个独立的js文件,在需要的时候采取加载它,这个时候就可以使用动态导入。

const btn1 = document.createElement('button')
btn1.onclick = function() {
  import('./foo.js')
}

只有当btn1点击之后才会会请求这个foo打包单独生成的文件

在使用import函数时,我们可以通过then拿到foo导出的内容

例如,foo.js如下

function sum(a,b){
	return a+b	
}

const name='zs'

export {
	sum
}
export default name
btn1.onclick = function() {
  import('./foo.js').then(res=>{
      res.sum(1,2);
      res.default()//这里拿到的是name
  }
}

动态导入的打包文件命名


module.exports = {
  mode: "production",
  entry: "./src/main.js",
  output: {
    clean: true,
    path: path.resolve(__dirname, "./build"),
    filename: "[name]-bundle.js",
    // 单独针对分包的文件进行命名
    chunkFilename: "[name]_chunk.js",
      }
  }

打包后的文件,如图下面src_foo_js_chunkl.js就是foo.js打包后的文件

image.png

若希望修改上面[name]_chunk.js的name,可以在import函数里面修改

btn1.onclick = function() {
  import(/* webpackChunkName: "bar" */'./foo.js')
}

经过上面的修改之后,打包后的文件的名字就会是这样bar_chunk.js

prefetch和preload

使用方法是在import的时候进行配置

  import(
    /* webpackChunkName: "foo" */
    /* webpackPrefetch: true */
    './foo.js')
  • prefetch(预获取):将来某些导航下可能需要的资源
  • preload(预加载):当前导航下可能需要资源

prefetch和preload对比

  • preload会在父 chunk 加载时,以并行方式开始加载。prefetch 会在父 chunk 加载结束后开始加载。
  • preload 加载后立即下载,而prefetch chunk 在浏览器闲置时下载。

防止重复

入口重复依赖

当我们的项目使用多入口,并且每个入口都有引用axios和lodash,那么此时可以将这两份都打包成一个文件,让他们进行共享。

webpack.config.js配置如下:

entry: {
    index: {
      import: './src/index.js',
      dependOn: 'shared'
    },
    main: {
      import: './src/main.js',
      dependOn: 'shared'
    },
    shared: ['axios','lodash']
  },
  output: {
    path: path.resolve(__dirname, './build'),
    filename: '[name]-bundle.js',
    clean: true
  },

最后打包结果如下,会将相同的部分抽离生成一个shared-bundle.js文件

image.png

SplitChunks

这种底层利用SplitChunksPlugin来实现的。该插件webpack已经默认安装和集成,所以我们并不需要单独安装和直接使用该插件。

默认配置如下:

module.exports={
    entry:...
    output:...

    optimization:{
    	splitChunks:{
    		chunks:'async',  默认是async,表示异步的(例如import()动态导入)进行分包,而其他import axios from 'axios',不进行分包
    	}
    }
}

我们可以进行如下自定义配置

module.export={
    entry:..
    output:...
    

  optimization: {
    // 分包插件: SplitChunksPlugin
    splitChunks: {
       //设置成all表示所有import导入的都需要分包,会生成一个个js文件
      chunks: "all",
      // 当一个包大于指定的大小时, 继续进行拆包
      // maxSize: 20000,
      // // 将包拆分成不小于minSize的包
      // minSize: 10000,
      minSize: 10,
    }
  },
  }

1.如果想要进一步分包处理,比如对来自某个文件夹的包进行进一步的分包,需要如下配置

webpack.config.js
 optimization: {
    splitChunks: {
      chunks: "all",
      maxSize: 20000,
      minSize: 10000,

      //对某些需要拆包的内容进行分包处理
      cacheGroups: {
         vendors:{
             test:/node_modules/,
             filename:'[id]_vendors.js',
         },
        util: {
          test: /utils/,
          filename: "[name]_utils.js",
        },
      },
    },
  },

上面会对来自utils和node_modules下面的包进一步分包。

image.png

上两个就是打包后的结果

2.如果需要对js代码进行压缩或者将注释提取出来,需要先安装terser-webpack-plugin插件

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

optimization:{
    splitChunks:{},
    
    minimize:true,//开启js压缩
    minimizer:{
       new TerserPlugin({
           extraComments:true
       })
    }
}

提取的这个单独的文件通常以 .LICENSE.txt 或类似的扩展名保存,如下图

image.png

3.optimization.chunkIds配置

这个配置用于告知webpack模块的id采用什么算法生成,有三个值

  • natural:按照数字的顺序使用id;

image.png

  • named:development下的默认值,一个可读的名称的id。一般是路径

image.png

  • deterministic:确定性的,在不同的编译中不变的短数字id

image.png