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';
主要是整理過來的,試用了幾個方法,首次編譯的速度能夠從以前半分多鐘減少到十秒左右了,固然,開啓了熱更新替換後簡直美不可言
固然還有不少方法沒整理出,這些方法是有使用場景的,並非每一個都須要用,須要在本身的項目中嘗試,結合配置它的複雜性和帶來的效應來權衡。