Webpack_1-Concepts

cc.
https://doc.webpack-china.org/concepts/

2018.1.22 星期三 15:30

2018.2.9 星期五 15:30

一 概念

本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。

它是高度可配置的,但是,在开始前你需要先理解四个核心概念:
入口(entry),输出(output),loader,插件(plugins)

本文档旨在给出这些概念的高度概述,同时提供具体概念的详尽相关用例。

1.1 入口(entry)

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

每个依赖项随即被处理,最后输出到称之为 bundles 的文件中,我们将在下一章节详细讨论这个过程。

可以通过在 webpack 配置中配置 entry 属性,来指定一个入口起点(或多个入口起点)。

1.2 出口(output)

output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件。你可以通过在配置中指定一个 output 字段,来配置这些处理过程:

1.3 loader

loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理

本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图可以直接引用的模块。

注意,loader 能够 import 导入任何类型的模块(例如 .css 文件),这是 webpack 特有的功能,其他打包程序或任务执行器的可能并不支持。我们认为这种语言扩展是有很必要的,因为这可以使开发人员创建出更准确的依赖关系图

在更高层面,在 webpack 的配置中 loader 有两个目标。

  1. 识别出应该被对应的 loader 进行转换的那些文件。(使用 test 属性)
  2. 转换这些文件,从而使其能够被添加到依赖图中(并且最终添加到 bundle 中)(use 属性)
    webpack.config.js

    const path = require(‘path’);
    const config = {

    entry: './path/to/my/entry/file.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'my-first-webpack.bundle.js'
    },
    module: {
        rules: [
        { test: /\.txt$/, use: 'raw-loader' }
        ]
    }
    

    };

    module.exports = config;
    以上配置中,对一个单独的 module 对象定义了 rules 属性,里面包含两个必须属性:test 和 use。这告诉 webpack 编译器(compiler) 如下信息:
    “嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 ‘.txt’ 的路径」时,在你对它打包之前,先使用 raw-loader 转换一下。”

重要的是要记得,在 webpack 配置中定义 loader 时,要定义在 module.rules 中,而不是 rules。然而,在定义错误时 webpack 会给出严重的警告。为了使你受益于此,如果没有按照正确方式去做,webpack 会“给出严重的警告”
loader 还有更多我们尚未提到的具体配置属性。

1.4 插件(plugins)

//webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件
const path = require('path');

const config = {
    entry: './path/to/my/entry/file.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'my-first-webpack.bundle.js'
    },
    module: {
        rules: [
        { test: /\.txt$/, use: 'raw-loader' }
        ]
    },
    plugins: [
        new webpack.optimize.UglifyJsPlugin(),
        new HtmlWebpackPlugin({template: './src/index.html'})
    ]
};

module.exports = config;

二 入口起点(Entry Points)

2.1 单个入口(简写)语法

2.2 对象语法

2.3 常见场景

1 分离应用程序

2 多页面应用程序

三 输出(Output)

配置 output 选项可以控制 webpack 如何向硬盘写入编译文件。注意,即使可以存在多个入口起点,但只指定一个输出配置。

3.1 用法

在 webpack 中配置 output 属性的最低要求是,将它的值设置为一个对象,包括以下两点:

  • filename 用于输出文件的文件名。
  • 目标输出目录 path 的绝对路径。

    3.2 多个入口起点

    如果配置创建了多个单独的 “chunk”(例如,使用多个入口起点或使用像 CommonsChunkPlugin 这样的插件),则应该使用占位符(substitutions)来确保每个文件具有唯一的名称。

    3.3 高级进阶

    以下是使用 CDN 和资源 hash 的复杂示例:
    config.js

    output: {

    path: "/home/proj/cdn/assets/[hash]",
    publicPath: "http://cdn.example.com/assets/[hash]/"
    

    }
    在编译时不知道最终输出文件的 publicPath 的情况下,publicPath 可以留空,并且在入口起点文件运行时动态设置。如果你在编译时不知道 publicPath,你可以先忽略它,并且在入口起点设置 webpack_public_path

    webpack_public_path = myRuntimePublicPath
    // 剩余的应用程序入口

四 Loader

loader 用于对模块的源代码进行转换。loader 可以使你在 import 或”加载”模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS文件!

4.1 示例

4.2 使用Loader

1 配置

2 内联

import Styles from 'style-loader!css-loader?modules!./styles.css';

3 CLI

webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'

4.3 Loader特性

4.4 解析Loader

五 插件(Plugins)

