最近有小伙伴跟我聊起setup函數,因為習慣了vue2.x的寫法導致了,setup用起來覺得奇奇怪怪的,在一些api混編的情況下,代碼變得更加混亂了,個人覺得在工程化思想比較強的團隊中使用setup確實能更好的使用模塊化開發,但是用得不好的話也確實降低了代碼的可讀性。本篇文章是從使用角度來聊聊setup的實際使用。
setup 使用
1、setup和以前的api(data,methods,computed等)並不沖突,也是可以相互訪問的,值得注意的是setup里面不能用this,並且在setup執行的時候組件實例還未創建完畢,故不也能使用data,methods,computed定義的變量和函數。如下混編示例:
import { getCurrentInstance} from 'vue'
setup() {
const count = 10; // 非響應式的。
const { proxy } = getCurrentInstance(); // 這里 proxy 相當於this
console.log(proxy.msg) // 不能在setup調用時訪問data里面的屬性
return {
count,
consoleMsg: () => {
console.log(proxy.msg) // 123,點擊按鈕時可以訪問了
}
}
},
data() {
return {
msg: '123'
}
},
created() {
console.log(this.count) // 10,這時可以訪問setup拋出的屬性了。
}
2、setup 還可以返回一個渲染函數,不過返回一個渲染函數將阻止我們返回任何其它的東西,當我們想暴漏函數給其父組件使用的時候,可以使用expose來處理這個問題。示例如下:
import { h, ref } from 'vue'
export default {
setup(props, { expose }) {
const count = ref(0) // 創建一個響應式的變量
const increment = () => ++count.value
expose({
increment
})
return () => h('div', count.value) // 請注意這里我們需要顯式使用 ref 的 value
}
}
看到這里就完全可以使用setup來做項目了,接下來就是封山開路遇水搭橋,碰到不會的就各種查,磕磕碰碰總能成功。然而一篇帖子不能寫到這里就結束了,后面還有一大堆相關的知識點呢。
響應式
為什么聊響應式呢,因為setup里面返回的變量雖然可以直接在模版語法中使用,但是它並不是響應式的,如上面第一個示例,我們如果在模版中使用了{{ count }}來展示count的值,然后我們改變count的值,值改變了,但是顯示不會變化。
// 非響應式示例,等價於錯誤示例,請盡量不要復制這塊代碼到你項目中去了。
<template>
<div>
<span>{{ count }}</span>
<button @click="count ++; consoleValue(count)">Increment count</button>
</div>
</template>
<script>
export default {
setup() {
let count = 10; // 這里不是響應式的。
return {
count,
consoleValue: (value) => {
console.log(value); // 這里值發生了變化
}
}
}
}
</script>
響應式這塊在官網api比較多,只說幾個用得比較多的。
1、上面有提到的ref,ref 接收參數並將其包裹在一個帶有 value property 的對象中返回,然后可以使用該 property 訪問或更改響應式變量的值。最簡單的例子,上面示例改成響應式的,如下:
<template>
<div>
<span>{{ count }}</span>
<button @click="count ++">Increment count</button>
<button @click="doubleCount">double count</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
let count = ref(10);
return {
count,
doubleCount: () => {
// 值得注意在setup里面需要使用.value來訪問
count.value *= 2;
}
}
}
}
</script>
2、reactive返回對象的響應式副本。這個比較好理解,跟以前2.x時代差別不大。
<template>
<div>
<span>{{ countObj.count }}</span>
<button @click="countObj.count ++">Increment count</button>
<button @click="doubleCount">double count</button>
</div>
</template>
<script>
import { reactive } from 'vue'
export default {
setup() {
const countObj = reactive({ count: 10 });
return {
countObj,
doubleCount: () => {
countObj.count *= 2;
}
}
}
}
</script>
3、toRef 和 toRefs 這兩個函數都是為了獲取一個響應式的子項,並且跟以前的響應式數據進行關聯
<template>
<div>
<div>{{ countObj.count }} {{countObj.changeTimes}}</div>
<div>{{ count }} {{ changeTimes }}</div>
<button @click="count ++; changeTimes ++">increment count</button>
<button @click="doubleCount">double count</button>
</div>
</template>
<script>
import { reactive, toRefs, toRef } from 'vue'
export default {
setup() {
const countObj = reactive({ count: 10, changeTimes: 0 });
const { count } = toRefs(countObj);
const changeTimes = toRef(countObj, 'changeTimes');
return {
countObj,
count,
changeTimes,
doubleCount: () => {
countObj.count *= 2;
changeTimes.value ++;
}
}
}
}
</script>
注意:toRefs 只會為源對象中包含的 property 生成 ref。如果要為特定的 property 創建 ref,則應當使用toRef,簡單粗暴的理解toRef可以給源對象添加一個關聯的響應式屬性,如:(本想寫在上面示例中,不過感覺不清晰,就單獨列了一塊偽代碼)
const countObj = reactive({ count: 10 });
const testV = toRef(countObj, 'test');
testV.value = 10;
console.log(countObj.test); // 10
setup 參數
使用 setup
函數時,它將接收兩個參數:
Props
setup
函數中的第一個參數是 props
。正如在一個標准組件中所期望的那樣,setup
函數中的 props
是響應式的,當傳入新的 prop 時,它將被更新。
export default {
props: {
title: String
},
setup(props) {
console.log(props.title)
}
}
但是,因為 props
是響應式的,你不能使用 ES6 解構,它會消除 prop 的響應性,上面說到的toRefs可以很好的解決這個問題。
import { toRefs } from 'vue'
setup(props) {
const { title } = toRefs(props)
console.log(title.value)
}
如果 title
是可選的 prop,則傳入的 props
中可能沒有 title
。在這種情況下,toRefs
將不會為 title
創建一個 ref 。你需要使用 toRef
替代它:
import { toRef } from 'vue'
setup(props) {
const title = toRef(props, 'title')
console.log(title.value)
}
Context
傳遞給 setup
函數的第二個參數是 context
。context
是一個普通 JavaScript 對象,暴露了其它可能在 setup
中有用的值:
export default {
setup(props, context) {
// Attribute (非響應式對象,等同於 $attrs)
console.log(context.attrs)
// 插槽 (非響應式對象,等同於 $slots)
console.log(context.slots)
// 觸發事件 (方法,等同於 $emit)
console.log(context.emit)
// 暴露公共 property (函數)
console.log(context.expose)
}
}
setup生命周期鈎子
在setup中可以訪問到以下生命周期鈎子:
- onBeforeMount
- onMounted
- onBeforeUpdate
- onUpdated
- onBeforeUnmount
- onUnmounted
- onErrorCaptured
- onRenderTracked
- onRenderTriggered
- onActivated
- onDeactivated
這些函數接受一個回調函數,當鈎子被組件調用時將會被執行,如:
export default {
setup() {
// mounted
onMounted(() => {
console.log('Component is mounted!')
})
}
}
原創不易,轉載請注明出處,歡迎留言提議。