vue3 文件梳理快速入門
目錄
- 一、setup
- 1. setup 函式中的第一個引數 —— props
- 2. contextcontext
- 二、setup 函式的返回值
- 1.setup 函式的返回值 —— 物件
- 三、響應式系統 API
- 1. reactive
- 2. ref
- 3. computed
- 4. readonly
- 5. watchEffect
- 5.1 停止偵聽
- 5.2 清除副作用
- 5.3 副作用重新整理時機
- 5.4 偵聽器除錯
- 6. watch
- 6.1 偵聽多個數據源
- 6.2 與 watchEffect 共享的行為
一、setup
組合式 API:
setup
選項應該為一個函式setup
選項函式接受兩個引數:props
和context
setup
選項函式需要返回要暴露給元件的內容setup
需要使用return
返回,才能給元件使用,包含資料和方法
1. setup 函式中的第一個引數 —— props
setup
函式中的 props
是響應式的,當傳入新的 prop 時,它將被更新
export default { props: { title: String },setup(props) { console.log(props.title) } }
但是,因為 props
是響應式的,你不能使用 ES6 解構,因為它會消除 prop 的響應性。
如果需要解構 prop,可以通過使用 setup 函式中的 toRefs
import { toRefs } from '' setup(props) { const { title } = toRefs(props) console.log(title.value) }
2. contextcontext
setup
函式中的第二個引數 ——contextcontext
上下文是一個普通的 物件,它暴露三個元件的 property
xport default { setup(props,context) { // Attribute (非響應式物件) console.log(context.attrs) // 插槽 (非響應式物件) console.log(context.slots) // 觸發事件 (方法) console.log(context.emit) } }
context
是一個普通的 Script
物件,也就是說,它不是響應式的,這意味著你可以安全地對 context 使用 ES6 解構
export default { setup(props,{ attrs,slots,emit }) { ... } }
二、setup 函式的返回值
1.setup 函式的返回值 —— 物件
如果 setup
返回一個物件,則可以在元件的模板中像傳遞給 setup
的 props property
一樣訪問該物件的 property:
<template> <!-- 模板中使用會被自動解開,所以不需要 .value --> <div>{{ readersNumber }} {{ book.title }}</div> </template> <script> import { ref,reactive } from 'vue' export default { setup() { const readersNumber = ref(0) const book = reactive({ title: 'Vue 3 Guide' }) // expose to template return { readersNumber,book } } } </script>
注意:從 setup 返回的 refs 在模板中訪問時是被自動解開的,因此不應在模板中使用 .value
三、響應式系統 API
1. reactive
reactive()
接收一個普通物件然後返回該普通物件的響應式代理。等同於 2.x 的 Vue.observable()
const obj = reactive({ count: 0 })
響應式轉換是“深層的”:會影響物件內部所有巢狀的屬性。基於 ES2015 的 Proxy 實現,返回的代理物件不等於原始物件。建議僅使用代理物件而避免依賴原始物件。
<template> <div id="app">{ state.count }</div> </template> <script> import { reactive } from 'vue' export default { setup() { // state 現在是一個響應式的狀態 const state = reactive({ count: 0,}) } } </script>
2. ref
接受一個引數值並返回一個響應式且可改http://www.cppcns.com變的 ref
物件。ref
物件擁有一個指向內部值的單一屬性 .value
。
const count = ref(0) console.log(count.value) // 0 count.value++ console.log(count.value) // 1
如果傳入 ref
的是一個物件,將呼叫 reactive 方法進行深層響應轉換。
模板中訪問:
當 http://www.cppcns.comref
作為渲染上下文的屬性返回(即在setup()
返回的物件中)並在模板中使用時,它會自動解套,無需在模板內額外書寫 .value:
<template> <div>{{ count }}</div> </template> <script> export default { setup() { return { count: ref(0),} },} </script>
作為響應式物件的屬性訪問:
當 ref 作為 reactive
物件的 property
被訪問或修改時,也將自動解套 value
值,其行為類似普通屬性:
const count = ref(0) const state = reactive({ count,}) console.log(state.count) // 0 state.count = 1 console.log(count.value) // 1
注意:如果將一個新的 ref
分配給現有的 ref
, 將替換舊的 ref
:
const otherCount = ref(2) state.count = otherCount console.log(state.count) // 2 console.log(count.value) // 1
注意:當巢狀在 reactive Object
中時,ref
才會解套。從 Array
或者 Map
等原生集合類中訪問 ref
時,不會自動解套:
const arr = reactive([ref(0)]) // 這裡需要 .value console.log(arr[0].value) const map = reactive(new Map([['foo',ref(0)]])) // 這裡需要 .value console.log(map.get('foo').value)
型別定義:
interface Ref<T> { value: T } function ref<T>(value: T): Ref<T>
有時我們可能需要為 ref
做一個較為複雜的型別標註。我們可以通過在呼叫 ref
時傳遞泛型引數來覆蓋預設推導:
const foo = ref<string | number>('foo') // foo 的型別: Ref<string | number> foo.value = 123 // 能夠通過!
3. computed
使用響應式 computed API
有兩種方式:
(1)傳入一個 getter
函式,返回一個預設不可手動修改的 ref
物件。
const count = ref(1) const plusOne = computed(() => count.value + 1) console.log(plusOne.value) // 2 plusOne.value++ // 錯誤!
(2)傳入一個擁有 get
和 set
函式的物件,建立一個可手動修改的計算狀態。
const count = ref(1) const plusOne = computed({ get: () => count.value + 1,set: (val) => { count.value = val - 1 },}) plusOne.value = 1 console.log(count.value) // 0
型別定義:
// 只讀的 function computed<T>(getter: () => T): Readonly<Ref<Readonly<T>>> // 可更改的 function computed<T>(options: { get: () => T set: (value: T) => void }): Ref<T>
4. readonly
傳入一個物件(響應式或普通)或 ref
,返回一個原始物件的只讀代理。一個只讀的代理是“深層的”,物件內部任何巢狀的屬性也都是隻讀的。
const original = reactive({ count: 0 }) const copy = readonly(original) watchEffect(() => { // 依賴追蹤 console.log(copy.count) }) // original 上的修改會觸發 copy 上的偵聽 original.count++ // 無法修改 copy 並會被警告 copy.count++ // warning!
5. watchEffect
立即執行傳入的一個函式,並響應式追蹤其依賴,並在其依賴變更時重新執行該函式。
const count = ref(0) watchEffect(() => console.log(count.value)) // -> 打印出 0 setTimeout(() => { count.value++ // -> 打印出 1 },100)
5.1 停止偵聽
當 watchEffect
在元件的 setup()
函式或生命週期鉤子被呼叫時, 偵聽器會被連結到該元件的生命週期,並在元件解除安裝時自動停止。
在一些情況下,也可以顯式呼叫返回值以停止偵聽:
const stop = watchEffect(() => { /* ... */ }) // 之後 stop()
5.2 清除副作用
有時副作用函式會執行一些非同步的副作用,這些響應需要在其失效時清除(即完成之前狀態已改變了)。所以偵聽副http://www.cppcns.com作用傳入的函式可以接收一個 onInvalidate
函式作入參,用來註冊清理失效時的回撥。當以下情況發生時,這個失效回撥會被觸發:
- 副作用即將重新執行時
- 偵聽器被停止 (如果在 setup() 或 生命週期鉤子函式中使用了 watchEffect,則在解除安裝元件時)
watchEffect((onInvalidate) => { const token = performAsyncOperation(id.value) onInvalidate(() => { // id 改變時 或 停止偵聽時 // 取消之前的非同步操作 token.cancel() }) })
我們之所以是通過傳入一個函式去註冊失效回撥,而不是從回撥返回它(如 React useEffect
中的方式),是因為返回值對於非同步錯誤處理很重要。
在執行資料請求時,副作用函式往往是一個非同步函式:
const data = ref(null) watchEffect(async () => { data.value = await fetchData(props.id) })
我們知道非同步函式都會隱式地返回一個 Promise
,但是清理函式必須要在 Promise
被 resolve
之前被註冊。另外,Vue
依賴這個返回的 Promise
來自動處理 Promise
鏈上的潛在錯誤。
5.3 副作用重新整理時機
Vue
的響應式系統會快取副作用函式,並非同步地重新整理它們,這樣可以避免同一個 tick 中多個狀態改變導致的不必要的重複呼叫。在核心的具體實現中,元件的更新函式也是一個被偵聽的副作用。當一個使用者定義的副作用函式進入佇列時,會在所有的元件更新後執行:
<template> <div>{{ count }}</div> </template> <script> export default { setup() { const count = ref(0) watchEffect(() => { console.log(count.value) }) return { count,} </script>
在這個例子中:
count
會在初始執行時同步打印出來
更改 count
時,將在元件更新後執行副作用。
請注意:初始化執行是在元件 mounted
之前執行的。因此,如果你希望在編寫副作用函式時訪問 DOM
(或模板 ref),請在 onMounted
鉤子中進行:
onMounted(() => { watchEffect(() => { // 在這裡可以訪問到 DOM 或者 template refs }) })
如果副作用需要同步或在元件更新之前重新執行,我們可以傳遞一個擁有 flush 屬性的物件作為選項(預設為 'post
'):
// 同步執行 watchEffect( () => { /* ... */ },{ flush: 'sync',} ) // 元件更新前執行 watchEffect( () => { /* ... */ },{ flush: 'pre',} )
5.4 偵聽器除錯
onTrack
和 onTrigger
選項可用於除錯一個偵聽器的行為。
- 當一個
reactive
物件屬性或一個ref
作為依賴被追蹤時,將呼叫onTrack
- 依賴項變更導致副作用被觸發時,將呼叫
onTrigger
這兩個回撥都將接收到一個包含有關所依賴項資訊的偵錯程式事件。建議在以下回調中編寫 debugger 語句來檢查依賴關係:
watchEffect( () => { /* 副作用的內容 */ },{ onTrigger(e) { debugger },} )
onTrack
和 onTrigger
僅在開發模式下生效。
型別定義:
function watchEffect( effect: (onInvalidate: InvalidateCbRegistrator) => void,options?: WatchEffectOptions ): StopHandle interface WatchEffectOptions { flush?: 'pre' | 'post' | 'sync' onTrack?: (event: DebuggerEvent) => void onTrigger?: (event: DebuggerEvent) => void } interface DebuggerEvent { effect: ReactiveEffect target: any type: OperationTypes key: string | symbol | undefined } type InvalidateCbRegistrator = (invalidate: () => void) => void type StopHandle = () => void
6. watch
watch API
完全等效於 2.x this.$watch
(以及 watch
中相應的選項)。watch
需要偵聽特定的資料來源,並在回撥函式中執行副作用。預設情況是懶執行的,也就是說僅在偵聽的源變更時才執行回撥。
- 對比
watchEffect,watch
允許我們:
- 懶執行副作用;
- 更明確哪些狀態的改變會觸發偵聽器重新執行副作用;
- 訪問偵聽狀態變化前後的值。
- 偵聽單個數據源
- 偵聽器的資料來源可以是一個擁有返回值的 getter 函式,也可以是 ref:
// 偵聽一個 getter const state = reactive({ count: 0 }) watch( () => state.count,(count,prevCount) => { /* ... */ } ) // 直接偵聽一個 ref const count = ref(0) watch(count,prevCount) => { /* ... */ })
6.1 偵聽多個數據源
watcher
也可以使用陣列來同時偵聽多個源:
watch([fooRef,barRef],([foo,bar],[prevFoo,prevBar]) => { /* ... */ })
6.2 與 watchEffect 共享的行為
watch
和 watchEffect
在停止偵聽,清除副作用 (相應地 onInvalidate
會作為回撥的第三個引數傳入),副作用重新整理時機 和 偵聽器除錯 等方面行為一致.
型別定義:
// 偵聽單資料來源 function watch<T>( source: WatcherSource<T>,callback: ( value: T,oldValue: T,onInvalidate: InvalidateCbRegistrator ) => void,options?: WatchOptions ): StopHandle // 偵聽多資料來源 function watch<T extends WatcherSource<unknown>[]>( sources: T callback: ( values: MapSources<T>,oldValues: MapSources<T>,options? : WatchOptions ): StopHandle type WatcherSource<T> = Ref<T> | (() => T) type MapSources<T> = { [K in keyof T]:www.cppcns.com T[K] extends WatcherSource<infer V> ? V : never } // 共有的屬性 請檢視 `watchEffect` 的型別定義 interface WatchOptions extends WatchEffectOptions { immediate?: boolean // default: false deep?: boolean }
到此這篇關於vue3 文件梳理快速入門的文章就介紹到這了,更多相關vue3 文件梳理內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!