[toc]
# 題1、vm.$nextTick 是干什么用的?為什么用?
答:
效果:通過 `this.$nextTick` 我們可以注冊一個回調函數,這個回調函數會在下次 DOM 更新結束之后執行
為什么用?
答:Vue 中的數據更新是 `延遲異步更新` 的?就是說當我們修改了數據之后,頁面并不會馬上就更新,所以如果我們修改了數據之后,馬上就獲取頁面中的數據,你會發現他還是原來的數據因為頁面還沒有更新,比如:
~~~
this.name = 'tom' // 更新數據,此時數據雖然修改了,但是頁面并沒有更新
?
// 獲取頁面中的 dom 元素的值,此時獲取到的并不是新修改的 tom ,而是修改之前的原值
// 因為此時上面的 tom 還沒有更新到頁面中(延遲異步更新)!!!!!
let domValue = document.getElementById('#name').innerHTML
?
// 所以如果要獲取修改之后的值,可以在新修改的數據更新到頁面中之后再獲取:
this.$nextTick(()=>{
// 這里獲取的就是更新之后的新值
let domValue = document.getElementById('#name').innerHTML
})
~~~
使用場景之一、
你在Vue生命周期的created()鉤子函數進行的DOM操作一定要放在Vue.nextTick()的回調函數中。原因是什么呢,原因是在created()鉤子函數執行的時候DOM 其實并未進行任何渲染,而此時進行DOM操作無異于徒勞,所以此處一定要將DOM操作的js代碼放進Vue.nextTick()的回調函數中。
# 題2、生命周期函數有哪些?項目中是怎么使用的?
答:
* beforeCreate:在實例初始化之后,數據觀測 (data observer) 和 event/watcher 事件配置之前被調用。此時組件的選項對象還未創建,el 和 data 并未初始化,因此無法訪問 methods, data, computed 等上的方法和數據。
* created:在實例創建完成后被立即調用。在這一步,實例已完成以下的配置:數據觀測 (data observer),property 和方法的運算,watch/event 事件回調。然而,掛載階段還沒開始,`$el` property 目前尚不可用。
* beforeMount:掛在開始之前被調用,相關的 render 函數首次被調用(虛擬 DOM),實例已完成以下的配置:編譯模板,把 data 里面的數據和模板生成 html,完成了 el 和 data 初始化,注意此時還沒有掛在 html 到頁面上。
* mounted:掛載完成(HTML已經被渲染到了頁面上)。這時可以安全的執行一些 DOM 操作。注意 `mounted` **不會**保證所有的子組件也都一起被掛載。如果你希望等到整個視圖都渲染完畢,可以在 `mounted` 內部使用 `this.$nextTick`。
* beforeUpdate:數據更新時調用,發生在虛擬 DOM 打補丁之前。這里適合在更新之前訪問現有的 DOM,比如手動移除已添加的事件監聽器。可以在該鉤子中進一步地更改狀態,不會觸發附加地重渲染過程
* updated:由于數據更改導致的虛擬 DOM 重新渲染和打補丁,在這之后會調用該鉤子。當這個鉤子被調用時,組件 DOM 已經更新,所以你現在可以執行依賴于 DOM 的操作。注意 `updated` **不會**保證所有的子組件也都一起被重繪。如果你希望等到整個視圖都重繪完畢,可以在 `updated` 里使用 vm.$nextTick。
* beforeDestroy:實例銷毀之前調用。在這一步,實例仍然完全可用。
* destroyed:實例銷毀后調用。該鉤子被調用后,對應 Vue 實例的所有指令都被解綁,所有的事件監聽器被移除,所有的子實例也都被銷毀。
* errorCaptured:當捕獲一個來自子孫組件的錯誤時被調用。此鉤子會收到三個參數:錯誤對象、發生錯誤的組件實例以及一個包含錯誤來源信息的字符串。此鉤子可以返回 `false` 以阻止該錯誤繼續向上傳播。
* activated:被 keep-alive 緩存的組件激活時調用。
* deactivated:被 keep-alive 緩存的組件停用時調用。
項目中的應用場景:
beforeCreate: 可以在此時加一些 loading 效果,在 created 時進行移除
created: 需要異步請求數據的方法可以在此時執行,完成數據的初始化
mounted: 當需要操作 dom 的時候執行,可以配合$.nextTick 使用進行單一事件對數據的更新后更新dom
updated: 當數據更新需要做統一業務處理的時候使用
# 題3、在 Vue 中是如何綁定事件?事件修飾符是干什么用的?說出10個常用的修飾符?
答:
1. 在 Vue 中使用 `v-on` 指令來綁定一個事件,事件可以是 methods 中定義的方法,也可以直接調用一個函數,比如綁定點擊事件:`v-on:click="methods中的方法"`,也可以使用 `@` 來進行簡寫。
2. 事件修飾符可以在綁定事件時添加額外的功能,比如:阻止事件冒泡等
3. 修飾符使用的方法是在綁定的事件名稱后面添加 `.修改符名稱`,如:`v-on:click.stop="xxx"`
4. 常用的修飾符有:
.prevent:阻止默認行為
.stop:阻止阻止冒泡
.once:事件只觸發一次
.capture:在捕獲階段觸發
.self:只當在 event.target 是當前元素自身時觸發處理函數
.native:在自定義組件上可以綁定原生事件,事件會被綁定到根元素上
.lazy:輸入框(input、textarea)默認在 input 事件時同步使用 v-model 綁定的數據,這樣事件觸發的非常頻繁,所以可以使用這個修飾符,改為在 `change` 事件時同步數據
.trim:如果要自動過濾用戶輸入的首尾空白字符,可以給 `v-model` 添加 `trim` 修飾符
.number:如果想自動將用戶的輸入值轉為數值類型,可以給 `v-model` 添加 `number` 修飾符。
.enter:使用鍵盤事件時捕獲點擊回車鍵的事件。
# 題4、Vue2中數據響應式的實現原理是什么?數組的響應式?
答:
Vue2 響應式原理:
1. Vue2 中使用了 `Object.defineProperty` 方法為 data 中定義的每個數據設置 get 和 set 攔截器
2. 在 get 中收集依賴(哪個組件使用了這個數據)
3. 在 set 中觸發依賴(讓使用了這個數組的組件進行更新)
4. 每個組件都是被包在一個 `Watcher` 類中的,這個類中保存了渲染這個組件的函數
5. 每個數據都通過一個 `Dep` 類來收集使用了這個數據的組件的 `Watcher` 類
6. 當數據被修改時從這個數據的 Dep 類中取出所有依賴這個數據的 Watcher 類,并調用這個類的渲染方法
7. Watcher 類中的渲染方法會重新進行模板解析得到模板的(AST:抽象語法樹),然后再將AST 轉成 VDOM(虛擬DOM),然后和原VDOM做DIFF算法生成PATCH(補丁)最后將補丁更新到頁面上
Vue2 中數組響應式原理:
Vue2 中重寫了 `Array.prototype` 上的七個方法(push、pop、splice、shift、unshift、sort、reverse),所以對于數組來說,只有調用這7個方法中的一個時,數據才是響應式的(頁面會自動更新),如果直接通過下標操作數組中的非對象類型的值,頁面是不會更新的,這時可以使用 `vm.$set` 方法來實現響應式:
~~~
this.$set(數組數據, 下標, 值)
~~~
# 題5、Vue 組件中的 data 為什么是函數?
答:
1. 跟按引用傳值有關。
2. Vue 中的 data 是一個對象類型,對象類型的數據是按 `引用傳值` 的。這就會導致所有組件的實例都 `共享` 同一份數據,這是不對的,我們要的是每個組件實例都是獨立的
3. 所以為了解決對象類型數據共享的問題,我們需要將 data 定義成一個函數,每個實例需要通過調用函數生成一個獨立的數據對象
# 題6、什么是計算屬性?有什么特點?如何修改?應用場景?和監聽器有什么區別?
答:
1. 在 Vue 中使用 `computed` 來定義計算屬性。
2. 每個計算屬性就是一個函數,這個函數需要有一個返回值。
3. 在模板中可以像數據一樣直接通過 `{{ xx }}`顯示計算屬性的值,顯示的值就是函數的返回值
4. 計算屬性的特點是:只有當函數中依賴的數據改變時,計算屬性函數才會重新調用。
5. 修改計算屬性的值:需要在定義計算屬性時將函數改成一個對象并包含 `getter` 和 `setter` 兩個函數,并在 setter 中編寫修改計算屬性的代碼。
6. 應用場景:購物車中的總價,等需要得到 `經過處理` 的數據時使用
7. 和監聽器的區別:
1. 計算屬性主要用途:得到一個`經過計算的數據` 并且當依賴的數據改變時重新計算,重點在于得到數據
2. 監聽器的主要用途:當依賴改變時函數一個函數,重點在干一件事,這件事通常比較復雜,比如:當搜索條件改變時,重新調用后端接口等
# 題7、什么是監聽器?深度監聽?應用場景?和計算屬性的區別?
答:
1. 在 Vue 中使用 `watch` 屬性來定義一個監聽器函數,當依賴的數據發生變化時觸發函數
2. watch 的本質就是調用了 Vue 中的 $watch 方法對數據進行監聽
3. $watch 方法的返回值一個函數,可以用來取消監聽
4. 默認是淺監聽,即當監聽的對象的子對象發生改變時是不會觸發監聽函數的,通過使用 `deep` 選項可以設置監聽器為深度監聽,即當子對象改變時會觸發監聽函數
5. 應用場景:在后臺管理系統中,在制作數據列表時,每當修改了搜索、排序、翻頁條件時就要重新根據新的條件調用接口獲取數據,這個功能就可以使用監聽器監聽這些條件,每當發生改變時就重新調用接口獲取數據。
6. 與計算屬性的區別:
1. 計算屬性主要用途:得到一個`經過計算的數據` 并且當依賴的數據改變時重新計算,重點在于得到數據
2. 監聽器的主要用途:當依賴改變時函數一個函數,重點在干一件事,這件事通常比較復雜,比如:當搜索條件改變時,重新調用后端接口等
# 題8、什么是過濾器?項目中是怎么使用的?如何使用過濾器?
答:
1. 過濾器主要用來對數據進行格式的修改,在變量后通過 `管道符(|)` 來使用
2. 過濾器分為 `全局過濾器` 和 `局部過濾器`。
全局過濾器:使用 `Vue.filter` 在創建 `new Vue` 之前執行定義全局過濾器,全局過濾器在任何一個組件中可以直接使用。
局部過濾器:只能在注冊了的組件中使用。組件中使用時需要先引入過濾器,然后再注冊到組件的 `filters` 屬性中,然后才能使用。
# 題9、什么是混入?項目中是怎么使用的?
答:在 Vue 組件中通過 `mixins` 屬性來引入一個混入的 JS 文件中的代碼。
用途:可以把多個組件共用的 JS 代碼單獨提取出來放到一個 JS 文件中,然后哪個組件中需要就直接混入。
之前寫的商城后臺的項目:使用了混入實現的把組件中的JS 代碼和 HTML+CSS 分離寫在兩個文件中(都寫在一個 .vue 文件中代碼太長,所以就把 JS 單獨分出來了,然后通過混入合并到 .vue 文件中)。
實現思路:
1. 把 JS 代碼單獨寫到一個 JS 文件中
2. 在 .vue 文件中使用 mixins: \[ js 文件\] 混入進來
# 題10、如何屬性值綁定?class 和 style 的屬性綁定有什么特點?
答:
1. 組件上的屬性值都通過 `v-bind` 來進行綁定,簡寫為 `:` ,比如:`v-bind:src='xxx'`
2. class 和 style 這兩個屬性在綁定時值有兩種形式:
1. 對象:`{[key: String] : Boolean}`,鍵是類名,值是布爾,含義是當值為真是添加這個鍵做為類,比如:`:class="{a: true,b:true,c:false}"` 結果:`class="a b"`
2. 數組:數組中的每一項都會添加上,比如:`:style="[a,b]"` 結果會把 a,b兩個變量中保存的樣式都應用上
# 題11、在 Vue 中使用一個自定義組件的流程是?
答:
自定義組件有兩種情況:
* 全局組件:
* 在創建 Vue(new Vue)實例之前通過 `Vue.component` 定義全局組件
* 在任何一個組件的模板中直接使用,如:
* 局部組件
* 創建一個自定義組件,比如:Hello.vue 組件
* 在組件中使用時需要先使用 import 引入這個自定義組件,比如:import Hello from 'Hello.vue'
* 引入之后需要在 components 屬性中注冊這個組件,如:`components: { Hello }`
* 注冊之后就可以在模板中使用了:
# 題12、組件之間如何傳值?
答:組件間傳值分為三種:父向子、子向父、兄弟間。
第一種:父傳子:主要通過 props 來實現的
具體實現:父組件通過 import 引入子組件,并注冊,在子組件標簽上添加要傳遞的屬性,子組件通過 props 接收,接收有兩種形式一是通過數組形式\[‘要接收的屬性’ \],二是通過對象形式{ }來接收,對象形式可以設置要傳遞的數據類型和默認值,而數組只是簡單的接收
第二種:子傳父:主要通過$emit 來實現
具體實現: 子組件通過通過綁定事件觸發函數, 在其中設置this.要派發的自定義事件,要傳遞的值,?emit 中有兩個參數一是要派發的自定義事件,第二個參數是要傳遞的值
第三種:兄弟之間傳值有兩種方法:
方法一:通過 event bus 實現
具體實現:創建一個空的 vue 并暴露出去,這個作為公共的 bus,即當作兩個組件的橋梁,在兩個兄弟組件中分別引入剛才創建的bus,在組件 A 中通過 bus.(自定義事件名,要發送的值)發送數據,在組件中通過?on(‘自定義事件名‘,function(v) { //v 即為要接收的值 })接收數據
方法二:通過 vuex 實現
具體實現:vuex 是一個狀態管理工具,主要解決大中型復雜項目的數據共享問題,主要包括 state,actions,mutations,getters 和 modules 5 個要素,主要流程:組件通過 dispatch 到 actions,actions 是異步操作,再 actions中通過 commit 到 mutations,mutations 再通過邏輯操作改變 state,從而同步到組件,更新其數據狀態
# 題13、組件上屬性的特點?注意事項?如何修改?
答:
1. 子組件中通過 `props` 來定義屬性
2. 定義屬性有兩種形式:數組和對象
3. 對象形式的屬性在定義時可以指定屬性值的類型、是否必填、默認值等
4. 屬性一般是單向的從父組件傳向子組件,一般子組件中不允許修改父組件傳過來的屬性值
5. 當父組件中的值改變時,子組件中的屬性值也會同步更新
6. 在子組件中如果要修改屬性值有以下兩種方法:
1. 通過 `$emit` 向父組件發送一個事件,然后在父組件中修改
2. 在父組件中綁定屬性值時使用 `.sync` 修飾符,這樣在子組件中修改屬性值時不會報錯
# 題14、style 上的 scoped 是什么意思?實現原理是什么?
答:
1. 定義在 scoped 的 style 標簽中的樣式只對當前組件生效,不影響其它組件和子組件。
2. 實現原理:
1. 當組件中使用了 scoped 的 style 標簽之后,Vue 會給當前組件中所有的標簽上都添加一個形為 `data-v-xxxx` 形式的屬性
2. Vue 會給 CSS 代碼中樣式的選擇器上添加屬性選擇器以限制只對當前組件生效,如:`a[data-v-xxx] {...}`
3. 需要注意的是:使用了 scoped 之后,組件中所有標簽和樣式選擇器上都會添加一個 `data-v-xxxx` 的屬性,頁面會因為多了這些字符串頁變大,影響頁面的加載性能
# 題15、什么是插槽?如何定義插槽?匿名和有名插槽?如何向插槽中傳值?
答:
1. 當我們向子組件中傳值時,我們可以使用屬性,但屬性只能傳一些數據,如果我們要傳的是一段HTML結構或者是一個組件時,屬性就做不到了。
2. 一個組件可以在特定的位置上定義插槽,然后父組件就可以向這些位置上傳入HTML結構或者是另一個組件,所以`插槽是用來向一個組件中傳入另一個組件或者一段HTML結構用的`
定義插槽:在組件中使用 `slot` 組件來定義一個插槽。
插槽分為兩種:
匿名插槽:沒有設置名稱的插槽,一個組件中只能有一個匿名插槽,
有名插槽:通過 name 屬性設置了名字的插槽,比如:
父組件向插槽傳值時分為兩種情況:
匿名插槽:在組件的開始和結構標簽之間放的內容都會放到匿名插槽中,如:這里的內容放至匿名插槽中
具名插槽:通過 `v-slot` 指令指定要放到的有名插槽中,如:放到a標簽中,簡寫是 `#`
# 題16、v-if 和 v-show 的區別?
答:
共同:v-if 和 v-show 都是控制一個元素是否顯示。
區別:v-if 如果是 false 就不渲染這個元素,頁面中沒有這個元素
v-show 無論 true 和 false 都會渲染這個元素,頁面中始終有這個元素,當 false 時使用 display: none 把它隱藏。
# 題17、Vue 中的 key 是干什么用的?
答:
在做循環時 Vue 要求我們必須要加上 key 屬性,并且要為循環的數據設置不一樣的 key 值,這樣的目的是用來區分和識別一個元素是否有改變的,有了這個 key 時在后續的排序、修改等更新操作時性能更快。
# 題18、template 組件的用途是?
答:當我們需要把多個連續的標簽做為一個整體進行操作時,我們一般會使用一個標簽把它們套起來然后統操作,比如:有3個a標簽,然后我們需要通過 v-if 來判斷是否顯示這三個a標簽,如果在這三個a標簽上都寫指令的話,需要寫3遍,所以我們一般把它套起來統一處理:
~~~
<div v-if="show">
<a href>xxx</a>
<a href>xxx</a>
<a href>xxx</a>
</div>
~~~
但這樣寫就會在頁面中多渲染出一個 div 來,所以這時我們可以使用 `template` 標簽來將多個標簽套起來統一操作,但最終在渲染時不渲染這個 template 標簽:
~~~
<template v-if="show">
<a href>xxx</a>
<a href>xxx</a>
<a href>xxx</a>
</template>
~~~
# 題19、描述在項目中使用 vue router 的流程?子路由?vue router 實現原理?
答:
使用流程:
1. 創建一個對象,通過 path 和 component 屬性配置 URL 路徑和組件的對應關系。
2. 通過這個配置對象創建路由:new VueRouter({routes})
3. 頁面中添加 `router-view` 組件顯示匹配到的組件
4. 頁面中使用 `router-link` 組件制作路由跳轉的按鈕
5. 在JS中可以使用 `this.$router.push` 方法實現路由頁面的跳轉
子路由:
~~~
1. 在路由配置中使用 children 屬性來配置子組件
2. 在子頁面中再使用 `router-view` 組件設置子頁面顯示的位置
~~~
# 題20、keep-alive 是干什么用的?怎么用?
答:一個路由在切換時會被銷毀,之前的數據全部丟失,下次再訪問這個組件時,需要重新創建,重新調接口,重新渲染,為了提高性能我們可以使用 keep-alive 把組件緩存起來,這樣在組件切換時,這個組件并沒有被銷毀,下次訪問時,可以就可以顯示出來,而且原組件中數據還在。
把需要緩存的組件使用 keep-alive 套起來即可。比如:把所有路由頁面都緩存起來,在切換時不銷毀:
~~~
<keep-alive>
<router-view />
</keep-alive>
~~~
有兩個參數 include - 字符串或正則表達,只有匹配的組件會被緩存
exclude - 字符串或正則表達式,任何匹配的組件都不會被緩存
還可以使用 include 和 exclude 來設置哪些緩存,哪些不緩存,比如:不緩存登錄頁:
~~~
<keep-alive exclude="login">
<router-view />
</keep-alive>
?
~~~
# 題21、如何實現路由之間的傳參數?
答:路由之間的傳參數主要有兩種方式:
方式一、? 后傳參數【query方式】
直接在跳轉時的路徑后面添加 ? 然后添加要傳的參數,如:`/users?id=100&page=1`
然后在下一個頁面中使用 `this.$route.query.xx` 來接收。
形式二、路由傳參數【params方式】
在配置路由對象時直接把參數部分定義在路由上,如在配置路由時的 path 上寫:`/home/user/:id`。
然后在下一個頁面中使用 `this.$route.params.xx` 來接收。
# 題22、什么是動態組件?如何使用?
答:可以動態的修改要顯示的組件。
Vue 中提供了一個 `component` 組件,這個組件只是一個占位符,它本身不顯示任何內容,這個組件上有一個 `is` 屬性,這個屬性用來設置要顯示的組件的名字。通過修改這個 is 屬性就可以讓 component 顯示不同的組件。
# 題23、路由導致守衛有幾種?參數的用途?
答:主要分三種:`全局守衛`、`組件內守衛` 和 `路由獨享守衛`。
* 全局路由守衛 :每次路由跳轉都會被調用。
全局路由守衛分為:`前置`和`后置`
前置:使用 beforeEach 來定義,在每次跳轉之前執行
后置:使用 afterEach 來定義,在每次跳轉之后執行
* 組件路由守衛:在進入某個 Vue 組件時調用。
在組件中定義路由守衛,有三個:
beforeRouteEnter(to, from ,next):在路由進入組件之前,組件實例還未渲染,所以無法獲取 this 實例,只能通過 vm 來訪問組件實例
beforeRouteUpdate(to, from, next):同一頁面,刷新不同數據時調用
beforeRouteLeave(to, from, next):離開當前路由頁面時調用
* 路由獨享的守衛
路由獨享守衛是在路由配置頁面單獨給某個路由配置的一個守衛。在配置路由時直接使用
beforeEnter(to, from, next):定義守衛
三個參數的含義:
to:對象,將要跳轉到的路由對象。
from:對象,跳轉前的路由對象。
next:函數,控制是否跳轉,分為同種情況:
next():進入下一個鉤子。
next(false):中斷當前的導航,阻止跳轉。
next(新的跳轉):跳轉到新的跳轉
# 題24、Vue 中的自定義指令?自定義指令的鉤子函數?鉤子函數的參數?
答:除了 Vue 中自帶的 v-if、v-bind、v-for、v-model 等指令之外,我們還可以自定義指令。
自定義的指令分為 `全局指令` 和 `局部指令` 兩種。
全局指令:在創建 Vue 實例之前,使用 `Vue.directive` 來創建全局指令,全局指令可以在所有組件中直接使用。
局部指令:在組件中使用 `directives` 屬性可以定義局部指令,這個指令只在當前這個組件中可以使用。
在創建自定義指令時,可以定義指令的生命周期函數:
* `bind`:只調用一次,指令第一次綁定到元素時調用。在這里可以進行一次性的初始化設置。
* `inserted`:被綁定元素插入父節點時調用 (僅保證父節點存在,但不一定已被插入文檔中)。
* `update`:所在組件的 VNode 更新時調用,**但是可能發生在其子 VNode 更新之前**。
* `componentUpdated`:指令所在組件的 VNode **及其子 VNode** 全部更新后調用。
* `unbind`:只調用一次,指令與元素解綁時調用。
鉤子函數中參數:
* `el`:指令所綁定的元素,可以用來直接操作 DOM。
* ~~~
binding
~~~
:一個對象,包含以下 property:
* `name`:指令名,不包括 `v-` 前綴。
* `value`:指令的綁定值,例如:`v-my-directive="1 + 1"` 中,綁定值為 `2`。
* `oldValue`:指令綁定的前一個值,僅在 `update` 和 `componentUpdated` 鉤子中可用。無論值是否改變都可用。
* `expression`:字符串形式的指令表達式。例如 `v-my-directive="1 + 1"` 中,表達式為 `"1 + 1"`。
* `arg`:傳給指令的參數,可選。例如 `v-my-directive:foo` 中,參數為 `"foo"`。
* `modifiers`:一個包含修飾符的對象。例如:`v-my-directive.foo.bar` 中,修飾符對象為 `{ foo: true, bar: true }`。
* `vnode`:Vue 編譯生成的虛擬節點。
* `oldVnode`:上一個虛擬節點,僅在 `update` 和 `componentUpdated` 鉤子中可用。
# 題25、使用 axios 時,如何配置基地址?為什么要配置?
答:
有兩種方法:
寫法一、直接在 axios 上設置:`axios.defaults.baseURL = 'http://xxxxx'`
寫法二、在創建時使用 baseURL 屬性:
~~~
const request = axios.create({
baseURL: 'http://xxxxx'
})
~~~
為什么要配置?
答:每次調用接口時都需要寫上完整的接口地址,但是每個接口前面的地址是相同的,所以我們可以把接口地址中前面相同的部分提取出來設置一下,這樣在后面調用接口時就不需要寫前面的地址了。
每個接口地址前面相同的部分就是基地址。
# 題26、axios 中有幾個攔截器?項目中是怎么使用的?
答:兩個攔截器:
前置(請求)攔截器:就是一個函數,在每次調用接口之前都會觸發的函數。
后置(響應)攔截器:就是一個函數,每次服務器返回結果之后觸發的函數。
基于這兩個函數的特點:
前置攔截器(為每次請求添加令牌):
\\1. 請求接口時要把令牌放到協議頭上提交給接口
\\2. 每次請求接口之前都放令牌比較麻煩
\\3. 所以,可以在前置攔截器中只寫一次代碼,就可以在每次請求時把令牌放上
后置攔截器(判斷每次請求是否失敗):
\\1. 每次調用完接口之后都要判斷是否失敗,如果失敗就提交錯誤信息
\\2. 我們可以在后置攔截器中寫一次代碼,判斷如果返回錯誤就提示錯誤信息
\\3. 這樣之后,以后在項目中就不用再判斷接口是否失敗了
# 題27、Vuex 是干什么用的?為什么要使用?怎么用?由哪幾部分組成?分別干什么用的?
答:
1. 用途:實現組件之間數據的共享。
2. 為什么用:在 Vue 中我們可以通過屬性和事件實現父子之間的傳值,但需要一級一級的傳值,如果層級較深,只通過屬性和事件傳值非常的麻煩,所以需要一個專門的工具來對數據進行管理,以方便的實現各層級組件之間數據的共享。
3. 組成部分:vuex 中主要有 6 部分組成:
state:定義數據
mutations:定義操作數據的方法,簡單的操作,不能是異步的
actions:定義操作數據的復雜的方法,比如AJAX等異步代碼
getters:先處理state中的數據,然后返回處理之后的結果,有點類似過濾器
modules:分模塊使用
plugins:插件
# 題28、什么是虛擬DOM?diff 算法?有什么用?
答:
1. 進行 DOM 時比較耗時的操作,因為瀏覽器需要重繪,我們應該盡量避免 DOM 操作
2. Vue 在內存了維護一個對象類型的數據,這個數據保存了當前 DOM 的結構,這個對象注叫做虛擬DOM
3. 每次再修改了數據之后,先算出修改數據之后的虛擬 DOM 結構,然后和原虛擬DOM 結構進行對比,找出不同的地方,然后只更新不同的地方進行 DOM 操作,這樣就有效的減少了 DOM 操作以提高性能,這種比較新舊虛擬DOM找出不同點的算法就叫做 DIFF 算法
4. DIFF 算法的流程是:只比較同一層級的元素,如果不同,那么子元素就不再比較了直接認為不同,如果相同再比較子元素找出不同的地上,使用的是深度優先遍歷的方法
5. 如果每個節點一個個的比較那么算法的復雜度的 n^3 ,這是無法接受的,所以 diff 算法只對同一層級同一位置的元素進行對比,這樣的時間復雜度是 n。
# 題29、如何在組件中使用 Vuex 中的數據?有幾種使用方式?
答:組件中使用 Vuex 中的數據有兩種方式:
方式一、直接通過 `this.$store.state.xxx` 讀取
~~~
<!-- 模板中 -->
<div>
{{ $store.state.name }}
</div>
// JS 中
console.log( this.$store.state.name )
console.log( this.$store.state.age )
~~~
方式二、引入并映射到計算屬性中使用
~~~
// 1. 引入 mapState
import { mapState } from 'vuex'
// 2. 在 computed 中進行映射
computed: {
// 把 Vuex 中的 name 和 age 映射到當前組件中來
...mapState(['name', 'age'])
}
// 3. 映射完之后,就相當于本組件中的數據,可以直接使用
<div>
{{ name }}
</div>
console.log( this.name )
~~~
# 題30、mutations 和 actions 分別如何使用?有什么區別?
答:
1. 只有 mutations 中定義的函數可以直接修改state中的數據,并且函數中只能編寫同步代碼,在組件中有兩種使用方法:1. 直接使用 commit 來調用 2. 先使用 mapMutations 函數映射到組件中然后直接當作本地函數調用
2. 一般在 actions 函數中編寫業務邏輯復雜或者異步的代碼,然后在 actions 中再調用 mutations 來修改數據。在組件中有兩種使用方法:1. 直接使用 dispatch 來調用 2. 先使用 mapActions 函數映射到組件中然后直接當作本地函數調用
題1、data 中定義的數據如何在頁面中顯示?
答:
1. 如果是字符串、數字等,使用 {{ ... }}
2. 如果是數組一般使用 v-for
# 題2、生命周期函數有哪些?項目中是怎么使用的?
答:
1. 創建前后、掛載前后、銷毀前后、更新前后、被激活、被隱藏等
2. 項目中常用的:
created(創建后):調接口獲取頁面初始數據。
mounted(掛載后):在頁面加載完之后執行一個 DOM 操作的 JS 代碼,比如:商城后中的圖表功能。
# 題3、在 Vue 中如何綁定事件?
答:v-on 或者 @ 。比如:v-on:click 或者 @click。
# 題4、什么是雙向綁定?項目中是怎么使用的?
答:使用 v-model 指令進行雙向綁定。
項目中:操作表單元素:單選框、復選框、下拉框等時需要定義數據并使用 v-model 進行綁定。
# 題5、Vue 組件中的 data 為什么是函數?
答:跟按引用傳值有關。如果不用函數,data 是對象類型的數據,對象都是按引用傳遞的,會導致:當組件使用多次時,它們會共享用同一個 data 數據,修改任何一個組件中的 data ,其他的組件也會跟著一起改變,這是不對的,每個組件實例在使用時應該是完全獨立 的,互不影響才對。
所以 data 必須是一個函數,每次在函數中返回一個全新的對象,這樣就不會出現共享的問題了。
# 擴展:什么是按引用傳值?
變量賦值時分為兩種情況:
- 按值傳遞:如果是數字、布爾、字符串等基礎類型。
原理:先把這個值在內存中復制一份,然后賦給另一個變量。
效果:賦值之后兩個值是兩個獨立的變量互不影響 。
~~~
/* 按值傳遞 */
let a = 100 // 數字
let b = a // 按值傳遞(因為 a 是數字)
// 現在 a 和 b 是兩個獨立 互不影響 的變量
b=200 // 修改 b 不影響 a
console.log(a) // a 還是 100
~~~
- 按引用傳遞:如果值類型是對象或者數組時。
原理:把這個變量在內存中的地址賦給另一個變量。
效果:賦值之后,兩個變量指向同一個內存地址,其實還是同一個變量,只不過有兩個變量名。
~~~
/* 按引用傳遞 */
let a = [1,2,3] // 數組
let b = a // 按引用傳遞(數組和對象都是引用傳遞)
// a 和 b 指向內存中的同一個地址,a和b是同一個數據
b[0] = 100 // 把 b 修改
console.log( a[0] ) // 100 也變成100,
// 所以如果希望復制出一個全新的數組,需要使用 “克隆技術”
/*
淺克隆:只克隆最外層的數據。(只克隆一部分)
方法:let b = [...a] , 把 a 克隆一份給 b (淺克隆)
深克隆:把內層的也復制一份。(完全的克隆)
方法:let b = JSON.parse( JSON.stringify(a) )
*/
~~~
# 題6、如何定義計算屬性?項目中是怎么使用的?
答:使用 computed 來定義計算屬性。制作購物車時里面的商品總價。
# 題7、什么是監聽器?監聽什么的?項目中是怎么使用的?
答:使用 watch 來定義監聽器,一個監聽器就是一個函數,函數名就是要監聽的 data 中的一個變量的名字,一旦監聽的變量發生變量,這個函數就被調用了。
監聽器分為淺監聽和深度監聽,當監聽的數據是一個復雜的數據類型(數組、對象)時需要使用深度監聽。
在項目中實現數據搜索、排序、翻頁時使用過,每當用戶點擊翻頁、排序、搜索條件的按鈕時就要重新調用接口,所以我定義了一個變量,保存翻頁、排序、搜索的信息,然后使用監聽器監聽這個變量,一量發生變化就重新調用接口獲取數據
代碼:
~~~
data() {
return {
// 保存翻頁、搜索關鍵字、排序信息
info: {
page: 1,
keywords: '',
sortby: 'id',
sortway: 'desc'
}
}
},
// 監聽器
watch: {
// 當條件改變時重新調用接口
info: {
deep: true, // 深度監聽
handle: function() {
// 重新調用接口獲取數據
}
}
}
~~~
# 題8、什么是過濾器?項目中是怎么使用的?如何使用過濾器?
答:使用 filter 定義過濾器。
過濾器分為全局過濾器 和 局部過濾器。
全局過濾器:在任何一個組件中可以直接使用。
局部過濾器:在組件中需要先引入,再注冊到組件的 filter 中,然后才能使用。
當一個絕對時間(發表文章時間)在顯示時轉化成一個相對時間,項目中的實現思路:
1. 先定義了一個叫做 relativeTime 的全局變量器
2. 在組件中使用: {{ time | relativeTime }}
# 題9、什么是混入?項目中是怎么使用的?
答:mixins 是定義混入。可以把一段 JS 代碼合并到一個 Vue 組件中。
用途:可以把多個組件共用的 JS 代碼單獨提取出來放到一個 JS 文件中,然后哪個組件中需要就直接混入。
之前寫的商城后臺的項目:使用了混入實現的把組件中的JS 代碼和 HTML+CSS 分離寫在兩個文件中。
實現思路:
1. 把 JS 代碼單獨寫到一個 JS 文件中
2. 在 .vue 文件中使用 mixins: [ js 文件] 混入進來
# 題10、如何將 data 中定義的圖片路徑綁定到 src 屬性上?
答:使用 v-bind 或者 : 。
比如:
~~~
<img :src="image" />
<img v-bind:src="image" />
data:{
image: 'http://www.ww.ww/1.jpg'
}
~~~
# 題11、在 Vue 中使用一個自定義組件的流程是?
答:
自定義組件有兩種情況:
- 全局組件:直接在頁面中使用 ,比如: <Hello />
- 局部組件:先引入、再組件,然后才能使用。
1.創建一個自定義組件,比如:Hello.vue 組件
2. 使用 import 引入這個自定義組件,比如:import Hello from 'Hello.vue'
3. 注冊這個組件,在 Vue 的 components: { Hello }
4. 使用:<Hello />
# 題12、組件之間如何傳值?父向子傳值時,如何設置屬性的默認值和屬性的類型?
答:
父>子 使用屬性 ,注意,需要在子組件中使用 `props` 來接收屬性。
子>父 使用 事件,注意:在子組件中使用 `this.$emit(事件名,數據)` 觸發父組件中的事件
兄弟之間 使用 `Bus 總線` 的中間組件實現。
**父向子傳值時,如何設置屬性的默認值和屬性的類型?**
在子組件中使用 props 接收屬性:
寫法一、不設置類型和默認值
props: [ 'name', 'age' ] // 接收 name 和 age ,不限制類型
寫法二、限制類型和默認值
props: {
name: {
type: String,
default: ''
},
age: {
type: Number,
default: 0
}
}
# 題13、style 上的 scoped 是什么意思?使用原理是什么?
答:里面的樣式只對當前這個組件生效,不會影響其他組件。
當添加了 scoped 之后,vue 會在這個組件中所有的標簽上都添加一個唯一的標識符,只有這個組件中的標簽上才有這個標簽符,然后在 CSS 上會通過這個屬性來限制 CSS 的應用范圍。
1. 組件中添加屬性

2. css 上通過屬性來限制范圍
~~~
/* 通過屬性選擇器就只對當前這個組件中的 .count 生效 */
.count [ data-v-26084dc2 ] {
color: blue;
}
~~~
# 題14、什么是插槽?干什么用的?
答:組件中會在特定的位置上留插槽,我們可以在使用這個組件時向這個插槽中添加HTML的結構,目的是可以自己擴展一個組件的功能。比如:項目中使用的 vant 的導航欄組件,但是它默認的圖標不滿足要求,所以我們可以使用它的插槽,自己來定義圖標這個區域的內容。
插槽分為兩種:
- 匿名插槽(默認插槽)一個組件中只能有一個匿名插槽。
如果在組件中間不使用 slot 默認放到匿名插槽的位置。
- 有名插槽,一個組件中可以有多個有名的插槽。
向有名插槽中放數據時需要使用 slot 屬性指定插槽的名字
# 題15、v-if 和 v-show 的區別?
答:
共同:v-if 和 v-show 都是控制一個元素是否顯示。
區別:v-if 如果是 false 就不渲染這個元素,頁面中沒有這個元素
v-show 無論 true 和 false 都會渲染這個元素,頁面中始終有這個元素,當 false 時使用 display: none 把它隱藏。
# 題16、v-html 的用途是?
答:把一個 HTML 字符串 `解析` 成 HTML 顯示出來。
比如:
~~~
{{ str }} ------------> 當成普通字符串顯示出來: <h1>hello</h1>
<div v-html="str"></div> ----------> 解析 h1 標簽,顯示出來是一個加粗放大的 hello
data: {
str: '<h1>hello</h1>'
}
~~~
# 題17、template 組件的用途是?
答:這個標簽在頁面中不會渲染出任何元素,它的用途是把多個標簽套起來當成一個整體。
比如有三個 div 標簽要循環
~~~
<div>abc</div>
<div>abc</div>
<div>abc</div>
~~~
必須要使用一元素套起來
~~~
<div v-for="v in 10">
<div>abc</div>
<div>abc</div>
<div>abc</div>
</div>
~~~
這樣最外層多了一個 div,如果不想要外層 div 可以使用 tempalte
~~~
<template v-for="v in 10">
<div>abc</div>
<div>abc</div>
<div>abc</div>
</template>
~~~
## $refs 是干什么用的?
Vue 提供給我們進行 dom 操作的一個屬性。
在原生 JS 中當我們需要獲取一個 DOM 元素時需要使用 `document.getElementById` 獲取,那么在 Vue 中如何獲取一個 DOM 元素呢?
示例代碼:
~~~
// 在標簽上添加一個 ref 屬性
<div ref="hello">...</div>
// 在 vue 中通過 $refs 獲取
this.$refs.hello
~~~