吃透 Vue 項目開發實踐|16個方面深入前端工程化開發技巧【下】



前言

前面兩篇文章總結了 Vue 開發的大部分技巧和內容,最後一篇文章來對它進行一個收尾

這篇文章我們來談談一些 Vue 理解和實踐要求高一點的問題

首先是生命週期這一塊內容,隨著實踐越多它的意義越大,理解也越深刻

mixin 功能強大,對代碼複用組織都有很高的要求,算是 Vue 後期發力的高級技巧

服務端渲染可能是學習 Vue 最後一塊陣地了,對於 SPA 框架的一個里程碑

最後,總結一下我在使用 Vue 中使用的技巧和經驗

常規操作,先點贊後觀看哦!你的點贊是我創作的動力之一!

前情提要

我將從 16 個方面來論述 Vue 開發過程中的一些技巧和原理。如果你還未觀看上節文章,可以移步至

本篇概覽

Vue 生命週期

什麼是 Vue 生命週期?

Vue 生命週期大概就是:一個從 Vue 實例的創建到組件銷燬的一個的過程。

具體情況下,我們分為幾個核心的階段,並且每個階段都有一套鉤子函數來執行我們需要的代碼。

生命週期階段與鉤子

我們整理分類一下這些生命週期鉤子,為了記憶方便分為 4 大核心階段:

方便讀者記憶,這裡儘量使用圖示:

可以看到每一個階段中的鉤子命名都很好記憶,階段開始前使用 beforeXxx,階段後結束後使用xxxed

除這 8 個核心鉤子,另外還有 3 個新增功能型鉤子,目前總共是 11 個鉤子 順帶提一下這 3 個鉤子的功能

組件緩存,activated 與 deactivated,這兩個鉤子也是一對的,分別表示被 keep-alive 緩存的組件激活和停用時調用。組件錯誤捕獲,errorCaptured,對組件中出現對異常錯誤進行處理,使用較少。

圖解生命週期

我們看看官方的圖解,在 Vue 教程實例這節 官方教程直接跳轉

官方圖解

官方圖解並沒有詳細解釋這張圖。我猜一方面原因是這個圖裡面涉及的細節都是在 vue 源碼裡面體現,要真正解釋起來也沒那麼簡單。另一方面是希望我們多在實踐中去理解裡面的意義。

Vue 源碼基本流程

對於上面那張圖的理解,我們需要對 Vue 源碼進行梳理,才能真正的理解。大概根據現有的源碼,我梳理了一下大致的流程:

我們可以清楚地看到,從 Vue 實例創建、組件掛載、渲染的一些過程中,有著明顯的週期節點。

簡化文字圖解

結合上面源碼的流程和相關實踐,簡化每一個階段做了哪些時期,每一個鉤子裡面是組件處於什麼狀態。

實踐驗證一下生命週期

提出問題

下面我們提出一些問題:

什麼時期創建 el ?什麼時期掛載 data ?什麼時期可以訪問 dom ?什麼情況下組件會更新?更新是同步更新還是異步更新?什麼情況下組件會被銷燬?銷燬組件後,還可以訪問哪些內容?

編寫代碼

首先寫一個小 demo,打印關鍵組件信息

<code><template>



{{message}}


/<template>


複製代碼/<code>增加核心的 8 個生命週期鉤子,分別調用打印方法

<code> // ...
beforeCreate() {
this.printComponentInfo('beforeCreate')
},
created() {


this.printComponentInfo('created')
},
beforeMount() {
this.printComponentInfo('beforeMount')
},
mounted() {
this.printComponentInfo('mounted')
},
beforeUpdate() {
this.printComponentInfo('beforeUpdate')
},
updated() {
this.printComponentInfo('updated')
},
beforeDestroy() {
this.printComponentInfo('beforeDestroy')
},
destroyed() {
this.printComponentInfo('destroyed')
},
// ...
複製代碼/<code>

創建階段

beforeCreate 中methods中方法直接報錯無法訪問,直接訪問 el 和 data 後

發現只能訪問到 watch, el 和 data 均不能訪問到

