webpack学习[一]:基本概念

webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具 。当 webpack 处理应用程序时,它会在内部从一个或多个入口点 构建一个依赖图 ,然后将你项目中所需的每一个模块组合成一个或多个 bundles*,它们均为静态资源,用于展示你的内容。

本文将从如下最基础的项目入手,介绍 webpack 的一些基本概念。

一、入口(entry)

官方解释: 入口起点(entry point) 指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。

默认值是 ./src/index.js,可以通过在webpack.config.js文件中配置 entry 属性,来指定一个(或多个)不同的入口起点

1.1 基本语法:entry: string | [string]

  • 单入口:当只有单个入口时,可以按如下方式简写:
JavaScript 复制代码
module.exports = {
  entry: './path/to/my/entry/file.js',
};
  • 多入口 :可以将一个文件路径数组传递给 entry 属性,这将创建一个所谓的 "multi-main entry"
JavaScript 复制代码
module.exports = {
  entry: ['./src/file_1.js', './src/file_2.js'],
  output: {
    filename: 'bundle.js',
  },
};

1.2 对象语法:entry: { <entryChunkName> string | [string] } | {}

JavaScript 复制代码
module.exports = {
  entry: {
    main: './path/to/my/entry/file.js',
  },
};

其实上述第一种写法就是这种写法的一种简写形式。

对于每个入口我们可以使用如下属性进行描述:

  • filename: 指定要输出的文件名称。即入口文件。
  • dependOn: 当前入口所依赖的入口。它们必须在该入口被加载前被加载。
  • import: 启动时需加载的模块。
  • library: 指定 library 选项,为当前 entry 构建一个 library。
  • runtime: 运行时 chunk 的名字。如果设置了,就会创建一个新的运行时 chunk。
  • publicPath: 当该入口的输出文件在浏览器中被引用时,为它们指定一个公共 URL 地址。
JavaScript 复制代码
module.exports = {
  entry: {
    a2: 'dependingfile.js',
    b2: {
      dependOn: 'a2',
      import: './src/app.js',
    },
  },
};

二、输出(output)

output 属性告诉 webpack 在哪里输出它所创建的 bundle ,以及如何命名这些文件。主要输出文件的默认值是 ./dist/main.js,其他生成文件默认放置在 ./dist 文件夹中。

output的基础配置如下:

JavaScript 复制代码
const path = require('path');

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    // 输出文件夹
    path: path.resolve(__dirname, 'dist'),
    // 输出文件名
    filename: 'bundle.js',
  },
};

其中需要引入node.js的path模块对路径进行处理,其中path.resolve是将一系列路径或路径段解析为绝对路径 。所以说 path 的属性值一定要是绝对路径。

当通过多个入口起点(entry point)、代码拆分(code splitting)或各种插件(plugin)创建多个 bundle,应该使用以下一种替换方式,来赋予每个 bundle 一个唯一的名称。

  • 使用入口名称
javascript 复制代码
module.exports = {
  //...
  output: {
    filename: '[name].bundle.js',
  },
};
  • 使用内部chunk id
javascript 复制代码
module.exports = {
  //...
  output: {
    filename: '[id].bundle.js',
  },
};
  • 使用内容产生的hash
javascript 复制代码
module.exports = {
  //...
  output: {
    filename: '[contenthash].bundle.js',
  },
};

当然output中还有很多配置属性,这里就不一一介绍了。

三、loader

webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。

loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。

在 webpack 的配置中,loader 有两个属性:

  1. test 属性,识别出哪些文件会被转换。
  2. use 属性,定义出在进行转换时,应该使用哪个 loader

如下:

JavaScript 复制代码
module.exports = {
  ...
  module: {
    rules: [
        { 
            test: /.txt$/, 
            use: 'raw-loader' 
        }
   ],
  },
  ...
};

该配置告诉 webpack 在打包时,如果遇到 import/require() 语句中存在解析为.txt的路径时,在打包之前,先用 raw-loader 进行转换。

我们也可以针对不同的类型的资源,配置不同的loader:

javascript 复制代码
module.exports = {
  module: {
    rules: [
      { test: /.css$/, use: 'css-loader' },
      { test: /.ts$/, use: 'ts-loader' },
    ],
  },
};

module.rules允许你在 webpack 配置中为某个类型资源指定多个 loader,以加载 CSS 文件为例:

JavaScript 复制代码
module.exports = {
  module: {
    rules: [
      {
        test: /.css$/,
        use: [
          { loader: 'style-loader' },
          {
            loader: 'css-loader',
            options: {
              modules: true
            }
          },
        ]
      }
    ]
  }
};
  • loader 支持链式调用 (从右往左 或 从下往上 )。链中的每个 loader 会将转换应用在已处理过的资源上。一组链式的 loader 将按照相反的顺序执行。链中的第一个 loader 将其结果(也就是应用过转换后的资源)传递给下一个 loader,依此类推。最后,链中的最后一个 loader,返回 webpack 所期望的 JavaScript。例如:上例中 CSS 文件会先被 css-loader 处理,处理后的资源紧接着传给 style-loader 处理。
  • loader 可以是同步的,也可以是异步的。
  • loader 运行在 Node.js 中,并且能够执行任何操作。
  • loader 可以通过 options 对象配置。
  • loader 能够产生额外的任意文件。

四、插件(plugin)

插件可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。

webpack 插件 是一个具有 apply 方法的 JavaScript 对象。apply 方法会被 webpack compiler 调用,并且在 整个 编译生命周期都可以访问 compiler 对象。

