一、概述
本文将简单介绍webpack的基本用途以及它的四个基本概念Entry(入口文件)、Loader(加载器)、Output(输出文件)和Plugins(插件)。
Webpack
是一个Javascript程序的静态资源打包器, 递归地分析文件并建立一个依赖关系图, 然后把所有模块和他们的依赖打包进一个或者多个文件中, 它是高度可配置的,下面是它的四个核心概念。Entry(入口文件)
指定了webpack该从那个文件开始去分析和建立依赖关系, webpack可找出文件的直接依赖和间接依赖。每个依赖会被打包进一个成为bundle的文件中。我们可以通过设置配置文件中的entry
属性来指定一个或者多个Entry(入口文件)。
webpack.config.js
module.exports = {
entry: './path/to/my/entry/file.js'
};
- Output(输出文件)
output
属性规定了webpack该在哪里存放它打包好的bundle文件, 以及规定这些文件的的命名。
webpack.config.js
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'
}
};
- Loaders(加载器)
Loaders使webpack能够处理除了Javascript之外的文件(如css), webpack本身只支持处理Javascript文件, loaders能将特殊的文件转化为webpack能够处理的有效的模块(通过module选项)。
在webpack配置文件中, loaders主要有两方面的配置:
指定哪些类型的文件应该由对应的loader来处理(通过
test
属性正则匹配文件)-
转换这些文件使之能够被添加到依赖图中(
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;
上述配置中使用了包含两个必要子属性
test
和use
的rules属性, 规定了 当webpack遇到.txt
文件时, 先使用raw-loader
进行转换, 再打包到bundle文件中。注意上述rules
是在module
下定义的。
- Plugins(插件)
loaders用来处理特定的文件, 而plugins可以用来执行更广泛、更复杂的任务。plugins的能力范围从优化、压缩到定义environment-like的变量。plugins接口是非常强大的。
为了使用插件, 必须使用require()
方法引入它, 并把它加入到plugins
数组中, 大多数插件可以通过指定选项来进行自定义配置。由于你可以在一个配置文件中多次使用同一类型的插件用作不同的目的, 所以你使用插件之前需要使用new
操作符创建它的实例对象。
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(入口文件)
有许多方法可以在配置文件中定义Entry Points
单入口(简写)语法
用法: entry: string|Array<string>
webpack.config.js
const config = {
entry: './path/to/my/entry/file.js'
};
module.exports = config;
它是以下配置的简写:
webpack.config.js
const config = {
entry: {
main: './path/to/my/entry/file.js'
}
};
module.exports = config;
若使用数组, 那就变成了多入口文件配置。
对象语法
用法: entry: {[entryChunkName: string]: string|Array<string>}
webpack.config.js
const config = {
entry: {
app: './src/app.js',
vendors: './src/vendors.js'
}
};
这种方式是最灵活的, 可伸缩的。Scalable webpack configurations/可伸缩webpack配置 指的是可重用的,并且可以与其他局部配置文件进行合并的配置。这是一个流行的技术用来分离不同的环境或运行时的配置文件, 然后通过webpack-merge
等工具进行配置的整合。
应用场景
下面是一些有关entry配置的真实用例
1. 分离应用程序(app)和第三方库(vendor) 入口
webpack.config.js
const config = {
entry: {
app: './src/app.js',
vendors: './src/vendors.js'
}
};
这是什么?
从表面上看,这告诉我们 webpack 从 app.js 和 vendors.js 开始创建依赖图(dependency graph)。这些依赖图是彼此完全分离、互相独立的(每个 bundle 中都有一个 webpack 引导(bootstrap))。这种方式比较常见于,只有一个入口起点(不包括 vendor)的单页应用程序(single page application)中。
为什么?
此设置允许你使用 CommonsChunkPlugin 从「应用程序 bundle」中提取 vendor 引用(vendor reference) 到 vendor bundle,并把引用 vendor 的部分替换为 webpack_require() 调用。如果应用程序 bundle 中没有 vendor 代码,那么你可以在 webpack 中实现被称为长效缓存的通用模式。
2. 多页面应用程序
webpack.config.js
const config = {
entry: {
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js'
}
};
这是什么?
告诉 webpack 需要 3 个独立分离的依赖图(如上面的示例)
为什么?
在多页应用中,每当页面跳转时,服务器将为你获取一个新的 HTML 文档。页面重新加载新文档,并且资源被重新下载。然而,这给了我们特殊的机会去做很多事:
- 使用 CommonsChunkPlugin 为每个页面间的应用程序共享代码创建 bundle。由于入口起点增多,多页应用能够复用入口起点之间的大量代码/模块,从而可以极大地从这些技术中受益
根据经验:每个 HTML 文档应该只使用一个入口文件
三、Loaders(加载器)
Loader用于对模块的源代码进行转换。 loader可以使你在import
或"加载"模块时预处理文件。 因此,loader类似于其他构建工具中"任务(task)", 并提供了处理前端构建步骤的强大方法。 loader可以将文件从不同的语言(如TypeScript)转换为 JavaScript, 或将内联图像转换为DataURL。 loader甚至允许你直接在JavaScript模块中import
CSS文件
例子
如果要求webpack处理css和Typescript文件, 首先需要从npm安装对应的loader
npm install --save-dev css-loader
npm install --save-dev ts-loader
然后在配置文件中进行设置
webpack.config.js
module.exports = {
module: {
rules: [
{ test: /\.css$/, use: 'css-loader' },
{ test: /\.ts$/, use: 'ts-loader' }
]
}
};
loaders使用方式
- Configuration(推荐): 在配置文件中进行配置
- Inline: 在每个
import
语句中额外指明所使用的loader - CLI: 在Shell命令中指定loader
1. Configuration方式配置
module.rules
允许你在webpack配置中指定多个loader。 这是展示loader的一种简明方式, 并且有助于使代码变得简洁。 同时让你对各个loader有个全局概览
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: ['style-loader'](/loaders/style-loader) },
{
loader: ['css-loader'](/loaders/css-loader),
options: {
modules: true
}
}
]
}
]
}
2. Inline方式配置
可以在import
语句或任何等效于"import"的方式中指定loader。 使用!
将资源中的loader分开。 分开的每个部分都相对于当前目录解析
import Styles from 'style-loader!css-loader?modules!./styles.css';
这可以覆盖Configuration中的loader设置,并且可以?
进行传参参数可以使JSON
对象
3. CLI方式配置
命令行示例
webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'
上述命令使用对.jade
文件使用jade-loader
, 对css
文件使用style-loader
和css-loader
任何情况下,都应该尽量使用配置文件的方式来配置loader
loader特性
- 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 能够产生额外的任意文件
loader 通过(loader)预处理函数,为 JavaScript 生态系统提供了更多能力。 用户现在可以更加灵活地引入细粒度逻辑,例如压缩、打包、语言翻译和其他更多。
四、Output(输出文件)
配置output选项可以控制webpack如何向硬盘写入编译文件。注意, 虽然可以存在多个入口文件, 但只能指定一个输出配置
用法
设置output的最低需求是在配置文件的output属性值中指定以下两个选项:
-
filename
: 输出文件的文件名 -
path
: 输出文件的绝对路径
webpack.config.js
const config = {
output: {
filename: 'bundle.js',
path: '/home/proj/public/assets'
}
};
module.exports = config;
该配置会在/home/proj/public/assets
中生成bundle.js
文件
多入口文件配置
如果配置创建了多个单独的"chunk"(例如,使用多个入口文件或使用像CommonsChunkPlugin这样的插件), 则应该使用占位符(substitutions)来确保每个文件具有唯一的名称。
{
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name].js',
path: __dirname + '/dist'
}
}
// 写入磁盘: ./dist/app.js, ./dist/search.js
高级用法
CDN上使用资源Hash
output: {
path: "/home/proj/cdn/assets/[hash]",
publicPath: "http://cdn.example.com/assets/[hash]/"
}
在编译时不知道最终输出文件的publicPath
的情况下,publicPath
可以留空, 并且在入口起点文件运行时使用__webpack_public_path__
动态设置
__webpack_public_path__ = myRuntimePublicPath
// 剩余的应用程序入口
五、Plugins(插件)
插件是webpack中的支柱功能, webpack本身也是构建于同一套插件系统之上, 插件是为了解决loader无法解决的事情
剖析
webpack插件是一个具有apply
方法的javascript对象。 这个apply
被webpack compiler
调用, 从而使插件获得对webpack编译的整个生命周期的访问能力
ConsoleLogOnBuildWebpackPlugin.js
function ConsoleLogOnBuildWebpackPlugin() {
};
ConsoleLogOnBuildWebpackPlugin.prototype.apply = function(compiler) {
compiler.plugin('run', function(compiler, callback) {
console.log("The webpack build process is starting!!!");
callback();
});
};
用法
由于插件可以携带参数/选项,你必须在 webpack 配置中, 向 plugins 属性传入 new 实例
根据使用webpack的方式,插件也有多种使用方式
Configuration方式使用
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
const webpack = require('webpack'); //to access built-in plugins
const path = require('path');
const config = {
entry: './path/to/my/entry/file.js',
output: {
filename: 'my-first-webpack.bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: 'babel-loader'
}
]
},
plugins: [
new webpack.optimize.UglifyJsPlugin(),
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
module.exports = config;
Node API方式使用
即便使用 Node API,用户也应该在配置文件中配置插件, compiler.apply 并不是推荐的使用方式
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) {
// ...
});