各种 Vue 指令用法
v-text(纯文本)
<template>
<div v-text="zs"></div>
</template>
<script setup lang="ts">
const zs: string = "ok";
</script>
v-html
<template>
<div v-html="zs"></div>
</template>
<script setup lang="ts">
//使用v-html,外面一层要是section
const zs: string = '<section style="color:red">小红帽班花姐姐</section>';
</script>
ref 与 reactive
ref
ref() 接收参数,并将其包裹在一个带有 .value 属性的 ref 对象中返回
要响应改变值的话,加.value,因为这是一个代理对象,真正的值存放在.value 中
ref 在模板中会自动解包,所以使用不需要加.value(包括在模版的 js 表达式中)
DOM 更新时机
当你修改了响应式状态时,DOM 会被自动更新。但是需要注意的是,DOM 更新不是同步的。Vue 会在“next tick”更新周期中缓冲所有状态的修改,以确保不管你进行了多少次状态修改,每个组件都只会被更新一次。
要等待 DOM 更新完成后再执行额外的代码,可以使用 nextTick() 全局 API
import { nextTick } from 'vue'
async function increment() {
count.value++
await nextTick()
// 现在 DOM 已经更新了
}
reactive
reactive 方法是不允许传递原始类型的值的,它并没有和 ref 一样检测到原始类型之后做对应的转换
const reactive1 = reactive(0); // NOT OK
const reactive2 = reactive({ count: 0 }) // OK
可以做到深层响应式
对解构操作不友好
当我们将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接
const state = reactive({ count: 0 })
// 当解构时,count 已经与 state.count 断开连接
let { count } = state
// 不会影响原始的 state
count++
// 该函数接收到的是一个普通的数字
// 并且无法追踪 state.count 的变化
// 我们必须传入整个对象以保持响应性
callSomeFunction(state.count)
readonly 属性
<script>
let obj = reactive({ name: "小余" });
const read = readonly(obj);
//readonly就是把被他包在里面的变成只读的,但会受到reactive的影响
//修改obj的属性值会影响read的值
</script>
shallowReactive
一样跟 shallowRef 是浅层次的修改
但一样跟 shallowRef 一样,shallowReactive 也会受到 Reactive 的影响
ref 与 reactive 的对比
1.ref 支持所有类型,Reactive 支持引用类型,Array,Object,Map,Set
2.ref 的 watch 需要加参数{ deep: true },而 reactive 不需要
3.ref 取值是需要.value 的,reactive 取值的话不需要.value(在 script 中) 4.都支持深层响应式
额外细节
为什么要使用 ref?
额外的 ref 解包细节
toRef,toRefs,toRaw
toRef
用法
let props = defineProps<{ msg: string; data: any }>();
//toref接收两个参数,第一个为对象,第二个key
const refName = toRef(props.data, "name");
toref 只能修改响应式对象的值,对于非响应式视图毫无变化,但值会改变(可以改变数据,但是数据此时不能驱动试图)
应用场景
vue 是单向数据流,父组件向子组件传值,子组件无法修改值,但通过 toref 即可影响父组件传过来的值
toRefs
let props = defineProps<{ msg: string ,data: any}>();
用于解构reactive的值,一旦reactive解构,响应式就不存在了,所以需要toRefs保持响应式
const {msg,data} = toRefs(props)
console.log(msg,data)
//注意这边无法修改传进来的值
msg.value = 'hhhh';
toRaw
toRaw 把 reactive 套上的 Proxy 外壳给脱掉了,一个响应式对象通过 toRaw 之后将变成一个普通对象
isRef
作用:判断一个东西是不是一个 ref 对象,结果返回布尔值
console.log(isRef(Man))
shallowRef
作用:shallowRef 是浅层次的响应,而 ref 是深层次的响应
shallowRef 只响应到.value,如果这个.value 后面在跟值进行修改,就只能在控制台中看见值改变了,而没办法响应到页面中
也就是创建一个跟踪自身 .value 变化的 ref,但不会使其值也变成响应式的
注意:ref 不能跟 shallowRef 一起使用,不然 shallowRef 会被 Ref 影响,从而造成视图的更新(失去浅层次响应的作用)
<template>
<div>Ref:{{XiaoYu}}</div>
<div>shallowRef:{{XiaoYu2}}</div>
<button @click="change()">修改</button>
</template>
<script setup lang="ts">
import {ref,isRef,shallowRef} from 'vue'
const XiaoYu = ref({name:"小余"})
const XiaoYu2 = shallowRef({name:"小余2号"})
const yupi = {name:"鱼皮"}
const change = () =>{
// XiaoYu.value.name = '小余抓住了一只yupi' 这里必须注销掉,不如ref会影响到shallowRef,导致视图的更新(也就是会造成下方的小余今天摸鱼了也一同刷新)
XiaoYu2.value.name = '小余今天摸鱼了'
console.log(XiaoYu)
console.log("这是小余",isRef(XiaoYu))
console.log("这是鱼皮",isRef(yupi))
}
</script>
triggerRef
- 因为在 ref 底层更新的逻辑的时候,会调用 triggerRef
- 而 triggerRef 会强制更新我们这个收集的依赖
- 强制更新页面 DOM
customRef
- 让我们自己去自定义一个 ref(可以自己去写 get,set 就是在实现响应式的时候去做其他事情)
- customRef 是个工厂函数要求我们返回一个对象 并且实现 get 和 set 适合去做防抖之类的
- customRef 也是一个浅层的响应
- 里面返回的是 set 跟 get 两个方法,回调里面接收的是两个函数,一个是 track,一个是 trigger
- track:收集依赖,收集完 return 回去
- 触发依赖交给 set,接收一个 newVal
- track 作用:通知 Vue 追踪 value 的变化 (相当于提前和 get 商量一下,让他认为这个 value 有用的!)
- trigger 作用:通知 Vue 重新解析模板。
<template>
<div>{{ obj }}</div>
//不用设置ref了,因为我们自己手动实现了一个
<hr />
<div>
{{ name }}
</div>
<hr />
<button @click="change()">修改</button>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, shallowRef, customRef } from 'vue'
//这下面是customRef的演示,自己定义ref,接近源码的表现
const change = ()=>{
function MyRef<T>(value:T){//泛型
let timer:any
return customRef((track, trigger)=>{//track:收集依赖的 trigger:触发依赖
return{
get(){
track()
return value//收集完依赖就返回 回去了
},
set(newVal){//这里会收到新的值,调用接口,调用太多次的话,服务器的压力就会很大,这个时候我们就可以自己设置一个防抖节流
clearTimeout(timer)//清除定时器,不让他同时存在多个定时器(同一时间只能有一个定时器)
timer = setTimeout(()=>{
console.log("吃午饭了")
value = newVal//将新值赋给value
timer = null//清空一下时间
trigger()
},500)//时间间隔设置为0.5秒,防抖节流
}
}
})
}
const obj = MyRef<string>('customRef小余')
const change = ()=>{
obj.value = "customRef被修改了,到饭点准备干饭了"
}
</script>
<style scoped></style>
ref 获取 DOM 元素
在 html 部分中 ref=”xxx”,这个 xxx 要与在 JavaScript 里面声明的变量名一样,比如 const xxx = ref<泛型>(),然后 console 控制台输出一下,ref.value.innerText 就能够获取到了
ref 小技巧
- 打开这个能够让我们观察 value 方便一点,控制台点一下就行,少点一下。就是格式化了,ref 系列跟 reactive 系列都可以使用
computer
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
若我们将同样的函数定义为一个方法而不是计算属性,两种方式在结果上确实是完全相同的,然而,不同之处在于计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。这意味着只要 author.books 不改变,无论多少次访问 publishedBooksMessage 都会立即返回先前的计算结果,而不用重复执行 getter 函数。
这也解释了为什么下面的计算属性永远不会更新,因为 Date.now() 并不是一个响应式依赖:
const now = computed(() => Date.now())
相比之下,方法调用总是会在重渲染发生时再次执行函数。
为什么需要缓存呢?想象一下我们有一个非常耗性能的计算属性 list,需要循环一个巨大的数组并做许多计算逻辑,并且可能也有其他计算属性依赖于 list。没有缓存的话,我们会重复执行非常多次 list 的 getter,然而这实际上没有必要!如果你确定不需要缓存,那么也可以使用方法调用。
事件处理
在内联事件处理器中访问事件参数
<!-- 使用特殊的 $event 变量 -->
<button @click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>
<!-- 使用内联箭头函数 -->
<button @click="(event) => warn('Form cannot be submitted yet.', event)">
Submit
</button>
组件
动态组件
异步组件