created 時期 el 無法訪問到,但是可以訪問到 data 了

掛載階段

beforeMount 中可以訪問 data 但是仍然訪問不到 el

mounted 中可以訪問到 el 了

首次加載頁面,更新階段和銷燬階段到鉤子都未觸發

更新階段

我們增加一行代碼

<code>this.message = this.message + 1
複製代碼/<code>

如果增加在 created 階段,發現 update鉤子仍然未觸發,但是 el 和 data 的值都變成了 2

如果增加在 mounted 階段,發現 update鉤子此時觸發了

銷燬階段

怎樣觸發銷燬的鉤子呢? 大概有這幾種方法

手動調用 $destoryv-if 與 v-for 指令,(v-show 不行)路由切換和關閉或刷新瀏覽器 我們在 mounted 鉤子裡面增加一行代碼手動銷燬當前組件,或者跳轉路由

<code>this.$destory('lifecycle')
複製代碼/<code>

發現beforeDestory 和 destoryed 都觸發了,而且el、data都一樣還是可以訪問到

生命週期鉤子常見使用的場景

beforeCreate 謹慎操作 this

beforeCreate 無法訪問到 this 中的 data、method

<code>// 錯誤實例
beforeCreate() {
// 允許
console.log('ok')
// 不允許
this.print() // 報錯找不到
this.message = 1 // 報錯找不到

}
複製代碼/<code>

請求應放在 created 鉤子中

created 可以訪問 this,但無法訪問 dom,dom 未掛載

<code>created() {
// 允許並推薦
this.$http.get(xxx).then(res => {
this.data = res.data
})
// 不允許
this.$el
this.$ref.demo
const a = document.getElementById('demo')
}
複製代碼/<code>

操作 DOM 代碼應放在 mounted 鉤子中

mounted 已經掛載 dom,可以訪問 this

<code>mounted() {
// 允許
this.$el
this.$ref.demo
let a = document.getElementById('')
}
複製代碼/<code>

生命週期相關demo 代碼見github-lifecycle-demo

理解併合理使用 mixin

什麼是 mixin(混入)

當組件使用混入對象時,所有混入對象的選項將被“混合”進入該組件本身的選項 大致原理就是將外來的組件、方法以某種方式進行合併。合併的規則有點像繼承和擴展。

當組件和混入對象含有同名選項時,這些選項將以恰當的方式進行“合併”

我們看一下一個組件裡面有哪些東西是可以合併的

<code>// mixins/demo
export default {
data() {
return {}
},
mounted() {},
methods: {},
computed: {},
components: {},
directives: {}
}


複製代碼/<code>

data、methods、computed、directives、components 生命週期鉤子

沒錯這些都可以混入

<code>import demoMixin form '@/mixins/demo'
export default {
mixins: [demoMixin]
}
複製代碼/<code>

這樣看來,很多頁面重複的代碼我們都可以直接抽取出來

或者是封裝成一個公共的 mixin

比如我們做 H5 頁面,裡面很多短信驗證的邏輯固有邏輯,但是需要訪問到 this。使用工具函數肯定不行。

這時候就可以考慮使用 mixin,封裝成一個具有響應式的模塊。供需要的地方進行引入。

mixin 規則

首先是優先級的問題,當重名選項時選擇哪一個為最後的結果

默認規則我這裡分為 3 類

data 混入: 以當前組件值為最後的值生命週期鉤子: 保留所有鉤子,先執行 mixins 的,後執行當前組件的methods、computed、directives、components 這種健值對形式,同名key,統統以當前組件為準

當然如果想改變規則,也可以通過配置來改變規則

<code>Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) {
// 返回合併後的值
}
複製代碼/<code>

mixin 的好處

我們知道 Vue 最能複用代碼的就是組件。一般情況,我們通過 props 來控制組件的,將原有組件封裝成 HOC 高階組件。而控制 props 的生成不一樣的功能的代碼還是寫在基礎組件裡。

<code><template>
div.dib
van-button.btn(
@click="$emit('click')"
:class="getClass" v-bind="$attrs"
:)
slot
/<template>

/<code>