Webpack5学习笔记(基础篇八)——代码分离

代码分离是 webpack 中最引人注目的特性之一。此特性能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的 bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。

常用的代码分离方法有三种

  • 入口起点:使用 entry 配置手动地分离代码
  • 防止重复:使用 Entry dependencies 或者 SplitChunksPlugin 去重和分离 chunk
  • 动态导入:通过模块的内联函数调用来分离代码。

1、入口起点(entry)

我们创建一个index.htmlwebpack.config.js配置文件,同时在src目录下面创建一个index.jsanother-code.js的文件
Webpack5学习笔记(基础篇八)——代码分离_第1张图片
index.js

console.log('index')

another-code.js

console.log('another-code')

index.html(随便写点东西)

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>中国男足,永远的神title>
head>
<body>
body>
html>

webpack.config.js

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    //代码入口分离
    entry : {
        index:'./src/index.js',
        another:'./src/another-code.js'
    },

    output : {
        filename:'[name].bundle.js',
        path:path.resolve(__dirname,'./dist'),
        clean:true,
    },

    mode : 'development',

    devtool:'inline-source-map',

    plugins:[
        new HtmlWebpackPlugin({
            template:'./index.html',
            filename:'app.html',
            inject:"body"
        }),
    ],

    devServer:{
        static:'./dist'
    },
}

我们在webpack.config.js配置文件的entry部分规定了2个入口,同时在output部分filename:'[name].bundle.js'规定了打包后的文件名字,防止打包后文件名之重复。

我们执行npx webpack打包,可以看出,打包后的文件体积很小,打包后的app.html也是引入了这两个打包的js
Webpack5学习笔记(基础篇八)——代码分离_第2张图片
Webpack5学习笔记(基础篇八)——代码分离_第3张图片
执行npx webpack-dev-server --open打开浏览器,控制台打印了indexanother-code
Webpack5学习笔记(基础篇八)——代码分离_第4张图片
我们看到,entry配置多个入口确实可以帮助我们实现代码分离,现在我们在2个js文件中分别引入lodash
index.js

import _ from 'lodash'

console.log('index')
console.log(_.join(['index','lodash']))

another-code.js

import _ from 'lodash'

console.log('another-code')
console.log(_.join(['another-code','lodash']))

执行npx webpack打包,我们发现,这次打包后的2个bundle.js体积一下就变大了许多
Webpack5学习笔记(基础篇八)——代码分离_第5张图片
执行npx webpack-dev-server --open打开浏览器,控制台打印了相关字符串
Webpack5学习笔记(基础篇八)——代码分离_第6张图片
所以我们看出这种方法有一个缺点

  • 如果入口 chunk 之间包含一些重复的模块,那些重复模块都会被引入到各个 bundle 中
  • 这种方法不够灵活,并且不能动态地将核心应用程序逻辑中的代码拆分出来

以上两点中,第一点对我们的示例来说无疑是个问题,因为之前我们在 ./src/index.js 中也引入过 lodash,这样就在两个 bundle 中造成重复引用。所以我们需要防止重复。

2、防止重复(prevent duplication)

2.1、入口依赖(dependOn)

我们在entry入口配置的时候,可以配置 dependOn option 选项,然后通过shared这个option选项,这样可以在多个 chunk 之间共享模块:
webpack.config.js

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    //代码入口分离
    entry : {
        //index:'./src/index.js',
        //another:'./src/another-code.js'
        
        //防止重复,抽离公共的方法,代码分离(1)
        index:{
            import:'./src/index.js',
            dependOn:'shared'
        },
        another:{
            import:'./src/another-code.js',
            dependOn:'shared'
        },
        shared:'lodash'
    },

    output : {
        filename:'[name].bundle.js',
        path:path.resolve(__dirname,'./dist'),
        clean:true,
    },

    mode : 'development',

    devtool:'inline-source-map',

    plugins:[
        new HtmlWebpackPlugin({
            template:'./index.html',
            filename:'app.html',
            inject:"body"
        }),
    ],

    devServer:{
        static:'./dist'
    },
}

上面shared:'lodash'就表明,会把各个模块间依赖的lodash这个库给抽取出来

我们执行npx webpack打包,可以看到,抽离出了shares.bundle.js这个文件,同时打包后的index.bundle.jsanother.bundle.js的体积大幅减小
Webpack5学习笔记(基础篇八)——代码分离_第7张图片
Webpack5学习笔记(基础篇八)——代码分离_第8张图片
我们看index.html中也分别引入了这3个打包后的js文件
Webpack5学习笔记(基础篇八)——代码分离_第9张图片
执行npx webpack-dev-server --open打开浏览器,看到body下面分别加载了三个js文件,控制台打印了相关字符串
Webpack5学习笔记(基础篇八)——代码分离_第10张图片
Webpack5学习笔记(基础篇八)——代码分离_第11张图片

