vue3.2单文件组件 <script setup> 语法糖常用写法

<script setup> 是在单文件组件中使用组合式 API 的编译时语法糖,当使用 <script setup> 的时候,任何在其中声明的顶层的绑定 (包括变量函数声明,以及 import 导入的内容) 都能在模板中直接使用。

<template>
  <div><Child></Child>{{ count }}</div>
</template>
<script setup>
import { ref } from 'vue'
import Child from './Child'
const count = ref(0)
</script>

响应式数据

定义响应数据

* ref()

接受一个内部值(所有类型),返回一个响应式的、可更改的 ref 对象,此对象只有一个指向其内部值的属性 .value,取值和赋值时都需要加.value
Vue 3中,可以使用ref来创建对DOM元素或组件实例的引用,从而操作dom

shallowRef()

创建浅层响应式数据的API。与‌ref‌不同,‌shallowRef‌只对数据结构的最外层进行操作或跟踪,不会递归地追踪嵌套对象或数组中的所有属性变化‌,即value 具有响应式,而 value 中的属性不具有响应式

customRef()

创建一个自定义的、可响应的引用对象。与 ref 和 shallowRef 不同的是, customRef 可以自定义 get 和 set 方法的实现逻辑,从而实现更加灵活的响应式行为。

triggerRef()

强制更新页面 dom ,使其具有响应式,需要配合 shallowRef 使用

isRef()

‌isRef函数用于检查一个值是否是一个ref对象

<template>
  <div>ref:{{ p1 }} <button @click="changeRef">改变ref</button></div>
  <div>shallowRef:{{ p2 }} <button @click="changeShallowRef1">改变shallowRef</button></div>
  <div>shallowRef配合triggerRef:{{ p2 }} <button @click="changeShallowRef2">改变shallowRef</button></div>
  <div>customRef:{{ p3 }} <button @click="changeCustomRef">改变customRef</button></div>
  <button ref="domRef" @click="getDom">获取dom</button>
</template>
<script setup lang="ts">
import {ref,isRef,shallowRef,triggerRef,customRef} from 'vue'
function myRef<T = any>(value: T) {
  let timer: any
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newVal) {
        clearTimeout(timer)
        timer = setTimeout(() => {
          console.log('触发了set')
          value = newVal
          trigger()
        }, 500)
      }
    }
  })
}
const p1 =ref({
  name:'default',
})
const p2 =shallowRef({
  name:'default'
})

const p3 = myRef<string>('default')
const domRef=ref<HTMLDivElement>()

const changeRef=()=>{
  p1.value.name='ref'
}
const changeShallowRef1=()=>{
  p2.value.name='shallowRef1' // 不会更新视图
  p2.value={
    name:'shallowRef2'
  }
}
const changeShallowRef2=()=>{
  p2.value.name='shallowRef3' // 不会更新视图
  triggerRef(p2)
}
const changeCustomRef=()=>{
  p3.value='customRef'
}
const getDom=()=>{
  console.dir(domRef.value)
}
console.log(p1)
console.log(isRef(p2))
console.dir(domRef.value) // undefined,此时获取不到
</script>

* reactive()

接受一个引用类型数据,返回一个响应式数据(Proxy(Object)或Proxy(Array))。

const list =reactive(['1'])
const obj =reactive({name:'张三'})

在这里插入图片描述

ps:返回的响应式数据不能直接整个赋值,因为VUE无法追踪到这个赋值操作,这样做数据将失去响应性‌(视图不会更新)。
修改reactive声明的数据,Proxy(Object),对其属性赋值,若想整体赋值可以使用Object.assign()方法,如果是Proxy(Array),可以通过push、splice等方法修改数组。

<template>
  <div>list:{{ list }}</div>
  <div>obj1:{{ obj1 }}</div>
  <div>obj2:{{ obj2 }}</div>
  <button @click="change">change</button>
</template>
<script setup lang="ts">
import {reactive} from 'vue'
const list =reactive(['1'])
let obj1 =reactive({name:'张三'})
let obj2 =reactive({age:18})

list.push(...['2','3'])
obj1.name='李四'
obj1.age=18 // 新增属性会响应
const change=()=>{
  // 直接赋值会失去相应
  obj1={
    name:'王五'
  }
  obj2=Object.assign(obj2, { age: 20}) // 这样赋值不会失去相应
  console.log(obj1,obj2)
}
</script>
</script>

* computed()

用于声明计算属性,计算属性会根据其依赖的响应式数据自动更新,并且具有缓存特性。适合计算派生状态或数据。当依赖的属性的值发生变化的时候,才会触发他的更改;如果依赖的值不发生变化,使用的是缓存中的属性值。
computed() 可以传入一个带有get和set方法的对象,或一个getter函数