插件是 wepback 的支柱功能。webpack 自身也是构建于,你在 webpack 配置中用到的相同的插件系统之上!

插件目的在于解决 loader 无法实现的其他事

5.1 剖析

5.2 用法

5.3 配置

5.4 Node API

六 配置(Configuration)

你可能已经注意到,很少有 webpack 配置看起来很完全相同。这是因为 webpack 的配置文件,是导出一个对象的 JavaScript 文件。此对象,由 webpack 根据对象定义的属性进行解析。

因为 webpack 配置是标准的 Node.js CommonJS 模块,你可以做到以下事情:

  1. 通过 require(…) 导入其他文件
  2. 通过 require(…) 使用 npm 的工具函数
  3. 使用 JavaScript 控制流表达式,例如 ?: 操作符
  4. 对常用值使用常量或变量
  5. 编写并执行函数来生成部分配置

请在合适的时机使用这些特性。
虽然技术上可行,但应避免以下做法:

  • 在使用 webpack 命令行接口(CLI)(应该编写自己的命令行接口(CLI),或使用 –env)时,访问命令行接口(CLI)参数
  • 导出不确定的值(调用 webpack 两次应该产生同样的输出文件)
  • 编写很长的配置(应该将配置拆分为多个文件)

你需要从这份文档中收获最大的点,就是你的 webpack 配置,可以有很多种的格式和风格。但为了你和你的团队能够易于理解和维护,你们要始终采取同一种用法、格式和风格。

接下来的例子展示了 webpack 配置对象(webpack configuration object)如何即具有表现力,又具有可配置性,这是因为配置对象即是代码:

6.1 最简单的配置

6.2 多个Target

6.3 使用其他配置语言

七 模块(Modules)

在模块化编程中,开发者将程序分解成离散功能块(discrete chunks of functionality),并称之为模块。

每个模块具有比完整程序更小的接触面,使得校验、调试、测试轻而易举。 精心编写的模块提供了可靠的抽象和封装界限,使得应用程序中每个模块都具有条理清楚的设计和明确的目的。

Node.js 从最一开始就支持模块化编程。然而,在 web,模块化的支持正缓慢到来。在 web 存在多种支持 JavaScript 模块化的工具,这些工具各有优势和限制。webpack 基于从这些系统获得的经验教训,并将模块的概念应用于项目中的任何文件

7.1 什么是webpack模块

对比 Node.js 模块,webpack 模块能够以各种方式表达它们的依赖关系,几个例子如下:

  1. ES2015 import 语句
  2. CommonJS require() 语句
  3. AMD define 和 require 语句
  4. css/sass/less 文件中的 @import 语句。
  5. 样式(url(…))或 HTML 文件()中的图片链接(image url)

    webpack 1 需要特定的 loader 来转换 ES6 2015 import,然而通过 webpack 2 可以开箱即用。

    7.2 支持的模块类型

    webpack 通过 loader 可以支持各种语言和预处理器编写模块。loader 描述了 webpack 如何处理 非 JavaScript(non-JavaScript) 模块,并且在bundle中引入这些依赖。 webpack 社区已经为各种流行语言和语言处理器构建了 loader,包括:
    CoffeeScript,TypeScript,ESNext (Babel),Sass,Less,Stylus

总的来说,webpack 提供了可定制的、强大和丰富的 API,允许任何技术栈使用 webpack,保持了在你的开发、测试和生成流程中无侵入性(non-opinionated)。

有关完整列表,请参考 loader 列表 或 自己编写。

八 模块解析(Module Resolution)

resolver 是一个库(library),用于帮助找到模块的绝对路径。一个模块可以作为另一个模块的依赖模块,然后被后者引用,如下:

import foo from 'path/to/module'
// 或者
require('path/to/module')

所依赖的模块可以是来自应用程序代码或第三方的库(library)。resolver 帮助 webpack 找到 bundle 中需要引入的模块代码,这些代码在包含在每个 require/import 语句中。 当打包模块时,webpack 使用 enhanced-resolve 来解析文件路径

8.1 webpack中的解析规则

使用 enhanced-resolve,webpack 能够解析三种文件路径:

1 绝对路径

import "/home/me/file";
import "C:\\Users\\me\\file";

由于我们已经取得文件的绝对路径,因此不需要进一步再做解析。

2 相对路径

import "../src/file1";
import "./file2";

在这种情况下,使用 import 或 require 的资源文件(resource file)所在的目录被认为是上下文目录(context directory)。在 import/require 中给定的相对路径,会添加此上下文路径(context path),以产生模块的绝对路径(absolute path)。

