## vue 的實例生命周期*****

## 請問 v-if 和 v-show 有什么區別*****
參考答案:
v-show 指令是通過修改元素的 display 的 CSS 屬性讓其顯示或者隱藏
v-if 指令是直接銷毀和重建 DOM 達到讓元素顯示和隱藏的效果
## 為什么避免 v-if 和 v-for 用在一起*****
參考答案:
vue2.x 中v-for優先級高于v-if,vue3.x 相反。所以2.x 版本中在一個元素上同時使用 v-if 和 v-for 時,v-for 會優先作用,造成性能浪費;3.x 版本中 v-if 總是優先于 v-for 生效,導致v-if訪問不了v-for中的變量。
v3 start
V3 版本查看官方文檔的處理方式
https://cn.vuejs.org/guide/essentials/list.html#v-for-with-v-if

v3 end
解析:
一般我們在兩種常見的情況下會傾向于這樣做:
* 為了過濾一個列表中的項目 (比如 v-for="user in users" v-if="user.isActive")。在這種情形下,請將 users 替換為一個計算屬性 (比如 activeUsers),讓其返回過濾后的列表。
* 為了避免渲染本應該被隱藏的列表 (比如 v-for="user in users" v-if="shouldShowUsers")。這種情形下,請將 v-if 移動至容器元素上 (比如 ul、ol)。
當 Vue 處理指令時,v-for 比 v-if 具有更高的優先級,所以這個模板:
~~~html
<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
~~~
將會經過如下運算:
~~~js
this.users.map(function (user) {
if (user.isActive) {
return user.name
}
})
~~~
因此哪怕我們只渲染出一小部分用戶的元素,也得在每次重渲染的時候遍歷整個列表,不論活躍用戶是否發生了變化。
通過將其更換為在如下的一個計算屬性上遍歷:
~~~js
computed: {
activeUsers: function () {
return this.users.filter(function (user) {
return user.isActive
})
}
}
~~~
~~~html
<ul>
<li
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
~~~
我們將會獲得如下好處:
* 過濾后的列表只會在 users 數組發生相關變化時才被重新運算,過濾更高效。
* 使用 v-for="user in activeUsers" 之后,我們在渲染的時候只遍歷活躍用戶,渲染更高效。
* 解耦渲染層的邏輯,可維護性 (對邏輯的更改和擴展) 更強。
為了獲得同樣的好處,我們也可以把:
~~~html
<ul>
<li
v-for="user in users"
v-if="shouldShowUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
~~~
更新為:
~~~html
<ul v-if="shouldShowUsers">
<li
v-for="user in users"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
~~~
通過將 v-if 移動到容器元素,我們不會再對列表中的每個用戶檢查 shouldShowUsers。取而代之的是,我們只檢查它一次,且不會在 shouldShowUsers 為否的時候運算 v-for。
反例:
~~~html
<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
<ul>
<li
v-for="user in users"
v-if="shouldShowUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
~~~
好例子
~~~html
<ul>
<li
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
<ul v-if="shouldShowUsers">
<li
v-for="user in users"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
~~~
## vue 中 key 值的作用*****
參考答案:
需要使用 key 來給每個節點做一個唯一標識,Diff 算法就可以正確的識別此節點,找到正確的位置區插入新的節點 所以一句話,key 的作用主要是為了高效的更新虛擬 DOM
## $nextTick 的使用***
參考答案:
1、什么是 Vue.nextTick()?
定義:在下次 DOM 更新循環結束之后執行延遲回調。在修改數據之后立即使用這個方法,獲取更新后的 DOM。
所以就衍生出了這個獲取更新后的 DOM 的 Vue 方法。所以放在 Vue.nextTick()回調函數中的執行的應該是會對 DOM 進行操作的 js 代碼;
理解:nextTick(),是將回調函數延遲在下一次 dom 更新數據后調用,簡單的理解是:當數據更新了,在 dom 中渲染后,自動執行該函數,
~~~
<template>
<div class="hello">
<div>
<button id="firstBtn" @click="testClick()" ref="aa">{{testMsg}}</button>
</div>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
testMsg:"原始值",
}
},
methods:{
testClick:function(){
let that=this;
that.testMsg="修改后的值";
console.log(that.$refs.aa.innerText); //that.$refs.aa獲取指定DOM,輸出:原始值
}
}
}
</script>
~~~
使用 this. $nextTick()
~~~js
methods: {
testClick: function() {
let that = this;
that.testMsg = "修改后的值";
that.$nextTick(function() {
console.log(that.$refs.aa.innerText); //輸出:修改后的值
});
}
}
~~~
注意:Vue 實現響應式并不是數據發生變化之后 DOM 立即變化,而是按一定的策略進行 DOM 的更新。$nextTick 是在下次 DOM 更新循環結束之后執行延遲回調,在修改數據之后使用 $nextTick,則可以在回調中獲取更新后的 DOM,
2、什么時候需要用的 Vue.nextTick()??
1、Vue 生命周期的 created()鉤子函數進行的 DOM 操作一定要放在 Vue.nextTick()的回調函數中,原因是在 created()鉤子函數執行的時候 DOM 其實并未進行任何渲染,而此時進行 DOM 操作無異于徒勞,所以此處一定要將 DOM 操作的 js 代碼放進 Vue.nextTick()的回調函數中。與之對應的就是 mounted 鉤子函數,因為該鉤子函數執行時所有的 DOM 掛載已完成。
~~~js
created() {
let that = this;
that.$nextTick(function() { //不使用this.$nextTick()方法會報錯
that.$refs.aa.innerHTML = "created中更改了按鈕內容"; //寫入到DOM元素
});
}
~~~
2、當項目中你想在改變 DOM 元素的數據后基于新的 dom 做點什么,對新 DOM 一系列的 js 操作都需要放進 Vue.nextTick()的回調函數中;通俗的理解是:更改數據后當你想立即使用 js 操作新的視圖的時候需要使用它
~~~
<template>
<div class="hello">
<h3 id="h">{{testMsg}}</h3>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
testMsg:"原始值",
}
},
methods:{
changeTxt:function(){
let that=this;
that.testMsg="修改后的文本值"; //vue數據改變,改變dom結構
let domTxt=document.getElementById('h').innerText; //后續js對dom的操作
console.log(domTxt); //輸出可以看到vue數據修改后DOM并沒有立即更新,后續的dom都不是最新的
if(domTxt==="原始值"){
console.log("文本data被修改后dom內容沒立即更新");
}else {
console.log("文本data被修改后dom內容被馬上更新了");
}
},
}
}
</script>
~~~
正確的用法是:vue 改變 dom 元素結構后使用 vue.$nextTick()方法來實現 dom 數據更新后延遲執行后續代碼
~~~js
changeTxt: function() {
let that = this;
that.testMsg = "修改后的文本值"; //修改dom結構
that.$nextTick(function() { //使用vue.$nextTick()方法可以dom數據更新后延遲執行
let domTxt = document.getElementById('h').innerText;
console.log(domTxt); //輸出可以看到vue數據修改后并沒有DOM沒有立即更新,
if (domTxt === "原始值") {
console.log("文本data被修改后dom內容沒立即更新");
} else {
console.log("文本data被修改后dom內容被馬上更新了");
}
});
}
~~~
3、在使用某個第三方插件時 ,希望在 vue 生成的某些 dom 動態發生變化時重新應用該插件,也會用到該方法,這時候就需要在 $nextTick 的回調函數中執行重新應用插件的方法。
Vue.nextTick(callback) 使用原理:
原因是,Vue 是異步執行 dom 更新的,一旦觀察到數據變化,Vue 就會開啟一個隊列,然后把在同一個事件循環 (event loop) 當中觀察到數據變化的 watcher 推送進這個隊列。如果這個 watcher 被觸發多次,只會被推送到隊列一次。這種緩沖行為可以有效的去掉重復數據造成的不必要的計算和 DOm 操作。而在下一個事件循環時,Vue 會清空隊列,并進行必要的 DOM 更新。 當你設置 vm.someData = 'new value',DOM 并不會馬上更新,而是在異步隊列被清除,也就是下一個事件循環開始時執行更新時才會進行必要的 DOM 更新。如果此時你想要根據更新的 DOM 狀態去做某些事情,就會出現問題。。為了在數據變化之后等待 Vue 完成更新 DOM ,可以在數據變化之后立即使用 Vue.nextTick(callback) 。這樣回調函數在 DOM 更新完成后就會調用。
## vue 的雙向數據綁定的原理****
參考答案:
Vue2.0 實現雙向數據綁定的原理就是利用了 Object.defineProperty() 這個方法重新定義了對象獲取屬性值(get)和設置屬性值(set)的操作來實現的。
Vue3.0 用原生 Proxy 替換 Object.defineProperty。
## vue slot是做什么的?***
參考答案:主要是讓組件的可擴展性更強,簡單點說就是,能夠在組件內寫其他內容
解析:
### 插槽
在 2.6.0 中,我們為具名插槽和作用域插槽引入了一個新的統一的語法 (即 v-slot 指令)。它取代了 slot 和 slot-scope 這兩個目前已被廢棄但未被移除且仍在文檔中的 attribute。
#### 插槽內容
Vue 實現了一套內容分發的 API,這套 API 的設計靈感源自 Web Components 規范草案,將 元素作為承載分發內容的出口。
它允許你像這樣合成組件:
~~~html
<navigation-link url="/profile">
Your Profile
</navigation-link>
~~~
然后你在 的模板中可能會寫為:
~~~js
<a
v-bind:href="url"
class="nav-link"
>
<slot></slot>
</a>
~~~
#### 編譯作用域
## vue 組件中 data 為什么必須是函數***
參考答案:
在 new Vue() 中,data 是可以作為一個對象進行操作的,然而在 component 中,data 只能以函數的形式存在,不能直接將對象賦值給它,這并非是 Vue 自身如此設計,而是跟 JavaScript 特性相關,我們來回顧下 JavaScript 的原型鏈
~~~js
var Component = function() {};
Component.prototype.data = {
message: "Love"
};
var component1 = new Component(),
component2 = new Component();
component1.data.message = "Peace";
console.log(component2.data.message); // Peace
~~~
以上**兩個實例都引用同一個原型對象,當其中一個實例屬性改變時,另一個實例屬性也隨之改變,只有當兩個實例擁有自己的作用域時,才不會互相干擾**!!!!!這句是重點!!!!!
~~~js
var Component = function() {
this.data = this.data();
};
Component.prototype.data = function() {
return {
message: "Love"
};
};
var component1 = new Component(),
component2 = new Component();
component1.data.message = "Peace";
console.log(component2.data.message); // Love
~~~
## 什么是 vue 生命周期和生命周期鉤子函數?*****
參考答案:
vue 的生命周期就是 vue 實例從創建到銷毀的過程
解析:


