1、代碼優化
1. 使用keep-alive緩存不活動的組件
keep-alive是一個抽象組件:它自身不會渲染一個DOM元素,也不會出如今父組件鏈中;使用keep-alive包裹動態組件時,會緩存不活動的組件實例,而不是銷燬它們。html
- 在動態組件中的應用
<keep-alive :include="whiteList" :exclude="blackList" :max="amount"> <component :is="currentComponent"></component> </keep-alive>
- 在vue-router中的應用
<keep-alive :include="whiteList" :exclude="blackList"> <router-view></router-view> </keep-alive>
include定義緩存白名單,keep-alive會緩存命中的組件;exclude定義緩存黑名單,被命中的組件將不會被緩存;
不少時候也能夠配合路由的meta屬性使用
前端
export default[ { path:'/', name:'home', components:Home, meta:{ keepAlive:true //須要被緩存的組件 }, { path:'/book', name:'book', components:Book, meta:{ keepAlive:false //不須要被緩存的組件 } ] <keep-alive> <router-view v-if="this.$route.meat.keepAlive"></router-view> <!--這裏是會被緩存的組件--> </keep-alive> <keep-alive v-if="!this.$router.meta.keepAlive"></keep-alive> <!--這裏是不會被緩存的組件-->
2. 使用路由懶加載
Vue 是單頁面應用,可能會有不少的路由引入 ,這樣使用 webpcak 打包後的文件很大,當進入首頁時,加載的資源過多,頁面會出現白屏的狀況,不利於用戶體驗。若是咱們能把不一樣路由對應的組件分割成不一樣的代碼塊,而後當路由被訪問的時候才加載對應的組件,這樣就更加高效了。這樣會大大提升首屏顯示的速度,可是可能其餘的頁面的速度就會降下來。vue
路由懶加載:webpack
export default new Router({ mode: 'history', routes: [ { path: '/', component: ()=>import('@/components/DefaultIndex') } ] })
詳見另外一篇博客:前端優化中的路由懶加載ios
3. 圖片懶加載
對於圖片過多的頁面,爲了加速頁面加載速度,因此不少時候咱們須要將頁面內未出如今可視區域內的圖片先不作加載, 等到滾動到可視區域後再去加載。這樣對於頁面加載性能上會有很大的提高,也提升了用戶體驗。咱們在項目中使用 Vue 的 vue-lazyload 插件:web
npm引入:npm i vue-lazyload -S CDN引入:[https://unpkg.com/vue-lazyload/vue-lazyload.js](https://unpkg.com/vue-lazyload/vue-lazyload.js)
使用:
main.js:
ajax
import Vue from 'vue' import App from './App.vue' import VueLazyload from 'vue-lazyload' Vue.use(VueLazyload) // or with options Vue.use(VueLazyload, { preLoad: 1.3, error: 'dist/error.png', loading: 'dist/loading.gif', attempt: 1 }) new Vue({ el: 'body', components: { App } })
template:vue-router
<ul> <li v-for="img in list"> <img v-lazy="img.src" > </li> </ul>
4. 使用節流防抖函數(性能優化)
那麼在 vue 中怎麼使用呢:
在公共方法中(如 untils.js 中),加入函數防抖和節流方法
vue-cli
// 防抖 export function _debounce(fn, delay) { var delay = delay || 200; var timer; return function () { var th = this; var args = arguments; if (timer) { clearTimeout(timer); } timer = setTimeout(function () { timer = null; fn.apply(th, args); }, delay); }; } // 節流 export function _throttle(fn, interval) { var last; var timer; var interval = interval || 200; return function () { var th = this; var args = arguments; var now = +new Date(); if (last && now - last < interval) { clearTimeout(timer); timer = setTimeout(function () { last = now; fn.apply(th, args); }, interval); } else { last = now; fn.apply(th, args); } } }
在須要使用的組件引用npm
import { _debounce } from "@/utils/public";
在 methods 中使用
methods: { // 改變場數 changefield: _debounce(function(_type, index, item) { // do something ... }, 200) }
應用:
函數防抖(debounce)
在事件被觸發n秒後再執行回調,若是在這n秒內又被觸發,則從新計時。
<body> <input type="text" id='unDebounce'> </body> </html> <script> //模擬一段ajax請求 function ajax(content){ console.log('ajax request ' + content) }; letinputa = document.getElementById('unDebounce'); function fn(e){ ajax(e.target.value) } //防抖函數,處理屢次被觸發的事件,只執行最後一次 inputa.addEventListener('input', fn) </script>
看一下運行結果:
能夠看到,咱們只要輸入一個字符,就會觸發此次ajax請求。不只從資源上來講是很浪費的行爲,並且實際應用中,用戶也是輸出完整的字符後,纔會請求。下面咱們優化一下:
<body> <input type="text" id='unDebounce'> </body> </html> <script> //防抖函數 function _debounce(fn, delay) { var delay = delay || 200; var timer; return function () { var th = this; var args = arguments; if (timer) { clearTimeout(timer); } timer = setTimeout(function () { timer = null; fn.apply(th, args); }, delay); }; } //模擬一段ajax請求 function ajax(content){ console.log('ajax request ' + content) }; let inputa = document.getElementById('unDebounce'); function fn(e){ ajax(e.target.value) } //防抖函數,處理屢次被觸發的事件,只執行最後一次 inputa.addEventListener('input', _debounce(fn,1000)) </script>
咱們加入了防抖之後,當你在頻繁的輸入時,並不會發送請求,只有當你在指定間隔內沒有輸入時,纔會執行函數。若是中止輸入可是在指定間隔內又輸入,會從新觸發計時。
我的理解 函數防抖就是法師發技能的時候要讀條,技能讀條沒完再按技能就會從新讀條。
函數節流(throttle)
規定在一個單位時間內,只能觸發一次函數。若是這個單位時間內觸發屢次函數,只有一次生效。
<body> <input type="text" id='unDebounce'> </body> </html> <script> //節流函數 function _throttle(fn, interval) { var last; var timer; var interval = interval || 200; return function () { var th = this; var args = arguments; var now = +new Date(); if (last && now - last < interval) { clearTimeout(timer); timer = setTimeout(function () { last = now; fn.apply(th, args); }, interval); } else { last = now; fn.apply(th, args); } } } //模擬一段ajax請求 function ajax(content){ console.log('ajax request ' + content) }; let inputa = document.getElementById('unDebounce'); function fn(e){ ajax(e.target.value) } //防抖節流,不管你輸入多塊,每隔1秒鐘執行一次 inputa.addEventListener('input', _throttle(fn,1000)) </script>
無論咱們設定的執行時間間隔多小,老是1s內只執行一次。
我的理解 函數節流就是fps遊戲的射速,就算一直按着鼠標射擊,也只會在規定射速內射出子彈。
總結
函數防抖和函數節流都是防止某一時間頻繁觸發,可是這兩兄弟之間的原理卻不同。
函數防抖是某一段時間內只執行一次,而函數節流是間隔時間執行。
結合應用場景
debounce
- search搜索聯想,用戶在不斷輸入值時,用防抖來節約請求資源。
- window觸發resize的時候,不斷的調整瀏覽器窗口大小會不斷的觸發這個事件,用防抖來讓其只觸發一次
throttle
- 鼠標不斷點擊觸發,mousedown(單位時間內只觸發一次)
- 拖拽事件,每拖動1px都會觸發onmousemove(能夠用throttle優化,每秒觸發一次)
- 監聽滾動事件,好比是否滑到底部自動加載更多,用throttle來判斷
5. v-for 遍歷必須爲 item 添加 key,且避免同時使用 v-if
- v-for 遍歷必須爲 item 添加 key
在列表數據進行遍歷渲染時,須要爲每一項 item 設置惟一 key 值,方便 Vue.js 內部機制精準找到該條列表數據。當 state
更新時,新的狀態值和舊的狀態值對比,較快地定位到 diff 。
- v-for 遍歷避免同時使用 v-if
v-for 比 v-if 優先級高,若是每一次都須要遍歷整個數組,將會影響速度,尤爲是當之須要渲染很小一部分的時候,必要狀況下應該替換成
computed 屬性。
推薦:
<template> <div class="home"> <ul> <li v-for="user in activeUsers" :key="user.id"> { { user.name }} </li> </ul> </div> </template> <script> export default { data(){ return { users:[{ id:1,name:'zhangsan',isActive:true},{ id:2,name:'lisi',isActive:true},{ id:3,name:'wangwu',isActive:false},{ id:4,name:'maliu',isActive:true},] } }, computed: { activeUsers: function () { // [js 的filter()方法](https://www.cnblogs.com/qiu2841/p/8961017.html) return this.users.filter(function (user) { return user.isActive }) } } } </script>
不推薦:
<ul> <li v-for="user in users" v-if="user.isActive" :key="user.id"> { { user.name }} </li> </ul>
6. v-if 和 v-show 區分使用場景
v-if 是 真正 的條件渲染,由於它會確保在切換過程當中條件塊內的事件監聽器和子組件適當地被銷燬和重建;也是惰性的:若是在初始渲染時條件爲假,則什麼也不作——直到條件第一次變爲真時,纔會開始渲染條件塊。
v-show就簡單得多, 無論初始條件是什麼,元素老是會被渲染,而且只是簡單地基於 CSS 的 display 屬性進行切換。
因此,v-if 適用於在運行時不多改變條件,不須要頻繁切換條件的場景; v-show則適用於須要很是頻繁切換條件的場景。
7. computed 和 watch 區分使用場景
computed: 是計算屬性,依賴其它屬性值,而且 computed 的值有緩存,只有它依賴的屬性值發生改變,下一次獲取 computed 的值時纔會從新計算 computed 的值;
watch: 更多的是「觀察」的做用,相似於某些數據的監聽回調 ,每當監聽的數據變化時都會執行回調進行後續操做;
運用場景:
-
當咱們須要進行數值計算,而且依賴於其它數據時,應該使用 computed,由於能夠利用 computed 的緩存特性,避免每次獲取值時,都要從新計算;
-
當咱們須要在數據變化時執行異步或開銷較大的操做時,應該使用 watch,使用 watch 選項容許咱們執行異步操做 ( 訪問一個 API ),限制咱們執行該操做的頻率,並在咱們獲得最終結果前,設置中間狀態。這些都是計算屬性沒法作到的。
8. 長列表性能優化
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); } };
9. 事件的銷燬
Vue 組件銷燬時,會自動清理它與其它實例的鏈接,解綁它的所有指令及事件監聽器,可是僅限於組件自己的事件。 若是在 js 內使用 addEventListene 等方式是不會自動銷燬的,咱們須要在組件銷燬時手動移除這些事件的監聽,以避免形成內存泄露,如:
created() { addEventListener('click', this.click, false) }, beforeDestroy() { removeEventListener('click', this.click, false) }
10. 第三方插件的按需引入
咱們在項目中常常會須要引入第三方插件,若是咱們直接引入整個插件,會致使項目的體積太大,咱們能夠藉助 babel-plugin-component ,而後能夠只引入須要的組件,以達到減少項目體積的目的。如下爲項目中引入 element-ui 組件庫爲例:
(1)首先,安裝 babel-plugin-component :
npm install babel-plugin-component -D `` (2)而後,將 .babelrc 修改成: ```js { "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)
2、webpack打包優化
詳見我上一篇博客vue-cli4打包優化(webapck優化)
最後別忘記 「點贊」