mixin 缺點(diǎn)
命名沖突
mixin
模式在運(yùn)行時(shí)盟庞,會(huì)將mixin
對(duì)象和組件對(duì)象合并吃沪,如果他們都有相同的屬性名時(shí),vue
組件默認(rèn)的合并策略是將本地選項(xiàng)覆蓋mixin
選項(xiàng)(可以配置合并策略)什猖,
隱式依賴
mixin
和使用的他的組件之間沒(méi)有層級(jí)關(guān)系票彪,這就意味著組件可以使用mixin
里定義的屬性
,mixin
也可以使用組件定義的屬性
不狮,這樣子可能輝導(dǎo)致一些問(wèn)題降铸,當(dāng)我們要修一個(gè)組件時(shí),修改mixin
的屬性名稱摇零,可能不會(huì)有什么問(wèn)題推掸,如果這個(gè)組件使用了大量的mixin
時(shí)候,會(huì)不會(huì)破壞mixin驻仅?
Composition Api
mixin
的缺點(diǎn)就是推動(dòng)Composition Api
的主要原因之一
Composition Api
的主要思想就是:我們將他們定義為從新的 setup
函數(shù)返回的JavaScript變量
谅畅,而不是將組件功能定義為對(duì)象屬性
setup
setup
函數(shù)是vue3
新增的方法,可以理解為Composition Api
的入口
噪服,在beforeCreate
之后create
之前執(zhí)行毡泻。
參數(shù)有,props
和content
粘优,props
是接受組件props
數(shù)據(jù)仇味,context
是個(gè)上下文對(duì)象,注意setup
中無(wú)法訪問(wèn)this
context
對(duì)象包含一下屬性:attrs
呻顽、emit
、slots
reactive
reactive
是用來(lái)創(chuàng)建個(gè)響應(yīng)式對(duì)象的邪铲,相當(dāng)于vue2
的observable
export declare function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export declare type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRef<T>
export declare type UnwrapRef<T> = T extends Ref<infer V> ? UnwrapRefSimple<V>
UnwrapRefSimple<T>
泛型T繼承了objec芬位,所以reactive函數(shù)的參數(shù)是對(duì)象類型
例子:
export default {
name: 'HelloWorld',
setup() {
const state = reactive({
name: '',
age: ''
})
return {
state,
}
},
}
ref
ref是給定植創(chuàng)建個(gè)響應(yīng)式數(shù)據(jù)對(duì)象无拗,ref()函數(shù)返回的是一個(gè)對(duì)象带到,該對(duì)象只包含一個(gè)屬性value。ref函數(shù)的參數(shù)是任意類型
export default {
name: 'HelloWorld',
setup() {
const age = ref(18)
return {
age,
}
},
}
??注意
如果將ref響應(yīng)式數(shù)據(jù)掛在到reactive中英染,不用使用.value就可以訪問(wèn),
export default {
name: 'HelloWorld',
setup() {
const num = ref (1)
const obj = reactive({num})
obj.num++
console.log(obj.num) //2
console.log(obj.num.value) //undefined
console.log(obj.value) // 2 因?yàn)閚um是對(duì)象 對(duì)于掛在的時(shí)候是將地址賦值給objnum了
return {
obj
}
},
}
isRef
isRef 用來(lái)判斷某個(gè)值是否是ref創(chuàng)建出來(lái)的
isRef<T>(r: Ref<T> | unknown): r is Ref<T>
返回值是個(gè)boolean
類型
unknown
類型揽惹,它是 any
類型對(duì)應(yīng)的安全類型
isRef
函數(shù)的參數(shù)是ref類型
或者任意類型
toRefs
toRefs
可以將reactive
創(chuàng)建出來(lái)的響應(yīng)式對(duì)象
轉(zhuǎn)化成普通對(duì)象
,只不過(guò)這個(gè)對(duì)象的每個(gè)屬性都是ref類型
的響應(yīng)式數(shù)據(jù)
toRefs<T extends object>(object: T): ToRefs<T>
接受參數(shù)是對(duì)象四康,返回值是 ToRefs<T>
type ToRefs<T = any> = {
[K in keyof T]: T[K] extends Ref ? T[K] : Ref<UnwrapRef<T[K]>>;
};
例子:
<div>{{name}}{{age}}</div>
export default {
name: 'HelloWorld',
setup() {
const obj = reactive({name:'小明',age:18})
return {
...toRefs(obj)
}
},
}
toRef
為源響應(yīng)式對(duì)象的某個(gè)屬性創(chuàng)建一個(gè)ref對(duì)象搪搏,相當(dāng)于淺拷貝
區(qū)別ref,ref是深拷貝
export declare function toRef<T extends object, K extends keyof T>(object: T, key: K): ToRef<T[K]>
declare type ToRef<T> = [T] extends [Ref] ? T : Ref<UnwrapRef<T>>;
第一個(gè)參數(shù)是對(duì)象類型闪金,第二個(gè)參數(shù)要是第一個(gè)參數(shù)的屬性疯溺,返回值是ref類型
export default {
name: 'HelloWorld',
setup() {
const obj = reactive({name:'小明',age:18})
const age1 = toRef(obj, 'age')
const age2 = ref(obj.age)
const update = ()=> {
obj.age ++ //obj會(huì)變,age1也會(huì)變
age2.value ++ //obj和age1都不會(huì)變
}
return {
update
}
},
}
computed
computed哎垦,用來(lái)創(chuàng)建計(jì)算器屬性囱嫩,返回ref值
export default {
name: 'HelloWorld',
setup() {
const num = ref(1)
const odd = computed(() => num.value * 2)
//odd 只讀
return {
num,
odd
}
},
}
創(chuàng)建可讀可寫的 computed
export default {
name: 'HelloWorld',
setup() {
const num = ref(1)
const odd = computed({
// 取值
get: () => num.value * 2,
// 賦值
set: val => num.value = val - 1
})
odd.value = 2 //觸發(fā)之后num的值也會(huì)改變
return {
num,
odd
}
},
}
watch
監(jiān)聽數(shù)據(jù)的變化
export declare function watch<T extends object, Immediate extends Readonly<boolean> = false>(source: T, cb: WatchCallback<T, Immediate extends true ? (T | undefined) : T>, options?: WatchOptions<Immediate>): WatchStopHandle
第一個(gè)參數(shù)類型是:WatchSource<T = any> = Ref<T> | ComputedRef<T> | (() => T)
第二個(gè)參數(shù)是回調(diào)函數(shù)
第三個(gè)參數(shù)是可選的,類型是: WatchOptions?:{immediate?: Immediate;deep?: boolean;}
返回值是個(gè)函數(shù)調(diào)用返回的函數(shù)會(huì)終止監(jiān)聽漏设,類型:WatchStopHandle = () => void
const state = ref(1)
watch(()=>{
console.log(state.value);
})
setTimeout(() => {
state.value ++
}, 1000)
指定監(jiān)聽源
const state = ref(1)
watch(()=>state.value,(newVal,oldVal) => {
console.log(oldVal,newVal);
})
watch(state,(newVal,oldVal) => {
console.log(oldVal,newVal);
})
setTimeout(() => {
state.value++
}, 1000)
監(jiān)聽多個(gè)
const state = reactive({
count: 0,
name: '12'
})
watch([() => state.count,() => state.name], ([newCount,newName], [oldCount,oldName]) => {
console.log('new',newCount,newName);
console.log('old',oldCount,oldName);
})
setTimeout(() => {
state.count++
console.log(state.name )
state.name = '121'
}, 1000)
在setup函數(shù)內(nèi)創(chuàng)建的watch在組件銷毀時(shí)墨闲,會(huì)自動(dòng)停止監(jiān)聽,如果想明確的停止監(jiān)聽的話郑口,可以調(diào)用watch函數(shù)的返回值watch函數(shù)返回值(WatchStopHandle = () => void)是一個(gè)函數(shù)
const state = reactive({
count: 0,
name: '12'
})
const stop = watch([() => state.count,() => state.name], ([newCount,newName], [oldCount,oldName]) => {
console.log('new',newCount,newName);
console.log('old',oldCount,oldName);
})
setInterval(() => {
state.count++
state.name = '121'
}, 1000)
setTimeout(() => {
stop()
}, 2000);
watchEffect
vue3 新增的屬性監(jiān)聽api
watchEffect(effect: WatchEffect, options?: WatchOptionsBase): WatchStopHandl
type WatchEffect = (onInvalidate: InvalidateCbRegistrator) => void;
nterface WatchOptionsBase {
flush?: 'pre' | 'post' | 'sync';
onTrack?: ReactiveEffectOptions['onTrack'];
onTrigger?: ReactiveEffectOptions['onTrigger'];
}
type WatchStopHandle = () => void;
和watch的區(qū)別:
1鸳碧、watchEffect不需要指定監(jiān)聽屬性,可以自動(dòng)收集依賴犬性,只要我們回調(diào)中引用了響應(yīng)式的屬性瞻离,那么這些屬性變更的時(shí)候,這個(gè)回調(diào)都會(huì)執(zhí)行乒裆,而watch只能監(jiān)聽指定的屬性而做出變更(v3中可以同時(shí)監(jiān)聽多個(gè))
2套利、watch可以獲取到新值和舊值,而watchEffect獲取不到
3缸兔、watchEffect會(huì)在組件初始化的時(shí)候就會(huì)執(zhí)行一次與computed同理日裙,而收集到的依賴變化后,這個(gè)回調(diào)才會(huì)執(zhí)行惰蜜,而watch不需要昂拂,除非設(shè)置了指定參數(shù)。
setup(props, contex) {
const state = reactive({
num: '121',
msg: '111'
})
const stop = watchEffect(()=>{
console.log('watchEffect', state.num)
console.log('watchEffect', state.msg)
})
stop()//停止監(jiān)聽
return {
state,
}
}
假設(shè)我們現(xiàn)在用一個(gè)用戶ID去查詢用戶的詳情信息抛猖,然后我們監(jiān)聽了這個(gè)用戶ID格侯, 當(dāng)用戶ID 改變的時(shí)候我們就會(huì)去發(fā)起一次請(qǐng)求鼻听,這很簡(jiǎn)單,用watch 就可以做到联四。 但是如果在請(qǐng)求數(shù)據(jù)的過(guò)程中撑碴,我們的用戶ID發(fā)生了多次變化,那么我們就會(huì)發(fā)起多次請(qǐng)求朝墩,而最后一次返回的數(shù)據(jù)將會(huì)覆蓋掉我們之前返回的所有用戶詳情醉拓。這不僅會(huì)導(dǎo)致資源浪費(fèi),還無(wú)法保證 watch 回調(diào)執(zhí)行的順序收苏。而使用watchEffect我們就可以做到.
onInvalidate(fn)傳入的回調(diào)會(huì)在watchEffect重新運(yùn)行或者watchEffect停止的時(shí)候執(zhí)行亿卤。
watchEffect(()=>{
//異步調(diào)用api
const obj= getData(user.id)
onInvalidate(()=>{
// 取消API的調(diào)用
obj.cancel()
})
})
customRef
創(chuàng)建一個(gè)自定義的ref,并對(duì)其依賴跟蹤和更新觸發(fā)進(jìn)行顯式控制
場(chǎng)景:實(shí)現(xiàn)防抖輸入框
export declare function customRef<T>(factory: CustomRefFactory<T>): Ref<T>;
declare type CustomRefFactory<T> = (track: () => void, trigger: () => void) => {
get: () => T;
set: (value: T) => void;
};
customRef
參數(shù)類型是CustomRefFactory<T>
,參數(shù)是個(gè)函數(shù)鹿霸,返回值是個(gè)對(duì)象{get: () => T;set: (value: T) => void;}
customRef
返回值是 Ref<T>
類型排吴,說(shuō)明返回值是個(gè)ref
對(duì)象
例子:
let keyworld = useDebouncedRef('')
function useDebouncedRef(value, t = 500) {
let tims = null;
return customRef((track, trigger) => {
return {
get: () => {
track();
return value;
},
set: (newValue) => {
clearTimeout(tims);
setTimeout(() => {
value = newValue;
trigger();
}, t);
},
};
});
};
自定義Hook函數(shù)
相當(dāng)于vue2的mixin,相對(duì)于vue2的mixin技術(shù)懦鼠,自定義hook函數(shù)知道代碼來(lái)源钻哩,方便復(fù)用
例子:
//hooks.js
import { reactive } from 'vue';
export default function () {
const state = reactive({
num: 1,
msg: 'text'
})
return {
state
}
}
//引用
import hooks from '../utils/hooks.js';
export default {
name: 'HelloWorld',
props: {
msg: String,
},
setup(props, contex) {
const { state } = hooks();
return {
state,
};
},
};
readonly與shallowReadonly
readonly 深度只讀
shallowReadonly 淺度只讀
template ref
通過(guò)ref() 可以拿到頁(yè)面上的元素和組件
例子:
<template>
<button @click="tap">點(diǎn)擊</button>
<p ref="pRef">測(cè)試</p>
<HelloWorld ref="helloRef"></HelloWorld>
</template>
setup(props, contex) {
const pRef = ref(null)
const helloRef = ref(null)
const tap = ()=>{
helloRef.value.divClick() //調(diào)用組件的方法
helloRef.value.count //獲取組件的值
pRef.value //dom節(jié)點(diǎn)
}
return {
pRef,
helloRef,
tap
};
},