2.2、SplitChunksPlugin

SplitChunksPlugin 插件可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。我们使用这个插件,同时将之前的webpack.config.js中重复的 lodash 模块去除

entry入口仍是简单的2个入口,我们通过optimization这个option选项,通过配置splitChunkschunks:'all',起到抽离作用

webpack.config.js

module.exports = {
    //代码入口分离
    entry : {
        index:'./src/index.js',
        another:'./src/another-code.js'
    },
     ···
     ···
     optimization:{ 
        //防止重复,抽离公共的方法,代码分离(2)
        splitChunks:{
           chunks:'all'
        }
    }
}

我们执行npx webpack打包,可以看到,这次没有生成shares.bundle.js这个文件,但是生成了vendors-node_modules_lodash_lodash_js.bundle.js这个文件
Webpack5学习笔记(基础篇八)——代码分离_第12张图片
我们看index.html中也分别引入了这3个打包后的js文件
Webpack5学习笔记(基础篇八)——代码分离_第13张图片
执行npx webpack-dev-server --open打开浏览器,看到body下面分别加载了三个js文件,控制台打印了相关字符串
Webpack5学习笔记(基础篇八)——代码分离_第14张图片
Webpack5学习笔记(基础篇八)——代码分离_第15张图片

3、动态导入(import)

当涉及到动态代码拆分时,webpack 提供了两个类似的技术。

  • 第一种,也是推荐选择的方式是,使用符合 ECMAScript 提案import() 语法来实现动态导入。
  • 第二种,则是 webpack 的遗留功能,使用 webpack 特定的 require.ensure

3.1、纯动态

这里我们用第一种import() 语法,在我们开始之前,先把之前示例的配置中移除掉多余的 entryoptimization.splitChunks,入口也先只从index.js导入

webpack.config.js

 module.exports = {
   mode: 'development',
   entry: {
     index: './src/index.js',
    
   },
   ···
  //optimization: {
  //  splitChunks: {
  //    chunks: 'all',
  //  },
  //},
 };

我们在src目录下新创建一个async-code.js,我们不再使用 statically import(静态导入)lodash,而是通过 dynamic import(动态导入)来分离出一个 chunk

async-code.js(由于 import() 会返回一个 promise,所以我们用then来连接)

function getComponent(){
    return import('lodash').then(({default:_})=>{
        const element = document.createElement('div')

        element.innerHTML = _.join(['async','code','lodash'],'-')

        return element
    })
}
getComponent().then((element) => {
    document.body.appendChild(element)
})

index.js(index.js中引入动态导入的js文件,同时先把静态导入lodash注释)

// import _ from 'lodash'
import './async-code.js'

console.log('index')
// console.log(_.join(['index','lodash']))

执行npx webpack打包,同样是生成了vendors-node_modules_lodash_lodash_js.bundle.js这个文件,帮助我们抽离出相关模块
Webpack5学习笔记(基础篇八)——代码分离_第16张图片
执行npx webpack-dev-server --open打开浏览器,页面上显示了我们动态导入中输出的字符串
Webpack5学习笔记(基础篇八)——代码分离_第17张图片
由此我们可以看出,动态import()也可以成功帮助我们成功分割代码,那么如果将刚才注释的代码恢复,使用 statically import(静态导入)lodash 和 dynamic import(动态导入)共同组合呢,这时是否会抽离出共同的chunk?

3.2、动态 + 静态

我们将刚才index.js的lodash恢复,同时将webpack.config.jsoptimization.splitChunks恢复
index.js(index.js中引入动态导入的js文件,同时使用静态导入lodash)

import _ from 'lodash'
import './async-code.js'

console.log('index')
console.log(_.join(['index','lodash']))

webpack.config.js

 module.exports = {
   mode: 'development',
   entry: {
     index: './src/index.js',
   },
   ···
 	optimization: {
    	splitChunks: {
    	   chunks: 'all',
    	},
  	},
 };

执行npx webpack打包,依旧正常
Webpack5学习笔记(基础篇八)——代码分离_第18张图片执行npx webpack-dev-server --open打开浏览器,页面和控制台中同时显示了我们动态导入静态导入的字符串,
Webpack5学习笔记(基础篇八)——代码分离_第19张图片

3.3、动态 + 静态

我们将另一个another-code.js静态导入也重新恢复

webpack.config.js

 module.exports = {
   mode: 'development',
   entry: {
     index: './src/index.js',
     another:'./src/another-code.js'
   },
   ···
 	optimization: {
    	splitChunks: {
    	   chunks: 'all',
    	},
  	},
 };

