webpack是react项目标配的打包工具,和NPM搭配起来使用管理模块实在非常方便。
webapck 把所有的静态资源都看做是一个 module,通过 webpack,将这些 module 组成到一个 bundle 中去,从而实现在页面上引入一个 bundle.js,来实现所有静态资源的加载。
定位 Webpack 速度慢的原因
打包的命令webpack后加三个参数:
- --colors 输出结果带彩色,比如:会用红色显示耗时较长的步骤
- --profile 输出性能数据,可以看到每一步的耗时
- --display-modules 默认情况下 node_modules 下的模块会被隐藏,加上这个参数可以显示这些被隐藏的模块
从命令行的结果里具体分析速度慢的原因。
通过Resolve设置,提高模块分析依赖的速度
resolve主要是用来配置解析模块路径和分析依赖,可以让require模块的定位更快。
1.resolve.alias别名是 Webpack 的一个配置项,它的作用是把用户的一个请求重定向到另一个路径 。如果在alias中定义了值,然后在代码中require该值的时候,会重定向到alias指定的位置。
关于alias的使用,官网给出了具体例子。不同的alias设置,在/abc/entry.js中使用require的效果:
2.resolve.root和resolve.modulesDirectories
root是通过绝对路径的方式来定义查找模块的文件夹。可以是一个数组,主要是用来增加模块的搜寻位置使用的。
这里root的值必须是绝对路径,使用path.resolve设置。
var path = require('path');
// ...
resolve: {
root: [
path.resolve('./app/modules'),
path.resolve('./vendor/modules')
]
}
这样设置后,会增加搜索app/modules和vendor/modules下所有node_modules里面的模块。
modulesDirectories是用来设置搜索的目录名的,默认值:["web_modules", "node_modules"]。如果把值设置成["mydir"]
, webpack会查询 “./mydir”, “../mydir”, “../../mydir”等。
注意: Passing"../someDir","app","."or an absolute path isn’t necessary here. Just use a directory name, not a path. Use only if you expect to have a hierarchy within these folders. Otherwise you may want to use theresolve.root
option instead.
配置module.noParse
module.noParse是webpack的另一个很有用的配置项,如果你 确定一个模块中没有其它新的依赖就可以配置这项,webpack将不再扫描这个文件中的依赖。
module: {
noParse: [/moment-with-locales/]
}
配置Loader的exclude或include来设定babel的使用范围
对于很多的 npm 包来说,他们完全没有经过 babel 的必要(成熟的 npm 包会在发布前将自己 es5,甚至 es3 化),让这些包通过 babel 会带来巨大的性能负担。
使用 exclude,屏蔽掉 npm 里的包,从而使整包的构建效率飞速提高。
module: {
loaders: [ {
test: /\.js(x)*$/,
loader: 'babel-loader',
exclude: function(path) {
// 路径中含有 node_modules 的就不去解析。
var isNpmModule = !!path.match(node_modules/);
return isNpmModule;
},
query: {
presets: ['react', 'es2015-ie', 'stage-1']
}
} ]
}
甚至,在十分确信的情况下,使用 include 来限定 babel 的使用范围,进一步提高效率。
module: {
loaders: [ {
test: /\.js(x)*$/,
loader: 'babel-loader',
include: [
// 只去解析运行目录下的 src 和 demo 文件夹
path.join(process.cwd(), './src'),
path.join(process.cwd(), './demo')
],
query: {
presets: ['react', 'es2015-ie', 'stage-1']
}
} ]
}
使用externals,把不需要打包的模块排除在外
Webpack 可以配置 externals来将依赖的库指向全局变量,从而不再打包这个库。在页面上引入 Web 上的公用 CDN 服务。
webpack中配置:
externals: { moment: true }
页面html上引入script:
<script src="//apps.bdimg.com/libs/moment/2.8.3/moment-with-locales.min.js"></script>
等于让 Webpack 知道,对于 moment这个模块就不要打包啦,直接指向 window.moment就好。不过别忘了加载moment-with-locales.min.js,让全局中有 moment这个变量。
使用DLL & DllReference
除了正在开发的源代码之外,通常还会引入很多第三方 NPM 包,这些包我们不会进行修改,但是仍然需要在每次 build 的过程中消耗构建性能,那有没有什么办法可以减少这些消耗呢?DLLPlugin 就是一个解决方案,他通过前置这些依赖包的构建,来提高真正的 build 和 rebuild 的构建效率。
首先,我们来写一个 DLLPlugin 的 config 文件。定义webpack-dll-config.js文件:
const path = require('path');
const webpack=require('webpack');
const vendors=[
'react',
'react-dom',
'react-router',
'history',
'immutable',
'redux',
'react-redux',
'redux-router',
'redux-thunk',
'moment',
'es6-promise',
'whatwg-fetch',
'lodash'
];
module.exports={
entry:{
'vendor':vendors,
},
output:{
path:path.join(__dirname, 'dist'),
filename:'[name].dll.js',//[name]的部分由entry的名字替换
/**
* output.library
* 将会定义为 window.${output.library}
* 在这次的例子中,将会定义为`window.vendor_library`
*/
library:'[name]_library',
},
plugins:[
new webpack.DllPlugin({
/**
* path 定义 manifest 文件生成的位置
*/
path:'manifest.json',
/**
* name 是dll暴露的对象名,要跟 output.library 保持一致;
* dll bundle 输出到那个全局变量上
*/
name: '[name]_library',
context:__dirname //是解析包路径的上下文,这个要跟接下来配置的 webpack.config.js 一致。
})
]
}
执行 命令【webpack --config webpack-dll-config.js 】就会在 dist 目录下生成 dll bundle 和在根目录生成对应的 manifest文件。manifest 文件的格式大致如下,由包含的 module 和对应的 id 的键值对构成。
接下来通过 DLLReferencePlugin 来使用刚才生成的 DLL Bundle。在默认的webpack-config.js中添加plugins:
new webpack.DllReferencePlugin({
context: __dirname,//context 需要跟dll中的保持一致,这个用来指导 Webpack 匹配 manifest 中库的路径;
manifest: require('./manifest.json')
}),
最后在页面中要引入两个js
<body>
<script src="dist/vendor.dll.js"></script>
<script src="dist/bundle.js"></script>
</body>
使用DLL这样的方式的可以把第三方包和自己的代码包分离,有修改也只需要发布自己的代码包。
关于优化插件OPTIMIZATION
webpack 提供了一些可以优化浏览器端性能的优化插件,如UglifyJsPlugin,OccurrenceOrderPlugin 和 DedupePlugin,都很实用,也都在消耗构建性能(UglifyJsPlugin 非常耗性能),如果你是在开发环境下,这些插件最好都不要使用,毕竟脚本大一些,跑的慢一些这些比起每次构建要耗费更多时间来说,显然还是后者更会消磨开发者的耐心,因此,只在正产环境中使用 OPTIMIZATION。
CommonsChunkPlugin
当你的 webpack 构建任务中有多个入口文件,而这些文件都 require 了相同的模块,如果你不做任何事情,webpack 会为每个入口文件引入一份相同的模块,显然这样做,会使得相同模块变化时,所有引入的 entry 都需要一次 rebuild,造成了性能的浪费,CommonsChunkPlugin 可以将相同的模块提取出来单独打包,进而减小 rebuild 时的性能消耗。
同时将多个入口文件相同的js打包处一个共同的后,也可以利用缓存,使加载第二页面的时候不需要再加载共同文件。
commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common', 'common.[hash].js');