欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

webpack配置及使用

程序员文章站 2022-03-06 13:56:45
...

概念

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

  • 入口retry
  • 输出output
  • loader
  • 插件plugins
入口(retry)

入口起点(retry point)指示webpack应该使用哪个模块来作为构建其内部依赖图的开始.进入入口起点后,webpack会找出有哪些模块和库是入口起点(直接和间接)依赖的.每个依赖项随机被处理,最后输出到称之为bundles的文件中.

  • 单个入口语法(字符串形式)
const config = {
  entry: './path/to/my/entry/file.js'
};
module.exports = config;
相当于
const config = {
  entry: {
    main: './path/to/my/entry/file.js'
  }
};

当向entry传递一个数组时,将创建"多个主入口".在需要多个依赖文件一起注入,并且将它们的依赖导向到一个"chunk"时.

  • 对象语法
const config = {
  entry: {
    app: './src/app.js',
    vendors: './src/vendors.js'
  }
};

对象语法是应用程序中定义入口的最可拓展的方式.

  • 常见场景
    分离 应用程序(app) 和 第三方库(vendor) 入口.
    这种方式比较常见于只有一个入口起点(不包括vendor)的单页应用程序(spa)中.此设置允许你使用CommonsChunkPlugin从[应用程序bundle]中提取vendor
    引用(vendor reference)到vendor bundle.
  • 多页面应用程序
const config = {
  entry: {
    pageOne: './src/pageOne/index.js',
    pageTwo: './src/pageTwo/index.js',
    pageThree: './src/pageThree/index.js'
  }
};

这是告诉webpack需要3个独立分离的依赖图

输出(output)

即使可以存在多个入口起点,但只能指定一个输出配置

  • 用法
    最低要求是将输出的值设置为一个对象,包括以下两个属性
    filename用于输出文件的文件名
    目标输出目录path的绝对路径
  • 多个入口起点
    如果配置了创建了多个单独的"chunk",则应该使用占位符来确保每个文件具有唯一的名称
{
  entry: {
    app: './src/app.js',
    search: './src/search.js'
  },
  output: {
    filename: '[name].js',
    path: __dirname + '/dist'
  }
}
  • 高级进阶
output: {
  path: "/home/proj/cdn/assets/[hash]",
  publicPath: "http://cdn.example.com/assets/[hash]/"
}

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

loader

loader用于对模块的源代码进行转换.loader可以使你在import或"加载"模块时预处理文件.因此,loader类似于其它构建工具中的"任务(task)",并提供了处理前端构建步骤的强大方法.

  • 使用loader
    • 配置(推荐): 在webpack.config.js文件中指定loader
    • 内联: 在每个import语句中显示指定loader
    • CLI: 在shell命令中指定它们
  • 配置[Configuration]
    module.rules荀彧你在webpack设置中指定多个loader.
  • 内联
    可以在import语句或任何等效于"import"的方式中指定loader.使用!将资源中的loader分开
import Styles from 'style-loader!css-loader?modules!./styles.css';

通过前置所有规则以及使用!,可以对应覆盖到配置的任意loader

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

这会对 .jade 文件使用 jade-loader,对 .css 文件使用 style-loadercss-loader

  • loader特新
    • 支持链式传递,能够对资源使用流水线
    • 可以是同步的,也可以是异步的
    • 运行在node.js中,并且能够执行任何可能的操作
    • 接受查询参数,用于传递配置
    • 也能够使用options对象进行配置
    • 处理使用package.json常见的main属性,可以将普通的npm模块导出为loader,做法是在package.json中定义一个loader字段
    • 插件可以为loader带来更多特性
    • loader能够产生额外的任意文件
  • 解析loader
    loader遵循标准的模块解析,多数情况下,loader将从模块路径解析.
    loader模块需要导出为一个函数,并且使用Node.js兼容的JavaScript编写.通常使用npm进行管理,但是也可以将自定义loader作为应用程序中的文件
插件(Plugins)

webpack的支柱功能,其自身也是构建于你在webpack配置中用到的相同的插件系统之上
插件目的在于解决与loader无法实现的其他事

  • 剖析
    webpack插件是一个具有apply属性的JavaScript对象.apply属性会被webpack compiler调用,并且compiler对象可在整个编译声明周期访问
  • 用法
    由于插件可以携带参数/选项,你必须在webpack配置中,向plugins属性传入new实例
  • 配置
