开发指南5——Vue API 盲点解析
開發(fā)指南篇 5:Vue API 盲點解析
在了解了一些實用的開發(fā)技巧和編碼理念后,我們在項目的開發(fā)過程中難免也會遇到因為不熟悉 Vue API 而導(dǎo)致的技術(shù)問題,而往往就是這樣的一些問題消耗了我們大量的開發(fā)時間,造成代碼可讀性下降、功能紊亂甚至 bug 量的增加,其根本原因還是自己對 Vue API 的 “無知”。
本文將介紹 Vue 項目開發(fā)中比較難以理解并可能被你忽視的 API,唯有知己知彼,才能百戰(zhàn)不殆。
API 解析
使用 performance 開啟性能追蹤
performance API 是 Vue 全局配置 API 中的一個,我們可以使用它來進行網(wǎng)頁性能的追蹤,我們可以在入口文件中添加:
if (process.env.NODE_ENV !== 'production') {Vue.config.performance = true; }來開啟這一功能,該 API(2.2.0 新增)功能只適用于開發(fā)模式和支持 performance.mark API 的瀏覽器上,開啟后我們可以下載 Vue Performance Devtool 這一 chrome 插件來看查看各個組件的加載情況,如圖:
從中我們可以清晰的看到頁面組件在每個階段的耗時情況,而針對耗時比較久的組件,我們便可以對其進行相應(yīng)優(yōu)化。
而其在 Vue 源碼中主要使用了 window.performance 來獲取網(wǎng)頁性能數(shù)據(jù),其中包含了 performance.mark 和 performance.measure。
- performance.mark 主要用于創(chuàng)建標記
- performance.measure 主要用于記錄兩個標記的時間間隔
例如:
performance.mark('start'); // 創(chuàng)建 start 標記 performance.mark('end'); // 創(chuàng)建 end 標記performance.measure('output', 'start', 'end'); // 計算兩者時間間隔performance.getEntriesByName('output'); // 獲取標記,返回值是一個數(shù)組,包含了間隔時間數(shù)據(jù)熟練的使用 performance 我們可以查看并分析網(wǎng)頁的很多數(shù)據(jù),為我們項目優(yōu)化提供保障。除了上述介紹的兩個方法,我們還可以使用 performance.timing 來計算頁面各個階段的加載情況,關(guān)于 performance.timing 的介紹可以查看我之前寫的一篇文章:利用 Navigation Timing 測量頁面加載時間
使用 errorHandler 來捕獲異常
在瀏覽器異常捕獲的方法上,我們熟知的一般有:try ... catch 和 window.onerror,這也是原生 JavaScript 提供給我們處理異常的方式。但是在 Vue 2.x 中如果你一如既往的想使用 window.onerror 來捕獲異常,那么其實你是捕獲不到的,因為異常信息被框架自身的異常機制捕獲了,你可以使用 errorHandler 來進行異常信息的獲取:
Vue.config.errorHandler = function (err, vm, info) {let { message, // 異常信息name, // 異常名稱stack // 異常堆棧信息} = err;// vm 為拋出異常的 Vue 實例// info 為 Vue 特定的錯誤信息,比如錯誤所在的生命周期鉤子 }在入口文件中加入上述代碼后,我們便可以捕獲到 Vue 項目中的一些異常信息了,但是需要注意的是 Vue 2.4.0 起的版本才支持捕獲 Vue 自定義事件處理函數(shù)內(nèi)部的錯誤,比如:
<template><my-component @eventFn="doSomething"></my-component> </template><script> export default {methods: {doSomething() {console.log(a); // a is not defined}} } </script>使用 Vue 中的異常捕獲機制,我們可以針對捕獲到的數(shù)據(jù)進行分析和上報,為實現(xiàn)前端異常監(jiān)控奠定基礎(chǔ)。關(guān)于對異常捕獲的詳細介紹,感興趣的同學(xué)可以查看我的這篇文章:談?wù)勄岸水惓2东@與上報
使用 nextTick 將回調(diào)延遲到下次 DOM 更新循環(huán)之后執(zhí)行
在某些情況下,我們改變頁面中綁定的數(shù)據(jù)后需要對新視圖進行一些操作,而這時候新視圖其實還未生成,需要等待 DOM 的更新后才能獲取的到,在這種場景下我們便可以使用 nextTick 來延遲回調(diào)的執(zhí)行。比如未使用 nextTick 時的代碼:
<template><ul ref="box"><li v-for="(item, index) in arr" :key="index"></li></ul> </template><script> export default {data() {return {arr: []}},mounted() {this.getData();},methods: {getData() {this.arr = [1, 2, 3];this.$refs.box.getElementsByTagName('li')[0].innerHTML = 'hello';}} } </script>上方代碼我們在實際運行的時候肯定會報錯,因為我們獲取 DOM 元素 li 的時候其還未被渲染,我們將方法放入 nextTick 回調(diào)中即可解決該問題:
this.$nextTick(() => {this.$refs.box.getElementsByTagName('li')[0].innerHTML = 'hello'; })當(dāng)然你也可以使用 ES6 的 async/await 語法來改寫上述方法:
methods: {async getData() {this.arr = [1, 2, 3];await this.$nextTick();this.$refs.box.getElementsByTagName('li')[0].innerHTML = 'hello';} }那么接下來我們來分析下 Vue 是如何做到的,其源碼中使用了 3 種方式:
- promise.then 延遲調(diào)用
- setTimeout(func, 0) 延遲功能
- MutationObserver 監(jiān)聽變化
前兩種方式相信大家都比較熟悉,其都具備延遲執(zhí)行的功能,我們也可以直接替換 nextTick 為這兩種方式中的一種,同樣可以解決問題。這里主要介紹下 MutationObserver 這一 HTML5 新特性,那么什么是 MutationObserver 呢?用一句話介紹就是:我們可以使用它創(chuàng)建一個觀察者對象,其會監(jiān)聽某個 DOM 元素,并在它的 DOM 樹發(fā)生變化時執(zhí)行我們提供的回調(diào)函數(shù)。實例化代碼及配置如下:
// 傳入回調(diào)函數(shù)進行實例化 var observer = new MutationObserver(mutations => {mutations.forEach(mutation => {console.log(mutation.type);}) });// 選擇目標節(jié)點 var target = document.querySelector('#box');// 配置觀察選項 var config = { attributes: true, // 是否觀察屬性的變動childList: true, // 是否觀察子節(jié)點的變動(指新增,刪除或者更改)characterData: true // 是否觀察節(jié)點內(nèi)容或節(jié)點文本的變動 };// 傳入目標節(jié)點和觀察選項 observer.observe(target, config);// 停止觀察 observer.disconnect();這樣我們便可以觀察 id 為 box 下的 DOM 樹變化,一旦發(fā)生變化就會觸發(fā)相應(yīng)的回調(diào)方法,實現(xiàn)延遲調(diào)用的功能。
使用 watch 的深度遍歷和立即調(diào)用功能
相信很多同學(xué)使用 watch 來監(jiān)聽數(shù)據(jù)變化的時候通常只使用過其中的 handler 回調(diào),其實其還有兩個參數(shù),便是:
- deep 設(shè)置為 true 用于監(jiān)聽對象內(nèi)部值的變化
- immediate 設(shè)置為 true 將立即以表達式的當(dāng)前值觸發(fā)回調(diào)
我們來看下代碼中的配置:
<template><button @click="obj.a = 2">修改</button> </template> <script> export default {data() {return {obj: {a: 1,}}},watch: {obj: {handler: function(newVal, oldVal) {console.log(newVal); },deep: true,immediate: true}} } </script>以上代碼我們修改了 obj 對象中 a 屬性的值,我們可以觸發(fā)其 watch 中的 handler 回調(diào)輸出新的對象,而如果不加 deep: true,我們只能監(jiān)聽 obj 的改變,并不會觸發(fā)回調(diào)。同時我們也添加了 immediate: true 配置,其會立即以 obj 的當(dāng)前值觸發(fā)回調(diào)。
在 Vue 源碼中,主要使用了 Object.defineProperty (obj, key, option) 方法來實現(xiàn)數(shù)據(jù)的監(jiān)聽,同時其也是 Vue 數(shù)據(jù)雙向綁定的關(guān)鍵方法之一。示例代碼如下:
function Observer() {var result = null;Object.defineProperty(this, 'result', {get: function() {console.log('你訪問了 result');return result;},set: function(value) {result = value;console.log('你設(shè)置了 result = ' + value);}}); }var app = new Observer(); // 實例化app.result; // 你訪問了 result app.result = 11; // 你設(shè)置了 result = 11我們通過實例化了 Observer 方法來實現(xiàn)了一個簡單的監(jiān)聽數(shù)據(jù)訪問與變化的功能。Object.defineProperty 是 ES5 的語法,這也就是為什么 Vue 不支持 IE8 以及更低版本瀏覽器的主要原因。
對低開銷的靜態(tài)組件使用 v-once
Vue 提供了 v-once 指令用于只渲染元素和組件一次,一般可以用于存在大量靜態(tài)數(shù)據(jù)組件的更新性能優(yōu)化,注意是大量靜態(tài)數(shù)據(jù),因為少數(shù)情況下我們的頁面渲染會因為一些靜態(tài)數(shù)據(jù)而變慢。如果你需要對一個組件使用 v-once,可以直接在組件上綁定:
<my-component v-once :data="msg"></my-component>這時候因為組件綁定了 v-once,所以無論 msg 的值如何變化,組件內(nèi)渲染的永遠是其第一次獲取到的初始值。因此我們在使用 v-once 的時候需要考慮該組件今后的更新情況,避免不必要的問題產(chǎn)生。
使用 $isServer 判斷當(dāng)前實例是否運行于服務(wù)器
當(dāng)我們的 Vue 項目中存在服務(wù)端渲染(SSR)的時候,有些項目文件可能會同時在客戶端和服務(wù)端加載,這時候代碼中的一些客戶端瀏覽器才支持的屬性或變量在服務(wù)端便會加載出錯,比如 window、 document 等,這時候我們需要進行環(huán)境的判斷來區(qū)分客戶端和服務(wù)端,如果你不知道 $isServer,那么你可能會使用 try ... catch 或者 process.env.VUE_ENV 來判斷:
try {document.title = 'test'; } catch(e) {}// process.env.VUE_ENV 需要在 webpack 中進行配置 if (process.env.VUE_ENV === 'client') {document.title = 'test'; }而使用 $isServer 則無需進行配置,在組件中直接使用該 API 即可:
if (this.$isServer) {document.title = 'test'; }其源碼中使用了 Object.defineProperty 來進行數(shù)據(jù)監(jiān)測:
Object.defineProperty(Vue.prototype, '$isServer', {get: isServerRendering });var _isServer; var isServerRendering = function () {if (_isServer === undefined) {if (!inBrowser && !inWeex && typeof global !== 'undefined') {_isServer = global['process'].env.VUE_ENV === 'server';} else {_isServer = false;}}return _isServer };當(dāng)我們訪問 $isServer 屬性時,其會調(diào)用 isServerRendering 方法,該方法會首先判斷當(dāng)前環(huán)境,如果在瀏覽器或者 Weex 下則返回 false,否則繼續(xù)判斷當(dāng)前全局環(huán)境下的 process.env.VUE_ENV 是否為 server 來返回最終結(jié)果。
總結(jié)
以上是生活随笔為你收集整理的开发指南5——Vue API 盲点解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: chrome 打开默认页 被篡改_为什么
- 下一篇: WVS安全测试工具使用教程