一、setup
setup
是組合Composition API
中的入口函數,也是第一個要使用的函數。
1、setup
只在初始化時執行一次,所有的Composition API
函數都在此使用。
2、setup
是在beforeCreate
生命周期之前執行的(只執行一次)
beforeCreate() { console.log('beforeCreate執行了'); }, setup() { console.log('setup執行了'); return {}; }, //setup執行了 //beforeCreate執行了
由此可以推斷出setup
執行的時候,組件對象還沒有創建,組件實例對象this
還不可用,此時this
是undefined
, 不能通過this
來訪問data/computed/methods/props
。
3、返回對象中的屬性會與data
函數返回對象的屬性合並成為組件對象的屬性,返回對象中的方法會與methods
中的方法合並成功組件對象的方法,如果有重名,setup
優先。
因為在setup
中this
不可用,methods
中可以訪問setup
提供的屬性和方法, 但在setup
方法中不能訪問data
和methods
里的內容,所以還是不建議混合使用。
4、setup
函數如果返回對象, 對象中的 屬性 或 方法 , 模板 中可以直接使用
//templete
<div>{{number}}</div>
//JS
setup() { const number = 18; return { number }; },
5、注意:setup
不能是一個async
函數: 因為返回值不再是return
的對象,而是promise,
模板中就不可以使用return
中返回對象的數據了。
6、setup的參數(props,context)
(1)props: 是一個對象,里面有父級組件向子級組件傳遞的數據,並且是在子級組件中使用 props
接收到的所有的屬性
(2)context:上下文對象,可以通過es6
語法解構 setup(props, {attrs, slots, emit})
- attrs:獲取當前組件標簽上所有沒有通過
props
接收的屬性的對象, 相當於this.$attrs
- slots:包含所有傳入的插槽內容的對象,相當於
this.$slots
- emit:用來分發自定義事件的函數,相當於
this.$emit
二、ref
1、作用:定義一個響應式的數據(一般用來定義一個基本類型的響應式數據Undefined
、Null
、Boolean
、Number
和String
)
2、語法:const xxx = ref(initValue);
注意:script
中操作數據需要使用xxx.value
的形式,而模板中不需要添加.value
3、ref 用於定義響應式數據
// 用一個例子來演示:實現一個按鈕,點擊可以增加數字
<template>
<div>{{count}}</div>
<button @click='updateCount'>增加</button>
</template>
// 在Vue3中
setup() { // ref用於定義一個響應式的數據,返回的是一個Ref對象,對象中有一個value屬性 //如果需要對數據進行操作,需要使用該Ref對象的value屬性
const count = ref(0); function updateCount() { count.value++; } return { count, updateCount, }; },
4、ref 用於獲取 dom 節點:在Vue2
中我們通過this.$refs
來獲取dom
節點,Vue3
中我們通過ref
來獲取節點
首先需要在標簽上添加 ref='xxx'
,然后再setup
中定義一個初始值為null
的ref
類型,名字要和標簽的ref
屬性一致
const xxx = ref(null)
注意:一定要在setup
的return
中返回,不然會報錯。
// 還是用一個例子來演示:讓輸入框自動獲取焦點
<template>
<h2>App</h2>
<input type="text" ref="inputRef">
</template>
<script lang="ts"> import { onMounted, ref } from 'vue'
/* ref獲取元素: 利用ref函數獲取組件中的標簽元素 功能需求: 讓輸入框自動獲取焦點 */ export default { setup() { const inputRef = ref<HTMLElement|null>(null) onMounted(() => { inputRef.value && inputRef.value.focus() }) return { inputRef } }, } </script>
三、reactive
1、語法:const proxy = reactive(obj)
2、作用:定義多個數據的響應式,接收一個普通對象然后返回該普通對象的響應式代理器對象(Proxy)
,響應式轉換是“深層的”:會影響對象內部所有嵌套的屬性,所有的數據都是響應式的。
<template>
<h3>姓名:{{user.name}}</h3>
<h3>wife:{{user.wife}}</h3>
<button @click="updateUser">更新</button>
</template> setup() { const user = reactive({ name: '**', wife: { name: 'xioaohong', age: 18, books: [], }, }); const updateUser = () => { user.name = '小紅'; user.age += 2; user.wife.books[0] = '**'; }; return { user, updateUser, }; },
3、重點來了:ref 他強調的是一個數據的value的更改,reactive 強調的是定義的對象的某一個屬性的更改。
四、toRefs
1、作用:把一個響應式對象轉換成普通對象,該普通對象的每個屬性都是一個 ref
2、應用:我們使用 reactive
創建的對象,如果想在模板中使用,就必須得使用 xxx.xxx
的形式;如果大量用到的話還是很麻煩的,但是使用 es6
解構以后,會失去響應式。
那么toRefs
的作用就體現在這,利用toRefs
可以將一個響應式 reactive
對象的所有原始屬性轉換為響應式的ref
屬性。
<template>
<div> name:{{name}} </div>
</template>
<script lang='ts'> import { defineComponent, reactive, toRefs } from 'vue'; export default defineComponent({ name: '', setup() { const state = reactive({ name: 'hzw', }); const state2 = toRefs(state); setInterval(() => { state.name += '==='; }, 1000); return { //通過toRefs返回的對象,解構出來的屬性也是響應式的
...state2, }; }, }); </script>
五、computed函數
1、與Vue2
中的computed
配置功能一致,返回的是一個ref
類型的對象
2、計算屬性的函數中如果只傳入一個回調函數 表示的是get
操作
3、計算屬性的函數中可以傳入一個對象,可以包含set
和get
函數,進行讀取和修改的操作
const fullName2 = computed({ get() { return user.firstName + '_' + user.lastName; }, set(val: string) { const names = val.split('_'); user.firstName = names[0]; user.lastName = names[1]; }, }); return { user, fullName2, };
六、watch 函數
1、與Vue2
中的watch
配置功能一致:(1)參數1 - 要監聽的數據;(2)參數2 - 回調函數;(3)參數3 - 配置。
2、作用:監視指定的一個或多個響應式數據,一旦數據變化,就自動執行監視回調。
(1)默認初始時不執行回調,但可以通過配置 immediate
為true
來指定初始時立即執行第一次
(2)通過配置 deep
為true
來指定深度監視
3、監聽單一數據
import { ref, reactive, computed, watch } from 'vue'; setup(props) { // ref
const age = ref(20); watch(() => age.value, (nv, ov) => { ... }); // reactive
const product = reactive({ name: '飲料', count: 1 }); watch(() => product.count, (nv, ov) => { ... }); // props
watch(() => props.msg, (nv, ov) => { ... }); // computed
const userAge = computed(() => `今年${age.value}歲了!`); watch(() => userAge.value, (nv, ov) => { ... }); }
4、監聽對象
import { ref, reactive, watch } from 'vue'; setup(props) { // ref
const user = ref({ name: 'zhang_san', age: 20 }); // 字面量引發的監聽觸發: user.value = { ... };
watch(() => user.value, (nv, ov) => { ... }); // 如果使用 user.value.age = 30這種方式去修改user的age值; 將不會觸發上面的監聽,需要使用watch的第三個參數(深度監聽),且觸發監聽后的nv===ov true
watch(() => user.value, (nv, ov) => { ... }, { deep: true }); // 如果我們只需要監聽name的值,那么
watch(() => user.value.name, (nv, ov) => { ... }); // reactive
const reactiveData = reactive({ user: { name: 'zhang_san', age: 20 } }); // 字面量引發的監聽觸發: reactiveData.user = { ... };
watch(() => reactiveData.user, (nv, ov) => { ... }); // 如果使用 user.user.age = 30這種方式去修改user的age值,將不會觸發監聽,需要使用watch的第三個參數(深度監聽),且觸發監聽后的nv===ov true
watch(() => reactiveData.user, (nv, ov) => { ... }, { deep: true }); // 如果我們只需要監聽name的值,那么
watch(() => reactiveData.user.name, (nv, ov) => { ... }); }
5、監聽數組
import { ref, reactive, watch } from 'vue'; setup(props) { // ref
const user = ref([ { name: 'zhang_san', age: 10 }, { name: 'li_si', age: 10 } ]); // 字面量引發的監聽觸發: user.value = [ ... ];
watch(() => user.value, (nv, ov) => { ... }); // 如果使用數組的操作方法(如:push())或者user.value[0].age = 20這類操作去修改數組某項的屬性值,將不會觸發監聽,也需要使用深度監聽模式,且觸發監聽后的nv===ov true
watch(() => user.value, (nv, ov) => { ... }, { deep: true }); // reactive
const reactiveData = reactive({ user: [ { name: 'zhang_san', age: 10 }, { name: 'li_si', age: 10 } ] }); // 字面量引發的監聽觸發: user.user = [ ... ];
watch(() => reactiveData.user, (nv, ov) => { ... }); // 如果使用數組的操作方法(如:push())或者user.value[0].age = 20這類操作去修改數組某項的屬性值,將不會觸發監聽,也需要使用深度監聽模式,且觸發監聽后的nv===ov true
watch(() => reactiveData.user, (nv, ov) => { ... }, { deep: true }); }
6、監聽多個數據
import { ref, reactive, computed, watch } from 'vue'; setup(props) { const age = ref(20); const user = ref({ name: 'zhang_san', age: 20 }); watch([() => age.value, () => user.name], ([newAge, newName], [oldAge, oldName]) => { ... }); }
7、終止監聽
import { ref, watch } from 'vue'; const age = ref(20); // watch監聽會返回一個方法
const stop = watch(age, (nv, ov) => { ... }); // 當調用此方法后,該監聽就會被移除
stop();
七、watchEffect函數
1、作用:監視數據發生變化時執行回調,不用直接指定要監視的數據,回調函數中使用的哪些響應式數據就監視哪些響應式數據,
2、默認初始時就會執行第一次,從而可以收集需要監視的數據。
import { watchEffect, ref } from 'vue'; const user = reactive({ firstName: '**', lastName: '**', }); const fullName4 = ref(''); watchEffect(() => { fullName4.value = user.firstName + '_' + user.lastName; }); return { user, fullName4, };
八、生命周期對比
注意:3.0
中的生命周期鈎子要比2.X
中相同生命周期的鈎子要快
setup() { onBeforeMount(() => { console.log('--onBeforeMount') }) onMounted(() => { console.log('--onMounted') }) ...... onBeforeUnmount(() => { console.log('--onBeforeUnmount') }) onUnmounted(() => { console.log('--onUnmounted') }) }
九、provide 與 inject
作用:實現跨層級組件(祖孫)間通信
// 父組件
setup() { const color = ref('red') provide('color', color) return { color } // 孫子組件
setup() { const color = inject('color') return { color }
這個 vue2 里也有,差不多一樣的用法。