<template>
  <div>{{ firstName }}-{{ lastName }}</div>
  <div>{{ name }} <button @click="change">change</button></div>
</template>
<script lang="ts" setup>
import { ref, computed} from 'vue'
const firstName=ref('张')
const lastName=ref('三')
const name=computed({
  get(){
    return firstName.value+lastName.value
  },
  set(newVal){
    firstName.value=newVal[0]
    lastName.value=newVal[1]
  }
})

const length = computed(() => {
  return firstName.value.length
})
const change=()=>{
  name.value='李四'
}
console.log(name,length)

</script>

toRef()、toRefs()、 toRaw()

  • toRef(obj, key) 接受一个响应式对象(源对象)和对象属性名,创建一个 ref 对象。即将对象中的某个属性转化为响应式数据。
  • toRefs(obj)是将一个响应式对象的所有属性,转换成Ref对象集合,这个方法可以方便的解构响应式对象。
  • toRaw(obj)将响应式对象转为原始对象。做一些不想被监听的事情,从 ref 或 reactive 得到原始数据。
    ps:修改原响应式数据,toRaw 转换得到的数据会被修改,视图也会更新;如果修改 toRaw 得到的原始数据,原数据也会被修改,但是视图不更新
<template>
  <div>obj1:{{ obj1 }}</div>
  <div>obj2:{{ obj2 }}</div>
  <div>name:{{ name }}</div>
  <div>age:{{ age }}</div>
  <div>like:{{ like }}</div>
  <button @click="change">change</button>
</template>
<script setup lang="ts">
import {reactive,toRef,toRefs} from 'vue'

const obj1 =reactive({
  name:'张三'
})

const obj2 =reactive({
  like:'篮球',
  age:18
})
const name=toRef(obj1,'name')
const {like,age}=toRefs(obj2)
const change=()=>{
  name.value='李四'
  like.value='足球'
  age.value=20
}
</script>

ps:如修改toRef()和toRefs()方法得到的Ref对象的值,源对象的属性值也会相应改变,如以上代码中修改name.value,obj的name属性也会被修改。

ref与reactive的区别

  • 数据类型支持‌:
    ‌ref‌可以处理基本数据类型(如字符串、数字、布尔值等)以及对象和数组。使用ref定义的数据需要通过.value属性访问和修改‌。
    ‌reactive‌用于处理对象和数组,对于基础数据类型无效。使用reactive定义的响应式对象,如果操作不当,可能会失去响应性‌,如:赋值给 reactive 一个对象或 reactive 对象,而不是对其属性进行赋值。
  • 使用场景‌:
    ‌ref‌:适用于大部分场景,如处理基本数据类型、对象和数组。
    ‌reactive‌:一般适用于复杂的嵌套对象。

监听器

当需要在数据变化时执行一些“副作用”:如更改 DOM、执行异步操作,可以使用监听器。
使用同步语句创建的侦听器当组件卸载的时候会停止监听;使用异步回调创建一个监听器器,必须手动停止,以防内存泄漏。

watch()

watch()用于监听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。
参数:

  1. 监听器的数据源(ref/响应式对象/函数)
  2. 发生变化时的回调函数
  3. 配置对象
    immediate:在监听器创建时立即触发回调
    deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。
    flush:指定回调函数的刷新时机。
    • pre (默认)dom 更新前调用回调
    • post dom 更新后调用回调
    • sync 同步调用回调
      onTrack / onTrigger:用于调试的钩子。在依赖收集和回调函数触发时被调用
import { watch } from 'vue'
// 监听ref
watch(hidden, (newValue, oldValue) => {
  console.log(`${oldValue} -> ${newValue}`)
})
// 监听proxy对象,vue3将强制开启deep深度监听,和vue2一样,oldValue和newValue相同
watch(state, newValue => {
  console.log(newValue)
})
// 使用函数监听proxy数据的某个属性
watch(
  () => state.family,
  newValue => {
    console.log(newValue)
  },
  {
    deep: true // family是引用数据,需开启deep属性
  }
)
// 使用数组监听proxy数据的某些属性
watch([() => state.name, () => state.age], ([newName, newAge], [oldName, oldAge]) => {
  console.log(`${newName}:${newAge}->${oldName}:${oldAge}`)
})
// 侦听多个数据源
watch([hidden, () => state.name], ([newHidden, newName], [oldHidden, oldName]) => {
  console.log(`${newName}:${newHidden}->${oldName}:${oldHidden}`)
})

watchEffect()

watchEffect()会立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行,当组件卸载的时候会停止监听。
参数:

  1. 要运行的副作用函数
  2. 一个可选的选项
    flush: 指定回调函数的刷新时机
    onTrack / onTrigger:用于调试的钩子。在依赖收集和回调函数触发时被调用
