1. 什麼是MVVM?
MVVM是Model-View-ViewModel的簡寫,它本質上就是MVC的改進版。MVVM就是將View的狀態和行爲抽象化,將視圖UI和業務邏輯分開,View和Model之間並沒有直接的聯繫,而是通過ViewModel進行交互,它們之間的交互是雙向的。
- Model - 代表數據模型,定義了視圖的狀態或行爲。
- View - 代表視圖,負責將數據模型轉化成可視化界面。
- ViewModel - 負責監聽模型數據和視圖狀態或行爲的變化,同步了View和Model的對象,即實現了雙向綁定。
以下是Vue技術棧中一個雙向綁定的典型例子:
<div id="app6">
<p>{{message}}</p>
<input type="text" v-model="message" />
</div>
<script>
var app6 = new Vue({
el: "#app",
data: {
message: "HelloWorld"
}
});
</script>
示例中當修改input標籤中的內容時,p標籤的內容也會同步修改。
2. Vue的生命週期函數有哪些?
Vue生命週期:
名稱 | 描述 |
---|---|
beforeCreate | 實例剛被創建,vue所有屬性都還不存在 |
created | 實例創建完成,但$el還不存在 |
beforeMount | 掛載之前 |
mounted | 掛載之後,即data中的數值已經被渲染到元素中 |
beforeUpdate | 更新之前 |
updated | 更新之後 |
activated | <keep-alive>組件被激活時 |
deactivated | <keep-alive>組件移除時 |
beforeDestroy | 實例被銷燬前 |
destroyed | 實例被銷燬後 |
示例:https://blog.csdn.net/zeping891103/article/details/78135698
3. Vue實現數據雙向綁定的原理是什麼?
雙向綁定的原理是利用Object.defineProperty()進行數據劫持,然後採用結合發佈者-訂閱者模式監聽數據的變化。當把一個 Javascript 對象傳給 Vue 實例來作爲它的 data 選項時,Vue 將遍歷它的屬性,用 Object.defineProperty 將它們轉爲 getter/setter。如第一題中的雙向綁定的示例代碼,它的數據劫持方式爲:
var data = { message: 'HelloWorld' }
Object.defineProperty(data, 'message', {
get: function () {
return data.message;
},
set: function (newValue) {
data.message = 'newValue';
// data-v-xxxxx爲Vue實例在初始化時給dom元素添加的唯一屬性值
// 當message數據發生變化時,通知相關v-model綁定的元素也發生變化
document.getElementByAttr('input','data-v-xxxxx','').value = newValue;
}
})
另外,當input元素被v-modal綁定時,Vue實例爲該元素添加了一個input事件,當內容發生變化時,觸發this.$emit('input',' ')函數發出事件告訴Vue實例 data.message發生變化了,實現數據同步。更多有關Object.defineProperty的用法可以參考本系列的基礎篇(四)中有介紹。
發佈者-訂閱者模式的設計思想是發佈者管理所有相依於它的訂閱者,並且在發佈者本身的狀態改變時主動發出通知,訂閱者接到通知後開始響應變化並刷新。
4.Vue組件間的參數通過什麼方法傳遞?
1.父組件與子組件傳參
父組件傳給子組件:子組件通過props方法接受參數
子組件傳給父組件:$emit方法傳遞參數
示例:父組件向子組件mycomp傳遞參數message,並添加對子組件的cbmsg事件監聽
<template>
<div>
<p>{{callback_msg}}</p>
<mycomp :message="message" @cbmsg="cbmsgHandle" />
</div>
</template>
<script>
import mycomp from './vmcomp.vue';
export default {
name: '我是父組件',
data() {
return {
message:'父組件向子組件傳參',
callback_msg:''
}
},
methods: {
cbmsgHandle(response){
this.callback_msg = response;
}
},
components: {
mycomp
}
}
</script>
子組件通過props屬性接收父組件傳入的參數,並通過觸發cbmsg事件向父組件傳遞參數
<template>
<div>
<p>{{message}}</p>
<button @click="clickHandle">click</button>
</div>
</template>
<script>
export default {
props: {
message: {
type: String,
default: ''
}
},
methods: {
clickHandle() {
this.$emit('cbmsg', '子組件向父組件傳參');
}
}
}
</script>
2.非父子組件間的數據傳遞,兄弟組件傳值
使用事件總線EventBus,創建一個事件中心,相當於中轉站,可以用它來傳遞事件和接收事件。
示例:創建Bus
// vue-bus.js
const install = function(Vue) {
const Bus = new Vue({
methods: {
emit(event, ...args) {
this.$emit(event, ...args);
},
on(event, callback) {
this.$on(event, callback);
},
off(event, callback) {
this.$off(event, callback);
}
}
});
Vue.prototype.$bus = Bus;
}
export default install;
// Vue首次創建實例時一次性引入
import VueBus from '@/assets/js/vue-bus';
Vue.use(VueBus);
new Vue({
el: '#app',
...
})
// 組件間使用
this.$bus.on('eventName',function(value){})
this.$bus.emit('eventName', value)
使用Vuex,父組件與子組件共享相同的狀態值,配合...mapGetters實現數據同步更新,這時會觸發變化組件的updated鉤子函數,但並不會重新掛載。這裏要加個題外話,在Vuex中狀態是存儲在一個共享對象裏的,所以當有相同getters的時候將會報錯,針對這種情況,可以使用命名空間namespaced: true,添加這個屬性後,無論是存值、取值還是執行函數都需要加上前綴命名空間,詳細可參考:https://blog.csdn.net/weixin_39015132/article/details/84143962。
5. Vue的路由實現:hash模式 和 history模式
hash模式:在瀏覽器顯示的URL中帶有符號“#”,#以及#後面的字符稱之爲hash,通過hashChange事件來監聽URL的改變,用window.location.hash讀取。
特點:hash雖然在URL中,但不被包括在HTTP請求中,因此hash不會重加載頁面。hash內容是用來指導瀏覽器動作,對服務端安全無用。
history模式:在瀏覽器顯示的URL中以“ / ”分割,沒有#。這種模式採用HTML5的新特性且提供了兩個新方法:pushState(),replaceState()可以對瀏覽器歷史記錄棧進行修改,以及popState事件的監聽到狀態變更。
特點:前端的 URL 必須和實際向後端發起請求的 URL 一致,如http://www.xxx.com/items/id,後端如果缺少對 /items/id 的路由處理,將返回 404 錯誤。解決辦法:當訪問不到頁面時,重定向到index.html頁面,即app依賴的頁面。
6. Vue路由的鉤子函數有哪些?
主要有兩個:beforeEach,afterEach。用於對路由導航跳轉前、後的監聽,如跳轉前檢查是否包括有效Token,沒有則返回登錄頁面。
beforeEach有3個參數to,from,next:
- to:route即將進入的目標路由對象。
- from:route當前正要離開的路由對象。
- next :一定要調用該方法完成路由跳轉,默認情況不設參數,跳轉至to指定的路由對象;也可以設置把路由對象作爲入參傳入,跳轉到指定頁面。
示例:
// Router跳轉頁面時,驗證token
export const SetRouterTransition = function(router, store) {
/* router before */
router.beforeEach((to, from, next) => {
// check this router need auth
if(to.meta.requireAuth) {
if(store.state.app.token || getLocalStorage('api_token')) {
next();
} else {
alert('沒有找到模擬TOKEN值')
next({
path: '/',
query: {
redirect: to.fullPath
}
})
}
} else {
next();
}
});
/* router after */
router.afterEach((transition) => {
let title = transition.name;
document.title = title;
});
}
// 在入口文件添加參數router和store
SetRouterTransition(router, store);
7. Vuex是什麼?
Vuex是一種用來實現狀態管理的機制,它的應用核心就是 store(倉庫)。“store”基本上就是一個容器,它包含着你的應用中大部分的狀態 (state)。Vuex主要有四個模塊組成:
- state:用來存儲組件狀態。Vuex使用單一狀態樹,即每個應用將僅僅包含一個store 實例,但單一狀態樹和模塊化並不衝突。存放的數據狀態,不可以直接修改裏面的數據。
- mutations:用於同步修改store中的狀態。使用this.$store.commit(mutationsType, ...param);觸發。
- getters:類似Vue的計算屬性,主要用來過濾一些數據或者直接返回某個狀態值。使用this.$store.getters.xxx獲取。
- action:用於同步修改store中的狀態。使用this.$store.dispatch(actionType, ...param) 觸發。
8. vue-cli如何新增自定義指令?
自定義指令使用Vue.directive來定義,簡單而言,就是從指令賦值中獲取對添加該指令元素的操作說明,通過對操作的解析後按要求初始化該元素。
定義指令有5個鉤子函數:
- bind:只調用一次,指令第一次綁定到元素時調用,用這個鉤子函數可以定義一個在綁定時執行的初始化動作。
- inserted:被綁定元素插入父節點時調用。
- update:所在組件的 VNode 更新時調用,指令的值可能發生了改變也可能沒有。
- componentUpdated:所在組件的 VNode 及其孩子的 VNode 全部更新時調用。
- unbind:只調用一次,指令與元素解綁時調用。
這些鉤子函數具有相同的回調參數:
- el:指令所綁定的元素,可以用來直接操作 DOM 。
- binding: 獲取外界數值的對象
- vnode:vue 編譯生成的虛擬節點
- oldVnode:上一個虛擬節點
全局指令:
Vue.directive('mydir', {
inserted(el) {
console.log(el);
}
})
局部指令:
var app = new Vue({
el: '#app',
// 創建指令(可以多個)
directives: {
// 指令名稱
mydir: {
inserted(el) {
console.log(el);
}
}
}
})
示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script src="../js/libs/vue/2.4.2/vue.js"></script>
</head>
<body>
<div id="app">
<!-- v-myorder:arg.a=value -->
<!-- arg用於對該指令區別類別 .a表示對該指令類別的修飾符,value類似於值對入參value -->
<!--如果你不需要arg.a,也可以直接寫成 v-myorder=value -->
<p v-didemo:foo.a="msg">{{msg}}</p>
<button @click="change">改變msg</button>
</div>
</body>
<script>
Vue.directive("didemo", {
bind: function(el, binding, vnode) {
console.log(1, el, binding);
el.style.color = "red"; //設置文本字體顏色爲紅色
},
inserted: function(el, binding, vnode) {
console.log(2, el, binding);
},
update: function(el, binding, vnode) {
console.log(3, el, binding);
},
componentUpdated: function(el, binding, vnode) {
console.log(4, el, binding);
},
unbind: function(el, binding, vnode) {
console.log(5, el, binding);
}
});
var app = new Vue({
el: "#app",
data: {
msg: "hello world"
},
methods: {
change: function() {
this.msg = "i am change";
}
}
});
</script>
</html>
指令內容更新前:
指令內容更新後:
9. vue如何自定義一個過濾器?
通過vue實例的 filters 屬性或 Vue.filter() 函數創建過濾器,在值運算中用豎槓 | 表示過濾:
<div id="app">
<input type="text" v-model="msg" />
{{ msg | capitalize }}
</div>
局部過濾器:
var vm=new Vue({
el:"#app",
data:{
msg:''
},
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
})
全局過濾器:
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
10. 對keep-alive 的瞭解?
keep-alive是 Vue 內置的一個組件,可以使被包含的組件保留狀態,或避免重新渲染。在vue 2.1.0 版本之後,keep-alive新加入了兩個屬性: include(包含的組件緩存) 與 exclude(排除的組件不緩存,優先級大於include) 。
<!-- 逗號分隔字符串,只有組件a與b被緩存。 -->
<keep-alive include="a,b">
<component></component>
</keep-alive>
該組件也常用於路由導航是否緩存頁面內容:
<keep-alive>
<router-view/>
</keep-alive>
這時,在進入導航頁面時將不會重新渲染頁面,如果需要更新頁面數據可以在生命週期鉤子函數 activated 裏操作。
11. 什麼是事件修飾符?
事件修飾符是指與事件觸發相關的修飾符。如要阻止冒泡事件需要用到event.stopPropagation(),然而使用事件修飾符,僅需@click.stop即可。
事件修飾符有以下幾種,以click事件爲例:
- @click.stop 阻止繼續冒泡
- @click.capture 由默認冒泡模式轉爲捕獲模式
- @click.prevent 提交事件不再重載頁面
- @click.self 只當事件在該元素本身觸發時觸發回調
- @click.once 僅執行一次
修飾符間可以串聯使用,如用 @click.stop.once,表示阻止冒泡並僅執行一次。
12. 什麼是按鍵修飾符?
按鍵修飾符是指與鍵盤事件相關的修飾符。如對Enter鍵的監聽@keyup.enter,快捷組合件的監聽等。按鍵修飾符一般與input標籤配合使用,有以下幾種:
- .enter
- .tab
- .delete (捕獲 “刪除” 和 “退格” 鍵)
- .esc
- .space
- .up
- .down
- .left
- .right
vue2.x版本後新增了下面幾種修飾符:
- .ctrl
- .alt
- .shift
- .meta
13. Vue中Methods與計算屬性computed的區別?
Methods裏的函數每次調用都會從頭開始執行一遍;computed裏的函數只要入參相同,只在首次執行計算過程,其它的會從緩存中直接返回已經計算過的值,不會重新計算。