官网:http://www.css88.com/doc/webpack2/concepts/css
webpack是js的模块打包器(module bundler)。html
webpack将建立全部应用程序的依赖关系图标(dependency graph)。node
入口起点(entry point):图表的起点。根上下文(contextual root) app第一个启动文件。webpack
入口起点告诉webpack从哪里开始,遵循依赖图表打包文件。git
webpack中使用webpack配置对象的entry属性定义入口。github
用法: entry: string | Array<string>web
webpack.config.jsnpm
1 module.exports = { 2 entry: './path/to/my/entry/file.js' 3 }; 4 module.exports = config;
entry属性的单个入口语法,是下面的简写:编程
1 module.exports = { 2 entry: { 3 main: './path/to/my/entry/file.js' 4 } 5 };
向entry传入一个数组会发生什么?将建立多个入口。json
用法:entry: {[entryChunkName: string]: string | Array<string>}
const config = { entry: { app: './src/app.js', // 应用程序(app) vendors: './src/vendors.js' // 公共库(vendor) } };
对象语法是应用程序中定义入口的最可扩展的方式。
可扩展的webpack配置:可重用并可与其余配置组合使用。用于将关注点从环境、构建目标、运行时中分离,而后用专门的工具(如 webpack-merge)将它们合并。
全部的资源(assets)归拢在一块儿后,须要告诉webpack在哪里打包应用程序。
output属性,描述了如何处理归拢在一块儿的代码(bundled code)。
webpack.config.js
module.exports = { entry: './path/to/my/entry/file.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'my-first-webpack.bundle.js' } };
将output设置为一个对象,包括如下两点:
1.编译文件的文件名(filename),推荐:main.js || bundle.js || index.js
2.output.path对应一个绝对路径(一次性打包的目录)。
webpack.config.js
const config = { output: { filename: 'bundle.js', path: '/home/proj/public/assets' } }; exports.module = config;
能够向output属性传入的值。
output.chuckFilename
非入口的 chunk(non-entry chunk) 的文件名,路径相对于 output.path
目录。
[id]
被 chunk 的 id 替换。
[name]
被 chunk 的 name 替换(或者,在 chunk 没有 name 时使用 id 替换)。
[hash]
被 compilation 生命周期的 hash 替换。
[chunkhash]
被 chunk 的 hash 替换。
output.crossOriginLoading
此选项启用跨域加载(cross-origin loading) chunk。
可选的值有:
false
- 禁用跨域加载
"anonymous"
- 启用跨域加载。当使用 anonymous
时,发送不带凭据(credential)的请求。
"use-credentials"
- 启用跨域加载。发送带凭据(credential)的请求。
output.devtoolLineToLine
全部/指定模块启用行到行映射(line-to-line mapped)模式。
行到行映射模式使用一个简单的 SourceMap,即生成资源(generated source)的每一行都映射到原始资源(original source)的同一行。这是一个可作性能优化之处。
true
在全部模块启用(不推荐)
{test, include, exclude}
对象,对特定文件启用(相似于 module.loaders
)。
默认值:false
output.filename
指定硬盘每一个输出文件的名称。
单个入口
{ entry: './src/app.js', output: { filename: 'bundle.js' path: __dirname + 'build', } } // 写入到硬盘: ./build/bundle.js
多个入口
如配置建立多个“chunk”(例如使用多个入口起点或使用相似CommonsChunkPlugin(提取公共代码)的插件),为确保每一个文件名都不重复,应使用如下替换方式:
[name]
被 chunk 的 name 替换。
[hash]
被 compilation 生命周期的 hash 替换。
[chunkhash]
被 chunk 的 hash 替换。
{ entry: { app: './src/app.js', search: './src/search.js' }, output: { filename: '[name].js', path: __dirname + '/build' } } // 写入到硬盘:./build/app.js, ./build/search.js
output.hotUpdateChunkFilename
热更新 chunk(Hot Update Chunk) 的文件名。在 output.path
目录中。
[id]
被 chunk 的 id 替换。
[hash]
被 compilation 生命周期的 hash 替换。(最后一个 hash 存储在记录中)
默认值:"[id].[hash].hot-update.js"
output.hotUpdateFunction
webpack 中用于异步加载(async load)热更新(hot update) chunk 的 JSONP 函数。
默认值:"webpackHotUpdate"
output.hotUpdateMainFilename
热更新主文件(hot update main file)的文件名。
[hash]
被 compilation 生命周期的 hash 替换。(最后一个 hash 存储在记录中)
默认值:"[hash].hot-update.json"
output.jsonpFunction
webpack 中用于异步加载(async loading) chunk 的 JSONP 函数。
较短的函数可能会减小文件大小。当单页有多个 webpack 实例时,请使用不一样的标识符(identifier)。
默认值:"webpackJsonp"
output.library
若是设置此选项,会将 bundle 导出为 library。output.library
是 library 的名称。
若是你正在编写 library,而且须要将其发布为单独的文件,请使用此选项。
output.libraryTarget
library 的导出格式
"var"
- 导出为一个变量:var Library = xxx
(默认)
"this"
- 导出为 this
的一个属性:this["Library"] = xxx
"commonjs"
- 导出为 exports
的一个属性:exports["Library"] = xxx
"commonjs2"
- 经过 module.exports
:module.exports = xxx
导出
"amd"
- 导出为 AMD(可选命名 - 经过 library 选项设置名称)
"umd"
- 导出为 AMD,CommonJS2 或者导出为 root 的属性
默认值:"var"
若是 output.library
未设置,可是 output.libraryTarget
被设置为 var
之外的值,则「所导出对象」的每一个属性都被复制到「对应的被导出对象」上(除了 amd
,commonjs2
和 umd
)。
output.path
导出目录为绝对路径(必选项)。
[hash]
被 compilation 生命周期的 hash 替换。
config.js
output: { path: "/home/proj/public/assets", publicPath: "/assets/" }
index.html
<head> <link href="/assets/spinner.gif" /> </head>
接下来是一个更复杂的例子,来讲明对资源使用 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 // 其余的应用程序入口
output.sourceMapFilename
JavaScript 文件的 SourceMap 的文件名。它们在 output.path
目录中。
[file]
被 JavaScript 文件的文件名替换。
[id]
被 chunk 的 id 替换。
[hash]
被 compilation 生命周期的 hash 替换。
默认值:"[file].map"
webpack的目标是让webpack聚焦于项目中的全部资源(asset),而浏览器不须要关注这些(并非资源都必须打包在一块儿)。webpack把每一个文件(.css, .html, .scss, .jpg, etc.) 都做为模块处理。并且webpack只理解js。
loader是对应用程序中资源文件进行转换。它们是(运行在Node.js中的)函数,能够将资源文件做为参数的来源,而后返回到新的资源文件。
webpack配置的目标:
1.识别(identity)被对应loader转换(transform)的文件
2. 进行过文件转换,能够将被转换的文件添加到依赖图表(最终添加到bundle中)(use属性)
webpack.config.js
const config = { entry: './path/to/my/entry/file.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'my-first-webpack.bundle.js' }, module: { rules: [ // 当遇到【在require() / import语句中被解析为'.js'或'.jsx'的路径】时,把它们添加并打包前,要先使用babel-loader转换 {test: /\.(js | jsx)$/, use: 'babel-loader'} ] } }; module.exports = config;
在webpack配置中定义loader时,要定义在module.rules中,而不是rules。定义错时webpack会提出严重警告。
可以使用 loader 告诉 webpack 加载 CSS 文件,或者将 TypeScript 转为 JavaScript。
首先,安装相对应的 loader:
npm install --save-dev css-loader
npm install --save-dev ts-loader
其次,配置 webpack.config.js
,对每一个 .css
文件使用 css-loader
,对每一个 .ts
文件使用 ts-loader
:
webpack.config.js
module.exports = { module: { rules: [ {test: /\.css$/, use: ['css-loader'](loaders/css-loader)}, {test: /\.ts$/, use: ['ts-loader']((https://github.com/TypeStrong/ts-loader))} ] } };
注意,根据配置选项,下面的规范定义了同等的 loader 用法:
{test: /\.css$/, [loader](/configuration/module#rule-loader): 'css-loader'} // or equivalently {test: /\.css$/, [use](/configuration/module#rule-use): 'css-loader'} // or equivalently {test: /\.css$/, [use](/configuration/module#rule-use): { loader: 'css-loader', options: {} }}
在你的应用程序中,有三种方式使用 loader:
require
语句中显示使用webpack.config.js
module.rules
容许在 webpack 配置中指定几个 loader。
这是展现 loader 的一种简明的方式,而且有助于简洁代码,以及对每一个相应的 loader 有一个完整的概述。
module: { rules: [ { test: /\.css$/, use: [ {loader: 'style-loader'}, { loader: 'css-loader', options: { module: true } } ] } ] }
require
能够在 require
语句(或 define
, require.ensure
, 等语句)中指定 loader。使用 !
将资源中的 loader 分开。分开的每一个部分都相对于当前目录解析。
require('style-loader!css-loader?modules!./styles.css');
经过前置全部规则及使用 !
,能够对应覆盖到配置中的任意 loader。
选项能够传递查询参数,就像在 web 中那样(?key=value&foo=bar
)。也可使用 JSON 对象(?{"key":"value","foo":"bar"}
)。
尽量使用 module.rules
,由于能够在源码中减小引用,而且更快调试和定位 loader,避免代码愈来愈糟。
可选项,经过 CLI 使用 loader:
webpack --module-bind jade --module-bind 'css=style!css'
对 .jade
文件使用 jade-loader
,对 .css
文件使用 style-loader
和 css-loader
。
options
对象进行配置。package.json
常见的 main
属性,还能够将普通的 npm 模块导出为 loader,作法是在 package.json
里定义一个 loader 字段。loader 经过(loader)预处理函数,为 JavaScript 生态系统提供了更多有力功能。用户如今能够更加灵活的引入细粒度逻辑,例如压缩(compression)、打包(package)、语言翻译(language translation)和其余更多。
loader 遵循标准的模块解析。多数状况下,loader 将从模块路径(一般是 npm install
, node_modules
)解析。
如何编写模块?
loader 模块须要导出为一个函数,而且使用 Node.js 兼容的 JavaScript 编写。一般使用 npm 管理 loader,也能够将 loader 模块做为应用程序中的文件。
按照约定,loader 一般被命名为 XXX-loader
,其中 XXX
是上下文的名称,例如 json-loader
。
loader 的名称约定和优先搜索顺序,由 webpack 配置 API 中的 resolveLoader.moduleTemplates
定义。
loader仅在每一个文件的基础上执行转换,插件目的在于解决loader没法实现的事情。
插件(plugins)最经常使用(但不限)于在打包模块的“compilation”和“chunk”生命周期执行操做和自定义功能。
webpack的插件系统强大且可定制化。
在一个配置中,屡次使用一个插件,用于不一样的目的。
1.想使用一个插件,只需require()它,而后把它添加到plugins数组中。多数插件能够经过选项(option)自定义。
2.须要使用new来建立插件的实例,而且经过实例来调用插件。
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // installed via npm(经过npm安装) const webpack = require('webpack'); //to access built-in plugins(访问内置插件) 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: /\.(js | jsx)$/, use: 'babel-loader'} ] }, plugins: [ new webpack.optimize.UglifyJsPlugin(), // 优化js组件 new HtmlWebpackPlugin({template: './src/index.html'}) ] }; module.exports = config;
webpack 插件是一个具备 apply
属性的 JavaScript 对象。 apply
属性会被 webpack compiler 调用,而且 compiler 对象可在整个 compilation 生命周期访问。
ConsoleLogOnBuildWebpackPlugin.js
function ConsoleLogOnBuildWebpackPlugin() { }; ConsoleLogOnBuildWebpackPlugin.prototype.apply = function(compiler) { compiler.plugin('run', function(compiler, callback) { console.log("webpack 构建过程开始!!!"); callback(); }); };
经过Function.prototype.apply方法能够把任意函数做为插件传递(this
将指向 compiler
)。在配置中使用这样的方式来内联自定义插件。
plugin 能够携带参数/选项,在 wepback 配置中,需向 plugins
属性传入 new
实例。
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // installed via npm(经过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: { loaders: [ { test: /\.(js | jsx)$/, loader: 'babel-loader' } ] }, plugins: [ new webpack.optimize.UglifyJsPlugin(), // 优化js组件 new HtmlWebpackPlugin({template: './src/index.html'}) ] }; module.exports = config;
webpack 的配置文件是 JavaScript 文件导出的一个对象。此对象,由 webpack 根据对象定义的属性进行解析。
由于 webpack 配置是标准的 Node.js CommonJS 模块,能够以下:
require(...)
导入其余文件require(...)
使用 npm 的工具函数?:
操做符不该该使用如下。从技术上讲能够这么作,可是并不推荐:
--env
)时,访问命令行接口(CLI)参数webpack.config.js
var path = require('path'); module.exports = { entry: './foo.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'foo.bundle.js' } };
webpack.config.js
var path = require('path'); var webpack = require('webpack'); var webpackMerge = require('webpack-merge'); var baseConfig = { target: 'async-node', entry: { entry: './entry.js' }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js' }, plugin: [ new webpack.optimize.CommonsChunkPlugin({ name: 'inline', filename: 'inline.js', minChunks: Infinity }), new webpack.optimize.AggressiveSplittingPlugin({ minSize: 5000, maxSize: 10000 }), ] }; let targets = ['web', 'webworker', 'node', 'async-node', 'node-webkit', 'electron-main'].map((target) => { let base = webpackMerge(baseConfig, { target: target, output: { path: path.resolve(__dirname, 'diat/' + target), filename: '[name].' + target + '.js' } }); return base; }); module.exports = targets;
在模块化编程,开发者将程序分解成称为模块的离散功能块。
对比 Node.js 模块,webpack 模块可以以各类方式表达它们的依赖关系,几个例子以下:
import
语句require()
语句define
和 require
语句@import
语句。url(...)
)或 HTML 文件(<img src=...>
)中的图片连接(image url) webpack 1 须要特定的 loader 来转换 ES 2015 import
,然而经过 webpack 2 能够开箱即用。
解析器是一个经过绝对路径来帮助定位模块的库(library)。 一个模块能够做为另外一个模块的依赖模块,而后被后者引用,以下:
import foo from 'path/to/module' // or require('path/to/module')
依赖模块能够来自应用程序代码或第三方库。解析器帮助 webpack
找到 bundle 中须要引入的模块代码,这些代码包含在 require
/import
语句中。 当打包模块时,webpack
使用加强解析来解析文件路径
使用 enhanced-resolve
,webpack 可以解析三种文件路径:
import "/home/me/file";
import "C:\\Users\\me\\file";
已经有了文件的绝对路径,不须要进一步解析。
import "../src/file1";
import "./file2";
出现 import
或 require
的资源文件的目录被认为是上下文目录(context directory)(当前处理文件的目录)。在 import/require
中给定的相对路径被追加到此上下文路径(context path),以生成模块的绝对路径(absolute path)。
import "module";
import "module/lib/file";
模块将在 resolve.modules
中指定的全部目录内搜索。 能够替换初始模块路径,此替换路径经过使用 resolve.alias
配置选项来建立一个别名。
一旦根据上述规则解析路径后,解析器(resolver)将检查路径是否指向文件或目录。若是路径指向一个文件:
resolve.extensions
] 选项做为文件扩展名来解析,此选项告诉解析器在解析中可以接受哪些扩展名(例如 .js
, .jsx
)。若是路径指向一个文件夹,则采起如下步骤找到具备正确扩展名的正确文件。
package.json
文件,则按照顺序查找 resolve.mainFields
配置选项中指定的字段。而且 package.json
中的第一个这样的字段肯定文件路径。webpack 根据构建目标(build target)为这些选项提供了合理的默认配置。
Loader 解析遵循与文件解析器指定的规则相同的规则。可是 resolveLoader
配置选项能够用来为 Loader 提供独立的解析规则。
每一个文件系统访问都被缓存,以便更快触发对同一文件的多个并行或穿行请求。在观察模式下,只有修改过的文件会从缓存中摘出。若是关闭观察模式,在每次编译前清理缓存。
任什么时候候一个文件依赖于另外一个文件,webpack 把这个文件看成依赖处理。这使得 webpack 能够接收非代码资源(non-code asset)(例如图像或 web 字体),而且也能把它们做为依赖提供给应用。
webpack 从命令行或配置文件定义的一个模块列表,开始处理应用。 从这些入口点开始,webpack 递归地构建一个依赖图表,这个依赖图表包括应用所需的每一个模块,而后将全部模块打包为少许的包(bundle) - 一般只有一个包 - 可由浏览器加载。
对于 HTTP/1.1 客户端,打包应用会尤为强大,由于当浏览器发起一个新请求时,它可以最大限度地减小应用的等待次数。对于 HTTP/2,能够经过 webpack 使用代码拆分(Code Splitting)和打包实现最佳优化。
由于服务器和浏览器代码均可以用 JavaScript 编写,因此 webpack 提供了多种构建目标(target),你能够在webpack 配置中设置。
要设置 target
属性,只须要在你的 webpack 配置中设置 target 的值。
webpack.config.js
module.exports = { target: 'node' };
在上面例子中,使用 node
webpack 会编译为用于「类 Node.js」环境(使用 Node.js 的 require
,而不是使用任意内置模块(如 fs
或 path
)来加载 chunk)。
每一个target都有各类部署(deployment)/环境(environment)特定的附加项,以支持知足其需求。查看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
文件。
模块热替换功能会在应用程序运行过程当中替换、添加或删除模块,而无需从新加载页面。在独立模块变动后,无需刷新整个页面,就能够更新这些模块,极大地加速了开发时间。
能够设置 HMR,使此进程自动触发更新,或者选择要求在用户交互后进行更新。
除了普通资源,编译器(compiler)须要发出 "update",以容许更新以前的版本到新的版本。"update" 由两部分组成:
manifest 包括新的编译 hash 和全部的待更新 chunk 目录。
每一个待更新 chunk 包括用于与全部被更新模块相对应 chunk 的代码(或一个 flag 用于代表模块要被移除)。
编译器确保模块 ID 和 chunk ID 在这些构建之间保持一致。一般将这些 ID 存储在内存中(例如,当使用 webpack-dev-server 时),可是也可能将它们存储在一个 JSON 文件中。
HMR 是可选功能,只会影响包含 HRM 代码的模块。举个例子,经过 style-loader
为 style 样式追加补丁。 为了运行追加补丁,style-loader
实现了 HMR 接口;当它经过 HRM 接收到更新,它会使用新的样式替换旧的样式。
相似的,当在一个模块中实现了 HMR 接口,你能够描述出当模块被更新后发生了什么。然而在多数状况下,不须要强制在每一个模块中写入 HMR 代码。若是一个模块没有 HMR 处理函数,更新就会冒泡。这意味着一个简单的处理函数可以对整个模块树(complete module tree)进行处理。若是在这个模块树中,一个单独的模块被更新,那么整个模块树都会被从新加载(只会从新加载,不会迁移)。
对于模块系统的 runtime,附加的代码被发送到 parents
和 children
跟踪模块。
在管理方面,runtime 支持两个方法 check
和 apply
。
check
发送 HTTP 请求来更新 manifest。若是请求失败,说明没有可用更新。若是请求成功,待更新 chunk 会和当前加载过的 chunk 进行比较。对每一个加载过的 chunk,会下载相对应的待更新 chunk。当全部待更新 chunk 完成下载,就会准备切换到 ready
状态。
apply
方法将全部被更新模块标记为无效。对于每一个无效模块,都须要在模块中有一个更新处理函数,或者在它的父级模块们中有更新处理函数。不然,无效标记冒泡,并将父级也标记为无效。每一个冒泡继续直到到达应用程序入口起点,或者到达带有更新处理函数的模块(以最早到达为准)。若是它从入口起点开始冒泡,则此过程失败。
以后,全部无效模块都被(经过 dispose 处理函数)处理和解除加载。而后更新当前 hash,而且调用全部 "accept" 处理函数。runtime 切换回闲置
状态,一切照常继续。
能够在开发过程当中将 HMR 做为 LiveReload 的替代。webpack-dev-server 支持热模式,在试图从新加载整个页面以前,热模式会尝试使用 HMR 来更新。查看如何实如今 React 项目中使用 HRM 为例。
一些 loader 已经生成可热更新的模块。例如,style-loader
可以置换出页面的样式表。对于这样的模块,不须要作任何特殊处理。