Webpack是个很流行的打包工具,但其打包速度却一直被吐槽着css
若是不用上一些打包的优化建议,单单打包两三个文件就能花上好几秒,放上几十个入口文件依赖几百上千个包的话,几分钟十几分钟妥妥的html
本文整理了常见的一些方法,部分使用以后就看到了很大改善,部分没什么明显的变化,也多是项目规模还不够大,先记录一下方法也好node
仍是太慢了,快快使用Webpack4react
webpack支持监听模式,此时须要从新编译时就能够进行增量构建,增量构建是很快的,基本不到一秒或几秒以内就能从新编译好jquery
注意区分一下开发环境和线上环境,开发环境启用热更新替换webpack
// 开发环境设置本地服务器,实现热更新 devServer: { contentBase: path.resolve(__dirname, 'static'), // 提供给外部访问 host: '0.0.0.0', port: 8388, // 容许开发服务器访问本地服务器的包JSON文件,防止跨域 headers: { 'Access-Control-Allow-Origin': '*' }, // 设置热替换 hot: true, // 设置页面引入 inline: true }, // 文件输出配置 output: { // 设置路径,防止访问本地服务器相关资源时,被开发服务器认为是相对其的路径 publicPath: 'http://localhost:8188/dist/js/', }, // 插件配置 plugins: [ // 热更新替换 new webpack.HotModuleReplacementPlugin() ]
线上环境的编译,加个 --watch 参数就能够了git
不少配置,在开发阶段是不须要去作的,咱们能够区分出开发和线上的两套配置,这样在须要上线的时候再全量编译便可es6
好比说 代码压缩、目录内容清理、计算文件hash、提取CSS文件等github
配置devtool能够支持使用sourceMap,但有些是耗时严重的,这个得多试试web
自带的JS压缩插件是单线程执行的,而webpack-parallel-uglify-plugin能够并行的执行,在个人小demo中使用后,速度直接从25s变成了14s
new webpack.optimize.UglifyJsPlugin({ sourceMap: true, compress: { warnings: false } }), ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin') new ParallelUglifyPlugin({ cacheDir: '.cache/', uglifyJS:{ output: { comments: false }, compress: { warnings: false } } }),
听闻这个版本以上的速度会慢许多,不过在个人小demo中还没看到明显变化
fast-sass-loader能够并行地处理sass,在提交构建以前会先组织好代码,速度也会快一些
babel-loader在执行的时候,可能会产生一些运行期间重复的公共文件,形成代码体积大冗余,同时也会减慢编译效率
能够加上cacheDirectory参数或使用 transform-runtime 插件试试
// webpack.config.js use: [{ loader: 'babel-loader', options: { cacheDirectory: true }] // .bablerc { "presets": [ "env", "react" ], "plugins": ["transform-runtime"] }
好比jQuery插件,react, react-dom等,代码量是不少的,打包起来可能会很耗时
能够直接用标签引入,而后在webpack配置里使用 expose-loader 或 externals 或 ProvidePlugin 提供给模块内部使用相应的变量
// @1 use: [{ loader: 'expose-loader', options: '$' }, { loader: 'expose-loader', options: 'jQuery' }] // @2 externals: { jquery: 'jQuery' }, // @3 new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery', 'window.jQuery': 'jquery' }),
这种方式其实和externals是相似的,主要用于有些模块没有能够在<script>标签中引入的资源(纯npm包)
Dll是动态连接库的意思,实际上就是将这些npm打包生成一个JSON文件,这个文件里包含了npm包的路径对应信息
这两个插件要一块儿用
首先,新建一个dll.config.js配置文件,先用webpack来打包这个文件
const webpack = require('webpack'); const path = require('path'); module.exports = { output: { // 将会生成./ddl/lib.js文件 path: path.resolve(__dirname, 'ddl'), filename: '[name].js', library: '[name]', }, entry: { "lib": [ 'react', 'react-dom', 'jquery' // ...其它库 ], }, plugins: [ new webpack.DllPlugin({ // 生成的映射关系文件 path: 'manifest.json', name: '[name]', context: __dirname, }), ], };
在manifest.json文件中就是相应的包对应的信息
而后在咱们的项目配置文件中配置DllReferencePlugin 使用这个清单文件
// 插件配置 plugins: [ new webpack.DllReferencePlugin({ context: __dirname, manifest: require('./manifest.json') }),
使用CommonsChunkPlugin提取公共的模块,能够减小文件体积,也有助于浏览器层的文件缓存,仍是比较推荐的
// 提取公共模块文件 new webpack.optimize.CommonsChunkPlugin({ chunks: ['home', 'detail'], // 开发环境下须要使用热更新替换,而此时common用chunkhash会出错,能够直接不用hash filename: '[name].js' + (isProduction ? '?[chunkhash:8]' : ''), name: 'common' }), // 切合公共模块的提取规则,有时后你须要明确指定默认放到公共文件的模块 // 文件入口配置 entry: { home: './src/js/home', detail: './src/js/detail', // 提取jquery入公共文件 common: ['jquery', 'react', 'react-dom'] },
HappyPack会采用多进程去打包构建,使用方式仍是蛮简单的,但并非支持全部的loader
首先引入,定义一下这个插件所开启的线程,推荐是四个,其实也能够直接使用默认的就好了
HappyPack = require('happypack'), os = require('os'), happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
而后在module的规则里改动一下,引入它,其中 id是一个标识符
{ test: /\.jsx?$/, // 编译js或jsx文件,使用babel-loader转换es6为es5 exclude: /node_modules/, loader: 'HappyPack/loader?id=js' // use: [{ // loader: 'babel-loader', // options: { // } // }] }
而后咱们调用插件,设置匹配的id,而后相关的配置能够直接把use:的规则部分套在loaders上
new HappyPack({ id: 'js', loaders: [{ loader: 'babel-loader', options: { // cacheDirectory: true } }] }),
要注意的第一点是,它对file-loader和url-loader支持很差,因此这两个loader就不须要换成happypack了,其余loader能够相似地换一下
要注意的第二点是,使用ExtractTextWebpackPlugin提取css文件也不是彻底就能转换过来,因此须要小小的改动一下,好比
module: { rules: [{ test: /\.css$/, // loader: 'HappyPack/loader?id=css' // 提取CSS文件 use: cssExtractor.extract({ // 若是配置成不提取,则此类文件使用style-loader插入到<head>标签中 fallback: 'style-loader', use: 'HappyPack/loader?id=css' // use: [{ // loader: 'css-loader', // options: { // // url: false, // minimize: true // } // }, // // 'postcss-loader' // ] }) }, { test: /\.scss$/, // loader: 'HappyPack/loader?id=scss' // 编译Sass文件 提取CSS文件 use: sassExtractor.extract({ // 若是配置成不提取,则此类文件使用style-loader插入到<head>标签中 fallback: 'style-loader', use: 'HappyPack/loader?id=scss' // use: [ // 'css-loader', // // 'postcss-loader', // { // loader: 'sass-loader', // options: { // sourceMap: true, // outputStyle: 'compressed' // } // } // ] }) }
由于它是直接函数调用的,咱们就放到里层的use规则就好了,而后配置插件便可
plugins: [ new HappyPack({ id: 'css', loaders: [{ loader: 'css-loader', options: { // url: false, minimize: true } }] }), new HappyPack({ id: 'scss', loaders: [{ 'loader': 'css-loader' }, { loader: 'fast-sass-loader', options: { sourceMap: true, outputStyle: 'compressed' } }] }),
在webpack打包时,会有各类各样的路径要去查询搜索,咱们能够加上一些配置,让它搜索地更快
好比说,方便改为绝对路径的模块路径就改一下,以纯模块名来引入的能够加上一些目录路径
还能够善于用下resolve alias别名 这个字段来配置
还有exclude等的配置,避免多余查找的文件,好比使用babel别忘了剔除不须要遍历的
{ test: /\.jsx?$/, // 编译js或jsx文件,使用babel-loader转换es6为es5 exclude: /node_modules/, use: [{ loader: 'babel-loader', options: { } }] }
检查一下代码,看看是否是有不须要引入的模块出如今代码里
webpack编译时加上参数 --json > stat.json 后,能够上传到 webpack-analyse 、webpack-visualizer 等分析站点上,看看打包的模块信息
这是webpack3的新特性(Scope Hoisting),实际上是借鉴了Rollup打包工具来的,它将一些有联系的模块,放到一个闭包函数里面去,经过减小闭包函数数量从而加快JS的执行速度
new webpack.optimize.ModuleConcatenationPlugin({ })
webpack打包的时候,有时不须要解析某些模块的依赖(这些模块并无依赖,或者并根本就没有模块化),咱们能够直接加上这个参数,直接跳过这种解析
module: { noParse: /node_modules\/(jquey\.js)/ }
这个算是能够减少模块的体积吧,在必定程度上也是为用户考虑的,使用require.ensure来设置哪些模块须要异步加载,webpack会将它打包到一个独立的chunk中,
在某个时刻(好比用户点击了查看)才异步地加载这个模块来执行
$('.bg-input').click(() => { console.log('clicked, loading async.js') require.ensure([], require => { require('./components/async2').log(); require('./components/async1').log(); console.log('loading async.js done'); }); });
有些模块是能够以模块化来引入的,就是说能够只引入其中的一部分,好比说lodash
// 原来的引入方式 import {debounce} from 'lodash'; //按模块化的引入方式 import debounce from 'lodash/debounce';
主要是整理过来的,试用了几个方法,首次编译的速度能够从以前半分多钟减少到十秒左右了,固然,开启了热更新替换后简直美不可言
固然还有不少方法没整理出,这些方法是有使用场景的,并非每一个都须要用,须要在本身的项目中尝试,结合配置它的复杂性和带来的效应来权衡。