mixin 缺点
命名冲突
mixin
模式在运行时,会将mixin
对象和组件对象合并,如果他们都有相同的属性名时,vue
组件默认的合并策略是将本地选项覆盖mixin
选项(可以配置合并策略),
隐式依赖
mixin
和使用的他的组件之间没有层级关系,这就意味着组件可以使用mixin
里定义的属性
,mixin
也可以使用组件定义的属性
,这样子可能辉导致一些问题,当我们要修一个组件时,修改mixin
的属性名称,可能不会有什么问题,如果这个组件使用了大量的mixin
时候,会不会破坏mixin?
Composition Api
mixin
的缺点就是推动Composition Api
的主要原因之一
Composition Api
的主要思想就是:我们将他们定义为从新的 setup
函数返回的JavaScript变量
,而不是将组件功能定义为对象属性
setup
setup
函数是vue3
新增的方法,可以理解为Composition Api
的入口
,在beforeCreate
之后create
之前执行。
参数有,props
和content
,props
是接受组件props
数据,context
是个上下文对象,注意setup
中无法访问this
context
对象包含一下属性:attrs
、emit
、slots
reactive
reactive
是用来创建个响应式对象的,相当于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函数的参数是对象类型
例子:
export default {
name: 'HelloWorld',
setup() {
const state = reactive({
name: '',
age: ''
})
return {
state,
}
},
}
ref
ref是给定植创建个响应式数据对象,ref()函数返回的是一个对象,该对象只包含一个属性value。ref函数的参数是任意类型
export default {
name: 'HelloWorld',
setup() {
const age = ref(18)
return {
age,
}
},
}
⚠️注意
如果将ref响应式数据挂在到reactive中,不用使用.value就可以访问,
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 因为num是对象 对于挂在的时候是将地址赋值给objnum了
return {
obj
}
},
}
isRef
isRef 用来判断某个值是否是ref创建出来的
isRef<T>(r: Ref<T> | unknown): r is Ref<T>
返回值是个boolean
类型
unknown
类型,它是 any
类型对应的安全类型
isRef
函数的参数是ref类型
或者任意类型
toRefs
toRefs
可以将reactive
创建出来的响应式对象
转化成普通对象
,只不过这个对象的每个属性都是ref类型
的响应式数据
toRefs<T extends object>(object: T): ToRefs<T>
接受参数是对象,返回值是 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
为源响应式对象的某个属性创建一个ref对象,相当于浅拷贝
区别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>>;
第一个参数是对象类型,第二个参数要是第一个参数的属性,返回值是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会变,age1也会变
age2.value ++ //obj和age1都不会变
}
return {
update
}
},
}
computed
computed,用来创建计算器属性,返回ref值
export default {
name: 'HelloWorld',
setup() {
const num = ref(1)
const odd = computed(() => num.value * 2)
//odd 只读
return {
num,
odd
}
},
}
创建可读可写的 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 //触发之后num的值也会改变
return {
num,
odd
}
},
}
watch
监听数据的变化
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
第一个参数类型是:WatchSource<T = any> = Ref<T> | ComputedRef<T> | (() => T)
第二个参数是回调函数
第三个参数是可选的,类型是: WatchOptions?:{immediate?: Immediate;deep?: boolean;}
返回值是个函数调用返回的函数会终止监听,类型:WatchStopHandle = () => void
const state = ref(1)
watch(()=>{
console.log(state.value);
})
setTimeout(() => {
state.value ++
}, 1000)
指定监听源
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)
监听多个
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函数内创建的watch在组件销毁时,会自动停止监听,如果想明确的停止监听的话,可以调用watch函数的返回值watch函数返回值(WatchStopHandle = () => void)是一个函数
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 新增的属性监听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的区别:
1、watchEffect不需要指定监听属性,可以自动收集依赖,只要我们回调中引用了响应式的属性,那么这些属性变更的时候,这个回调都会执行,而watch只能监听指定的属性而做出变更(v3中可以同时监听多个)
2、watch可以获取到新值和旧值,而watchEffect获取不到
3、watchEffect会在组件初始化的时候就会执行一次与computed同理,而收集到的依赖变化后,这个回调才会执行,而watch不需要,除非设置了指定参数。
setup(props, contex) {
const state = reactive({
num: '121',
msg: '111'
})
const stop = watchEffect(()=>{
console.log('watchEffect', state.num)
console.log('watchEffect', state.msg)
})
stop()//停止监听
return {
state,
}
}
假设我们现在用一个用户ID去查询用户的详情信息,然后我们监听了这个用户ID, 当用户ID 改变的时候我们就会去发起一次请求,这很简单,用watch 就可以做到。 但是如果在请求数据的过程中,我们的用户ID发生了多次变化,那么我们就会发起多次请求,而最后一次返回的数据将会覆盖掉我们之前返回的所有用户详情。这不仅会导致资源浪费,还无法保证 watch 回调执行的顺序。而使用watchEffect我们就可以做到.
onInvalidate(fn)传入的回调会在watchEffect重新运行或者watchEffect停止的时候执行。
watchEffect(()=>{
//异步调用api
const obj= getData(user.id)
onInvalidate(()=>{
// 取消API的调用
obj.cancel()
})
})
customRef
创建一个自定义的ref,并对其依赖跟踪和更新触发进行显式控制
场景:实现防抖输入框
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
参数类型是CustomRefFactory<T>
,参数是个函数,返回值是个对象{get: () => T;set: (value: T) => void;}
customRef
返回值是 Ref<T>
类型,说明返回值是个ref
对象
例子:
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函数
相当于vue2的mixin,相对于vue2的mixin技术,自定义hook函数知道代码来源,方便复用
例子:
//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
通过ref() 可以拿到页面上的元素和组件
例子:
<template>
<button @click="tap">点击</button>
<p ref="pRef">测试</p>
<HelloWorld ref="helloRef"></HelloWorld>
</template>
setup(props, contex) {
const pRef = ref(null)
const helloRef = ref(null)
const tap = ()=>{
helloRef.value.divClick() //调用组件的方法
helloRef.value.count //获取组件的值
pRef.value //dom节点
}
return {
pRef,
helloRef,
tap
};
},
网友评论