3 模块路径

import "module";
import "module/lib/file";

模块将在 resolve.modules 中指定的所有目录内搜索。 你可以替换初始模块路径,此替换路径通过使用 resolve.alias 配置选项来创建一个别名。

  1. 一旦根据上述规则解析路径后,解析器(resolver)将检查路径是否指向文件或目录。如果路径指向一个文件:
    1. 如果路径具有文件扩展名,则被直接将文件打包。
    2. 否则,将使用 [resolve.extensions] 选项作为文件扩展名来解析,此选项告诉解析器在解析中能够接受哪些扩展名(例如 .js, .jsx)。
  2. 如果路径指向一个文件夹,则采取以下步骤找到具有正确扩展名的正确文件:
    1. 如果文件夹中包含 package.json 文件,则按照顺序查找 resolve.mainFields 配置选项中指定的字段。并且 package.json 中的第一个这样的字段 确定文件路径。
    2. 如果 package.json 文件不存在或者 package.json 文件中的 main 字段没有返回一个有效路径,则按照顺序查找 resolve.mainFiles 配置选项中指定的文件名,看是否能在 import/require 目录下匹配到一个存在的文件名。
    3. 文件扩展名通过 resolve.extensions 选项采用类似的方法进行解析。
      webpack 根据构建目标(build target)为这些选项提供了合理的默认配置。

      8.2 解析Loader(Resolving Loader)

      Loader 解析遵循与文件解析器指定的规则相同的规则。但是 resolveLoader 配置选项可以用来为 Loader 提供独立的解析规则。

      8.3 缓存

      每个文件系统访问都被缓存,以便更快触发对同一文件的多个并行或串行请求。在观察模式下,只有修改过的文件会从缓存中摘出。如果关闭观察模式,在每次编译前清理缓存。

有关上述配置的更多信息,请查看解析 API学习。

九 依赖图(Dependency Graph)

任何时候,一个文件依赖于另一个文件,webpack 就把此视为文件之间有依赖关系。这使得 webpack 可以接收非代码资源(non-code asset)(例如图像或 web 字体),并且可以把它们作为依赖提供给你的应用程序。

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

对于 HTTP/1.1 客户端,由 webpack 打包你的应用程序会尤其强大,因为在浏览器发起一个新请求时,它能够减少应用程序必须等待的时间。对于 HTTP/2,你还可以使用代码拆分(Code Splitting)以及通过 webpack 打包来实现最佳优化。

零 构建目标(Targets)

因为服务器和浏览器代码都可以用 JavaScript 编写,所以 webpack 提供了多种构建目标(target),你可以在你的 webpack 配置中设置。

webpack 的 target 属性不要和 output.libraryTarget 属性混淆。有关 output 属性的更多信息,请查看我们的指南。

0.1 用法

要设置 target 属性,只需要在你的 webpack 配置中设置 target 的值。
webpack.config.js

module.exports = {
    target: 'node'
};

在上面例子中,使用 node webpack 会编译为用于「类 Node.js」环境(使用 Node.js 的 require ,而不是使用任意内置模块(如 fs 或 path)来加载 chunk)。

每个target都有各种部署(deployment)/环境(environment)特定的附加项,以支持满足其需求。查看target 的可用值。

0.2 多个Target

尽管 webpack 不支持向 target 传入多个字符串,你可以通过打包两份分离的配置来创建同构的库:
webpack.config.js

var path = require('path');
var serverConfig = {
    target: 'node',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'lib.node.js'
    }
    //…
};

var clientConfig = {
    target: 'web', // <=== 默认是 'web',可省略
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'lib.js'
    }
    //…
};

module.exports = [ serverConfig, clientConfig ];

上面的例子将在你的 dist 文件夹下创建 lib.js 和 lib.node.js 文件。

0.3 资源

一 Manifest

在使用 webpack 构建的典型应用程序或站点中,有三种主要的代码类型:

  1. 你或你的团队编写的源码。
  2. 你的源码会依赖的任何第三方的 library 或 “vendor” 代码。
  3. webpack 的 runtime 和 manifest,管理所有模块的交互。
    本文将重点介绍这三个部分中的最后部分,runtime 和 manifest。

    1.1 Runtime

    1.2 Manifest

    1.3 问题

二 模块热替换(Hot Module Replace)

2.1 这一切是如何运行的?

2.2 在应用程序中

2.3 在编译器中

2.4 在HMT Runtime中

2.5 入门

2.9 18:22

knowledge is no pay,reward is kindness
0%