久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長(zhǎng)資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      29個(gè)Vue經(jīng)典面試題(附源碼級(jí)詳解)

      本篇文章給大家總結(jié)分享29+個(gè)Vue經(jīng)典面試題(附源碼級(jí)詳解),帶你梳理基礎(chǔ)知識(shí),增強(qiáng)Vue知識(shí)儲(chǔ)備,值得收藏,快來(lái)看看吧!

      01-Vue 3.0的設(shè)計(jì)目標(biāo)是什么?做了哪些優(yōu)化?

      分析

      還是問(wèn)新特性,陳述典型新特性,分析其給你帶來(lái)的變化即可。(學(xué)習(xí)視頻分享:vue視頻教程)

      思路

      從以下幾方面分門(mén)別類闡述:易用性、性能、擴(kuò)展性、可維護(hù)性、開(kāi)發(fā)體驗(yàn)等

      范例

      • Vue3的最大設(shè)計(jì)目標(biāo)是替代Vue2(皮一下),為了實(shí)現(xiàn)這一點(diǎn),Vue3在以下幾個(gè)方面做了很大改進(jìn),如:易用性、框架性能、擴(kuò)展性、可維護(hù)性、開(kāi)發(fā)體驗(yàn)等

      • 易用性方面主要是API簡(jiǎn)化,比如v-model在Vue3中變成了Vue2中v-modelsync修飾符的結(jié)合體,用戶不用區(qū)分兩者不同,也不用選擇困難。類似的簡(jiǎn)化還有用于渲染函數(shù)內(nèi)部生成VNode的h(type, props, children),其中props不用考慮區(qū)分屬性、特性、事件等,框架替我們判斷,易用性大增。

      • 開(kāi)發(fā)體驗(yàn)方面,新組件Teleport傳送門(mén)、Fragments 、Suspense等都會(huì)簡(jiǎn)化特定場(chǎng)景的代碼編寫(xiě),SFC Composition API語(yǔ)法糖更是極大提升我們開(kāi)發(fā)體驗(yàn)。

      • 擴(kuò)展性方面提升如獨(dú)立的reactivity模塊,custom renderer API等

      • 可維護(hù)性方面主要是Composition API,更容易編寫(xiě)高復(fù)用性的業(yè)務(wù)邏輯。還有對(duì)TypeScript支持的提升。

      • 性能方面的改進(jìn)也很顯著,例如編譯期優(yōu)化、基于Proxy的響應(yīng)式系統(tǒng)

      • 。。。

      可能的追問(wèn)

      1. Vue3做了哪些編譯優(yōu)化?
      2. ProxydefineProperty有什么不同?

      02-你了解哪些Vue性能優(yōu)化方法?

      分析

      這是一道綜合實(shí)踐題目,寫(xiě)過(guò)一定數(shù)量的代碼之后小伙伴們自然會(huì)開(kāi)始關(guān)注一些優(yōu)化方法,答得越多肯定實(shí)踐經(jīng)驗(yàn)也越豐富,是很好的題目。

      答題思路:

      根據(jù)題目描述,這里主要探討Vue代碼層面的優(yōu)化

      回答范例

      • 我這里主要從Vue代碼編寫(xiě)層面說(shuō)一些優(yōu)化手段,例如:代碼分割、服務(wù)端渲染、組件緩存、長(zhǎng)列表優(yōu)化等

      • 最常見(jiàn)的路由懶加載:有效拆分App尺寸,訪問(wèn)時(shí)才異步加載

        const router = createRouter({   routes: [     // 借助webpack的import()實(shí)現(xiàn)異步組件     { path: '/foo', component: () => import('./Foo.vue') }   ] })
      • keep-alive緩存頁(yè)面:避免重復(fù)創(chuàng)建組件實(shí)例,且能保留緩存組件狀態(tài)

        <router-view v-slot="{ Component }">     <keep-alive>     <component :is="Component"></component>   </keep-alive> </router-view>
      • 使用v-show復(fù)用DOM:避免重復(fù)創(chuàng)建組件

        <template>   <div class="cell">     <!-- 這種情況用v-show復(fù)用DOM,比v-if效果好 -->     <div v-show="value" class="on">       <Heavy :n="10000"/>     </div>     <section v-show="!value" class="off">       <Heavy :n="10000"/>     </section>   </div> </template>
      • v-for 遍歷避免同時(shí)使用 v-if:實(shí)際上在Vue3中已經(jīng)是個(gè)錯(cuò)誤寫(xiě)法

        <template>     <ul>       <li         v-for="user in activeUsers"         <!-- 避免同時(shí)使用,vue3中會(huì)報(bào)錯(cuò) -->         <!-- v-if="user.isActive" -->         :key="user.id">         {{ user.name }}       </li>     </ul> </template> <script>   export default {     computed: {       activeUsers: function () {         return this.users.filter(user => user.isActive)       }     }   } </script>
      • v-once和v-memo:不再變化的數(shù)據(jù)使用v-once

        <!-- single element --> <span v-once>This will never change: {{msg}}</span> <!-- the element have children --> <div v-once>   <h1>comment</h1>   <p>{{msg}}</p> </div> <!-- component --> <my-component v-once :comment="msg"></my-component> <!-- `v-for` directive --> <ul>   <li v-for="i in list" v-once>{{i}}</li> </ul>

        按條件跳過(guò)更新時(shí)使用v-momo:下面這個(gè)列表只會(huì)更新選中狀態(tài)變化項(xiàng)

        <div v-for="item in list" :key="item.id" v-memo="[item.id === selected]">   <p>ID: {{ item.id }} - selected: {{ item.id === selected }}</p>   <p>...more child nodes</p> </div>

        https://vuejs.org/api/built-in-directives.html#v-memo

      • 長(zhǎng)列表性能優(yōu)化:如果是大數(shù)據(jù)長(zhǎng)列表,可采用虛擬滾動(dòng),只渲染少部分區(qū)域的內(nèi)容

        <recycle-scroller   class="items"   :items="items"   :item-size="24" >   <template v-slot="{ item }">     <FetchItemView       :item="item"       @vote="voteItem(item)"     />   </template> </recycle-scroller>

        一些開(kāi)源庫(kù):

        • vue-virtual-scroller:https://github.com/Akryum/vue-virtual-scroller
        • vue-virtual-scroll-grid:https://github.com/rocwang/vue-virtual-scroll-grid
      • 事件的銷毀:Vue 組件銷毀時(shí),會(huì)自動(dòng)解綁它的全部指令及事件監(jiān)聽(tīng)器,但是僅限于組件本身的事件。

        export default {   created() {     this.timer = setInterval(this.refresh, 2000)   },   beforeUnmount() {     clearInterval(this.timer)   } }
      • 圖片懶加載

        對(duì)于圖片過(guò)多的頁(yè)面,為了加速頁(yè)面加載速度,所以很多時(shí)候我們需要將頁(yè)面內(nèi)未出現(xiàn)在可視區(qū)域內(nèi)的圖片先不做加載, 等到滾動(dòng)到可視區(qū)域后再去加載。

        <img v-lazy="/static/img/1.png">

        參考項(xiàng)目:https://github.com/hilongjw/vue-lazyload

      • 第三方插件按需引入

        element-plus這樣的第三方組件庫(kù)可以按需引入避免體積太大。

        import { createApp } from 'vue'; import { Button, Select } from 'element-plus';  const app = createApp() app.use(Button) app.use(Select)
      • 子組件分割策略:較重的狀態(tài)組件適合拆分

        <template>   <div>     <ChildComp/>   </div> </template>  <script> export default {   components: {     ChildComp: {       methods: {         heavy () { /* 耗時(shí)任務(wù) */ }       },       render (h) {         return h('div', this.heavy())       }     }   } } </script>

        但同時(shí)也不宜過(guò)度拆分組件,尤其是為了所謂組件抽象將一些不需要渲染的組件特意抽出來(lái),組件實(shí)例消耗遠(yuǎn)大于純dom節(jié)點(diǎn)。參考:https://vuejs.org/guide/best-practices/performance.html#avoid-unnecessary-component-abstractions

      • 服務(wù)端渲染/靜態(tài)網(wǎng)站生成:SSR/SSG

        如果SPA應(yīng)用有首屏渲染慢的問(wèn)題,可以考慮SSR、SSG方案優(yōu)化。參考:https://vuejs.org/guide/scaling-up/ssr.html


      03-Vue組件為什么只能有一個(gè)根元素?

      這題現(xiàn)在有些落伍,vue3已經(jīng)不用一個(gè)根了。因此這題目很有說(shuō)頭!

      體驗(yàn)一下

      vue2直接報(bào)錯(cuò),test-v2.html

      new Vue({   components: {     comp: {       template: `         <div>root1</div>         <div>root2</div>       `     }   } }).$mount('#app')

      29個(gè)Vue經(jīng)典面試題(附源碼級(jí)詳解)

      vue3中沒(méi)有問(wèn)題,test-v3.html

      Vue.createApp({   components: {     comp: {       template: `         <div>root1</div>         <div>root2</div>       `     }   } }).mount('#app')

      29個(gè)Vue經(jīng)典面試題(附源碼級(jí)詳解)

      回答思路

      • 給一條自己的結(jié)論
      • 解釋為什么會(huì)這樣
      • vue3解決方法原理

      范例

      • vue2中組件確實(shí)只能有一個(gè)根,但vue3中組件已經(jīng)可以多根節(jié)點(diǎn)了。
      • 之所以需要這樣是因?yàn)?code>vdom是一顆單根樹(shù)形結(jié)構(gòu),patch方法在遍歷的時(shí)候從根節(jié)點(diǎn)開(kāi)始遍歷,它要求只有一個(gè)根節(jié)點(diǎn)。組件也會(huì)轉(zhuǎn)換為一個(gè)vdom,自然應(yīng)該滿足這個(gè)要求。
      • vue3中之所以可以寫(xiě)多個(gè)根節(jié)點(diǎn),是因?yàn)橐肓?code>Fragment的概念,這是一個(gè)抽象的節(jié)點(diǎn),如果發(fā)現(xiàn)組件是多根的,就創(chuàng)建一個(gè)Fragment節(jié)點(diǎn),把多個(gè)根節(jié)點(diǎn)作為它的children。將來(lái)patch的時(shí)候,如果發(fā)現(xiàn)是一個(gè)Fragment節(jié)點(diǎn),則直接遍歷children創(chuàng)建或更新。

      知其所以然

      • patch方法接收單根vdom:

        https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/renderer.ts#L354-L355

        // 直接獲取type等,沒(méi)有考慮數(shù)組的可能性 const { type, ref, shapeFlag } = n2
      • patch方法對(duì)Fragment的處理:

        https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/renderer.ts#L1091-L1092

        // a fragment can only have array children // since they are either generated by the compiler, or implicitly created // from arrays. mountChildren(n2.children as VNodeArrayChildren, container, ...)

      04-這是基本應(yīng)用能力考察,稍微上點(diǎn)規(guī)模的項(xiàng)目都要拆分vuex模塊便于維護(hù)。

      體驗(yàn)

      https://vuex.vuejs.org/zh/guide/modules.html

      const moduleA = {   state: () => ({ ... }),   mutations: { ... },   actions: { ... },   getters: { ... } } const moduleB = {   state: () => ({ ... }),   mutations: { ... },   actions: { ... } } const store = createStore({   modules: {     a: moduleA,     b: moduleB   } }) store.state.a // -> moduleA 的狀態(tài) store.state.b // -> moduleB 的狀態(tài) store.getters.c // -> moduleA里的getters store.commit('d') // -> 能同時(shí)觸發(fā)子模塊中同名mutation store.dispatch('e') // -> 能同時(shí)觸發(fā)子模塊中同名action

      思路

      • 概念和必要性

      • 怎么拆

      • 使用細(xì)節(jié)

      • 優(yōu)缺點(diǎn)

      范例

      • 用過(guò)module,項(xiàng)目規(guī)模變大之后,單獨(dú)一個(gè)store對(duì)象會(huì)過(guò)于龐大臃腫,通過(guò)模塊方式可以拆分開(kāi)來(lái)便于維護(hù)

      • 可以按之前規(guī)則單獨(dú)編寫(xiě)子模塊代碼,然后在主文件中通過(guò)modules選項(xiàng)組織起來(lái):createStore({modules:{...}})

      • 不過(guò)使用時(shí)要注意訪問(wèn)子模塊狀態(tài)時(shí)需要加上注冊(cè)時(shí)模塊名:store.state.a.xxx,但同時(shí)getters、mutationsactions又在全局空間中,使用方式和之前一樣。如果要做到完全拆分,需要在子模塊加上namespace選項(xiàng),此時(shí)再訪問(wèn)它們就要加上命名空間前綴。

      • 很顯然,模塊的方式可以拆分代碼,但是缺點(diǎn)也很明顯,就是使用起來(lái)比較繁瑣復(fù)雜,容易出錯(cuò)。而且類型系統(tǒng)支持很差,不能給我們帶來(lái)幫助。pinia顯然在這方面有了很大改進(jìn),是時(shí)候切換過(guò)去了。

      可能的追問(wèn)

      • 用過(guò)pinia嗎?都做了哪些改善?


      05-怎么實(shí)現(xiàn)路由懶加載呢?

      分析

      這是一道應(yīng)用題。當(dāng)打包應(yīng)用時(shí),JavaScript 包會(huì)變得非常大,影響頁(yè)面加載。如果我們能把不同路由對(duì)應(yīng)的組件分割成不同的代碼塊,然后當(dāng)路由被訪問(wèn)時(shí)才加載對(duì)應(yīng)組件,這樣就會(huì)更加高效。

      // 將 // import UserDetails from './views/UserDetails' // 替換為 const UserDetails = () => import('./views/UserDetails')  const router = createRouter({   // ...   routes: [{ path: '/users/:id', component: UserDetails }], })

      參考:https://router.vuejs.org/zh/guide/advanced/lazy-loading.html

      思路

      • 必要性

      • 何時(shí)用

      • 怎么用

      • 使用細(xì)節(jié)

      回答范例

      • 當(dāng)打包構(gòu)建應(yīng)用時(shí),JavaScript 包會(huì)變得非常大,影響頁(yè)面加載。利用路由懶加載我們能把不同路由對(duì)應(yīng)的組件分割成不同的代碼塊,然后當(dāng)路由被訪問(wèn)的時(shí)候才加載對(duì)應(yīng)組件,這樣會(huì)更加高效,是一種優(yōu)化手段。

      • 一般來(lái)說(shuō),對(duì)所有的路由都使用動(dòng)態(tài)導(dǎo)入是個(gè)好主意。

      • component選項(xiàng)配置一個(gè)返回 Promise 組件的函數(shù)就可以定義懶加載路由。例如:

        { path: '/users/:id', component: () => import('./views/UserDetails') }

      • 結(jié)合注釋() => import(/* webpackChunkName: "group-user" */ './UserDetails.vue')可以做webpack代碼分塊

        vite中結(jié)合rollupOptions定義分塊

      • 路由中不能使用異步組件

      知其所以然

      component (和 components) 配置如果接收一個(gè)返回 Promise 組件的函數(shù),Vue Router 只會(huì)在第一次進(jìn)入頁(yè)面時(shí)才會(huì)獲取這個(gè)函數(shù),然后使用緩存數(shù)據(jù)。

      https://github1s.com/vuejs/router/blob/HEAD/src/navigationGuards.ts#L292-L293


      06-ref和reactive異同

      這是Vue3數(shù)據(jù)響應(yīng)式中非常重要的兩個(gè)概念,自然的,跟我們寫(xiě)代碼關(guān)系也很大。

      體驗(yàn)

      ref:https://vuejs.org/api/reactivity-core.html#ref

      const count = ref(0) console.log(count.value) // 0  count.value++ console.log(count.value) // 1

      reactive:https://vuejs.org/api/reactivity-core.html#reactive

      const obj = reactive({ count: 0 }) obj.count++

      回答思路

      • 兩者概念

      • 兩者使用場(chǎng)景

      • 兩者異同

      • 使用細(xì)節(jié)

      • 原理

      回答范例

      • ref接收內(nèi)部值(inner value)返回響應(yīng)式Ref對(duì)象,reactive返回響應(yīng)式代理對(duì)象

      • 從定義上看ref通常用于處理單值的響應(yīng)式,reactive用于處理對(duì)象類型的數(shù)據(jù)響應(yīng)式

      • 兩者均是用于構(gòu)造響應(yīng)式數(shù)據(jù),但是ref主要解決原始值的響應(yīng)式問(wèn)題

      • ref返回的響應(yīng)式數(shù)據(jù)在JS中使用需要加上.value才能訪問(wèn)其值,在視圖中使用會(huì)自動(dòng)脫ref,不需要.value;ref可以接收對(duì)象或數(shù)組等非原始值,但內(nèi)部依然是reactive實(shí)現(xiàn)響應(yīng)式;reactive內(nèi)部如果接收Ref對(duì)象會(huì)自動(dòng)脫ref;使用展開(kāi)運(yùn)算符(…)展開(kāi)reactive返回的響應(yīng)式對(duì)象會(huì)使其失去響應(yīng)性,可以結(jié)合toRefs()將值轉(zhuǎn)換為Ref對(duì)象之后再展開(kāi)。

      • reactive內(nèi)部使用Proxy代理傳入對(duì)象并攔截該對(duì)象各種操作(trap),從而實(shí)現(xiàn)響應(yīng)式。ref內(nèi)部封裝一個(gè)RefImpl類,并設(shè)置get value/set value,攔截用戶對(duì)值的訪問(wèn),從而實(shí)現(xiàn)響應(yīng)式。

      知其所以然

      • reactive實(shí)現(xiàn)響應(yīng)式:

        https://github1s.com/vuejs/core/blob/HEAD/packages/reactivity/src/reactive.ts#L90-L91

      • ref實(shí)現(xiàn)響應(yīng)式:

        https://github1s.com/vuejs/core/blob/HEAD/packages/reactivity/src/ref.ts#L73-L74


      07-watch和watchEffect異同

      我們經(jīng)常性需要偵測(cè)響應(yīng)式數(shù)據(jù)的變化,vue3中除了watch之外又出現(xiàn)了watchEffect,不少同學(xué)會(huì)混淆這兩個(gè)api。

      體驗(yàn)

      watchEffect立即運(yùn)行一個(gè)函數(shù),然后被動(dòng)地追蹤它的依賴,當(dāng)這些依賴改變時(shí)重新執(zhí)行該函數(shù)。

      Runs a function immediately while reactively tracking its dependencies and re-runs it whenever the dependencies are changed.

      const count = ref(0)  watchEffect(() => console.log(count.value)) // -> logs 0  count.value++ // -> logs 1

      watch偵測(cè)一個(gè)或多個(gè)響應(yīng)式數(shù)據(jù)源并在數(shù)據(jù)源變化時(shí)調(diào)用一個(gè)回調(diào)函數(shù)。

      Watches one or more reactive data sources and invokes a callback function when the sources change.

      const state = reactive({ count: 0 }) watch(   () => state.count,   (count, prevCount) => {     /* ... */   } )

      思路

      • 給出兩者定義

      • 給出場(chǎng)景上的不同

      • 給出使用方式和細(xì)節(jié)

      • 原理闡述

      范例

      • watchEffect立即運(yùn)行一個(gè)函數(shù),然后被動(dòng)地追蹤它的依賴,當(dāng)這些依賴改變時(shí)重新執(zhí)行該函數(shù)。watch偵測(cè)一個(gè)或多個(gè)響應(yīng)式數(shù)據(jù)源并在數(shù)據(jù)源變化時(shí)調(diào)用一個(gè)回調(diào)函數(shù)。

      • watchEffect(effect)是一種特殊watch,傳入的函數(shù)既是依賴收集的數(shù)據(jù)源,也是回調(diào)函數(shù)。如果我們不關(guān)心響應(yīng)式數(shù)據(jù)變化前后的值,只是想拿這些數(shù)據(jù)做些事情,那么watchEffect就是我們需要的。watch更底層,可以接收多種數(shù)據(jù)源,包括用于依賴收集的getter函數(shù),因此它完全可以實(shí)現(xiàn)watchEffect的功能,同時(shí)由于可以指定getter函數(shù),依賴可以控制的更精確,還能獲取數(shù)據(jù)變化前后的值,因此如果需要這些時(shí)我們會(huì)使用watch。

      • watchEffect在使用時(shí),傳入的函數(shù)會(huì)立刻執(zhí)行一次。watch默認(rèn)情況下并不會(huì)執(zhí)行回調(diào)函數(shù),除非我們手動(dòng)設(shè)置immediate選項(xiàng)。

      • 從實(shí)現(xiàn)上來(lái)說(shuō),watchEffect(fn)相當(dāng)于watch(fn,fn,{immediate:true})

      知其所以然

      watchEffect定義:https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/apiWatch.ts#L80-L81

      export function watchEffect(   effect: WatchEffect,   options?: WatchOptionsBase ): WatchStopHandle {   return doWatch(effect, null, options) }

      watch定義如下:https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/apiWatch.ts#L158-L159

      export function watch<T = any, Immediate extends Readonly<boolean> = false>(   source: T | WatchSource<T>,   cb: any,   options?: WatchOptions<Immediate> ): WatchStopHandle {   return doWatch(source as any, cb, options) }

      很明顯watchEffect就是一種特殊的watch實(shí)現(xiàn)。


      08-SPA、SSR的區(qū)別是什么

      我們現(xiàn)在編寫(xiě)的Vue、React和Angular應(yīng)用大多數(shù)情況下都會(huì)在一個(gè)頁(yè)面中,點(diǎn)擊鏈接跳轉(zhuǎn)頁(yè)面通常是內(nèi)容切換而非頁(yè)面跳轉(zhuǎn),由于良好的用戶體驗(yàn)逐漸成為主流的開(kāi)發(fā)模式。但同時(shí)也會(huì)有首屏加載時(shí)間長(zhǎng),SEO不友好的問(wèn)題,因此有了SSR,這也是為什么面試中會(huì)問(wèn)到兩者的區(qū)別。

      思路分析

      • 兩者概念

      • 兩者優(yōu)缺點(diǎn)分析

      • 使用場(chǎng)景差異

      • 其他選擇

      回答范例

      • SPA(Single Page Application)即單頁(yè)面應(yīng)用。一般也稱為 客戶端渲染(Client Side Render), 簡(jiǎn)稱 CSR。SSR(Server Side Render)即 服務(wù)端渲染。一般也稱為 多頁(yè)面應(yīng)用(Mulpile Page Application),簡(jiǎn)稱 MPA。

      • SPA應(yīng)用只會(huì)首次請(qǐng)求html文件,后續(xù)只需要請(qǐng)求JSON數(shù)據(jù)即可,因此用戶體驗(yàn)更好,節(jié)約流量,服務(wù)端壓力也較小。但是首屏加載的時(shí)間會(huì)變長(zhǎng),而且SEO不友好。為了解決以上缺點(diǎn),就有了SSR方案,由于HTML內(nèi)容在服務(wù)器一次性生成出來(lái),首屏加載快,搜索引擎也可以很方便的抓取頁(yè)面信息。但同時(shí)SSR方案也會(huì)有性能,開(kāi)發(fā)受限等問(wèn)題。

      • 在選擇上,如果我們的應(yīng)用存在首屏加載優(yōu)化需求,SEO需求時(shí),就可以考慮SSR。

      • 但并不是只有這一種替代方案,比如對(duì)一些不常變化的靜態(tài)網(wǎng)站,SSR反而浪費(fèi)資源,我們可以考慮預(yù)渲染(prerender)方案。另外nuxt.js/next.js中給我們提供了SSG(Static Site Generate)靜態(tài)網(wǎng)站生成方案也是很好的靜態(tài)站點(diǎn)解決方案,結(jié)合一些CI手段,可以起到很好的優(yōu)化效果,且能節(jié)約服務(wù)器資源。

      知其所以然

      內(nèi)容生成上的區(qū)別:

      SSR

      29個(gè)Vue經(jīng)典面試題(附源碼級(jí)詳解)

      SPA

      29個(gè)Vue經(jīng)典面試題(附源碼級(jí)詳解)

      部署上的區(qū)別

      29個(gè)Vue經(jīng)典面試題(附源碼級(jí)詳解)


      09-vue-loader是什么?它有什么作用?

      分析

      這是一道工具類的原理題目,相當(dāng)有深度,具有不錯(cuò)的人才區(qū)分度。

      體驗(yàn)

      使用官方提供的SFC playground可以很好的體驗(yàn)vue-loader。

      sfc.vuejs.org

      有了vue-loader加持,我們才可以以SFC的方式快速編寫(xiě)代碼。

      <template>   <div class="example">{{ msg }}</div> </template>  <script> export default {   data() {     return {       msg: 'Hello world!',     }   }, } </script>  <style> .example {   color: red; } </style>

      思路

      • vue-loader是什么東東
      • vue-loader是做什么用的
      • vue-loader何時(shí)生效
      • vue-loader如何工作

      回答范例

      • vue-loader是用于處理單文件組件(SFC,Single-File Component)的webpack loader

      • 因?yàn)橛辛?code>vue-loader,我們就可以在項(xiàng)目中編寫(xiě)SFC格式的Vue組件,我們可以把代碼分割為<template>、<script>和<style>,代碼會(huì)異常清晰。結(jié)合其他loader我們還可以用Pug編寫(xiě)<template>,用SASS編寫(xiě)<style>,用TS編寫(xiě)<script>。我們的<style>還可以單獨(dú)作用當(dāng)前組件。

      • webpack打包時(shí),會(huì)以loader的方式調(diào)用vue-loader

      • vue-loader被執(zhí)行時(shí),它會(huì)對(duì)SFC中的每個(gè)語(yǔ)言塊用單獨(dú)的loader鏈處理。最后將這些單獨(dú)的塊裝配成最終的組件模塊。

      知其所以然

      1、vue-loader會(huì)調(diào)用@vue/compiler-sfc模塊解析SFC源碼為一個(gè)描述符(Descriptor),然后為每個(gè)語(yǔ)言塊生成import代碼,返回的代碼類似下面:

      // source.vue被vue-loader處理之后返回的代碼  // import the <template> block import render from 'source.vue?vue&type=template' // import the <script> block import script from 'source.vue?vue&type=script' export * from 'source.vue?vue&type=script' // import <style> blocks import 'source.vue?vue&type=style&index=1'  script.render = render export default script

      2、我們想要script塊中的內(nèi)容被作為js處理(當(dāng)然如果是<script lang="ts">被作為ts處理),這樣我們想要webpack把配置中跟.js匹配的規(guī)則都應(yīng)用到形如source.vue?vue&type=script的這個(gè)請(qǐng)求上。例如我們對(duì)所有*.js配置了babel-loader,這個(gè)規(guī)則將被克隆并應(yīng)用到所在Vue SFC的

      import script from 'source.vue?vue&type=script'

      將被展開(kāi)為:

      import script from 'babel-loader!vue-loader!source.vue?vue&type=script'

      類似的,如果我們對(duì).sass文件配置了style-loader + css-loader + sass-loader,對(duì)下面的代碼:

      <style scoped lang="scss">

      vue-loader將會(huì)返回給我們下面結(jié)果:

      import 'source.vue?vue&type=style&index=1&scoped&lang=scss'

      然后webpack會(huì)展開(kāi)如下:

      import 'style-loader!css-loader!sass-loader!vue-loader!source.vue?vue&type=style&index=1&scoped&lang=scss'

      1)當(dāng)處理展開(kāi)請(qǐng)求時(shí),vue-loader將被再次調(diào)用。這次,loader將會(huì)關(guān)注那些有查詢串的請(qǐng)求,且僅針對(duì)特定塊,它會(huì)選中特定塊內(nèi)部的內(nèi)容并傳遞給后面匹配的loader。

      2)對(duì)于<script>塊,處理到這就可以了,但是<template><style>還有一些額外任務(wù)要做,比如:

      • 需要用Vue 模板編譯器編譯template,從而得到render函數(shù)
      • 需要對(duì)<style scoped>中的CSS做后處理(post-process),該操作在css-loader之后但在style-loader之前

      實(shí)現(xiàn)上這些附加的loader需要被注入到已經(jīng)展開(kāi)的loader鏈上,最終的請(qǐng)求會(huì)像下面這樣:

      // <template lang="pug"> import 'vue-loader/template-loader!pug-loader!source.vue?vue&type=template'  // <style scoped lang="scss"> import 'style-loader!vue-loader/style-post-loader!css-loader!sass-loader!vue-loader!source.vue?vue&type=style&index=1&scoped&lang=scss'

      10-你寫(xiě)過(guò)自定義指令嗎?使用場(chǎng)景有哪些?

      分析

      這是一道API題,我們可能寫(xiě)的自定義指令少,但是我們用的多呀,多舉幾個(gè)例子就行。

      體驗(yàn)

      定義一個(gè)包含類似組件生命周期鉤子的對(duì)象,鉤子函數(shù)會(huì)接收指令掛鉤的dom元素:

      const focus = {   mounted: (el) => el.focus() }  export default {   directives: {     // enables v-focus in template     focus   } } <input v-focus />
      <input v-focus />

      思路

      • 定義

      • 何時(shí)用

      • 如何用

      • 常用指令

      • vue3變化

      回答范例

      • Vue有一組默認(rèn)指令,比如v-model或v-for,同時(shí)Vue也允許用戶注冊(cè)自定義指令來(lái)擴(kuò)展Vue能力

      • 自定義指令主要完成一些可復(fù)用低層級(jí)DOM操作

      • 使用自定義指令分為定義、注冊(cè)和使用三步:

        • 定義自定義指令有兩種方式:對(duì)象和函數(shù)形式,前者類似組件定義,有各種生命周期;后者只會(huì)在mounted和updated時(shí)執(zhí)行
        • 注冊(cè)自定義指令類似組件,可以使用app.directive()全局注冊(cè),使用{directives:{xxx}}局部注冊(cè)
        • 使用時(shí)在注冊(cè)名稱前加上v-即可,比如v-focus
      • 我在項(xiàng)目中常用到一些自定義指令,例如:

        • 復(fù)制粘貼 v-copy
        • 長(zhǎng)按 v-longpress
        • 防抖 v-debounce
        • 圖片懶加載 v-lazy
        • 按鈕權(quán)限 v-premission
        • 頁(yè)面水印 v-waterMarker
        • 拖拽指令 v-draggable
      • vue3中指令定義發(fā)生了比較大的變化,主要是鉤子的名稱保持和組件一致,這樣開(kāi)發(fā)人員容易記憶,不易犯錯(cuò)。另外在v3.2之后,可以在setup中以一個(gè)小寫(xiě)v開(kāi)頭方便的定義自定義指令,更簡(jiǎn)單了!

      知其所以然

      編譯后的自定義指令會(huì)被withDirective函數(shù)裝飾,進(jìn)一步處理生成的vnode,添加到特定屬性中。

      https://sfc.vuejs.org/#eyJBcHAudnVlIjoiPHNjcmlwdCBzZXR1cD5cbmltcG9ydCB7IHJlZiB9IGZyb20gJ3Z1ZSdcblxuY29uc3QgbXNnID0gcmVmKCdIZWxsbyBXb3JsZCEnKVxuXG5jb25zdCB2Rm9jdXMgPSB7XG4gIG1vdW50ZWQoZWwpIHtcbiAgICAvLyDojrflj5ZpbnB1dO+8jOW5tuiwg+eUqOWFtmZvY3VzKCnmlrnms5VcbiAgICBlbC5mb2N1cygpXG4gIH1cbn1cbjwvc2NyaXB0PlxuXG48dGVtcGxhdGU+XG4gIDxoMT57eyBtc2cgfX08L2gxPlxuICA8aW5wdXQgdi1tb2RlbD1cIm1zZ1wiIHYtZm9jdXM+XG48L3RlbXBsYXRlPiIsImltcG9ydC1tYXAuanNvbiI6IntcbiAgXCJpbXBvcnRzXCI6IHtcbiAgICBcInZ1ZVwiOiBcImh0dHBzOi8vc2ZjLnZ1ZWpzLm9yZy92dWUucnVudGltZS5lc20tYnJvd3Nlci5qc1wiXG4gIH1cbn0ifQ==

      11-說(shuō)下$attrs和$listeners的使用場(chǎng)景

      分析

      API考察,但$attrs和$listeners是比較少用的邊界知識(shí),而且vue3有變化,$listeners已經(jīng)移除,還是有細(xì)節(jié)可說(shuō)的。

      思路

      • 這兩個(gè)api的作用

      • 使用場(chǎng)景分析

      • 使用方式和細(xì)節(jié)

      • vue3變化

      體驗(yàn)

      一個(gè)包含組件透?jìng)鲗傩缘膶?duì)象。

      An object that contains the component's fallthrough attributes.

      <template>     <child-component v-bind="$attrs">         將非屬性特性透?jìng)鹘o內(nèi)部的子組件     </child-component> </template>

      范例

      • 我們可能會(huì)有一些屬性和事件沒(méi)有在props中定義,這類稱為非屬性特性,結(jié)合v-bind指令可以直接透?jìng)鹘o內(nèi)部的子組件。

      • 這類“屬性透?jìng)鳌背3S糜诎b高階組件時(shí)往內(nèi)部傳遞屬性,常用于爺孫組件之間傳參。比如我在擴(kuò)展A組件時(shí)創(chuàng)建了組件B組件,然后在C組件中使用B,此時(shí)傳遞給C的屬性中只有props里面聲明的屬性是給B使用的,其他的都是A需要的,此時(shí)就可以利用v-bind="$attrs"透?jìng)飨氯ァ?/p>

      • 最常見(jiàn)用法是結(jié)合v-bind做展開(kāi);$attrs本身不是響應(yīng)式的,除非訪問(wèn)的屬性本身是響應(yīng)式對(duì)象。

      • vue2中使用

        listeners獲取事件,vue3中已移除,均合并到listeners獲取事件,vue3中已移除,均合并到

        attrs中,使用起來(lái)更簡(jiǎn)單了。

      原理

      查看透?jìng)鲗傩詅oo和普通屬性bar,發(fā)現(xiàn)vnode結(jié)構(gòu)完全相同,這說(shuō)明vue3中將分辨兩者工作由框架完成而非用戶指定:

      <template>   <h1>{{ msg }}</h1>   <comp foo="foo" bar="bar" /> </template>
      <template>   <div>     {{$attrs.foo}} {{bar}}   </div> </template> <script setup> defineProps({   bar: String }) </script>
      _createVNode(Comp, {     foo: "foo",     bar: "bar" })
      https://sfc.vuejs.org/#eyJBcHAudnVlIjoiPHNjcmlwdCBzZXR1cD5cbmltcG9ydCB7IHJlZiB9IGZyb20gJ3Z1ZSdcbmltcG9ydCBDb21wIGZyb20gJy4vQ29tcC52dWUnXG5jb25zdCBtc2cgPSByZWYoJ0hlbGxvIFdvcmxkIScpXG48L3NjcmlwdD5cblxuPHRlbXBsYXRlPlxuICA8aDE+e3sgbXNnIH19PC9oMT5cbiAgPGNvbXAgZm9vPVwiZm9vXCIgYmFyPVwiYmFyXCIgLz5cbjwvdGVtcGxhdGU+IiwiaW1wb3J0LW1hcC5qc29uIjoie1xuICBcImltcG9ydHNcIjoge1xuICAgIFwidnVlXCI6IFwiaHR0cHM6Ly9zZmMudnVlanMub3JnL3Z1ZS5ydW50aW1lLmVzbS1icm93c2VyLmpzXCJcbiAgfVxufSIsIkNvbXAudnVlIjoiPHRlbXBsYXRlPlxuXHQ8ZGl2PlxuICAgIHt7JGF0dHJzLmZvb319IHt7YmFyfX1cbiAgPC9kaXY+XG48L3RlbXBsYXRlPlxuPHNjcmlwdCBzZXR1cD5cbmRlZmluZVByb3BzKHtcbiAgYmFyOiBTdHJpbmdcbn0pXG48L3NjcmlwdD4ifQ==

      12-v-once的使用場(chǎng)景有哪些?

      分析

      v-once是Vue中內(nèi)置指令,很有用的API,在優(yōu)化方面經(jīng)常會(huì)用到,不過(guò)小伙伴們平時(shí)可能容易忽略它。

      體驗(yàn)

      僅渲染元素和組件一次,并且跳過(guò)未來(lái)更新

      Render the element and component once only, and skip future updates.

      <!-- single element --> <span v-once>This will never change: {{msg}}</span> <!-- the element have children --> <div v-once>   <h1>comment</h1>   <p>{{msg}}</p> </div> <!-- component --> <my-component v-once :comment="msg"></my-component> <!-- `v-for` directive --> <ul>   <li v-for="i in list" v-once>{{i}}</li> </ul>

      思路

      • v-once是什么

      • 什么時(shí)候使用

      • 如何使用

      • 擴(kuò)展v-memo

      • 探索原理

      回答范例

      • v-once是vue的內(nèi)置指令,作用是僅渲染指定組件或元素一次,并跳過(guò)未來(lái)對(duì)其更新。

      • 如果我們有一些元素或者組件在初始化渲染之后不再需要變化,這種情況下適合使用v-once,這樣哪怕這些數(shù)據(jù)變化,vue也會(huì)跳過(guò)更新,是一種代碼優(yōu)化手段。

      • 我們只需要作用的組件或元素上加上v-once即可。

      • vue3.2之后,又增加了v-memo指令,可以有條件緩存部分模板并控制它們的更新,可以說(shuō)控制力更強(qiáng)了。

      • 編譯器發(fā)現(xiàn)元素上面有v-once時(shí),會(huì)將首次計(jì)算結(jié)果存入緩存對(duì)象,組件再次渲染時(shí)就會(huì)從緩存獲取,從而避免再次計(jì)算。

      知其所以然

      下面例子使用了v-once:

      <script setup> import { ref } from 'vue'  const msg = ref('Hello World!') </script>  <template>   <h1 v-once>{{ msg }}</h1>   <input v-model="msg"> </template>

      我們發(fā)現(xiàn)v-once出現(xiàn)后,編譯器會(huì)緩存作用元素或組件,從而避免以后更新時(shí)重新計(jì)算這一部分:

      // ... return (_ctx, _cache) => {   return (_openBlock(), _createElementBlock(_Fragment, null, [     // 從緩存獲取vnode     _cache[0] || (       _setBlockTracking(-1),       _cache[0] = _createElementVNode("h1", null, [         _createTextVNode(_toDisplayString(msg.value), 1 /* TEXT */)       ]),       _setBlockTracking(1),       _cache[0]     ), // ...

      13-什么是遞歸組件?舉個(gè)例子說(shuō)明下?

      分析

      遞歸組件我們用的比較少,但是在Tree、Menu這類組件中會(huì)被用到。

      體驗(yàn)

      組件通過(guò)組件名稱引用它自己,這種情況就是遞歸組件。

      An SFC can implicitly refer to itself via its filename.

      <template>   <li>     <div> {{ model.name }}</div>     <ul v-show="isOpen" v-if="isFolder">       <!-- 注意這里:組件遞歸渲染了它自己 -->       <TreeItem         class="item"         v-for="model in model.children"         :model="model">       </TreeItem>     </ul>   </li> <script> export default {   name: 'TreeItem',   // ... } </script>

      思路

      • 下定義
      • 使用場(chǎng)景
      • 使用細(xì)節(jié)
      • 原理闡述

      回答范例

      • 如果某個(gè)組件通過(guò)組件名稱引用它自己,這種情況就是遞歸組件。

      • 實(shí)際開(kāi)發(fā)中類似Tree、Menu這類組件,它們的節(jié)點(diǎn)往往包含子節(jié)點(diǎn),子節(jié)點(diǎn)結(jié)構(gòu)和父節(jié)點(diǎn)往往是相同的。這類組件的數(shù)據(jù)往往也是樹(shù)形結(jié)構(gòu),這種都是使用遞歸組件的典型場(chǎng)景。

      • 使用遞歸組件時(shí),由于我們并未也不能在組件內(nèi)部導(dǎo)入它自己,所以設(shè)置組件name屬性,用來(lái)查找組件定義,如果使用SFC,則可以通過(guò)SFC文件名推斷。組件內(nèi)部通常也要有遞歸結(jié)束條件,比如model.children這樣的判斷。

      • 查看生成渲染函數(shù)可知,遞歸組件查找時(shí)會(huì)傳遞一個(gè)布爾值給resolveComponent,這樣實(shí)際獲取的組件就是當(dāng)前組件本身。

      知其所以然

      遞歸組件編譯結(jié)果中,獲取組件時(shí)會(huì)傳遞一個(gè)標(biāo)識(shí)符 _resolveComponent("Comp", true)

      const _component_Comp = _resolveComponent("Comp", true)

      就是在傳遞maybeSelfReference

      export function resolveComponent(   name: string,   maybeSelfReference?: boolean ): ConcreteComponent | string {   return resolveAsset(COMPONENTS, name, true, maybeSelfReference) || name }

      resolveAsset中最終返回的是組件自身:

      if (!res && maybeSelfReference) {     // fallback to implicit self-reference     return Component }
      https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/helpers/resolveAssets.ts#L22-L23   https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/helpers/resolveAssets.ts#L110-L111   https://sfc.vuejs.org/#eyJBcHAudnVlIjoiPHNjcmlwdCBzZXR1cD5cbmltcG9ydCB7IHJlZiB9IGZyb20gJ3Z1ZSdcbmltcG9ydCBjb21wIGZyb20gJy4vQ29tcC52dWUnXG5jb25zdCBtc2cgPSByZWYoJ+mAkuW9kue7hOS7ticpXG5jb25zdCBtb2RlbCA9IHtcbiAgbGFiZWw6ICdub2RlLTEnLFxuICBjaGlsZHJlbjogW1xuICAgIHtsYWJlbDogJ25vZGUtMS0xJ30sXG4gICAge2xhYmVsOiAnbm9kZS0xLTInfVxuICBdXG59XG48L3NjcmlwdD5cblxuPHRlbXBsYXRlPlxuICA8aDE+e3sgbXNnIH19PC9oMT5cbiAgPGNvbXAgOm1vZGVsPVwibW9kZWxcIj48L2NvbXA+XG48L3RlbXBsYXRlPiIsImltcG9ydC1tYXAuanNvbiI6IntcbiAgXCJpbXBvcnRzXCI6IHtcbiAgICBcInZ1ZVwiOiBcImh0dHBzOi8vc2ZjLnZ1ZWpzLm9yZy92dWUucnVudGltZS5lc20tYnJvd3Nlci5qc1wiXG4gIH1cbn0iLCJDb21wLnZ1ZSI6Ijx0ZW1wbGF0ZT5cbiAgPGRpdj5cbiAgICB7e21vZGVsLmxhYmVsfX1cbiAgPC9kaXY+XG4gIDxDb21wIHYtZm9yPVwiaXRlbSBpbiBtb2RlbC5jaGlsZHJlblwiIDptb2RlbD1cIml0ZW1cIj48L0NvbXA+XG4gIDxjb21wMj48L2NvbXAyPlxuPC90ZW1wbGF0ZT5cbjxzY3JpcHQ+XG5cdGV4cG9ydCBkZWZhdWx0IHtcbiAgICBuYW1lOiAnQ29tcCcsXG4gICAgcHJvcHM6IHtcbiAgICAgIG1vZGVsOiBPYmplY3RcbiAgICB9LFxuICAgIGNvbXBvbmVudHM6IHtcbiAgICAgIGNvbXAyOiB7XG4gICAgICAgIHJlbmRlcigpe31cbiAgICAgIH1cbiAgICB9XG4gIH1cbjwvc2NyaXB0PiJ9

      14-異步組件是什么?使用場(chǎng)景有哪些?

      分析

      因?yàn)楫惒铰酚傻拇嬖?,我們使用異步組件的次數(shù)比較少,因此還是有必要兩者的不同。

      體驗(yàn)

      大型應(yīng)用中,我們需要分割應(yīng)用為更小的塊,并且在需要組件時(shí)再加載它們。

      In large applications, we may need to divide the app into smaller chunks and only load a component from the server when it's needed.

      import { defineAsyncComponent } from 'vue' // defineAsyncComponent定義異步組件 const AsyncComp = defineAsyncComponent(() => {   // 加載函數(shù)返回Promise   return new Promise((resolve, reject) => {     // ...可以從服務(wù)器加載組件     resolve(/* loaded component */)   }) }) // 借助打包工具實(shí)現(xiàn)ES模塊動(dòng)態(tài)導(dǎo)入 const AsyncComp = defineAsyncComponent(() =>   import('./components/MyComponent.vue') )

      思路

      • 異步組件作用

      • 何時(shí)使用異步組件

      • 使用細(xì)節(jié)

      • 和路由懶加載的不同

      范例

      • 在大型應(yīng)用中,我們需要分割應(yīng)用為更小的塊,并且在需要組件時(shí)再加載它們。

      • 我們不僅可以在路由切換時(shí)懶加載組件,還可以在頁(yè)面組件中繼續(xù)使用異步組件,從而實(shí)現(xiàn)更細(xì)的分割粒度。

      • 使用異步組件最簡(jiǎn)單的方式是直接給defineAsyncComponent指定一個(gè)loader函數(shù),結(jié)合ES模塊動(dòng)態(tài)導(dǎo)入函數(shù)import可以快速實(shí)現(xiàn)。我們甚至可以指定loadingComponent和errorComponent選項(xiàng)從而給用戶一個(gè)很好的加載反饋。另外Vue3中還可以結(jié)合Suspense組件使用異步組件。

      • 異步組件容易和路由懶加載混淆,實(shí)際上不是一個(gè)東西。異步組件不能被用于定義懶加載路由上,處理它的是vue框架,處理路由組件加載的是vue-router。但是可以在懶加載的路由組件中使用異步組件。

      知其所以然

      defineAsyncComponent定義了一個(gè)高階組件,返回一個(gè)包裝組件。包裝組件根據(jù)加載器的狀態(tài)決定渲染什么內(nèi)容。

      https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-core/src/apiAsyncComponent.ts#L43-L44


      15-你是怎么處理vue項(xiàng)目中的錯(cuò)誤的?

      分析

      這是一個(gè)綜合應(yīng)用題目,在項(xiàng)目中我們常常需要將App的異常上報(bào),此時(shí)錯(cuò)誤處理就很重要了。

      這里要區(qū)分錯(cuò)誤的類型,針對(duì)性做收集。

      然后是將收集的的錯(cuò)誤信息上報(bào)服務(wù)器。

      思路

      • 首先區(qū)分錯(cuò)誤類型

      • 根據(jù)錯(cuò)誤不同類型做相應(yīng)收集

      • 收集的錯(cuò)誤是如何上報(bào)服務(wù)器的

      回答范例

      • 應(yīng)用中的錯(cuò)誤類型分為"接口異常"和“代碼邏輯異常

      • 我們需要根據(jù)不同錯(cuò)誤類型做相應(yīng)處理:接口異常是我們請(qǐng)求后端接口過(guò)程中發(fā)生的異常,可能是請(qǐng)求失敗,也可能是請(qǐng)求獲得了服務(wù)器響應(yīng),但是返回的是錯(cuò)誤狀態(tài)。以Axios為例,這類異常我們可以通過(guò)封裝Axios,在攔截器中統(tǒng)一處理整個(gè)應(yīng)用中請(qǐng)求的錯(cuò)誤。代碼邏輯異常是我們編寫(xiě)的前端代碼中存在邏輯上的錯(cuò)誤造成的異常,vue應(yīng)用中最常見(jiàn)的方式是使用全局錯(cuò)誤處理函數(shù)app.config.errorHandler收集錯(cuò)誤。

      • 收集到錯(cuò)誤之后,需要統(tǒng)一處理這些異常:分析錯(cuò)誤,獲取需要錯(cuò)誤信息和數(shù)據(jù)。這里應(yīng)該有效區(qū)分錯(cuò)誤類型,如果是請(qǐng)求錯(cuò)誤,需要上報(bào)接口信息,參數(shù),狀態(tài)碼等;對(duì)于前端邏輯異常,獲取錯(cuò)誤名稱和詳情即可。另外還可以收集應(yīng)用名稱、環(huán)境、版本、用戶信息,所在頁(yè)面等。這些信息可以通過(guò)vuex存儲(chǔ)的全局狀態(tài)和路由信息獲取。

      實(shí)踐

      axios攔截器中處理捕獲異常:

      // 響應(yīng)攔截器 instance.interceptors.response.use(   (response) => {     return response.data;   },   (error) => {     // 存在response說(shuō)明服務(wù)器有響應(yīng)     if (error.response) {       let response = error.response;       if (response.status >= 400) {         handleError(response);       }     } else {       handleError(null);     }     return Promise.reject(error);   }, );

      vue中全局捕獲異常:

      import { createApp } from 'vue'  const app = createApp(...)  app.config.errorHandler = (err, instance, info) => {   // report error to tracking services }

      處理接口請(qǐng)求錯(cuò)誤:

      function handleError(error, type) {   if(type == 1) {     // 接口錯(cuò)誤,從config字段中獲取請(qǐng)求信息     let { url, method, params, data } = error.config     let err_data = {        url, method,        params: { query: params, body: data },        error: error.data?.message || JSON.stringify(error.data),     })   } }

      處理前端邏輯錯(cuò)誤:

      function handleError(error, type) {   if(type == 2) {     let errData = null     // 邏輯錯(cuò)誤     if(error instanceof Error) {       let { name, message } = error       errData = {         type: name,         error: message       }     } else {       errData = {         type: 'other',         error: JSON.strigify(error)       }     }   } }

      16-如果讓你從零開(kāi)始寫(xiě)一個(gè)vuex,說(shuō)說(shuō)你的思路

      思路分析

      這個(gè)題目很有難度,首先思考vuex解決的問(wèn)題:存儲(chǔ)用戶全局狀態(tài)并提供管理狀態(tài)API。

      • vuex需求分析
      • 如何實(shí)現(xiàn)這些需求

      回答范例

      • 官方說(shuō)vuex是一個(gè)狀態(tài)管理模式和庫(kù),并確保這些狀態(tài)以可預(yù)期的方式變更。可見(jiàn)要實(shí)現(xiàn)一個(gè)vuex

        • 要實(shí)現(xiàn)一個(gè)Store存儲(chǔ)全局狀態(tài)
        • 要提供修改狀態(tài)所需API:commit(type, payload), dispatch(type, payload)
      • 實(shí)現(xiàn)Store時(shí),可以定義Store類,構(gòu)造函數(shù)接收選項(xiàng)options,設(shè)置屬性state對(duì)外暴露狀態(tài),提供commit和dispatch修改屬性state。這里需要設(shè)置state為響應(yīng)式對(duì)象,同時(shí)將Store定義為一個(gè)Vue插件。

      • commit(type, payload)方法中可以獲取用戶傳入mutations并執(zhí)行它,這樣可以按用戶提供的方法修改狀態(tài)。 dispatch(type, payload)類似,但需要注意它可能是異步的,需要返回一個(gè)Promise給用戶以處理異步結(jié)果。

      實(shí)踐

      Store的實(shí)現(xiàn):

      class Store {     constructor(options) {         this.state = reactive(options.state)         this.options = options     }     commit(type, payload) {         this.options.mutations[type].call(this, this.state, payload)     } }

      知其所以然

      Vuex中Store的實(shí)現(xiàn):https://github1s.com/vuejs/vuex/blob/HEAD/src/store.js#L19-L20


      17-vuex中actions和mutations有什么區(qū)別?

      題目分析

      mutationsactionsvuex帶來(lái)的兩個(gè)獨(dú)特的概念。新手程序員容易混淆,所以面試官喜歡問(wèn)。

      我們只需記住修改狀態(tài)只能是mutations,actions只能通過(guò)提交mutation修改狀態(tài)即可。

      體驗(yàn)

      看下面例子可知,Action 類似于 mutation,不同在于:

      • Action 提交的是 mutation,而不是直接變更狀態(tài)。
      • Action 可以包含任意異步操作。
      const store = createStore({   state: {     count: 0   },   mutations: {     increment (state) {       state.count++     }   },   actions: {     increment (context) {       context.commit('increment')     }   } })

      答題思路

      • 給出兩者概念說(shuō)明區(qū)別

      • 舉例說(shuō)明應(yīng)用場(chǎng)景

      • 使用細(xì)節(jié)不同

      • 簡(jiǎn)單闡述實(shí)現(xiàn)上差異

      回答范例

      • 官方文檔說(shuō):更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation,mutation 非常類似于事件:每個(gè) mutation 都有一個(gè)字符串的類型 (type)和一個(gè) 回調(diào)函數(shù) (handler) 。Action 類似于 mutation,不同在于:Action可以包含任意異步操作,但它不能修改狀態(tài), 需要提交mutation才能變更狀態(tài)。

      • 因此,開(kāi)發(fā)時(shí),包含異步操作或者復(fù)雜業(yè)務(wù)組合時(shí)使用action;需要直接修改狀態(tài)則提交mutation。但由于dispatch和commit是兩個(gè)API,容易引起混淆,實(shí)踐中也會(huì)采用統(tǒng)一使用dispatch action的方式。

      • 調(diào)用dispatch和commit兩個(gè)API時(shí)幾乎完全一樣,但是定義兩者時(shí)卻不甚相同,mutation的回調(diào)函數(shù)接收參數(shù)是state對(duì)象。action則是與Store實(shí)例具有相同方法和屬性的上下文context對(duì)象,因此一般會(huì)解構(gòu)它為{commit, dispatch, state},從而方便編碼。另外dispatch會(huì)返回Promise實(shí)例便于處理內(nèi)部異步結(jié)果。

      • 實(shí)現(xiàn)上commit(type)方法相當(dāng)于調(diào)用options.mutations[type](state)dispatch(type)方法相當(dāng)于調(diào)用options.actions[type](store),這樣就很容易理解兩者使用上的不同了。

      知其所以然

      我們可以像下面這樣簡(jiǎn)單實(shí)現(xiàn)commitdispatch,從而辨別兩者不同:

      class Store {     constructor(options) {         this.state = reactive(options.state)         this.options = options     }     commit(type, payload) {         // 傳入上下文和參數(shù)1都是state對(duì)象         this.options.mutations[type].call(this.state, this.state, payload)     }     dispatch(type, payload) {         // 傳入上下文和參數(shù)1都是store本身         this.options.actions[type].call(this, this, payload)     } }

      18-使用vue渲染大量數(shù)據(jù)時(shí)應(yīng)該怎么優(yōu)化?說(shuō)下你的思路!

      分析

      企業(yè)級(jí)項(xiàng)目中渲染大量數(shù)據(jù)的情況比較常見(jiàn),因此這是一道非常好的綜合實(shí)踐題目。

      思路

      • 描述大數(shù)據(jù)量帶來(lái)的問(wèn)題

      • 分不同情況做不同處理

      • 總結(jié)一下

      回答

      • 在大型企業(yè)級(jí)項(xiàng)目中經(jīng)常需要渲染大量數(shù)據(jù),此時(shí)很容易出現(xiàn)卡頓的情況。比如大數(shù)據(jù)量的表格、樹(shù)。

      • 處理時(shí)要根據(jù)情況做不通處理:

        • 可以采取分頁(yè)的方式獲取,避免渲染大量數(shù)據(jù)
        • vue-virtual-scroller等虛擬滾動(dòng)方案,只渲染視口范圍內(nèi)的數(shù)據(jù)
        • 如果不需要更新,可以使用v-once方式只渲染一次
        • 通過(guò)v-memo可以緩存結(jié)果,結(jié)合v-for使用,避免數(shù)據(jù)變化時(shí)不必要的VNode創(chuàng)建
        • 可以采用懶加載方式,在用戶需要的時(shí)候再加載數(shù)據(jù),比如tree組件子樹(shù)的懶加載
      • 總之,還是要看具體需求,首先從設(shè)計(jì)上避免大數(shù)據(jù)獲取和渲染;實(shí)在需要這樣做可以采用虛表的方式優(yōu)化渲染;最后優(yōu)化更新,如果不需要更新可以v-once處理,需要更新可以v-memo進(jìn)一步優(yōu)化大數(shù)據(jù)更新性能。其他可以采用的是交互方式優(yōu)化,無(wú)線滾動(dòng)、懶加載等方案。


      19-怎么監(jiān)聽(tīng)vuex數(shù)據(jù)的變化?

      分析

      vuex數(shù)據(jù)狀態(tài)是響應(yīng)式的,所以狀態(tài)變視圖跟著變,但是有時(shí)還是需要知道數(shù)據(jù)狀態(tài)變了從而做一些事情。

      既然狀態(tài)都是響應(yīng)式的,那自然可以watch,另外vuex也提供了訂閱的API:store.subscribe()。

      思路

      • 總述知道的方法
      • 分別闡述用法
      • 選擇和場(chǎng)景

      回答范例

      • 我知道幾種方法:

        • 可以通過(guò)watch選項(xiàng)或者watch方法監(jiān)聽(tīng)狀態(tài)
        • 可以使用vuex提供的API:store.subscribe()
      • watch選項(xiàng)方式,可以以字符串形式監(jiān)聽(tīng)$store.state.xx;subscribe方式,可以調(diào)用store.subscribe(cb),回調(diào)函數(shù)接收mutation對(duì)象和state對(duì)象,這樣可以進(jìn)一步判斷mutation.type是否是期待的那個(gè),從而進(jìn)一步做后續(xù)處理。

      • watch方式簡(jiǎn)單好用,且能獲取變化前后值,首選;subscribe方法會(huì)被所有commit行為觸發(fā),因此還需要判斷mutation.type,用起來(lái)略繁瑣,一般用于vuex插件中。

      實(shí)踐

      watch方式

      const app = createApp({     watch: {       '$store.state.counter'() {         console.log('counter change!');       }     }   })

      subscribe方式:

        store.subscribe((mutation, state) => {     if (mutation.type === 'add') {       console.log('counter change in subscribe()!');     }   })

      20-router-link和router-view是如何起作用的?

      分析

      vue-router中兩個(gè)重要組件router-linkrouter-view,分別起到導(dǎo)航作用和內(nèi)容渲染作用,但是回答如何生效還真有一定難度哪!

      思路

      • 兩者作用
      • 闡述使用方式
      • 原理說(shuō)明

      回答范例

      • vue-router中兩個(gè)重要組件router-linkrouter-view,分別起到路由導(dǎo)航作用和組件內(nèi)容渲染作用
      • 使用中router-link默認(rèn)生成一個(gè)a標(biāo)簽,設(shè)置to屬性定義跳轉(zhuǎn)path。實(shí)際上也可以通過(guò)custom和插槽自定義最終的展現(xiàn)形式。router-view是要顯示組件的占位組件,可以嵌套,對(duì)應(yīng)路由配置的嵌套關(guān)系,配合name可以顯示具名組件,起到更強(qiáng)的布局作用。
      • router-link組件內(nèi)部根據(jù)custom屬性判斷如何渲染最終生成節(jié)點(diǎn),內(nèi)部提供導(dǎo)航方法navigate,用戶點(diǎn)擊之后實(shí)際調(diào)用的是該方法,此方法最終會(huì)修改響應(yīng)式的路由變量,然后重新去routes匹配出數(shù)組結(jié)果,router-view則根據(jù)其所處深度deep在匹配數(shù)組結(jié)果中找到對(duì)應(yīng)的路由并獲取組件,最終將其渲染出來(lái)。

      知其所以然

      • RouterLink定義

      https://github1s.com/vuejs/router/blob/HEAD/src/RouterLink.ts#L184-L185

      • RouterView定義

      https://github1s.com/vuejs/router/blob/HEAD/src/RouterView.ts#L43-L44


      21-Vue-router 除了 router-link 怎么實(shí)現(xiàn)跳轉(zhuǎn)

      分析

      vue-router導(dǎo)航有兩種方式:聲明式導(dǎo)航編程方式導(dǎo)航

      體驗(yàn)

      聲明式導(dǎo)航

      <router-link to="/about">Go to About</router-link>

      編程導(dǎo)航

      // literal string path router.push('/users/eduardo')  // object with path router.push({ path: '/users/eduardo' })  // named route with params to let the router build the url router.push({ name: 'user', params: { username: 'eduardo' } })

      思路

      • 兩種方式
      • 分別闡述使用方式
      • 區(qū)別和選擇
      • 原理說(shuō)明

      回答范例

      • vue-router導(dǎo)航有兩種方式:聲明式導(dǎo)航編程方式導(dǎo)航
      • 聲明式導(dǎo)航方式使用router-link組件,添加to屬性導(dǎo)航;編程方式導(dǎo)航更加靈活,可傳遞調(diào)用router.push(),并傳遞path字符串或者RouteLocationRaw對(duì)象,指定path、name、params等信息
      • 如果頁(yè)面中簡(jiǎn)單表示跳轉(zhuǎn)鏈接,使用router-link最快捷,會(huì)渲染一個(gè)a標(biāo)簽;如果頁(yè)面是個(gè)復(fù)雜的內(nèi)容,比如商品信息,可以添加點(diǎn)擊事件,使用編程式導(dǎo)航
      • 實(shí)際上內(nèi)部?jī)烧哒{(diào)用的導(dǎo)航函數(shù)是一樣的

      知其所以然

      https://github1s.com/vuejs/router/blob/HEAD/src/RouterLink.ts#L240-L241

      routerlink點(diǎn)擊跳轉(zhuǎn),調(diào)用的是navigate方法

      29個(gè)Vue經(jīng)典面試題(附源碼級(jí)詳解)

      navigate內(nèi)部依然調(diào)用的push


      22-Vue3.0 性能提升體現(xiàn)在哪些方面?

      分析

      vue3在設(shè)計(jì)時(shí)有幾個(gè)目標(biāo):更小、更快、更友好,這些多數(shù)適合性能相關(guān),因此可以圍繞介紹。

      思路

      • 總述和性能相關(guān)的新特性
      • 逐個(gè)說(shuō)細(xì)節(jié)
      • 能說(shuō)點(diǎn)原理更佳

      回答范例

      • 我分別從代碼、編譯、打包三方面介紹vue3性能方面的提升
      • 代碼層面性能優(yōu)化主要體現(xiàn)在全新響應(yīng)式API,基于Proxy實(shí)現(xiàn),初始化時(shí)間和內(nèi)存占用均大幅改進(jìn);
      • 編譯層面做了
      贊(0)
      分享到: 更多 (0)
      網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)