官网v3.5.5,webpack2.2.0,webpack4.8.3.本教程基于v3.5.5.
一个大现代js应用程序的模块打包工具(module bundler)。webpack可以纯粹在服务端运行。
当webpack处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序需要地每个模块,然后将所有地这些模块打包成少量的bundle,通常只有一个,由浏览器加载。它是高度可配置的。
webpack创建应用程序所有依赖的关系图。图的起点被称为入口起点(entry point)。
入口起点告诉webpack从哪里开始,并根据依赖关系图确定需要打包的内容。可以将应用程序的入口起点认为是根上下文或app第一个启动文件。
在webpack配置对象中,我们使用entry属性来定义入口
module.exports={
entry: './path/to/entry/file.js'
}
将所有的资源归拢到一起后,还需要告诉webpack在哪里打包应用程序。webpack的output
属性描述了如何处理归拢在一起的代码(bundle code)
const path= require('path');
module.exports={
entry:'./path/to/my/file.js',
output:{
path: path.resolve(__dirname,'dist'),
filename: 'my-first-webpack.bundle.js'
}
}
以上代码,我门店通过output.filename
和output.path
属性,告诉webpack bundle的名称,以及我们想要生成(emit)到哪里。
oustput属性除了这两个属性还有更多的属性。
loader在文件被添加到依赖时将文件转为模块。webpack的目标时让webpack聚焦于项目中的所有资源(asset),webpack把每个文件(.css,.html,.scss,.jpg等)都作模块处理,而webpack自身只理解js
loader的两个目标:
(1)识别出应该被对应的loader进行转换的那些文件。(test
属性)
(2)转换这些文件,使其能够被添加到依赖图中(并且最终添加到bundle中)。(use
属性)
const path= require('path');
const config={
entry:'./path/to/file.js',
putput: {
path: path.resolve(__dirname,'dist');
filename:'my-first-bundle.js'
},
module:{
rules:[
{ test: /\.txt$/,use:'raw-loader'}
]
}
}
module.exports=config;
以上对一个单独的module对象定义了rules
属性,包含两个必须的属性:test
,use
。这告诉webpack编译器compiler如下信息,当在reqquire()/import语句中被解析为.txt的路径时,你在对它打包前用raw-loader转换一下。
必须要明确的是,在webpack中定义loader时,要定义在module.rules中,而不是rules。
想要使用一个插件,require(),然后添加到plugins数组中。多数插件可以通过选项opstion自定义,如果因多次不同的目的而多次使用同一个插件,这时需要通过使用new
创建实例。
const HtmlWebpackPugin = require('html-weebpack-plugin');
const webpack = require('webpack');
const path = require('path');
const config = {
entry: './path/myfile.js',
output:{
path: path.resolve(__dirname,'dist'),
filename: 'my-fisr-budle.js'
},
module:{
rules:[
{ test:/\.txt$/, use: 'raw-loader' }
]
},
plugins:[
new webpack.optimize.UglifyJsPlugin(),
new HtmlWebpackPlugin({ template:'./src/index.html'})
]
}
module.exports= config;
插件列表
插件了解更多
webpack配置是标准的Nodes.js CommonJS模块,你可以做到以下事情:
- 通过require()导入其他文件
- 通过require()使用npm的工具函数
- 使用js控制流表达式,例如 ?:操作符
- 编写并执行函数来生成部分配置
- 对常用值使用常量或变量
虽然技术上可行,但是应该避免以下做法:
(1)在使用命令行接口(CLI)时(应该编写自己的命令行接口CLI,或使用 k–env),访问命令行接口参数。
(2)导出不确定值。(调用webpack两次应该产生同样的输出文件)
(3)编写很长的配置(应该将配置拆分为多个文件)
最简单的配置
var path = require('path');
module.exports = {
entry: './foo.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'foo.bundle.js'
}
};
webpack 接受以多种编程和数据语言编写的配置文件。
webpack将模块化应用于任何文件。每个模块具有比完整程序更小的接触面,使得校验、调试、测试轻而易举。
webpack可以以各种方式表达它们之间的依赖关系,几个例子如下:
es6import
语句;
CommonJSrequire()
语句;
AMD的define
和require
语句;
css/sass/less文件中的@import
语句;
样式(url(...)
)或HTML文件()中的图片链接、
webpack 1 需要特定的 loader 来转换 ES 2015 import,然而通过 webpack 2 可以开箱即用。
loader是什么呢?loader描述了webpack如何处理非js模块。webpack社区为各种流行语言和语言处理器构建了loader。包括:
CoffeeScript
TypeScript
ESNext (Babel)
Sass
Less
Stylus
loader列表。我们的也可以自己编写loader。
resolver是一个库,用于帮助模块找到绝对路径。所依赖的模块可以是应用程序的代码或者第三方库。resolver帮webpack找到bundle中需要引用的模块代码,这些代码包含在每个require/import语句中。webpack使用enhanced-resolve来解析文件路径。
使用enhanced-resolve,wepack可以解析三种文件路径:
(1)绝对路径
(2)相对路径
(3)模块路径
//绝对路径
import "/home/me/file"
import "c:\\user\\me\\file"
//相对路径
import "../src/file1";
import "./files2"
//模块路径
import "module";
import "module/lib/file";
一旦根据上述规则解析路径后,解析器将检查路径是否指向文件或目录。如果路径指向一个文件:
- 如果文件具有扩展名,则直接将文件打包。
- 否则,将使用【resolve.extensions】选项作为文件扩展名来解析,此选项告诉解析器在解析中能够接收哪些扩展名。(比如.js,.jsx)
- 如果文件夹中包含package.json文件,则按照顺序查找resolve.mainFields配置选项中指定的字段。并且package.json中的第一个这样的字段确定文件路径。
任何时候,一个文件依赖另一个文件,webpack就把此视为文件之间有依赖关系。这使得webpack可以接收任何非代码资源(non-code asset)。
wepack从命令行或配置文件中定义的一个模块列表开始,处理你的应用程序。从这些入口起点开始,webpack递归地构建一个依赖图,这个依赖图包含你需要的每个模块,然后所有的模块打包为少量的bundle,通常只有一个-可由浏览器加载
使用wepack构建的应用程序或站点中,有三种主要的代码类型:
1.你或你的团队编写的源码;
2.你的源码会依赖的任何第三方的library或vendor代码
3.webpack的runtime和manifest,管理所有模块的交互。
runtime以及伴随的manifest数据,主要是指:在浏览器运行时,webpack用来连接模块化的应用程序的所有代码。runtime包含:在模块交互时,连接模块所需的加载和解析逻辑。包含浏览器中的已加载模块的连接,以及懒加载模块的执行逻辑。
一旦你的应用程序中,形如index.html文件、一些bundle和各种资源加载到浏览器中,会发生什么呢?你精心安排的/src目录的文件结构已经不在,webpack如何管理所有模块之间的交互?这就是manifest数据用途的来源……
当编译器(compiler)开始执行时、解析和映射应用程序时,它会保留所有模块的详细要点,这个数据集合称为Manifest。
当完成打包并发送到浏览器时,会在运行时通过Manifest来解析和加载模块。无论你选择哪种模块语法,你那些import 或require现在已经转为webpack_require方法,此方法指向模块标识符。通过使用manifest中的数据,runtime将能够查询模块标识符,检索出背后对应的模块
什么是target?target就是webpack用于加载块(chunk)的环境。
要在webpack配置文件(webpack.config.js)中设置target属性
module.exports={
target:'node'
};
上面的例子中,使用node,webpack会编译为用于【类Node.js】环境。(使用Node.js的require,而不是使用任意内置模块(如fs,或path),来加载chunk)
target可用值
webpacck不支持向target传入多个字符串,但是,你可以通过打包两份分离的配置来创建同构的库。
var path=require('path');
var serveConfig={
target:'node',
output:{
path:path.resolve(__dirname,'dist');
filename:'lib.node.js'
}
//......
};
//第二个配置
var clientConfig={
target: 'web',
output:{
path: path.resolve(__dirname,'dist');
filename: 'lib.js'
}
//......
}
module.exports=[serverConfig,clientConfig];
模块热替换功能会在应用程序运行过程中替换、添加或删除模块,而无须重新加载整个页面。
- 保留在完全重新加载页面时丢失的应用程序状态;
- 只更新变更内容,以节省宝贵的开发时间;
- 调整样式更加快速-几乎等于在浏览器调试器中更改样式。
1.应用程序代码要求HMR检测更新;
2.HMR runtime(异步)下载更新,然后通知应用程序代码;
3.应用程序代码要求HMR runtime应用更新;
4.HMR runtime(异步)应用更新。
你可以设置HMR,以使此进程自动触发更新,或者你可以选择要求在用户交互时进行更新。
除了普通资源(什么是普通资源?暂时不清楚),编译器需要发出’update’,以允许更新之前的模板到新的版本。’update’由两部分组成:
1.更新后的manifest(JSON);
2.一个或多个更新后的chunk(js)。
manifest包括新的编译hash和所有的待更新chunk目录。每个更新chunk都含有应用于此chunk的全部更新模块的代码或一个flag用于表明此模块要被移除。
所以,chunk是一些模块的集合
编译器确保模块ID和chunk ID在这些构建之间保持一致。通常将这些ID存储在内存中(例如使用webpack-dev-server时),也可能存储在JSON文件中。
HMR是可选功能,只影响包含HMR代码的模块。举个例子,通过style-loader为style样式追加补丁。为了运行追加补丁,style-loader实现了HMR接口;当它通过HMR接收到更新,它会使用新的样式替换旧的样式。
然而在多数情况下,不需要强制在每个模块中写入HMR代码,如果一个模块没有HMR处理处理函数,更新就会冒泡
。这意味着一个简单的处理函数能够对整个模块树进行更新
;如果在这个模块树中,一个单独的模块树被更新,那么整组依赖模块都会被重新加载。
module.hot接口的详细信息,HMR API
webpack强大之处在于它的可定制化。更多细节可以看模块热更新指南