VUE項目性能優化-實戰-案例記錄

VUE項目性能優化

  • Vue 代碼層面的優化;
  • Webpack 配置層面的優化;
  • 基礎的Web技術層面的優化;

1、代碼層面的優化

1.1 v-if 和 v-show區分使用場景

v-if真正 的條件渲染,由於它會確保在切換過程當中條件塊內的事件監聽器和子組件適當地被銷燬和重建;也是惰性的:若是在初始渲染時條件爲假,則什麼也不作——直到條件第一次變爲真時,纔會開始渲染條件塊。html

v-show 就簡單得多, 無論初始條件是什麼,元素老是會被渲染,而且只是簡單地基於 CSS 的 display 屬性進行切換。vue

因此,v-if 適用於在運行時不多改變條件,不須要頻繁切換條件的場景;v-show 則適用於須要很是頻繁切換條件的場景。node

1.2 computed 和 watch 區分使用場景

computed 是計算屬性,依賴其餘屬性值,而且computed的值有緩存,只有它依賴的屬性值發生改變,下一次獲取 computed的值時纔會從新計算computed的值。webpack

watch更多的是「觀察」的做用,相似於某些數據的監聽回調 ,每當監聽的數據變化時都會執行回調進行後續操做;ios

運用場景:web

  • 當咱們須要進行數值計算,而且依賴於其它數據時,應該使用 computed,由於能夠利用 computed 的緩存特性,避免每次獲取值時,都要從新計算;
  • 咱們須要在數據變化時執行異步或開銷較大的操做時,應該使用 watch,使用 watch 選項容許咱們執行異步操做 ( 訪問一個 API ),限制咱們執行該操做的頻率,並在咱們獲得最終結果前,設置中間狀態。這些都是計算屬性沒法作到的。
1.3 v-for 遍歷必須爲 item 添加key,且避免同事使用 v-if

(1)v-for 遍歷必須爲 item 添加 key 在列表數據進行遍歷渲染時,須要爲每一項 item 設置惟一 key 值,方便 Vue.js 內部機制精準找到該條列表數據。當 state 更新時,新的狀態值和舊的狀態值對比,較快地定位到 diff 。vue-cli

(2)v-for 遍歷避免同時使用 v-if v-for 比 v-if 優先級高,若是每一次都須要遍歷整個數組,將會影響速度,尤爲是當之須要渲染很小一部分的時候,必要狀況下應該替換成 computed 屬性。express

推薦:npm

html
<ul>
	<li v-for="user in activeUsers" :key="user.id">
		{{user.name}}
	</li>
</ul>

js
computed:{
	activeUsers:function(){
		return this.users.filter((user)=>{
			return user.isActive
		})
	}
}
複製代碼

不推薦:element-ui

<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id">
    {{ user.name }}
  </li>
</ul>
複製代碼
1.四、長列表性能優化

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.五、事件的銷燬

Vue 組件銷燬時,會自動清理它與其它實例的鏈接,解綁它的所有指令及事件監聽器,可是僅限於組件自己的事件。若是在 js 內使用addEventListene 等方式是不會自動銷燬的,咱們須要在組件銷燬時手動移除這些事件的監聽,以避免形成內存泄露,如:

created() {
	addEventListener('click',this.click,false);
},
beforeDestroy() {
	removeEventListener('click',this.click,false);
}
複製代碼
1.六、圖片資源懶加載

對於圖片過多的頁面,爲了加速頁面加載速度,因此不少時候咱們須要將頁面內未出如今可視區域內的圖片先不作加載, 等到滾動到可視區域後再去加載。這樣對於頁面加載性能上會有很大的提高,也提升了用戶體驗。咱們在項目中使用 Vue 的vue-lazyload插件: (1) 安裝插件

npm install vue-lazyload --save-dev
複製代碼

(2)在入口文件main.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 的 地址。

1.7 路由懶加載

Vue是單頁面應用,可能會有不少的路由引入,這樣使用webpack打包後的文件很大,當進入首頁時,加載的資源過多, 頁面會出現白屏的狀況, 不利於用戶體驗。若是咱們能把不一樣路由對應的組件分割成不一樣的代碼塊,而後當路由被訪問的時候才加載對應的組件,這樣就更加高效了。會大大提升首頁白屏顯示的速度,可是可能其餘的頁面的速度就會降下來。 路由懶加載

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-listvue-virtual-scroller 來優化這種無限列表的場景的。

1.10 服務端渲染SSR or 預渲染

服務端渲染是指 Vue 在客戶端將標籤渲染成的整個 html 片斷的工做在服務端完成,服務端造成的 html 片斷直接返回給客戶端這個過程就叫作服務端渲染。

