一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情。
上一篇文章中,我们简单介绍了reactive极其相关的一些API,本章打算介绍一下computed,watch,watchEffect等。
Vue3实践小结
Computed-计算属性
计算属性就是当依赖的属性的值发生变化的时候,才会触发它的更改,如果依赖的值不发生变化时,使用的是缓存中的属性值。
函数形式的Computed
接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式 ref 对象
import { computed, ref } from 'vue'
let count = ref<number>(1);
let data = computed<string>(()=>{
return `+++` + count.value;
});
console.log(data.value) // +++ 1
data.value += '6666'; // 错误
对象形式的Computed
接受一个具有 get 和 set 函数的对象,用来创建可写的 ref 对象
import { computed, ref } from 'vue'
let count = ref<number>(1);
let data = computed({
get: () => {
return '++++' + count.value
},
set: (value) => {
count.value = value
}
});
console.log(data.value); // ++++ 1
data.value = 2; //Ok
这边同样的看一下Computed的类型声明
//reactivity.d.ts
//函数形式的Computed,接收一个getter函数和一个用于调试的debugger选项
//返回值是一个readonly的值,只读的ref对象
export declare function computed<T>(getter: ComputedGetter<T>, debugOptions?: DebuggerOptions): ComputedRef<T>;
export declare type ComputedGetter<T> = (...args: any[]) => T;
export declare interface ComputedRef<T = any> extends WritableComputedRef<T> {
readonly value: T;
[ComputedRefSymbol]: true;
}
//对象形式的Computed
export declare function computed<T>(options: WritableComputedOptions<T>, debugOptions?: DebuggerOptions): WritableComputedRef<T>;
export declare interface WritableComputedOptions<T> {
get: ComputedGetter<T>;
set: ComputedSetter<T>;
}
export declare interface WritableComputedRef<T> extends Ref<T> {
readonly effect: ReactiveEffect<T>;
}
我们在来看看debugger的选项对象中都有那些属性方法:
//debugger的options对象
// onTrack => 会在某个响应式 property 或 ref 作为依赖被追踪时调用
// onTrigger => 在侦听回调被某个依赖的修改触发时调用
export declare interface DebuggerOptions {
onTrack?: (event: DebuggerEvent) => void;
onTrigger?: (event: DebuggerEvent) => void;
}
export declare type DebuggerEvent = {
effect: ReactiveEffect;
} & DebuggerEventExtraInfo;
export declare type DebuggerEventExtraInfo = {
target: object;
type: TrackOpTypes | TriggerOpTypes;
key: any;
newValue?: any;
oldValue?: any;
oldTarget?: Map<any, any> | Set<any>;
};
怎么使用onTrack和onTrigger来调试:
import { ref, computed } from 'vue';
const count = ref<number>(1);
//对象形式的Computed,第一个参数是gutter函数,第二个是用于调试的选项对象
const data = computed(() => count.value + 1, {
onTrack(e) {
// 当 count.value 作为依赖被追踪时触发
debugger
},
onTrigger(e) {
// 当 count.value 被修改时触发
debugger
}
})
// 访问 data,应该触发 onTrack
console.log(data.value)
// 修改 count.value,应该触发 onTrigger
count.value++
onTrack 和 onTrigger 仅在开发模式下生效
watch
watch需要侦听特定的数据源,并在单独的回调函数中执行副作用,默认情况下,它也是惰性的——即回调仅在侦听源发生变化时被调用
- watch接收的第一个参数: 要监听的数据源
- watch接收的第二个参数: 回调函数cb(newVal,oldVal)
- watch接收的第三个参数一个options配置项是一个对象
侦听单一源 侦听器数据源可以是一个具有返回值的 getter 函数,也可以直接是一个 ref
// 侦听一个getter
const state = reactive({ count: 0 })
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
)
// 直接侦听一个 ref
const count = ref(0)
watch(count, (count, prevCount) => {
/* ... */
})
侦听多个源 侦听器还可以使用数组以同时侦听多个源
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
})
类型声明
// 侦听单一源
function watch<T>(
source: WatcherSource<T>,
callback: (
value: T, //当前值
oldValue: T, //旧值
onInvalidate: InvalidateCbRegistrator
) => void,
options?: WatchOptions // {immediate, deep..}
): StopHandle
// 侦听多个源
// 可以是多个getter函数的数组,或者多个ref的数组,或者两者混合
function watch<T extends WatcherSource<unknown>[]>(
sources: T
callback: (
values: MapSources<T>,
oldValues: MapSources<T>,
onInvalidate: InvalidateCbRegistrator
) => void,
options? : WatchOptions
): StopHandle
type WatcherSource<T> = Ref<T> | (() => T)
type MapSources<T> = {
[K in keyof T]: T[K] extends WatcherSource<infer V> ? V : never
}
interface WatchOptions extends WatchEffectOptions {
immediate?: boolean // 默认:false -> 惰性的, 第一次不执行
deep?: boolean
}
watchEffect
立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。
用到几个参数就监听几个,而且是非惰性 会默认调用一次
const count = ref(0)
watchEffect(() => console.log(count.value));
//进来时就会打印0 -> 非惰性
setTimeout(() => {
count.value++;
}, 100);
类型声明
function watchEffect(
effect: (onInvalidate: InvalidateCbRegistrator) => void, //没有返回值
options?: WatchEffectOptions //调试options对象
): StopHandle
interface WatchEffectOptions {
flush?: 'pre' | 'post' | 'sync' // 默认:'pre'
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
组件和生命周期
组件从创建到销毁的过程称为生命周期 Vue3的组合式API是没有
beforeCreate和created这两个生命周期的==> setup就相当于这两
- beforeCreate: 在实例初始化之后、进行数据侦听和事件/侦听器的配置之前同步调用
- created: 在实例创建完成后被立即同步调用.在这一步中,实例已完成对选项的处理,意味着以下内容已被配置完毕:数据侦听、计算属性、方法、事件/侦听器的回调函数。然而,挂载阶段还没开始,且
$elproperty 目前尚不可用。 - onBeforeMount: 在组件DOM实际渲染之前调用。
- onMounted: 在组件的第一次渲染后调用,该元素现在可用,允许直接DOM访问.
- onBeforeUpdate: 在数据发生改变后,DOM 被更新之前被调用。
- updated: 在数据更改导致的虚拟 DOM 重新渲染和更新完毕之后被调用。
- onBeforeUnmounnted: 卸载组件实例之前调用。这个阶段,实例仍然是完全正常的,一般在此阶段,进行清除定时器等操作。
- onUnmounted: 卸载组件实例后调用。