- 该文章是在学习 小满vue3 课程的随堂记录
- 示例均采用
,且包含
typescript
的基础用法
本章 ref 全家桶
主要包括以下几个api 和 对应源码的学习:
ref
isRef
shallowRef
triggerRef
customRef
定义响应式数据
主要通过 ref
、reactive
任何类型数据
,reactive 用来定义对象类型.value
,template 中使用时不需要 .value获取dom
:dom上绑定 ref
属性,并用 ref 创建同名的响应式变量
<div>{{ name }}</div>
import { ref, Ref } from "vue"
const name: Ref<string> = ref('xiaoman');
name.value = 'blue'
<div ref="dom">我是dom</div>
<button @click="getDom">获取dom</button>
import { ref } from "vue"
const dom = ref<HTMLElement>();
function getDom() {
// ?.的形式,先判断再获取
console.log(dom.value?.innerText);
}
字如其意,就是 判断一个变量是不是 ref 类型的响应式数据
,返回布尔值
import { ref, Ref, isRef } from "vue"
const name: Ref<string> = ref('xiaoman');
name.value = 'blue'
console.log(isRef(name)); // true
ref
是 深层
的响应式 ,shallowRef
是 浅层
的响应式深层
的响应式:不管怎么修改都会 直接触发视图更新
浅层
的响应式:修改 深层数据
时,数据修改成功
,但 不会立刻更新视图
不要同时使用!
<div>Man:{{ Man }}div>
<button @click="changeMan">改变refbutton>
import { ref, Ref } from "vue"
// 使用 type 定义类型
type M = {
name: string;
};
const Man: Ref<M> = ref({ name: "小满" });
function changeMan() {
Man.value.name = "大满1";
}
<div>Man2:{{ Man2 }}div>
<button @click="changeMan">改变refbutton>
import { shallowRef, ShallowRef } from "vue"
// 使用 type 定义类型
type M = {
name: string;
};
const Man2: ShallowRef<M> = shallowRef({ name: "小满" });
function changeMan() {
Man2.value.name = "大满2";
// 这里打印更新,但是不更新视图
console.log("Man2.value.name", Man2.value.name);
}
控制台打印数据已更新:
但视图未更新:
<div>Man:{{ Man }}div>
<div>Man2:{{ Man2 }}div>
<button @click="changeMan">改变refbutton>
import { ref, Ref, shallowRef, ShallowRef } from "vue"
// 使用 type 定义类型
type M = {
name: string;
};
const Man: Ref<M> = ref({ name: "小满" });
const Man2: ShallowRef<M> = shallowRef({ name: "小满" });
function changeMan() {
Man.value.name = "大满1";
Man2.value.name = "大满2";
}
视图全部更新:
强制触发视图的更新
ref
在源码中 会调用 triggerRef
进行视图的 强制更新
,这也就是 ref 和 shallowRef 混用时 shallowRef 视图也会被更新的原因我们使用 shallowRef + triggerRef 看看效果:
<div>Man2:{{ Man2 }}div>
<button @click="changeMan">改变refbutton>
import { shallowRef, ShallowRef, triggerRef } from "vue"
// 使用 type 定义类型
type M = {
name: string;
};
const Man2: ShallowRef<M> = shallowRef({ name: "小满" });
function changeMan() {
Man2.value.name = "大满2";
triggerRef(Man2);
}
果然,视图更新:
自定义 ref
两个参数
,track 收集变化
、trigger 触发更新
返回一个对象
,里面自定义 get
和 set
函数。get 中先收集变化再返回值
,set 中先设置新值再触发更新
<div>custom:{{ custom }}div>
<button @click="change">改变customRefbutton>
import { customRef } from "vue"
function MyRef<T>(value: T) {
return customRef((track, trigger) => {
return {
get() {
track();
return value;
},
set(newVal) {
value = newVal;
console.log("触发set");
trigger();
},
};
});
}
const custom = MyRef<string>("xiaoman");
function change() {
custom.value = "daman";
}
点击后视图会立刻更新:
function MyRef<T>(value: T, delay = 500) {
let timer: any = null;
return customRef((track, trigger) => {
return {
get() {
track();
return value;
},
set(newVal) {
clearTimeout(timer);
timer = setTimeout(() => {
console.log("触发set", newVal);
value = newVal;
trigger();
}, delay);
},
};
});
}
/**
* createRef 中
* 1、若 isRef = true,则直接返回;否则走进 RefImpl(实现 ref 的类)中
*
* 2、RefImpl:跟上面的 customRef 非常类似,原理相同(get\set,track\trigger)
*
* - get 时要追踪变化,trackRefValue(依赖收集)
* - set 时要触发更新,triggerRefValue -> triggerEffects(依赖更新)
*
* ref 和 triggerRef 在源码中都会调用 triggerRefValue -> triggerEffects,所以都会触发更新
* 所以 ref 不能和 shallowRef 混用!会导致 shallowRef 被更新
*
* 3、若 shallow 传 true(即为shallowRef)
* - _rawValue 直接返回
* - _value 直接返回
*
* 4、若 shallow 传 false(即为 ref)
* - _rawValue 走进 toRaw 中(后续学习)
* - _value 则会走进 toReactive 中,toReactive 中先判断是不是引用类型,
* - 如果是的话调用 toReactive 函数将其变为响应式对象
* - 如果不是直接返回
* - 相当于 ref 内部实现响应式还是用的 reactive !!
*
*/