您好,登錄后才能下訂單哦!
這篇文章主要介紹了Vue面試題及答案有哪些的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Vue面試題及答案有哪些文章都會有所收獲,下面我們一起來看看吧。
答題思路:
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
)中
vue2文檔vue3文檔
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
。但是可以在懶加載的路由組件中使用異步組件。
避免大數據量:可以采用分頁的方式獲取
避免渲染大量數據:vue-virtual-scroller等虛擬滾動方案,只渲染視口范圍內的數據
避免更新:可以使用v-once
方式只渲染一次
優化更新:通過v-memo緩存組數,有條件更新,提高服用,避免不必要更新
按需加載數據:可以采用 懶加載
方式,在用戶需要的時候在加載數據。
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
為例:
新的子節點是文本,老的子節點是數組則清空,并設置文本;
新的子節點是文本,老的子節點是文本則直接更新文本;
新的子節點是數組,老的子節點是文本則清空文本,并創建新子節點數組中的子元素;
新的子節點是數組,老的子節點也是數組,那么比較兩組子節點,更新細節blabla
首先判斷兩個節點是否為相同同類節點,不同則刪除重新創建
如果雙方都是文本則更新文本內容
如果雙方都是元素節點則遞歸更新子元素,同時更新元素屬性
更新子節點時又分了幾種情況:
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
的響應式系統
更小:更好的搖樹優化
更容易維護:TS + 模塊化
更容易擴展
獨立的響應化模塊
自定義渲染器
最大設計目標就是替代 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
給用戶用于處理異步結果。
關于“Vue面試題及答案有哪些”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“Vue面試題及答案有哪些”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。