构建webpack5.x 知识体系:1、基础之概念

198 阅读14分钟

这是一个系列文的分享记录,本篇主要是webpack的基础概念知识的分享,webpack的介绍,五个核心概念,初始体验webpack,快速上手。webpack文档推荐:webpack.docschina.org/plugins/ter…

本系列知识:

image.png

后续还有相关webpack经典的面试问题解答。

面试中经常会问到的问题

  • 说一下模块打包运行原理?
  • webpack 的构建流程是什么
  • webpack 常见的plugin有哪些
  • webpack如何实现持久化缓存
  • sourceMap是什么吗?
  • Webpack 配置中用过哪些 Loader ?都有什么作用?
  • 简单描述一下编写loader的思路?
  • 如何编写 Plugin ? 介绍一下思路?
  • Loader 和 Plugin 有什么区别?
  • Webpack 层面如何性能优化?
  • tree-shaking 实现原理是怎样的?
  • webpack的作用是什么吗?
  • Webpack 热更新(HMR)是如何实现?
  • Webpack 和 Rollup 有什么相同点与不同点?
  • Webpack5 更新了哪些新特性?
  • Webpack 打包中 Babel 插件是如何工作的?

正式开始:

预备技能

基本nodejs知识和npm指令

注意:版本问题导致不同,本系列实战中用到的webpack版本是:"webpack": "^5.68.0",

"webpack-cli": "^4.9.2",

1. webpack介绍

  • webpack是一个前端资源加载/打包工具,一个静态模块打包器(module bundler)。它将根据模块的依赖关系进行静态分析(分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等)),然后将这些模块按照指定的规则生成对应的静态资源(bundle)。

image.png

构建就是把源代码转换成发布到线上的可执行 JavaScrip、CSS、HTML 代码,包括如下内容。

  • 代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等。
  • 文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等。
  • 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载。
  • 模块合并:在采用模块化的项目里会有很多个模块和文件,需要构建功能把模块分类合并成一个文件。
  • 自动刷新:监听本地源代码的变化,自动重新构建、刷新浏览器。
  • 代码校验:在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过。
  • 自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。

构建其实是工程化、自动化思想在前端开发中的体现,把一系列流程用代码去实现,让代码自动化地执行这一系列复杂的流程。 构建给前端开发注入了更大的活力,解放了我们的生产力。

2、webpack 五个核心概念

Webpack 启动后会从Entry里配置的Module开始递归解析 Entry 依赖的所有 Module。 每找到一个 Module, 就会根据配置的Loader去找出对应的转换规则,对 Module 进行转换后,再解析出当前 Module 依赖的 Module。 这些模块会以 Entry 为单位进行分组,一个 Entry 和其所有依赖的 Module 被分到一个组也就是一个 Chunk。最后 Webpack 会把所有 Chunk 转换成文件输出。 在整个流程中 Webpack 会在恰当的时机执行 Plugin 里定义的逻辑。

2.1 Entry

入口(Entry)指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。

使用:

  1. 1.1 string

    单入口

    • 打包生成一个chunk 输出一个bundle文件

    • 此时chunk默认名字是main

entry:'./src/index.js'

2.1.2 array

多入口

  • 所有入口文件最终只会形成一个chunk,输出一个bundle文件

  • 此时chunk默认名字是main

  • 一般用这种方式 是咋hi又在HMR功能中让html热更新生效~

entry: ["./src/index.js", './src/test.js'],

2.1.3 object

多入口

  • 有几个入口就从生成几个chunk,输出几个bundle文件

  • 此时chunk的名称是key (例如main,test)

entry: {main: './src/index.js', test: './src/test.js'},

特殊-->

entry: {main: ['./src/index.js','./src/add.js'], test: './src/test.js'}, 例如写 dll文件的时候

ps:如果传入一个字符串或字符串数组,chunk 会被命名为 main。如果传入一个对象,则每个属性的键(key)会是 chunk 的名称,该属性的值描述了 chunk 的入口点。

2.2 Output

输出(Output)指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。在 Webpack 经过一系列处理并得出最终想要的代码后输出结果。

可以通过配置 output 选项,告知 webpack 如何向硬盘写入编译文件。注意,即使可以存在多个 entry 起点,但只能指定一个 output 配置。

使用