import { watchEffect } from 'vue'
watchEffect(()=>{
  console.log("watchEffect配置的回调执行了",hidden.value)
})

停止监听

const stop=watch(hidden, (newValue, oldValue) => {
  console.log(`${oldValue} -> ${newValue}`)
})

setTimeout(()=>{
    stop()
}, 3000)

组件传值

defineProps() / defineEmits() 接收父组件的props/向父级组件传值

  • defineProps(): 子组件使用defineProps接收父组件的props
  • defineEmits(): 子级组件向父级组件传值
    子组件
<template>
  <span >{{ props.name }} </span>
  <input type="text" @change="changeName" v-model="name">
</template>

<script setup>
import { ref} from 'vue'
const props = defineProps({
  name: { type: String, required: true }
})
const emit = defineEmits(['updateName'])
const name= ref(props.name)
const changeName = () => {
  emit('updateName', name.value)
}
</script>

父组件

<template>
  <Child :name="name" @updateName="handleUpdate" />
</template>

<script setup>
import { ref} from 'vue'
import Child from './components/Child.vue'
const name=ref('张三')
const handleUpdate=(newName)=>{
  console.log(newName)
  name.value=newName
}
</script>

模板ref / defineExpose()

  • 模板ref 和选项式api一样,在模板中用ref 注册元素或子组件的引用
  • defineExpose():script setup语法糖不能直接使用ref 取到子组件的方法和变量,需使用defineExpose方法暴露组件的属性以及方法供外部使用。

子组件

<template>
  <input type="text" @change="changeName" v-model="name">
</template>
<script setup>
import { ref,defineExpose } from 'vue'
const emit = defineEmits(['updateName'])
const name= ref('张三')
const changeName = () => {
  emit('updateName', name.value)
}
// 将方法、变量暴露给父组件使用,父组件才可通过ref API拿到子组件暴露的数据
defineExpose({
  name,
  changeName
})
</script>

父组件

<template>
  <Child ref="root" />
</template>

<script setup>
import { ref,onMounted } from 'vue'
import Child from './components/Child.vue'
const root = ref(null)
onMounted(() => {
  console.log(root.value) // DOM 元素将在初始渲染后分配给 ref
})
</script>

provide() / inject()

依赖注入

  • provide()
    提供一个值,可以被后代组件注入,provide()接受两个参数:第一个参数是要注入的 key,可以是一个字符串或者一个 symbol,第二个参数是要注入的值。
  • inject()
    注入一个由祖先组件或整个应用 (通过 app.provide()) 提供的值

父组件

import { ref,provide  } from 'vue'
// 提供静态值
provide('size','small')
// 提供响应式的值
const loading = ref(false)
provide('loading', loading)

后端组件

import { inject } from 'vue'
const size = inject('size')
const loading = inject(loading)

插槽

  1. 在子组件的模板中使用<slot>元素定义插槽出口
<template>
 <div>
   <slot name="title"></slot>
    <slot name="content" msg="子组件的数据"></slot>
    <slot></slot>
 </div>
</template>
  1. 在父组件的模板中使用v-slot:(缩写:#)指令声明具名插槽或是期望接收 props 的作用域插槽.
<template>
  <Child>
    <template v-slot:title>标题</template>
    <template #content="{ msg }">内容:{{msg}}</template>
    <span>作者:admin</span>
  </Child>
</template>

参考:vue中slot组件的使用

useSlots() 和 useAttrs()

import { useSlots, useAttrs } from 'vue'
// 获取插槽的信息
const slots = useSlots()
// 获取过来的所有属性
const attrs = useAttrs()

常用生命周期钩子

钩子函数生命周期选项执行条件备注
onBeforeMount()beforeMount在组件被挂载之前被调用
onMounted()mounted在组件挂载完成后执行
onBeforeUpdate()beforeUpdate在组件即将因为响应式状态变更而更新其 DOM 树之前调用
onUpdated()updated在组件因为响应式状态变更而更新其 DOM 树之后调用
onBeforeUnmountbeforeDestroy在组件实例被卸载之前调用
onUnmounted()destroyed在组件实例被卸载之后调用
onErrorCaptured()errorCaptured在捕获了后代组件传递的错误时调用

顶层 await

await setup前面会自动加一个async

const data = await fetch("/api/demo")

使用路由

import { useRoute, useRouter } from 'vue-router'
const route = useRoute() // 返回当前路由地址
const router = useRouter() // 返回 router 实例
import { nextTick } from 'vue'
// DOM更新完毕之后执行回调的两种写法
nextTick(() => {
  console.log('DOM更新完毕')
})
await nextTick()
console.log('DOM更新完毕')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值