关于前端:弄懂这几个概念后我对webpack有了新的理解

36次阅读

共计 7523 个字符,预计需要花费 19 分钟才能阅读完成。

前言

随着 vite 的诞生,webpack仿佛慢慢的被大家摈弃。前阵子我也用 vue@3.x + vite@4.x 开发了一个后盾管理系统,体验了一把,的确有被 vite 飞快的启动速度给惊艳到。

然而毕竟 webpack 曾经诞生了许久,也通过市场的一些考验,并且它有着丰盛的插件,丰盛的性能,一些大型的我的项目也应用过它,目前来说,它是一个绝对于 vite 来说更稳固的打包工具。

基于以下起因:

  • 有些公司因为历史起因,我的项目构建也是基于 webpack,所以如果去到一些公司要做脚手架迁徙降级什么的,会应用webpack 就显得非常有必要了。
  • 前端构建工具,应用步骤,思维根本差不多,因而学会webpack,对其余构建工具的学习也有帮忙

所以,思来想去,还是决定开个 webpack 的专栏,跟大家一起学习 webpack 怎么配置(尽管专栏开的有点晚了)。

本篇文章次要外围,是跟大家一起搞懂 应用 webpack 时,波及到的几个概念,让咱们对 webpack 编译流程有个更好的了解

webpack 是什么

它就是一个打包工具:

  • 它能够把咱们源码中的 es6+ jslesssassimgfont 等多个模块资源文件,通过一系列解决,编译出咱们浏览器能辨认并运行 cssjshtml 等前端资源
  • 同时,它还提供很多可高度配置的性能(tree shaking代码宰割 模块热替换)等,帮忙咱们更好的优化治理咱们的代码和资源

webpack.config.js

webpackv4.0.0 当前领有开箱即用的性能,即咱们装置了 webpackwebpack-cli 两个依赖当前,无需任何配置文件,就能够间接进行打包。这是因为:

  • webpack默认入口为:根目录 /src/index.js
  • webpack默认输入编译后的文件及文件名为:根目录 /dist/main.js

但咱们个别会在咱们的我的项目根目录新建一个名为 webpack.config.js 的配置文件。这样能够批示 webpack 如何解决咱们我的项目中的文件、资源等;通过更高度的自定义配置来满足咱们我的项目的需要。

咱们看看这个配置文件大略得框架是什么样,心里有个印象:

// webpack.config.js
module.exports = {
    // 入口
    entry: {},

    // 打包输入
    output: {},

    // 配置模块如何解析
    resolve: {},

    // 配置各种 loader
    module: {},

    // 配置插件
    plugins: [],

    // 优化(能够进行代码宰割)optimization: {},

    // webpack-dev-server 开发时的配置,个别用于 development 模式
    devServer: {}};

webapck当然还反对很多配置项,只是咱们平时用到的个别就这几个,其余配置项能够看文档

留神

webpack配置文件,外面的代码轻易大家怎么玩,配置文件名字也不肯定肯定要为 webpack.config.js,只有保障 最初输入的是一个 webpack 配置的对象就能够了

// myconfig.js

// 各种逻辑代码
...
...

// 保障最初导出一个 `webpack` 配置的对象就能够
module.exports = {
    // 各种 webpack 配置
    entry: {},
    output: {},
    ....
}
"build": "webpack --config myconfig.js"

优化

所有配置都写在一个文件,这不利于咱们保护与开发;而且咱们开发我的项目时,会有多个环境(本地开发环境,测试环境,生产环境),所以咱们个别会有不同环境的配置文件,跟所有环境都通用的配置文件。
咱们个别会将 ” 通用的配置文件 ” 与“不同环境的配置文件”合并,最初再由 webpack 运行这个合并后的这个配置文件。