(1) 服務端渲染的優勢:

  • 更好的SEO: 由於SPA頁面的內容是經過Ajax獲取的,而搜索引擎爬取工具並不會等待Ajax異步完成後再抓取頁面內容,因此在SPA中是抓取不到頁面經過Ajax獲取到的內容;而SSR是直接有服務端返回已渲染好的頁面(數據已經包含在頁面中),因此搜索引擎爬取工具能夠抓取渲染好的頁面;
  • 更快等待內容到達時間(首屏加載更快):SPA會等待全部Vue編譯後的JS文件都下載完成後,纔開始進行頁面的渲染,文件下載等須要必定的時間等,因此首屏渲染須要必定的時間;SSR直接由服務端渲染好頁面直接返回顯示,無需等待下載js文件及再去渲染等,因此SSR有更快的內容到達時間。

(2) 服務端渲染的缺點

  • 更多的開發條件限制:例如服務端渲染只支持beforeCreate和created兩個鉤子函數,這樣會致使一些外部擴展庫須要特殊處理,才能在服務端渲染應用程序中運行;而且與能夠部署在任何靜態文件服務器上的徹底靜態單頁面應用程序SPA不一樣,服務端渲染應用程序,須要處於 Node.js server運行環境。
  • 更多的服務器負載:在Node.js中渲染完整的應用程序,顯然會比僅僅提供靜態文件的server更加大量佔用CPU資源,所以若是你預料在高流量環境下使用,請準備相應的服務器負載,並明智的採用緩存策略。

若是你的項目的SEO和首屏渲染是評價項目的關鍵指標,那麼你的項目就須要服務端渲染來幫組你實現最佳的初始加載性能和SEO,具體的Vue SSR如何實現,能夠參考做者的另外一篇《Vue SSR 踩坑之旅》

2、Webpack層面的優化

2.一、Webpack 對圖片進行壓縮

在 vue 項目中除了能夠在 webpack.base.conf.jsurl-loader 中設置 limit 大小來對圖片處理,對小於limit的圖片轉化爲 base64 格式,其他的不作操做。因此對有些較大的圖片資源,在請求資源的時候,加載會很慢,咱們能夠用image-webpack-loader來壓縮圖片: (1) 首先安裝 image-webpack-loader:

npm install image-webpack-loader --save-dev
複製代碼

(2) 而後,在webpack.base.conf.js中進行配置:

