代码分割是一个概念,和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

ssl

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

ssl

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'
+        }
    },
    ...
}

ssl

从小菜截图中可以看到,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

ssl ssl

发现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

ssl

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
        }
      }
    }
  }
};

      

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

cacheGroups参数

参数类型说明 
prioritynumber有限权,当一个模块都符合cacheGroups分组条件,将按照优先权进行分组,priority值越大,优先权越高-10 
filenameString|Function拆分的名称,一般不设置,默认生成vendors~mianvendors分组名称,~连接符,main引入模块的入口文件  
reuseExistingChunkboolean如果当前块包含已从主束拆分的模块,则将重用它而不是生成新的块。比如
import a from ‘A’
import b from ‘B’在打包时候,按照打包顺序也会将b打包进a模块,但是在a打包之前,如果已经将b模块进行过打包,那么就不会将b模块在打包到a模块中
  
testfunction (module, chunk) | RegExp | string控制此缓存组选择的模块。test:/[\\/]node_modules[\\/]/必须要在node_modules模块在才可以  
enforceboolean将对
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的原始块并行加载

原因:

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单独的块及其所有依赖项。在导入调用时,此块与原始块并行加载

原因:

webpack4.x 教程

webpack 准备工作(1) webpack 手写最基本的配置(2) webpack 打包样式(3) webpack 让打包更便捷(4) webpack 打包图片(5) webpack iconfont打包(6) webpack devServer本地开发(7) webpack 处理ES6语法(8) webpack SourceMap配置(9) webpack Tree Shaking(10) webpack js代码分割(11) webpack 懒加载(12) webpack 打包分析,Preloading,Prefetching(13) webpack 模块化区分开发和生产(14) webpack 样式文件的代码分割(15) webpack 浏览器缓存(16) webpack shimming(17) webpack 环境变量(18) webpack Esline(19)