寫在前頭--在公司搬磚也差很少一年了,眼看着項目愈來愈大,優化問題亟待解決。優化是一件很矛盾的事情,可是爲了詩和遠方,咱們仍是得走一趟坑坑窪窪的優化之路。css
本文可能涉及的內容--html
整個項目大概有60+個頁面,用到的組件大概150+,package裏面的依賴大概有70+個,應該勉強算得上是一箇中型的React的項目了。react
下面給你們看看咱們如今build一次項目的結果--webpack
打包時間約150s,打包完以後的資源gzip以後約1.2m,儘管以前分離了一些公用依賴,可是index包的體積達到了600+仍是使人難以接受的。css3
開始優化以前,最重要的就是搞清楚咱們到底要優化什麼。肯定了優化的目標才能着手思考優化方案,進而實施優化方案。結合對項目的bundle分析以及自身對項目的瞭解,咱們初步能夠定出如下幾點優化方向--git
首先咱們須要足夠了解咱們的項目,才能着手進行瘦身。在這裏有一個很給力的工具能夠推薦給你們webpack-bundle-analyzeres6
盜用一下github上的圖github
如圖所示,咱們能夠很清晰的看到每一個js文件裏的module組成,還能夠看到每一個module的大小以及module的組成成分,這對咱們分析代碼冗餘以及優化方向都可以提供很大的幫助。web
具體食用方式也很簡單--json
這樣一來咱們就對項目有了一個比較具體的認識,大到項目的依賴一覽,小到某個頁面的組件引用都能在分析報告中找到。接下來就能夠開始咱們的瘦身之旅了。
當咱們第一次看到bundle的分析報告時,總能找到一些出乎意料的「大個子」,若是是必不可缺的依賴則沒辦法,但若是是一些能夠被取代的依賴就有別的說法了。這裏恰好能夠看看以前我對create-react-app中moment.js依賴的處理,若是處理順利的話能夠很直觀的看到bundle大小的變化。
總結來講,若是有小的而且知足需求的依賴能夠替換,請不要遲疑;但若是沒有可知足的依賴,能夠嘗試本身造一個輪子,固然後者須要結合自身情況考慮。
相信你們在開發網站的時候都用到了很多依賴,可是這些依賴在輸出以後是和業務代碼打包在一塊兒的,這個明顯不符合咱們的預期。面對這些基本不會變動的依賴,咱們更傾向它們可以主動抱團而且遠離咱們的業務代碼。
這時候,就該使用webpack的CommonsChunkPlugin(貌似在wp4中已經被別的插件替代了,在此咱們先不討論wp4),它能夠幫助咱們將一些指定的module打包進指定的bundle裏。具體使用方案能夠參考wp官網中的相關介紹,有一個坑點就是--假若但願負責集合依賴bundle的文件名在打包時不變,則須要生成manifest。
咱們能夠將一些低頻頁面完全拉出項目,拿咱們的項目來講,一共60+個頁面,用戶大多都是隻會訪問其中的幾個或十幾個頁面,不可能將全部的頁面都訪問一遍。這樣一來就必然會有一些頁面的訪問頻率相比之下會十分低下,該怎麼處理這些頁面呢?這裏有幾種方案:
以上三種方案我的以爲各有優劣,第一個方案很簡單
,適用場景就是相似Q&A的靜態頁,能夠將其脫離項目,寫成靜態頁部署在其餘的地方,可是很差的地方就是可能無法複用原項目組件。
方案二呢則是時間至上
的方案,能夠作到快速遷移,可是很差的地方在於遷移出去的頁面其實仍是塞在一個籃子裏,只不過換了個新的籃子罷了。
方案三則是質量至上
的方案,以時間做爲成本,換來一個新的「低頻頁面項目」。具體要使用哪一種方案,我以爲也是根據當前項目情況而定,不追求最完美,追求最合適。
首屏加載,大概是優化的永恆話題,全部的優化都避不開這一個話題,由於只有它能最直觀的讓「你們」都感覺到咱們此次優化的成果。對於用戶來講,認爲網頁首屏很快的標準其實很單一,就是一打開頁面,看了多久的白屏。因此咱們須要作的就是弱化用戶對白屏的感知
,圍繞這一點,我的認爲,首屏加載這一優化能夠有兩個方向:一個是速度,另外一個是體驗。
其實這個就是前段時間很火的「骨架屏」,咱們能夠在頁面真正被渲染出來以前,先給用戶看到一個「假的」頁面,等到某個時間節點(例如數據已經準備完畢...)就將真正的內容替換上去。這裏有一個我寫的很不走心的例子:)
在這個優惠券列表頁面個人處理方案是,初始化頁面的時候就渲染3個列表項骨架,等待接口數據返回就將真實內容替換上去。
在咱們的首屏其實也是相似的,咱們能夠根據首屏的展現結構,作一個匹配的骨架組件,而後按需求進行展現便可,這樣能夠有效減小用戶看到白屏的時間。下面是我這個骨架的代碼,優化的空間很大,不過因爲優先級不是很高,因此就沒有進行迭代了。
大概結構就是這樣,樣式方面很粗暴,由於每一項都是獨立的一個組件,直接能夠用absolute定位堆砌一個簡潔的佔位列表項。裏面那個相似進度條的效果則是經過css3的animation實現的,咱們能夠將每一個block的背景色變成漸變的,而後經過background-positon的變化來達到圖中的效果。
這應該是個老生常談的優化方向了,原理大概是將視圖以外的圖片都用同一個佔位圖進行佔位,將其真正的圖連接存在data-*中,經過監聽滾動來判斷圖片是否進入視圖中,來控制img標籤src的值。具體的實現不少地方都能搜到,你們能夠根據自身狀況,按需選擇。
其實在整個優化過程當中個人重心是放在這個地方的,其餘的都是半路上想到的...
讓咱們回想一下,上面咱們講過將低頻頁面分離,那麼,必然就有會那麼幾個訪問量十分高的頁面,那麼對於這幾個頁面應該怎麼辦呢?
由於訪問頻率高,因此咱們能夠認爲這些頁面與咱們的核心業務是強相關的,因此將其分離就顯得不那麼划算了(極可能會出現維護多套代碼的窘況)。
可是這樣高頻頁面纔是優化的重點區域呀,應該怎麼辦呢?面對這樣頁面咱們仍是可使用懶加載大法(頁面懶加載 || 組件懶加載 || 依賴懶加載)。
想要在js層面實現各種懶加載,咱們都須要藉助webpack中的特性Code Splitting,它能夠將咱們原本打包在一塊兒的js分解成一塊一塊,並能達到按需加載並使用的效果。
由於咱們使用了react-router,因此咱們可使用react-router的getComponent輕鬆達到頁面懶加載這一需求。以下圖所示,將mainpage這樣引入route的話,在打包的時候會將其分離成一個獨立的js。
組件和依賴的懶加載也是十分簡單的,以下圖這樣寫就能達到懶加載的效果,但若是咱們使用了babel則須要修改一下babel的配置,讓它可以順利解析動態import()的語法。
咱們一般的優化都是爲了用戶而優化,但其實爲了咱們自身可以良好的開發體驗,也應該爲開發人員優化優化開發體驗,打包優化則成了不二之選。
因爲時間緣由,在公司的項目中並無嘗試使用DLL,可是看到網上有很多同窗都推薦介紹了它,因此我選擇在此說起一下~有關於webpack DLL的文檔
因爲項目是在差很少一年多之前正式啓動的,因此接手的時候是webpack1.x,在剛接手的時候爲了懶加載硬是升級到了2.x。
可是到如今發現,2.x好像也不夠用了,畢竟已經落下了兩個大版本了,更新以後的新特性、新功能或是新優化都應該成爲我將項目遷移至新版本的動力。wp4具體的配置細節,在掘金上就見到過挺多同窗介紹的,這裏我想介紹一下,我是怎麼將舊項目遷移到wp4的:
在開始進行版本遷移以前,我設想了兩個方案,一個是在原有項目上直接升級並修改配置;第二個方案是新建webpack4項目,搭建好以後將業務代碼遷移過來。
通過對成功率以及時間成本的評估,我最後選擇的是第二個方案。那麼這個新建的項目應該完善到什麼程度才能進行遷移呢?我我的是通過如下幾個步驟--
這回的項目和以前的都不太同樣了,咱們沒有藉助大神們的腳手架來搭建項目骨架了,咱們須要本身從零開始一點一點的摸索webpack的用法以及新舊版本的差別。
關於一些基礎的知識以及配置十分推薦查閱webpack官網的文檔以及一些以前參考過的文章webpack4-用之初體驗、webpack 4.0.0-beta.0 新特性介紹。
相信你們看完這些以後都會對wp的配置有基本的認知,緊接下來就是建目錄、裝依賴巴拉巴拉。最終咱們會獲得一個這樣的目錄結構--
咱們應該如何判斷將項目代碼遷入新項目的時機呢?很簡單,當這個新項目能夠正常的調試或打包一個相應框架的Hello World便可。
拿咱們的項目來講,搭建完項目目錄以及一些基礎配置以後,接下來就是徹底模擬原有項目的技術棧,在新的項目中寫幾個簡單的demo頁。固然這些demo頁並非隨便寫的,是帶有目的性的,按我此次的經從來說,我寫了這麼幾個文件index.js、App.js、Hello.js、Global.scss、router.js。
剝開非核心依賴,咱們最核心的依賴其實就是react & react-router & sass,只要webpack可以正確的解析es6和sass咱們就能很大程度的還原舊項目的環境。(babel中與jsx相關的配置在package裏)
上面的demo完成以後,咱們新項目就初具雛形了,接下來咱們就須要將舊項目package.json遷移到新項目中,這裏須要注意的幾點是:
① "scripts"中的指令要注意,咱們要看裏面的每條指令分別有什麼做用,而後再思考應該怎麼在新項目中寫一個功能同樣的指令。
② "dependencies" && "devDependencies"舊項目的依賴也應該無縫遷移過來,不過咱們能夠趁這個機會把沒有用到的依賴剔除出去。
③ "babel" || "autoprefixer"等輔助工具的配置也應該與舊項目保持一致。
上述步驟都跑通以後,就能刪掉原有的demo,將舊項目的全部業務代碼都遷移過來。接下來就看着報錯,一個一個修復便可。這裏遇到這麼幾個坑,困擾了我許久。頁面很順利的遷移過來了,依賴補全以後也順利的跑起來了。
可是在dev環境下切換頁面總是會404。相信你們看到這裏就懂了,我用了history模式的路由,在devServer中應該要加上這樣一行配置
devServer {
historyApiFallback: true
}
複製代碼
好啦,404消滅以後又有新的情況了,靜態資源總是引用不到,這是爲啥?其實這是由於咱們在output的時候沒有設置publicPath引發的,在dev的webpack.config中個人output是這樣配置的
output: {
path: path.resolve('dist'),
publicPath: '/'
},
// ...
devServer = {
contentBase: './dist',
port: 9000,
historyApiFallback: true
}
複製代碼
這個問題解決以後,咱們的開發環境算是還原的差很少了。接下來就該踩踩打包的坑了,我遇到的第一個問題就是,打包完成以後,文件夾裏面只有打包輸出,index.html咋不見了...這說好的不太同樣。後面發現是少了copy-webpack-plugin
const CopyWebpackPlugin = require('copy-webpack-plugin')
const config = {
// ...
plugins: [
new CopyWebpackPlugin([{from: 'public', to: ''}])
]
}
複製代碼
加上這個依賴以後,咱們public文件夾的內容就會乖乖的在dist裏面出現。但緊接着又出現新的問題了,第二次打包的時候,怎麼dist沒有被清空呢?和上面同樣,年輕的我少用了一個插件clean-webpack-plugin
const CleanWebpackPlugin = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
let cleanOptions = {
root: path.join(__dirname, '..'),
verbose: true,
dry: false
}
const config = {
// ...
plugins: [
new CleanWebpackPlugin(['dist'], cleanOptions),
new CopyWebpackPlugin([{from: 'public', to: ''}])
]
}
複製代碼
完成上述配置後,每次打包webpack都會清空dist文件夾,而且在打包完成以後,將public中的內容複製到dist。好了看來應該能夠了,可是在本地開了個服務器跑頁面的時候發現,各類靜態資源404。這又是什麼玩意?實話說在這裏踩坑時間是最多的,可是解決方案又是使人窒息的簡單...都怪本身沒有好好看文檔
const CleanWebpackPlugin = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
let cleanOptions = {
root: path.join(__dirname, '..'),
verbose: true,
dry: false
}
const config = {
output: {
filename: 'static/js/[name].[chunkhash:8].js',
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
publicPath: 'http://localhost:5000/' // !!!這裏必定要使用絕對路徑,否則就會被坑到
}
// ...
plugins: [
new CleanWebpackPlugin(['dist'], cleanOptions),
new CopyWebpackPlugin([{from: 'public', to: ''}])
]
}
複製代碼
好了,不知道有多少同窗會看到這裏,先謝謝你們看我在這嘮叨一堆~各種優化的方案在網上看了好多好多,可是好像你們都只講方案沒有涉及實踐,等到本身真正去玩的時候才發現,其實優化沒有想象中那麼簡單,要兼顧原有的,又要儘可能使用更新更好的,不少時候都會在夾縫中取捨。
對了,好像忘記放懶加載優化後的圖了(仍是wp2打包的),效果可能沒有想象中那麼理想吧,但也算往前踏出很不容易的一步了:)
其實,可以優化的還有不少不少,請求方面、業務方面甚至是代碼寫法...都是能夠優化的,可是這些怎麼能一蹴而就呢?仍是得走一步,看一步,選擇最適合自家項目的優化方案纔是最佳方案~