$nextTick 是什么饭耳?
Vue 實現響應式并不是在數據發(fā)生后立即更新 DOM串述,使用 vm.$nextTick
是在下次 DOM 更新循環(huán)結束之后立即執(zhí)行延遲回調。在修改數據之后使用寞肖,則可以在回調中獲取更新后的 DOM纲酗。
一般在哪個生命周期請求異步數據
我們可以在鉤子函數 created、beforeMount新蟆、mounted 中進行調用觅赊,因為在這三個鉤子函數中,data 已經創(chuàng)建琼稻,可以將服務端端返回的數據進行賦值吮螺。
推薦在 created 鉤子函數中調用異步請求,因為在 created 鉤子函數中調用異步請求有以下優(yōu)點:
- 能更快獲取到服務端數據,減少頁面加載時間鸠补,用戶體驗更好萝风;
- SSR不支持 beforeMount 、mounted 鉤子函數紫岩,放在 created 中有助于一致性闹丐。
寫過自定義指令嗎 原理是什么
指令本質上是裝飾器,是 vue 對 HTML 元素的擴展被因,給 HTML 元素增加自定義功能。vue 編譯 DOM 時衫仑,會找到指令對象梨与,執(zhí)行指令的相關方法。
自定義指令有五個生命周期(也叫鉤子函數)文狱,分別是 bind粥鞋、inserted、update瞄崇、componentUpdated呻粹、unbind
1. bind:只調用一次,指令第一次綁定到元素時調用苏研。在這里可以進行一次性的初始化設置等浊。
2. inserted:被綁定元素插入父節(jié)點時調用 (僅保證父節(jié)點存在,但不一定已被插入文檔中)摹蘑。
3. update:被綁定于元素所在的模板更新時調用筹燕,而無論綁定值是否變化。通過比較更新前后的綁定值衅鹿,可以忽略不必要的模板更新撒踪。
4. componentUpdated:被綁定元素所在模板完成一次更新周期時調用。
5. unbind:只調用一次大渤,指令與元素解綁時調用制妄。
那vue中是如何檢測數組變化的呢?
數組就是使用object.defineProperty
重新定義數組的每一項泵三,那能引起數組變化的方法我們都是知道的耕捞,pop
、push
切黔、shift
砸脊、unshift
、splice
纬霞、sort
凌埂、reverse
這七種,只要這些方法執(zhí)行改了數組內容诗芜,我就更新內容就好了瞳抓,是不是很好理解埃疫。
- 是用來函數劫持的方式,重寫了數組方法孩哑,具體呢就是更改了數組的原型栓霜,更改成自己的,用戶調數組的一些方法的時候横蜒,走的就是自己的方法胳蛮,然后通知視圖去更新。
- 數組里每一項可能是對象丛晌,那么我就是會對數組的每一項進行觀測仅炊,(且只有數組里的對象才能進行觀測,觀測過的也不會進行觀測)
vue3:改用proxy
澎蛛,可直接監(jiān)聽對象數組的變化抚垄。
Vue.js的template編譯
簡而言之,就是先轉化成AST樹谋逻,再得到的render函數返回VNode(Vue的虛擬DOM節(jié)點)呆馁,詳細步驟如下:
首先,通過compile編譯器把template編譯成AST語法樹(abstract syntax tree 即 源代碼的抽象語法結構的樹狀表現形式)毁兆,compile是createCompiler的返回值浙滤,createCompiler是用以創(chuàng)建編譯器的。另外compile還負責合并option气堕。
然后瓷叫,AST會經過generate(將AST語法樹轉化成render funtion字符串的過程)得到render函數,render的返回值是VNode送巡,VNode是Vue的虛擬DOM節(jié)點摹菠,里面有(標簽名、子節(jié)點骗爆、文本等等)
了解nextTick嗎次氨?
異步方法,異步渲染最后一步摘投,與JS事件循環(huán)聯系緊密煮寡。主要使用了宏任務微任務(setTimeout
、promise
那些)犀呼,定義了一個異步方法幸撕,多次調用nextTick
會將方法存入隊列,通過異步方法清空當前隊列外臂。
nextTick 使用場景和原理
nextTick 中的回調是在下次 DOM 更新循環(huán)結束之后執(zhí)行的延遲回調坐儿。在修改數據之后立即使用這個方法,獲取更新后的 DOM。主要思路就是采用微任務優(yōu)先的方式調用異步方法去執(zhí)行 nextTick 包裝的方法
相關代碼如下
let callbacks = [];
let pending = false;
function flushCallbacks() {
pending = false; //把標志還原為false
// 依次執(zhí)行回調
for (let i = 0; i < callbacks.length; i++) {
callbacks[i]();
}
}
let timerFunc; //定義異步方法 采用優(yōu)雅降級
if (typeof Promise !== "undefined") {
// 如果支持promise
const p = Promise.resolve();
timerFunc = () => {
p.then(flushCallbacks);
};
} else if (typeof MutationObserver !== "undefined") {
// MutationObserver 主要是監(jiān)聽dom變化 也是一個異步方法
let counter = 1;
const observer = new MutationObserver(flushCallbacks);
const textNode = document.createTextNode(String(counter));
observer.observe(textNode, {
characterData: true,
});
timerFunc = () => {
counter = (counter + 1) % 2;
textNode.data = String(counter);
};
} else if (typeof setImmediate !== "undefined") {
// 如果前面都不支持 判斷setImmediate
timerFunc = () => {
setImmediate(flushCallbacks);
};
} else {
// 最后降級采用setTimeout
timerFunc = () => {
setTimeout(flushCallbacks, 0);
};
}
export function nextTick(cb) {
// 除了渲染watcher 還有用戶自己手動調用的nextTick 一起被收集到數組
callbacks.push(cb);
if (!pending) {
// 如果多次調用nextTick 只會執(zhí)行一次異步 等異步隊列清空之后再把標志變?yōu)閒alse
pending = true;
timerFunc();
}
}
了解nextTick嗎貌矿?
異步方法炭菌,異步渲染最后一步,與JS事件循環(huán)聯系緊密逛漫。主要使用了宏任務微任務(setTimeout
黑低、promise
那些),定義了一個異步方法酌毡,多次調用nextTick
會將方法存入隊列克握,通過異步方法清空當前隊列。
vue 中使用了哪些設計模式
1.工廠模式 - 傳入參數即可創(chuàng)建實例
虛擬 DOM 根據參數的不同返回基礎標簽的 Vnode 和組件 Vnode
2.單例模式 - 整個程序有且僅有一個實例
vuex 和 vue-router 的插件注冊方法 install 判斷如果系統存在實例就直接返回掉
3.發(fā)布-訂閱模式 (vue 事件機制)
4.觀察者模式 (響應式數據原理)
5.裝飾模式: (@裝飾器的用法)
6.策略模式 策略模式指對象有某個行為,但是在不同的場景中,該行為有不同的實現方案-比如選項的合并策略
Vue 修飾符有哪些
事件修飾符
- .stop 阻止事件繼續(xù)傳播
- .prevent 阻止標簽默認行為
- .capture 使用事件捕獲模式,即元素自身觸發(fā)的事件先在此處處理枷踏,然后才交由內部元素進行處理
- .self 只當在 event.target 是當前元素自身時觸發(fā)處理函數
- .once 事件將只會觸發(fā)一次
- .passive 告訴瀏覽器你不想阻止事件的默認行為
v-model 的修飾符
- .lazy 通過這個修飾符玛荞,轉變?yōu)樵?change 事件再同步
- .number 自動將用戶的輸入值轉化為數值類型
- .trim 自動過濾用戶輸入的首尾空格
鍵盤事件的修飾符
- .enter
- .tab
- .delete (捕獲“刪除”和“退格”鍵)
- .esc
- .space
- .up
- .down
- .left
- .right
系統修飾鍵
- .ctrl
- .alt
- .shift
- .meta
鼠標按鈕修飾符
- .left
- .right
- .middle
computed 的實現原理
computed 本質是一個惰性求值的觀察者。
computed 內部實現了一個惰性的 watcher,也就是 computed watcher,computed watcher 不會立刻求值,同時持有一個 dep 實例呕寝。
其內部通過 this.dirty 屬性標記計算屬性是否需要重新求值。
當 computed 的依賴狀態(tài)發(fā)生改變時,就會通知這個惰性的 watcher,
computed watcher 通過 this.dep.subs.length 判斷有沒有訂閱者,
有的話,會重新計算,然后對比新舊值,如果變化了,會重新渲染婴梧。 (Vue 想確保不僅僅是計算屬性依賴的值發(fā)生變化下梢,而是當計算屬性最終計算的值發(fā)生變化時才會觸發(fā)渲染 watcher 重新渲染,本質上是一種優(yōu)化塞蹭。)
沒有的話,僅僅把 this.dirty = true孽江。 (當計算屬性依賴于其他數據時,屬性并不會立即重新計算番电,只有之后其他地方需要讀取屬性的時候岗屏,它才會真正計算,即具備 lazy(懶計算)特性漱办。)
v-model 的原理这刷?
我們在 vue 項目中主要使用 v-model 指令在表單 input、textarea娩井、select 等元素上創(chuàng)建雙向數據綁定暇屋,我們知道 v-model 本質上不過是語法糖,v-model 在內部為不同的輸入元素使用不同的屬性并拋出不同的事件:
- text 和 textarea 元素使用 value 屬性和 input 事件洞辣;
- checkbox 和 radio 使用 checked 屬性和 change 事件咐刨;
- select 字段將 value 作為 prop 并將 change 作為事件。
以 input 表單元素為例:
<input v-model='something'>
相當于
<input v-bind:value="something" v-on:input="something = $event.target.value">
復制代碼
如果在自定義組件中扬霜,v-model 默認會利用名為 value 的 prop 和名為 input 的事件定鸟,如下所示:
父組件:
<ModelChild v-model="message"></ModelChild>
子組件:
<div>{{value}}</div>
props:{
value: String
},
methods: {
test1(){
this.$emit('input', '小紅')
},
},
computed 和 watch 的區(qū)別和運用的場景?
computed: 是計算屬性著瓶,依賴其它屬性值联予,并且 computed 的值有緩存,只有它依賴的屬性值發(fā)生改變,下一次獲取 computed 的值時才會重新計算 computed 的值躯泰;
watch: 更多的是「觀察」的作用谭羔,類似于某些數據的監(jiān)聽回調 ,每當監(jiān)聽的數據變化時都會執(zhí)行回調進行后續(xù)操作麦向;
運用場景:
- 當我們需要進行數值計算瘟裸,并且依賴于其它數據時,應該使用 computed诵竭,因為可以利用 computed 的緩存特性话告,避免每次獲取值時,都要重新計算卵慰;
- 當我們需要在數據變化時執(zhí)行異步或開銷較大的操作時沙郭,應該使用 watch,使用 watch 選項允許我們執(zhí)行異步操作 ( 訪問一個 API )裳朋,限制我們執(zhí)行該操作的頻率病线,并在我們得到最終結果前,設置中間狀態(tài)鲤嫡。這些都是計算屬性無法做到的。
虛擬 DOM 的優(yōu)缺點暖眼?
優(yōu)點:
- 保證性能下限: 框架的虛擬 DOM 需要適配任何上層 API 可能產生的操作惕耕,它的一些 DOM 操作的實現必須是普適的,所以它的性能并不是最優(yōu)的诫肠;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虛擬 DOM 至少可以保證在你不需要手動優(yōu)化的情況下栋豫,依然可以提供還不錯的性能漱受,即保證性能的下限骡送;
- 無需手動操作 DOM: 我們不再需要手動去操作 DOM昂羡,只需要寫好 View-Model 的代碼邏輯,框架會根據虛擬 DOM 和 數據雙向綁定摔踱,幫我們以可預期的方式更新視圖虐先,極大提高我們的開發(fā)效率;
- 跨平臺: 虛擬 DOM 本質上是 JavaScript 對象,而 DOM 與平臺強相關派敷,相比之下虛擬 DOM 可以進行更方便地跨平臺操作蛹批,例如服務器渲染撰洗、weex 開發(fā)等等。
缺點:
- 無法進行極致優(yōu)化: 雖然虛擬 DOM + 合理的優(yōu)化腐芍,足以應對絕大部分應用的性能需求差导,但在一些性能要求極高的應用中虛擬 DOM 無法進行針對性的極致優(yōu)化。
雙向數據綁定的原理
Vue.js 是采用數據劫持結合發(fā)布者-訂閱者模式的方式猪勇,通過Object.defineProperty()來劫持各個屬性的setter设褐,getter,在數據變動時發(fā)布消息給訂閱者泣刹,觸發(fā)相應的監(jiān)聽回調助析。主要分為以下幾個步驟:
- 需要observe的數據對象進行遞歸遍歷,包括子屬性對象的屬性椅您,都加上setter和getter這樣的話外冀,給這個對象的某個值賦值,就會觸發(fā)setter掀泳,那么就能監(jiān)聽到了數據變化
- compile解析模板指令雪隧,將模板中的變量替換成數據,然后初始化渲染頁面視圖员舵,并將每個指令對應的節(jié)點綁定更新函數脑沿,添加監(jiān)聽數據的訂閱者,一旦數據有變動固灵,收到通知,更新視圖
- Watcher訂閱者是Observer和Compile之間通信的橋梁劫流,主要做的事情是: ①在自身實例化時往屬性訂閱器(dep)里面添加自己 ②自身必須有一個update()方法 ③待屬性變動dep.notice()通知時巫玻,能調用自身的update()方法,并觸發(fā)Compile中綁定的回調祠汇,則功成身退仍秤。
- MVVM作為數據綁定的入口,整合Observer可很、Compile和Watcher三者诗力,通過Observer來監(jiān)聽自己的model數據變化,通過Compile來解析編譯模板指令我抠,最終利用Watcher搭起Observer和Compile之間的通信橋梁苇本,達到數據變化 -> 視圖更新;視圖交互變化(input) -> 數據model變更的雙向綁定效果菜拓。
Vue 中的 key 到底有什么用瓣窄?
key 是給每一個 vnode 的唯一 id,依靠 key,我們的 diff 操作可以更準確、更快速 (對于簡單列表頁渲染來說 diff 節(jié)點也更快,但會產生一些隱藏的副作用,比如可能不會產生過渡效果,或者在某些節(jié)點有綁定數據(表單)狀態(tài)纳鼎,會出現狀態(tài)錯位俺夕。)
diff 算法的過程中,先會進行新舊節(jié)點的首尾交叉對比,當無法匹配的時候會用新節(jié)點的 key 與舊節(jié)點進行比對,從而找到相應舊節(jié)點.
更準確 : 因為帶 key 就不是就地復用了,在 sameNode 函數 a.key === b.key 對比中可以避免就地復用的情況裳凸。所以會更加準確,如果不加 key,會導致之前節(jié)點的狀態(tài)被保留下來,會產生一系列的 bug。
更快速 : key 的唯一性可以被 Map 數據結構充分利用,相比于遍歷查找的時間復雜度 O(n),Map 的時間復雜度僅僅為 O(1),源碼如下:
function createKeyToOldIdx(children, beginIdx, endIdx) {
let i, key;
const map = {};
for (i = beginIdx; i <= endIdx; ++i) {
key = children[i].key;
if (isDef(key)) map[key] = i;
}
return map;
}
你有對 Vue 項目進行哪些優(yōu)化劝贸?
(1)代碼層面的優(yōu)化
- v-if 和 v-show 區(qū)分使用場景
- computed 和 watch 區(qū)分使用場景
- v-for 遍歷必須為 item 添加 key姨谷,且避免同時使用 v-if
- 長列表性能優(yōu)化
- 事件的銷毀
- 圖片資源懶加載
- 路由懶加載
- 第三方插件的按需引入
- 優(yōu)化無限列表性能
- 服務端渲染 SSR or 預渲染
(2)Webpack 層面的優(yōu)化
- Webpack 對圖片進行壓縮
- 減少 ES6 轉為 ES5 的冗余代碼
- 提取公共代碼
- 模板預編譯
- 提取組件的 CSS
- 優(yōu)化 SourceMap
- 構建結果輸出分析
- Vue 項目的編譯優(yōu)化
(3)基礎的 Web 技術的優(yōu)化
- 開啟 gzip 壓縮
- 瀏覽器緩存
- CDN 的使用
- 使用 Chrome Performance 查找性能瓶頸
如何在組件中重復使用Vuex的mutation
使用mapMutations輔助函數,在組件中這么使用
import { mapMutations } from 'vuex'
methods:{
...mapMutations({
setNumber:'SET_NUMBER',
})
}
復制代碼
然后調用this.setNumber(10)
相當調用this.$store.commit('SET_NUMBER',10)
v-for 為什么要加 key
如果不使用 key,Vue 會使用一種最大限度減少動態(tài)元素并且盡可能的嘗試就地修改/復用相同類型元素的算法映九。key 是為 Vue 中 vnode 的唯一標記梦湘,通過這個 key,我們的 diff 操作可以更準確氯迂、更快速
更準確:因為帶 key 就不是就地復用了践叠,在 sameNode 函數 a.key === b.key 對比中可以避免就地復用的情況。所以會更加準確嚼蚀。
更快速:利用 key 的唯一性生成 map 對象來獲取對應節(jié)點禁灼,比遍歷方式更快