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