配置文件个别有:

  • 开发与生产环境通用的配置(webpack.common.js
  • 开发环境配置(webpack.dev.js
  • 生产环境配置(webpack.pro.js

后续文章会教大家如何进行这种配置办法

entry

它指的是 webpack 开始解析,构建依赖图的终点。咱们个别用 {key: value} 对象的模式,来配置entry,例如:

module.exports = {
    entry: {
        index: './src/index.js',
        share: './src/share.js',
    },
}

这示意:

  • 咱们我的项目中有两个入口indexshare
  • webpack会从 indexshare 两个入口开始构建它们相干依赖的模块,从而造成一个依赖图(前面会有更具体的解释)
  • 打包后会以 key 为打包后的文件名字

output

它非常好了解,它能够设置通过 webpack 打包后的编译文件的名称,及应该输入到哪个地位。

须要留神的是,即便咱们设置了多个 entry 的入口,然而只能指定一个 output 配置。

Module

咱们通过 importrequire进来的资源文件,或咱们我的项目中的每个文件,都能够看作为一个独立的模块。因而,它能够是 js 文件、css文件、也能够是图片等任何一种资源。所以咱们开发中常常会看到以下语句:

  • import 'index.css'
  • import A from './x.js'
  • import pic from './x.png'

模块之间的组合又会造成一个 ChunkChunk 是一个很重要的概念,后文会具体讲。

参考文章:Modules

Loader

通过上文咱们晓得,我的项目中每个一个文件都能够看作是一个模块,又因为咱们的文件类型多种多样,因而咱们须要某个货色,它能够把这些模块解析成 webpack 可能辨认的无效模块,并将他们增加到依赖图中,这个货色就是Loader

webpack也很贴心的给咱们提供了一个 module 的配置项,它专门用来配置不同的 loader 以至于解析不同的文件类型(模块)。这是因为 webpack 默认只能解析 jsjson文件,所以如果要解析不同的类型的文件(模块),咱们就要装置相应的loader

// webpack.config.js
module.exports = {
    ...,
    modules: {
        rules: [
            // 解析.txt 文件,应用 raw-loader
            {test: /.txt$/, use: 'raw-loader'},
        ],
    }
}

loader有两个属性:

  • test 属性,辨认出哪些文件会被转换。
  • use 属性,定义出在进行转换时,应该应用哪个loader

webpack中,还有哪些 loader 能够看这里

Plugin

Loader用于转化模块,Plugin则用来增强 webpack 打包编译时的性能。所以它个别有打包优化,资源管理,注入环境变量等性能。

因为 webpack 也很贴心的给咱们提供了一个 plugin 的配置项,咱们想加强什么性能,去 plugin 外面配置就好了,例如咱们在咱们我的项目中间接定义一些全局变量:

// webpack.config.js
module.exports = {
    ...,
    plugins: [
         new webpack.DefinePlugin({AUTHOR_NAME: JSON.stringify('Lee'),
        })
    ]
}

// index.js
console.log(AUTHOR_NAME); // Lee

webpack中,还有哪些 plugin 能够看这里

Dependency graph(依赖图)

当咱们一个文件依赖另一个文件时,webpack都会将文件视为间接存在“依赖关系”。

举个简略的例子,假如咱们有三个文件,它们的关系如下:

// main.js
import './index.js';

// index.js
import 'plugin.js'
console.log('I am from index.js');

// plugin.js
console.log('I am plugin.js');

咱们来剖析一下:

  1. main.jsindex.jsplugin.js相当于三个模块;
  2. 而后 main.js 引入了 index.jsindex.js 又引入了plugin.js
  3. 因而这三个文件都存在互相援用关系;
  4. 这时造成了这样一个有援用关系的图谱:main.jsindex.jsplugin.js

总结:

webpack会以 entry 为终点,并把它作为依赖图的起始点;而后剖析解决 entry 外面外部的import,一直递归查问相似上述示例的依赖关系,这个过程最初会造成一个具备依赖关系的图谱,这个图谱就是“依赖图”。

webpack会依据这个依赖图,再进一步操作。

⭐️ Chunk

在咱们查阅 webpack 中文文档时,咱们常常会看到 Chunk 这个单词,它并没有被翻译成中文。这是因为 Chunkwebpack打包过程中产生的一个 逻辑概念,须要联合上下文能力了解出它的意思。

Chunkwebapck 外面比拟重要的概念,如果咱们弄懂它,对 webpack 打包整个流程,代码宰割也会有很好的了解

解释

webpack 打包过程中,会将一个或一组模块(咱们下面说到的 webpack 中的任何一个文件,都能够看作是一个模块)组合成一个整体,那么这个整体就能够当做一个 Chunk。一般来说,webpack 打包过程中,有几个 Chunk,最初就会输入几个js 文件。

咱们通过 learn-01 这个案例,外面是最简略的配置,最简略的代码,这样更好了解 Chunk 是什么。

咱们有三个 js 文件:

  • index.js:入口文件
  • async.js:用来异步引入的文件
  • vendors.js:能够把它当做某个第三方依赖文件

文件代码及 webpack 配置如下:

// webpack.config.js
module.exports = {
    entry: {index: './src/index.js'},
    output: {filename: '[name]-bundle.js'
    }
}

// index.js
import './vendors';
import(/* webpackChunkName: "async" */ './async');
console.log('I am from index.js');

// async.js
console.log('I am from async.js');

// vendors.js
console.log('I am from vendors.js');

看到这,咱们能够先猜一下打包进去的文件有几个。如果猜对了,阐明大家对 Chunk 也有肯定理解了。

剖析

咱们先剖析一下打包后的文件及构造:

dist
├── async-bundle.js
└── index-bundle.js

一共输入了两个 js 文件。

通过上文,咱们晓得:

  • 每个文件都能够看成一个module(模块)
  • webpack 打包过程中,会将一个或一组模块组合成一个整体,那么这个整体就能够当做一个Chunk

咱们接着剖析:

  • 通过查看 dist/index-bundle.js 文件,咱们会发现外面蕴含了 非异步引入的 js 文件 :入口文件index.jsvendors.js(还有一些webpack 打包时,本人退出的代码 runtime)。这阐明 index.jsvendors.js 这两个模块组成了一个Chunk。这里咱们称它为chunk[initial]
  • 通过查看 dist/async-bundle.js 文件,外面只蕴含了 async.js 的代码。这阐明 异步引入的模块,会被独自分成一个 Chunk。这里咱们把这个Chunk 称为chunk[no-initial]
  • chunk[initial]chunk[no-initial] 都来自于同一个入口 index.js,所以这两个Chunk 组合起来,能够看成一个 Chunk 组,或者说入口文件会组成一个整体的 Chunk 组。咱们称它为Chunk[index]

好,置信到这里,大家应该对 Chunk 造成有个大抵印象了,咱们再来捋一下这个过程。

webpack编译时,通过咱们的配置:

  1. 会先找到 enrty,有几个entry,就会以这些entry 组成一个 Chunk 组(示例中的Chunk[index]
  2. 再剖析这些 Chunk 组,将入口 js 及这个入口所有相干的依赖模块,组成一个chunk(示例中的chunk[initial]
  3. 如果有异步引入的模块,则这个模块独自再组成一个Chunk(示例中的chunk[no-initial]
  4. 最初打包输入chunk[initial]index-bundle.js)、chunk[no-initial]async-bundle.js

上述示例,chunkmodule(模块)的关系如下:

模式

通过上述剖析,咱们能够晓得,Chunk有两种模式:

  • initial:初始的。咱们的入口 js 及这个入口所有相干的依赖模块,组合成的一个汇合,能够看成一个 Chunk。(即上文index.jsvendors.js 组成的chunk[initial]
  • non-initial:非初始的。阐明它是异步加载的模块,如果咱们在代码中用到了相似 import('./A.js') 的语句,这这个 js 会被独自分成一个异步的 Chunk。(即上文async.js 组成的chunk[no-initial]

如何产生 Chunk

  • 通过 entry 配置,产生一个以入口为整体的 Chunk 组(这个组不会被打包进去,只是会造成这个 Chunk 组)
  • 咱们的入口 js 及这个入口所有相干的依赖模块,产生initial Chunk
  • 通过异步import(),产生non-initial Chunk
  • 通过 webpack 弱小的 代码宰割,产生其余chunk

通过下面的解释与剖析,心愿大家当前用 webpack 时,能够在脑海中有一个 Chunk 造成的大略过程,这对咱们应用代码宰割是非常有帮忙的。

参考文章:揭示外部原理

Bundle

Bundle指的是 webpack 打包后的所有产物。

如果咱们 output 配置打包后输入的文件目录是 dist,咱们的Bundle 就是 dist 文件夹外面的所有产物。

一般来说有几个 Chunk,就会打包出多少个js bundle 文件。

打包过程

浅析

为了大家更好的了解上文解析的概念,咱们浅析 webpack 的打包流程,看看上文的概念体现在哪些流程中。

咱们在终端运行 webpack 后,它会经验以下过程:

  1. 读取咱们指定的配置文件(webpack.config.js
  2. 从入口 entry 开始,剖析咱们的Module(模块)并递归咱们整个我的项目模块间的依赖关系
  3. 加载相应的 Loader,将这些Module(模块)解析成webpack 可能辨认的无效模块,并它们退出到依赖图(Dependency graph
  4. 编译过程会触发多个事件,执行配置的Plugin(插件)
  5. 将剖析好的模块进行分组,造成Chunk
  6. 依据配置文件(output),输入最初的Bundle

上述过程能够看作三个阶段:

  • 初始化阶段(过程 1)
  • 编译阶段(过程 2 - 过程 5)
  • 输入阶段(过程 6)

可总结为下图:

webpack理论打包的过程当然简单得多,这里为了更好的了解,简化了

体验

咱们从理论登程,通过 learn-02 这个案例,用理论代码再深刻体验了解一下 webpack 打包过程,跟波及到的概念。

咱们来看看 learn-02 我的项目构造:

learn-02
├── index.html
├── package-lock.json
├── package.json
├── project.config.js
└── src
    ├── assets
    │   └── style.less
    ├── index.js
    ├── plugin
    │   ├── common.js
    │   ├── index-vendors.js
    │   └── share-vendors.js
    └── share.js

咱们来介绍一下相应的文件:

  • index.html:用来运行咱们打包后的文件,查看成果
  • project.config.jswebpack配置文件(为了区别webpack.config.js,专门另起一个名字)
  • style.less:款式
  • index.js:入口文件
  • share.js:入口文件
  • common.js:寄存专用办法,别离会被两个入口文件,援用 两次
  • index-vendors.js:能够当做 index.js 的一些依赖
  • share-vendors.js:能够当做 share.js 的一些依赖

以下我的项目中的相干代码:

咱们看到 project.config.js 的配置后,当前一样能够猜猜打包后会输入几个文件。

好,当初咱们开始剖析:

1️⃣ 当咱们在终端运行 npm run build 后,webpack会读取咱们指定的文件project.config.js

2️⃣ 从 entry 开始,剖析咱们的模块。咱们的入口有两个 indexshare,所以这时会造成两个Chunk 组:Chunk[index]Chunk[share],并且递归咱们模块相应的依赖关系。

3️⃣ index.js引入了 style.less,所以会加载相应的Loader,将它解析成webpack 能辨认的无效模块,并将其退出到依赖图中。这时会造成两个依赖图:

  • 一个由入口 index.js 及其依赖组成的依赖图(index.js -> style.less,common.js、index-vendors.js
  • 一个由入口 share.js 及其依赖组成的依赖图(share.js -> common.js、share-vendors.js

4️⃣ 而后将这些模块进行分组:

  • 入口 index.js 及其依赖组成的一个chunk[initial-index]
  • 入口 share.js 及其依赖组成的chunk[initial-share]

5️⃣ 发现咱们的配置中,还利用代码宰割把 commonjs 也独立宰割进去,因而它独立组成了一个chunk[initial-common]

6️⃣ 至此,webpack曾经分出了三个chunk

  • chunk[initial-index]
  • chunk[initial-share]
  • chunk[initial-common]

7️⃣ 依据 output 最初输入Bundle

同样,理论打包过程必定要简单得多

最初

  • 这篇文章剖析解说了 webpack 外面波及到的一些概念,尤其是 Chunk 的常识比拟重要。了解了 Chunk,大家肯定会对webpack 有一个更好的了解。心愿读完这篇文章后,咱们在应用 webpack 时,脑海会有一个大抵的过程,跟分辨出大略有几个Chunk
  • 后续的文章会开始教大家怎么配置webpack,如果感兴趣的话能够关注一下这个👉🏻专栏
  • 文章波及到的案例曾经上传到 github,十分欢送 starfork 学习

最初的最初,如果大家感觉文章有帮忙到,创作不易,还请大家多点赞转发,如果有异同点,欢送评论探讨。

正文完
 0