Webpack是如今主流的功能強大的模塊化打包工具,在使用Webpack時,若是不注意性能優化,有很是大的可能會產生性能問題,性能問題主要分爲開發時打包構建速度慢、開發調試時的重複性工做、以及輸出文件質量不高等,所以性能優化也主要從這些方面來分析。本文主要是根據本身的理解對《深刻淺出 Webpack》這本書進行總結,涵蓋了大部分的優化方法,能夠做爲Webpack性能優化時的參考和檢查清單。基於Webpack3.4版本,閱讀本文須要您熟悉Webpack基本使用方法。磨刀不誤砍柴工,讓咱們花點時間先掌握webpack性能優化吧。javascript
深刻淺出 Webpack、吳浩麟css
Webpack在啓動後會根據Entry
配置的入口出發,遞歸地解析所依賴的文件。這個過程分爲搜索文件和把匹配的文件進行分析、轉化的兩個過程,所以能夠從這兩個角度來進行優化配置。html
因爲Loader對文件的轉換操做很耗時,因此須要讓儘量少的文件被Loader處理java
module.export = {
modeule: {
rules: [
{
// 若是項目源碼中只有js文件,就不要寫成/\.jsx?$/,以提高正則表達式的性能
test: /\.js$/,
// babel-loader支持緩存轉換出的結果,經過cacheDirectory選項開啓
use: ['babel-loader?cacheDirectory'],
// 只對項目根目錄下的src目錄中的文件採用babel-loader
include: path.resolve(__dirname, 'src'),
}
]
}
}
複製代碼
resolve.modules:[path.resolve(__dirname, 'node_modules')]
複製代碼
避免層層查找。node
resolve.modules
告訴webpack去哪些目錄下尋找第三方模塊,默認值爲['node_modules']
,會依次查找./node_modules、../node_modules、../../node_modules。react
resolve.mainFields:['main']
複製代碼
設置儘可能少的值能夠減小入口文件的搜索步驟 第三方模塊爲了適應不一樣的使用環境,會定義多個入口文件,mainFields定義使用第三方模塊的哪一個入口文件,因爲大多數第三方模塊都使用main字段描述入口文件的位置,因此能夠設置單獨一個main值,減小搜索jquery
對龐大的第三方模塊設置resolve.alias
, 使webpack直接使用庫的min文件,避免庫內解析 如對於react:webpack
resolve.alias:{
'react':patch.resolve(__dirname,'./node_modules/react/dist/react.min.js')
}
複製代碼
這樣會影響Tree-Shaking,適合對總體性比較強的庫使用,若是是像lodash這類工具類的比較分散的庫,比較適合Tree-Shaking,避免使用這種方式。git
默認值:extensions:['.js', '.json']
,當導入語句沒帶文件後綴時,Webpack會根據extensions定義的後綴列表進行文件查找,因此:es6
require(./data)
要寫成require(./data.json)
module.noParse
字段告訴Webpack沒必要解析哪些文件,能夠用來排除對非模塊化庫文件的解析如jQuery、ChartJS,另外若是使用resolve.alias配置了react.min.js,則也應該排除解析,由於react.min.js通過構建,已是能夠直接運行在瀏覽器的、非模塊化的文件了。noParse值能夠是RegExp、[RegExp]、function
module:{ noParse:[/jquery|chartjs/, /react\.min\.js$/] }
DllPlugin動態連接庫插件,其原理是把網頁依賴的基礎模塊抽離出來打包到dll文件中,當須要導入的模塊存在於某個dll中時,這個模塊再也不被打包,而是去dll中獲取。爲何會提高構建速度呢?緣由在於dll中大多包含的是經常使用的第三方模塊,如react、react-dom,因此只要這些模塊版本不升級,就只需被編譯一次。我認爲這樣作和配置resolve.alias和module.noParse的效果有殊途同歸的效果。
使用方法:
使用DllPlugin配置一個webpack_dll.config.js來構建dll文件:
// webpack_dll.config.js
const path = require('path');
const DllPlugin = require('webpack/lib/DllPlugin');
module.exports = {
entry:{
// 將react相關的模塊放到一個單獨的動態連接庫中
react:['react','react-dom'],
polyfill:['core-js/fn/promise','whatwg-fetch']
},
output:{
filename:'[name].dll.js',
path:path.resolve(__dirname, 'dist'),
library:'_dll_[name]', //dll的全局變量名
},
plugins:[
new DllPlugin({
name:'_dll_[name]', //dll的全局變量名
path:path.join(__dirname,'dist','[name].manifest.json'),//描述生成的manifest文件
})
]
}
複製代碼
須要注意DllPlugin的參數中name值必須和output.library值保持一致,而且生成的manifest文件中會引用output.library值。
最終構建出的文件:
|-- polyfill.dll.js
|-- polyfill.manifest.json
|-- react.dll.js
└── react.manifest.json
複製代碼
其中xx.dll.js包含打包的n多模塊,這些模塊存在一個數組裏,並以數組索引做爲ID,經過一個變量假設爲_xx_dll暴露在全局中,能夠經過window._xx_dll訪問這些模塊。xx.manifest.json文件描述dll文件包含哪些模塊、每一個模塊的路徑和ID。而後再在項目的主config文件裏使用DllReferencePlugin插件引入xx.manifest.json文件。
在主config文件裏使用DllReferencePlugin插件引入xx.manifest.json文件:
//webpack.config.json
const path = require('path');
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
module.exports = {
entry:{ main:'./main.js' },
//... 省略output、loader等的配置
plugins:[
new DllReferencePlugin({
manifest:require('./dist/react.manifest.json')
}),
new DllReferenctPlugin({
manifest:require('./dist/polyfill.manifest.json')
}),
// 須要手動引入react.dll.js
new AddAssetHtmlWebpackPlugin(
{ filepath: path.resolve(__dirname,'dll/react.dll.js') }
)
]
}
複製代碼
最終構建生成main.js
執行構建
webpack --config webpack_dll.config.js
複製代碼
在整個構建流程中,最耗時的就是Loader對文件的轉換操做了,而運行在Node.js之上的Webpack是單線程模型的,也就是隻能一個一個文件進行處理,不能並行處理。HappyPack能夠將任務分解給多個子進程,最後將結果發給主進程。JS是單線程模型,只能經過這種多進程的方式提升性能。
HappyPack的核心原理就是將這部分任務分解到多個進程中去並行執行,從而減小總的構建時間。
HappyPack使用以下:
npm i -D happypack
// webpack.config.json
const path = require('path');
const HappyPack = require('happypack');
// 構建出共享進程池,包含5個子進程 多個HappyPack實例都用同個進程池中的子進程處理任務,以防資源佔用過多
// const happyThreadPool = HappyPack.ThreadPool({size: 5});
module.exports = {
//...
module:{
rules:[{
test:/\.js$/,
// 將對.js文件的處理轉交給id爲babel的HappyPack的實例
use:['happypack/loader?id=babel']
exclude:path.resolve(__dirname, 'node_modules')
},{
test:/\.css/,
// 將對.css文件的處理轉交給id爲css的HappyPack的實例
use:['happypack/loader?id=css']
}],
plugins:[
new HappyPack({
id:'babel',
loaders:['babel-loader?cacheDirectory'],
//threads: 3,// 開啓幾個子進程去處理這一類的文件,默認3個
//verbose: true,// 是否容許happyPack輸出日誌,默認true
//threadPool: happyThreadPool// 表明共享進程池
}),
new HappyPack({
id:'css',
loaders:['css-loader']
})
]
}
}
複製代碼
使用UglifyJS插件壓縮JS代碼時,須要先將代碼解析成Object表示的AST(抽象語法樹),再去應用各類規則去分析和處理AST,因此這個過程計算量大耗時較多。ParallelUglifyPlugin能夠開啓多個子進程,每一個子進程使用UglifyJS壓縮代碼,能夠並行執行,能顯著縮短壓縮時間。
使用也很簡單,把原來的UglifyJS插件換成本插件便可,使用以下:
npm i -D webpack-parallel-uglify-plugin
// webpack.config.json
const ParallelUglifyPlugin = require('wbepack-parallel-uglify-plugin');
//...
plugins: [
new ParallelUglifyPlugin({
uglifyJS:{
//...這裏放uglifyJS的參數
},
//...其餘ParallelUglifyPlugin的參數,設置cacheDir能夠開啓緩存,加快構建速度
})
]
複製代碼
經過new ParallelUglifyPlugin()實例化時,支持如下參數:
開發過程當中修改源碼後,須要自動構建和刷新瀏覽器,以查看效果。這個過程可使用Webpack實現自動化,Webpack負責監聽文件的變化,DevServer負責刷新瀏覽器。
Webpack可使用兩種方式開啓監聽:1. 啓動webpack時加上--watch參數;2. 在配置文件中設置watch:true。此外還有以下配置參數。合理設置watchOptions能夠優化監聽體驗。
module.exports = {
watch: true,
watchOptions: {
ignored: /node_modules/,
aggregateTimeout: 300, //文件變更後多久發起構建,越大越好
poll: 1000, //每秒詢問次數,越小越好
}
}
複製代碼
ignored:設置不監聽的目錄,排除node_modules後能夠顯著減小Webpack消耗的內存
aggregateTimeout:文件變更後多久發起構建,避免文件更新太快而形成的頻繁編譯以致卡死,越大越好,下降重構頻率
poll:經過向系統輪詢文件是否變化來判斷文件是否改變,poll爲每秒詢問次數,越小越好,下降檢查頻率
DevServer刷新瀏覽器有兩種方式:
默認狀況下,以及 devserver: {inline:true}
都是採用第一種方式刷新頁面。第一種方式DevServer由於不知道網頁依賴哪些Chunk,因此會向每一個chunk中都注入客戶端代碼,當要輸出不少chunk時,會致使構建變慢。而一個頁面只須要一個客戶端,因此關閉inline模式能夠減小構建時間,chunk越多提高越明顯。關閉方式:
devserver:{inline:false}
關閉inline後入口網址變爲http://localhost:8080/webpack-dev-server/
另外devServer.compress
參數可配置是否採用Gzip壓縮,默認爲false
模塊熱替換不刷新整個網頁而只從新編譯發生變化的模塊,並用新模塊替換老模塊,因此預覽反應更快,等待時間更少,同時不刷新頁面能保留當前網頁的運行狀態。原理也是向每個chunk中注入代理客戶端來鏈接DevServer和網頁。開啓方式:
開啓後若是修改子模塊就能夠實現局部刷新,但若是修改的是根JS文件,會整頁刷新,緣由在於,子模塊更新時,事件一層層向上傳遞,直到某層的文件接收了當前變化的模塊,而後執行回調函數。若是一層層向外拋直到最外層都沒有文件接收,就會刷新整頁。
使用 NamedModulesPlugin
可使控制檯打印出被替換的模塊的名稱而非數字ID,另外同webpack監聽,忽略node_modules目錄的文件能夠提高性能。
const NamedModulesPlugin = require('webpack/lib/NamedModulesPlugin');
modules.export = {
plugins: [
// 顯示出被替換模塊的名稱
new NamedModulesPlugin()
]
}
複製代碼
代碼運行環境分爲開發環境和生產環境,代碼須要根據不一樣環境作不一樣的操做,許多第三方庫中也有大量的根據開發環境判斷的if else代碼,構建也須要根據不一樣環境輸出不一樣的代碼,因此須要一套機制能夠在源碼中區分環境,區分環境以後可使輸出的生產環境的代碼體積減少。Webpack中使用DefinePlugin插件來定義配置文件適用的環境。
const DefinePlugin = require('webpack/lib/DefinePlugin');// 只對webpack須要處理的代碼有效
//...
plugins:[
new DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
})
]
複製代碼
注意,JSON.stringify('production')
的緣由是,環境變量值須要一個雙引號包裹的字符串,而stringify後的值是'"production"'
而後就能夠在源碼中使用定義的環境:
if(process.env.NODE_ENV === 'production') {
console.log('你正在使用生產環境')
// TODO
}else {
console.log('你正在使用開發環境')
// TODO
}
複製代碼
當代碼中使用了process時,Webpack會自動打包進process模塊的代碼以支持非Node.js的運行環境,這個模塊的做用是模擬Node.js中的process,以支持process.env.NODE_ENV === 'production'
語句。
壓縮JS:Webpack內置UglifyJS插件、ParallelUglifyPlugin
會分析JS代碼語法樹,理解代碼的含義,從而作到去掉無效代碼、去掉日誌輸入代碼、縮短變量名等優化。經常使用配置參數以下:
const UglifyJSPlugin = require('webpack/lib/optimize/UglifyJsPlugin');
//...
plugins: [
new UglifyJSPlugin({
compress: {
warnings: false, //刪除無用代碼時不輸出警告
drop_console: true, //刪除全部console語句,能夠兼容IE
collapse_vars: true, //內嵌已定義但只使用一次的變量
reduce_vars: true, //提取使用屢次但沒定義的靜態值到變量
},
output: {
beautify: false, //最緊湊的輸出,不保留空格和製表符
comments: false, //刪除全部註釋
}
})
]
複製代碼
使用webpack --optimize-minimize
啓動webpack,能夠注入默認配置的UglifyJSPlugin
壓縮ES6:第三方UglifyJS插件
隨着愈來愈多的瀏覽器支持直接執行ES6代碼,應儘量的運行原生ES6,這樣比起轉換後的ES5代碼,代碼量更少,且ES6代碼性能更好。直接運行ES6代碼時,也須要代碼壓縮,第三方的uglify-webpack-plugin提供了壓縮ES6代碼的功能:
npm i -D uglify-webpack-plugin@beta //要使用最新版本的插件
//webpack.config.json
const UglifyESPlugin = require('uglify-webpack-plugin');
//...
plugins:[
new UglifyESPlugin({
uglifyOptions: { //比UglifyJS多嵌套一層
compress: {
warnings: false,
drop_console: true,
collapse_vars: true,
reduce_vars: true
},
output: {
beautify: false,
comments: false
}
}
})
]
複製代碼
另外要防止babel-loader轉換ES6代碼,要在.babelrc中去掉babel-preset-env,由於正是babel-preset-env負責把ES6轉換爲ES5。
壓縮CSS:css-loader?minimize、PurifyCSSPlugin
cssnano基於PostCSS,不只是刪掉空格,還能理解代碼含義,例如把color:#ff0000
轉換成 color:red
,css-loader內置了cssnano,只須要使用 css-loader?minimize
就能夠開啓cssnano壓縮。
另一種壓縮CSS的方式是使用PurifyCSSPlugin,須要配合 extract-text-webpack-plugin
使用,它主要的做用是能夠去除沒有用到的CSS代碼,相似JS的Tree Shaking。
Tree Shaking能夠剔除用不上的死代碼,它依賴ES6的import、export的模塊化語法,最早在Rollup中出現,Webpack 2.0將其引入。適合用於Lodash、utils.js等工具類較分散的文件。它正常工做的前提是代碼必須採用ES6的模塊化語法,由於ES6模塊化語法是靜態的(在導入、導出語句中的路徑必須是靜態字符串,且不能放入其餘代碼塊中)。若是採用了ES5中的模塊化,例如module.export = {...}、require( x+y )、if (x) { require( './util' ) },則Webpack沒法分析出能夠剔除哪些代碼。
啓用Tree Shaking:
修改.babelrc以保留ES6模塊化語句:
{
"presets": [
[
"env",
{ "module": false }, //關閉Babel的模塊轉換功能,保留ES6模塊化語法
]
]
}
複製代碼
啓動webpack時帶上 --display-used-exports能夠在shell打印出關於代碼剔除的提示
使用UglifyJSPlugin,或者啓動時使用--optimize-minimize
在使用第三方庫時,須要配置 resolve.mainFields: ['jsnext:main', 'main']
以指明解析第三方庫代碼時,採用ES6模塊化的代碼入口
CND加速的原理
CDN經過將資源部署到世界各地,使得用戶能夠就近訪問資源,加快訪問速度。要接入CDN,須要把網頁的靜態資源上傳到CDN服務上,在訪問這些資源時,使用CDN服務提供的URL。
因爲CDN會爲資源開啓長時間的緩存,例如用戶從CDN上獲取了index.html,即便以後替換了CDN上的index.html,用戶那邊仍會在使用以前的版本直到緩存時間過時。業界作法:
另外,HTTP1.x版本的協議下,瀏覽器會對於向同一域名並行發起的請求數限制在4~8個。那麼把全部靜態資源放在同一域名下的CDN服務上就會遇到這種限制,因此能夠把他們分散放在不一樣的CDN服務上,例如JS文件放在js.cdn.com下,將CSS文件放在css.cdn.com下等。這樣又會帶來一個新的問題:增長了域名解析時間,這個能夠經過dns-prefetch來解決 <link rel='dns-prefetch' href='//js.cdn.com'>
來縮減域名解析的時間。形如**//xx.com 這樣的URL省略了協議**,這樣作的好處是,瀏覽器在訪問資源時會自動根據當前URL採用的模式來決定使用HTTP仍是HTTPS協議。
總之,構建須要知足如下幾點:
最終配置:
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const {WebPlugin} = require('web-webpack-plugin');
//...
output:{
filename: '[name]_[chunkhash:8].js',
path: path.resolve(__dirname, 'dist'),
publicPatch: '//js.cdn.com/id/', //指定存放JS文件的CDN地址
},
module:{
rules:[{
test: /\.css/,
use: ExtractTextPlugin.extract({
use: ['css-loader?minimize'],
publicPatch: '//img.cdn.com/id/', //指定css文件中導入的圖片等資源存放的cdn地址
}),
},{
test: /\.png/,
use: ['file-loader?name=[name]_[hash:8].[ext]'], //爲輸出的PNG文件名加上Hash值
}]
},
plugins:[
new WebPlugin({
template: './template.html',
filename: 'index.html',
stylePublicPath: '//css.cdn.com/id/', //指定存放CSS文件的CDN地址
}),
new ExtractTextPlugin({
filename:`[name]_[contenthash:8].css`, //爲輸出的CSS文件加上Hash
})
]
複製代碼
咱們但願經過cdn的方式引入資源
const AddAssetHtmlCdnPlugin = require('add-asset-html-cdn-webpack-plugin')
new AddAssetHtmlCdnPlugin(true,{
'jquery':'https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js'
})
複製代碼
可是在代碼中還但願引入jquery
來得到提示
import $ from 'jquery'
console.log('$',$)
複製代碼
可是打包時依然會將jquery
進行打包
externals:{
'jquery':'$'
}
複製代碼
在配置文件中標註jquery
是外部的,這樣打包時就不會將jquery進行打包了
原理
大型網站一般由多個頁面組成,每一個頁面都是一個獨立的單頁應用,多個頁面間確定會依賴一樣的樣式文件、技術棧等。若是不把這些公共文件提取出來,那麼每一個單頁打包出來的chunk中都會包含公共代碼,至關於要傳輸n份重複代碼。若是把公共文件提取出一個文件,那麼當用戶訪問了一個網頁,加載了這個公共文件,再訪問其餘依賴公共文件的網頁時,就直接使用文件在瀏覽器的緩存,這樣公共文件就只用被傳輸一次。
應用方法
把多個頁面依賴的公共代碼提取到common.js中,此時common.js包含基礎庫的代碼
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
//...
plugins:[
new CommonsChunkPlugin({
chunks:['a','b'], //從哪些chunk中提取
name:'common', // 提取出的公共部分造成一個新的chunk
})
]
複製代碼
找出依賴的基礎庫,寫一個base.js文件,再與common.js提取公共代碼到base中,common.js就剔除了基礎庫代碼,而base.js保持不變
//base.js
import 'react';
import 'react-dom';
import './base.css';
//webpack.config.json
entry:{
base: './base.js'
},
plugins:[
new CommonsChunkPlugin({
chunks:['base','common'],
name:'base',
//minChunks:2, 表示文件要被提取出來須要在指定的chunks中出現的最小次數,防止common.js中沒有代碼的狀況
})
]
複製代碼
獲得基礎庫代碼base.js,不含基礎庫的公共代碼common.js,和頁面各自的代碼文件xx.js。
頁面引用順序以下:base.js--> common.js--> xx.js
base.js是爲了長期緩存
原理
單頁應用的一個問題在於使用一個頁面承載複雜的功能,要加載的文件體積很大,不進行優化的話會致使首屏加載時間過長,影響用戶體驗。作按需加載能夠解決這個問題。具體方法以下:
作法
一個最簡單的例子:網頁首次只加載main.js,網頁展現一個按鈕,點擊按鈕時加載分割出去的show.js,加載成功後執行show.js裏的函數
//main.js
document.getElementById('btn').addEventListener('click',function(){
import(/* webpackChunkName:"show" */ './show').then((show)=>{
show('Webpack');
})
})
//show.js
module.exports = function (content) {
window.alert('Hello ' + content);
}
複製代碼
import(/* webpackChunkName:show */ './show').then()
是實現按需加載的關鍵,Webpack內置對import( *)語句的支持,Webpack會以./show.js
爲入口從新生成一個Chunk。代碼在瀏覽器上運行時只有點擊了按鈕纔會開始加載show.js,且import語句會返回一個Promise,加載成功後能夠在then方法中獲取加載的內容。這要求瀏覽器支持Promise API,對於不支持的瀏覽器,須要注入Promise polyfill。/* webpackChunkName:show */
是定義動態生成的Chunk的名稱,默認名稱是[id].js,定義名稱方便調試代碼。爲了正確輸出這個配置的ChunkName,還須要配置Webpack:
//...
output:{
filename:'[name].js',
chunkFilename:'[name].js', //指定動態生成的Chunk在輸出時的文件名稱
}
複製代碼
書中另外提供了更復雜的ReactRouter中異步加載組件的實戰場景。P212
原理:
Prepack是一個部分求值器,編譯代碼時提早將計算結果放到編譯後的代碼中,而不是在代碼運行時纔去求值。經過在編譯階段預先執行源碼來獲得執行結果,再直接將運行結果輸出以提高性能。可是如今Prepack還不夠成熟,用於線上環境還爲時過早。
使用方法
const PrepackWebpackPlugin = require('prepack-webpack-plugin').default;
module.exports = {
plugins:[
new PrepackWebpackPlugin()
]
}
複製代碼
原理
譯做「做用域提高」,是在Webpack3中推出的功能,它分析模塊間的依賴關係,儘量將被打散的模塊合併到一個函數中,但不能形成代碼冗餘,因此只有被引用一次的模塊才能被合併。因爲須要分析模塊間的依賴關係,因此源碼必須是採用了ES6模塊化的,不然Webpack會降級處理不採用Scope Hoisting。
使用方法
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');
//...
plugins:[
// 開啓Scope Hoisting
new ModuleConcatenationPlugin();
],
resolve:{
// 針對npm中的第三方模塊優先採用jsnext:main中指向的es6模塊化語法的文件
mainFields:['jsnext:main','browser','main']
複製代碼
}
`webpack --display-optimization-bailout` 輸出日誌中會提示哪一個文件致使了降級處理
3. **例子**
```javascript
let a = 1;
let b = 2;
let c = 3;
let d = a+b+c
export default d;
// 引入d
import d from './d';
console.log(d)
複製代碼
最終打包後的結果會變成
console.log(6)
啓動Webpack時帶上這兩個參數能夠生成一個json文件,輸出分析工具大多依賴該文件進行分析:
webpack --profile --json > stats.json
其中 --profile
記錄構建過程當中的耗時信息,--json
以JSON的格式輸出構建結果,>stats.json
是UNIX / Linux系統中的管道命令,含義是將內容經過管道輸出到stats.json文件中。
官方工具Webpack Analyse
打開該工具的官網webpack.github.io/anal...,就能夠獲得分析結果
webpack-bundle-analyzer
可視化分析工具,比Webapck Analyse更直觀。使用也很簡單:
webpack-bundle-analyzer
,瀏覽器會自動打開結果分析頁面。npm install --save-dev webpack-bundle-analyzer
複製代碼
使用插件
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');
mode !== "development" && new BundleAnalyzerPlugin()
複製代碼
默認就會展示當前應用的分析圖表
const path = require('path');
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
const {AutoWebPlugin} = require('web-webpack-plugin');
const HappyPack = require('happypack');
// 自動尋找 pages 目錄下的全部目錄,把每個目錄當作一個單頁應用
const autoWebPlugin = new AutoWebPlugin('./src/pages', {
// HTML 模版文件所在的文件路徑
template: './template.html',
// 提取出全部頁面公共的代碼
commonsChunk: {
// 提取出公共代碼 Chunk 的名稱
name: 'common',
},
});
module.exports = {
// AutoWebPlugin 會找爲尋找到的全部單頁應用,生成對應的入口配置,
// autoWebPlugin.entry 方法能夠獲取到生成入口配置
entry: autoWebPlugin.entry({
// 這裏能夠加入你額外須要的 Chunk 入口
base: './src/base.js',
}),
output: {
filename: '[name].js',
},
resolve: {
// 使用絕對路徑指明第三方模塊存放的位置,以減小搜索步驟
// 其中 __dirname 表示當前工做目錄,也就是項目根目錄
modules: [path.resolve(__dirname, 'node_modules')],
// 針對 Npm 中的第三方模塊優先採用 jsnext:main 中指向的 ES6 模塊化語法的文件,使用 Tree Shaking 優化
// 只採用 main 字段做爲入口文件描述字段,以減小搜索步驟
mainFields: ['jsnext:main', 'main'],
},
module: {
rules: [
{
// 若是項目源碼中只有 js 文件就不要寫成 /\.jsx?$/,提高正則表達式性能
test: /\.js$/,
// 使用 HappyPack 加速構建
use: ['happypack/loader?id=babel'],
// 只對項目根目錄下的 src 目錄中的文件採用 babel-loader
include: path.resolve(__dirname, 'src'),
},
{
test: /\.js$/,
use: ['happypack/loader?id=ui-component'],
include: path.resolve(__dirname, 'src'),
},
{
// 增長對 CSS 文件的支持
test: /\.css$/,
use: ['happypack/loader?id=css'],
},
]
},
plugins: [
autoWebPlugin,
// 使用 HappyPack 加速構建
new HappyPack({
id: 'babel',
// babel-loader 支持緩存轉換出的結果,經過 cacheDirectory 選項開啓
loaders: ['babel-loader?cacheDirectory'],
}),
new HappyPack({
// UI 組件加載拆分
id: 'ui-component',
loaders: [{
loader: 'ui-component-loader',
options: {
lib: 'antd',
style: 'style/index.css',
camel2: '-'
}
}],
}),
new HappyPack({
id: 'css',
// 如何處理 .css 文件,用法和 Loader 配置中同樣
loaders: ['style-loader', 'css-loader'],
}),
// 提取公共代碼
new CommonsChunkPlugin({
// 從 common 和 base 兩個現成的 Chunk 中提取公共的部分
chunks: ['common', 'base'],
// 把公共的部分放到 base 中
name: 'base'
}),
],
watchOptions: {
// 使用自動刷新:不監聽的 node_modules 目錄下的文件
ignored: /node_modules/,
}
};
複製代碼
const path = require('path');
const DefinePlugin = require('webpack/lib/DefinePlugin');
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const {AutoWebPlugin} = require('web-webpack-plugin');
const HappyPack = require('happypack');
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
// 自動尋找 pages 目錄下的全部目錄,把每個目錄當作一個單頁應用
const autoWebPlugin = new AutoWebPlugin('./src/pages', {
// HTML 模版文件所在的文件路徑
template: './template.html',
// 提取出全部頁面公共的代碼
commonsChunk: {
// 提取出公共代碼 Chunk 的名稱
name: 'common',
},
// 指定存放 CSS 文件的 CDN 目錄 URL
stylePublicPath: '//css.cdn.com/id/',
});
module.exports = {
// AutoWebPlugin 會找爲尋找到的全部單頁應用,生成對應的入口配置,
// autoWebPlugin.entry 方法能夠獲取到生成入口配置
entry: autoWebPlugin.entry({
// 這裏能夠加入你額外須要的 Chunk 入口
base: './src/base.js',
}),
output: {
// 給輸出的文件名稱加上 Hash 值
filename: '[name]_[chunkhash:8].js',
path: path.resolve(__dirname, './dist'),
// 指定存放 JavaScript 文件的 CDN 目錄 URL
publicPath: '//js.cdn.com/id/',
},
resolve: {
// 使用絕對路徑指明第三方模塊存放的位置,以減小搜索步驟
// 其中 __dirname 表示當前工做目錄,也就是項目根目錄
modules: [path.resolve(__dirname, 'node_modules')],
// 只採用 main 字段做爲入口文件描述字段,以減小搜索步驟
mainFields: ['jsnext:main', 'main'],
},
module: {
rules: [
{
// 若是項目源碼中只有 js 文件就不要寫成 /\.jsx?$/,提高正則表達式性能
test: /\.js$/,
// 使用 HappyPack 加速構建
use: ['happypack/loader?id=babel'],
// 只對項目根目錄下的 src 目錄中的文件採用 babel-loader
include: path.resolve(__dirname, 'src'),
},
{
test: /\.js$/,
use: ['happypack/loader?id=ui-component'],
include: path.resolve(__dirname, 'src'),
},
{
// 增長對 CSS 文件的支持
test: /\.css$/,
// 提取出 Chunk 中的 CSS 代碼到單獨的文件中
use: ExtractTextPlugin.extract({
use: ['happypack/loader?id=css'],
// 指定存放 CSS 中導入的資源(例如圖片)的 CDN 目錄 URL
publicPath: '//img.cdn.com/id/'
}),
},
]
},
plugins: [
autoWebPlugin,
// 開啓ScopeHoisting
new ModuleConcatenationPlugin(),
// 使用HappyPack
new HappyPack({
// 用惟一的標識符 id 來表明當前的 HappyPack 是用來處理一類特定的文件
id: 'babel',
// babel-loader 支持緩存轉換出的結果,經過 cacheDirectory 選項開啓
loaders: ['babel-loader?cacheDirectory'],
}),
new HappyPack({
// UI 組件加載拆分
id: 'ui-component',
loaders: [{
loader: 'ui-component-loader',
options: {
lib: 'antd',
style: 'style/index.css',
camel2: '-'
}
}],
}),
new HappyPack({
id: 'css',
// 如何處理 .css 文件,用法和 Loader 配置中同樣
// 經過 minimize 選項壓縮 CSS 代碼
loaders: ['css-loader?minimize'],
}),
new ExtractTextPlugin({
// 給輸出的 CSS 文件名稱加上 Hash 值
filename: `[name]_[contenthash:8].css`,
}),
// 提取公共代碼
new CommonsChunkPlugin({
// 從 common 和 base 兩個現成的 Chunk 中提取公共的部分
chunks: ['common', 'base'],
// 把公共的部分放到 base 中
name: 'base'
}),
new DefinePlugin({
// 定義 NODE_ENV 環境變量爲 production 去除 react 代碼中的開發時才須要的部分
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
// 使用 ParallelUglifyPlugin 並行壓縮輸出的 JS 代碼
new ParallelUglifyPlugin({
// 傳遞給 UglifyJS 的參數
uglifyJS: {
output: {
// 最緊湊的輸出
beautify: false,
// 刪除全部的註釋
comments: false,
},
compress: {
// 在UglifyJs刪除沒有用到的代碼時不輸出警告
warnings: false,
// 刪除全部的 `console` 語句,能夠兼容ie瀏覽器
drop_console: true,
// 內嵌定義了可是隻用到一次的變量
collapse_vars: true,
// 提取出出現屢次可是沒有定義成變量去引用的靜態值
reduce_vars: true,
}
},
}),
]
};
複製代碼
因爲字數的限制,有些內容沒法寫了,掘金的一大坑啊||-_-