在 webpack 配置中,output 属性的最低要求是,将它的值设置为一个对象,然后为将输出文件的文件名配置为一个 output.filename

    • filename: 'js/[name].[contenthash:10].js',

    文件名称(指定名称+目录) 目录不写,默认是文件输出到dist目录中

    • path: resolve(__dirname, 'build')

    输出文件路径目录(将来所有资源输出的公共目录)

    • publicPath:'/'

    所有资源引入公共路径前缀()---->'imgs/a.jpg' 打包后编程 '/imgs/a.jpg'

    • chunkFilename:'js/[name]_chunk.js'

    非入口chunk的名称

    • library:'[name]'

    整个库向外暴露的变量名

    • libraryTarget:'window'

    变量名添加到哪 window-->添加到浏览器browser libraryTarget:'global'-->添加到node

libraryTarget使用者的引入方式使用者提供给被使用者的模块的方式
var只能以script标签的形式引入我们的库只能以全局变量的形式提供这些被依赖的模块
commonjs只能按照commonjs的规范引入我们的库被依赖模块需要按照commonjs规范引入
amd只能按amd规范引入被依赖的模块需要按照amd规范引入
umd可以用script、commonjs、amd引入按对应的方式引入
output: {
    filename: 'js/[name].[contenthash:10].js', // 输出名
    path: resolve(__dirname, 'build'),  // 输出路径 __dirname nodejs变量 代表当前文件的目录绝对路径
    library: '[name]_[hash]', //  整个库向外暴露的变量名
    publicPath:'/', //所有资源引入公共路径前缀
    libraryTarget:'window', //  变量名添加到哪 window-->添加到浏览器browser libraryTarget:'global'-->添加到node
     // library: '[name]', // 整个库向外暴露的变量名
    // libraryTarget: 'window', // 变量名添加到哪个上 browser
    // libraryTarget: 'global', // 变量名添加到哪个上 node
    // libraryTarget: 'commonjs',
    chunkFilename:'js/[name]_chunk.js' //非入口chunk的名称 
  }

2.3 Loader

Loader:模块转换器,用于把模块原内容按照需求转换成新内容。比如:让 webpack 能够去处理那些非 JS 的文件,比如样式文件、图片文件(webpack 自身只理解JS)

使用

loader的常用属性
  • test:匹配处理文件的扩展名的正则表达式
  • use:loader名称,就是你要使用模块的名称
  • include/exclude:手动指定必须处理的文件夹或屏蔽不需要处理的文件夹
  • query:为loaders提供额外的设置选项
loader三种写法

1、loader

loader:['style-loader','css-loader']
loader:'css-loader'

2、use

  use:['style-loader','css-loader']

3、use+loader

use: [{
        loader: 'style-loader',
        options: {
            insert:'top'
        }
        },'css-loader'
     ]

2.4 Plugins

插件(Plugins):扩展插件,在 Webpack 构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要的事情。可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。

2.5 Mode

模式(Mode):指示 webpack 使用相应模式的配置。默认是production

选项描述特点
development会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin。能让代码本地调试运行的环境
production会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 TerserPlugin。能让代码优化上线运行的环境
none不使用任何默认优化选项

2.6 resolve

解析

使用

2.6.1 extensions

指定extension之后可以不用在require或是import的时候加文件扩展名,会依次尝试添加扩展名进行匹配

resolve: {
  extensions: [".js",".jsx",".json",".css"]
},
2.6.2 alias

配置别名可以加快webpack查找模块的速度

  • 每当引入bootstrap模块的时候,它会直接引入bootstrap,而不需要从node_modules文件夹中按模块的查找规则查找
const bootstrap = path.resolve(__dirname,'node_modules/_bootstrap@3.3.7@bootstrap/dist/css/bootstrap.css');
resolve: {
  alias:{
       "bootstrap":bootstrap
  }
},
2.6.3 modules
  • 对于直接声明依赖名的模块(如 react ),webpack 会类似 Node.js 一样进行路径搜索,搜索node_modules目录

  • 这个目录就是使用

    resolve.modules
    

    字段进行配置的 默认配置

    resolve: {
    modules: ['node_modules'],
    }
    

    如果可以确定项目内所有的第三方依赖模块都是在项目根目录下的 node_modules 中的话

    resolve: {
    modules: [path.resolve(__dirname, 'node_modules')],
    }
    
2.6.4 mainFields

默认情况下package.json 文件则按照文件中 main 字段的文件名来查找文件

resolve: {
  // 配置 target === "web" 或者 target === "webworker" 时 mainFields 默认值是:
  mainFields: ['browser', 'module', 'main'],
  // target 的值为其他时,mainFields 默认值为:
  mainFields: ["module", "main"],
}
2.6.5 mainFiles

当目录下没有 package.json 文件时,我们说会默认使用目录下的 index.js 这个文件,其实这个也是可以配置的

resolve: {
  mainFiles: ['index'], // 你可以添加其他默认使用的文件名
},
2.6.6 resolveLoader