执行npx webpack打包
Webpack5学习笔记(基础篇八)——代码分离_第20张图片执行npx webpack-dev-server --open打开浏览器,页面和控制台中同时显示了我们动态导入和2个静态导入的字符串
Webpack5学习笔记(基础篇八)——代码分离_第21张图片
可以看出,我们现在代码抽离chunk已完全实现

4、懒加载

懒加载或者按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把
你的代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用
或即将引用另外一些新的代码块。这样加快了应用的初始加载速度减轻了它的总体体积,因为有些代码模块永远也用不到。

我们配合上面聊的动态导入import()的方式,来说说懒加载,在src目录下面创建一个math.js,里面输出addminus两种方法
Webpack5学习笔记(基础篇八)——代码分离_第22张图片

export const add = (x,y) => {
    return x + y
}

export const minus = (x,y) => {
    return x - y
}

在index.js中通过import引入math.js,

index.js

import _ from 'lodash'
import './async-code.js'

console.log('index')
console.log(_.join(['index','lodash']))

const BTN = document.createElement('button')
BTN.textContent = '点击我运行'
BTN.addEventListener('click',()=>{
     import('./math.js').then(({add,minus})=>{
        console.log(add(3,5))
        console.log(minus(3,5))
     })
})
document.body.appendChild(BTN)

执行npx webpack打包,我们发现,又多打包了个src_math_js.bundle.js
Webpack5学习笔记(基础篇八)——代码分离_第23张图片
Webpack5学习笔记(基础篇八)——代码分离_第24张图片
npx webpack-dev-server --open打开浏览器,页面中有个按钮,刚开始控制台没有打印,当我们点击按钮后,控制台console才打印了addminus的执行结果,同时我们观察network中,开始没有请求该js文件,但是点击后,请求了该打包后的src_math_js.bundle.js,这样会节省网络流量
Webpack5学习笔记(基础篇八)——代码分离_第25张图片
Webpack5学习笔记(基础篇八)——代码分离_第26张图片
webpackChunkName(魔法注释)

我们可以使用 magic comment 来修改动态 import 导出的 chunkname

index.js

import _ from 'lodash'
import './async-code.js'

console.log('index')
console.log(_.join(['index','lodash']))

const BTN = document.createElement('button')
BTN.textContent = '点击我运行'
BTN.addEventListener('click',()=>{
    import(/* webpackChunkName: 'math' */'./math.js').then(({add,minus})=>{
       console.log(add(3,5))
       console.log(minus(3,5))
    })
})
document.body.appendChild(BTN)

执行npx webpack打包,这时打包后的bundle就是起的math别名
Webpack5学习笔记(基础篇八)——代码分离_第27张图片
Webpack5学习笔记(基础篇八)——代码分离_第28张图片

5、预获取(prefetch)

Webpack v4.6.0+ 增加了对预获取的支持。

prefetch(预获取):将来某些导航下可能需要的资源

我们在刚才懒加载webpack.config.js的配置文件的基础上,添加一句魔法注释

/* webpackPrefetch: true */

webpack.config.js

import _ from 'lodash'
import './async-code.js'

console.log('index')
console.log(_.join(['index','lodash']))

const BTN = document.createElement('button')
 BTN.textContent = '点击我运行'
 BTN.addEventListener('click',()=>{
     import(/* webpackChunkName: 'math',webpackPrefetch: true */'./math.js').then(({add,minus})=>{
        console.log(add(3,5))
        console.log(minus(3,5))
     })
 })
 document.body.appendChild(BTN)

不用打包,直接在浏览器上刷新页面,我们发现之前的math.bundle.js提前加载了出来,此时控制台也未打印任何值
Webpack5学习笔记(基础篇八)——代码分离_第29张图片
Webpack5学习笔记(基础篇八)——代码分离_第30张图片

此时会生成 并追加到页面head头部,这表示浏览器在首页面其他加载完毕后,浏览器在闲置时间预获取 math.bundle.js 文件
Webpack5学习笔记(基础篇八)——代码分离_第31张图片

我们点击按钮,发现math.bundle.js又被懒加载了出来,控制台打印了字符串
Webpack5学习笔记(基础篇八)——代码分离_第32张图片

Webpack5学习笔记(基础篇八)——代码分离_第33张图片
关于另一种预加载preload,我在这里就不多描述了,有兴趣的可以点击这里
查看详细用法。

__
本博客参考:

  • webpack中文文档——代码分离
  • webpack 代码分离快速指北——JS菌

你可能感兴趣的:(Webpack,webpack,javascript,vue.js)