<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          最全的 Vue 面試題+詳解答案

          共 33512字,需瀏覽 68分鐘

           ·

          2021-05-17 15:53

          前言

          本文整理了高頻出現(xiàn)的 Vue 相關(guān)面試題并且附帶詳解答案 難度分為簡單 中等 困難 三種類型 大家可以先不看答案自測一下自己的 Vue 水平哈 

          整理不易 如果覺得本文有幫助 記得點(diǎn)贊三連哦 十分感謝!


          簡單

          1 MVC 和 MVVM 區(qū)別

          MVC

          MVC 全名是 Model View Controller,是模型(model)-視圖(view)-控制器(controller)的縮寫,一種軟件設(shè)計典范

          • Model(模型):是應(yīng)用程序中用于處理應(yīng)用程序數(shù)據(jù)邏輯的部分。通常模型對象負(fù)責(zé)在數(shù)據(jù)庫中存取數(shù)據(jù)
          • View(視圖):是應(yīng)用程序中處理數(shù)據(jù)顯示的部分。通常視圖是依據(jù)模型數(shù)據(jù)創(chuàng)建的
          • Controller(控制器):是應(yīng)用程序中處理用戶交互的部分。通常控制器負(fù)責(zé)從視圖讀取數(shù)據(jù),控制用戶輸入,并向模型發(fā)送數(shù)據(jù)
          mvc.png

          MVC 的思想:一句話描述就是 Controller 負(fù)責(zé)將 Model 的數(shù)據(jù)用 View 顯示出來,換句話說就是在 Controller 里面把 Model 的數(shù)據(jù)賦值給 View。

          MVVM

          MVVM 新增了 VM 類

          • ViewModel 層:做了兩件事達(dá)到了數(shù)據(jù)的雙向綁定 一是將【模型】轉(zhuǎn)化成【視圖】,即將后端傳遞的數(shù)據(jù)轉(zhuǎn)化成所看到的頁面。實(shí)現(xiàn)的方式是:數(shù)據(jù)綁定。二是將【視圖】轉(zhuǎn)化成【模型】,即將所看到的頁面轉(zhuǎn)化成后端的數(shù)據(jù)。實(shí)現(xiàn)的方式是:DOM 事件監(jiān)聽。
          mvvm.png

          MVVM 與 MVC 最大的區(qū)別就是:它實(shí)現(xiàn)了 View 和 Model 的自動同步,也就是當(dāng) Model 的屬性改變時,我們不用再自己手動操作 Dom 元素,來改變 View 的顯示,而是改變屬性后該屬性對應(yīng) View 層顯示會自動改變(對應(yīng)Vue數(shù)據(jù)驅(qū)動的思想)

          整體看來,MVVM 比 MVC 精簡很多,不僅簡化了業(yè)務(wù)與界面的依賴,還解決了數(shù)據(jù)頻繁更新的問題,不用再用選擇器操作 DOM 元素。因?yàn)樵?MVVM 中,View 不知道 Model 的存在,Model 和 ViewModel 也觀察不到 View,這種低耦合模式提高代碼的可重用性

          注意:Vue 并沒有完全遵循 MVVM 的思想 這一點(diǎn)官網(wǎng)自己也有說明

          vue-mvvm.png

          那么問題來了 為什么官方要說 Vue 沒有完全遵循 MVVM 思想呢?

          • 嚴(yán)格的 MVVM 要求 View 不能和 Model 直接通信,而 Vue 提供了$refs 這個屬性,讓 Model 可以直接操作 View,違反了這一規(guī)定,所以說 Vue 沒有完全遵循 MVVM。

          2 為什么 data 是一個函數(shù)

          組件中的 data 寫成一個函數(shù),數(shù)據(jù)以函數(shù)返回值形式定義,這樣每復(fù)用一次組件,就會返回一份新的 data,類似于給每個組件實(shí)例創(chuàng)建一個私有的數(shù)據(jù)空間,讓各個組件實(shí)例維護(hù)各自的數(shù)據(jù)。而單純的寫成對象形式,就使得所有組件實(shí)例共用了一份 data,就會造成一個變了全都會變的結(jié)果

          3 Vue 組件通訊有哪幾種方式

          1. props 和emit 觸發(fā)事件來做到的

          2. children 獲取當(dāng)前組件的父組件和當(dāng)前組件的子組件

          3. listeners A->B->C。Vue 2.4 開始提供了listeners 來解決這個問題

          4. 父組件中通過 provide 來提供變量,然后在子組件中通過 inject 來注入變量。(官方不推薦在實(shí)際業(yè)務(wù)中使用,但是寫組件庫時很常用)

          5. $refs 獲取組件實(shí)例

          6. envetBus 兄弟組件數(shù)據(jù)傳遞 這種情況下可以使用事件總線的方式

          7. vuex 狀態(tài)管理

          4 Vue 的生命周期方法有哪些 一般在哪一步發(fā)請求

          beforeCreate 在實(shí)例初始化之后,數(shù)據(jù)觀測(data observer) 和 event/watcher 事件配置之前被調(diào)用。在當(dāng)前階段 data、methods、computed 以及 watch 上的數(shù)據(jù)和方法都不能被訪問

          created 實(shí)例已經(jīng)創(chuàng)建完成之后被調(diào)用。在這一步,實(shí)例已完成以下的配置:數(shù)據(jù)觀測(data observer),屬性和方法的運(yùn)算, watch/event 事件回調(diào)。這里沒有nextTick 來訪問 Dom

          beforeMount 在掛載開始之前被調(diào)用:相關(guān)的 render 函數(shù)首次被調(diào)用。

          mounted 在掛載完成后發(fā)生,在當(dāng)前階段,真實(shí)的 Dom 掛載完畢,數(shù)據(jù)完成雙向綁定,可以訪問到 Dom 節(jié)點(diǎn)

          beforeUpdate 數(shù)據(jù)更新時調(diào)用,發(fā)生在虛擬 DOM 重新渲染和打補(bǔ)丁(patch)之前。可以在這個鉤子中進(jìn)一步地更改狀態(tài),這不會觸發(fā)附加的重渲染過程

          updated 發(fā)生在更新完成之后,當(dāng)前階段組件 Dom 已完成更新。要注意的是避免在此期間更改數(shù)據(jù),因?yàn)檫@可能會導(dǎo)致無限循環(huán)的更新,該鉤子在服務(wù)器端渲染期間不被調(diào)用。

          beforeDestroy 實(shí)例銷毀之前調(diào)用。在這一步,實(shí)例仍然完全可用。我們可以在這時進(jìn)行善后收尾工作,比如清除計時器。

          destroyed Vue 實(shí)例銷毀后調(diào)用。調(diào)用后,Vue 實(shí)例指示的所有東西都會解綁定,所有的事件監(jiān)聽器會被移除,所有的子實(shí)例也會被銷毀。該鉤子在服務(wù)器端渲染期間不被調(diào)用。

          activated keep-alive 專屬,組件被激活時調(diào)用

          deactivated keep-alive 專屬,組件被銷毀時調(diào)用

          異步請求在哪一步發(fā)起?

          可以在鉤子函數(shù) created、beforeMount、mounted 中進(jìn)行異步請求,因?yàn)樵谶@三個鉤子函數(shù)中,data 已經(jīng)創(chuàng)建,可以將服務(wù)端端返回的數(shù)據(jù)進(jìn)行賦值。

          如果異步請求不需要依賴 Dom 推薦在 created 鉤子函數(shù)中調(diào)用異步請求,因?yàn)樵?created 鉤子函數(shù)中調(diào)用異步請求有以下優(yōu)點(diǎn):

          • 能更快獲取到服務(wù)端數(shù)據(jù),減少頁面 loading 時間;
          • ssr 不支持 beforeMount 、mounted 鉤子函數(shù),所以放在 created 中有助于一致性;

          5 v-if 和 v-show 的區(qū)別

          v-if 在編譯過程中會被轉(zhuǎn)化成三元表達(dá)式,條件不滿足時不渲染此節(jié)點(diǎn)。

          v-show 會被編譯成指令,條件不滿足時控制樣式將對應(yīng)節(jié)點(diǎn)隱藏 (display:none)

          使用場景

          v-if 適用于在運(yùn)行時很少改變條件,不需要頻繁切換條件的場景

          v-show 適用于需要非常頻繁切換條件的場景

          擴(kuò)展補(bǔ)充:display:none、visibility:hidden 和 opacity:0 之間的區(qū)別?

          display.png

          6 說說 vue 內(nèi)置指令

          內(nèi)置指令.png

          7 怎樣理解 Vue 的單向數(shù)據(jù)流

          數(shù)據(jù)總是從父組件傳到子組件,子組件沒有權(quán)利修改父組件傳過來的數(shù)據(jù),只能請求父組件對原始數(shù)據(jù)進(jìn)行修改。這樣會防止從子組件意外改變父級組件的狀態(tài),從而導(dǎo)致你的應(yīng)用的數(shù)據(jù)流向難以理解。

          注意:在子組件直接用 v-model 綁定父組件傳過來的 prop 這樣是不規(guī)范的寫法 開發(fā)環(huán)境會報警告

          如果實(shí)在要改變父組件的 prop 值 可以再 data 里面定義一個變量 并用 prop 的值初始化它 之后用$emit 通知父組件去修改

          8 computed 和 watch 的區(qū)別和運(yùn)用的場景

          computed 是計算屬性,依賴其他屬性計算值,并且 computed 的值有緩存,只有當(dāng)計算值變化才會返回內(nèi)容,它可以設(shè)置 getter 和 setter。

          watch 監(jiān)聽到值的變化就會執(zhí)行回調(diào),在回調(diào)中可以進(jìn)行一些邏輯操作。

          計算屬性一般用在模板渲染中,某個值是依賴了其它的響應(yīng)式對象甚至是計算屬性計算而來;而偵聽屬性適用于觀測某個值的變化去完成一段復(fù)雜的業(yè)務(wù)邏輯

          計算屬性原理詳解 傳送門

          偵聽屬性原理詳解 傳送門

          9 v-if 與 v-for 為什么不建議一起使用

          v-for 和 v-if 不要在同一個標(biāo)簽中使用,因?yàn)榻馕鰰r先解析 v-for 再解析 v-if。如果遇到需要同時使用時可以考慮寫成計算屬性的方式。


          中等

          10 Vue2.0 響應(yīng)式數(shù)據(jù)的原理

          整體思路是數(shù)據(jù)劫持+觀察者模式

          對象內(nèi)部通過 defineReactive 方法,使用 Object.defineProperty 將屬性進(jìn)行劫持(只會劫持已經(jīng)存在的屬性),數(shù)組則是通過重寫數(shù)組方法來實(shí)現(xiàn)。當(dāng)頁面使用對應(yīng)屬性時,每個屬性都擁有自己的 dep 屬性,存放他所依賴的 watcher(依賴收集),當(dāng)屬性變化后會通知自己對應(yīng)的 watcher 去更新(派發(fā)更新)。

          相關(guān)代碼如下

          class Observer {
            // 觀測值
            constructor(value) {
              this.walk(value);
            }
            walk(data) {
              // 對象上的所有屬性依次進(jìn)行觀測
              let keys = Object.keys(data);
              for (let i = 0; i < keys.length; i++) {
                let key = keys[i];
                let value = data[key];
                defineReactive(data, key, value);
              }
            }
          }
          // Object.defineProperty數(shù)據(jù)劫持核心 兼容性在ie9以及以上
          function defineReactive(data, key, value) {
            observe(value); // 遞歸關(guān)鍵
            // --如果value還是一個對象會繼續(xù)走一遍odefineReactive 層層遍歷一直到value不是對象才停止
            //   思考?如果Vue數(shù)據(jù)嵌套層級過深 >>性能會受影響
            Object.defineProperty(data, key, {
              get() {
                console.log("獲取值");

                //需要做依賴收集過程 這里代碼沒寫出來
                return value;
              },
              set(newValue) {
                if (newValue === value) return;
                console.log("設(shè)置值");
                //需要做派發(fā)更新過程 這里代碼沒寫出來
                value = newValue;
              },
            });
          }
          export function observe(value) {
            // 如果傳過來的是對象或者數(shù)組 進(jìn)行屬性劫持
            if (
              Object.prototype.toString.call(value) === "[object Object]" ||
              Array.isArray(value)
            ) {
              return new Observer(value);
            }

          響應(yīng)式數(shù)據(jù)原理詳解 傳送門

          11 Vue 如何檢測數(shù)組變化

          數(shù)組考慮性能原因沒有用 defineProperty 對數(shù)組的每一項(xiàng)進(jìn)行攔截,而是選擇對 7 種數(shù)組(push,shift,pop,splice,unshift,sort,reverse)方法進(jìn)行重寫(AOP 切片思想)

          所以在 Vue 中修改數(shù)組的索引和長度是無法監(jiān)控到的。需要通過以上 7 種變異方法修改數(shù)組才會觸發(fā)數(shù)組對應(yīng)的 watcher 進(jìn)行更新

          相關(guān)代碼如下

          // src/obserber/array.js
          // 先保留數(shù)組原型
          const arrayProto = Array.prototype;
          // 然后將arrayMethods繼承自數(shù)組原型
          // 這里是面向切片編程思想(AOP)--不破壞封裝的前提下,動態(tài)的擴(kuò)展功能
          export const arrayMethods = Object.create(arrayProto);
          let methodsToPatch = [
            "push",
            "pop",
            "shift",
            "unshift",
            "splice",
            "reverse",
            "sort",
          ];
          methodsToPatch.forEach((method) => {
            arrayMethods[method] = function (...args) {
              //   這里保留原型方法的執(zhí)行結(jié)果
              const result = arrayProto[method].apply(this, args);
              // 這句話是關(guān)鍵
              // this代表的就是數(shù)據(jù)本身 比如數(shù)據(jù)是{a:[1,2,3]} 那么我們使用a.push(4)  this就是a  ob就是a.__ob__ 這個屬性就是上段代碼增加的 代表的是該數(shù)據(jù)已經(jīng)被響應(yīng)式觀察過了指向Observer實(shí)例
              const ob = this.__ob__;

              // 這里的標(biāo)志就是代表數(shù)組有新增操作
              let inserted;
              switch (method) {
                case "push":
                case "unshift":
                  inserted = args;
                  break;
                case "splice":
                  inserted = args.slice(2);
                default:
                  break;
              }
              // 如果有新增的元素 inserted是一個數(shù)組 調(diào)用Observer實(shí)例的observeArray對數(shù)組每一項(xiàng)進(jìn)行觀測
              if (inserted) ob.observeArray(inserted);
              // 之后咱們還可以在這里檢測到數(shù)組改變了之后從而觸發(fā)視圖更新的操作--后續(xù)源碼會揭曉
              return result;
            };
          }); 

          數(shù)組的觀測原理詳解 傳送門

          12 vue3.0 用過嗎 了解多少

          • 響應(yīng)式原理的改變 Vue3.x 使用 Proxy 取代 Vue2.x 版本的 Object.defineProperty

          • 組件選項(xiàng)聲明方式 Vue3.x 使用 Composition API setup 是 Vue3.x 新增的一個選項(xiàng), 他是組件內(nèi)使用 Composition API 的入口。

          • 模板語法變化 slot 具名插槽語法 自定義指令 v-model 升級

          • 其它方面的更改 Suspense 支持 Fragment(多個根節(jié)點(diǎn))和 Protal(在 dom 其他部分渲染組建內(nèi)容)組件,針對一些特殊的場景做了處理。基于 treeshaking 優(yōu)化,提供了更多的內(nèi)置功能。

          Vue3.0 新特性以及使用經(jīng)驗(yàn)總結(jié) 傳送門

          13 Vue3.0 和 2.0 的響應(yīng)式原理區(qū)別

          Vue3.x 改用 Proxy 替代 Object.defineProperty。因?yàn)?Proxy 可以直接監(jiān)聽對象和數(shù)組的變化,并且有多達(dá) 13 種攔截方法。

          相關(guān)代碼如下

          import { mutableHandlers } from "./baseHandlers"; // 代理相關(guān)邏輯
          import { isObject } from "./util"; // 工具方法

          export function reactive(target) {
            // 根據(jù)不同參數(shù)創(chuàng)建不同響應(yīng)式對象
            return createReactiveObject(target, mutableHandlers);
          }
          function createReactiveObject(target, baseHandler) {
            if (!isObject(target)) {
              return target;
            }
            const observed = new Proxy(target, baseHandler);
            return observed;
          }

          const get = createGetter();
          const set = createSetter();

          function createGetter() {
            return function get(target, key, receiver) {
              // 對獲取的值進(jìn)行放射
              const res = Reflect.get(target, key, receiver);
              console.log("屬性獲取", key);
              if (isObject(res)) {
                // 如果獲取的值是對象類型,則返回當(dāng)前對象的代理對象
                return reactive(res);
              }
              return res;
            };
          }
          function createSetter() {
            return function set(target, key, value, receiver) {
              const oldValue = target[key];
              const hadKey = hasOwn(target, key);
              const result = Reflect.set(target, key, value, receiver);
              if (!hadKey) {
                console.log("屬性新增", key, value);
              } else if (hasChanged(value, oldValue)) {
                console.log("屬性值被修改", key, value);
              }
              return result;
            };
          }
          export const mutableHandlers = {
            get, // 當(dāng)獲取屬性時調(diào)用此方法
            set, // 當(dāng)修改屬性時調(diào)用此方法
          }; 

          14 Vue 的父子組件生命周期鉤子函數(shù)執(zhí)行順序

          • 加載渲染過程

          父 beforeCreate->父 created->父 beforeMount->子 beforeCreate->子 created->子 beforeMount->子 mounted->父 mounted

          • 子組件更新過程

          父 beforeUpdate->子 beforeUpdate->子 updated->父 updated

          • 父組件更新過程

          父 beforeUpdate->父 updated

          • 銷毀過程

          父 beforeDestroy->子 beforeDestroy->子 destroyed->父 destroyed

          15 虛擬 DOM 是什么 有什么優(yōu)缺點(diǎn)

          由于在瀏覽器中操作 DOM 是很昂貴的。頻繁的操作 DOM,會產(chǎn)生一定的性能問題。這就是虛擬 Dom 的產(chǎn)生原因。Vue2 的 Virtual DOM 借鑒了開源庫 snabbdom 的實(shí)現(xiàn)。Virtual DOM 本質(zhì)就是用一個原生的 JS 對象去描述一個 DOM 節(jié)點(diǎn),是對真實(shí) DOM 的一層抽象。

          優(yōu)點(diǎn):

          1. 保證性能下限:框架的虛擬 DOM 需要適配任何上層 API 可能產(chǎn)生的操作,它的一些 DOM 操作的實(shí)現(xiàn)必須是普適的,所以它的性能并不是最優(yōu)的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虛擬 DOM 至少可以保證在你不需要手動優(yōu)化的情況下,依然可以提供還不錯的性能,即保證性能的下限;

          2. 無需手動操作 DOM:我們不再需要手動去操作 DOM,只需要寫好 View-Model 的代碼邏輯,框架會根據(jù)虛擬 DOM 和 數(shù)據(jù)雙向綁定,幫我們以可預(yù)期的方式更新視圖,極大提高我們的開發(fā)效率;

          3. 跨平臺:虛擬 DOM 本質(zhì)上是 JavaScript 對象,而 DOM 與平臺強(qiáng)相關(guān),相比之下虛擬 DOM 可以進(jìn)行更方便地跨平臺操作,例如服務(wù)器渲染、weex 開發(fā)等等。

          缺點(diǎn):

          1. 無法進(jìn)行極致優(yōu)化:雖然虛擬 DOM + 合理的優(yōu)化,足以應(yīng)對絕大部分應(yīng)用的性能需求,但在一些性能要求極高的應(yīng)用中虛擬 DOM 無法進(jìn)行針對性的極致優(yōu)化。

          2. 首次渲染大量 DOM 時,由于多了一層虛擬 DOM 的計算,會比 innerHTML 插入慢。

          16 v-model 原理

          v-model 只是語法糖而已

          v-model 在內(nèi)部為不同的輸入元素使用不同的 property 并拋出不同的事件:

          • text 和 textarea 元素使用 value property 和 input 事件;
          • checkbox 和 radio 使用 checked property 和 change 事件;
          • select 字段將 value 作為 prop 并將 change 作為事件。

          注意:對于需要使用輸入法 (如中文、日文、韓文等) 的語言,你會發(fā)現(xiàn) v-model 不會在輸入法組合文字過程中得到更新。

          在普通標(biāo)簽上

           <input v-model="sth" />  //這一行等于下一行
              <input v-bind:value="sth" v-on:input="sth = $event.target.value" /> 

          在組件上

          <currency-input v-model="price"></currentcy-input>
          <!--上行代碼是下行的語法糖
           <currency-input :value="price" @input="price = arguments[0]"></currency-input>
          -->

          <!-- 子組件定義 -->
          Vue.component('currency-input', {
           template: `
            <span>
             <input
              ref="input"
              :value="value"
              @input="$emit('input', $event.target.value)"
             >
            </span>
           `,
           props: ['value'],
          }) 

          17 v-for 為什么要加 key

          如果不使用 key,Vue 會使用一種最大限度減少動態(tài)元素并且盡可能的嘗試就地修改/復(fù)用相同類型元素的算法。key 是為 Vue 中 vnode 的唯一標(biāo)記,通過這個 key,我們的 diff 操作可以更準(zhǔn)確、更快速

          更準(zhǔn)確:因?yàn)閹?key 就不是就地復(fù)用了,在 sameNode 函數(shù) a.key === b.key 對比中可以避免就地復(fù)用的情況。所以會更加準(zhǔn)確。

          更快速:利用 key 的唯一性生成 map 對象來獲取對應(yīng)節(jié)點(diǎn),比遍歷方式更快

          相關(guān)代碼如下

          // 判斷兩個vnode的標(biāo)簽和key是否相同 如果相同 就可以認(rèn)為是同一節(jié)點(diǎn)就地復(fù)用
          function isSameVnode(oldVnode, newVnode) {
            return oldVnode.tag === newVnode.tag && oldVnode.key === newVnode.key;
          }

          // 根據(jù)key來創(chuàng)建老的兒子的index映射表  類似 {'a':0,'b':1} 代表key為'a'的節(jié)點(diǎn)在第一個位置 key為'b'的節(jié)點(diǎn)在第二個位置
          function makeIndexByKey(children) {
            let map = {};
            children.forEach((item, index) => {
              map[item.key] = index;
            });
            return map;
          }
          // 生成的映射表
          let map = makeIndexByKey(oldCh); 

          diff 算法詳解 傳送門

          18 Vue 事件綁定原理

          原生事件綁定是通過 addEventListener 綁定給真實(shí)元素的,組件事件綁定是通過 Vue 自定義的$on 實(shí)現(xiàn)的。如果要在組件上使用原生事件,需要加.native 修飾符,這樣就相當(dāng)于在父組件中把子組件當(dāng)做普通 html 標(biāo)簽,然后加上原生事件。

          emit 是基于發(fā)布訂閱模式的,維護(hù)一個事件中心,on 的時候?qū)⑹录疵Q存在事件中心里,稱之為訂閱者,然后 emit 將對應(yīng)的事件進(jìn)行發(fā)布,去執(zhí)行事件中心里的對應(yīng)的監(jiān)聽器

          手寫發(fā)布訂閱原理 傳送門

          19 vue-router 路由鉤子函數(shù)是什么 執(zhí)行順序是什么

          路由鉤子的執(zhí)行流程, 鉤子函數(shù)種類有:全局守衛(wèi)、路由守衛(wèi)、組件守衛(wèi)

          完整的導(dǎo)航解析流程:

          1. 導(dǎo)航被觸發(fā)。
          2. 在失活的組件里調(diào)用 beforeRouteLeave 守衛(wèi)。
          3. 調(diào)用全局的 beforeEach 守衛(wèi)。
          4. 在重用的組件里調(diào)用 beforeRouteUpdate 守衛(wèi) (2.2+)。
          5. 在路由配置里調(diào)用 beforeEnter。
          6. 解析異步路由組件。
          7. 在被激活的組件里調(diào)用 beforeRouteEnter。
          8. 調(diào)用全局的 beforeResolve 守衛(wèi) (2.5+)。
          9. 導(dǎo)航被確認(rèn)。
          10. 調(diào)用全局的 afterEach 鉤子。
          11. 觸發(fā) DOM 更新。
          12. 調(diào)用 beforeRouteEnter 守衛(wèi)中傳給 next 的回調(diào)函數(shù),創(chuàng)建好的組件實(shí)例會作為回調(diào)函數(shù)的參數(shù)傳入。

          20 vue-router 動態(tài)路由是什么 有什么問題

          我們經(jīng)常需要把某種模式匹配到的所有路由,全都映射到同個組件。例如,我們有一個 User 組件,對于所有 ID 各不相同的用戶,都要使用這個組件來渲染。那么,我們可以在 vue-router 的路由路徑中使用“動態(tài)路徑參數(shù)”(dynamic segment) 來達(dá)到這個效果:

          const User = {
            template: "<div>User</div>",
          };

          const router = new VueRouter({
            routes: [
              // 動態(tài)路徑參數(shù) 以冒號開頭
              { path: "/user/:id", component: User },
            ],
          }); 

          問題:vue-router 組件復(fù)用導(dǎo)致路由參數(shù)失效怎么辦?

          解決方法:

          1.通過 watch 監(jiān)聽路由參數(shù)再發(fā)請求

          watch: { //通過watch來監(jiān)聽路由變化

           "$route"function(){
           this.getData(this.$route.params.xxx);
           }

          2.用 :key 來阻止“復(fù)用”

          <router-view :key="$route.fullPath" /> 

          21 談一下對 vuex 的個人理解

          vuex 是專門為 vue 提供的全局狀態(tài)管理系統(tǒng),用于多個組件中數(shù)據(jù)共享、數(shù)據(jù)緩存等。(無法持久化、內(nèi)部核心原理是通過創(chuàng)造一個全局實(shí)例 new Vue)

          主要包括以下幾個模塊:

          • State:定義了應(yīng)用狀態(tài)的數(shù)據(jù)結(jié)構(gòu),可以在這里設(shè)置默認(rèn)的初始狀態(tài)。
          • Getter:允許組件從 Store 中獲取數(shù)據(jù),mapGetters 輔助函數(shù)僅僅是將 store 中的 getter 映射到局部計算屬性。
          • Mutation:是唯一更改 store 中狀態(tài)的方法,且必須是同步函數(shù)。
          • Action:用于提交 mutation,而不是直接變更狀態(tài),可以包含任意異步操作。
          • Module:允許將單一的 Store 拆分為多個 store 且同時保存在單一的狀態(tài)樹中。

          22 Vuex 頁面刷新數(shù)據(jù)丟失怎么解決

          需要做 vuex 數(shù)據(jù)持久化 一般使用本地存儲的方案來保存數(shù)據(jù) 可以自己設(shè)計存儲方案 也可以使用第三方插件

          推薦使用 vuex-persist 插件,它就是為 Vuex 持久化存儲而生的一個插件。不需要你手動存取 storage ,而是直接將狀態(tài)保存至 cookie 或者 localStorage 中

          23 Vuex 為什么要分模塊并且加命名空間

          模塊:由于使用單一狀態(tài)樹,應(yīng)用的所有狀態(tài)會集中到一個比較大的對象。當(dāng)應(yīng)用變得非常復(fù)雜時,store 對象就有可能變得相當(dāng)臃腫。為了解決以上問題,Vuex 允許我們將 store 分割成模塊(module)。每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊。

          命名空間:默認(rèn)情況下,模塊內(nèi)部的 action、mutation 和 getter 是注冊在全局命名空間的——這樣使得多個模塊能夠?qū)ν?mutation 或 action 作出響應(yīng)。如果希望你的模塊具有更高的封裝度和復(fù)用性,你可以通過添加 namespaced: true 的方式使其成為帶命名空間的模塊。當(dāng)模塊被注冊后,它的所有 getter、action 及 mutation 都會自動根據(jù)模塊注冊的路徑調(diào)整命名。

          24 使用過 Vue SSR 嗎?說說 SSR

          SSR 也就是服務(wù)端渲染,也就是將 Vue 在客戶端把標(biāo)簽渲染成 HTML 的工作放在服務(wù)端完成,然后再把 html 直接返回給客戶端。

          優(yōu)點(diǎn):

          SSR 有著更好的 SEO、并且首屏加載速度更快

          缺點(diǎn): 開發(fā)條件會受到限制,服務(wù)器端渲染只支持 beforeCreate 和 created 兩個鉤子,當(dāng)我們需要一些外部擴(kuò)展庫時需要特殊處理,服務(wù)端渲染應(yīng)用程序也需要處于 Node.js 的運(yùn)行環(huán)境。

          服務(wù)器會有更大的負(fù)載需求

          25 vue 中使用了哪些設(shè)計模式

          1.工廠模式 - 傳入?yún)?shù)即可創(chuàng)建實(shí)例

          虛擬 DOM 根據(jù)參數(shù)的不同返回基礎(chǔ)標(biāo)簽的 Vnode 和組件 Vnode

          2.單例模式 - 整個程序有且僅有一個實(shí)例

          vuex 和 vue-router 的插件注冊方法 install 判斷如果系統(tǒng)存在實(shí)例就直接返回掉

          3.發(fā)布-訂閱模式 (vue 事件機(jī)制)

          4.觀察者模式 (響應(yīng)式數(shù)據(jù)原理)

          5.裝飾模式: (@裝飾器的用法)

          6.策略模式 策略模式指對象有某個行為,但是在不同的場景中,該行為有不同的實(shí)現(xiàn)方案-比如選項(xiàng)的合并策略

          ...其他模式歡迎補(bǔ)充

          26 你都做過哪些 Vue 的性能優(yōu)化

          這里只列舉針對 Vue 的性能優(yōu)化 整個項(xiàng)目的性能優(yōu)化是一個大工程 可以另寫一篇性能優(yōu)化的文章 哈哈

          • 對象層級不要過深,否則性能就會差
          • 不需要響應(yīng)式的數(shù)據(jù)不要放到 data 中(可以用 Object.freeze() 凍結(jié)數(shù)據(jù))
          • v-if 和 v-show 區(qū)分使用場景
          • computed 和 watch 區(qū)分使用場景
          • v-for 遍歷必須加 key,key 最好是 id 值,且避免同時使用 v-if
          • 大數(shù)據(jù)列表和表格性能優(yōu)化-虛擬列表/虛擬表格
          • 防止內(nèi)部泄漏,組件銷毀后把全局變量和事件銷毀
          • 圖片懶加載
          • 路由懶加載
          • 第三方插件的按需引入
          • 適當(dāng)采用 keep-alive 緩存組件
          • 防抖、節(jié)流運(yùn)用
          • 服務(wù)端渲染 SSR or 預(yù)渲染

          困難

          27 Vue.mixin 的使用場景和原理

          在日常的開發(fā)中,我們經(jīng)常會遇到在不同的組件中經(jīng)常會需要用到一些相同或者相似的代碼,這些代碼的功能相對獨(dú)立,可以通過 Vue 的 mixin 功能抽離公共的業(yè)務(wù)邏輯,原理類似“對象的繼承”,當(dāng)組件初始化時會調(diào)用 mergeOptions 方法進(jìn)行合并,采用策略模式針對不同的屬性進(jìn)行合并。當(dāng)組件和混入對象含有同名選項(xiàng)時,這些選項(xiàng)將以恰當(dāng)?shù)姆绞竭M(jìn)行“合并”。

          相關(guān)代碼如下

          export default function initMixin(Vue){
            Vue.mixin = function (mixin) {
              //   合并對象
                this.options=mergeOptions(this.options,mixin)
            };
          }
          };

          // src/util/index.js
          // 定義生命周期
          export const LIFECYCLE_HOOKS = [
            "beforeCreate",
            "created",
            "beforeMount",
            "mounted",
            "beforeUpdate",
            "updated",
            "beforeDestroy",
            "destroyed",
          ];

          // 合并策略
          const strats = {};
          // mixin核心方法
          export function mergeOptions(parent, child) {
            const options = {};
            // 遍歷父親
            for (let k in parent) {
              mergeFiled(k);
            }
            // 父親沒有 兒子有
            for (let k in child) {
              if (!parent.hasOwnProperty(k)) {
                mergeFiled(k);
              }
            }

            //真正合并字段方法
            function mergeFiled(k) {
              if (strats[k]) {
                options[k] = strats[k](parent[k], child[k]);
              } else {
                // 默認(rèn)策略
                options[k] = child[k] ? child[k] : parent[k];
              }
            }
            return options;

          Vue.mixin 原理詳解 傳送門

          28 nextTick 使用場景和原理

          nextTick 中的回調(diào)是在下次 DOM 更新循環(huán)結(jié)束之后執(zhí)行的延遲回調(diào)。在修改數(shù)據(jù)之后立即使用這個方法,獲取更新后的 DOM。主要思路就是采用微任務(wù)優(yōu)先的方式調(diào)用異步方法去執(zhí)行 nextTick 包裝的方法

          相關(guān)代碼如下

          let callbacks = [];
          let pending = false;
          function flushCallbacks() {
            pending = false; //把標(biāo)志還原為false
            // 依次執(zhí)行回調(diào)
            for (let i = 0; i < callbacks.length; i++) {
              callbacks[i]();
            }
          }
          let timerFunc; //定義異步方法  采用優(yōu)雅降級
          if (typeof Promise !== "undefined") {
            // 如果支持promise
            const p = Promise.resolve();
            timerFunc = () => {
              p.then(flushCallbacks);
            };
          else if (typeof MutationObserver !== "undefined") {
            // MutationObserver 主要是監(jiān)聽dom變化 也是一個異步方法
            let counter = 1;
            const observer = new MutationObserver(flushCallbacks);
            const textNode = document.createTextNode(String(counter));
            observer.observe(textNode, {
              characterData: true,
            });
            timerFunc = () => {
              counter = (counter + 1) % 2;
              textNode.data = String(counter);
            };
          else if (typeof setImmediate !== "undefined") {
            // 如果前面都不支持 判斷setImmediate
            timerFunc = () => {
              setImmediate(flushCallbacks);
            };
          else {
            // 最后降級采用setTimeout
            timerFunc = () => {
              setTimeout(flushCallbacks, 0);
            };
          }

          export function nextTick(cb) {
            // 除了渲染watcher  還有用戶自己手動調(diào)用的nextTick 一起被收集到數(shù)組
            callbacks.push(cb);
            if (!pending) {
              // 如果多次調(diào)用nextTick  只會執(zhí)行一次異步 等異步隊列清空之后再把標(biāo)志變?yōu)?span style="color: #56b6c2;line-height: 26px;">false
              pending = true;
              timerFunc();
            }

          nextTick 原理詳解 傳送門

          29 keep-alive 使用場景和原理

          keep-alive 是 Vue 內(nèi)置的一個組件,可以實(shí)現(xiàn)組件緩存,當(dāng)組件切換時不會對當(dāng)前組件進(jìn)行卸載。

          • 常用的兩個屬性 include/exclude,允許組件有條件的進(jìn)行緩存。

          • 兩個生命周期 activated/deactivated,用來得知當(dāng)前組件是否處于活躍狀態(tài)。

          • keep-alive 的中還運(yùn)用了 LRU(最近最少使用) 算法,選擇最近最久未使用的組件予以淘汰。

          相關(guān)代碼如下

          export default {
            name: "keep-alive",
            abstract: true, //抽象組件

            props: {
              include: patternTypes, //要緩存的組件
              exclude: patternTypes, //要排除的組件
              max: [String, Number], //最大緩存數(shù)
            },

            created() {
              this.cache = Object.create(null); //緩存對象  {a:vNode,b:vNode}
              this.keys = []; //緩存組件的key集合 [a,b]
            },

            destroyed() {
              for (const key in this.cache) {
                pruneCacheEntry(this.cache, key, this.keys);
              }
            },

            mounted() {
              //動態(tài)監(jiān)聽include  exclude
              this.$watch("include", (val) => {
                pruneCache(this, (name) => matches(val, name));
              });
              this.$watch("exclude", (val) => {
                pruneCache(this, (name) => !matches(val, name));
              });
            },

            render() {
              const slot = this.$slots.default; //獲取包裹的插槽默認(rèn)值
              const vnode: VNode = getFirstComponentChild(slot); //獲取第一個子組件
              const componentOptions: ?VNodeComponentOptions =
                vnode && vnode.componentOptions;
              if (componentOptions) {
                // check pattern
                const name: ?string = getComponentName(componentOptions);
                const { include, exclude } = this;
                // 不走緩存
                if (
                  // not included  不包含
                  (include && (!name || !matches(include, name))) ||
                  // excluded  排除里面
                  (exclude && name && matches(exclude, name))
                ) {
                  //返回虛擬節(jié)點(diǎn)
                  return vnode;
                }

                const { cache, keys } = this;
                const key: ?string =
                  vnode.key == null
                    ? // same constructor may get registered as different local components
                      // so cid alone is not enough (#3269)
                      componentOptions.Ctor.cid +
                      (componentOptions.tag ? `::${componentOptions.tag}` : "")
                    : vnode.key;
                if (cache[key]) {
                  //通過key 找到緩存 獲取實(shí)例
                  vnode.componentInstance = cache[key].componentInstance;
                  // make current key freshest
                  remove(keys, key); //通過LRU算法把數(shù)組里面的key刪掉
                  keys.push(key); //把它放在數(shù)組末尾
                } else {
                  cache[key] = vnode; //沒找到就換存下來
                  keys.push(key); //把它放在數(shù)組末尾
                  // prune oldest entry  //如果超過最大值就把數(shù)組第0項(xiàng)刪掉
                  if (this.max && keys.length > parseInt(this.max)) {
                    pruneCacheEntry(cache, keys[0], keys, this._vnode);
                  }
                }

                vnode.data.keepAlive = true; //標(biāo)記虛擬節(jié)點(diǎn)已經(jīng)被緩存
              }
              // 返回虛擬節(jié)點(diǎn)
              return vnode || (slot && slot[0]);
            },
          }; 

          擴(kuò)展補(bǔ)充:LRU 算法是什么?

          lrusuanfa.png

          LRU 的核心思想是如果數(shù)據(jù)最近被訪問過,那么將來被訪問的幾率也更高,所以我們將命中緩存的組件 key 重新插入到 this.keys 的尾部,這樣一來,this.keys 中越往頭部的數(shù)據(jù)即將來被訪問幾率越低,所以當(dāng)緩存數(shù)量達(dá)到最大值時,我們就刪除將來被訪問幾率最低的數(shù)據(jù),即 this.keys 中第一個緩存的組件。

          30 Vue.set 方法原理

          了解 Vue 響應(yīng)式原理的同學(xué)都知道在兩種情況下修改數(shù)據(jù) Vue 是不會觸發(fā)視圖更新的

          1.在實(shí)例創(chuàng)建之后添加新的屬性到實(shí)例上(給響應(yīng)式對象新增屬性)

          2.直接更改數(shù)組下標(biāo)來修改數(shù)組的值

          Vue.set 或者說是$set 原理如下

          因?yàn)轫憫?yīng)式數(shù)據(jù) 我們給對象和數(shù)組本身都增加了__ob__屬性,代表的是 Observer 實(shí)例。當(dāng)給對象新增不存在的屬性 首先會把新的屬性進(jìn)行響應(yīng)式跟蹤 然后會觸發(fā)對象__ob__的 dep 收集到的 watcher 去更新,當(dāng)修改數(shù)組索引時我們調(diào)用數(shù)組本身的 splice 方法去更新數(shù)組

          相關(guān)代碼如下

          export function set(target: Array | Object, key: any, val: any): any {
            // 如果是數(shù)組 調(diào)用我們重寫的splice方法 (這樣可以更新視圖)
            if (Array.isArray(target) && isValidArrayIndex(key)) {
              target.length = Math.max(target.length, key);
              target.splice(key, 1, val);
              return val;
            }
            // 如果是對象本身的屬性,則直接添加即可
            if (key in target && !(key in Object.prototype)) {
              target[key] = val;
              return val;
            }
            const ob = (target: any).__ob__;

            // 如果不是響應(yīng)式的也不需要將其定義成響應(yīng)式屬性
            if (!ob) {
              target[key] = val;
              return val;
            }
            // 將屬性定義成響應(yīng)式的
            defineReactive(ob.value, key, val);
            // 通知視圖更新
            ob.dep.notify();
            return val;

          響應(yīng)式數(shù)據(jù)原理詳解 傳送門

          31 Vue.extend 作用和原理

          官方解釋:Vue.extend 使用基礎(chǔ) Vue 構(gòu)造器,創(chuàng)建一個“子類”。參數(shù)是一個包含組件選項(xiàng)的對象。

          其實(shí)就是一個子類構(gòu)造器 是 Vue 組件的核心 api 實(shí)現(xiàn)思路就是使用原型繼承的方法返回了 Vue 的子類 并且利用 mergeOptions 把傳入組件的 options 和父類的 options 進(jìn)行了合并

          相關(guān)代碼如下

          export default function initExtend(Vue) {
            let cid = 0; //組件的唯一標(biāo)識
            // 創(chuàng)建子類繼承Vue父類 便于屬性擴(kuò)展
            Vue.extend = function (extendOptions) {
              // 創(chuàng)建子類的構(gòu)造函數(shù) 并且調(diào)用初始化方法
              const Sub = function VueComponent(options) {
                this._init(options); //調(diào)用Vue初始化方法
              };
              Sub.cid = cid++;
              Sub.prototype = Object.create(this.prototype); // 子類原型指向父類
              Sub.prototype.constructor = Sub; //constructor指向自己
              Sub.options = mergeOptions(this.options, extendOptions); //合并自己的options和父類的options
              return Sub;
            };

          Vue 組件原理詳解 傳送門

          32 寫過自定義指令嗎 原理是什么

          指令本質(zhì)上是裝飾器,是 vue 對 HTML 元素的擴(kuò)展,給 HTML 元素增加自定義功能。vue 編譯 DOM 時,會找到指令對象,執(zhí)行指令的相關(guān)方法。

          自定義指令有五個生命周期(也叫鉤子函數(shù)),分別是 bind、inserted、update、componentUpdated、unbind

          1. bind:只調(diào)用一次,指令第一次綁定到元素時調(diào)用。在這里可以進(jìn)行一次性的初始化設(shè)置。

          2. inserted:被綁定元素插入父節(jié)點(diǎn)時調(diào)用 (僅保證父節(jié)點(diǎn)存在,但不一定已被插入文檔中)。

          3. update:被綁定于元素所在的模板更新時調(diào)用,而無論綁定值是否變化。通過比較更新前后的綁定值,可以忽略不必要的模板更新。

          4. componentUpdated:被綁定元素所在模板完成一次更新周期時調(diào)用。

          5. unbind:只調(diào)用一次,指令與元素解綁時調(diào)用。 

          原理

          1.在生成 ast 語法樹時,遇到指令會給當(dāng)前元素添加 directives 屬性

          2.通過 genDirectives 生成指令代碼

          3.在 patch 前將指令的鉤子提取到 cbs 中,在 patch 過程中調(diào)用對應(yīng)的鉤子

          4.當(dāng)執(zhí)行指令對應(yīng)鉤子函數(shù)時,調(diào)用對應(yīng)指令定義的方法

          33 Vue 修飾符有哪些

          事件修飾符

          • .stop 阻止事件繼續(xù)傳播
          • .prevent 阻止標(biāo)簽?zāi)J(rèn)行為
          • .capture 使用事件捕獲模式,即元素自身觸發(fā)的事件先在此處處理,然后才交由內(nèi)部元素進(jìn)行處理
          • .self 只當(dāng)在 event.target 是當(dāng)前元素自身時觸發(fā)處理函數(shù)
          • .once 事件將只會觸發(fā)一次
          • .passive 告訴瀏覽器你不想阻止事件的默認(rèn)行為

          v-model 的修飾符

          • .lazy 通過這個修飾符,轉(zhuǎn)變?yōu)樵?change 事件再同步

          • .number 自動將用戶的輸入值轉(zhuǎn)化為數(shù)值類型

          • .trim 自動過濾用戶輸入的首尾空格

          鍵盤事件的修飾符

          • .enter
          • .tab
          • .delete (捕獲“刪除”和“退格”鍵)
          • .esc
          • .space
          • .up
          • .down
          • .left
          • .right

          系統(tǒng)修飾鍵

          • .ctrl
          • .alt
          • .shift
          • .meta

          鼠標(biāo)按鈕修飾符

          • .left
          • .right
          • .middle

          34 Vue 模板編譯原理

          Vue 的編譯過程就是將 template 轉(zhuǎn)化為 render 函數(shù)的過程 分為以下三步

          第一步是將 模板字符串 轉(zhuǎn)換成 element ASTs(解析器)
          第二步是對 AST 進(jìn)行靜態(tài)節(jié)點(diǎn)標(biāo)記,主要用來做虛擬DOM的渲染優(yōu)化(優(yōu)化器)
          第三步是 使用 element ASTs 生成 render 函數(shù)代碼字符串(代碼生成器) 

          相關(guān)代碼如下

          export function compileToFunctions(template) {
            // 我們需要把html字符串變成render函數(shù)
            // 1.把html代碼轉(zhuǎn)成ast語法樹  ast用來描述代碼本身形成樹結(jié)構(gòu) 不僅可以描述html 也能描述css以及js語法
            // 很多庫都運(yùn)用到了ast 比如 webpack babel eslint等等
            let ast = parse(template);
            // 2.優(yōu)化靜態(tài)節(jié)點(diǎn)
            // 這個有興趣的可以去看源碼  不影響核心功能就不實(shí)現(xiàn)了
            //   if (options.optimize !== false) {
            //     optimize(ast, options);
            //   }

            // 3.通過ast 重新生成代碼
            // 我們最后生成的代碼需要和render函數(shù)一樣
            // 類似_c('div',{id:"app"},_c('div',undefined,_v("hello"+_s(name)),_c('span',undefined,_v("world"))))
            // _c代表創(chuàng)建元素 _v代表創(chuàng)建文本 _s代表文Json.stringify--把對象解析成文本
            let code = generate(ast);
            //   使用with語法改變作用域?yàn)閠his  之后調(diào)用render函數(shù)可以使用call改變this 方便code里面的變量取值
            let renderFn = new Function(`with(this){return ${code}}`);
            return renderFn;

          模板編譯原理詳解 傳送門

          35 生命周期鉤子是如何實(shí)現(xiàn)的

          Vue 的生命周期鉤子核心實(shí)現(xiàn)是利用發(fā)布訂閱模式先把用戶傳入的的生命周期鉤子訂閱好(內(nèi)部采用數(shù)組的方式存儲)然后在創(chuàng)建組件實(shí)例的過程中會一次執(zhí)行對應(yīng)的鉤子方法(發(fā)布)

          相關(guān)代碼如下

          export function callHook(vm, hook) {
            // 依次執(zhí)行生命周期對應(yīng)的方法
            const handlers = vm.$options[hook];
            if (handlers) {
              for (let i = 0; i < handlers.length; i++) {
                handlers[i].call(vm); //生命周期里面的this指向當(dāng)前實(shí)例
              }
            }
          }

          // 調(diào)用的時候
          Vue.prototype._init = function (options) {
            const vm = this;
            vm.$options = mergeOptions(vm.constructor.options, options);
            callHook(vm, "beforeCreate"); //初始化數(shù)據(jù)之前
            // 初始化狀態(tài)
            initState(vm);
            callHook(vm, "created"); //初始化數(shù)據(jù)之后
            if (vm.$options.el) {
              vm.$mount(vm.$options.el);
            }
          }; 

          生命周期實(shí)現(xiàn)詳解 傳送門

          36 函數(shù)式組件使用場景和原理

          函數(shù)式組件與普通組件的區(qū)別

          1.函數(shù)式組件需要在聲明組件是指定 functional:true
          2.不需要實(shí)例化,所以沒有this,this通過render函數(shù)的第二個參數(shù)context來代替
          3.沒有生命周期鉤子函數(shù),不能使用計算屬性,watch
          4.不能通過$emit 對外暴露事件,調(diào)用事件只能通過context.listeners.click的方式調(diào)用外部傳入的事件
          5.因?yàn)楹瘮?shù)式組件是沒有實(shí)例化的,所以在外部通過ref去引用組件時,實(shí)際引用的是HTMLElement
          6.函數(shù)式組件的props可以不用顯示聲明,所以沒有在props里面聲明的屬性都會被自動隱式解析為prop,而普通組件所有未聲明的屬性都解析到$attrs里面,并自動掛載到組件根元素上面(可以通過inheritAttrs屬性禁止) 

          優(yōu)點(diǎn) 1.由于函數(shù)式組件不需要實(shí)例化,無狀態(tài),沒有生命周期,所以渲染性能要好于普通組件 2.函數(shù)式組件結(jié)構(gòu)比較簡單,代碼結(jié)構(gòu)更清晰

          使用場景:

          一個簡單的展示組件,作為容器組件使用 比如 router-view 就是一個函數(shù)式組件

          “高階組件”——用于接收一個組件作為參數(shù),返回一個被包裝過的組件

          相關(guān)代碼如下

          if (isTrue(Ctor.options.functional)) {
            // 帶有functional的屬性的就是函數(shù)式組件
            return createFunctionalComponent(Ctor, propsData, data, context, children);
          }
          const listeners = data.on;
          data.on = data.nativeOn;
          installComponentHooks(data); // 安裝組件相關(guān)鉤子 (函數(shù)式組件沒有調(diào)用此方法,從而性能高于普通組件) 

          37 能說下 vue-router 中常用的路由模式實(shí)現(xiàn)原理嗎

          hash 模式

          1. location.hash 的值實(shí)際就是 URL 中#后面的東西 它的特點(diǎn)在于:hash 雖然出現(xiàn) URL 中,但不會被包含在 HTTP 請求中,對后端完全沒有影響,因此改變 hash 不會重新加載頁面。

          2. 可以為 hash 的改變添加監(jiān)聽事件

          window.addEventListener("hashchange", funcRef, false); 

          每一次改變 hash(window.location.hash),都會在瀏覽器的訪問歷史中增加一個記錄利用 hash 的以上特點(diǎn),就可以來實(shí)現(xiàn)前端路由“更新視圖但不重新請求頁面”的功能了

          特點(diǎn):兼容性好但是不美觀

          history 模式

          利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。

          這兩個方法應(yīng)用于瀏覽器的歷史記錄站,在當(dāng)前已有的 back、forward、go 的基礎(chǔ)之上,它們提供了對歷史記錄進(jìn)行修改的功能。這兩個方法有個共同的特點(diǎn):當(dāng)調(diào)用他們修改瀏覽器歷史記錄棧后,雖然當(dāng)前 URL 改變了,但瀏覽器不會刷新頁面,這就為單頁應(yīng)用前端路由“更新視圖但不重新請求頁面”提供了基礎(chǔ)。

          特點(diǎn):雖然美觀,但是刷新會出現(xiàn) 404 需要后端進(jìn)行配置

          原文地址:

          https://juejin.cn/post/6961222829979697165

          瀏覽 107
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  波多野结衣免费不卡视频 | 免费的av网站 | 男人的天堂最新资源 | 色情免费在线观看 | 91偷拍与自偷拍精品无码 |