plugins: [
    new webpack.optimize.UglifyJsPlugin(),
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
  • Node API
    即便使用Node API,用户也应该在配置中传入plugins属性.webpack compiler并不是推荐的使用方式
some-node-script.js

const webpack = require('webpack'); //访问 webpack 运行时(runtime)
  const configuration = require('./webpack.config.js');

  let compiler = webpack(configuration);
  compiler.apply(new webpack.ProgressPlugin());

  compiler.run(function(err, stats) {
    // ...
  });
配置

webpack配置是标准的Node.js CommonJS模块:

  • 通过require(...)导入其他文件
  • 通过require(...)使用npm的工具函数
  • 使用JavaScript控制流表达式,例如三元表达式
  • 对常用值使用常量或变量
  • 编写并执行函数来生成部分配置
模块

在模块化编程中,开发者将程序分解成离散功能块,并称之为模块

  • 什么是webpack模块
    对比Node.js模块,webpack模块能够以各种方式表达它们的依赖关系
    • ES2015import语句
    • CommonJSrequire()语句
    • AMDdefinerequire语句
    • css/sass/less文件中的@import语句
    • 样式(url(...))或html文件(<img src=...>)中的图片链接
  • 支持的模块类型
    webpack通过loader可以支持各种语句和预处理器编写模块.loader描述了webpack如何处理 非JavaScript模块,并且在bundle中引入这些依赖
模块解析

resolver是一个库,用于帮助找到模块的绝对路径.一个模块可以作为另一个模块的依赖模块,然后被后者引用.当打包模块时,webpack使用enhanced-resolve来解析文件路径

  • webpack中的解析规则
    使用enhanced-resolve,webpack能够解析三种文件路径
    1 绝对路径
    2 相对路径
    这种情况下,使用importrequire的资源文件所在的目录认为是上下文目录.在import/require中给定的相对路径会添加上下文路径来产生模块的绝对路径
    3 模块路径
    模块将在resolve.modules中指定的所有目录内搜索.可以使用resolve.alias配置选项来创建一个别名.
    一旦根据上述规则解析路径后,解析器(resolve)将检查路径是否指向文件或目录.
    如果路径指向一个文件:

    • 如果路径具有文件拓展名,则被直接打包
    • 否则,将使用resolve.extensions选项作为文件拓展名来解析(接受哪些拓展名)

    如果路径指向一个文件夹:

    • 如果文件夹包含package.json文件,则按照顺序查找resolve.mainFiles配置选项中指定的字段.并且package.json中的第一个这样的字段确定文件路径.
    • 如果package.json文件不存在或者文件中的main字段没有返回一个有效路径,则按照顺序查找resolve.mainFiles配置选项中指定的文件名,看是否能在import/require目录下匹配到一个存在的文件名
    • 文件拓展名通过resolve.extensions选择采用类似的方法进行解析

webpack根据构建目标为这些选项提供了合理的默认配置

构建目标

因为服务器和浏览器代码都可以使用JavaScript编写,所以webpack提供了多种构建目标.

  • 用法
    只需要在webpack配置中设置target的值
module.exports = {
  target: 'node'
};

这个例子中,使用nodewebpack会编译为用于[类 Node.js]环境(使用Node.js的require,而不是使用任意内置模块(如fspath)来加载chunk)

  • 多个Target
    尽管webpack不支持向target传入多个字符串,但是可以通过打包两份分离的配置来创建同构的库
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.jslib.node.js文件

Manifest

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

  1. 编写的源码
  2. 源码依赖的任何第三方的library或vendor代码
  3. webpack的runtime和manifest,管理所有模块的交互
  • Runtime
    包含:在模块交互时,连接模块所需的加载和解析逻辑.包括浏览器中的已加载模块的连接以及懒加载模块的执行逻辑
  • Manifest
    当编译器开始执行,解析和映射应用程序时,它会保留所有模块的详细要点,这个数据集合成为"Manifest",当完成打包发送到浏览器时,会在运行时通过Manifest来解析和加载模块.无论选择哪种模块语法,那些语句现在都已经转换为__webpack_require__方法,此方法指向模块标识符.通过使用manifest中的数据,runtime将能够查询模块标识符,检索出背后对应的模块.
模块热替换

在应用程序运行过程中替换,添加或删除模块,而无需重新加载整个页面.主要通过以下几种方式:

  1. 保留在完全重新加载页面时丢失的应用程序状态
  2. 只更新变更内容,以节省宝贵的开发时间
  3. 调整样式更加快速