beforeCreate
,在實例初始化以後,數據觀測 (data observer) 和 event/watcher 事件配置以前被調用。created
,在實例建立完成後被當即調用。在這一步,實例已完成如下的配置:數據觀測 (data observer),屬性和方法的運算,watch/event 事件回調。然而,掛載階段還沒開始,$el 屬性目前尚不可用。beforeMount
,在掛載開始以前被調用:相關的 render 函數首次被調用。mounted
,實例被掛載後調用,這時 el
被新建立的 vm.$el
替換了。 若是根實例掛載到了一個文檔內的元素上,當mounted被調用時 vm.$el
也在文檔內。beforeUpdate
,數據更新時調用,發生在虛擬 DOM 打補丁以前。這裏適合在更新以前訪問現有的 DOM,好比手動移除已添加的事件監聽器。updated
,因爲數據更改致使的虛擬 DOM 從新渲染和打補丁,在這以後會調用該鉤子。activated
,被 keep-alive 緩存的組件激活時調用。deactivated
,被 keep-alive 緩存的組件停用時調用。beforeDestroy
,實例銷燬以前調用。在這一步,實例仍然徹底可用。destroyed
,實例銷燬後調用。該鉤子被調用後,對應 Vue 實例的全部指令都被解綁,全部的事件監聽器被移除,全部的子實例也都被銷燬。errorCaptured
,當捕獲一個來自子孫組件的錯誤時被調用。參考:cn.vuejs.org/v2/api/#選項-…javascript
如下是整個生命週期圖示:html
參考:cn.vuejs.org/v2/guide/in…vue
被替換java
重命名react
新增的編程
新增的如下2個方便調試 debug
的回調鉤子:api
參考:vue-composition-api-rfc.netlify.com/api.html#li…緩存
特別說明bash
因爲 Vue3.x 是兼容 Vue2.x 的語法的,所以爲了保證 Vue2.x 的語法能正常在 Vue3.x 中運行,大部分 Vue2.x 的回調函數仍是獲得了保留。好比:雖然 beforeCreate
、 created
被 setup()
函數替代了,也就是說在 Vue3.x 中建議使用 setup()
,而不是舊的API,可是若是你要用,代碼也是正常執行的。app
可是,如下2個生命週期鉤子函數被更名後,在 Vue3.x 中將不會再有 beforeDestroy
和 destroyed
。
另外,假如 Vue3.x 在 Q2 如期 Release 的話,你們必定要注意,在混合使用 Vue2.x 和 Vue3.x 語法的時候,特別要注意這2套API的回調函數的執行順序。
若是你們看了我上一篇文章 VUE 3.0 學習探索入門系列 - 糾結要不要升級到Vue3.0?該如何升級?(5),我說過當 Vue3.x 正式 Realease 之後,我可能會先使用 Vue2.x + Composition API
,完了再使用 Vue3.x
搭建新的基礎框架。
那我今天主要關心的一個問題,一旦我使用 Vue2.x + Composition API
進入過渡期後,當我再使用 Vue3.x
搭建新的框架,那麼以前的基礎組件還能使用麼?
先測試下生命週期函數的執行順序。
以下示例,在 Vue2.x 中引入兼容包 Composition API,而後Vue2.x 和 Vue3.x 的生命週期函數混合使用。
<template>
<div>
<p> {{ id }} </p>
<p> {{ name }} </p>
</div>
</template>
<script> import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from '@vue/composition-api'; export default { setup() { const id = ref(1) console.log('setup') onBeforeMount(() => { console.log('onBeforeMount') }) onMounted(() => { console.log('onMounted') }) onBeforeUpdate(() => { console.log('onBeforeUpdate') }) onUpdated(() => { console.log('onUpdated') }) onBeforeUnmount(() => { console.log('onBeforeUnmount') }) onUnmounted(() => { console.log('onUnmounted') }) // 測試 update 相關鉤子 setTimeout(() => { id.value = 2 }, 2000) return { id } }, data() { console.log('data') return { name: 'lilei' } }, beforeCreate() { console.log('beforeCreate') }, created() { console.log('created') }, beforeMount() { console.log('beforeMount') }, mounted() { console.log('mounted') setTimeout(() => { this.id = 3; }, 4000) }, beforeUpdate() { console.log('beforeUpdate') }, updated() { console.log('updated') }, beforeUnmount() { }, unmounted() { console.log('unmounted') }, beforeDestroy() { console.log('beforeDestroy') }, destroyed() { console.log('destroyed') } } </script>
複製代碼
執行結果:
1. beforeCreate
2. setup
3. data
4. created
5. beforeMount
6. onBeforeMount
7. mounted
8. onMounted
9. beforeUpdate
10. onBeforeUpdate
11. updated
12. onUpdated
13. beforeDestroy
14. onBeforeUnmount
15. destroyed
16. onUnmounted
複製代碼
結論
在 Vue2.x
中經過補丁形式引入 Composition API
,進行 Vue2.x
和 Vue3.x
的回調函數混用時:Vue2.x 的回調函數會相對先執行,好比:mounted
優先於 onMounted
。
如下直接使用 Vue3.x 語法,看看其在兼容 Vue2.x 狀況下,生命週期回調函數混合使用的執行順序。
<template>
<div>
<p> {{ id }} </p>
<p> {{ name }} </p>
</div>
</template>
<script> import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onRenderTracked, onRenderTriggered } from 'vue'; export default { setup() { const id = ref(1) console.log('setup') onBeforeMount(() => { console.log('onBeforeMount') }) onMounted(() => { console.log('onMounted') }) onBeforeUpdate(() => { console.log('onBeforeUpdate') }) onUpdated(() => { console.log('onUpdated') }) onBeforeUnmount(() => { console.log('onBeforeUnmount') }) onUnmounted(() => { console.log('onUnmounted') }) onRenderTracked(() => { console.log('onRenderTracked') }) onRenderTriggered(() => { console.log('onRenderTriggered') }) // 測試 update 相關鉤子 setTimeout(() => { id.value = 2; }, 2000) return { id } }, data() { console.log('data') return { name: 'lilei' } }, beforeCreate() { console.log('beforeCreate') }, created() { console.log('created') }, beforeMount() { console.log('beforeMount') }, mounted() { console.log('mounted') setTimeout(() => { this.id = 3; }, 4000) }, beforeUpdate() { console.log('beforeUpdate') }, updated() { console.log('updated') }, beforeUnmount() { console.log('beforeUnmount') }, unmounted() { console.log('unmounted') } } </script>
<style scoped> </style>
複製代碼
執行結果:
1. beforeCreate
2. data
3. created
4. onRenderTracked
5. onRenderTracked
6. onBeforeMount
7. beforeMount
8. onMounted
9. mounted
10. onRenderTriggered
11. onRenderTracked
12. onRenderTracked
13. onBeforeUpdate
14. beforeUpdate
15. onUpdated
16. updated
17. onBeforeUnmount
18. beforeUnmount
19. onUnmounted
20. unmounted
複製代碼
結論
在 Vue3.x
中,爲了兼容 Vue2.x
的語法,全部舊的生命週期函數獲得保留(除了 beforeDestroy
和 destroyed
),當生命週期混合使用時:Vue3.x 的生命週期相對優先於 Vue2.x 的執行,好比:onMounted
比 mounted
先執行。
綜上所述:
Vue2.x
中經過補丁形式引入 Composition API
,進行 Vue2.x
和 Vue3.x
的回調函數混用時:Vue2.x 的回調函數會相對先執行,好比:mounted
優先於 onMounted
。Vue3.x
中,爲了兼容 Vue2.x
的語法,全部舊的生命週期函數獲得保留(除了 beforeDestroy
和 destroyed
)。當生命週期混合使用時:Vue3.x 的生命週期相對優先於 Vue2.x 的執行,好比:onMounted
比 mounted
先執行。經過對比能夠得出:當你的主版本是哪一個,當生命週期混用時,誰的回調鉤子就會相對優先執行。
因此,這裏就會有點坑!爲了給減少之後沒必要要的麻煩,若是你們在 Vue2.x
中經過補丁形式引入 Composition API
的使用的時候,建議:
如下內容,大部分參考官方: Vue Composition API
setup
是 Composition API 的核心,能夠說也是整個 Vue3.x 的核心。
setup
就是將 Vue2.x 中的 beforeCreate
和 created
代替了,以一個 setup
函數的形式,能夠靈活組織代碼了。setup
還能夠 return 數據或者 template,至關於把 data
和 render
也一併代替了!爲何說 setup
靈活了呢?由於在這個函數中,每一個生命週期能夠是一個函數,在裏面執行,以函數的方式編程。
下面看看其幾個核心特色:
1 返回一組數據給template
<template>
<div>{{ count }} {{ object.foo }}</div>
</template>
<script> import { ref, reactive } from 'vue' export default { setup() { const count = ref(0) const object = reactive({ foo: 'bar' }) // expose to template return { count, object } } } </script>
複製代碼
2 使用jsx語法
<script> import { h, ref, reactive } from 'vue' export default { setup() { const count = ref(0) const object = reactive({ foo: 'bar' }) return () => h('div', [ h('p', { style: 'color: red' }, count.value), h('p', object.foo) ]) } } </script>
複製代碼
3 Typescript Type
interface Data {
[key: string]: unknown
}
interface SetupContext {
attrs: Data
slots: Slots
emit: ((event: string, ...args: unknown[]) => void)
}
function setup( props: Data, context: SetupContext ): Data 複製代碼
4 參數
須要注意的是,在 setup
函數中,取消了 this
!兩方面的緣由:
因爲 setup
是一個入口函數,本質是面向函數編程了,而 this
是面向對象的一種體現!在徹底基於函數的編程世界中,這個 this
就很難在能達到跟 Vue2.x 那種基於 OOP 思想的 Options 機制的實現的效果。
一樣是基於函數式編程,那麼若是加上 this,對於新手而言,原本 this 就很差理解,這時候就更加懵逼了。好比:
setup() {
function onClick() {
this // 若是有 this,那麼這裏的 this 可能並非你期待的!
}
}
複製代碼
取消了 this
,取而代之的是 setup
增長了2個參數:
setup(props, context) {
// props
// context.attrs
// context.slots
// context.emit
}
複製代碼
也許你會有疑問,僅有這2個參數就夠了麼?夠了。你在 Vue2.x 的時候,this
沒法就是獲取一些 data、props、computed、methods 等麼?
其實,這2個參數都是外部引入的,這個沒辦法只能帶入初始化函數中。除此以外,你組件上用到的全部 this 能獲取的數據,如今都至關於在 setup 中去定義了,至關於局部變量同樣,你還要 this 幹嗎呢?
好比:
setup(props, context) {
// data
const count = ref(1)
// 生命週期鉤子函數
onMounted(() => {
console.log('mounted!')
})
// 計算函數
const plusOne = computed(() => count.value + 1)
// methods 方法
const testMethod = () => {
console.log('methods');
}
// return to template
return {
count,
testMethod
}
}
複製代碼
一切在 setup
中,都至關於變成了 局部變量
了,你還要 this
幹嗎?
固然,若是你要講 Vue2.x 和 Vue3.x 混用!那就很彆扭了,之後用 this,之後又不能用,你本身也會懵逼,因此在此建議:雖然Vue3.x兼容Vue2.x語法,可是不建議混合使用各自語法!
被 reactive
方法包裹後的 對象
就變成了一個代理對象,至關於 Vue2.x 中的 Vue.observable()
。也就能夠實現頁面和數據之間的雙向綁定了。
這個包裹的方法是 deep
的,對全部嵌套的屬性都生效。
注意: 通常約定 reactive
的參數是一個對象,而下文提到的 ref
的參數是一個基本元素。但若是反過來也是能夠的,reactive
其實能夠是任意值,好比:reactive(123)
也是能夠變成一個代理元素,能夠實現雙向綁定。
好比:
<template>
<div>
<p>{{ obj1.cnt }}</p>
<p>{{ obj2.cnt }}</p>
</div>
</template>
<script> import { reactive } from 'vue' setup() { // 普通對象 const obj1 = { cnt: 1 } // 代理對象 const obj2 = reactive({ cnt: 1 }) obj1.cnt++ obj2.cnt++ return { obj1, obj2 } } </script>
複製代碼
頁面顯示結果:
1
2
複製代碼
能夠看到,普通對象屬性更新時,頁面是不會同步更新的。只有代理對象,才能夠實現雙向綁定。
被 ref
方法包裹後的 元素
就變成了一個代理對象。通常而言,這裏的元素參數指 基本元素
或者稱之爲 inner value
,如:number, string, boolean,null,undefied 等,object 通常不使用 ref
,而是使用上文的 reactive
。
也就是說 ref
通常適用於某個元素的;而 reactive
適用於一個對象。
ref
也就至關於把單個元素轉換成一個 reactive
對象了,對象默認的鍵值名是:value
。
好比:
setup() {
const count = ref(100)
}
複製代碼
被 ref
包裹後的元素變成一個代理對象,效果就至關於:
setup() {
const count = reactive({
value: 100
})
}
複製代碼
由於變成了一個代理對象,因此取值的時候須要 .value
:
setup() {
const count = ref(100)
console.log(count.value) // output: 100
}
複製代碼
另外 ref
的結果在 template
上使用時,會自動打開 unwrap
,不須要再加 .value
<template>
<div>{{ count }}</div>
</template>
<script> export default { setup() { return { count: ref(0) } } } </script>
複製代碼
如下是一些基本元素 ref
的結果:
setup() {
console.log(ref(100).value) // output: 100
console.log(ref('test').value) // output: test
console.log(ref(true).value) // output: true
console.log(ref(null).value) // output: null
console.log(ref(undefined).value) // output: undefined
console.log(ref({}).value) // output: {}
}
複製代碼
判斷一個對象是否 ref
代理對象。
const unwrapped = isRef(foo) ? foo.value : foo
複製代碼
將一個 reactive
代理對象打平,轉換爲 ref
代理對象,使得對象的屬性能夠直接在 template
上使用。
看看下面的例子你可能就明白它的做用了。
<template>
<p>{{ obj.count }}</p>
<p>{{ count }}
<p>{{ value }}
</template>
<script> export default { setup() { const obj = reactive({ count: 0, value: 100 }) return { obj, // 若是這裏的 obj 來自另外一個文件, // 這裏就能夠不用包裹一層 key,能夠將 obj 的元素直接平鋪到這裏 // template 中能夠直接獲取屬性 ...toRefs(obj) } } } </script>
複製代碼
與 Vue2.x 中的做用相似,獲取一個計算結果。固然功能有所加強,不只支持取值 get
(默認),還支持賦值 set
。
注意: 結果是一個 ref
代理對象,js中取值須要 .value
。
正常獲取一個計算結果:
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // 報錯,由於未實現 set 函數,沒法賦值操做!
複製代碼
當 computed 參數使用 object 對象書寫時,使用 get 和 set 屬性。set 屬性能夠將這個對象編程一個可寫的對象。
也就是說 computed
不只能夠獲取一個計算結果,它還能夠反過來處理 ref
或者 reactive
對象!
const count = ref(1)
const plusOne = computed({
get: () => count.value + 100,
set: val => { count.value = val - 1 }
})
plusOne.value = 1
console.log(count.value) // 0
console.log(plusOne.value) // 100
複製代碼
plusOne.value = 1
至關於給計算對象賦值,會觸發 set
函數,因而 count
值被修改了。
使用 readonly
函數,能夠把 普通 object 對象
、reactive 對象
、ref 對象
返回一個只讀對象。
返回的 readonly 對象,一旦修改就會在 console 有 warning 警告。程序仍是會照常運行,不會報錯。
const original = reactive({ count: 0 })
const copy = readonly(original)
watchEffect(() => {
// 只要有數據變化,這個函數都會執行
console.log(copy.count)
})
// 這裏會觸發 watchEffect
original.count++
// 這裏不會觸發上方的 watchEffect,由於是 readonly。
copy.count++ // warning!
複製代碼
還有一些 API 諸如 watch
、watchEffect
等,這裏就不說了,以上是一些比較重要的的語法,但願能對你們有所幫助。
本文也是 VUE 3.0 學習探索入門系列
裏面的最後一篇,但願能對你們入門 Vue3 有所幫助。
接下來就等 Vue3.0 正式 Release 之後,再帶給你們 VUE 3.0 實戰上手篇
系列,歡迎你們關注我,及時瞭解動態。
本系列歷時20天完成,再次感謝你們。
(全劇終)