每天前端開發(fā)干貨第一時間送達(dá)!
前言Vue 框架通過數(shù)據(jù)雙向綁定和虛擬 DOM 技術(shù),幫我們處理了前端開發(fā)中最臟最累的 DOM 操作部分, 我們不再需要去考慮如何操作 DOM 以及如何最高效地操作 DOM;但 Vue 項目中仍然存在項目首屏優(yōu)化、Webpack 編譯配置優(yōu)化等問題,所以我們?nèi)匀恍枰リP(guān)注 Vue 項目性能方面的優(yōu)化,使項目具有更高效的性能、更好的用戶體驗。 本文是作者通過實際項目的優(yōu)化實踐進(jìn)行總結(jié)而來,希望讀者讀完本文,有一定的啟發(fā)思考,從而對自己的項目進(jìn)行優(yōu)化起到幫助。本文內(nèi)容分為以下三部分組成:
辛苦整理良久,還望手動點贊鼓勵~ github地址為:github.com/fengshi123/…,匯總了作者的所有博客,也歡迎關(guān)注及 star ~ 一、代碼層面的優(yōu)化1.1、v-if 和 v-show 區(qū)分使用場景v-if 是 真正 的條件渲染,因為它會確保在切換過程中條件塊內(nèi)的事件監(jiān)聽器和子組件適當(dāng)?shù)乇讳N毀和重建;也是惰性的:如果在初始渲染時條件為假,則什么也不做——直到條件第一次變?yōu)檎鏁r,才會開始渲染條件塊。 v-show 就簡單得多, 不管初始條件是什么,元素總是會被渲染,并且只是簡單地基于 CSS 的 display 屬性進(jìn)行切換。 所以,v-if 適用于在運(yùn)行時很少改變條件,不需要頻繁切換條件的場景;v-show 則適用于需要非常頻繁切換條件的場景。 1.2、computed 和 watch 區(qū)分使用場景computed: 是計算屬性,依賴其它屬性值,并且 computed 的值有緩存,只有它依賴的屬性值發(fā)生改變,下一次獲取 computed 的值時才會重新計算 computed 的值; watch: 更多的是「觀察」的作用,類似于某些數(shù)據(jù)的監(jiān)聽回調(diào) ,每當(dāng)監(jiān)聽的數(shù)據(jù)變化時都會執(zhí)行回調(diào)進(jìn)行后續(xù)操作; 運(yùn)用場景:
1.3、v-for 遍歷必須為 item 添加 key,且避免同時使用 v-if(1)v-for 遍歷必須為 item 添加 key 在列表數(shù)據(jù)進(jìn)行遍歷渲染時,需要為每一項 item 設(shè)置唯一 key 值,方便 Vue.js 內(nèi)部機(jī)制精準(zhǔn)找到該條列表數(shù)據(jù)。當(dāng) state 更新時,新的狀態(tài)值和舊的狀態(tài)值對比,較快地定位到 diff 。 (2)v-for 遍歷避免同時使用 v-if v-for 比 v-if 優(yōu)先級高,如果每一次都需要遍歷整個數(shù)組,將會影響速度,尤其是當(dāng)之需要渲染很小一部分的時候,必要情況下應(yīng)該替換成 computed 屬性。 推薦: <ul> <li v-for='user in activeUsers' :key='user.id'> {{ user.name }} </li> </ul> computed: { activeUsers: function () { return this.users.filter(function (user) { return user.isActive }) } } 不推薦:
1.4、長列表性能優(yōu)化Vue 會通過 Object.defineProperty 對數(shù)據(jù)進(jìn)行劫持,來實現(xiàn)視圖響應(yīng)數(shù)據(jù)的變化,然而有些時候我們的組件就是純粹的數(shù)據(jù)展示,不會有任何改變,我們就不需要 Vue 來劫持我們的數(shù)據(jù),在大量數(shù)據(jù)展示的情況下,這能夠很明顯的減少組件初始化的時間,那如何禁止 Vue 劫持我們的數(shù)據(jù)呢?可以通過 Object.freeze 方法來凍結(jié)一個對象,一旦被凍結(jié)的對象就再也不能被修改了。 export default { data: () => ({ users: {} }), async created() { const users = await axios.get('/api/users'); this.users = Object.freeze(users); } }; 1.5、事件的銷毀Vue 組件銷毀時,會自動清理它與其它實例的連接,解綁它的全部指令及事件監(jiān)聽器,但是僅限于組件本身的事件。如果在 js 內(nèi)使用 addEventListene 等方式是不會自動銷毀的,我們需要在組件銷毀時手動移除這些事件的監(jiān)聽,以免造成內(nèi)存泄露,如:
1.6、圖片資源懶加載對于圖片過多的頁面,為了加速頁面加載速度,所以很多時候我們需要將頁面內(nèi)未出現(xiàn)在可視區(qū)域內(nèi)的圖片先不做加載, 等到滾動到可視區(qū)域后再去加載。這樣對于頁面加載性能上會有很大的提升,也提高了用戶體驗。我們在項目中使用 Vue 的 vue-lazyload 插件: (1)安裝插件 npm install vue-lazyload --save-dev (2)在入口文件 man.js 中引入并使用
然后再 vue 中直接使用 Vue.use(VueLazyload) 或者添加自定義選項
(3)在 vue 文件中將 img 標(biāo)簽的 src 屬性直接改為 v-lazy ,從而將圖片顯示方式更改為懶加載顯示: <img v-lazy='/static/img/1.png'> 以上為 vue-lazyload 插件的簡單使用,如果要看插件的更多參數(shù)選項,可以查看 vue-lazyload 的 github 地址。 1.7、路由懶加載Vue 是單頁面應(yīng)用,可能會有很多的路由引入 ,這樣使用 webpcak 打包后的文件很大,當(dāng)進(jìn)入首頁時,加載的資源過多,頁面會出現(xiàn)白屏的情況,不利于用戶體驗。如果我們能把不同路由對應(yīng)的組件分割成不同的代碼塊,然后當(dāng)路由被訪問的時候才加載對應(yīng)的組件,這樣就更加高效了。這樣會大大提高首屏顯示的速度,但是可能其他的頁面的速度就會降下來。 路由懶加載:
1.8、第三方插件的按需引入我們在項目中經(jīng)常會需要引入第三方插件,如果我們直接引入整個插件,會導(dǎo)致項目的體積太大,我們可以借助 (1)首先,安裝 npm install babel-plugin-component -D (2)然后,將 .babelrc 修改為:
(3)在 main.js 中引入部分組件: import Vue from 'vue'; import { Button, Select } from 'element-ui';
Vue.use(Button) Vue.use(Select) 1.9、優(yōu)化無限列表性能如果你的應(yīng)用存在非常長或者無限滾動的列表,那么需要采用 窗口化 的技術(shù)來優(yōu)化性能,只需要渲染少部分區(qū)域的內(nèi)容,減少重新渲染組件和創(chuàng)建 dom 節(jié)點的時間。你可以參考以下開源項目 vue-virtual-scroll-list 和 vue-virtual-scroller 來優(yōu)化這種無限列表的場景的。 1.10、服務(wù)端渲染 SSR or 預(yù)渲染服務(wù)端渲染是指 Vue 在客戶端將標(biāo)簽渲染成的整個 html 片段的工作在服務(wù)端完成,服務(wù)端形成的 html 片段直接返回給客戶端這個過程就叫做服務(wù)端渲染。 (1)服務(wù)端渲染的優(yōu)點:
(2)服務(wù)端渲染的缺點:
如果你的項目的 SEO 和 首屏渲染是評價項目的關(guān)鍵指標(biāo),那么你的項目就需要服務(wù)端渲染來幫助你實現(xiàn)最佳的初始加載性能和 SEO,具體的 Vue SSR 如何實現(xiàn),可以參考作者的另一篇文章《Vue SSR 踩坑之旅》。如果你的 Vue 項目只需改善少數(shù)營銷頁面(例如 二、Webpack 層面的優(yōu)化2.1、Webpack 對圖片進(jìn)行壓縮在 vue 項目中除了可以在 (1)首先,安裝 image-webpack-loader :
(2)然后,在 webpack.base.conf.js 中進(jìn)行配置: { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, use:[ { loader: 'url-loader', options: { limit: 10000, name: utils.assetsPath('img/[name].[hash:7].[ext]') } }, { loader: 'image-webpack-loader', options: { bypassOnDebug: true, } } ] } 2.2、減少 ES6 轉(zhuǎn)為 ES5 的冗余代碼Babel 插件會在將 ES6 代碼轉(zhuǎn)換成 ES5 代碼時會注入一些輔助函數(shù),例如下面的 ES6 代碼:
這段代碼再被轉(zhuǎn)換成能正常運(yùn)行的 ES5 代碼時需要以下兩個輔助函數(shù): babel-runtime/helpers/createClass // 用于實現(xiàn) class 語法 babel-runtime/helpers/inherits // 用于實現(xiàn) extends 語法 在默認(rèn)情況下, Babel 會在每個輸出文件中內(nèi)嵌這些依賴的輔助函數(shù)代碼,如果多個源代碼文件都依賴這些輔助函數(shù),那么這些輔助函數(shù)的代碼將會出現(xiàn)很多次,造成代碼冗余。為了不讓這些輔助函數(shù)的代碼重復(fù)出現(xiàn),可以在依賴它們時通過 (1)首先,安裝
(2)然后,修改 .babelrc 配置文件為: 'plugins': [ 'transform-runtime' ] 如果要看插件的更多詳細(xì)內(nèi)容,可以查看babel-plugin-transform-runtime 的 詳細(xì)介紹。 2.3、提取公共代碼如果項目中沒有去將每個頁面的第三方庫和公共模塊提取出來,則項目會存在以下問題:
所以我們需要將多個頁面的公共代碼抽離成單獨(dú)的文件,來優(yōu)化以上問題 。Webpack 內(nèi)置了專門用于提取多個Chunk 中的公共部分的插件 CommonsChunkPlugin,我們在項目中 CommonsChunkPlugin 的配置如下:
如果要看插件的更多詳細(xì)內(nèi)容,可以查看 CommonsChunkPlugin 的 詳細(xì)介紹。 2.4、模板預(yù)編譯當(dāng)使用 DOM 內(nèi)模板或 JavaScript 內(nèi)的字符串模板時,模板會在運(yùn)行時被編譯為渲染函數(shù)。通常情況下這個過程已經(jīng)足夠快了,但對性能敏感的應(yīng)用還是最好避免這種用法。 預(yù)編譯模板最簡單的方式就是使用單文件組件——相關(guān)的構(gòu)建設(shè)置會自動把預(yù)編譯處理好,所以構(gòu)建好的代碼已經(jīng)包含了編譯出來的渲染函數(shù)而不是原始的模板字符串。 如果你使用 webpack,并且喜歡分離 JavaScript 和模板文件,你可以使用 vue-template-loader,它也可以在構(gòu)建過程中把模板文件轉(zhuǎn)換成為 JavaScript 渲染函數(shù)。 2.5、提取組件的 CSS當(dāng)使用單文件組件時,組件內(nèi)的 CSS 會以 style 標(biāo)簽的方式通過 JavaScript 動態(tài)注入。這有一些小小的運(yùn)行時開銷,如果你使用服務(wù)端渲染,這會導(dǎo)致一段 “無樣式內(nèi)容閃爍 (fouc) ” 。將所有組件的 CSS 提取到同一個文件可以避免這個問題,也會讓 CSS 更好地進(jìn)行壓縮和緩存。 查閱這個構(gòu)建工具各自的文檔來了解更多:
2.6、優(yōu)化 SourceMap我們在項目進(jìn)行打包后,會將開發(fā)中的多個文件代碼打包到一個文件中,并且經(jīng)過壓縮、去掉多余的空格、babel編譯化后,最終將編譯得到的代碼會用于線上環(huán)境,那么這樣處理后的代碼和源代碼會有很大的差別,當(dāng)有 bug的時候,我們只能定位到壓縮處理后的代碼位置,無法定位到開發(fā)環(huán)境中的代碼,對于開發(fā)來說不好調(diào)式定位問題,因此 sourceMap 出現(xiàn)了,它就是為了解決不好調(diào)式代碼問題的。 SourceMap 的可選值如下(+ 號越多,代表速度越快,- 號越多,代表速度越慢, o 代表中等速度 ) 開發(fā)環(huán)境推薦:cheap-module-eval-source-map 生產(chǎn)環(huán)境推薦:cheap-module-source-map 原因如下:
2.7、構(gòu)建結(jié)果輸出分析Webpack 輸出的代碼可讀性非常差而且文件非常大,讓我們非常頭疼。為了更簡單、直觀地分析輸出結(jié)果,社區(qū)中出現(xiàn)了許多可視化分析工具。這些工具以圖形的方式將結(jié)果更直觀地展示出來,讓我們快速了解問題所在。接下來講解我們在 Vue 項目中用到的分析工具: 我們在項目中 if (config.build.bundleAnalyzerReport) { var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; webpackConfig.plugins.push(new BundleAnalyzerPlugin()); } 執(zhí)行 2.8、Vue 項目的編譯優(yōu)化如果你的 Vue 項目使用 Webpack 編譯,需要你喝一杯咖啡的時間,那么也許你需要對項目的 Webpack 配置進(jìn)行優(yōu)化,提高 Webpack 的構(gòu)建效率。具體如何進(jìn)行 Vue 項目的 Webpack 構(gòu)建優(yōu)化,可以參考作者的另一篇文章《 Vue 項目 Webpack 優(yōu)化實踐》 三、基礎(chǔ)的 Web 技術(shù)優(yōu)化3.1、開啟 gzip 壓縮gzip 是 GNUzip 的縮寫,最早用于 UNIX 系統(tǒng)的文件壓縮。HTTP 協(xié)議上的 gzip 編碼是一種用來改進(jìn) web 應(yīng)用程序性能的技術(shù),web 服務(wù)器和客戶端(瀏覽器)必須共同支持 gzip。目前主流的瀏覽器,Chrome,firefox,IE等都支持該協(xié)議。常見的服務(wù)器如 Apache,Nginx,IIS 同樣支持,gzip 壓縮效率非常高,通??梢赃_(dá)到 70% 的壓縮率,也就是說,如果你的網(wǎng)頁有 30K,壓縮之后就變成了 9K 左右 以下我們以服務(wù)端使用我們熟悉的 express 為例,開啟 gzip 非常簡單,相關(guān)步驟如下:
var compression = require('compression'); var app = express(); app.use(compression())
3.2、瀏覽器緩存為了提高用戶加載頁面的速度,對靜態(tài)資源進(jìn)行緩存是非常必要的,根據(jù)是否需要重新向服務(wù)器發(fā)起請求來分類,將 HTTP 緩存規(guī)則分為兩大類(強(qiáng)制緩存,對比緩存),如果對緩存機(jī)制還不是了解很清楚的,可以參考作者寫的關(guān)于 HTTP 緩存的文章《深入理解HTTP緩存機(jī)制及原理》,這里不再贅述。 3.3、CDN 的使用瀏覽器從服務(wù)器上下載 CSS、js 和圖片等文件時都要和服務(wù)器連接,而大部分服務(wù)器的帶寬有限,如果超過限制,網(wǎng)頁就半天反應(yīng)不過來。而 CDN 可以通過不同的域名來加載文件,從而使下載文件的并發(fā)連接數(shù)大大增加,且CDN 具有更好的可用性,更低的網(wǎng)絡(luò)延遲和丟包率 。 3.4、使用 Chrome Performance 查找性能瓶頸Chrome 的 Performance 面板可以錄制一段時間內(nèi)的 js 執(zhí)行細(xì)節(jié)及時間。使用 Chrome 開發(fā)者工具分析頁面性能的步驟如下。
更多關(guān)于 Performance 的內(nèi)容可以點擊這里查看。 總結(jié)本文通過以下三部分組成:Vue 代碼層面的優(yōu)化、webpack 配置層面的優(yōu)化、基礎(chǔ)的 Web 技術(shù)層面的優(yōu)化;來介紹怎么去優(yōu)化 Vue 項目的性能。希望對讀完本文的你有幫助、有啟發(fā),如果有不足之處,歡迎批評指正交流! 覺得本文對你有幫助?請分享給更多人 關(guān)注「前端教程」,一起提升前端技能! |
|