## vue 更新數組時觸發視圖更新的方法****
參考答案:
1.vue.set 可以設置對象或數組的值,通過 key 或數組索引,可以觸發視圖更新
~~~
數組修改
Vue.set(array, indexOfItem, newValue)
this.array.$set(indexOfItem, newValue)
對象修改
Vue.set(obj, keyOfItem, newValue)
this.obj.$set(keyOfItem, newValue)
~~~
2.vue.delete 刪除對象或數組中元素,通過 key 或數組索引,可以觸發視圖更新
~~~
數組修改
Vue.delete(array, indexOfItem)
this.array.$delete(indexOfItem)
對象修改
Vue.delete(obj, keyOfItem)
this.obj.$delete(keyOfItem)
~~~
3. 數組對象直接修改屬性,可以觸發視圖更新
~~~
this.array[0].show = true;
this.array.forEach(function(item){
item.show = true;
});
~~~
4. splice 方法修改數組,可以觸發視圖更新
~~~
this.array.splice(indexOfItem, 1, newElement)
~~~
5. 數組整體修改,可以觸發視圖更新
~~~
var tempArray = this.array;
tempArray[0].show = true;
this.array = tempArray;
~~~
6. 用 Object. assign 或 lodash. assign 可以為對象添加響應式屬性,可以觸發視圖更新
~~~
//Object.assign的單層的覆蓋前面的屬性,不會遞歸的合并屬性
this.obj = Object.assign({},this.obj,{a:1, b:2})
//assign與Object.assign一樣
this.obj = _.assign({},this.obj,{a:1, b:2})
//merge會遞歸的合并屬性
this.obj = _.merge({},this.obj,{a:1, b:2})
~~~
7.vue 提供了如下的數組的變異方法,可以觸發視圖更新
~~~
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
~~~
## 什么是 vue 的計算屬性?****
參考答案:先來看一下計算屬性的定義: 當其依賴的屬性的值發生變化的時,計算屬性會重新計算。反之則使用緩存中的屬性值。 計算屬性和vue中的其它數據一樣,都是響應式的,只不過它必須依賴某一個數據實現,并且只有它依賴的數據的值改變了,它才會更新。
## $route和$router的區別***
參考答案:$route 是路由信息對象,包括path,params,hash,query,fullPath,matched,name 等路由信息參數。
而 $router 是路由實例對象,包括了路由的跳轉方法,鉤子函數等
## watch的作用是什么****
參考答案:watch 主要作用是監聽某個數據值的變化。和計算屬性相比除了沒有緩存,作用是一樣的。
借助 watch 還可以做一些特別的事情,例如監聽頁面路由,當頁面跳轉時,我們可以做相應的權限控制,拒絕沒有權限的用戶訪問頁面。
## 實現通信方式*****
參考答案:
~~~
方式1: props
1) 通過一般屬性實現父向子通信
2) 通過函數屬性實現子向父通信
3) 缺點: 隔代組件和兄弟組件間通信比較麻煩
方式2: vue自定義事件
1) vue內置實現, 可以代替函數類型的props
a. 綁定監聽: <MyComp @eventName="callback"
b. 觸發(分發)事件: this.$emit("eventName", data)
2) 缺點: 只適合于子向父通信
方式3: 消息訂閱與發布
1) 需要引入消息訂閱與發布的實現庫, 如: pubsub-js
a. 訂閱消息: PubSub.subscribe('msg', (msg, data)=>{})
b. 發布消息: PubSub.publish(‘msg’, data)
2) 優點: 此方式可用于任意關系組件間通信
方式4: vuex
1) 是什么: vuex是vue官方提供的集中式管理vue多組件共享狀態數據的vue插件
2) 優點: 對組件間關系沒有限制, 且相比于pubsub庫管理更集中, 更方便
方式5: slot
1) 是什么: 專門用來實現父向子傳遞帶數據的標簽
a. 子組件
b. 父組件
2) 注意: 通信的標簽模板是在父組件中解析好后再傳遞給子組件的
~~~
## axios有哪些常用方法?****
參考答案:
~~~
一、axios.get(url[, config]) //get請求用于列表和信息查詢
二、axios.delete(url[, config]) //刪除
三、axios.post(url[, data[, config]]) //post請求用于信息的添加
四、axios.put(url[, data[, config]]) //更新操作
~~~
## computed 和 watch 的差異?****
參考答案:
~~~
(1)computed 是計算一個新的屬性,并將該屬性掛載到 Vue 實例上,而 watch 是監聽已經存在且已掛載到 Vue 實例上的數據,所以用 watch 同樣可以監聽 computed 計算屬性的變化。
(2)computed 本質是一個惰性求值的觀察者,具有緩存性,只有當依賴變化后,第一次訪問 computed 屬性,才會計算新的值。而 watch 則是當數據發生變化便會調用執行函數。
(3)從使用場景上說,computed 適用一個數據被多個數據影響,而 watch 適用一個數據影響多個數據。
~~~
詳細資料可以參考:[《做面試的不倒翁:淺談 Vue 中 computed 實現原理》](https://juejin.im/post/5b98c4da6fb9a05d353c5fd7)[《深入理解 Vue 的 watch 實現原理及其實現方式》](https://juejin.im/post/5af908ea5188254265399009)
## vue 常用的修飾符?****
參考答案:
~~~
.prevent: 提交事件不再重載頁面;.stop: 阻止單擊事件冒泡;.self: 當事件發生在該元素本身而不是子元素的時候會觸發;
~~~
## vue 中 key 值的作用?****
參考答案:
~~~
vue 中 key 值的作用可以分為兩種情況來考慮。
第一種情況是 v-if 中使用 key。由于 Vue 會盡可能高效地渲染元素,通常會復用已有元素而不是從頭開始渲染。因此當我們使用 v-if 來實現元素切換的時候,如果切換前后含有相同類型的元素,那么這個元素就會被復用。如果是相同的 input 元素,那么切換前后用戶的輸入不會被清除掉,這樣是不符合需求的。因此我們可以通過使用 key 來唯一的標識一個元素,這個情況下,使用 key 的元素不會被復用。這個時候 key 的作用是用來標識一個獨立的元素。
第二種情況是 v-for 中使用 key。用 v-for 更新已渲染過的元素列表時,它默認使用“就地復用”的策略。如果數據項的順序發生了改變,Vue 不會移動 DOM 元素來匹配數據項的順序,而是簡單復用此處的每個元素。因此通過為每個列表項提供一個 key 值,來以便 Vue 跟蹤元素的身份,從而高效的實現復用。這個時候 key 的作用是為了高效的更新渲染虛擬 DOM。
~~~
詳細資料可以參考:[《Vue 面試中,經常會被問到的面試題 Vue 知識點整理》](https://segmentfault.com/a/1190000016344599)[《Vue2.0 v-for 中 :key 到底有什么用?》](https://www.zhihu.com/question/61064119)[《vue 中 key 的作用》](https://www.cnblogs.com/RainyBear/p/8563101.html)
## computed 和 watch 區別?****
參考答案:
~~~
computed 是計算屬性,依賴其他屬性計算值,并且 computed 的值有緩存,只有當計算值變化才會返回內容。
watch 監聽到值的變化就會執行回調,在回調中可以進行一些邏輯操作。
~~~
## keep-alive 組件有什么作用?****
參考答案:
~~~
如果你需要在組件切換的時候,保存一些組件的狀態防止多次渲染,就可以使用 keep-alive 組件包裹需要保存的組件。
~~~
## vue 雙向數據綁定原理?****
參考答案:
~~~
vue 通過使用雙向數據綁定,來實現了 View 和 Model 的同步更新。vue 的雙向數據綁定主要是通過使用數據劫持和發布訂閱者模式來實現的。
首先我們通過 Object.defineProperty() 方法來對 Model 數據各個屬性添加訪問器屬性,以此來實現數據的劫持,因此當 Model 中的數據發生變化的時候,我們可以通過配置的 setter 和 getter 方法來實現對 View 層數據更新的通知。
數據在 html 模板中一共有兩種綁定情況,一種是使用 v-model 來對 value 值進行綁定,一種是作為文本綁定,在對模板引擎進行解析的過程中。
如果遇到元素節點,并且屬性值包含 v-model 的話,我們就從 Model 中去獲取 v-model 所對應的屬性的值,并賦值給元素的 value 值。然后給這個元素設置一個監聽事件,當 View 中元素的數據發生變化的時候觸發該事件,通知 Model 中的對應的屬性的值進行更新。
如果遇到了綁定的文本節點,我們使用 Model 中對應的屬性的值來替換這個文本。對于文本節點的更新,我們使用了發布訂閱者模式,屬性作為一個主題,我們為這個節點設置一個訂閱者對象,將這個訂閱者對象加入這個屬性主題的訂閱者列表中。當 Model 層數據發生改變的時候,Model 作為發布者向主題發出通知,主題收到通知再向它的所有訂閱者推送,訂閱者收到通知后更改自己的數
據。
~~~
詳細資料可以參考:[《Vue.js 雙向綁定的實現原理》](http://www.cnblogs.com/kidney/p/6052935.html?utm_source=gold_browser_extension)
## vue 中的性能優化*****
參考答案:
1)編碼優化
* 盡量減少data中的數據,data中的數據都會增加getter和setter,會收集對應的watcher
* v-if和v-for不能連用
* 如果需要使用v-for給每項元素綁定事件時使用事件代理
* SPA 頁面采用keep-alive緩存組件
* 在更多的情況下,使用v-if替代v-show
* key保證唯一
* 使用路由懶加載、異步組件
* 防抖、節流
* 第三方模塊按需導入
* 長列表滾動到可視區域動態加載
* 圖片懶加載
2)用戶體驗優化
* 骨架屏
* PWA(漸進式WEB應用)
* 還可以使用緩存(客戶端緩存、服務端緩存)優化、服務端開啟gzip壓縮等。
3)SEO優化
* 預渲染
* 服務端渲染SSR
4)打包優化
* 壓縮代碼;
* Tree Shaking/Scope Hoisting;
* 使用cdn加載第三方模塊;
* 多線程打包happypack;
* splitChunks抽離公共文件;
* sourceMap優化;
說明:優化是個大工程,會涉及很多方面