vue3 ref和reactive全家桶(小满zs vue3 笔记六)
tip1: 无响应式数据(控制台数据已经变化,但是页面无刷新),为什么要用ref和reactive
<template>
<div>认识Ref全家桶</div>
<div>{{ message }}</div>
<button @click="change">改变</button>
</template>
<script setup lang="ts">
let message: string = '无响应式数据' // console.log 打印改变了,但message没有刷新,原因不是响应式
const change = () => {
message = '改变数据'
console.log(message)
}
</script>
tip2: ref和reactive的区别
1. ref用于原始值基本数据类型不支持对象(不是深层次的可以用ref),reactive支持对象
2. ref定义的需要.value来访问,reactive则不需要
3. ...
tip3: 技巧2 格式化vue3打印结构
原格式(包几层还需要在dep里的_value里取):

优化后的格式:



tip4: 用户代码片段,自动生成模板面板,
1. 设置模板 vscode 最下面齿轮设置图标->用户代码片段->输入vue.json来设置模板,如下:
{
"vue3": {
"prefix": "vue3",
"body": [
"<template>",
"<div>",
"</div>",
"</template>",
"",
"<script setup lang='ts'>",
"",
"</script>",
"",
"<style>",
"",
"</style>",
"$2"
],
"description": "Log output to console"
}
}
tip5: 如何获取dom上元素内容
dom:
<div ref="div"></div>
ref的名称和获取后的一致,这里的名称是div
js:
const div = ref<HTMLDivElement>()
const change = () => {
message.value = '改变customRef'
console.log(div.value?.innerText) // 这样可以获取ref元素为div上的属性数据
}
一. ref, Ref (响应式数据)
<template>
<div>认识Ref全家桶</div>
<div>{{ messageRe }}</div>
<div>{{ messageR }}</div>
<button @click="change">改变</button>
</template>
<script setup lang="ts">
import { ref, Ref } from 'vue'
let messageR = ref('响应式数据') // 自动推类型
// 或
let messageRe: Ref<string> = ref('响应式数据') // 通过Ref设置类型,因为只设置类型会报错,两边不一致<string> !== ref<string>,
需要通过Ref来设置,这样两边类型才能相等,才能改变它的值
const change = () => {
messageR.value = '响应式数据-改变'
messageRe.value = '响应式数据-改变Ref'
console.log(messageR, messageRe)
}
</script>
二. isRef
<template>
<div>isRef,是判断是否是ref类型</div>
<div>{{ messageRe }}</div>
<button @click="change">改变</button>
</template>
<script setup lang="ts">
import { ref, Ref, isRef } from 'vue'
let messageR = ref('响应式数据')
// 或
let messageRe: Ref<string> = ref('响应式数据')
const change = () => {
messageR.value = '响应式数据-改变'
console.log(messageR, messageRe, isRef(messageR), isRef(messageRe))
}
</script>
三. shallowRef(浅层次,不能改变vlaue值)
<template>
<div>
<div>{{ message }}</div>
<button @click="change">改变</button>
</div>
</template>
<script setup lang='ts'>
import { Ref, shallowRef } from 'vue'
type Obj = {
name: string
}
let message: Ref<Obj> = shallowRef({
name: '认识shallowRef'
})
const change = () => {
message.value.name = '改变shallowRef'
// message.value = { name: '监听改变shallowRef'}
// console.log里的数据已经改变,但页面数据一直不刷新
// 强制改变triggerRef, shallowRef和ref不能放在一块写,放在一块会多次调用triggerRef,ref底层就是调用triggerRef
// triggerRef(message)
console.log(message)
}
</script>
四. customRef,
track收集依赖,
trigger 触发依赖
<template>
<div ref="div">
customRef: {{ message }}
</div>
<button @click="change">customRef更新</button>
</template>
<script setup lang='ts'>
import { customRef, ref } from 'vue'
let timer: any // 防抖
// T泛型
function myRef <T = any>(value: T) {
// track
return customRef((track, trigger) => {
return {
get () {
track() // 收集依赖
return value
},
set (newVal) { // 更新值
// 调用接口时
clearTimeout(timer)
timer = setTimeout(() => {
value = newVal
console.log('=====触发了')
timer = null
}, 500);
trigger() // 触发依赖
}
}
})
}
let message = myRef<string>('customRef')
const div = ref<HTMLDivElement>()
const change = () => {
message.value = '改变customRef'
console.log(div.value?.innerText) // 这样可以获取ref元素为div上的属性数据
}
</script>
五. ref源码解析
packages -> reactivity -> ref.ts
// 构造多种场景类型
export function ref<T extends Ref>(value: T): T
export function ref<T>(value: T): Ref<UnwrapRef<T>>
export function ref<T = any>(): Ref<T | undefined>
export function ref(value?: unknown) {
// 创建ref ,传入当前value及false
return createRef(value, false)
}
function createRef(rawValue: unknown, shallow: boolean) {
// 判断如果是Ref时直接返回
if (isRef(rawValue)) {
return rawValue
}
// 否则创建,rawValue当前value值, shallow false
return new RefImpl(rawValue, shallow)
}
// 创建
class RefImpl<T> {
private _value: T
private _rawValue: T
public dep?: Dep = undefined
public readonly __v_isRef = true
// 接收,如果是true时直接返回value,否则执行toReactive
constructor(value: T, public readonly __v_isShallow: boolean) {
this._rawValue = __v_isShallow ? value : toRaw(value)
this._value = __v_isShallow ? value : toReactive(value)
}
// 收集依赖
get value() {
trackRefValue(this)
return this._value
}
// 触发依赖
set value(newVal) {
const useDirectValue =
this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
newVal = useDirectValue ? newVal : toRaw(newVal)
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal
this._value = useDirectValue ? newVal : toReactive(newVal)
triggerRefValue(this, newVal)
}
}
}
shallowRef, 直接传的是true,所以它的值不会变
六. reactive, readonly只读,shallowReactive(只到第一个属性那里.a = {},会被reactive影响)
1. 表单提交,只能触发对象,字符串和number不支持
<template>
<div>
reactive:
<form>
<div>{{ form.name }}</div>
<div>{{ form.age }}</div>
<input v-model="form.name"/>
<input v-model="form.age"/>
<!-- 不写.prevent 会触发本身的组件属性来刷新 -->
<button @click.prevent="submit">提交表单</button>
</form>
</div>
</template>
<script setup lang='ts'>
import { reactive } from 'vue';
type Ages = {
name: string,
age: number
}
let form = reactive<Ages>({
name: 'li li',
age: 32
})
const submit = () => {
console.log(form)
}
</script>
2. 数组添加,reactive proxy 不能直接赋值,否则破坏响应式对象,可以作为一个属性去赋值解决
<template>
<div>
reactive:
<form>
<ul>
<li v-for="(item, index) in items" :key="index">{{ item }}</li>
</ul>
<!-- 不写.prevent 会触发本身的组件属性来刷新 -->
<button @click.prevent="submit">提交表单</button>
</form>
</div>
</template>
<script setup lang='ts'>
import { reactive } from 'vue';
type Obj = {
array?: Array<string>
}
const items = reactive<Obj>({
array: []
})
const readOnlyItems = readOnly(items)
const submit = () => {
// 如果是接口返回的话
const data = ['aa', 'bb', 'cc']
// 直接赋值会报错,
// items = data
// 1. push
// items.push(...data)
// 2. 可以为它增加一个属性
items.array = data
// 直接修改会报警告,可以修改items.array,不能修改readOnlyItems的array,因为是只读的
readOnlyItems.array = data
console.log(items) } </script>
七. reactive源码
packages -> reactivity -> reactive.ts
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive(target: object) {
// if trying to observe a readonly proxy, return the readonly version.
// 判断是否是只读的代理,如果是直接返回
if (isReadonly(target)) {
return target
}
// 创建reactive对象
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap
)
}
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>
) {
// 判断是否是object对象类型, 否则报警告,reactive是不能处理string,number常规类型的
if (!isObject(target)) {
if (__DEV__) {
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
// target is already a Proxy, return it.
// exception: calling readonly() on a reactive object
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}
// target already has corresponding Proxy
// 判断是否存在,如果存在直接返回
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
// only specific value types can be observed.
// 判断是否在白名单中,如果在直接返回
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) {
return target
}
// 创建代理对象
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
proxyMap.set(target, proxy)
return proxy
}
将来的自己,会感谢现在不放弃的自己!

浙公网安备 33010602011771号