写这篇文章是为了让本身在自学 webpack
的过程当中有所产出,因而边读 webpack 中文文档 边写下了这篇文章,里面的不少实例都是直接挪用的文档中的实例,但在一些概念的理解上我加入了本身的想法「未必精确」,因此读的时候要抱着「怀疑的态度」。javascript
文章内容不只仅是简单的「概念堆叠」,还有一些「重点」概念的「深刻理解」,不过篇幅有限我不但愿这篇文章变成一份冗长的伪文档,因此所有的内容都是围绕 webpack
的 4个 核心概念延展开来的,每一个配置后面我都会尽可能跟上一个实例以更加形象的展现配置的具体做用。css
站在个人角度上,读完这篇文章并不能让你精通 webpack 可是理解 webpack 中的重要概念,本身编写一个 webpack.config.js 配置文件仍是能够的。html
本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(static module bundler)。在 webpack 处理应用程序时,它会在内部建立一个依赖图(dependency graph),用于映射到项目须要的每一个模块,而后将全部这些依赖生成到一个或多个bundle。前端
来自 webpack 中文文档vue
目前都是使用一些成熟的 CLI
工具,通常都内置 webpack
因此我对 webpack
的认知一直比较少,只是大概的了解它是用来管理项目中的 .js
文件依赖,而后打包整个项目的。java
对应属性:entry
默认值:./src/index.js
node
做用说明: 用来规定 webpack
应该使用哪一个模块做为构建内部依赖图的起点。 webpack
会找出全部「入口模块」(直接或间接)依赖的「模块」和 [library]。webpack
代码示例:git
// weboack.config.js
module.exports = {
entry: './path/to/entry/file.js'
}
复制代码
对应属性:output
主输出文件默路径:./dist/main.js
其余文件默认路径:./dist/<filename>
github
做用说明: 用来规定 webpack
在那里输出 bundles
以及如何命名这些文件。
// weboack.config.js
const path = require('path') // Node.js 核心模块,用于操做文件路径
代码示例:
module.exports = {
entry: './path/to/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '<WhateverYouLike>.js'
}
}
复制代码
对应属性:module->rules
做用说明: 做为开箱即用的自带特性,webpack
自身只支持处理 JavaScript
文件。而 loader
可以让 webpack
处理那些非 JavaScript
文件,而且先将它们转换为有效「模块」,而后添加到「依赖图」中,提供给应用程序使用。
属性特征:
test
: 利用「正则表达式」规定 loader
用于哪些或哪一个文件。use
: 规定运行时使用哪一个 loader
。代码示例:
// webpack.config.js
const path = require('path')
module.exports = {
...
module: {
rules: [
{
test: /\.txt$/,
use: 'raw-loader'
}
]
}
}
复制代码
代码做用: 当运行包含 .txt 文件的 require() 或 import 语句时,在它打包以前,先使用 raw-loader 转换。
对应属性:plugings
做用说明: 打包优化、资源管理和注入环境变量。
代码实例:
// webpack.config.js
const HtmlWebpackPlugn = require('html-webpack-plugin') // 提早经过 npm 安装
const webpack = require('webpack') //用于访问内置插件
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
}
复制代码
// webpack.config.js
module.exports = {
entry: {
main: './path/to/entry/file.js'
}
}
// 可简写为以下形式
module.exports = {
enrty: './path/to/enrty/file.js'
}
/* * 当你须要为只有一个入口的应用程序或工具(library)快速设置 webpack 配置时, * 简写会是个很不错的选择。然而,使用此语法在扩展配置时有失灵活性。 */
复制代码
思考:当你向 entry
传入一个数组时会发生什么? 解释:向 entry
传入「文件路径数组」将建立「多个主入口」。在你想要多个依赖文件一块儿注入,而且将它们的依赖导向到一个 chunk
时,传入数组的方式就颇有用。
用法:entry: {<enrtyChunkName: String>: <Path: String | Array>}
// webpack.config.js
module.exports = {
entry: {
app: './src/app.js',
vendors: './src/vendors.js'
}
}
// 对象语法会比较繁琐。然而,这是应用程序中定义入口的最可扩展的方式。
复制代码
// webpack.config.js
module.exports = {
entry: {
app: './src/app.js'
vendors: './src/vendors.js'
}
}
/* * webpack 从 app.js 和 vendors.js 开始建立依赖图。 * 这些依赖图是彼此彻底分离、互相独立的(每一个 bundle 中都有一个 webpack 引导)。 * 这种方式比较常见于,只有一个入口起点(不包括 vendor)的单页应用程序中。 * * 此设置容许你使用 CommonsChunkPlugin 从应用程序依赖图中提取 vendor 到 vendor 依赖图,并把引用 vendor 的部分替换为 __webpack_require__() 调用。 * 若是应用程序依赖图中没有 vendor 代码,那么你能够在 webpack 中实现被称为长效缓存的通用模式。 * 说实话,目前看不懂上面这段话,因此也不晓得怎么通俗的表述。 */
复制代码
// webpack.config.js
module.exports = {
entry: {
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js'
}
}
/* * webpack 分离 3 个的依赖图 * * 在多页应用中,每当页面跳转时服务器将为你获取一个新的 HTML 文档。 * 页面从新加载新文档,而且资源被从新下载。这给了咱们特殊的机会去作不少事: * 使用 CommonsChunkPlugin 使全部页面的应用程序共享代码建立依赖图, * 入口增多,多页应用可以复用不一样入口的大量重复代码/模块。 */
复制代码
注意,即便能够存在多个入口,但只配置一个出口设置。
在 webpack
中配置 output
的最低要求是,将它的值是一个包括如下两点的对象:
filename
: 输出文件的文件名。path
: 输出目录的绝对路径。// webpack.config.js
module.exports = {
output: {
filename: '<WhateverYouLike>.js',
path: '/path/to/project'
}
}
// 此配置将一个单独的 .js 文件输出到 /path/to/project 目录中。
复制代码
若是配置建立了多个单独的入口,则应该使用 占位符 来确保每一个文件具备惟一的名称。
// webpack.config.js
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name].js',
path: __dirname + '/dist'
}
};
// 写入到硬盘:./dist/app.js, ./dist/search.js
复制代码
内部ID:[id]
入口名称:[name]
基于构建的hash(每次构建都会改变):[hash]
基于内容的hash(文件内容改变才会改变):[chunkhash]
官网所谓高级进阶其实就是利用哈希占位符构建随版本迭代的文件命名方式这里不展现了。
比较有用的是如何动态设置 publicPath
:
output.publicPath
: 全部资源的基础路径,它被称为公共路径,以 /
结束,示例:// webpack.config.js
module.exports = {
...
output: {
publicPath: '/assets/',
chunkFilename: '[id].chunk.js'
}
};
/* * HTML loader 输入出:<link href="/assets/spinner.gif" /> * CSS:background-image: url(/assets/spinner.gif); * 静态资源最终访问路径 = output.publicPath + loader 或插件等配置路径 */
复制代码
devServer.publicPath
: 肯定从哪里提供 bundle假设服务器运行在 http://localhost:8080
而且 output.filename
被设置为 bundle.js
。默认 publicPath
是 /
,因此你的包能够经过 http://localhost:8080/bundle.js
访问。
能够修改经过 devServer.publicPath
来修改请求资源时的服务器前缀,示例:
// webpack.config.js
module.exports = {
...
devServer: {
publicPath: '/assets/'
}
};
/* * 如今能够经过 http://localhost:8080/assets/bundle.js 访问 bundle。 * 确保 publicPath 老是以斜杠(/)开头和结尾。 * devServer.publicPath 也能够是一个完整的 URL。 * 通常状况下都要保证 devServer.publicPath 与 output.publicPath 保持一致。 */
复制代码
devServer.contentBase
: 告诉服务器从哪里提供内容,只有在提供静态文件时才须要默认状况下,将使用当前工做目录做为提供内容的目录,可是你能够修改成其余目录,示例:
// webpack.config.js
module.exports = {
...
devServer: {
// 推荐使用绝对路径。
contentBase: path.join(__dirname, 'public')
}
};
// 也能够从多个目录提供内容
module.exports = {
...
devServer: {
contentBase: [path.join(__dirname, 'public'), path.join(__dirname, 'assets')]
}
};
// 具体做用不详,官网并无给出说明也懒得查了
复制代码
// webpack.config.js
...
const BASE_URL = process.env.NODE_ENV === 'production'
? '/'
: '/'
module.exports = {
...
publicPath: BASE_URL,
...
}
// 方法来自 iview-admin vue.config.js
// 我不知道我理解的动态设置对不对,不过官网给的 __webpack_public_path__ 我没看明白
复制代码
loader
用于对模块的源代码进行转换,可使你在「载入」模块时预处理文件。
loader
相似于其余构建工具中「任务(task
)」,提供了处理前端构建步骤的方法。
loader
能够将文件从不一样的语言(如 TypeScript
)转换为 JavaScript
,或将内联图像转换为 data URL
。容许你直接在 JavaScript
模块中 import CSS
文件。
配置 loader
使 webpack
加载 CSS
文件,或者将 TypeScript
转为 JavaScript
。
首先安装相对应的 loader
:
npm install --save-dev css-loader
npm install --save-dev ts-loader
复制代码
而后配置 webpack
对每一个 .css
使用 css-loader
,全部 .ts
文件使用 ts-loader
:
// webpack.config.js
module.exports = {
...
module: {
rules: [
{ test: /\.css$/, use: 'css-loader' },
{ test: /\.ts$/, use: 'ts-loader' }
]
}
}
复制代码
webpack.config.js
文件中指定 loader
。(推荐)前面展现过了,这里就不重复了。
import
语句中显式指定 loader
。能够在 import
语句或任何等效于 import
的方式中指定 loader
。使用 !
将资源中的 loader
分开。分开的每一个部分都相对于当前目录解析,示例:
import Styles from 'style-loader!css-loader?modules!./styles.css';
复制代码
shell
命令指定 loader
。webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'
复制代码
loader
支持链式传递。loader
链中每一个 loader
,都对前一个 loader
处理后的资源进行转换。loader
链会按照相反的顺序执行。第一个 loader
将(应用转换后的资源做为)返回结果传递给下一个 loader
,依次这样执行下去。最终,在链中最后一个 loader
,返回 webpack
所预期的 JavaScript
。loader
能够是同步的,也能够是异步的。loader
运行在 Node.js
中,而且可以执行任何可能的操做。loader
接收查询参数,用于对 loader
传递配置。loader
也可以使用 options 对象进行配置。package.json
常见的 main
属性,还能够将普通的 npm
模块导出为 loader
,作法是在 package.json
里定义一个 loader
字段。loader
带来更多特性。loader
可以产生额外的任意文件。loader
遵循标准的 模块解析。多数状况下,loader
将从模块路径(一般将模块路径认为是 node_modules
)解析。
loader
模块须要导出为一个函数,而且使用 Node.js
兼容的 JavaScript
编写。一般使用 npm
进行管理,可是也能够将自定义 loader
做为应用程序中的文件。按照约定,loader
一般被命名为 xxx-loader
(例如 json-loader
)。有关详细信息,请查看 如何编写 loader?。
插件是 webpack
的支柱功能。webpack
自身也构建于插件系统之上。
插件目的在于解决 loader
没法实现的其余事。
webpack
插件是一个具备 apply
方法的 JavaScript
对象。apply
属性会被 webpack compiler
调用,而且 compiler
对象可在整个编译生命周期访问。
// ConsoleLogOnBuildWebpackPlugin.js
const pluginName = 'ConsoleLogOnBuildWebpackPlugin';
class ConsoleLogOnBuildWebpackPlugin {
apply(compiler) {
// compiler hook 的 tap 方法的第一个参数,应该是驼峰式命名的插件名称。
// 建议为此使用一个常量,以便它能够在全部 hook 中复用。
compiler.hooks.run.tap(pluginName, compilation => {
console.log('webpack 构建过程开始!');
});
}
}
// 插件编写属于比较深刻的内容,这里不过多探讨,目前仅须要知道实现原理便可
复制代码
因为插件能够携带参数/选项,你必须在 webpack
配置中,向 plugins
属性传入 new
实例。
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin') //经过 npm 安装
const webpack = require('webpack') //访问内置的插件
const path = require('path')
module.exports = {
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 HtmlWebpackPlugin({template: './src/index.html'})
]
}
复制代码
对应属性:mode | String
做用说明: 经过将 mode
参数设置为 development
, production
或 none
,能够启用对应环境下 webpack
内置的优化。默认值为 production
。
// webpack.config.js
module.exports = {
...
mode: 'production'
};
复制代码
webpack --mode=production
复制代码
选项 | 描述 |
---|---|
development | 会将 process.env.NODE_ENV 的值设为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin。 |
production | 会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin. |
None | 不选用任何默认优化选项 |
// webpack.config.js
var config = {
entry: './app.js'
...
}
module.exports = (env, argv) => {
if (argv.mode === 'development') {
config.devtool = 'source-map';
}
if (argv.mode === 'production') {
...
}
return config
}
复制代码
在模块化编程中,开发者将程序分解成离散功能块,并称之为「模块」。
每一个模块具备比完整程序更小的接触面,使得校验、调试、测试垂手可得。 精心编写的「模块」提供了可靠的抽象和封装界限,使得应用程序中每一个模块都具备条理清楚的设计和明确的目的。
webpack
将「模块」的概念应用于项目中的任何文件。
对比 Node.js
模块,webpack
「模块」可以以各类方式表达它们的依赖关系,几个例子以下:
样式:(url(...))
ES2015
: import
CommonJS
: require()
HTML
: <img src=...>
AMD
: define | require
css/sass/less
: @import
webpack
经过 loader
能够支持各类语言和预处理器编写模块。loader
描述了 webpack
如何处理「非 JavaScript(non-JavaScript) 模块」,而且在 bundle
中引入这些「依赖」。
目前 webpack
已经但不限于支持如下语言的 loader
:
resolver
是一个库,用于帮助找到模块的绝对路径。 它帮助 webpack
从每一个如 require/import
语句中,找到须要引入到 bundle
中的模块代码。 当打包模块时,webpack
使用 enhanced-resolve
来解析文件路径。
使用 enhanced-resolve
,webpack
可以解析三种文件路径:
// 已经取得文件的绝对路径,所以不须要进一步再作解析。
import '/home/me/file';
import 'C:\\Users\\me\\file';
复制代码
// 在这种状况下,使用 import 或 require 的资源文件所在的目录,被认为是上下文目录。
// 在 import/require 中给定的相对路径,会拼接此上下文路径,以产生模块的绝对路径。
import '../src/file1';
import './file2';
复制代码
import 'module';
import 'module/lib/file';
// 解释很啰嗦,感兴趣能够本身去看一下文档
复制代码
每次文件系统访问都会被缓存,以便更快触发对同一文件的多个并行或串行请求。在 观察模式下,只有修改过的文件会从缓存中摘出。若是关闭观察模式,会在每次编译前清理缓存。
任什么时候候,一个文件依赖于另外一个文件,webpack
就把此视为文件之间有「依赖关系」。这使得 webpack
能够接收非代码资源(例如 images
或 web fonts
),而且能够把它们做为「依赖」提供给你的应用程序。
webpack
从命令行或配置文件中定义的「入口」开始,递归地构建一个依赖图,这个依赖图包含着应用程序所需的每一个模块,而后将全部这些模块打包为少许可由浏览器加载的 bundle
(一般只有一个)。
webpack
支持全部 ES5
兼容(IE8
及如下不提供支持)的浏览器。webpack
的 import()
和 require.ensure()
须要环境中有 Promise
。若是你想要支持旧版本浏览器,你应该在使用这些 webpack
提供的表达式以前,先 加载一个 polyfill。
经过整理这篇文档我已经对 webpack
有了一个初步的认识和了解了。
固然若是你要真正的在项目中投入使用 webpack
仅仅阅读这一篇文章是不够的,你还须要去深刻地阅读了解文档里的各类配置参数和其余经常使用的前端构建工具或预处理器配合 webpack
进行调试使用。
前路漫漫,与君共勉。