這篇文章主要介紹了Vue面試題及答案有哪些的相關(guān)知識,內(nèi)容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Vue面試題及答案有哪些文章都會有所收獲,下面我們一起來看看吧。

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