答題思路:
Vue
生命週期是什麼?
Vue
生命週期有哪些階段?
Vue
生命週期的流程?
結合實踐
擴充套件:在 Vue3
變化 Vue
生命週期的變化 【相關推薦:、】
回答範例:
生命週期這個詞應該是很好理解的,在我們生活中就會常常碰到,比如談到一個人的生命週期,我們會說人這一生會經歷嬰兒、兒童、少年、青年、中年、老年這幾個階段。
而 Vue
的生命週期也是如此,在 Vue
中的每個元件都會經歷從建立到掛載到更新再到銷燬這幾個階段,而在這些階段中,Vue
會執行一種叫做生命週期勾點的函數,方便我們在特定的階段有機會新增上我們自己的程式碼。
Vue 生命週期總共可以分為 8
各階段:建立前後、掛載前後、更新前後、銷燬前後,以及一些特殊場景的生命週期(keep-alive
啟用時、捕獲後代元件錯誤時)。Vue3
中還新增了三個用於偵錯和伺服器端渲染場景。
這幾個階段對應的勾點函數 API依次為:beforeCreate
create
beforeMount
mounted
beforeUpdate
updated
activated(keep-alive 啟用時呼叫)
deactivated(keep-alive 停用時呼叫)
beforeDestory
destoryed
errorCaptured(捕獲子孫元件錯誤時呼叫)
。
在 Vue3 中的變化 絕大多數只要加上字首 on 即可,比如 mounted
變為 onMounted
,除了 beforeDestroy
和 destroyed
被重新命名為 beforeUnmount
和 unMounted
(這樣與前面的 beforeMount
和 mounted
對應,強迫症表示很贊?)
beforeCreate
在元件建立前呼叫,通常用於外掛開發中執行一些初始化任務;created
元件建立完畢呼叫,可以存取各種資料,請求介面資料等;mounted
元件掛載時呼叫 可以存取資料、dom
元素、子元件等;beforeUpdate
更新前呼叫 此時 view
層還未更新,可用於獲取更新前的各種狀態;updated
完成更新時呼叫 此時view層已經完成更新,所有狀態已經是最新的了;beforeUnmount
範例被銷燬前呼叫,可用於一些定時器或訂閱的取消;unMounted
銷燬一個範例時呼叫 可以清理與其他範例的連結,解綁它的全部指令以及事件監聽器。
在 Vue3 中: setup
是比 created
先執行的; 而且沒有 beforeCreate
和 created
。
許可權管理一般需求就是對頁面許可權和按鈕許可權的管理
具體實現的時候分前端實現和後端實現兩種方案:
前端方案會把所有路由資訊在前端設定,通過路由守衛要求使用者登入,使用者登入後根據角色過濾出路由表,然後在動態新增路由。比如我會設定一個 asyncRoutes
陣列,需要認證的頁面在路由的 meta
中新增一個 roles
欄位,等獲取使用者角色之後取兩者的交集,若結果不為空則說明可以存取。過濾結束後剩下的路由就是使用者能存取的頁面,最後通過 router.addRoutes(accessRoutes)
方式動態新增路由即可。
後端方案會把所有頁面路由資訊存在資料庫中,使用者登入的時候根據其角色查詢得到其能存取的所有路由資訊返回給前端,前端再通過 addRoute
動態新增路由資訊。
按鈕許可權的控制通常會實現一個指令,例如 v-permission
,將按鈕要求角色通過值傳給 v-permission
指令,在指令的 mounted
勾點中可以判斷當前使用者角色和按鈕是否存在交集,有就保留按鈕,沒有就移除按鈕。
純前端方案的優點是實現簡單,不需要額外許可權管理頁面,但是維護起來問題比較大,有新的頁面和角色需求就要修改前端程式碼和重新打包部署;伺服器端方案就不存在這個問題,通過專門的角色和許可權管理頁面,設定頁面和按鈕許可權資訊到資料庫,應用每次登陸時獲取的都是最新的路由資訊。
自己的話:許可權管理一般分頁面許可權和按鈕許可權,而具體實現方案又分前端實現和後端實現,前端實現就是會在前端維護一份動態的路由陣列,通過使用者登入後的角色來篩選它所擁有許可權的頁面,最後通過 addRoute
將動態新增到 router
中;而後端實現的不同點就是這些路由是後端返回給前端,前端再動態新增進去的。
按鈕許可權一般會實現一個 v-permission
,通過判斷使用者有沒有許可權來控制按鈕是否顯示。
純前端方案的優點是實現簡單,但是維護問題大,有新的頁面和角色需求都需要改程式碼重新打包部署,伺服器端則不存在這個問題。
回答思路:
什麼是雙向繫結?
雙向繫結的好處?
在什麼地方使用雙向繫結?
雙向繫結的使用方式、使用細節、Vue3中的變化
原理實現描述
回答:
Vue中的雙向繫結是一個指令 v-model
,它可以繫結一個響應式資料到檢視,同時檢視中變化也能改變該值。
v-model
是一個語法糖,它的原理(預設請情況下)就是通過 :value
將變數掛到 dom
上,再通過 input
事件 監聽 dom
的變化改變變數的值。使用 v-model
的好處就是方便呀,減少了大量的繁瑣的事件處理,提高開發效率。
通常在表單上使用 v-model
,還可以在自定義元件上使用,表示某個值得輸入和輸出控制。
可以結合修飾符做進一步限定(lazy/number/trim),用在自定義元件上時有些不同,它相當於是給了子元件一個 modelValue
的 屬性 和 update:modelValue
的 事件; 在 Vue3 中還可以用引數形式指定多個不同的繫結,如 v-model:foo
這個時候就相當於 給了子元件一個 foo
的 屬性 和 update:foo
的事件。
v-model
作為一個指令,它的原理就是 Vue 編譯器會把它轉換成 value屬性繫結和input的監聽事件,上面說過是預設情況下,實際上編譯器會根據表單元素的不同分配不同的事件,比如 checkbox
和 radio
型別的input
會轉換為 checked
和 change
事件。
Vue 元件之間通訊有以下這麼幾種:
props
$emit
、$on
、$off
、$once
(後三者在Vue3中已被廢除)
$children
(Vue3中廢除)、$parent
$attrs
、$listeners
(Vue3中廢除)
ref
$root
eventbus
(Vue3中不好使了,需要自己封裝)
vuex
、pinia
provide + inject
以上的方法長按使用場景可以分為:
父子元件之間可以使用
props
/$emit
/ $parent
/ ref
/$attrs
兄弟元件之間可以使用
$parent
/ $root
/ eventbus
/ vuex
跨層及元件之間可以使用
eventbus
/ vuex pinia
/ provide + inject
路由懶載入:有效拆分 App
尺寸,存取時才非同步載入
const router = createRouter({
routes: [
{ path : '/foo', component: () => import('./foo.vue)}
]
})
登入後複製
keep-alive
快取頁面:避免重複建立元件範例,且能儲存快取元件狀態
<keep-alive>
<router-view v-if="$route.meta.keepAlive == true"></router-view>
</keep-alive>
<router-view v-if="$route.meta.keepAlive != true"></router-view>
登入後複製
使用 v-show
複用 DOM
:避免重複建立元件
v-for
遍歷避免同時使用 v-if
(實際上這在 Vue3 中是錯誤的寫法)
v-once
和 v-memo
: 不再變化的資料使用 v-once
;按條件跳過更新時使用 v-memo
長列表效能優化:如果是巨量資料長列表,可採用虛擬捲動,只渲染少部分割區域的內容。一些開源庫(vue-virtual-scroller
/ vue-virtual-scroll-grid
)
事件的銷燬:Vue元件銷燬時,會自動解綁它的全部指令以及事件監聽器,但是僅限於元件本身的事件。
圖片懶載入,自定義 v-lazy
指令 (參考專案:vue-lazyload
)
第三方外掛按需引入 element-plus
避免體積太大
子元件分割策略:較重的狀態元件適合拆分
SSR
伺服器端渲染 解決首屏渲染慢的問題
思路:
重新整理後 Vuex 狀態為什麼會丟失?
解決方法
第三方庫以及原理探討
個人理解
回答:
因為 Vuex 只是在記憶體中儲存狀態,重新整理後就會丟失,如果要持久化就要存起來。
可以是用 localStorage
儲存 Vuex
的狀態,store
中把值取出來作為 state
的初始值,提交 mutation
的時候就存入 localStorage
。
可以用 vuex-persist
、vuex-persistedstate
這種外掛,可以通過外掛選項控制哪些需要持久化。內部的原理就是通過訂閱 mutation
變化做統一處理。
這裡有兩個問題,一是如果使用者手動改了 localStorage
怎麼辦?那我 Vuex
裡的狀態不是也改變了?二是由於 localStorage API
的原因只能儲存字串,所以我們只能將資料通過 JSON.stringify
轉換為字串,而當我們儲存的資料為 Map
、Set
、Function
這種參照型別的資料時,JSON.stringify
轉換後會變味 {}
而丟失。
對應第一個問題我的解決方法是可以通過 監聽 storage
事件來清除資料
window.addEventListener("storage", function () {
localStorage.clear();
window.location.href = '/login'
console.error("不要修改localStorage的值~~~");
});
登入後複製
對於第二個問題沒辦法了,只能選擇不適用 Map
和 Set
這種參照型別。
思路:
屬性攔截的幾種方式
defineProperty的問題
Proxy的優點
其他考量
回答:
JS
中做屬性攔截常見的方式有三種:defineProperty
、getter/setters
和 Proxy
Vue2
中使用 defineProperty
的原因是, 2013 年只能使用這種方式,由於該 API
存在一些侷限性,比如對於陣列的攔截有問題,為此 Vue
需要專門為陣列響應式做一套實現。另外不能攔截那些新增、刪除屬性;最後 defineProperty
方案在初始化時需要深度遞迴遍歷處理物件才能對它進行完全攔截,明顯增加了初始化的時間。
以上兩點在 Proxy
出現後迎刃而解,不僅可以對陣列實現攔截,還能對 Map
、Set
實現攔截;另外 Proxy
的攔截也是懶處理行為,如果使用者沒有存取巢狀物件,那麼也不會實施攔截,這就讓初始化的速度和記憶體佔用改善了。
Proxy
有相容性問題,完全不支援IE
思路:
必要性
何時用
怎麼用
使用細節
回答:
當打包構建時,Javascript 抱回變得非常大,影響頁面載入。利用路由懶載入我們能把不同路由對應的元件分割成不同的程式碼塊,然後當路由被存取的時候才載入對應最賤,這樣更加高效,是一種優化手段。
一般來說,對於所有的路由都使用動態匯入是個好主意
給 component
選項設定一個返回 Promise元件的函數就可以定義懶載入路由.例如:
{
path: '/login',
component: () => import('../views/login/Login.vue')
},
登入後複製
結合註釋
{
path: '/login',
component: () => import(/* webpackChunkName: "login" */'../views/login/Login.vue')
},
登入後複製
vite中結合rollupOptions定義分塊 5. 路由中不能使用非同步元件
Vue-Router 有三個模式,其中 history 和 hash 更為常用。兩者差別主要在顯示形式和部署上,
hash模式在位址列現實的時候有一個 #
,這種方式使用和部署都較簡單;history模式url看起來更優雅沒關,但是應用在部署時需要做特殊設定,web伺服器需要做回退處理,否則會出現重新整理頁面404的問題。
在實現上 hash
模式是監聽hashchange
事件觸發路由跳轉,history
模式是監聽popstate
事件觸發路由跳轉。
在 Vue
中 nextTick
是等待下一次 DOM
更新重新整理的工具方法。
Vue
有一個非同步更新策略,意思是如果資料變化,Vue
不會立刻更新 DOM
,而是開啟一個佇列,把元件更新函數儲存在佇列中,在同一時間迴圈中發生的所有資料變更會非同步的批次更新。這一策略導致我們對資料的修改不會立刻體現在 DOM
上,此時如果想要獲取更新後的 DOM
狀態,就需要使用 nextTick
nextTick
接受一個函數,我們可以在這個函數內部存取最新的 DOM
狀態
在開發時,有兩個場景我們會用到 nextTick
:
created
中想要獲取 DOM
;DOM
更新後的狀態;nextTick
的原理:在 Vue
內部,nextTick
之所以能夠讓我們看到 DOM
更新後的結果,是因為我們傳入的 callback
會被新增到佇列重新整理函數的後面,這樣等佇列內部的更新函數都執行完畢,所有 DOM
操作也就結束了,callback
自然能夠獲取最新的 DOM
值。
先回答答案:在 vue2
中, v-for
的優先順序更高
但是在 vue3
中, v-if
的優先順序更高
拓展:無論什麼時候,我們都不應該把 v-for
和 v-if
放在一起,
怎麼解決呢?一是可以定義一個計算屬性,讓 v-for
遍歷計算屬性。二是可以把 if
移到內部容器裡(ul
ol
)或者把v-for
移植外部容器(template
)中
watch
store.subscribe()
watch
方式,可以以字串形式監聽 $store.state.xx
; subscribe
方法引數是一個回撥函數,回撥函數接受mutation
物件和 state
物件,可以通過 mutation.type
判斷監聽的目標。
wtach 方法更簡單好用, subscribe
會略繁瑣,一般用 vuex
外掛中(可以提一下vuex的持久化外掛vuex-persist
、vuex-persistedstate
)
不支援持久化,頁面重新整理狀態就會丟失
使用模組比較繁瑣
不支援 ts
(或者說很不友好)
vue3 + pinia 會是更好的組合。
兩者都能返回響應式物件,ref
返回的是一個響應式Ref
物件, reactive
返回的是響應式代理物件。
ref
通常是處理單值得響應式,reactive
用於處理物件型別的資料響應式
ref
需要通過 .value
存取, 在檢視中會自動脫 ref
,不需要 .value
,ref
可以接收物件或陣列但內部依然是 reactive
實現的;reactive
如果接收 Ref
物件會自動脫 ref
;使用展開運運算元展開 reactive
返回的響應式物件會使其失去響應性,可以結合 toRefs()
將值轉換為 Ref
物件後再展開。
reactive
內部使用 Prxoy
代理攔截物件各種操作,而 ref
內部封裝一個 RefImpl
類,設定 get value/set value
,攔截使用者對值得存取。
邏輯擴充套件:mixins
、extends
、composition api
:
內容擴充套件:slots
mixins
很靈活,但是會衝突很混亂。extends
是一個不太常用的選項,更 mixins
的不同是它只能擴充套件單個物件,優先順序比 mixins
高。
混入的資料和方法 不能明確判斷來源 而且可能和當前元件內變數 產生命名衝突,composition api 可以很好解決這些問題,利用獨立出來的響應式模組可以很方便的編寫獨立邏輯並提供響應式資料局,增強程式碼的可讀性和維護性。
擴充套件:Vue.mixin(全域性混入) Vue.extend(有點像是 類/元件的繼承 建立一個子類)
vue-loader
是用於處理單檔案元件(SFC)的webpack loader
因為有了 vue-loader
,我們才能用 .vue
檔案形式編寫程式碼,將程式碼分割為 template
script
style
webpack
在打包的時候,會以 loader
的方式呼叫 vue-loader
vue-loader
被執行時,它會對 SFC
中的每個語言塊用單獨的 loader
鏈處理,最後將這些單獨的塊裝配成最終的元件模組
不能直接改。
元件化開發中有一個單向資料流原則,不在子元件修改父元件資料是個常識
如果你確實需要改,請通過emit向父元件傳送一個事件,在父元件中修改
我麼可以在路徑中使用一個動態欄位來實現,例如/users/:id
其中 :id
就是路徑引數。
可以通過 this.$route.parmas
獲取,引數還可以有多個, $route
物件還公開了其他有用的資訊如 query
hash
等。
思路:
什麼是響應式?
為什麼vue需要響應式?
有什麼好處?
vue的響應式怎麼實現的,有哪些優缺點?
vue3中的響應式的新變化
回答:
資料響應式就是 能夠監測到資料變化並且做出響應的一種機制
在 vue
中要解決的一個核心問題就是連線資料層和檢視層,通過資料變化驅動檢視更新,要做到這點就需要對資料做響應式處理。
通過資料響應式加上虛擬 DOM
和 patch
演演算法,我們只需要運算元據,關心業務,完全不需要接觸繁瑣的 DOM
操作,打打提升了開發效率,降低開發難度。
vue2
中實現資料響應式的核心就是通過 Object.defineProperty()
方法對資料進行攔截,當 get
資料時做依賴收集 set
資料時做更新通知。這種機制很好的及絕了資料響應式的問題,但是實際使用也存在缺點,比如在初始化時的遞迴遍歷會造成效能損失;無法監聽新增或刪除屬性,在 vue
中要通過像 Vue.set/delete
這種特定的 API
才能實現對物件陣列屬性的新增和刪除,而且也不支援 Ma
、Set
這些資料結構,
為了解決這些問題,Vue3
重寫了這部分實現,利用的是 ES6
中的 Proxy
代理要響應化的資料。它有很多好處,初始化效能和記憶體都大幅改善,也不需要特殊的 API
,但是不支援 IE
瀏覽器。
問 template
到 render
的過程其實是問的 vue 編譯器
工作原理。
思路:
引入編譯器概念
說明編譯器的必要性
闡述編譯器工作流程
回答:
Vue
中有個獨特的編譯模組,稱為 compiler
,它的主要作用是將 template
編譯為 js
可執行的 render
函數
之所以需要這個編譯過程是為了便於我們高校的編寫試圖模版。相比而言,我們還是更願意用 HTML
來編寫檢視,直觀且高效。手寫 render
函數不僅效率低下,而且失去了被編譯器的優化能力。
Vue
編譯器 首先會對 template進行解析
( Parse
),結束後會得到一個抽象語法樹AST
,然後對 AST
進行深加工轉換(transform
),最後將得到的 AST
生成為 js
程式碼,也就是 render
函數
快取元件可以使用 keep-alive
元件,include 和 exclude 可以指定包含不包含哪些元件。
Vue3
結合 vue-router
使用變化非常大,之前是 keep-alive
包含 router-view
,現在是 router-view
包含 keep-alive
快取後如果想要獲取資料可以使用 actived
勾點 或者 beforeRouteEnter
( vue-router
的一個守衛)
keep-alive
是一個通用元件,它內部定義了一個 map
,快取建立過的元件範例,它返回的渲染函數內部會查詢內嵌的 component
元件對應元件的 vnode
,如果改元件在map中存在就直接返回它。由於 component
的 is
屬性是一個響應式資料,因此只要它變化,keep-alive
的 render
函數就會重新執行。
虛擬 DOM
是什麼?
虛擬 DOM
的本質就是一個 Javascript
物件。
為什麼要引入虛擬 DOM
?(好處)
它能有效減少操作 DOM
的次數,方便實現跨平臺
虛擬DOM如何生成?compiler
編譯器會把 template
模版編譯成渲染函數,接下來在 mount
掛載的過程會呼叫這個渲染函數,返回的物件就是 虛擬DOM
。掛載結束後,會進入更新流程。如果某些響應式資料發生變化,將會引起元件重新 render
,此時會生成新的 虛擬DOM
,和上次渲染結果做 diff
操作,最小量的操作 dom
,從而高效更新檢視。
非同步元件就是不會立即載入而是會在需要的時候載入的元件。在大型應用中,我們需要分割程式碼為更小的塊試就可以用非同步元件。
不僅可以在路由切換時懶載入元件,還可以在元件中使用非同步元件,從而更細的分割程式碼。
使用非同步元件最簡單的方式是直接給 defineAsyncComponet
指定一個 loader
函數,結合 ES 模組 動態匯入函數 import
可以快速實現。Vue3
還可以結合 Suspense
元件使用非同步元件。
非同步元件容易和路由懶載入混淆,實際上不是一個東西。非同步元件不能被用於定義懶載入路由上,處理它的是 Vue
框架,處理路由元件載入的是 vue-router
。但是可以在懶載入的路由元件中使用非同步元件。
v-once
方式只渲染一次懶載入
方式,在使用者需要的時候在載入資料。computed
是計算屬性,watch
是偵聽器。
computed
通常用於處理模版中複雜的邏輯,而 watch
通常用於需要監聽一個響應式物件的變化而做一些操作的時候
watch
可以進行非同步操作,computed
不行。
計算屬性傳遞一個物件 有 set
和 get
兩個選項,是它稱為即可讀又可寫的計算屬性,如果傳遞的是函數的話預設就是 get
選項,watch
可以傳遞一個物件,設定deep、immediate等選項
vue3
中 watch
發生了一些變化,例如不能再偵測一個點操符之外的字串表示式,reactivity API
中新出的 watch
、watchEffect
可以完全替代 watch
選項,而且功能更加強大
SPA
(Single Page Application)是單頁面應用。一般也稱為使用者端渲染,簡稱 CSR
。SSR(Server Side Render) 即伺服器端渲染。一般也稱為多頁面應用(Mulpile Page Application),簡稱 MPA。
SPA
只會首次請求 html
檔案,後續只需要請求 JSON
資料即可,因此使用者體驗更好,節約流量,伺服器端壓力也較小。但是首屏載入的時間會變長,而且 SEO
不友好。為了解決以上缺點,就有了 SSR
方案,由於 HTML
內容在伺服器一次性生成出來,首屏載入快,搜尋引擎也可以很方便的抓取頁面資訊。但同時 SSR
方案也會有效能,開發受限等問題。
選擇上,如果有首屏載入優化需求,SEO需求時,就可以考慮SSR。
但並不是只有這一種替代方案,比如對一些不常變化的靜態網站,SSR反而浪費資源,我們可以考慮預渲染的方案。另外 nuxt.js/next.js
中給我們提供了SSG靜態網站生成方案也是很好的靜態站點解決方案,結合一些CI手段,可以起到很好的優化效果。
回答思路:
diff演演算法是幹什麼的?
必要性
何時執行
具體執行方式
拔高:說一下vue3中的優化
回答:
Vue
中的 diff
演演算法稱為 patching
演演算法,虛擬DOM要想轉化為真實DOM就需要通過 patch
方法轉換。
最初 Vue1.x
檢視中農每個依賴均有更新函數對應,可以做到精確更新,因此不需要 虛擬DOM
和 patching
演演算法支援,但是這樣粒度過細導致 Vue1.x
無法承載較大應用;Vue2.x
中為了降低 Watcher
粒度,每個元件只有一個 Watcher
與之對應,此時就需要引入 patching
演演算法才能精確找到發生變化的地方並高效更新。
vue
中 diff
執行的時刻是元件內響應式資料變更觸發範例執行其更新函數時,更新函數會再次執行 render函數
獲得最新的 虛擬DOM
,然後執行 patch函數
,對比新舊虛擬DOM,將其轉化為對應的 DOM
操作。
patch
過程是一個遞迴過程,遵循深度優先、同層比較的策略;以 vue3
的patch
為例:
vue3
中引入的更新策略:編譯期優化 patchFlags
、block
等
從 0 建立專案我大致會做以下事情:專案構建、引入必要外掛、程式碼規範、提交規範、常用庫和元件
目前vue3專案我會用vite或者create-vue建立專案
接下來引入必要外掛:vue-router、vuex/pinia、element-plus、antd-vue、axios等等
其他常用的庫有 像lodash、dayjs、nprogress等等..
下面是程式碼規範: editorconfig、prettier、eslint
最後是提交規範,可以使用husky、Commitizen
目錄結構我喜歡按照下面的結構來
+ |- /src
+ |- /assets 存放資源
+ |- /img
+ |- /css
+ |- /font
+ |- /data
+ |- base-ui 存放多個專案中都會用到的公共元件
+ |- components 存放這個專案用到的公共元件
+ |- hooks 存放自定義hook
+ |- views 檢視
+ |- store 狀態管理
+ |- router 路由
+ |- service 網路請求
+ |- utils 工具
+ |- global 全域性註冊、全域性常數..
登入後複製
一個 SPA
應用的路由需要解決的問題時頁面跳轉內容改變同時不重新整理,同時路由還需要已外掛形式存在,所以:
首先我會定義一個 createRouter
函數,返回路由器範例,範例內部做幾件事;
hash
或者 popstate
事件path
匹配對應路由將 router
定義成一個 Vue
外掛,即實現 install
方法,內部做兩件事:
router-link
和 router-view
,分別實現頁面跳轉和內容顯示$router
和 $route
,元件內可以存取當前路由和路由器範例在專案規模變大的之後,單獨一個store物件會過於龐大臃腫,此時通過模組方式可以拆分來便於維護
可以按之前規則單獨編寫資規模程式碼,然後在主檔案中通過 modules
選項組織起來:createStore({modules: {...}})
使用時需要注意存取子模組狀態時需要加上註冊模組名。但同時getters
、mutations
和 actions
又在全域性空間中,使用方式和之前一樣。如果要做到完全拆分,需要在子模組加上 namespace
選項,此時再存取它們就要加上名稱空間字首。
模組的方式可以拆分程式碼,但是缺點也很明顯,使用起來比較繁瑣,容易出錯,而且型別系統支援很差,不能給我們帶來幫助。pinia 顯然在這方面有了很大改進,是時候切換過去了。
vue2
中元件確實只能有一個跟,但 vue3
中元件已經可以多根元件了。
之所以需要這樣是因為 vdom
是一顆單根樹形結構,patch
方法在遍歷的時候從根節點開始遍歷,它要求只有一個根節點。元件也會轉換為一個 vdom
,自然應該滿足這個要求。
vue3
中之所以可以寫多個根節點,是因為引入了 Fragment
的概念,這是一個抽象的節點,如果發現元件時多根的,就建立一個 Fragment
節點,把多個根節點作為它的 children
。將來 pathch
的時候,如果發現是一個 Fragment
節點,則直接遍歷 children
建立或更新。
v-once
是 vue
的內建指令,作用是僅渲染指定元件或元素一次,並跳過未來對其更新。
如果我們有一些元素或者元件再初始化渲染之後不再需要變化,這種情況下適合使用 v-once
,這樣哪怕這些資料變化,vue
也會跳過更新,是一種程式碼優化手段。
我們只需要作用的元件或元素上加上 v-once
即可。
補充:
vue3.2
之後,又增加了 v-memo
,這個指令可以有條件的快取模板並控制他們的更新。
v-once
的原理:編譯器發現有 v-once
時,會將首次計算結果存入快取物件,元件再次渲染時就會從快取獲取,從而避免再次計算。
url
各部分通常對應某個巢狀的元件,vue-router
中可以使用巢狀路由表示這種關係。view-router
,從而形成物理上的巢狀,和邏輯上的巢狀對應起來。定義巢狀路由時使用 children
屬性組織巢狀關係router-view
元件內部判斷其所處巢狀的深度,將這個深度作為匹配元件陣列 matched
的索引,獲取對應渲染元件並渲染之。如果你說不出來,可以直接舉例子。當我開發一個頁面時,如果需要顯示一個頂部導航欄,通過導航欄跳轉到不同的頁面,而頂部的導航欄又必須要在每個頁面顯示時,就可以使用巢狀路由;還可以舉例,當我需要檢視某個列表的詳情頁面時,往往需要巢狀路由 (detail/:id
)
watch
store.subscribe()
watch
方式,可以以字串形式監聽 $store.state.xx
; subscribe
方法引數是一個回撥函數,回撥函數接受mutation
物件和 state
物件,可以通過 mutation.type
判斷監聽的目標。
wtach 方法更簡單好用, subscribe
會略繁瑣,一般
掛載範例的過程就是 app.mount()的過程,整體上就做了兩件事:初始化和建立更新機制
初始化會建立元件範例、初始化元件狀態、建立各種響應式資料
簡歷更新機制這一步會立即執行一次元件更新函數,這會首次執行渲染函數並執行 patch
將前面獲得vnode
轉換為 dom
;同時會建立它內部響應式資料和元件更新函數之間的依賴關係,這使得以後資料變化時會執行對應的更新函數。
key
的作用主要是為了更高效的更新虛擬 DOM
。
key
是 vue
在 patch
過程中判斷兩個節點是否是相同節點的關鍵條件(另一個是元素型別),如果不設定 key
,它的值就是 undefined
,vue
則可能永遠認為這是兩個相同節點,只能去做更新操作,這造成了大量的 dom
更新操作,明顯是不可取的。
實際使用的過程中必須設定 key
,而且應該儘量避免使用陣列索引,這可能導致一些隱藏 bug
。
watchEffect
立即執行函數,被動地追蹤它的依賴,傳入的函數即是依賴收集的資料來源,也是回撥函數;watch
偵測一個或多個響應式資料來源,在資料來源變化時呼叫一個回撥函數,通過 immediate
選項也可以設定立即執行一次。
watchEffect
是一種特殊的 watch
。如果不關心響應式資料前後的值,可以使用 watchEffect
。其他情況都可以用 watch
。
parent created -> child created -> child mounted -> parent mounted
原因:Vue
建立是一個遞迴的過程,先建立父元件,有子元件就會建立子元件,因此建立時先有父元件再有子元件;子元件首次建立時會新增 Mounted
勾點到佇列,等到 patch
結束再執行它們,可見子元件的 mounted
勾點是選進入到佇列中的,因此等到 patch
結束執行這些勾點時也先執行。
vuex是一個專門為vue應用開發的狀態管理模式庫,
當你遇到多個元件共用狀態時或者專案中的元件難以管理的時候就可以使用vuex,它以一個全域性單例模式管理全域性的狀態。
基本核心概念有 state、mutation、action、getters、module等
說些使用過程的感受 ts不友好 模組使用繁瑣 頁面重新整理資料也會消失
如果某個元件通過元件名稱參照它自己,這種情況就是遞迴元件。
類似 Tree
、Menu
這類元件,它們的節點往往包含子節點,子節點結構和父節點往往是相同的。這類元件的資料往往也是樹形結構,這種都是使用遞迴元件的典型場景。
使用自定義指令分為定義、註冊、和使用
定義有兩種方式,物件和函數形式,前者類似元件定義,有各種生命週期;後者只會在 mounted
和 updated
時執行
註冊:可以使用 app.directive
全域性註冊 也可以通過選項區域性註冊
使用時在註冊名稱前加上 v-即可。
v-copy
複製貼上
v-lazy
圖片懶載入
v-debounce
防抖
v-permission
按鈕許可權
v-longpress
長按
API 層面
Composition API
setup
語法糖
Teleport
傳送門
Fragments
可以多個根節點
Emits
createRenderer
自定義渲染器
SFC
狀態驅動 css
變數 (v-bind in <style>
)
此外,Vue3在框架層面也有很多兩點和改進
DOM
重寫patchFlags
、block
等Proxy
的響應式系統最大設計目標就是替代 Vue2
,為了實現這一點,Vue3
在以下幾個方面做了很大改進,如:易用性,框架效能、擴充套件性、可維護性、開發體驗等
易用性方面:主要有 API
簡化 v-model
變成了 v-model
和 sync
修飾符的結合體。類似的還有 h(type,props,children)
函數中的 props
不用考慮區分屬性、特性、事件等,框架替我們判斷,易用性增。
開發體驗方面:新組建 Teleport
Fragment
Suspense
等都會簡化特定場景的程式碼編寫。 setup
語法糖更是極大提升了我們的開發體驗。
擴充套件性方面提升: 如獨獨立的 reactivity
模組,custom render API
等
可維護性方面主要是 Composition API
,更容易編寫高複用性的業務邏輯。還有對TS支援的提升。
效能方面:編譯器優化、基於 Proxy
的響應式系統。
。。。
程式碼方面:全新的響應式API,基於 Proxy
實現,初始化事件和記憶體佔用均大幅改進;
編譯方面:做了更多編譯優化處理,比如靜態提升、動態內容標記、事件快取、區塊等,可以有效跳過大量diff過程
打包方面:更好的支援 tree-shaking
,因此體積更小,載入更快.(因為vue3 所有的API都通過ES6模組化的方式引入,這樣就能讓webpack或rollup等打包工具在打包時對沒有用到API進行剔除,最小化bundle體積)
$attrs
和 $listeners
是做什麼的?$attrs
獲取沒有在 props
中定義的屬性,v-bind="$attrs"
可以用於屬性透傳$listeners
用於獲取事件,vue3
中已經移除合併到 attrs
中,使用起來更方便
Composition API
是一組API,包括 Reactivity API
、生命勾點、依賴注入,使使用者可以通過匯入函數方式編寫元件,而 Options API
則通過宣告元件選項的物件形式編寫元件。
Composition API
更簡潔、邏輯複用更高效。解決的過去 Options API
中 mixins
的各種缺點(會衝突很混亂);另外 Composition API
更自由,沒有 Options API
那樣固定的寫法,並且可以更有效的將邏輯程式碼組織在一起,而不用東一塊西一塊搞得很混亂,最後 Composition API
擁有更好的型別推斷,對 ts
支援友好。
編碼風格方面:
元件命名時使用 多詞風格避免和html元素衝突
屬性名峰命名,模板或jsx中使用 肉串命名
v-for 務必加上key 且不要和v-if寫在一起‘’
效能方面:
路由懶載入減少應用尺寸
SSR
減少首屏載入事件
v-once
v-memo
長列表 虛擬捲動技術
對於深層巢狀物件的巨量資料可以使用 shallowRef
或 shallowReactive
降低開銷
避免不必要的元件抽象
mutation
用於修改 state
action
用於提交一個 mutation
,而且 action
可以包含非同步操作
要實現一個 Store
儲存全域性狀態
要提供修改狀態所需的API:commit({type, payload})
, dispatch(type,payload)
實現 Store
,可以定義 Store
類,建構函式接受選項 options
,設定屬性 state
對外暴露狀態,提供 commit
和 dispatch
修改屬性。這裡需要設定 state
為響應式物件,同時將 Store
定義為一個 Vue
外掛(install方法)。
commit
可以獲取使用者傳入 mutations
並執行它,這樣可以按使用者提供的方法修改狀態,dispatch
類似,但是 dispatch
需要返回一個 Promise
給使用者用於處理非同步結果。
(學習視訊分享:、)
以上就是【整理彙總】45+個Vue面試題,帶你鞏固知識點!的詳細內容,更多請關注TW511.COM其它相關文章!