1.vue
優點?
- 輕量級框架:
只關注檢視層,是一個構建資料的檢視集合,大小隻有幾十kb; - 資料繫結:
保留了angular的特點,在資料操作方面更為簡單; - 元件化:
保留了react的優點,實現了html的封裝和重用,在構建單頁面應用方面有著獨特的優勢;
檢視,資料,結構分離:
使資料的更改更為簡單,不需要進行邏輯程式碼的修改,只需要運算元據就能完成相關操作; - 虛擬DOM:
dom操作是非常耗費效能的, 不再使用原生的dom操作節點,極大解放dom操作,但具體操作的還是dom不過是換了另一種方式;
執行速度更快:
相比較與react而言,同樣是操作虛擬dom,就效能而言,vue存在很大的優勢。
2.vue
父元件向子元件傳遞資料?
父子間通訊:父親提供資料透過屬性 props傳給兒子;兒子透過 $on 綁父親的事件,再透過 $emit 觸發自己的事件(釋出訂閱)
利用父子關係 $parent 、$children
父元件提供資料,子元件注入。 provide 、 inject ,外掛用得多。
ref 獲取元件例項,呼叫元件的屬性、方法
vuex 狀態管理實現通訊
祖孫節點可以使用: $attrs/$listeners
3.子元件向父元件傳遞事件?
- $emit方法
自定義子元件
<template>
<div class="container">
<p @click="clickAction">{{titleName}}</p>
<div class="line"></div>
</div>
</template>
export default {
name: "HelloWorld",
props:{
titleName:{
type:string,
default:""
}
},
methods: {
clickAction(){
this.$emit('clickChild',this.titleName);
}
}
};
父元件中呼叫子元件
<div class="message">
<ActivityHead
:titleName="msgRight"
@clickChild="clickChild">
</ActivityHead>
</div>
import ActivityHead from "./ActivityHead.vue";
export default {
name: "HelloWorld",
components: {
ActivityHead
},
methods: {
clickChild(msg){
console.log(msg);
}
}
};
4.v-if
與v-show
的區別
共同點:
都能控制元素的顯示和隱藏;
不同點:
實現本質方法不同,v-show本質就是透過控制css中的display設定為none,控制隱藏,只會編譯一次;v-if是動態的向DOM樹內新增或者刪除DOM元素,若初始值為false,就不會編譯了。
而且v-if不停的銷燬和建立比較消耗效能。 總結:如果要頻繁切換某節點,使用v-show(切換開銷比較小,初始開銷較大)。
如果不需要頻繁切換某節點使用v-if(初始渲染開銷較小,切換開銷比較大)。
5.v-if
與 v-for
的優先順序?
核心答案:
1、v-for優先於v-if被解析
2、如果同時出現,每次渲染都會先執行迴圈再判斷條件,無論如何迴圈都不可避免,浪費了效能
3、要避免出現這種情況,則在外層巢狀template,在這一層進行v-if判斷,然後在內部進行v-for迴圈
4、如果條件出現在迴圈內部,可透過計算屬性提前過濾掉那些不需要顯示的項
6.vue
元件中data
為什麼必須是一個函式?
如果data
是一個函式的話,這樣每複用一次元件,就會返回一份新的data
,類似於給每個元件例項建立一個私有的資料空間,讓各個元件例項維護各自的資料。
而單純的寫成物件形式,就使得所有元件例項共用了一份data
,就會造成一個變了全都會變的結果。
所以說vue
元件的data
必須是函式。這都是因為js
的特性帶來的,跟vue本身設計無關。
js
本身的物件導向程式設計也是基於原型鏈和建構函式,應該會注意原型鏈上新增一般都是一個函式方法而不會去新增一個物件了。
7.VueX
之actions
與mutations
的區別?
actions
1、用於透過提交mutation改變資料
2、會預設將自身封裝為一個Promise
3、可以包含任意的非同步操作
mutations
1、透過提交commit
改變資料
2、只是一個單純的函式
3、不要使用非同步操作,非同步操作會導致變數不能追蹤
2.如何在vuex中使用非同步修改?
在呼叫vuex
中的方法action
的時候,用promise
實現非同步修改
const actions = {
asyncLogin({ commit }, n){
return new Promise(resolve => {
setTimeout(() => {
commit(types.UserLogin, n);
resolve();
},3000)
})
}
}
8.Vue
有哪些元件間的通訊方式?
核心答案:
Vue 元件間通訊只要指以下 3 類通訊:
父子元件通訊、隔代元件通訊、兄弟元件通訊,下面我們分別介紹每種通訊方式且會說明此種方法可適用於哪類元件間通訊。
方法一
props/$emit
父元件A
透過props
的方式向子元件B
傳遞,B to A
透過在 B 元件中 $emit
, A
元件中 v-on
的方式實現。
1.父元件向子元件傳值
接下來我們透過一個例子,說明父元件如何向子元件傳遞值:在子元件Users.vue中如何獲取父元件App.vue中的資料
userList:["Henry","Bucky","Emily"]
//App.vue父元件
<template>
<div id="app">
<hook-users
:userList="userList"/>
//前者自定義名稱便於子元件呼叫,後者要傳遞資料名
</div>
</template>
<script>
import AppUsers from "./Components/AppUsers"
export default {
name: 'App',
data(){
return{
userList:["Henry","Bucky","Emily"]
}
},
components:{
"app-users":AppUsers
}
}
//users子元件
<template>
<div class="hello">
<ul>
//遍歷傳遞過來的值,然後呈現到頁面
<li v-for="user in userList">{{user}}</li>
</ul>
</div>
</template>
<script>
export default {
name: 'AppUsers',
props:{
userList:{
type:Array,
required:true
}
}
}
</script>
總結:
父元件透過props
向下傳遞資料給子元件。注:元件中的資料共有三種形式:data
、props
、computed
2.子元件向父元件傳值(透過事件形式)
// 子元件
<template>
<header>
<h1 @click="changeTitle">{{title}}</h1>//繫結一個點選事件
</header>
</template>
<script>
export default {
name: 'AppHeader',
data() {
return {
title:"Vue.js Demo"
}
},
methods:{
changeTitle() {
this.$emit("titleChanged","子向父元件傳值");
//自定義事件 傳遞值“子向父元件傳值”
}
}
}
</script>
// 父元件
<template>
<div id="app">
<app-header v-on:titleChanged="updateTitle" ></app-header>
//與子元件titleChanged自定義事件保持一致
// updateTitle($event)接受傳遞過來的文字
<h2>{{title}}</h2>
</div>
</template>
<script>
import Header from "./components/Header"
export default {
name: 'App',
data(){
return{
title:"傳遞的是一個值"
}
},
methods:{
updateTitle(e){ //宣告這個函式
this.title = e;
}
},
components:{
"app-header":Header,
}
}
</script>
總結:
子元件透過events
給父元件傳送訊息,實際上就是子元件把自己的資料傳送到父元件。
方法二、$emit/$on
這種方法透過一個空的Vue
例項作為中央事件匯流排(事件中心),用它來觸發事件和監聽事件,巧妙而輕量地實現了任何元件間的通訊,包括父子、兄弟、跨級。當我們的專案比較大時,可以選擇更好的狀態管理解決方案vuex
。
1.具體實現方式:
var App=new Vue();
App.$emit(事件名,資料);
App.$on(事件名,data => {});
或者自己實現一個
class MyEventEmitter {
constructor() {
this.event = {};
}
// 監聽
on(type, listener) {
if (this.event[type]) {
this.event[type].push(listener);
} else {
this.event[type] = [listener];
}
}
//傳送監聽
emit(type, ...rest) {
if (this.event[type]) {
this.event[type].map(fn => fn.apply(this, rest));
}
}
//移除監聽器
removeListener(type) {
if (this.event[type]) {
delete this.event[type];
console.log(this.event);
}
}
//移除所有的監聽器
removeAllListener() {
this.event = {};
}
}
var MyEvent=new MyEventEmitter();
MyEvent.$emit(事件名,資料);
MyEvent.$on(事件名,data => {});
但是這種方式,記得在每次觸發監聽的時候,記得移除上一個監聽器
方法三、Vuex與localStorage
vuex 是 vue 的狀態管理器,儲存的資料是響應式的。但是並不會儲存起來,重新整理之後就回到了初始狀態,具體做法應該在vuex裡資料改變的時候把資料複製一份儲存到localStorage裡面,重新整理之後,如果localStorage裡有儲存的資料,取出來再替換store裡的state。
const jsonToString=(json)=>{
return JSON.stringify(json)
}
const stringToJson=(keyName)=>{
//暫不驗證資料格式
return window.localStorage.getItem(keyName)?
JSON.parse(window.localStorage.getItem(keyName))
:{};
}
export default new Vuex.Store({
state: {
selectCity:stringToJson("selectCity")
},
mutations: {
changeCity(state, selectCity) {
state.selectCity = selectCity
try {
window.localStorage.setItem('selectCity',jsonToString(state.selectCity));
// 資料改變的時候把資料複製一份儲存到localStorage裡面
} catch (e) {}
}
}
})
方法四、$attrs/$listeners
場景
有些變態需求:比如說A父元件裡面匯入了B元件,可是B元件裡面又匯入了C元件,現在需要A父元件傳值給C元件,或者是C元件需要傳值給父元件,這時候就需要用到$attrs和$listeners了。
$attrs
包含了父作用域中不作為 prop 被識別 (且獲取) 的特性繫結 (class 和 style 除外)。當一個元件沒有宣告任何 prop 時,這裡會包含所有父作用域的繫結 (class和 style 除外),並且可以透過 v-bind="$attrs" 傳入內部元件——在建立高階別的元件時非常有用。(父傳孫專用)
$listener
包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監聽器。它可以透過 v-on="$listeners" 傳入內部元件——在建立更高層次的元件時非常有用。(孫傳父專用)
在父元件當中,最外層元件
<template>
<div>
<Child1
:child1Info="child1"
:child2Info="child2"
v-on:test1="onTest1"
v-on:test2="onTest2">
</Child1>
</div>
</template>
<script>
import Child1 from './child1';
export default {
data() {
return {
child1:"hahha",
child2:"asdsdasd"
};
},
components: { Child1 },
methods: {
onTest1(msg) {
console.log('test1 running...',msg);
},
onTest2(msg) {
console.log('test2 running',msg);
}
}
};
</script>
//在子元件中
<template>
<div class="child-1">
<p>在子元件當中:</p>
<p>props-child1Info: {{child1Info}}</p>
<p>$attrs: {{$attrs}}</p>
<hr>
<!-- Child2元件中能直接觸發test的原因在於 B元件呼叫C元件時 使用 v-on 繫結了$listeners 屬性 -->
<!-- 透過v-bind 繫結$attrs屬性,Child2元件可以直接獲取到A元件中傳遞下來的props(除了child1元件中props宣告的) -->
<Child2 v-bind="$attrs" v-on="$listeners"></Child2>
</div>
</template>
<script>
import Child2 from './child2';
export default {
props: ['child1Info'],
data() {
return {};
},
components: { Child2 },
mounted() {
this.$emit('test1','嘻嘻');
}
};
</script>
//在孫子元件當中:
<template>
<div class="child-2">
<p>在最裡層元件當中child2:</p>
<p>props-child2Info: {{child2Info}}</p>
<p> $attrs 的值: {{$attrs}}</p>
<hr>
</div>
</template>
<script>
export default {
props: ['child2Info'],
data() {
return {};
},
mounted() {
this.$emit('test2','哈哈');
}
};
</script>
程式碼詳細說明:
9.Vue
中雙向資料繫結是如何實現的?
1.vue.js
則是採用資料劫持結合釋出者-訂閱者模式的方式。
2.透過Object.defineProperty()
來劫持各個屬性的setter
,getter
.
3.在資料變動時釋出訊息給訂閱者,觸發相應的監聽回撥。我們先來看Object.defineProperty()
這個方法:
var obj = {};
Object.defineProperty(obj, 'name', {
get: function() {
console.log('我被獲取了')
return val;
},
set: function (newVal) {
console.log('我被設定了')
}
})
obj.name = 'fei';
//在給obj設定name屬性的時候,觸發了set這個方法
var val = obj.name;
//在得到obj的name屬性,會觸發get方法
10.單頁面應用和多頁面應用區別及優缺點?
單頁面應用(SPA
),通俗一點說就是指只有一個主頁面的應用,瀏覽器一開始要載入所有必須的 html
, js
, css
。所有的頁面內容都包含在這個所謂的主頁面中。但在寫的時候,還是會分開寫(頁面片段),然後在互動的時候由路由程式動態載入,單頁面的頁面跳轉,僅重新整理區域性資源。多應用於pc
端。
多頁面(MPA
),就是指一個應用中有多個頁面,頁面跳轉時是整頁重新整理
單頁面的優點:
1,使用者體驗好,快,內容的改變不需要重新載入整個頁面,基於這一點spa對伺服器壓力較小
2,前後端分離
3,頁面效果會比較炫酷(比如切換頁面內容時的專場動畫)
單頁面缺點:
1,不利於seo
2,導航不可用,如果一定要導航需要自行實現前進、後退。(由於是單頁面不能用瀏覽器的前進後退功能,所以需要自己建立堆疊管理)
3,初次載入時耗時多
4,頁面複雜度提高很多
11.vue
中v-if
和v-for
優先順序?
v-for
和v-if
不應該一起使用,必要情況下應該替換成computed
屬性。原因:v-for
比v-if
優先,如果每一次都需要遍歷整個陣列,將會影響速度,尤其是當之需要渲染很小一部分的時候。
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id">
{{ user.name }}
</li>
如上情況,即使100個user中之需要使用一個資料,也會迴圈整個陣列。
omputed: {
activeUsers: function () {
return this.users.filter(function (user) {
return user.isActive
})
}
}
<ul>
<li
v-for="user in activeUsers"
:key="user.id">
{{ user.name }}
</li>
</ul>
12.Vue
事件的修飾符()?
1).stop`:等同於`JavaScript`中的`event.stopPropagation()
,防止事件冒泡
2).prevent
:等同於JavaScript
中的event.preventDefault()
,防止執行預設的行為(如果事件可取消,則取消該事件,而不停止事件的進一步傳播)
3).capture
:與事件冒泡的方向相反,事件捕獲由外到內
4).self
:只會觸發自己範圍內的事件,不包含子元素
5).once
:只會觸發一次
6).passive
:passive
表示listener
函式不會呼叫preventDefault()
passive
主要用在移動端的scroll
事件,來提高瀏覽器響應速度,提升使用者體驗。因為passive=true
等於提前告訴了瀏覽器,touchstart
和touchmove
不會阻止預設事件,手剛開始觸控,瀏覽器就可以立刻給與響應;
否則,手觸控螢幕了,但要等待touchstart
和touchmove
的結果,多了這一步,響應時間就長了,使用者體驗也就差了。
13.Vue
的兩個核心是什麼?
1、資料驅動:
在vue中,資料的改變會驅動檢視的自動更新。傳統的做法是需要手動改變DOM來使得檢視更新,而vue只需要改變資料。
2、元件
元件化開發,優點很多,可以很好的降低資料之間的耦合度。將常用的程式碼封裝成元件之後(vue元件封裝方法),就能高度的複用,提高程式碼的可重用性。一個頁面/模組可以由多個元件所組成。
14、react
和vue
的區別
相同點
- 資料驅動頁面提供響應式的試圖元件
- 都有
virtual DOM
,元件化的開發透過props
引數進行父子之間元件傳遞資料都實現了webComponents
規範 - 資料流動單向都支援伺服器的渲染SSR
- 都有支援
native
的方法react
有React native vue
有wexx
不同點
- 資料繫結
Vue
實現了雙向的資料繫結react
資料流動是單向的 - 資料渲染大規模的資料渲染
react
更快 - 使用場景
React
配合Redux
架構適合大規模多人協作複雜專案Vue適合小快的專案 - 開發風格
react
推薦做法jsx
+inline style
把html
和css
都寫在js
了 vue
是採用webpack
+vue-loader
單檔案元件格式html
,js
,css
同一個檔案
16.Vue
效能最佳化方法
1)編碼階段
- 儘量減少data中的資料,data中的資料都會增加getter和setter,會收集對應的watcher;
- 如果需要使用v-for給每項元素繫結事件時使用事件代理;
- SPA 頁面採用keep-alive快取元件;
- 在更多的情況下,使用v-if替代v-show;
- key保證唯一;
- 使用路由懶載入、非同步元件;
- 防抖、節流;
- 第三方模組按需匯入;
- 長列表滾動到可視區域動態載入;
- 圖片懶載入;
2)使用者體驗:
- 骨架屏;
- PWA;
- 還可以使用快取(客戶端快取、服務端快取)最佳化、服務端開啟gzip壓縮等。
3)SEO最佳化
- 預渲染;
- 服務端渲染SSR;
4)打包最佳化
- 壓縮程式碼;
- Tree Shaking/Scope Hoisting;
- 使用cdn載入第三方模組;
- 多執行緒打包happypack;
- splitChunks抽離公共檔案;
- sourceMap最佳化;
17.v-model
的原理
v-model
本質就是一個語法糖,可以看成是value + input方法的語法糖。可以透過model屬性的prop和event屬性來進行自定義。
原生的v-model,會根據標籤的不同生成不同的事件和屬性。
v-model 在內部為不同的輸入元素使用不同的屬性並丟擲不同的事件:
1)text 和 textarea 元素使用 value 屬性和 input 事件;
2)checkbox 和 radio 使用 checked 屬性和 change 事件;
3)select 欄位將 value 作為 prop 並將 change 作為事件。
例子
model: {
prop: 'checked',
event: 'change'
}
如果想要更改 checked
這個 prop
可以在 Vue
的 instance
中用以下這行程式碼傳送 change
這個 event
,並將目標的變動值傳給 checked
這個 prop
。
this.$emit('change', $event.target.value);
18.nextTick
的實現原理是什麼?
在下次 DOM
更新迴圈結束之後執行延遲迴調。nextTick
主要使用了宏任務和微任務。根據執行環境分別嘗試採用
- Promise
- MutationObserver
- setImmediate
- 如果以上都不行則採用setTimeout
定義了一個非同步方法,多次呼叫nextTick
會將方法存入佇列中,透過這個非同步方法清空當前佇列。
19.談談Computed
和Watch
!
Computed
本質是一個具備快取的watcher
,依賴的屬性發生變化就會更新檢視。
適用於計算比較消耗效能的計算場景。當表示式過於複雜時,在模板中放入過多邏輯會讓模板難以維護,可以將複雜的邏輯放入計算屬性中處理。
Watch
沒有快取性,更多的是觀察的作用,可以監聽某些資料執行回撥。
當我們需要深度監聽物件中的屬性時,可以開啟deep:true
選項,這樣便會對物件中的每一項進行監聽。
這樣會帶來效能問題,最佳化的話可以使用字串形式監聽,如果沒有寫到元件中,不要忘記使用unWatch
手動登出哦。
22.Vue
的優點及缺點?
首先Vue最核心的兩個特點,資料驅動和元件化。
響應式:
這也就是vue.js最大的優點,透過MVVM思想實現資料的雙向繫結,透過虛擬DOM讓我們可以用資料來操作DOM,而不必去操作真實的DOM,提升了效能。且讓開發者有更多的時間去思考業務邏輯。
元件化:
把一個單頁應用中的各個模組拆分到一個個元件當中,或者把一些公共的部分抽離出來做成一個可複用的元件。所以元件化帶來的好處就是,提高了開發效率,方便重複使用,使專案的可維護性更強。
虛擬DOM:
當然,這個不是vue中獨有的。
缺點:
基於物件配置檔案的寫法,也就是options寫法,開發時不利於對一個屬性的查詢。另外一些缺點,在小專案中感覺不太出什麼,vuex的魔法字串,對ts的支援。相容性上存在一些問題。
不利於seo:
導航不可用,如果一定要導航需要自行實現前進、後退。(由於是單頁面不能用瀏覽器的前進後退功能,所以需要自己建立堆疊管理)。
初次載入時耗時多。
23.Vue
中hash
模式和history
模式的區別?
最明顯的是在顯示上,hash
模式的URL
中會夾雜著#
號,而history沒有。
Vue
底層對它們的實現方式不同。hash模式是依靠onhashchange
事件(監聽location.hash
的改變),而history
模式是主要是依靠的HTML5 history
中新增的兩個方法,pushState()
可以改變url地址且不會傳送請求,replaceState()
可以讀取歷史記錄棧,還可以對瀏覽器記錄進行修改。
當真正需要透過URL
向後端傳送HTTP請求的時候,比如常見的使用者手動輸入URL
後回車,或者是重新整理(重啟)瀏覽器,這時候history
模式需要後端的支援。
因為history
模式下,前端的URL
必須和實際向後端傳送請求的URL一致,例如有一個URL
是帶有路徑path
的(例如www.lindaidai.wang/blogs/id
),如果後端沒有對這個路徑做處理的話,就會返回404
錯誤。所以需要後端增加一個覆蓋所有情況的候選資源,一般會配合前端給出的一個404
頁面。
hash:
window.onhashchange = function(event){
// location.hash獲取到的是包括#號的,如"#heading-3"
// 所以可以擷取一下
let hash = location.hash.slice(1);
}
24.你的介面請求一般放在哪個生命週期中?
介面請求一般放在mounted
中,但需要注意的是服務端渲染時不支援mounted
,需要放到created
中。
- 如果不需要操作Dom元素或子元件, 那放到created或者mounted中都可以, 如果需要操作Dom元素, 則需要放到mounted中. 並且, 需要注意的是, 即便是放到created, Vue不會等待非同步返回的資料再去執行下一步. 從體驗的角度考慮, 放在mounted中更好.
25.Vue SSR
渲染原理?
優點:
更利於SEO
不同爬蟲工作原理類似,只會爬取原始碼,不會執行網站的任何指令碼(Google除外,據說Googlebot可以執行javaScript)。使用了Vue或者其它MVVM框架之後,頁面大多數DOM元素都是在客戶端根據js動態生成,可供爬蟲抓取分析的內容大大減少。另外,瀏覽器爬蟲不會等待我們的資料完成之後再去抓取我們的頁面資料。服務端渲染返回給客戶端的是已經獲取了非同步資料並執行JavaScript指令碼的最終HTML,網路爬中就可以抓取到完整頁面的資訊。
更利於首屏渲染
首屏的渲染是node傳送過來的html字串,並不依賴於js檔案了,這就會使使用者更快的看到頁面的內容。尤其是針對大型單頁應用,打包後檔案體積比較大,普通客戶端渲染載入所有所需檔案時間較長,首頁就會有一個很長的白屏等待時間。
場景:
互動少,資料多,例如新聞,部落格,論壇類等
原理:
相當於服務端前面加了一層url分配,可以假想為服務端的中間層,
當位址列url改變或者直接重新整理,其實直接從伺服器返回內容,是一個包含內容部分的html模板,是服務端渲染
而在互動過程中則是ajax處理操作,區域性重新整理,首先是在history模式下,透過history. pushState方式進而url改變,然後請求後臺資料服務,拿到真正的資料,做到區域性重新整理,這時候接收的是資料而不是模板
缺點
服務端壓力較大
本來是透過客戶端完成渲染,現在統一到服務端node服務去做。尤其是高併發訪問的情況,會大量佔用服務端CPU資源;
開發條件受限
在服務端渲染中,created和beforeCreate之外的生命週期鉤子不可用,因此專案引用的第三方的庫也不可用其它生命週期鉤子,這對引用庫的選擇產生了很大的限制;
安全問題
因為做了node服務,因此安全方面也需要考慮DDOS攻擊和sql注入
學習成本相對較高
除了對webpack、Vue要熟悉,還需要掌握node、Express相關技術。相對於客戶端渲染,專案構建、部署過程更加複雜。
26.new Vue()
發生了什麼?
1)new Vue()
是建立Vue
例項,它內部執行了根例項的初始化過程。
2)具體包括以下操作:
- 選項合併
$children
,$refs
,$slots
,$createElement
等例項屬性的方法初始化- 自定義事件處理
- 資料響應式處理
- 生命週期鉤子呼叫 (
beforecreate created
) - 可能的掛載
總結:
new Vue()
建立了根例項並準備好資料和方法,未來執行掛載時,此過程還會遞迴的應用於它的子元件上,最終形成一個有緊密關係的元件例項樹。
27.Vue.use
是幹什麼的?原理是什麼?
vue.use
是用來使用外掛的,我們可以在外掛中擴充套件全域性元件、指令、原型方法等。
1、檢查外掛是否註冊,若已註冊,則直接跳出;
2、處理入參,將第一個引數之後的引數歸集,並在首部塞入 this
上下文;
3、執行註冊方法,呼叫定義好的 install
方法,傳入處理的引數,若沒有 install
方法並且外掛本身為 function
則直接進行註冊;
1) 外掛不能重複的載入
install
方法的第一個引數是vue的建構函式,其他引數是Vue.set中除了第一個引數的其他引數; 程式碼:args.unshift(this)
2) 呼叫外掛的install 方法 程式碼:
typeof plugin.install === 'function'
3) 外掛本身是一個函式,直接讓函式執行。 程式碼:
plugin.apply(null, args)
4) 快取外掛。 程式碼:
installedPlugins.push(plugin)
29.Vue
中是如何檢測陣列變化?
陣列考慮效能原因沒有用defineProperty
對陣列的每一項進行攔截,而是選擇重寫陣列 方法以進行重寫。
當陣列呼叫到這 7 個方法的時候,執行 ob.dep.notify()
進行派發通知 Watcher
更新;
在Vue中修改陣列的索引和長度是無法監控到的。
需要透過以下7種變異方法修改陣列才會觸發陣列對應的wacther
進行更新。
陣列中如果是物件資料型別也會進行遞迴劫持。
說明:那如果想要改索引更新資料怎麼辦?
可以透過Vue.set()
來進行處理 =》 核心內部用的是 splice
方法。
30.Vue.set
方法是如何實現的?
核心答案
為什麼$set
可以觸發更新,我們給物件和陣列本身都增加了dep
屬性,當給物件新增不存在的屬性則觸發物件依賴的watcher
去更新,當修改陣列索引時我們呼叫陣列本身的splice
方法去更新陣列。
補充答案
1) 如果是陣列,呼叫重寫的splice方法 (這樣可以更新檢視 )
程式碼:
target.splice(key, 1, val)
2) 如果不是響應式的也不需要將其定義成響應式屬性。
3) 如果是物件,將屬性定義成響應式的
defineReactive(ob.value, key, val)
31.Vue
中模板編譯原理?
核心答案
將template
轉換成render
函式
補充說明
這裡要注意的是我們在開發時儘量不要使用template.
因為將template轉化成render方法需要在執行時進行編譯操作會有效能損耗,同時引用帶有complier包的vue體積也會變大.
預設.vue檔案中的 template處理是透過vue-loader 來進行處理的並不是透過執行時的編譯。
流程如下
1) 將 template 模板轉換成 ast 語法樹 - parserHTML
2) 對靜態語法做靜態標記 - markUp
3) 重新生成程式碼 - codeGen
32.Vue Loader
的作用?
Vue Loader 是一個 webpack 的 loader,它允許你以一種名為單檔案元件 (SFCs)的格式撰寫 Vue 元件
作用:解析和轉換 .vue 檔案,提取出其中的邏輯程式碼 script、樣式程式碼 style、以及 HTML 模版 template,再分別把它們交給對應的 Loader 去處理。
34.Vue
的生命週期方法有哪些?
總共分為8個階段:建立前/後,載入前/後,更新前/後,銷燬前/後。
1、建立前/後:
1) beforeCreate
階段:vue
例項的掛載元素el
和資料物件data
都為undefined
,還未初始化。
說明:在當前階段data、methods、computed以及watch上的資料和方法都不能被訪問。
2) created階段:vue例項的資料物件data有了,el還沒有。
說明:可以做一些初始資料的獲取,在當前階段無法與Dom進行互動,如果非要想,可以透過vm.$nextTick來訪問Dom。
2、載入前/後:
1) beforeMount階段:vue例項的$el和data都初始化了,但還是掛載之前為虛擬的dom節點。
說明:當前階段虛擬Dom已經建立完成,即將開始渲染。在此時也可以對資料進行更改,不會觸發updated。
2) mounted階段:vue例項掛載完成,data.message成功渲染。
說明:在當前階段,真實的Dom掛載完畢,資料完成雙向繫結,可以訪問到Dom節點,使用$refs屬性對Dom進行操作。
3、更新前/後:
1) beforeUpdate階段:響應式資料更新時呼叫,發生在虛擬DOM打補丁之前,適合在更新之前訪問現有的DOM,比如手動移除已新增的事件監聽器。
說明:可以在當前階段進行更改資料,不會造成重渲染。
2) updated階段:虛擬DOM重新渲染和打補丁之後呼叫,組成新的DOM已經更新,避免在這個鉤子函式中運算元據,防止死迴圈。
說明:當前階段元件Dom已完成更新。要注意的是避免在此期間更改資料,因為這可能會導致無限迴圈的更新。
4、銷燬前/後:
1) beforeDestroy階段:例項銷燬前呼叫,例項還可以用,this能獲取到例項,常用於銷燬定時器,解綁事件。
說明:在當前階段例項完全可以被使用,我們可以在這時進行善後收尾工作,比如清除計時器。
2) destroyed階段:例項銷燬後呼叫,呼叫後所有事件監聽器會被移除,所有的子例項都會被銷燬。
說明:當前階段元件已被拆解,資料繫結被卸除,監聽被移出,子例項也統統被銷燬。
補充說明
第一次頁面載入時會觸發:beforeCreate, created, beforeMount, mounted。
1) created 例項已經建立完成,因為它是最早觸發的原因可以進行一些資料,資源的請求。(伺服器渲染支援created方法)
2) mounted 例項已經掛載完成,可以進行一些DOM操作。(介面請求)
35.生命週期鉤子是如何實現的?
核心答案:
Vue的生命週期鉤子就是回撥函式而已,當建立元件例項的過程中會呼叫對應的鉤子方法。
補充說明
內部主要是使用callHook方法來呼叫對應的方法。核心是一個釋出訂閱模式,將鉤子訂閱好(內部採用陣列的方式儲存),在對應的階段進行釋出。
36.Vue
的父元件和子元件生命週期鉤子執行順序?
核心答案:
第一次頁面載入時會觸發 beforeCreate, created, beforeMount, mounted 這幾個鉤子。
渲染過程:
父元件掛載完成一定是等子元件都掛載完成後,才算是父元件掛載完,所以父元件的mounted在子元件mouted之後
- 父beforeCreate ->
- 父created ->
- 父beforeMount ->
- 子beforeCreate ->
- 子created ->
- 子beforeMount ->
- 子mounted ->
- 父mounted
子元件更新過程:
影響到父元件:
- 父beforeUpdate ->
- 子beforeUpdate->
- 子updated ->
- 父updted
父元件更新過程:
影響到子元件:
- 父beforeUpdate ->
- 子beforeUpdate->
- 子updated ->
- 父updted
銷燬過程:
- 父beforeDestroy ->
- 子beforeDestroy ->
- 子destroyed ->
- 父destroyed
重要:父元件等待子元件完成後,才會執行自己對應完成的鉤子。
37.元件中寫 name
選項有哪些好處及作用?
1) 可以透過名字找到對應的元件 ( 遞迴元件 )
2) 可以透過name屬性實現快取功能 (keep-alive)
3) 可以透過name來識別元件 (跨級元件通訊時非常重要)
38.keep-alive
平時在哪裡使用?原理是?
keep-alive 主要是元件快取,採用的是LRU演算法。最近最久未使用法。
常用的兩個屬性include/exclude,允許元件有條件的進行快取。
兩個生命週期activated/deactivated,用來得知當前元件是否處於活躍狀態。
39.Vue.mixin
的使用場景和原理?
核心答案:
Vue.mixin的作用就是抽離公共的業務邏輯,原理類似“物件的繼承”,當元件初始化時會呼叫 mergeOptions方法進行合併,採用策略模式針對不同的屬性進行合併,如果混入的資料和本身元件中的資料衝突,會採用“就近原則”以元件的資料為準。
補充回答:
mixin
中有很多缺陷“命名衝突問題”,“依賴問題”,“資料來源問題”,這裡強調一下mixin
的資料是不會被共享的。
注意事項
如果只是提取公用的資料或者通用的方法,並且這些資料或者方法,不需要元件間進行維護,就可以使用mixins
。(類似於js中封裝的一些公用的方法)
40.mixins
和vuex
的區別?
vuex
公共狀態管理,在一個元件被引入後,如果該元件改變了vuex裡面的資料狀態,其他引入vuex資料的元件也會對應修改,所有的vue元件應用的都是同一份vuex資料。(在js中,有點類似於淺複製)
vue
引入mixins
資料,mixins
資料或方法,在每一個元件中都是獨立的,互不干擾的,都屬於vue元件自身。(在js中,有點類似於深度複製)
41.mixins
和公共元件的區別?
通用的資料和方法,確實可以提出一個通用的元件,由父子元件傳參的形式進行分享公用。
公共元件
子元件透過props接收來自父元件(公共元件)的引數或者方法,但vue不建議,子元件直接修改props接收到的父元件的資料。需要在子元件的data中或者computed中定義一個欄位來接收。(有點麻煩)
公共元件最主要的作用還是複用相同的vue元件(有檢視,有方法,有狀態
)。
mixins
如果只是提取公用的資料或者通用的方法,並且這些資料或者方法,不需要元件間進行維護,就可以使用mixins。(類似於js中封裝的一些公用的方法)
42.Vue-router
有幾種鉤子函式?具體是什麼及執行流程是怎樣的?
核心答案:
路由鉤子的執行流程,鉤子函式種類有:全域性守衛、路由守衛、元件守衛。
完整的導航解析流程
1.
導航被觸發;
2.
在失活的元件裡呼叫beforeRouteLeave守衛;
3.
呼叫全域性beforeEach守衛;
4.
在複用元件裡呼叫beforeRouteUpdate守衛;
5.
呼叫路由配置裡的beforeEnter守衛;
6.
解析非同步路由元件;
7.
在被啟用的元件裡呼叫beforeRouteEnter守衛;
8.
呼叫全域性beforeResolve守衛;
9.
導航被確認;
10.
呼叫全域性的afterEach鉤子;
11.
DOM更新;
12.
用建立好的例項呼叫beforeRouteEnter守衛中傳給next的回撥函式。
43.vue-router
兩種模式的區別?
核心答案:
vue-router
有 3 種路由模式:hash
、history
、abstract
。
1) hash
模式:hash + hashChange
特點:hash雖然在URL中,但不被包括在HTTP請求中;用來指導瀏覽器動作,對服務端安全無用,hash不會重載入頁面。透過監聽 hash(#)的變化來執行js程式碼 從而實現 頁面的改變。
核心程式碼:
window.addEventListener(‘hashchange‘,function(){
self.urlChange()
})
2) history
模式:historyApi + popState
HTML5
推出的history API,由pushState()記錄操作歷史,監聽popstate事件來監聽到狀態變更;
因為 只要重新整理 這個url(www.ff.ff/jjkj/fdfd/fdf/fd)就會請求伺服器,然而伺服器上根本沒有這個資源,所以就會報404,解決方案就 配置一下伺服器端。
說明:
1)hash
: 使用 URL hash 值來作路由。支援所有瀏覽器,包括不支援 HTML5 History Api 的瀏覽器;
2)history
: 依賴 HTML5 History API 和伺服器配置。具體可以檢視 HTML5 History 模式;
3)abstract
: 支援所有 JavaScript 執行環境,如 Node.js 伺服器端。如果發現沒有瀏覽器的 API,路由會自動強制進入這個模式.
44.Vue
為什麼需要虛擬DOM? 虛擬DOM
的優劣如何?
核心答案:
Virtual DOM
就是用js物件來描述真實DOM,是對真實DOM的抽象,由於直接操作DOM效能低但是js層的操作效率高,可以將DOM操作轉化成物件操作,最終透過diff演算法比對差異進行更新DOM (減少了對真實DOM的操作)。虛擬DOM不依賴真實平臺環境從而也可以實現跨平臺。
補充回答:
虛擬DOM
的實現就是普通物件包含tag、data、hildren等屬性對真實節點的描述。(本質上就是在JS和DOM之間的一個快取)
Vue2
的 Virtual DOM 借鑑了開源庫snabbdom的實現。
VirtualDOM
對映到真實DOM要經歷VNode的create、diff、patch等階段。
45.Vue
中key
的作用和工作原理,說說你對它的理解
核心答案:
例如:
v-for="(item, index) in tableList" :key="index"
key的作用主要是為了高效的更新虛擬DOM,其原理是vue在patch過程中透過key可以精準判斷兩個節點是否是同一個,從而避免頻繁更新不同元素,使得整個patch過程更加高效,減少DOM操作量,提高效能。
補充回答:
1) 若不設定key還可能在列表更新時引發一些隱蔽的bug
2) vue中在使用相同標籤名元素的過渡切換時,也會使用到key屬性,其目的也是為了讓vue可以區分它們,否則vue只會替換其內部屬性而不會觸發過渡效果。
47.computed
和 watch
的區別和運用的場景?
核心答案:
computed
:
計算屬性。依賴其它屬性值,並且 computed 的值有快取,只有它依賴的屬性值發生改變,下一次獲取 computed 的值時才會重新計算 computed 的值;
watch
:
監聽資料的變化。更多的是「觀察」的作用,類似於某些資料的監聽回撥 ,每當監聽的資料變化時都會執行回撥進行後續操作;
運用場景:
1)當我們需要進行數值計算,並且依賴於其它資料時,應該使用 computed,因為可以利用 computed 的快取特性,避免每次獲取值時,都要重新計算;
2)當我們需要在資料變化時執行非同步或開銷較大的操作時,應該使用 watch,使用 watch 選項允許我們執行非同步操作 ( 訪問一個 API ),限制我們執行該操作的頻率,並在我們得到最終結果前,設定中間狀態。
這些都是計算屬性無法做到的。
48.如何理解自定義指令?
核心答案:
指令的實現原理,可以從編譯原理 =>程式碼生成=> 指令鉤子實現進行概述
1)在生成 ast 語法樹時,遇到指令會給當前元素新增directives屬性
2)透過 genDirectives 生成指令程式碼
3)在patch前將指令的鉤子提取到 cbs中,在patch過程中呼叫對應的鉤子。
4)當執行指令對應鉤子函式時,呼叫對應指令定義的方法
49.$router
和$route
的區別?
this.$router
與this.$route
兩者相差 一個 r
但是差距蠻大的。
Vue Router
是Vue.js
的路由管理器,在Vue例項內部,可以透過$router
訪問路由例項
透過$route
可以訪問當前啟用的路由的狀態資訊,包含了當前URL
解析得到的資訊,還有URL匹配到的路由記錄,可以將$router
理解為一個容器去管理了一組$route
,
而$route
是進行了當前URL和元件的對映。
50.$router
物件有哪些屬性?
$router.app
: 配置了router的Vue根例項。$router.mode
: 路由使用的模式。$router.currentRoute
: 當前路由對應的路由資訊物件。
51.$router
物件有哪些方法?
- $router.beforeEach(to, from, next)
- $router.beforeResolve(to, from, next)
- $router.afterEach(to, from)
- $router.push(location[, onComplete[, onAbort]])
- $router.replace(location[, onComplete[, onAbort]])
- $router.go(n)
- $router.back()$router.back
- $history.forward()
- $router.getMatchedComponents([location])
- $router.resolve(location[, current[, append]])
- $router.addRoutes(route)
- $router.onReady(callback[, errorCallback])
- $router.onError(callback)
52.$route
物件有哪些屬性?
$route.path
:
返回字串,對應當前路由的路徑,總是解析為絕對路徑。$route.params
:
返回一個key-value物件,包含了動態片段和全匹配片段,如果沒有路由引數,就是一個空物件。$route.query
:
返回一個key-value物件,表示URL查詢引數。$route.hash
:
返回當前路由的帶#的hash值,如果沒有hash值,則為空字串。$route.fullPath
:
返回完成解析後的URL,包含查詢引數和hash的完整路徑。$route.matched
:
返回一個陣列,包含當前路由的所有巢狀路徑片段的路由記錄,路由記錄就是routes配置陣列中的物件副本。$route.name
:
如果存在當前路由名稱則返回當前路由的名稱。$route.redirectedFrom
:
如果存在重定向,即為重定向來源的路由的名字。
53.對MVC (react)
MVVM(vue)
的瞭解?
什麼是MVC
M(modal)
:是應用程式中處理資料邏輯的部分。V (view)
:是應用程式中資料顯示的部分。C(controller)
:是應用程式中處理使用者互動的地方
什麼是MVVM
- M(modal):模型,定義資料結構。
- C(controller):實現業務邏輯,資料的增刪改查。在MVVM模式中一般把C層算在M層中,(只有在理想的雙向繫結模式下,Controller 才會完全的消失。這種理想狀態一般不存在)。
- VM(viewModal):檢視View的模型、對映和顯示邏輯(如if for等,非業務邏輯),另外繫結器也在此層。ViewModel是基於檢視開發的一套模型,如果你的應用是給盲人用的,那麼也可以開發一套基於Audio的模型AudioModel。
- V(view) :將ViewModel透過特定的GUI展示出來,並在GUI控制元件上繫結檢視互動事件,V(iew)一般由MVVM框架自動生成在瀏覽器中。
54.Vue.js
如何讓CSS
只在當前元件中起作⽤?
將當前元件的 <style>
修改為 <style scoped>
55.Vue
中的diff
原理?
核心答案:
vue的diff演算法是平級比較,不考慮跨級比較的情況。內部採用深度遞迴的方式 + 雙指標的方式進行比較。
補充回答:
1) 先比較是否是相同節點
2) 相同節點比較屬性,並複用老節點
3) 比較兒子節點,考慮老節點和新節點兒子的情況
4) 最佳化比較:頭頭、尾尾、頭尾、尾頭
5) 比對查詢進行復用
Vue2 與 Vue3.x 的diff演算法:
Vue2的核心Diff演算法採用了雙端比較的演算法,同時從新舊children的兩端開始進行比較,藉助key值找到可複用的節點,再進行相關操作。
Vue3.x借鑑了ivi演算法和 inferno演算法,該演算法中還運用了動態規劃的思想求解最長遞迴子序列。(實際的實現可以結合Vue3.x原始碼看。)
56.Vuex的5個核心屬性是什麼?
分別是 state、getters、mutations、actions、modules 。
57.在Vuex中使用mutationvuex要注意什麼?
mutation 必須是同步函式
58.Vuex中action和mutation有什麼相同點?
第二引數都可以接收外部提交時傳來的引數。
this.$store.dispatch('ACTION_NAME',data)
和
this.$store.commit('SET_NUMBER',10)
在元件中多次提交同一個action,怎麼寫使用更方便。
59.那為什麼new Vue裡data可以是一個物件?
new Vue({
el: '#app',
router,
template: '<App/>',
components: {App}
})
原因:因為JS裡的物件是引用關係,而且new Vue是不會被複用的,所以不存在引用物件的問題。