手记

webpack js代码分割(11)

获取全套webpack 4.x教程,请访问瓦力博客

代码分割是一个概念,和webpack无关,在没有webpack的时候,代码分割这个概念也存在。通过合理的代码分割会让我们程序运行的性能更高,在没有webpack的时候,我们需要思考手动做代码分割怎么样合适。但是在有webpack后,我们只需在webpack配置中使用几个配置项,webpack就会知道我们的代码怎么样做代码分割合适,就会自动做代码分割,不在需要我们去考虑这个事情。

1.手动代码分割演示

安装loadsh

yarn add loadsh

src/index.js

import _ from "loadsh";

let str = _.join(['hello','world'],'-');
console.log(str)

运行webpack

yarn run build

webpack打包输出的信息,main.js文件是1.38M。main.js文件之所以这么大,是因为将整个loadsh库全部打包进main.js文件中。当我们的业务代码非常长,引用的库文件也比较多时,main文件会非常大,这会导致用户体验非常差。接下来手动拆分一下

文件结构

myProject
 |-dist
 |-node_modules
 |-src
     |-util
        |-math.js
+        |-loadsh.js
     |-assets
        |-css
            |-index.css
        |-less
            |-index.less     
        |-sass
            |-index.scss
        |-images
            |-wali_logo.png
     |-index.html
     |-index.js
 |-package.json
 |-webpack.config.js
 |-postcss.config.js
 |-.babelrc

src/util/loadsh.js

import _ from "loadsh"

window._ = _;

src/index.js

- import _ from "loadsh";
  let str = _.join(['hello','world'],'-');
  console.log(str)

webpack.config.js

module.exports = {
    ...
    entry:{
+       loadsh:'./src/util/loadsh.js'
        main:'./src/index.js'
    },
    ...
}

运行webpack

yarn run build

webpack打包看到main.js文件29.1KB,并没有将loadsh打包进main.js文件,而是单独打包成一个文件。我们测试下打包后能不能运行,打开dist/index.html,打开chrom控制台,看到控制台输出hello-world。其实webpack可以自动帮我们进行代码分割,不需要我们从架构上区分,减轻了开发者的工作。

注意:不知道大家在写entry时,有没有将loadshmain顺序写反,小菜第一次就他们顺序写反了。导致打完包后,浏览器包了一个_ is not defined。看index.html源码中也将loadsh库加载进来了。

为什么会出现这个问题?

是因为在index.html中先加载main.js然后在加载loadsh但是在执行顺序上main.js文件需要依赖loadsh库文件。虽然浏览器是并行加载js文件的。但是在有依赖关系js代码中,我们并不能保证loadsh一定加载在main文件前。

2.webpack配置代码分割

在配置webpack之前,我们将上面的代码进行回滚

回滚webpack.config.js

module.exports = {
    ...
    entry:{
-       loadsh:'./src/util/loadsh.js'
        main:'./src/index.js'
    },
    ...
}

回滚文件

myProject
 |-dist
 |-node_modules
 |-src
     |-util
        |-math.js
-        |-loadsh.js
     |-assets
        |-css
            |-index.css
        |-less
            |-index.less     
        |-sass
            |-index.scss
        |-images
            |-wali_logo.png
     |-index.html
     |-index.js
 |-package.json
 |-webpack.config.js
 |-postcss.config.js
 |-.babelrc

代码回滚完成后,我们在webpack参数中配置下,让webpack帮助我们做代码分割。

修改src/index.js

import _ from "loadsh";
let str = _.join(['hello','world'],'-');
console.log(str)

webpack.config.js

module.exports = {
    ...
    optimization:{
        usedExports: true,
+        splitChunks:{
+            chunks:'all'
+        }
    },
    ...
}

从小菜截图中可以看到,webpack将公共的类库提取出来,打包成一个文件。

3.异步代码分割

在上面我们已经将loadsh进行了代码分割,不过在src/index.js中的代码是同步的,那webpack能不能将我们的代码做异步分割呢?

src/index.js

function getComponent(){
    return import('loadsh').then(({ default:_ }) =>{
        let element = document.createElement('div');
        element.innerHTML = _.join(['hello','world'],'**');

        return element;
    })
}

getComponent().then(ele=>{
    document.body.appendChild(ele);
})

安装

yarn add @babel/plugin-syntax-dynamic-import

修改.babelrc

