LOADING

vue3

2022/1/23

各种 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 小技巧
  1. 打开这个能够让我们观察 value 方便一点,控制台点一下就行,少点一下。就是格式化了,ref 系列跟 reactive 系列都可以使用

image.png

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>

组件

动态组件
异步组件

watch