Webpack
Webpack 是一个打包器,英语叫做 bundler,意思是将不同的脚本打包成一个文件,浏览器可以运行这个文件。
Webpack 的特色。
- 能将依赖的模块,区分成不同的代码块(chunk),按需加载。
- 能将静态资源(样式表、图片、字体等等),像加载模块那样加载。
基本用法
Webpack 的基本用法,就是将多个文件打包成一个文件。
上面代码将App.js
及其依赖的脚本,打包成一个文件bundle.js
。
假定App.js
的代码如下。
那么,打包出来的bundle.js
就会同时包含App.js
和jquery
的代码。
命令行用法
--watch
或-w
表示监视功能。一旦发现脚本有变动,就立刻重新构建。
--devtool
用来指定如何生成 Source map 文件。
--config
参数用来指定配置文件。
配置文件 webpack.config.js
Webpack 的配置文件默认为webpack.config.js
。它采用 CommonJS 模块格式,输出一个配置对象。
下面是一个例子。
Webpack 配置对象包含多个字段。
entry 字段
entry
字段指定打包的入口脚本。它可以是一个字段,一个数组,也可以是一个对象。
entry: 'app.js',
// 或者
entry: [
'react-hot-loader/patch',
'webpack-dev-server/client?http://localhost:8080',
'webpack/hot/only-dev-server',
'./src/client.jsx'
],
entry
的值是对象时,通常用于与output
字段配合,打出多个包。
module.exports = {
entry: {
desktop: './src/desktop.js',
mobile: './src/mobile.js'
},
output: {
path: './dist',
filename: '[name].bundle.js',
// 多个 bundle.js 共享的异步加载部分
chunkFilename: '[id].common.js'
}
};
output 字段
output
字段指定打包后的输出。
output: {
// 输出路径,即打包后的文件的输出路径
path: './dist',
// 输出文件名
filename: 'bundle.js',
// 浏览器的实际加载的路径,
// 即浏览器到什么位置下载打包后的文件
publicPath: '/',
// 模块的输出格式,下面是指定输出为全局变量
libraryTarget: "var",
// 输出的全局变量的名字
library: "Foo"
},
Webpack 允许文件名包含哈希,比如编译后生成形如47a0cc1b840cb310842cb85fb5b6116c.js
的脚本名。只要文件发生过变更,哈希就会不同,这对长时间缓存文件很有帮助。
externals 字段
externals
字段指定哪些模块不需要打包,全局环境本身会提供。
module 字段
有些文件在打包前,需要先进行处理。比如,某些新的语法需要 Babel 转码。module.rules
用来指定转码规则。
resolve 字段
resolve
字段是一个对象,规定了模块解析的设置。
(1)resolve.modules
resolve.modules
设置模块加载时,依次搜索的目录。
上面的设置以后,模块的引用路径就可以简化。
下面是src/dir1/foo.js
的源码。
上面代码引用src/dir2/bar.js
。设置了resolve.modules
以后,就可以改成下面的写法。
(2)resolve.extensions
resolve.extensions
设置脚本文件的后缀名,即不指定脚本的后缀名时,Webpack 会自动添加的后缀名。
(3)resolve.mainFields
package.json
文件里面有main
字段,指明模块的入口文件。有时,不同的平台要求不同的入口文件,比如浏览器的入口文件很可能不同于 Node 的入口文件。
目前,通行的做法是在package.json
里面设置三个不同的字段,指明不同平台的入口文件。
browser
:浏览器的入口文件module
:ES6 模块格式或 CommonJS 格式的入口文件,通常是main
文件的另一种写法main
:通用的入口文件,用来覆盖默认的入口文件index.js
下面是一个例子(d3
的package.json
)。
resolve.mainFields
就是设置 Webpack 应该采用的入口文件。
如果 Webpack 配置文件的target
字段设成webworker
、web
、或者根本没有设置,那么resolve.mainFields
默认采用下面的值。
上面的代码表示,Webpack 优先采用package.json
的browser
字段作为入口文件。如果该字段不存在,则采用module
字段。如果module
字段也不存在,则采用main
字段。
target
字段的其他值(包括设成node
),resolve.mainFields
字段都默认采用下面的值。
plugins 字段
plugins
指定打包时需要的插件。
require()
require
支持模块加载以后,执行回调函数。
require.ensure()
某些脚本只是在一些特定情况下运行,比如用户点击某个按钮后运行。这时,如果把它和其他必备的脚本打包在一起,会增大打包后的体积。理想的方法是视情况而定,只有满足条件时才加载。
这种动态加载就要求,该脚本与其他脚本分开打包。require.ensure
方法就是用来指定分开打包的脚本。
require('./a');
if (condition) {
require.ensure([], function(require){
require('./b');
console.log('done!');
});
}
上面代码中,a.js
会与b.js
打在两个包里面,默认是bundle.js
和0.bundle.js
。网页运行时只加载bundle.js
,然后由bundel.js
根据condition
,动态加载0.bundle.js
。
require.ensure
方法接受三个函数。
第一个参数是一个数组,表示0.bundle.js
依赖的模块,你可以不输入任何值,表示没有任何依赖。
第二个参数是一个回调函数,该函数将在0.bundle.js
加载后执行。该函数的参数require
函数,凡是在函数体内用require
加载的模块都会被打包进入0.bundle.js
。
第三个参数是一个字符串,表示当前require.ensure
打包的这段代码的名字,用于使用多个require.ensure
时,所有代码可以打包成一个文件,避免打包成多个文件。
require.ensure([], function(require){
require('./a');
}, 'foo');
require.ensure(['foo'], function(require){
require('./b');
});
上面代码中,两段require.ensure
将打包在一个文件里面。
注意,require.ensure
的第一个参数只用来作为依赖模块,如果不用到它是不会运行的。
上面代码中,如果c.js
没有用到b.js
,那么b.js
是不会运行的,但是会打包在0.bundle.js
里面。所以,不要随意把模块写在第一个参数里面。
Loader
Webpack 的强大在于,它可以通过require
加载任何东西。默认情况下,Webpack 认为加载的是 JavaScript 文件。如果不是,就要通过 Loader 变形。
上面的css-loader
会将CSS文件转为一个字符串。
多个 Loader 可以连用。
上面的 style-loader,会将CSS字符串转成一个link
标签。
loader的参数
可以在配置文件里面,指定同一类文件,都使用某个loader。
还可以排除某些文件。
{ test: /\.js$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
query: {
presets: ['es2015']
}
},
现在只要直接加载.css
文件就可以了。
还可以设置 preloader
preLoaders: [
{ test: /\.js?$/, exclude: /node_modules/, loader: 'eslint-loader', include: __dirname + '/' }
],
Plugin
插件都用于特定用途。
提取文本:将特定代码提取为一个文件
module.exports = {
.....,
module: {
loaders: [
// for less files
{ test: /\.less$/,
exclude: /(node_modules|bower_components)/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader", "less-loader")
}
]
},
plugins: [
new ExtractTextPlugin("style.css")
]
}
Node API
Webpack 可以在 Node 环境中调用,它是一个构造函数,接受一个配置对象作为参数,生成 Webpack 实例。
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./webpack.config');
var server = new WebpackDevServer(webpack(config), {
contentBase: './dev',
publicPath: config.output.publicPath,
hot: true
});
server.listen(3000, 'localhost', function (err, result) {
if (err) {
console.log(err);
}
console.log('Listening at localhost:3000');
});
// require the webpack Node.js library
var webpack = require('webpack');
webpack({
// The first argument is your webpack config
entry: './src/entry.js',
output: {
path: './dist',
filename: 'bundle.js'
}
}, function(err, stats) {
// The second argument is a callback function that returns
// more information about your bundle when it is complete
});
另一种写法。
var webpack = require('webpack');
// Create an instance of the compiler
var compiler = webpack({ /* webpack config */ });
// Run the compiler manually
compiler.run(function(err, stats) { });
watch
方法可以监视源文件的变动后重新编译。
参考链接
- Module Bundling with Webpack: Getting Started Guide,by Chimeremeze Ukah