{
	"presets": [
		[
			"@babel/preset-env",
			{
				"targets": {
					"chrome": "67"
				},
				"useBuiltIns": "usage"
			}
		]
	],
	"plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "absoluteRuntime": false,
        "corejs": 2,
        "helpers": true,
        "regenerator": true,
        "useESModules": false
      }
    ]
+	  "@babel/plugin-syntax-dynamic-import"    
  ]
}

然后在运行webpack

yarn run build


发现webpack将异步引入的库文件也打包成了一个文件。

4.代码分割修改文件名

上面webpack将loadsh库文件做代码分割后,文件名称变成了0.js,不在是我们熟悉的loadsh.js如果想要修改文件名称,那就跟小菜继续往下走。

scr/index.js

function getComponent(){
+    return import(/*webpackChunkName:"loadsh"*/ 'loadsh').then(({ default:_ }) =>{
        let element = document.createElement('div');
        element.innerHTML = _.join(['hello','world'],'**');

        return element;
    })
}

getComponent().then(ele=>{
    document.body.appendChild(ele);
})

运行webpack

yarn run build

5.详解SpiltChunksPlugin参数

SpiltChunksPlugin默认参数

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
};

参数 默认 说明
chunks async 设置代码分割类型,和cacheGroups配置配合使用 async 对异步代码分割
all 对同步和异步代码分割
minSize 30000(30kb) 当引入的模块大于30kb才会做代码分割
maxSize 0 当引入的模块大于maxSize时,会尝试对引入的模块进行二次拆分,一般不用配置
minChunks 1 当一个模块被至少引入1次,才会做代码分割
maxAsyncRequests 5 当引入模块10个时,只会将前5个模块进行打包
maxInitialRequests 3 入口文件引入的模块如果超过3个,只会将前3个模块做代码分割
automaticNameDelimiter ~ 文件连接符
name true 拆分块的名称,让cacheGroups里面的名字有效
cacheGroups {} 对符合代码拆分的模块进行一个分类

cacheGroups参数

参数 类型 说明
priority number 有限权,当一个模块都符合cacheGroups分组条件,将按照优先权进行分组,priority值越大,优先权越高 -10
filename String|Function 拆分的名称,一般不设置,默认生成vendors~mianvendors分组名称,~连接符,main引入模块的入口文件
reuseExistingChunk boolean 如果当前块包含已从主束拆分的模块,则将重用它而不是生成新的块。比如
import a from 'A’
import b from 'B’在打包时候,按照打包顺序也会将b打包进a模块,但是在a打包之前,如果已经将b模块进行过打包,那么就不会将b模块在打包到a模块中
test function (module, chunk) | RegExp | string 控制此缓存组选择的模块。test:/[\\/]node_modules[\\/]/必须要在node_modules模块在才可以
enforce boolean 将对
splitChunks.minSize
splitChunks.minChunks
splitChunks.maxAsyncRequests
splitChunks.maxInitialRequests
配置忽略

webpack.config.js

module.exports = {
  ...
  optimization: {
+    splitChunks: {
+      chunks: 'all',
+      minSize: 30000,
+      maxSize: 0,
+      minChunks: 1,
+      maxAsyncRequests: 5,
+      maxInitialRequests: 3,
+      automaticNameDelimiter: '~',
+      name: true,
+      cacheGroups: {
+        vendors: {
+          test: /[\\/]node_modules[\\/]/,
+          priority: -10
+        },
+        default: {
+          minChunks: 2,
+          priority: -20,
+          reuseExistingChunk: true
+        }
+      }
+    }
  }
};

运行webpack

yarn run build

6.示例1说明

// index.js

import('./a'); // dynamic import

// a.js
import 'react';

//...

结果:创建包含react的单独块,在导入调用时,react块和./a的原始块并行加载

原因:

  • react块来自node_modules的模块
  • react大于30kb
  • 导入调用时的并行请求数为2
  • 不影响初始页面加载时的请求

7.示例2说明

// entry.js

// dynamic imports
import('./a');
import('./b');

// a.js
import './helpers'; // helpers is 40kb in size

//...

// b.js
import './helpers';
import './more-helpers'; // more-helpers is also 40kb in size

//...

结果:将创建一个./helpers单独的块及其所有依赖项。在导入调用时,此块与原始块并行加载

原因:

  • ./helpers块在两个导入调用之间共享
  • helpers大于30kb
  • 导入调用的并行请求数为2
  • 不影响初始页面加载时的请求
0人推荐
随时随地看视频
慕课网APP