Vue 框架通過數據雙向綁定和虛擬 DOM 技術,幫我們處理了前端開發中最臟最累的 DOM 操作部分, 我們不再需要去考慮如何操作 DOM 以及如何最高效地操作 DOM;但 Vue 項目中仍然存在項目首屏優化、Webpack 編譯配置優化等問題,所以我們仍然需要去關註 Vue 項目性能方面的優化,使 ...
Vue 框架通過數據雙向綁定和虛擬 DOM 技術,幫我們處理了前端開發中最臟最累的 DOM 操作部分, 我們不再需要去考慮如何操作 DOM 以及如何最高效地操作 DOM;但 Vue 項目中仍然存在項目首屏優化、Webpack 編譯配置優化等問題,所以我們仍然需要去關註 Vue 項目性能方面的優化,使項目具有更高效的性能、更好的用戶體驗。本文是作者通過實際項目的優化實踐進行總結而來,希望讀者讀完本文,有一定的啟發思考,從而對自己的項目進行優化起到幫助。本文內容分為以下三部分組成:
- Vue 代碼層面的優化;
- webpack 配置層面的優化;
- 基礎的 Web 技術層面的優化。
一、代碼層面的優化
1.1、v-if 和 v-show 區分使用場景
v-if 是 真正 的條件渲染,因為它會確保在切換過程中條件塊內的事件監聽器和子組件適當地被銷毀和重建;也是惰性的:如果在初始渲染時條件為假,則什麼也不做——直到條件第一次變為真時,才會開始渲染條件塊。
v-show 就簡單得多, 不管初始條件是什麼,元素總是會被渲染,並且只是簡單地基於 CSS 的 display 屬性進行切換。
所以,v-if 適用於在運行時很少改變條件,不需要頻繁切換條件的場景;v-show 則適用於需要非常頻繁切換條件的場景。
1.2、computed 和 watch 區分使用場景
computed: 是計算屬性,依賴其它屬性值,並且 computed 的值有緩存,只有它依賴的屬性值發生改變,下一次獲取 computed 的值時才會重新計算 computed 的值;
watch: 更多的是「觀察」的作用,類似於某些數據的監聽回調 ,每當監聽的數據變化時都會執行回調進行後續操作;
運用場景:
-
當我們需要進行數值計算,並且依賴於其它數據時,應該使用 computed,因為可以利用 computed 的緩存特性,避免每次獲取值時,都要重新計算;
-
當我們需要在數據變化時執行非同步或開銷較大的操作時,應該使用 watch,使用 watch 選項允許我們執行非同步操作 ( 訪問一個 API ),限制我們執行該操作的頻率,併在我們得到最終結果前,設置中間狀態。這些都是計算屬性無法做到的。
1.3、v-for 遍歷必須為 item 添加 key,且避免同時使用 v-if
(1)v-for 遍歷必須為 item 添加 key
在列表數據進行遍歷渲染時,需要為每一項 item 設置唯一 key 值,方便 Vue.js 內部機制精準找到該條列表數據。當 state 更新時,新的狀態值和舊的狀態值對比,較快地定位到 diff 。
(2)v-for 遍歷避免同時使用 v-if
v-for 比 v-if 優先順序高,如果每一次都需要遍歷整個數組,將會影響速度,尤其是當之需要渲染很小一部分的時候,必要情況下應該替換成 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 }) } }
不推薦:
<ul> <li v-for="user in users" v-if="user.isActive" :key="user.id"> {{ user.name }} </li> </ul>
1.4、長列表性能優化
Vue 會通過 Object.defineProperty 對數據進行劫持,來實現視圖響應數據的變化,然而有些時候我們的組件就是純粹的數據展示,不會有任何改變,我們就不需要 Vue 來劫持我們的數據,在大量數據展示的情況下,這能夠很明顯的減少組件初始化的時間,那如何禁止 Vue 劫持我們的數據呢?可以通過 Object.freeze 方法來凍結一個對象,一旦被凍結的對象就再也不能被修改了。
export default { data: () => ({ users: {} }), async created() { const users = await axios.get("/api/users"); this.users = Object.freeze(users); } };
1.5、事件的銷毀
Vue 組件銷毀時,會自動清理它與其它實例的連接,解綁它的全部指令及事件監聽器,但是僅限於組件本身的事件。 如果在 js 內使用 addEventListene 等方式是不會自動銷毀的,我們需要在組件銷毀時手動移除這些事件的監聽,以免造成記憶體泄露,如:
created() { addEventListener('click', this.click, false) }, beforeDestroy() { removeEventListener('click', this.click, false) }
1.6、圖片資源懶載入
對於圖片過多的頁面,為了加速頁面載入速度,所以很多時候我們需要將頁面內未出現在可視區域內的圖片先不做載入, 等到滾動到可視區域後再去載入。這樣對於頁面載入性能上會有很大的提升,也提高了用戶體驗。我們在項目中使用 Vue 的 vue-lazyload 插件:
(1)安裝插件
npm install vue-lazyload --save-dev
(2)在入口文件 man.js 中引入並使用
import VueLazyload from 'vue-lazyload'
然後再 vue 中直接使用
Vue.use(VueLazyload)
或者添加自定義選項
Vue.use(VueLazyload, { preLoad: 1.3, error: 'dist/error.png', loading: 'dist/loading.gif', attempt: 1 })
(3)在 vue 文件中將 img 標簽的 src 屬性直接改為 v-lazy ,從而將圖片顯示方式更改為懶載入顯示:
<img v-lazy="/static/img/1.png">
以上為 vue-lazyload 插件的簡單使用,如果要看插件的更多參數選項,可以查看 vue-lazyload 的 github 地址。
1.7、路由懶載入
Vue 是單頁面應用,可能會有很多的路由引入 ,這樣使用 webpcak 打包後的文件很大,當進入首頁時,載入的資源過多,頁面會出現白屏的情況,不利於用戶體驗。如果我們能把不同路由對應的組件分割成不同的代碼塊,然後當路由被訪問的時候才載入對應的組件,這樣就更加高效了。這樣會大大提高首屏顯示的速度,但是可能其他的頁面的速度就會降下來。
路由懶載入:
const Foo = () => import('./Foo.vue') const router = new VueRouter({ routes: [ { path: '/foo', component: Foo } ] })
1.8、第三方插件的按需引入
我們在項目中經常會需要引入第三方插件,如果我們直接引入整個插件,會導致項目的體積太大,我們可以藉助 babel-plugin-component
,然後可以只引入需要的組件,以達到減小項目體積的目的。以下為項目中引入 element-ui 組件庫為例:
(1)首先,安裝 babel-plugin-component
:
npm install babel-plugin-component -D
(2)然後,將 .babelrc 修改為:
{ "presets": [["es2015", { "modules": false }]], "plugins": [ [ "component", { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" } ] ] }
(3)在 main.js 中引入部分組件:
import Vue from 'vue'; import { Button, Select } from 'element-ui'; Vue.use(Button) Vue.use(Select)
1.9、優化無限列表性能
如果你的應用存在非常長或者無限滾動的列表,那麼需要採用 視窗化 的技術來優化性能,只需要渲染少部分區域的內容,減少重新渲染組件和創建 dom 節點的時間。 你可以參考以下開源項目 vue-virtual-scroll-list 和 vue-virtual-scroller 來優化這種無限列表的場景的。
1.10、服務端渲染 SSR or 預渲染
服務端渲染是指 Vue 在客戶端將標簽渲染成的整個 html 片段的工作在服務端完成,服務端形成的 html 片段直接返回給客戶端這個過程就叫做服務端渲染。
(1)服務端渲染的優點:
-
更好的 SEO: 因為 SPA 頁面的內容是通過 Ajax 獲取,而搜索引擎爬取工具並不會等待 Ajax 非同步完成後再抓取頁面內容,所以在 SPA 中是抓取不到頁面通過 Ajax 獲取到的內容;而 SSR 是直接由服務端返回已經渲染好的頁面(數據已經包含在頁面中),所以搜索引擎爬取工具可以抓取渲染好的頁面;
-
更快的內容到達時間(首屏載入更快): SPA 會等待所有 Vue 編譯後的 js 文件都下載完成後,才開始進行頁面的渲染,文件下載等需要一定的時間等,所以首屏渲染需要一定的時間;SSR 直接由服務端渲染好頁面直接返回顯示,無需等待下載 js 文件及再去渲染等,所以 SSR 有更快的內容到達時間;
(2)服務端渲染的缺點:
-
更多的開發條件限制: 例如服務端渲染只支持 beforCreate 和 created 兩個鉤子函數,這會導致一些外部擴展庫需要特殊處理,才能在服務端渲染應用程式中運行;並且與可以部署在任何靜態文件伺服器上的完全靜態單頁面應用程式 SPA 不同,服務端渲染應用程式,需要處於 Node.js server 運行環境;
-
更多的伺服器負載:在 Node.js 中渲染完整的應用程式,顯然會比僅僅提供靜態文件的 server 更加大量占用CPU 資源,因此如果你預料在高流量環境下使用,請準備相應的伺服器負載,並明智地採用緩存策略。
如果你的項目的 SEO 和 首屏渲染是評價項目的關鍵指標,那麼你的項目就需要服務端渲染來幫助你實現最佳的初始載入性能和 SEO,具體的 Vue SSR 如何實現,可以參考作者的另一篇文章《Vue SSR 踩坑之旅》。如果你的 Vue 項目只需改善少數營銷頁面(例如 /, /about, /contac
t 等)的 SEO,那麼你可能需要預渲染,在構建時 (build time) 簡單地生成針對特定路由的靜態 HTML 文件。優點是設置預渲染更簡單,並可以將你的前端作為一個完全靜態的站點,具體你可以使用 prerender-spa-plugin 就可以輕鬆地添加預渲染 。
轉載地址:https://github.com/fengshi123/blog/issues/13