Vue優(yōu)點(diǎn)
數(shù)據(jù)驅(qū)動(dòng)和組件化
- 數(shù)據(jù)驅(qū)動(dòng):自動(dòng)計(jì)算屬性和追蹤依賴的模板表達(dá)
- 組件化:可復(fù)用,解耦的組件來構(gòu)造頁面
Vue生命周期
- beforeCreated:創(chuàng)建前。數(shù)據(jù)觀測和初始化事件未開始。
- created:創(chuàng)建后。完成數(shù)據(jù)觀測、屬性和方法的處理。以及初始化事件。
- beforeMounted:掛載前。掛載開始前被調(diào)用,render函數(shù)被首次調(diào)用。完成編譯模板,data里的數(shù)據(jù)和模板生成HTML
- mounted:掛在后。el屬性被新創(chuàng)建出來的vm.$el代替,并掛載到實(shí)例上之后被調(diào)用。
- beforeUpdate:虛擬DOM重新渲染之前
- update:虛擬DOM重新渲染之后
- beforeDestory:實(shí)例被摧毀之前調(diào)用,實(shí)例仍然可用。
- destoryed:實(shí)例被摧毀之后調(diào)用;調(diào)用時(shí),所有的監(jiān)聽器都會(huì)被移除。
Vue的指令有哪些
v-if/v-else/v-else-if/v-bind(:)/v-on(@)/v-show/v-html/v-text/v-model/v-for/v-once;
Vue常用修飾符
.prevent: 提交事件不再重載頁面
.stop: 阻止單擊事件冒泡
.self: 當(dāng)事件發(fā)生在該元素本身而不是子元素的時(shí)候會(huì)觸發(fā)
.capture: 事件偵聽,事件發(fā)生的時(shí)候會(huì)調(diào)用
.once: 跟v-once作用類似,只渲染一次,第二次不會(huì)執(zhí)行
watch和computed的區(qū)別
- computed:計(jì)算屬性。通過其他變量來獲取另一個(gè)屬性,具有緩存。計(jì)算屬性只有在他們依賴的屬性改變的時(shí)候才會(huì)重新計(jì)算求值
- watch:監(jiān)聽某一屬性。回調(diào)里面可以傳入新舊值。
watch詳解
watch:可以用來監(jiān)測VUE實(shí)例上數(shù)據(jù)的變動(dòng);
- 簡單運(yùn)用:
watch: function(newVal,OldVal) {}; - 復(fù)雜運(yùn)用:
1.immediate: 由于watch有一個(gè)特點(diǎn),就是最初綁定的時(shí)候 不會(huì)去執(zhí)行,只有當(dāng)值發(fā)生改變的時(shí)候 才會(huì)去執(zhí)行監(jiān)聽計(jì)算。如果我們想要在最初綁定的時(shí)候就去執(zhí)行監(jiān)聽計(jì)算的話,就需要在watch中設(shè)置immediate屬性值為true;
2.deep: 比如說我們data里面監(jiān)聽的是一個(gè)對象,但是我們想要監(jiān)聽的是object.a的值的變化,就監(jiān)聽不到的。由于handler只監(jiān)聽data里的obj的變化。這時(shí)候我們就需要這是deep屬性為true。deep就是指深度觀察,監(jiān)聽器會(huì)逐一的遍歷下去,給obj的每一屬性都加上監(jiān)聽器,但是這樣是非常耗性能的。
watch: {
handler:functio(newVal,OldVal) {},
immediate:true,
}
Vue中如何獲取DOM
只有在mounted階段之后才能獲取dom
- ref:ref被用來給元素或者是子組件注冊引用信息,引用信息將會(huì)注冊在父組件的$refs對象上。如果是在普通的DOM上用,那么指向的就是普通的元素,如果是在子組件上使用,指向的就是子組件實(shí)例;
- 選擇器獲取:比如docment.querySelector("#id");
組件之間的傳值
- 父組件傳給子組件:子組件通過props接收傳遞的數(shù)據(jù)
- 子組件傳遞給父組件:通過$emit傳遞參數(shù)
- 同級之間傳遞:eventBus,創(chuàng)建一個(gè)事件中心,相當(dāng)于一個(gè)中轉(zhuǎn)站,可以用它來傳遞數(shù)據(jù)和接收數(shù)據(jù);
- vuex
- 祖?zhèn)鲗O:通過provide
//祖
provide: {
foo: {
a: 1,
b: 2,
c: 3
}
}
//在孫組件中
inject: ["foo"],
data() {
return {
bar: this.foo.a
};
},
created() {
console.log(1);
console.log(this.bar); // => "bar"
}
Vue-router的鉤子函數(shù)
- 全局導(dǎo)航鉤子函數(shù)
- router.beforeEach(to, from, next),
- router.beforeResolve(to, from, next),
- router.afterEach(to, from ,next)
- 組件內(nèi)鉤子:
- beforeRouterEnter
- beforeRouterUpdate
- beforeRouterLeave
- 單獨(dú)路由獨(dú)享組件:
beforeEnter
完整的導(dǎo)航解析流程
- 導(dǎo)航被觸發(fā)
- 在失活的組件里調(diào)用離開守衛(wèi)
- 調(diào)用全局的 beforeEach 守衛(wèi)
- 在重用的組件里調(diào)用 beforeRouteUpdate 守衛(wèi)
- 在路由配置里調(diào)用 beforEnter
- 解析異步路由組件
- 在被激活的組件里調(diào)用 beforeRouteEnter
- 調(diào)用全局的 beforeResolve 守衛(wèi)
- 導(dǎo)航被確認(rèn)
- 調(diào)用全局的 afterEach 鉤子
- 觸發(fā) DOM 更新
- 在創(chuàng)建好的實(shí)例調(diào)用 beforeRouteEnter 守衛(wèi)中傳給 next 的回調(diào)函數(shù)
vue組件中data為什么必須是函數(shù)
因?yàn)橐粋€(gè)組件是可以共享的,但他們的data是私有的,所以每個(gè)組件都要return一個(gè)新的data對象,返回一個(gè)唯一的對象,不要和其他組件共用一個(gè)對象
從源碼看Vue中數(shù)組的問題
由于JavaScript的限制,Vue不能做以下操作
- 利用索引直接設(shè)置一個(gè)數(shù)組項(xiàng)。vm.items[indexofitem] = newValue
- 修改數(shù)組的長度,vm.items.length = newLength
解決方法
在源代碼中,采用數(shù)組變異思路,首先對功能進(jìn)行擴(kuò)展,之后進(jìn)行數(shù)組劫持。
功能擴(kuò)展思路:
- 創(chuàng)建一個(gè)繼承原Array的新函數(shù)對象。
- 重新定義函數(shù)對象
- 在新定義的函數(shù)中調(diào)用原函數(shù)
// 變異方法名稱
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
const arrayProto = Array.prototype
// 繼承原有數(shù)組的方法
const arrayMethods = Object.create(arrayProto)
mutationMethods.forEach(method => {
// 緩存原生數(shù)組方法
const original = arrayProto[method]
arrayMethods[method] = function (...args) {
const result = original.apply(this, args)
console.log('執(zhí)行響應(yīng)式功能')
return result
}
共有7種變異函數(shù)。
數(shù)據(jù)劫持思路
使用原型鏈,將普通函數(shù)指向我們所擴(kuò)展的新組對象
let arr = []
// 通過隱式原型繼承arrayMethods
arr.__proto__ = arrayMethods
// 執(zhí)行變異后方法
arr.push(1)
Vue 的變異數(shù)組從本質(zhì)上是來說是一種裝飾器模式,通過學(xué)習(xí)它的原理,我們在實(shí)際工作中可以輕松處理這類保持原有功能不變的前提下對其進(jìn)行功能拓展的需求。
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// vm.$set,Vue.set的一個(gè)別名
vm.$set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
vm.items.splice(newLength)
在vue中有的時(shí)候是不能獲取到數(shù)組的,那么vue源碼是怎樣去處理這些數(shù)組,并獲取到的呢?
答:組件處理 vue做了攔截,重寫了數(shù)組的方法,最終還是通過數(shù)據(jù)劫持獲取到的。
Vue 怎么用 vm.$set() 解決對象新增屬性不能響應(yīng)的問題 ?
vue 無法檢測到對象屬性的添加或者刪除。所以Vue提供了Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value) 來實(shí)現(xiàn)為對象添加響應(yīng)式屬性。
源碼實(shí)現(xiàn)思想
如果目標(biāo)是數(shù)組,直接使用擴(kuò)展數(shù)組的splice方法觸發(fā)響應(yīng)式。
如果目標(biāo)是對象,會(huì)先判讀屬性是否存在、對象是否是響應(yīng)式,最終如果要對屬性進(jìn)行響應(yīng)式處理,則是通過調(diào)用 defineReactive 方法進(jìn)行響應(yīng)式處理( defineReactive 方法就是 Vue 在初始化對象時(shí),給對象屬性采用 Object.defineProperty 動(dòng)態(tài)添加 getter 和 setter 的功能所調(diào)用的方法)
父子組件生命周期調(diào)用順序
父組件beforeMounted階段之后進(jìn)入子組件,子組件完成mounted之后繼續(xù)父組件的周期。當(dāng)子組件觸發(fā)數(shù)據(jù)更新,先觸發(fā)父組件beforeUpdate,之后觸發(fā)子組件beforeUpdate,然后觸發(fā)子組件updated,最后觸發(fā)父組件updated。
Vuex
- state: vuex store實(shí)例的根狀態(tài)對象,用于定位共享的狀態(tài)
- action: 動(dòng)作,執(zhí)行本地操作或者異步操作(相當(dāng)于state的methods)。action可以進(jìn)行異步操作。store.dispatch()來執(zhí)行action
- Mutations: 修改器。唯一只用于修改state。store.commit()來執(zhí)行。
- getter:讀取器,外部程序通過他獲取變量的具體值,或者是在取值前做一些計(jì)算,可以認(rèn)為是store的計(jì)算屬性。
vuex提供了三種輔助函數(shù)用于獲取、修改vuex:
mapState、mapGetters、mapActions
即將vuex的變量或者方法映射到vue組件this指針上。
methods: {
//下述中的 ... 是拓展運(yùn)算符
// 使用 [] 是解構(gòu)賦值
...mapActions([
'increment', // 將 `this.increment()` 映射為 `this.$store.dispatch('increment')`
// `mapActions` 也支持載荷:
'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // 將 `this.add()` 映射為 `this.$store.dispatch('increment')`
})
}
Vue-router原理
spa實(shí)現(xiàn)方式分為三種
hash模式
地址欄中#符號。特點(diǎn)是hash雖然出現(xiàn)在瀏覽器url中,但是不會(huì)包含在HTTP請求中,對后端沒有影響,不會(huì)重新加載頁面
history模式
利用HTML5新增的pushState()和replaceState()實(shí)現(xiàn)。這兩個(gè)方法應(yīng)用于瀏覽器的歷史記錄棧。但是需要后端對路由進(jìn)行配置,重定向到Vue打包生成的index.html的頁面上尋找相應(yīng)的代碼,否則會(huì)報(bào)錯(cuò)。
window.history.pushState(state, title, url)
- state: 一個(gè)與指定網(wǎng)址相關(guān)的狀態(tài)對象,popstate事件觸發(fā)時(shí),該對象會(huì)傳入回調(diào)函數(shù)
- title:新頁面的標(biāo)題,大部分瀏覽器不支持這個(gè)
- url: 新的網(wǎng)址,必須是與當(dāng)前頁面同一個(gè)域名。瀏覽器顯示的地址
重點(diǎn)是 pushstate方法不會(huì)觸發(fā)頁面刷新,只是會(huì)導(dǎo)致history對象發(fā)生變化,地址欄會(huì)有反應(yīng)
window.onpopstate = function (event) {}
popstate事件會(huì)在點(diǎn)擊后退、前進(jìn)按鈕(或調(diào)用history.back()、history.forward()、history.go()方法)時(shí)觸發(fā)。前提是不能真的發(fā)生了頁面跳轉(zhuǎn),而是在由history.pushState()或者h(yuǎn)istory.replaceState()形成的歷史節(jié)點(diǎn)中前進(jìn)后退
注意:用history.pushState()或者h(yuǎn)istory.replaceState()不會(huì)觸發(fā)popstate事件。
在Vue中需要這樣配置node:'history'
abstract模式
abstract模式是使用一個(gè)不依賴于瀏覽器的瀏覽歷史虛擬管理后端。
根據(jù)平臺(tái)差異可以看出,在 Weex 環(huán)境中只支持使用 abstract 模式。 不過,vue-router 自身會(huì)對環(huán)境做校驗(yàn),如果發(fā)現(xiàn)沒有瀏覽器的 API,vue-router 會(huì)自動(dòng)強(qiáng)制進(jìn)入 abstract 模式,所以 在使用 vue-router 時(shí)只要不寫 mode 配置即可,默認(rèn)會(huì)在瀏覽器環(huán)境中使用 hash 模式,在移動(dòng)端原生環(huán)境中使用 abstract 模式。 (當(dāng)然,你也可以明確指定在所有情況下都使用 abstract 模式)
注意
- hash模式不會(huì)生成404的錯(cuò)誤
- history模式需要后端支持。指定的路徑需要返回對應(yīng)的HTML頁面。