文章目錄
- 1. Vue的基本原理
- 2. 雙向資料綁定的原理
- 3. 使用 Object.defineProperty() 來進行資料劫持有什麼缺點?
- 4. MVVM、MVC、MVP的差別
- 5. Computed 和 Watch 的差別
- 6. Computed 和 Methods 的差別
- 7. slot是什麼?有什麼作用?原理是什麼?
- 8. 過濾器的作用,如何實作一個過濾器
- 9. 如何儲存頁面的目前的狀态
- 10. 常見的事件修飾符及其作用
- 11. v-if、v-show、v-html 的原理
- 13. v-if和v-show的差別
1. Vue的基本原理
當一個Vue執行個體建立時,Vue會周遊data中的屬性,用 Object.defineProperty(vue3.0使用proxy )将它們轉為 getter/setter,并且在内部追蹤相關依賴,在屬性被通路和修改時通知變化。 每個元件執行個體都有相應的 watcher 程式執行個體,它會在元件渲染的過程中把屬性記錄為依賴,之後當依賴項的setter被調用時,會通知watcher重新計算,進而緻使它關聯的元件得以更新。
2. 雙向資料綁定的原理
Vue.js 是采用資料劫持結合釋出者-訂閱者模式的方式,通過Object.defineProperty()來劫持各個屬性的setter,getter,在資料變動時釋出消息給訂閱者,觸發相應的監聽回調。主要分為以下幾個步驟:
- 需要observe的資料對象進行遞歸周遊,包括子屬性對象的屬性,都加上setter和getter這樣的話,給這個對象的某個值指派,就會觸發setter,那麼就能監聽到了資料變化
- compile解析模闆指令,将模闆中的變量替換成資料,然後初始化渲染頁面視圖,并将每個指令對應的節點綁定更新函數,添加監聽資料的訂閱者,一旦資料有變動,收到通知,更新視圖
- Watcher訂閱者是Observer和Compile之間通信的橋梁,主要做的事情是: ①在自身執行個體化時往屬性訂閱器(dep)裡面添加自己 ②自身必須有一個update()方法 ③待屬性變動dep.notice()通知時,能調用自身的update()方法,并觸發Compile中綁定的回調,則功成身退。
- MVVM作為資料綁定的入口,整合Observer、Compile和Watcher三者,通過Observer來監聽自己的model資料變化,通過Compile來解析編譯模闆指令,最終利用Watcher搭起Observer和Compile之間的通信橋梁,達到資料變化 -> 視圖更新;視圖互動變化(input) -> 資料model變更的雙向綁定效果。
3. 使用 Object.defineProperty() 來進行資料劫持有什麼缺點?
在對一些屬性進行操作時,使用這種方法無法攔截,比如通過下标方式修改數組資料或者給對象新增屬性,這都不能觸發元件的重新渲染,因為 Object.defineProperty 不能攔截到這些操作。更精确的來說,對于數組而言,大部分操作都是攔截不到的,隻是 Vue 内部通過重寫函數的方式解決了這個問題。
在 Vue3.0 中已經不使用這種方式了,而是通過使用 Proxy 對對象進行代理,進而實作資料劫持。使用Proxy 的好處是它可以完美的監聽到任何方式的資料改變,唯一的缺點是相容性的問題,因為 Proxy 是 ES6 的文法。
4. MVVM、MVC、MVP的差別
MVC、MVP 和 MVVM 是三種常見的軟體架構設計模式,主要通過分離關注點的方式來組織代碼結構,優化開發效率。
(1)MVC
MVC 通過分離 Model、View 和 Controller 的方式來組織代碼結構。其中 View 負責頁面的顯示邏輯,Model 負責存儲頁面的業務資料,以及對相應資料的操作。并且 View 和 Model 應用了觀察者模式,當 Model 層發生改變的時候它會通知有關 View 層更新頁面。Controller 層是 View 層和 Model 層的紐帶,它主要負責使用者與應用的響應操作,當使用者與頁面産生互動的時候,Controller 中的事件觸發器就開始工作了,通過調用 Model 層,來完成對 Model 的修改,然後 Model 層再去通知 View 層更新.
(2)MVVM
MVVM 分為 Model、View、ViewModel:
- Model代表資料模型,資料和業務邏輯都在Model層中定義;
- View代表UI視圖,負責資料的展示;
- ViewModel負責監聽Model中資料的改變并且控制視圖的更新,處理使用者互動操作;
Model和View并無直接關聯,而是通過ViewModel來進行聯系的,Model和ViewModel之間有着雙向資料綁定的聯系。是以當Model中的資料改變時會觸發View層的重新整理,View中由于使用者互動操作而改變的資料也會在Model中同步。
這種模式實作了 Model和View的資料自動同步,是以開發者隻需要專注于資料的維護操作即可,而不需要自己操作DOM。
(3)MVP
MVP 模式與 MVC 唯一不同的在于 Presenter 和 Controller。在 MVC 模式中使用觀察者模式,來實作當 Model 層資料發生變化的時候,通知 View 層的更新。這樣 View 層和 Model 層耦合在一起,當項目邏輯變得複雜的時候,可能會造成代碼的混亂,并且可能會對代碼的複用性造成一些問題。MVP 的模式通過使用 Presenter 來實作對 View 層和 Model 層的解耦。MVC 中的Controller 隻知道 Model 的接口,是以它沒有辦法控制 View 層的更新,MVP 模式中,View 層的接口暴露給了 Presenter 是以可以在 Presenter 中将 Model 的變化和 View 的變化綁定在一起,以此來實作 View 和 Model 的同步更新。這樣就實作了對 View 和 Model 的解耦,Presenter 還包含了其他的響應邏輯。
5. Computed 和 Watch 的差別
對于Computed:
- 支援緩存,隻有依賴的資料發生變化,才重新計算
- 不支援異步,當computed存在異步的時候,無法監聽資料
- computed預設走緩存,計算屬性是基于他們的響應式依賴進行緩存的,也就是data聲明的,props傳遞過來的資料進行計算的
- 如果一個屬性是基于其他屬性的時候,那麼這個屬性依賴的屬性也會使用computed
- 如果computed屬性的屬性值是函數,那麼預設使用get方法,函數的傳回值就是屬性的屬性值;在computed中,屬性有一個get方法和一個set方法,當資料發生變化時,會調用set方法。
對于watch:
- 不支援緩存
- 支援異步
- 監聽的函數接收兩個參數,第一個參數是最新的值,第二個是變化之前的值
- 當一個屬性發生變化時,就需要執行相應的操作
-
監聽資料必須是data中聲明的或者父元件傳遞過來的props中的資料,當發生變化時,會觸發其他操作,函數有兩個的參數:
immediate:元件加載立即觸發回調函數
deep:深度監聽,發現資料内部的變化,在複雜資料類型中使用,例如數組中的對象發生變化。需要注意的是,deep無法監聽到數組和對象内部的變化。
當想要執行異步或者昂貴的操作以響應不斷的變化時,就需要使用watch。
總結:
總結:
- computed 計算屬性 : 依賴其它屬性值,并且 computed 的值有緩存,隻有它依賴的屬性值發生改變,下一次擷取 computed 的值時才會重新計算 computed 的值。
- watch 偵聽器 : 更多的是觀察的作用,無緩存性,類似于某些資料的監聽回調,每當監聽的資料變化時都會執行回調進行後續操作。
運用場景:
- 當需要進行數值計算,并且依賴于其它資料時,應該使用 computed,因為可以利用 computed 的緩存特性,避免每次擷取值時都要重新計算。
- 當需要在資料變化時執行異步或開銷較大的操作時,應該使用 watch,使用 watch 選項允許執行異步操作 ( 通路一個 API ),限制執行該操作的頻率,并在得到最終結果前,設定中間狀态。這些都是計算屬性無法做到的。
6. Computed 和 Methods 的差別
可以将同一函數定義為一個 method 或者一個計算屬性。對于最終的結果,兩種方式是相同的
不同點:
- computed: 計算屬性是基于它們的依賴進行緩存的,隻有在它的相關依賴發生改變時才會重新求值;
- method 調用總會執行該函數。
7. slot是什麼?有什麼作用?原理是什麼?
slot又名插槽,是Vue的内容分發機制,元件内部的模闆引擎使用slot元素作為承載分發内容的出口。插槽slot是子元件的一個模闆标簽元素,而這一個标簽元素是否顯示,以及怎麼顯示是由父元件決定的。slot又分三類,預設插槽,具名插槽和作用域插槽。
- 預設插槽:又名匿名查抄,當slot沒有指定name屬性值的時候一個預設顯示插槽,一個元件内隻有有一個匿名插槽。
- 具名插槽:帶有具體名字的插槽,也就是帶有name屬性的slot,一個元件可以出現多個具名插槽。
- 作用域插槽:預設插槽、具名插槽的一個變體,可以是匿名插槽,也可以是具名插槽,該插槽的不同點是在子元件渲染作用域插槽時,可以将子元件内部的資料傳遞給父元件,讓父元件根據子元件的傳遞過來的資料決定如何渲染該插槽。
實作原理:當子元件vm執行個體化時,擷取到父元件傳入的slot标簽的内容,存放在vm.slot.default,具名插槽為vm.slot中的内容進行替換,此時可以為插槽傳遞資料,若存在資料,則可稱該插槽為作用域插槽。
8. 過濾器的作用,如何實作一個過濾器
根據過濾器的名稱,過濾器是用來過濾資料的,在Vue中使用filters來過濾資料,filters不會修改資料,而是過濾資料,改變使用者看到的輸出(計算屬性 computed ,方法 methods 都是通過修改資料來處理資料格式的輸出顯示)。
使用場景:
- 需要格式化資料的情況,比如需要處理時間、價格等資料格式的輸出 / 顯示。
- 比如後端傳回一個 年月日的日期字元串,前端需要展示為 多少天前 的資料格式,此時就可以用fliters過濾器來處理資料。
過濾器是一個函數,它會把表達式中的值始終當作函數的第一個參數。過濾器用在插值表達式 {{ }} 和 v-bind 表達式 中,然後放在操作符“ | ”後面進行訓示。
例如,在顯示金額,給商品價格添加機關:
filters: {
filterPrice (price) {
return price ? ('¥' + price) : '--'
}
}
9. 如何儲存頁面的目前的狀态
既然是要保持頁面的狀态(其實也就是元件的狀态),那麼會出現以下兩種情況:
- 前元件會被解除安裝
- 前元件不會被解除安裝
元件會被解除安裝:
(1)将狀态存儲在LocalStorage / SessionStorage
隻需要在元件即将被銷毀的生命周期 componentWillUnmount (react)中在 LocalStorage / SessionStorage 中把目前元件的 state 通過 JSON.stringify() 儲存下來就可以了。在這裡面需要注意的是元件更新狀态的時機。
比如從 B 元件跳轉到 A 元件的時候,A 元件需要更新自身的狀态。但是如果從别的元件跳轉到 B 元件的時候,實際上是希望 B 元件重新渲染的,也就是不要從 Storage 中讀取資訊。是以需要在 Storage 中的狀态加入一個 flag 屬性,用來控制 A 元件是否讀取 Storage 中的狀态。
優點:
- 相容性好,不需要額外庫或工具。
- 簡單快捷,基本可以滿足大部分需求。
缺點:
- 狀态通過 JSON 方法儲存(相當于深拷貝),如果狀态中有特殊情況(比如 Date 對象、Regexp 對象等)的時候會得到字元串而不是原來的值。(具體參考用 JSON 深拷貝的缺點)
- 如果 B 元件後退或者下一頁跳轉并不是前元件,那麼 flag 判斷會失效,導緻從其他頁面進入 A 元件頁面時 A 元件會重新讀取 Storage,會造成很奇怪的現象
(2)路由傳值
通過 react-router 的 Link 元件的 prop —— to 可以實作路由間傳遞參數的效果。
在這裡需要用到 state 參數,在 B 元件中通過 history.location.state 就可以拿到 state 值,儲存它。傳回 A 元件時再次攜帶 state 達到路由狀态保持的效果。
優點:
- 簡單快捷,不會污染 LocalStorage / SessionStorage。
- 可以傳遞 Date、RegExp 等特殊對象(不用擔心 JSON.stringify / parse 的不足)
缺點:
- 如果 A 元件可以跳轉至多個元件,那麼在每一個跳轉元件内都要寫相同的邏輯。
前元件不會被解除安裝
(1)單頁面渲染
要切換的元件作為子元件全屏渲染,父元件中正常儲存頁面狀态。
優點:
- 代碼量少
- 不需要考慮狀态傳遞過程中的錯誤
- 增加 A 元件維護成本
- 需要傳入額外的 prop 到 B 元件
- 無法利用路由定位頁面
{
path: '/',
name: 'xxx',
component: ()=>import('../src/views/xxx.vue'),
meta:{
keepAlive: true // 需要被緩存
}
},
10. 常見的事件修飾符及其作用
- .stop:等同于 JavaScript 中的 event.stopPropagation() ,防止事件冒泡;
- prevent :等同于 JavaScript 中的 event.preventDefault() ,防止執行預設的行為(如果事件可取消,則取消該事件,而不停止事件的進一步傳播);
- .capture :與事件冒泡的方向相反,事件捕獲由外到内;
- .self :隻會觸發自己範圍内的事件,不包含子元素;
- .once :隻會觸發一次。
11. v-if、v-show、v-html 的原理
- v-if會調用addIfCondition方法,生成vnode的時候會忽略對應節點,render的時候就不會渲染;
- v-show會生成vnode,render的時候也會渲染成真實節點,隻是在render過程中會在節點的屬性中修改show屬性值,也就是常說的display;
- v-html會先移除節點下的所有節點,調用html方法,通過addProp添加innerHTML屬性,歸根結底還是設定innerHTML為v-html的值。
13. v-if和v-show的差別
- 手段:v-if是動态的向DOM樹内添加或者删除DOM元素;v-show是通過設定DOM元素的display樣式屬性控制顯隐;
- 編譯過程:v-if切換有一個局部編譯/解除安裝的過程,切換過程中合适地銷毀和重建内部的事件監聽和子元件;v-show隻是簡單的基于css切換;
- 編譯條件:v-if是惰性的,如果初始條件為假,則什麼也不做;隻有在條件第一次變為真時才開始局部編譯; v-show是在任何條件下,無論首次條件是否為真,都被編譯,然後被緩存,而且DOM元素保留;
- 性能消耗:v-if有更高的切換消耗;v-show有更高的初始渲染消耗;
- 使用場景:v-if适合營運條件不大可能改變;v-show适合頻繁切換。