想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建一个插件实例

JavaScript 复制代码
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); // 用于访问内置插件

module.exports = {
  module: {
    rules: [{ test: /.txt$/, use: 'raw-loader' }],
  },
  plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};

在上面的示例中,html-webpack-plugin 为应用程序生成一个 HTML 文件,并自动将生成的所有 bundle 注入到此文件中。

五、模式(mode)

设置 mode 参数,你可以启用 webpack 内置在相应环境下的优化。可以设置为 development, production 或 none其中一个,默认值为production。

选项 描述
development 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development. 为模块和 chunk 启用有效的名。
production 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production。为模块和 chunk 启用确定性的混淆名称,FlagDependencyUsagePlugin,FlagIncludedChunksPlugin,ModuleConcatenationPlugin,NoEmitOnErrorsPlugin 和 TerserPlugin 。
none 不使用任何默认优化选项

简单来说,在production模式下,webpack会进行更加细致的打包优化,例如代码压缩等。不同模式下会有不同的配置。

上述入口(entry)、输出(output)、loader、插件(plugin)、模式(mode)是webpack的核心概念。了解了这些,我们在配置文件中进行如下简单的配置:

javascript 复制代码
const path = require('path');

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'main.js',
    },
    mode: 'development'
}

然后在入口文件 ./src/index.js 中写入如下代码:

javascript 复制代码
function f(a, b) {
    return a + b;
}

然后再命令行运行 webpack 进行打包,可以看到 dist 目录下会生成 main.js,内容如下图:

六、模块(Modules)

在模块化编程中,开发者将程序分解为功能离散的 chunk,并称之为 模块。对于 webpack 而言,天生支持以下模块类型:

  • ECMAScript 模块
  • CommonJS 模块
  • AMD 模块
  • Assets
  • WebAssembly 模块

通过 loader 可以使 webpack 支持多种语言和预处理器语法编写的模块。loader 向 webpack 描述了如何处理非原生模块 ,并将相关依赖 引入到你的 bundles中。

webpack 能够解析三种文件路径:

  • 绝对路径
arduino 复制代码
import 'C:\Users\me\file';

此种情况下不需要进行进一步解析。

  • 相对路径
arduino 复制代码
import './file2';

在这种情况下,使用 import 或 require 的资源文件所处的目录,被认为是上下文目录 。在 import/require 中给定的相对路径,会拼接此上下文路径,来生成模块的绝对路径。

  • 模块路径
arduino 复制代码
import 'module';

该种路径与配置项 resolve 相关,这里暂不详细描述。

七、依赖图

当 webpack 处理应用程序时,它会根据命令行参数中或配置文件中定义的模块列表开始处理。 从入口开始,webpack 会递归的构建一个 依赖关系图 ,这个依赖图包含着应用程序中所需的每个模块,然后将所有模块打包为少量的 bundle ------ 通常只有一个 ------ 可由浏览器加载

八、runtime 和 manifest

runtime

runtime,以及伴随的 manifest 数据,主要是指:在浏览器运行过程中,webpack 用来连接模块化应用程序所需的所有代码。它包含:在模块交互时,连接模块所需的加载和解析逻辑。包括:已经加载到浏览器中的连接模块逻辑,以及尚未加载模块的延迟加载逻辑。

manifest

一旦你的应用在浏览器中以 index.html 文件的形式被打开,一些 bundle 和应用需要的各种资源都需要用某种方式被加载与链接起来。在经过打包、压缩、为延迟加载而拆分为细小的 chunk 这些 webpack 优化之后,你精心安排的 /src 目录的文件结构都已经不再存在。所以 webpack 如何管理所有所需模块之间的交互呢?这就是 manifest 数据用途的由来......

当 compiler 开始执行、解析和映射应用程序时,它会保留所有模块的详细要点。这个数据集合称为 "manifest",当完成打包并发送到浏览器时,runtime 会通过 manifest 来解析和加载模块。无论你选择哪种 模块语法,那些 import 或 require 语句现在都已经转换为 __webpack_require__ 方法,此方法指向模块标识符(module identifier)。通过使用 manifest 中的数据,runtime 将能够检索这些标识符,找出每个标识符背后对应的模块。

以上内容均参考 webpack 官方文档,详细内容可见官方文档。

参考: webpack文档

相关推荐
guzicheng19902 天前
webpack 安装笔记:全局 vs 本地,以及一个 PATH 环境变量的坑
前端·webpack
liangshanbo121511 天前
面试题:Webpack 的 publicPath 有什么作用?
前端·webpack·node.js
liangshanbo121513 天前
Webpack vs Vite 底层原理高频追问的问题
前端·webpack·node.js
liangshanbo121513 天前
面试题:AI 对话上下文超限怎么办?
面试·webpack·devops
liangshanbo121514 天前
Webpack 文件指纹面试题整理
前端·webpack·node.js
liangshanbo121514 天前
Webpack 常见 Plugin 面试题
前端·webpack·node.js
liangshanbo121514 天前
Webpack Tree Shaking 面试题整理
前端·webpack·node.js
陪我一起学编程14 天前
vue3笔记
前端·webpack·vue3·vite·脚手架·vue3笔记·组件式
liangshanbo121514 天前
Webpack的分包策略面试题
前端·webpack·node.js
liangshanbo121516 天前
面试题:如何优化 Webpack 的打包速度?
前端·webpack·node.js