大意就是新的命名更容易理解(反正对于我来说就是换了个英文单词:-D),同时还会兼容老的方式,也就是说,你照样写module.loaders
还是可以的。
module : {// webpack1 way// loaders : [...]// nowrules : [ ... ]}
3.2 module[*].loader写法
如果需要加载的模块只需要一个loader
,那么你还是可以直接用loader
这个关键词;如果要加载的模块需要多个loader
,那么你需要使用use
这个关键词,在每个loader
中都可以配置参数。代码如下:
module : {rules : [{ test : /\.js|\.jsx$/, loader : 'babel-loader' }, /* 如果后面有参数需要传递到当前的loader,则在后面继续加上options关键词,例如: { test : /\.js|\.jsx$/, loader : 'babel-loader', options : { presets : [ 'es2015', 'react' ] } } */ {test : /\.css$/,// webpack1 way// loader : 'style!css' use : [ 'style-loader', 'css-loader' ]},{test : /\.less$/,use : ['style-loader', // 默认相当于 { loader : 'style-loader' }{loader : 'css-loader',options : {modules : true}},'less-loader']}]}
-loader
后缀之前写loader通常是这样的:
loader : 'style!css!less'// equals toloader : 'style-loader!css-loader!less-loader'
都自动添加了-loader
后缀,在webpack2中不再自动添加,如果需要保持和webpack1相同的方式,可以在配置中添加一个属性,如下:
module.exports = {...resolveLoader : {moduleExtensions : ["-loader"]}}// 然后就可以继续这样写,但是官方并推荐这样写// 不推荐的原因主要就是为了照顾新手,直接写会让刚接触的童鞋感到困惑// github.com/webpack/webpack/issues/2986use : [ 'style', 'css', 'less' ]
如果要加载json
文件的童鞋再也不需要配置json-loader
了,因为webpack2已经内置了。
压缩插件中的warnings
和sourceMap
不再默认为true,如果要开启,可以这样配置
plugins : [new UglifyJsPlugin({souceMap : true,warnings : true}) ]
主要是写法上的变动,要和webpack2配合使用的话,需要使用version 2版本
// webpack1 waymodules : {loaders : [{ test : /\.css$/, loader : ExtractTextPlugin.extract('style-loader', 'css-loader', { publicPath : '/dist' })} ]},plugins : [new ExtractTextPlugin('bunlde.css', { allChunks : true, disable : false }) ]// webapck2 waymodules : {rules : [{ test : /\.css$/, use : ExtractTextPlugin.extract({fallback : 'style-loader',use : 'css-loader',publicPath : '/dist'})}]},plugins : [new ExtractTextPlugin({filename : 'bundle.css',disable : false,allChunks : true}) ]
在webpack1中要开启loaders的调试模式,需要加载debug
选项,在webpack2中不再使用,在webpack3或者之后会被删除。如果你想继续使用,那么请使用以下写法:
// webpack1 waydebug : true// webapck2 way // webapck2将loader调试移到了一个插件中plugins : [new webpack.LoaderOptionsPlugin({debug : true}) ]
在webpack1中,如果要按需加载一个模块,可以使用require.ensure([], callback)
方式,在webpack2中,ES2015 loader定义了一个import()
方法来代替之前的写法,这个方法会返回一个promise.
// 在js目录中新增一个main.js// js/main.jsconsole.log('main.js');// webpack1 wayrequire.ensure([], function(require) {var _ = require('./lodash').default;console.log(_);console.log('require ensure');console.log(_.isObject(1));});// webpack2 way// 采用这种方式,需要promise 的 polyfill// 两种方式:// 1. npm install es6-promise --save-dev// require('es6-promise').polyfill();//// 2. babel方式,在webpack中配置babel插件// npm install babel-syntax-dynamic-import --save-dev// options : {// presets : ['es2015'],// plugins : ['syntax-dynamic-import']// }import('./lodash').then(module => {let _ = module.default;console.log(_);console.log('require ensure');console.log(_.isObject(1));});
会得到的chunk文件,如下图:
可以动态的传递参数来加载你需要的模块,例如:
function route(path, query) {return import(`./routes/${ path }/route`) .then(route => { ... })}
webpack2中提供了一种更简单的使用热替换功能的方法。当然如果要用node启动热替换功能,依然可以按照webpack1中的方式。
npm install webpack-dev-server --save-dev// webpack.config.jsmodule.exports = {// ...,devServer : {contentBase : path.join(__dirname, 'build'),hot : true,compress : true,port : 8080,publicPath : '/build/'},plugins : [new webpack.HotModuleReplacementPlugin() ]}
主要是介绍之前在webpack1中忽略的以及v2版本中新加的一些东西。
浏览器为了不重复加载相同的资源,因此加入了缓存功能。通常如果请求的文件名没有变的话,浏览器就认为你请求了相同的资源,因此加载的文件就是从缓存里面拿取的,这样就会造成一个问题,实际上确实你的文件内容变了,但是文件名没有变化,这样还是从缓存中加载文件的话,就出事了。
那么,之前传统的做法就是给每个文件打上加上版本号,例如这样:
app.js?version=1app.css?version=1
每次变动的时候就给当前的版本号加1,但是如果每次只有一个文件内容变化就要更新所有的版本号,那么没有改变的文件对于浏览器来说,缓存就失效了,需要重新加载,这样就很浪费了。那么,结合数据摘要算法,版本号根据文件内容生成,那么现在的版本可能是这样的。
// beforeapp.js?version=0add34app.css?version=1ef4a2// after// change app.js contentapp.js?versoin=2eda1capp.css?version=1ef4a2
关于怎么部署前端代码,可以查看大公司怎样开发和部署前端代码
webpack为我们提供了更简单的方式,为每个文件生成唯一的哈希值。为了找到对应的入口文件对应的版本号,我们需要获取统计信息,例如这样的:
{ "main.js?1.1.11": "main.facdf96690cca2fec8d1.js?1.1.11", "vendor.js?1.1.11": "vendor.f4ba2179a28531d3cec5.js?1.1.11"}
同时,我们结合html-webpack-plugin
使用的话,就不需要这么麻烦,他会自动给文件带上对应的版本。具体看法参看之前写的webpack1知识梳理,那么我们现在的配置变成了这个样子:
npm install webpack-manifest-plugin --save-dev// webpack.config.jsmodule.exports = {entry : { /* ... */ },output : {path : path.resolve(__dirname, 'build-init'),filename : '[name].[chunkhash].js',chunkFilename : '[name].[chunkhash].js'},module : {// ...},plugins : [new htmlWebpackPlugin({title : 'webpack caching'}),new WebpackManifestPlugin() ]}
html引入情况
<!DOCTYPE html><html> <head><meta charset="UTF-8"><title>webpack caching</title> </head> <body> <div id="container"></div> <script type="text/javascript" src="main.facdf96690cca2fec8d1.js?1.1.11"></script><script type="text/javascript" src="vendor.f4ba2179a28531d3cec5.js?1.1.11"></script></body></html>
WARNNING:
不要在开发环境下使用[chunkhash],因为这会增加编译时间。将开发和生产模式的配置分开,并在开发模式中使用[name].js的文件名,在生产模式中使用[name].[chunkhash].js文件名。
为了使文件更小化,webpack使用标识符而不是模块名称,在编译的时候会生成一个名字为manifest的chunk块,并且会被放入到entry中。那么当我们更新了部分内容的时候,由于hash值得变化,会引起manifest块文件重新生成,这样就达不到长期缓存的目的了。webpack提供了一个插件ChunkManifestWebpackPlugin
,它会将manifest映射提取到一个单独的json文件中,这样在manifest块中只需要引用而不需要重新生成,所以最终的配置是这样的:
var path = require('path'),webpack = require('webpack'),htmlWebpackPlugin = require('html-webpack-plugin'),ChunkManifestWebpackPlugin = require('chunk-manifest-webpack-plugin'),WebpackChunkHash = require('webpack-chunk-hash');module.exports = {entry : {main : path.resolve(__dirname, 'js/app.js'),vendor : path.resolve(__dirname, 'js/vendor.js')},output : {path : path.resolve(__dirname, 'build'),filename : '[name].[chunkhash].js',chunkFilename : '[name].[chunkhash].js'},module : {// ...},plugins : [new webpack.optimize.CommonsChunkPlugin({name : ['vendor', 'manifest'],minChunks : Infinity}),new webpack.HashedModuleIdsPlugin(),new WebpackChunkHash(),new htmlWebpackPlugin({title : 'webpack caching'}),new ChunkManifestWebpackPlugin({filename : 'chunk-mainfest.json',manifestVariable : 'webpackManifest',inlineManifest : true}) ]}
tips:如果还不是很明白,去对比一下加了ChunkManifestWebpackPlugin
和没加的区别就可以清楚的感受到了。在本文的代码文件夹caching
中可以看到这一差别