{
	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.二、減小 ES6 轉爲 ES5 的冗餘代碼

Babel 插件會在將 ES6 代碼轉換成 ES5 代碼時會注入一些輔助函數,例以下面的 ES6 代碼:

class HelloWebpack extends Component{...}
複製代碼

這段代碼再被轉換成能正常運行的 ES5 代碼時須要如下兩個輔助函數:

babel-runtime/helpers/createClass  // 用於實現 class 語法
babel-runtime/helpers/inherits  // 用於實現 extends 語法
複製代碼

在默認狀況下, Babel 會在每一個輸出文件中內嵌這些依賴的輔助函數代碼,若是多個源代碼文件都依賴這些輔助函數,那麼這些輔助函數的代碼將會出現不少次,形成代碼冗餘。爲了避免讓這些輔助函數的代碼重複出現,能夠在依賴它們時經過require('babel-runtime/helpers/createClass')的方式導入,這樣就能作到只讓它們出現一次。babel-plugin-transform-runtime 插件就是用來實現這個做用的,將相關輔助函數進行替換成導入語句,從而減少 babel 編譯出來的代碼的文件大小。

(1) 首先,安裝 babel-plugin-transform-runtime:

npm install babel-plugin-transform-runtime --save-dev
複製代碼

(2) 而後,修改 .babelrc配置文件:

"plugins": [
    "transform-runtime"
]
複製代碼

若是要看插件的更多詳細內容,能夠查看babel-plugin-transform-runtime詳細介紹

ps:vue-cli腳手架建立的項目都會自帶這個插件的好像.

2.三、 提取公共代碼

若是項目中沒有去講每一個頁面的第三方庫和公共模塊提取出來,則項目會存在如下問題:

  • 相同的資源被重複加載,浪費用戶的流量和服務器的成本。
  • 每一個頁面須要加載的資源太大,致使網頁首屏加載緩慢,影響用戶體驗。

因此咱們須要將多個頁面的公共代碼抽離成單獨的文件,來優化以上問題。Webpack內置專門用於提取多個Chunk中公共部分的插件 CommonsChunkPlugin,咱們在項目中 CommonsChunkPlugin的配置以下:

// 全部在 package.json 裏面依賴的包,都會被打包進 vendor.js這個文件中。
new webpack.optimize.CommonsChunkPlugin({
	name:'vendor',
	minChunks:function(module,count){
		return(
			module.resource && 
			/\.js$/.test(module.resource) &&
			module.resource.indexOf(
				path.join(__dirname,'../node_modules')
			) === 0
		);
	}
}),

//抽取出代碼模塊的映射關係
new webpack.optimize.CommonsChunkPlugin({
	name:'manifest',
	chunks:['vendor']
})
複製代碼

若是要查看插件的更多詳細內容,能夠查看commonsChunkPlugin 的詳細介紹

2.4模板預編譯

當使用DOM內模板或JavaScript內的字符串模板時,模板會在運行時編譯爲渲染函數。一般狀況下這個過程已經足夠快了,但對於性能敏感的應用仍是最好避免這種用法。

預編譯模板最簡單的方式就是使用單文件組件--相關的構建設置會自動預編譯處理好,因此構建好的代碼已經包含了編譯出來的渲染函數而不是原始的模板字符串。

若是你使用webpack,而且喜歡分離JavaScript和模板文件,你可使用vue-template-loader,它也能夠在構建過程當中把模板文件轉換成爲JavaScript渲染函數。

2.5 提取組件的CSS

當使用單文件組件時,組件內的CSS會以style標籤的方式經過JavaScript動態注入。這有一些小小的運行時開銷,若是你使用服務端渲染,這會致使一段「無樣式內容閃爍(fouc)」。將全部組件的CSS提取到同一個文件能夠避免這個問題,也會讓CSS更好地進行壓縮和緩存。 查閱這個構建工具各自的文檔來了解更多:

2.六、優化SourceMap

SourceMap使用教程

咱們在項目進行打包後,會將開發中的多個文件代碼打包到一個文件中,而且通過壓縮、去掉多餘的空格、babel編譯化後,最終將編譯獲得的代碼會用於線上環境,那麼這樣處理後的代碼和源代碼會有很大的差異,當有bug的時候,咱們只能定位到壓縮處理後的代碼位置,沒法定位到開發環境中的代碼,對於開發來講很差調試定位問題,所以sourceMap出現了,它就是爲了解決很差調試代碼問題的。

SourceMap的可選值以下(+號越多,表明速度越快,-號越多,表明速度越慢,o 表明中等速度)

開發環境推薦:cheap-module-eval-source-map 生產環境推薦:cheap-module-source-map

緣由以下:

  • cheap: 源代碼中的列表信息是沒有任何做用,所以咱們打包後的文件不但願包含列相關信息,只有行信息能創建打包先後的依賴關係。所以無論是開發環境或生產環境,咱們都但願添加 cheap 的基本類型來忽略打包先後的列信息。
  • module: 無論是開發環境仍是正式環境,咱們都但願能定位到bug的源代碼具體的位置,好比說某個vue文件報錯了,咱們但願能定位到具體Vue文件,所以咱們也須要module配置;
  • source-map: source-map 會爲每個打包後的模塊生成獨立的sourcemap文件,所以咱們須要增長source-map屬性。
  • eval-source-map: eval打包代碼的速度很是快,由於他不生成map文件,可是能夠對eval組合使用 eval-source-map 使用會將map文件以 DataURL的形式存在打包後的js文件中。在正式環境中不要使用eval-source-map,由於它會增長文件的大小,可是在開發環境中,能夠試用下,由於他們的打包速度很快。
2.7 構建結構輸出分析

Webpack輸出的代碼可讀性很是差並且文件很是大,讓咱們很是頭疼。爲了更簡單、直觀地分析輸出結構,社區中出現了許多可視化分析工具。這些工具以圖形的方式將結果更直觀地展示出來,讓咱們快速瞭解問題所在。接下來說解咱們在Vue項目中用到的分析工具:webpack-bundle-analyzer.

咱們在項目中 webpack.prod.conf.js進行配置:

if(config.build.bundleAnalyzerReport){
	var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
  webpackConfig.plugins.push(new BundleAnalyzerPlugin());
}
複製代碼

執行 npm run build \--report後生成分析報告以下:

2.八、 Vue項目的編譯優化

若是你的Vue項目使用Webpack編譯,須要你喝一杯咖啡的時間,那麼也許你須要對項目的webpack配置進行遊湖啊,提升Webpack的構建效率。具體如何進行Vue項目的Webpack構建優化。能夠參考:輕鬆優化你的webpack

3、基礎的Web技術優化

3.一、開啓gzip壓縮

gzip 是 GNUzip 的縮寫,最先用於 UNIX 系統的文件壓縮。HTTP 協議上的 gzip 編碼是一種用來改進 web 應用程序性能的技術,web 服務器和客戶端(瀏覽器)必須共同支持 gzip。目前主流的瀏覽器,Chrome,firefox,IE等都支持該協議。常見的服務器如 Apache,Nginx,IIS 一樣支持,gzip 壓縮效率很是高,一般能夠達到 70% 的壓縮率,也就是說,若是你的網頁有 30K,壓縮以後就變成了 9K 左右

如下咱們以服務端使用咱們熟悉的 express 爲例,開啓 gzip 很是簡單,相關步驟以下:

  • 安裝:
npm install compression --save
複製代碼
  • 添加代碼邏輯
var compression = require('compression');
var app = express();
app.use(compression())
複製代碼
  • 重啓服務,觀察網絡面板裏面的 response header,若是看到以下紅圈裏的字段則代表 gzip 開啓成功 :

3.二、瀏覽器緩存

爲了提升用戶加載頁面的速度,對靜態資源進行緩存是很是必要的,根據是否須要從新向服務器發起請求來分類,將 HTTP 緩存規則分爲兩大類(強制緩存,對比緩存),若是對緩存機制還不是瞭解很清楚的,關於 HTTP 緩存的文章《深刻理解HTTP緩存機制及原理》,這裏再也不贅述。

相關文章
相關標籤/搜索