resolve.resolveLoader用于配置解析 loader 时的 resolve 配置,默认的配置:

module.exports = {
  resolveLoader: {
    modules: [ 'node_modules' ],
    extensions: [ '.js', '.json' ],
    mainFields: [ 'loader', 'main' ]
  }
};

2.7 optimization

使用

2.7.1 splitChunks

可以将node_modules单独打包成一个chunk

minSize:分割的chunk最小大小

minChunks:要提取的chunk最少被引用次数

maxAsyncRequests:按需加载时并行加载的文件的最大数量

maxInitalRequests:入口js文件最大并行请求数量

automaticNameDelimiter:名称连接符

name:可以使用命名规则

cacheGroups分割chunk的组

optimization:{
    splitChunks: {
            chunks: 'all',
            // minSize: 30 * 1024, // 分割的chunk最小为30kb
            // maxSize: 0, // 最大没有限制
            // minChunks: 1, // 要提取的chunk最少被引用1次
            // maxAsyncRequests:5, // 按需加载时并行加载的文件的最大数量
            // maxInitalRequests: 2 ,// 入口js文件最大并行请求数量
            // automaticNameDelimiter:'~' ,// 名称连接符
            // name:true, // 可以使用命名规则
            // cacheGroups:{ // 分割chunk的组
            //     vendors:{
                        vendors: {
            test: /[\/]node_modules[\/]/,
            // 优先级
            priority: -10
          },
          default: {
            // 要提取的chunk最少被引用2次
            minChunks: 2,
            // 优先级
            priority: -20,
            // 如果当前要打包的模块,和之前已经被提取的模块是同一个,就会复用,而不是重新打包模块
            reuseExistingChunk: true
          } 
            //     }
            // }
        }}
2.7.2 minimizer

