0%

webpack概念整理

概念

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

入口 Entry

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

1
2
3
module.exports = {
entry: './path/to/my/entry/file.js'
};

可以向entry传入一个数组,将会创建多个主入口。在想要将多个依赖文件一起注入,并且将依赖导向到一个chunk时就很有用,比如分离应用程序和第三方库入口。

1
2
3
4
5
6
const config = {
entry: {
app: './src/app.js',
vendors: './src/vendors.js'
}
};

多页面应用程序

1
2
3
4
5
6
7
const config = {
entry: {
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js'
}
};

出口 output

output属性告诉webpack在哪里输出所创建的bundles,以及如何命名这些文件,默认值为”./dist”。基本上,整个应用程序结构,都会被编译到指定的输出路径的文件夹中;

1
2
3
4
5
6
7
8
9
const path = require('path');

module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
}
};

CDN和资源hash的复杂示例

1
2
3
4
output: {
path: "/home/proj/cdn/assets/[hash]",
publicPath: "http://cdn.example.com/assets/[hash]/"
}

这里hash代表文件名为hash值,每次部署会比对hash值,当值不同时,会加载最新的文件。

加载器 loader

loader让webpack处理非JavaScript文件。loader可以将所有类型的文件转换为webpack能够处理的有效模版。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const path = require('path');

const config = {
output: {
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
}
};

module.exports = config;

loader有两个属性:

  1. test 属性,用于表示应该被对应loader进行转换的某个或某些文件,可以模糊匹配;
  2. use 属性,表示进行转换时,应该使用哪个loader。
配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

```js
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true
}
}
]
}
]
}
特性
  • loader 支持链式传递。能够对资源使用流水线(pipeline)。一组链式的 loader 将按照相反的顺序执行。loader 链中的第一个 loader 返回值给下一个 loader。在最后一个 loader,返回 webpack 所预期的 JavaScript。
  • loader 可以是同步的,也可以是异步的。
  • loader 运行在 Node.js 中,并且能够执行任何可能的操作。
  • loader 接收查询参数。用于对 loader 传递配置。
  • loader 也能够使用 options 对象进行配置。
  • 除了使用 package.json 常见的 main 属性,还可以将普通的 npm 模块导出为 loader,做法是在 package.json 里定义一个 loader 字段。
  • 插件(plugin)可以为 loader 带来更多特性。
  • loader 能够产生额外的任意文件。

插件 Plugins

插件可以用于执行范围更广的任务。插件范围包括,从打包优化和压缩,一直到重新定义环境中的变量。想要使用插件,需要require()它,然后添加到plugins数组中,多数插件可以通过选项(option)自定义。可以通过使用new操作符来创建它的实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件

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

模式 module

通过选择developmentproduction之中的一个,来设置mode参数,可以启用相应模式下的内置优化。

1
2
3
module.exports = {
mode: 'production'
};

配置

使用不同语言进行配置

TypeScript

首先使用相关依赖:

1
npm install --save-dev typescript ts-node @types/node @types/webpack

使用的webpack.config.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
import path from 'path';
import webpack from 'webpack';

const config: webpack.Configuration = {
mode: 'production',
entry: './foo.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'foo.bundle.js'
}
};

export default config;

Webpack中的解析规则

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

绝对路径
1
2
3
import "/home/me/file";

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

绝对路径不需要进一步解析。

相对路径
1
2
import "../src/file1";
import "./file2";

使用importrequire的资源文件所在的目录会认为是上下文目录,在其中给定的相对路径会添加此上下文路径,产生模块的绝对路径。

模块路径
1
2
import "module";
import "module/lib/file";

模块会在resolve.modules中指定的所有目录中搜索。

多种配置类型

导出为一个函数

作为导出一个配置对象的替代,可以从webpack配置文件中导出一个函数。该函数在调用时,可以传入两个参数:

环境对象作为第一个参数。一个选项map对象(argv)作为第二个参数,这个对象描述了传递给webpack的选项,并且具有output-filenameoptimize-minimize等key。

1
2
3
4
5
6
7
8
9
10
11
12
-module.exports = {
+module.exports = function(env, argv) {
+ return {
+ mode: env.production ? 'production' : 'development',
+ devtool: env.production ? 'source-maps' : 'eval',
plugins: [
new webpack.optimize.UglifyJsPlugin({
+ compress: argv['optimize-minimize'] // 只有传入 -p 或 --optimize-minimize
})
]
+ };
};
导出一个Promise

webpack将运行由配置文件导出的函数,并且Promise返回。便于需要一部加载所需的配置变量。

1
2
3
4
5
6
7
8
9
10
module.exports = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
entry: './app.js',
/* ... */
})
}, 5000)
})
}
导出多个配置对象

对于针对多个构建目标,打包一个library非常有用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports = [{
output: {
filename: './dist-amd.js',
libraryTarget: 'amd'
},
entry: './app.js',
mode: 'production',
}, {
output: {
filename: './dist-commonjs.js',
libraryTarget: 'commonjs'
},
entry: './app.js',
mode: 'production',
}]

DefinePlugin插件

允许创建一个在编译时可以配置的全局常量。这可能会对开发模式和发布模式的构建允许不同的行为非常有用。

用法

每个传进 DefinePlugin 的键值都是一个标志符或者多个用 . 连接起来的标志符。

  • 如果这个值是一个字符串,它会被当作一个代码片段来使用。
  • 如果这个值不是字符串,它会被转化为字符串(包括函数)。
  • 如果这个值是一个对象,它所有的 key 会被同样的方式定义。
  • 如果在一个 key 前面加了 typeof,它会被定义为 typeof 调用

如:

1
2
3
4
5
6
7
new webpack.DefinePlugin({
PRODUCTION: JSON.stringify(true),
VERSION: JSON.stringify('5fa3b9'),//变量
BROWSER_SUPPORTS_HTML5: true,
TWO: '1+1',//当做代码片段
'typeof window': JSON.stringify('object')
});
Feature Flags(Feature Toggle)

用来标记启用/禁用 生产/开发 构建中的功能。

1
2
3
4
new webpack.DefinePlugin({
'NICE_FEATURE': JSON.stringify(true),
'EXPERIMENTAL_FEATURE': JSON.stringify(false)
});

Process-进程

该对象是一个全局变量,它提供当前Node.js 进程的有关信息,以及控制当前 Node.js 进程。 因为是全局变量,所以无需使用 require()process.env属性返回一个包含用户环境信息的对象,可以对这个对象进行操作。