1.vue.js的兩個核心是什么?
vue.js的兩個核心分別是數據驅動(MVVM)和組件化。
一、數據驅動
數據驅動也就是組件之間數據之間的傳輸。以前用jq或者原生js都是通過操作dom來改變數據。而vue是通過MVVM形式進行數據之間的傳輸。即以view——viewModel——model形式;
從vue的created狀態到beforeMount狀態這段過程是將數據以model的形式傳遞到viewModel,
然后從beforeMount到mounted這段過程是將數據從viewModel掛載到dom上,然后在view層進行顯示;
當view視圖上面的數據發生改變時,通過beforeUpdate鉤子函數或者是watch屬性監聽到數據的變化并通過updated鉤子函數將更改之后的數據掛載到dom上,從而使得其他與該數據相關的值也會發生改變。
二、組件化
組件系統是Vue的另一個重要概念,因為它是一種抽象,允許我們使用小型、獨立和通常可復用的組件去構建大型應用。
2.vue的生命周期是什么?
在vue中主要由四部分組成的,分別是創建(create)、掛載(mount)、更新(update)、摧毀(destory)組成的,根據這四部分的前后關系,又分為8個鉤子函數。分別是創建前(beforeCreate)、創建后(created)、掛載前(beforeMount)、掛載后(mounted)、更新前(beforeUpdate)、更新后(updated)、注銷前(beforeDestory)、注銷后(destoryed);
下面就要插入一段代碼更清楚的了解vue從beforeCreate到mounted過程的生命周期
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vue生命周期學習</title>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="message" />
<h1>{{message}}</h1>
</div>
</body>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Vue的生命周期'
},
beforeCreate: function() {
console.group('------beforeCreate創建前狀態------');
console.log("%c%s", "color:red" , "el : " + this.$el); //undefined
console.log("%c%s", "color:red","data : " + this.$data); //undefined
console.log("%c%s", "color:red","message: " + this.message)
},
created: function() {
console.group('------created創建完畢狀態------');
console.log("%c%s", "color:red","el : " + this.$el); //undefined
console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
console.log("%c%s", "color:red","message: " + this.message); //已被初始化
},
beforeMount: function() {
console.group('------beforeMount掛載前狀態------');
console.log("%c%s", "color:red","el : " + (this.$el)); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
console.log("%c%s", "color:red","message: " + this.message); //已被初始化
},
mounted: function() {
console.group('------mounted 掛載結束狀態------');
console.log("%c%s", "color:red","el : " + this.$el); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data); //已被初始化
console.log("%c%s", "color:red","message: " + this.message); //已被初始化
},
beforeUpdate: function () {
console.group('beforeUpdate 更新前狀態===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message);
},
updated: function () {
console.group('updated 更新完成狀態===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message);
},
beforeDestroy: function () {
console.group('beforeDestroy 銷毀前狀態===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message);
},
destroyed: function () {
console.group('destroyed 銷毀完成狀態===============》');
console.log("%c%s", "color:red","el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red","data : " + this.$data);
console.log("%c%s", "color:red","message: " + this.message)
}
})
</script>
打開頁面,然后看控制臺,會輸出以上結果
然后看我畫箭頭的部分,相信大家應該明白什么意思了。
打開頁面時,vue的生命周期會從vue實例創建之前到掛載完成,在控制臺中科院看到
1.beforeCreate到created鉤子函數之間的生命周期
這段時間主要是對vue實例進行初始化事件,并進行數據的觀測(比如說data屬性、computed屬性、methods屬性);
2.created到beforeMount鉤子函數之間的生命周期
從圖片上面的流程圖可得知,created到beforeMount這個過程主要還是將查找el并將模板放到dom上,
首先是判斷vue實例中是否有el選項,如果沒有的話就會等到vm.$mount(el)掛載之后才能繼續判斷有無template參數選項;如果存在el選項的話就直接判斷有無template參數選項,如果有的話則將其作為模板并編譯成render函數
如果沒有template選項,則將外部的HTML模板進行編譯;
所以template中的模板優先級高于outerHTML的優先級。
3.beforeMount到mounted鉤子函數之間的生命周期
由上面兩張圖片可以得知,從beforeMount到mounted鉤子函數這個過程主要是給vue實例添加$el成員,并且替換掉掛載的DOM元素。
4.mounted鉤子函數
此時數據已經掛載到dom上,并在view層顯示。
5.beforeUpdate鉤子函數到updated鉤子函數之間的生命周期
[圖片上傳中...(image-14cea9-1543655591207-2)]
從上面兩圖片可得知,當監聽到數據發生變化的時候,先觸發beforeUpdate事件,這時候vm.message的值重新渲染到虛擬dom上;
6.beforeDestory到destoryed鉤子函數之間的生命周期
beforeDestory鉤子函數在實例銷毀之前調用。在這一步,實例仍然完全可用。
destroyed鉤子函數在Vue 實例銷毀后調用。調用后,Vue 實例指示的所有東西都會解綁定,所有的事件監聽器會被移除,所有的子實例也會被銷毀。
3.請問v-if和v-show有什么區別?
v-if是“真正”的條件渲染,因為它會確保在切換過程中條件塊內的事件監聽器和子組件適當的銷毀和重建。
v-if也是惰性的:如果在處室渲染時條件塊為假,則什么也不做——直到條件第一次變為真時,才會開始渲染條件塊;
相比之下,v-show就簡單得多——不管初始條件是什么,元素總是被渲染,并能進行簡單的css切換;
所以一般來說,v-if 有更高的切換開銷,而v-show有更高的初始渲染開銷。因此,如果是需要進行頻繁的切換,則用v-show比較好;如果運行時條件很少改變,則用v-if比較好。
v-if還可以用來實現異步獲取組件信息
4.vue常用的修飾符?
主要分為事件修飾符和按鍵修飾符;事件修飾符有以下幾個:
.stop - 調用 event.stopPropagation()。
.prevent - 調用 event.preventDefault()。
.capture - 添加事件偵聽器時使用 capture 模式。
.self - 只當事件是從偵聽器綁定的元素本身觸發時才觸發回調。
.{keyCode | keyAlias} - 只當事件是從特定鍵觸發時才觸發回調。
.native - 監聽組件根元素的原生事件。
.once - 只觸發一次回調。
.left - (2.2.0) 只當點擊鼠標左鍵時觸發。
.right - (2.2.0) 只當點擊鼠標右鍵時觸發。
.middle - (2.2.0) 只當點擊鼠標中鍵時觸發。
.passive - (2.3.0) 以 { passive: true } 模式添加偵聽器
5.v-on可以監聽多個方法嗎?
可以。監聽一個方法里面運行若干個方法,就能監聽若干個方法了;
6.vue中key值的作用
key的特殊屬性主要用在Vue的虛擬dom算法,新舊nodes對比時辨識VNodes.如果不使用key,Vue會使用一種最大限度減少動態元素并且盡可能的嘗試修復/再利用相同類型元素的算法。使用key,它會基于key的變化重新排列元素順序,并且會移除key不存在的元素。
它可用于與v-for的結合,例如
<ul>
<li v-for="item in items" :key="item.id">...</li>
</ul>
它也可以用于強制替換元素/組件而不是重復使用它。當遇到如下場景時它可能會很有用:
完整地觸發組件的生命周期鉤子;
觸發過渡
7.什么是雙向綁定?在Vue中有幾種方法實現雙向綁定?
雙向綁定是view層數據發生變化的時候會改變model層的數據變化;然后根據model層的數據變化進同步文本節點內容,使之也發生變化,即從model到view層的數據變化;
如果是輸入框可以使用v-model實現雙向綁定;因為v-model可以看做v-bind:value="aaa",v-on:input="aaa"組成的;
如果用原生表示的話,則可通過Object.defineProperty()中的get和set方法來完成數據雙向綁定。即
var obj = {
foo: 'foo'
}
Object.defineProperty(obj, 'foo', {
get: function () {
console.log('將要讀取obj.foo屬性');
},
set: function (newVal) {
console.log('當前值為', newVal);
}
});
obj.foo; // 將要讀取obj.foo屬性
obj.foo = 'name'; // 當前值為 name
不過一般情況下出于后期項目維護考慮,在Vue中一般都是使用單向數據流的形式對數據進行傳輸,這樣方便追蹤數據來源,有利于項目維護。
8.Vue組件間的通信有幾種場景?這些場景又有幾種方法能夠實現?
總共有以下幾種場景:
1.父->子組件間的數據傳遞
父子組件的通信是開發是最常用的也是最重要的,一般情況下都是父元素設置屬性然后子元素通過prop接受父元素傳遞的屬性值。
注意項:
父組件傳遞數據時類似在標簽中寫了一個屬性,如果是傳遞的數據是data中的自然是要在傳遞屬性前加v-bind:,如果傳遞的是一個已知的固定值呢
字符串是靜態的可直接傳入無需在屬性前加v-bind
數字,布爾,對象,數組,因為這些是js表達式而不是字符串,所以即使這些傳遞的是靜態的也需要加v-bind,把數據放到data中引用,
如果prop傳到子組件中的數據是一個對象的話,要注意傳遞的是一個對象引用,雖然父子組件看似是分離的但最后都是在同一對象下
如果prop傳到子組件的值只是作為初始值使用,且在父組件中不會變化賦值到data中使用
如果傳到子組件的prop的數據在父組件會被改變的,放到計算屬性中監聽變化使用。因為如果傳遞的是個對象的話,只改變下面的某個屬性子組件中是不會響應式更新的,如果子組件需要在數據變化時響應式更新那只能放到computed中或者用watch深拷貝deep:true才能監聽到變化
當然如果你又需要在子組件中通過prop傳遞數據的變化做些操作,那么寫在computed中會報警告,因為計算屬性中不推薦有任何數據的改變,最好只進行計算。如果你非要進行數據的操作那么可以把監聽寫在watch(注意deep深拷貝)或者使用computed的get和set如下圖:
但問題又來了,如果你傳進來的是個對象,同時你又需要在子組件中操作傳進來的這個數據,那么在父組件中的這個數據也會改變,因為你傳遞的只是個引用, 即使你把prop的數據復制到data中也是一樣的,無論如何賦值都是引用的賦值,你只能對對象做深拷貝創建一個副本才能繼續操作,你可以用JSON的方法先轉化字符串在轉成對象更方便一點,
所以在父子傳遞數據時要先考慮好數據要如何使用,否則你會遇到很多問題或子組件中修改了父組件中的數據,這是很隱蔽并且很危險的
2.子->父組件間的數據傳遞
在vue中子向父傳遞數據一般用$emit自定義事件,在父組件中監聽這個事件并在回調中寫相關邏輯
// 父組件監聽子組件定義的事件
<editor :inputIndex="index" @editorEmit='editorEmit'></editor>
// 子組件需要返回數據時執行,并可以傳遞數據
this.$emit('editorEmit', data)
那么問題來了,我是不是真的有必要去向父組件返回這個數據,用自定義事件可以在當子組件想傳遞數據或向子組件傳遞的數據有變化需要重新傳遞時執行,那么另外一種場景,父組件需要子組件的一個數據但子組件并不知道或者說沒有能力在父組件想要的時候給父組件,那么這個時候就要用到組件的一個選項ref:
<editor ref="editor" @editorEmit='editorEmit'></editor>
父組件在標簽中定義ref屬性,在js中直接調用this.$refs.editor就是調用整個子組件,子組件的所有內容都能通過ref去調用,當然我們并不推薦因為這會使數據看起來非常混亂,
所以我們可以在子組件中定義一種專供父組件調用的函數,,比如我們在這個函數中返回子組件data中某個數據,當父組件想要獲取這個數據就直接主動調用ref執行這個函數獲取這個數據,這樣能適應很大一部分場景,邏輯也更清晰一點
另外,父向子傳遞數據也可以用ref,有次需要在一個父組件中大量調用同一個子組件,而每次調用傳遞的prop數據都不同,并且傳遞數據會根據之后操作變化,這樣我需要在data中定義大量相關數據并改變它,我可以直接用ref調用子組件函數直接把數據以參數的形式傳給子組件,邏輯一下子清晰了
如果調用基礎組件可以在父組件中調用ref執行基礎組件中暴露的各種功能接口,比如顯示,消失等
3.兄弟組件間的數據傳遞
vue中兄弟組件間的通信是很不方便的,或者說不支持的,那么父子組件中都有什么通信方式呢
- 路由URL參數
在傳統開發時我們常常把需要跨頁面傳遞的數據放到url后面,跳轉到另外頁面時直接獲取url字符串獲取想要的參數即可,在vue跨組件時一樣可以這么做,
// router index.js 動態路由
{
path:'/params/:Id',
component:Params,
name:Params
}
// 跳轉路由
<router-link :to="/params/12">跳轉路由</router-link>
在跳轉后的組件中用$route.params.id去獲取到這個id參數為12,但這種只適合傳遞比較小的數據,數字之類的
Bus通信
在組件之外定義一個bus.js作為組件間通信的橋梁,適用于比較小型不需要vuex又需要兄弟組件通信的
bus.js中添加如下
import Vue from 'vue'
export default new Vue
組件中調用bus.js通過自定義事件傳遞數據
import Bus from './bus.js'
export default {
methods: {
bus () {
Bus.$emit('msg', '我要傳給兄弟組件們')
}
}
}
兄弟組件中監聽事件接受數據
import Bus from './bus.js'
export default {
mounted() {
Bus.$on('msg', (e) => {
console.log(e)
})
}
}
注:以上兩種使用場景并不高所以只是簡略提一下,這兩點都是很久以前寫過,以上例子網上直接搜集而來如有錯誤,指正
- Vuex集中狀態管理
vuex是vue的集中狀態管理工具,對于大型應用統一集中管理數據,很方便,在此對vuex的用法并不過多介紹只是提一下使用過程中遇到的問題
規范:對于多人開發的大型應用規范的制定是至關重要的,對于所有人都會接觸到的vuex對其修改數據調用數據都應有一個明確嚴格的使用規范
vuex分模塊:項目不同模塊間維護各自的vuex數據
限制調用:只允許action操作數據,getters獲取數據,使用mapGetters,mapActions輔助函數調用數據
- 對于vuex的使用場景也有一些爭論,有人認為正常組件之間就是要用父子組件傳值的方式,即使子組件需要使vuex中的數據也應該由父組件獲取再傳到子組件中,但有的時候組件間嵌套很深,只允許父組件獲取數據并不是一個方便的方法,所以對于祖先元組件與子組件傳值又有了新問題,vue官網也有一些方法解決,如下
4.組件深層嵌套,祖先組件與子組件之間的數據傳遞
provide/inject
除了正常的父子組件傳值外,vue也提供了provide/inject
這對選項需要一起使用,以允許一個祖先組件向其所有子孫后代注入一個依賴,不論組件層次有多深,并在起上下游關系成立的時間里始終生效
官網實例
// 父級組件提供 'foo'
var Provider = {
provide: {
foo: 'bar'
},
}
// 子組件注入 'foo'
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
}
- provide 選項應該是一個對象或返回一個對象的函數。該對象包含可注入其子孫的屬性。
一個字符串數組,或
- 一個對象,對象的 key 是本地的綁定名,value 是:
在可用的注入內容中搜索用的 key (字符串或 Symbol),或
一個對象,該對象的:
from 屬性是在可用的注入內容中搜索用的 key (字符串或 Symbol)
default 屬性是降級情況下使用的 value
提示:provide 和 inject 綁定并不是可響應的。這是刻意為之的。然而,如果你傳入了一個可監聽的對象,那么其對象的屬性還是可響應的。
具體細節移步vue相關介紹****https://cn.vuejs.org/v2/api/#...
provide/inject還未在項目中應用過,后面會做嘗試