配置生产环境的压缩方案:

 optimization: {
        minimizer: [
            // terser 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
            // `...`,
            new CssMinimizerWebpackPlugin(),
​
            // 配置生产环境的压缩方案js
            new TerserWebpackPlugin({
                // 用来匹配需要压缩的文件。
                test: /.js(?.*)?$/i,
                // 开启多进程打包 以提高构建速度
                parallel: true
            })
        ],
        minimize: true, // 如果还想在开发环境下启用 CSS 优化,请将 optimization.minimize 设置为 true:
​
        // 可以将node_modules单独打包成一个chunk
        splitChunks: {
            chunks: 'all',
            // minSize: 30 * 1024, // 分割的chunk最小为30kb
            // maxSize: 0, // 最大没有限制
            // minChunks: 1, // 要提取的chunk最少被引用1次
            // maxAsyncRequests:5, // 按需加载时并行加载的文件的最大数量
            // maxInitalRequests: 2 ,// 入口js文件最大并行请求数量
            // automaticNameDelimiter:'~' ,// 名称连接符
            // name:true, // 可以使用命名规则
            // cacheGroups:{ // 分割chunk的组
            //     vendors:{
​
            //     }
            // }
        },

其他:

  • Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
  • context:context即是项目打包的路径上下文,如果指定了context,那么entry和output都是相对于上下文路径的,contex必须是一个绝对路径

    \

3、webpack初体验

1、创建项目

例如创建一个webpack5-demo

mkdir webpack5-demo
cd webpack5-demo
npm init -y

会生成package.json文件

2、安装

npm install webpack webpack-cli -g(全局安装)(即使以前安装过,现在也是可以安装的,会对以前的进行更新)

必须要本地安装,这样构建的项目中package.json,才会有相关依赖,其他的同事协同工作时,才不会出错~~

#本地安装
npm install webpack webpack-cli -D  

查看package.json文件,可以看到此时webpack版本

 "webpack": "^5.68.0",
  "webpack-cli": "^4.9.2",

本人的版本时"^5.68.0", 所以可能跟大家的不一致,大家根据我的后续使用相关配置时,出错也是有可能的。需要注意~~

3、初测webpack

mkdir src

  • 在创建./src/hello.js

    export default 'hello webpack5'
    
  • 在创建./src/index.js

    import hello from './hello.js'
    console.log(hello)
    
  • 运行 webpack

image.png 我们可以看到,项目中多了一个dist文件夹,下边多了一个main.js ,说明默认打包的文件夹叫dist,默认打包的文件main

且编译时,有一个警告!提示mode没有配置。没事不用担心,后边会详细讲解~

那究竟时通过读取哪些文件来打包的呢?我们可以执行以下命令详细查看~
  • webpack --stats detailed

    详细的打包信息

image.png

问题讲解

为什么我的wepack命令打包报错呢?

因为你没有全局安装,webpack命令使用的时全局安装的webpack,不是本地安装的webpack!!!

比如我删除了全局安装

npm uninstall webpack webpack-cli -g

再在命令行中输入webpack

image.png

发现报错!!!

那我就是不安装全局的,怎么打包呢?

那就需要用到npx 工具命令了

npx依托与npm,我们npm现在安装好了,npx也是可以用的。

npx作用是观察当前文件夹是否有要后边配置的命令执行的依赖。没有就会往上一层目录寻找,一直到根目录。

运行npx webpack

image.png

也是可以!!!

当然还是最好安装全局的哈~~

4、创建webpack的配置文件webpack.config.js

所有构建工具都是基于nodejs平台运行 的模块话 默认采用的是 commonjs

在 webpack v5 中,可以无须任何配置(比如上边的webpack初测),然而大多数项目会需要很复杂的设置,这就是为什么 webpack 仍然要支持 配置文件。这比在 terminal(终端) 中手动输入大量命令要高效的多,所以让我们创建一个配置文件:

const path = require('path');// node 内置核心模块,用来处理路径问题。
module.exports = {
    // webpack 配置
    // 入口起点
    entry: './src/index.js',
    // 输出
    output: {
        //  输出路径 __dirname nodejs变量 代表当前文件的目录绝对路径
        path: path.resolve(__dirname, 'build'),
        // 输出ming
        filename: 'built.js'
    },
    // loader 配置
    module: {
        rules: [
​
​
​
        ]
    },
    // plugins 的配置插件 
    plugins: [
​
​
    ],
​
​
    mode: 'development', //开发模式
    // mode: 'production', //生产模式
}

5、创建项目目录

创建src mkdir src

  • 传教index.js

    function add(x, y) {
      return x + y;
    }
    console.log(add(1, 2));
    

创建build mkdir build

  • 创建index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script src="built.js"></script>
</body>
</html>

6、执行webpack命令

运行命令

webpack

image.png

我们看到命令行中出现了 webpack 5.68.0 compiled successfully in 115 ms 表示编译成功了!!!

我们看到输出的内容可以看到,asset built.js 1.24 KiB [emitted] (name: main) 此时chunk默认名字是main, 但是我们在output的输出改了名字,所以输出的资源为 built.js,体积为1.24 KiB

我们可以看到build文件夹下多了一个文件buit.js,内容为:

image.png

在浏览器中打开index.html文件,并打开控制台,我们看到了输出:

image.png

我们还可以通过其他的在线工具,来快速上手一下~~

Tip

createapp.dev 是一个创建自定义 webpack 配置的在线工具。它允许你选择各种特性,其将会被添加到最后的配置文件中。它也会基于生成的 webpack 配置初始化一个示例项目,你可以在浏览器中查看该项目并且下载。

image.png

问题:打包其他类型文件报错

例子1: 在src目录下创建data.json文件

{
    "className": "webapck",
    "des": "构建webpack5.x 知识体系:2、基础之概念"
}

修改src/index.js文件,引入data.json,并进行打印,index.js内容如下:

import dataJson from './data.json';
​
function add(x, y) {
  return x + y;
}
console.log(add(1, 2));
console.log(dataJson, 'dataJson');

然后再进行编译:webpack

image.png

打开浏览器,查看控制台输出:

image.png

文件buit.js,内容为:

image.png

例子2: 我们测试一个css文件

创建一个css文件:

src/index.css、

html,
body {
    padding: 0;
    margin: 0;
    background-color: cadetblue;
}

src/index.less

#title{
    color: chocolate;
}

修改html文件

<!DOCTYPE html>
<html lang="en"><head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head><body>
    <div id="title">webpack5知识</div>
    <script src="built.js"></script>
</body></html>

修改src/index.js,引入刚刚创建的样式文件,先引入css文件:

import dataJson from './data.json';
import './index.css';
// 引入样式文件
function add(x, y) {
  return x + y;
}
console.log(add(1, 2));
console.log(dataJson, 'dataJson');

然后再进行编译: webpack

image.png

咦,报错了!!!

----》编译不通过,因为webpack根本不识别css文件

我们再试试less文件

image.png

😔,还是报错。

当然你还可以试试,其他的sass也是样的报错。还有其他的资源,如图片,字体等。

结论:webpack能够天然的识别js文件和json文件,但是其他的资源编译就会报错。怎么办呢???

这个时候为了webpack能够识别css文件,我们需要配置相应的loader。具体怎么操作,因为文章的篇幅,我们会在一下篇具体讲解~~

感谢

到此,本篇介绍到此结束,之后将陆续整理 webpack 知识体系的内容分享,尽情期待,感谢您的阅读~~