Vue3的基本使用

1. vue3的基本简介

Vue.js 3Vue 框架的重大版本升级,专注于性能提升、开发体验优化和更好的 TypeScript 支持。其核心特性如下:

特性说明
Composition API引入 setup() 函数和响应式函数(ref, reactive),逻辑复用更灵活,替代 Options API 的碎片化问题。
性能优化虚拟 DOM 重构(Diff 算法优化)、Tree-shaking 支持(按需打包代码),包体积减少 41%。
TypeScript 支持源码用 TypeScript 重写,提供完整的类型定义,开发体验更友好。
新内置组件<Teleport>:跨组件层级渲染内容 - <Suspense>:异步组件加载状态管理
Fragment 支持单组件支持多根节点,无需外层包裹 <div>

2. 创建Vue3工程

  • vue-cli创建

    npm create vue
    
  • vite创建

    npm create vite@latest project-name -- --template vue
    cd <project-name>
    npm install
    // 启动开发服务器
    npm run dev
    

Vue3项目结构说明:

my-vue-app/
├── src/
│   ├── assets/       # 静态资源(图片、CSS)
│   ├── components/   # 组件目录
│   ├── App.vue       # 根组件
│   └── main.js       # 应用入口文件
├── index.html        # 主 HTML 文件
├── vite.config.js    # Vite 配置文件
└── package.json

使用vite创建vue3项目和使用vuecli创建vue3项目的核心区别如下:

特性ViteVue CLI
底层构建工具基于 ESBuild(开发) + Rollup(生产)基于 Webpack
启动速度极快(毫秒级冷启动)较慢(需打包所有模块)
热更新(HMR)极快(按需更新)较慢(需重新打包部分模块)
配置复杂度轻量级,开箱即用复杂,依赖 vue.config.js
生态兼容性兼容 Rollup 插件生态依赖 Webpack 插件生态
浏览器支持默认面向现代浏览器(ES Modules)默认兼容旧浏览器(通过 polyfill)
官方定位下一代前端工具,Vue 3 官方推荐旧版 Vue 的官方脚手架

3. 常用的Composition API

3.1 setup函数

Vue 3 中,setup 函数是 Composition API 的核心,用于替代 Vue 2datamethods 等选项。setup函数在nbeforeCreate之前执行一次,所以thisundefined,所以在setup中不能使用thissetup函数的作用如下:

  • 初始化响应式数据:使用 refreactive 声明响应式变量。

  • 定义方法:直接在函数内声明并返回方法。

  • 访问组件上下文:通过参数获取 propsemitslots 等。

  • 组合逻辑:将相关逻辑组织在一起,提升代码可维护性。setup 中无法访问 this,所有逻辑通过 Composition API 实现。

    // 基本使用
    import { ref, reactive, onMounted } from 'vue';export default {props: {title: String},setup(props, context) {// 响应式数据const count = ref(0);const state = reactive({ name: 'Vue 3' });// 方法const increment = () => {count.value++;};// 生命周期钩子onMounted(() => {console.log('组件已挂载');});// 暴露给模板的数据和方法return {count,state,increment};}
    };
    

从上述案例可以看出setup函数有两个参数,如下:

  • prosprops 是组件接收的响应式属性,对应组件通过 props 选项声明的属性。

    • 响应式:props 是响应式对象,修改会导致组件更新。

    • 禁止直接解构赋值,否则会丢失响应式,需使用toRefstoRef

      // 父组件通过<Demo msg='你好' school='CQUT'>向子组件传递数据import { toRefs, toRef } from 'vue'// 需要声明
      props = ['msg', 'school']
      setup(props) {// 解构所有 props 并保持响应性const { title } = toRefs(props)// 处理可选 propconst subtitle = toRef(props, 'subtitle')return { title, subtitle }
      }
      
    • 只读性:子组件不应直接修改 props,需通过父组件传递新值。

  • context:提供组件上下文信息,包含三个核心属性:attrsslotsemit

    • attrs:包含未在 props 中声明的属性,如 classstylev-on 监听器等。避免基于 attrs 创建副作用,如 watch

    • slots:收到的插槽内容,相当于this.$slots

      <Demo @hello="sayHello">// vue3中推荐使用v-slot<template v-slot:aaa><span>你好子组件</span></template>
      </Demo>
      
    • emit:触发自定义事件,替代 Vue 2this.$emit

      // 父组件
      <Demo @hello="sayHello"></Demo>
      <script>setup() {function sayHello() {}
      }<script>// 子组件
      emits = ['hello']
      export default {emits: ['submit'],setup(props, { emit }) {const handleClick = () => {emit('submit', '数据')}return { handleClick }}
      }
      

3.2 _ref函数

Vue 3 中,ref 是组合式 API的核心函数,用于创建响应式的引用对象。

<template>count: {{ count }} <button @click="increment">count++</button>str: {{ str }}person.name: {{ person.name }}<hr>person.person_info.number: {{ person.person_info.number}}
</template><script>
import {ref} from "vue";export default {name: 'App',setup(props, context) {const count = ref(0);const str = ref('Hello Vue3');const person = ref({name: 'Tom',age: 18,person_info: {number: '666',}});function increment() {// 修改基本类型数据直接使用.valuecount.value++;// 对象数据类型只需要第一层.valueperson.value.person_info.number = '777';}return {count,str,increment,person};},
};
</script><style scoped></style>

对于ref函数,接收的数据可以是基本数据类型,也可以是对象数据类型,区别如下:

  • 基本数据类型:响应式依然是靠Object.defineProperty()getset完成的。
  • 对象数据类型:响应式本质上还是使用Vue3中的reactive,使用Proxy完成响应式。

3.3 reactive函数

Vue 3 中,reactiveComposition API 的核心函数之一,用于创建响应式的对象或数组。reactive函数不能用于基本数据类型。从现象上来看,reactive函数封装的对象数据操作不需要.value

import { reactive } from 'vue';const state = reactive({name: 'Alice',age: 30,address: {city: 'New York'}
});// 修改属性(自动触发视图更新)
state.age = 31;
state.address.city = 'Los Angeles';// 数组
const list = reactive(['apple', 'banana']);
list[0] = 'orange'; // 触发更新
list.push('grape');  // 触发更新
<template><div><p>Name: {{ state.name }}</p><p>Age: {{ state.age }}</p><p>City: {{ state.address.city }}</p></div>
</template>

reactive 返回原始对象的Proxy 代理,而非原始对象本身。验证如下:

console.log(state === reactive(state)); // true(同一代理)

ref对比如下:

特性reactiveref
适用类型对象、数组基本类型、对象、数组
访问方式直接访问属性(state.name.value(脚本中)
解构赋值需使用 toRefs 保持响应性需保持 .value 引用
模板使用直接使用属性(state.name自动解包(无需 .value

3.4 computed计算属性

通过 computed 函数创建计算属性,支持两种形式:

  • Getter 函数,只读

  • 对象形式,可读可写,含 getset 方法

    import { ref, computed } from 'vue'export default {setup() {const count = ref(0)// 1. 只读计算属性Getterconst doubleCount = computed(() => count.value * 2)// 2. 可读写计算属性Getter + Setterconst writableCount = computed({get: () => count.value + 1,set: (val) => {count.value = val - 1}})return { count, doubleCount, writableCount }}
    }
    
    <template><div><p>原始值: {{ count }}</p><p>只读计算值: {{ doubleCount }}</p><p>可写计算值: {{ writableCount }}</p><button @click="writableCount = 10">修改可写计算属性</button></div>
    </template>
    

computedref的区别如下:

特性refcomputed
数据来源直接赋值依赖其他响应式数据计算得出
缓存有【依赖不变时复用缓存值】
可写性可直接修改 .value默认只读,需配置 set 方法

下面是一些计算属性的使用场景:

  • 依赖多个数据源

    const firstName = ref('John')
    const lastName = ref('Doe')const fullName = computed(() => `${firstName.value} ${lastName.value}`)
    
  • 结合reactive对象

    const state = reactive({price: 100,quantity: 2,total: computed(() => state.price * state.quantity)
    })
    
  • 链式计算属性

    const discount = ref(0.8)
    const discountedTotal = computed(() => state.total * discount.value)
    

对于vue3中的计算属性的使用,有以下注意点:

  • 不要在计算属性内执行异步操作或副作用,如修改 DOM、发送请求。副作用应使用 watchwatchEffect 处理。

  • 确保计算函数轻量,避免复杂计算。复杂逻辑可拆分为多个计算属性。如果计算属性依赖大量数据,考虑使用 computed 的缓存特性替代 method

  • 解构 reactive 对象中的计算属性会丢失响应性,需用 toRefs

    const state = reactive({count: 0,double: computed(() => state.count * 2)
    })// ❌ 错误:解构后失去响应性
    const { double } = state// ✅ 正确:保持响应性
    const { double } = toRefs(state)
    

3.5 watch监视

Vue 3 Composition API 中,watch 函数用于观察响应式数据的变化并执行副作用操作,如异步请求、DOM 操作等。语法结构如下:

import { watch } from 'vue'watch(source,              // 要监听的数据源 ref/reactive/getter/数组callback,            // 变化回调函数options?: {          // 可选的配置项immediate?: boolean,deep?: boolean,flush?: 'pre' | 'post' | 'sync'}
)
选项说明
immediate是否立即执行回调,默认 false
deep是否深度监听对象/数组,默认 false
flush回调触发时机: pre:组件更新前【默认】; post:组件更新后 ;sync:同步触发

watch可以监听不同类型的数据源,如下:

  • 基本数据类型

    setup(props, context) {const count = ref(0);const str = ref('Hello Vue3');function increment() {count.value++;}function changePName() {person.name = 'Jerry';}// 监视单个基本数据类型watch(count, () => {console.log('count changed');})// 监视多个基本数据类型watch([count, str], () => {console.log('count 或 str changed');})
    }
    
  • 对象数据类型

    • 监听整个reactive对象,默认浅层监听。

      import { reactive, watch } from 'vue'const state = reactive({ name: 'Alice', profile: { age: 25 }
      })// 浅层监听,只响应对象引用变化
      watch(state,(newVal, oldVal) => {console.log('state 变化:', newVal)}
      )// ❌ 不会触发, 修改嵌套属性,对象引用未变
      state.profile.age = 30 // ✅ 触发(替换整个对象)
      state = reactive({ name: 'Bob' }) 
      
    • 深度监听嵌套属性,即deep: true。在该情况需要注意newValoldVal会指同一引用。

      watch(state,(newVal) => {console.log('深度监听:', newVal)},{ deep: true } // 启用深度监听
      )// ✅ 触发 修改嵌套属性
      state.profile.age = 30 
      
    • 监听特定属性

      • 使用getter函数监听单个属性

        watch(() => state.name,(newName, oldName) => {console.log('name变化:', oldName, '→', newName)}
        )// ✅ 触发
        state.name = 'Bob' 
        
      • 监听嵌套对象属性

        watch(() => state.profile.age,(newAge) => {console.log('age变化:', newAge)}
        )// ✅ 触发
        state.profile.age = 30 
        
      • 监听多个属性,使用数组形式

        watch([() => state.name, () => state.profile.age],([newName, newAge], [oldName, oldAge]) => {console.log(`多个变化: name=${oldName}${newName}, age=${oldAge}${newAge}`)}
        )
        

3.6 watchEffect函数

Vue 3Composition API 中,watchEffect 是一个用于自动追踪依赖并执行副作用的函数。它的特点是无需显式声明依赖,而是通过回调函数内部实际使用的响应式数据自动收集依赖。语法结构如下:

import { watchEffect } from 'vue'const stop = watchEffect((onInvalidate) => {// 副作用逻辑(自动追踪依赖)// onInvalidate: 注册清理函数(可选)},{flush?: 'pre' | 'post' | 'sync', // 回调执行时机onTrack?: (event) => void,        // 调试依赖追踪onTrigger?: (event) => void       // 调试依赖触发}
)// 手动停止监听
stop()

其核心特性如下:

  • 自动依赖收集:watchEffect 会自动追踪回调函数内部使用到的响应式数据。当依赖的数据变化时,回调函数会重新执行。
  • 立即执行:默认在初始化时立即执行一次回调,类似 watchimmediate: true
  • 无新旧值:回调函数不提供新旧值参数,仅关注当前值。
特性watchEffectwatch
依赖声明自动收集回调内的依赖显式声明数据源
初始执行立即执行需配置 immediate: true
新旧值提供 newValoldVal
适用场景依赖复杂或需要立即执行的副作用需要精确控制监听源和访问旧值时

3.7 Vue3生命周期

首先是Option API钩子,如下:

钩子函数执行阶段
beforeCreate组件实例初始化前【数据观测和事件配置未完成】
created组件实例初始化完成【数据观测完成,但 DOM 未生成】
beforeMount组件挂载到 DOM
mounted组件挂载到 DOM 后【可访问 DOM 元素】
beforeUpdate响应式数据变化,DOM 重新渲染前
updated响应式数据变化,DOM 重新渲染后
beforeUnmount组件卸载前【替代 Vue 2 的 beforeDestroy
unmounted组件卸载后【替代 Vue 2 的 destroyed
errorCaptured捕获子孙组件错误时触发
renderTracked渲染函数依赖的响应式数据被追踪时触发【开发模式调试用】
renderTriggered渲染函数依赖的响应式数据触发重新渲染时【开发模式调试用】

上述钩子使用配置项,符合Vue2风格,在Vue3中,推荐使用Composition API,如下::

钩子函数对应 Options API 钩子
onBeforeMountbeforeMount
onMountedmounted
onBeforeUpdatebeforeUpdate
onUpdatedupdated
onBeforeUnmountbeforeUnmount
onUnmountedunmounted
onErrorCapturederrorCaptured
onRenderTrackedrenderTracked
onRenderTriggeredrenderTriggered

生命周期执行流程图如下:同时使用Option API钩子和Composition API优先使用Composition API

父组件 setup → 父组件 beforeCreate → 父组件 created → 父组件 beforeMount↓
子组件 setup → 子组件 beforeCreate → 子组件 created → 子组件 beforeMount → 子组件 mounted↓
父组件 mounted → 父组件 beforeUpdate → 子组件 beforeUpdate → 子组件 updated → 父组件 updated↓
父组件 beforeUnmount → 子组件 beforeUnmount → 子组件 unmounted → 父组件 unmounted

setup() 函数中,通过 onXxx 函数注册生命周期钩子,如下:

import { onMounted, onUpdated } from 'vue'export default {setup() {// 挂载后操作onMounted(() => {// 异步请求推荐位置const response = await fetch('https://api.example.com/data')data.value = await response.json()timer.value = setInterval(() => {console.log('定时器运行中...')}, 1000)})// 更新后操作onUpdated(() => {clearInterval(timer.value)})}
}

3.8 自定义hook

组合式函数是一个普通 JavaScript 函数,内部使用VueComposition API,如 ref, reactivewatch 等的封装逻辑。命名通常以 use 开头,如 useFetch, useMouse,以便代码可读性。与Mixins对比如下:

特性组合式函数Mixins
逻辑复用方式函数调用,显式暴露状态和方法隐式合并到组件选项对象中
命名冲突无【通过解构命名控制】容易发生【需手动处理】
类型支持天然支持 TypeScript类型推断困难
代码组织按功能组织,代码集中分散在多个选项中

从上述介绍不难看出,自定义hook就是将setup中的Composition API进行了封装,以提高代码复用率。通常建立一个hooks文件夹,在里面存放自己的hook。如下面封装一个获取鼠标位置的hook

// hooks/useMouse.js
import { ref, onMounted, onUnmounted } from 'vue'export function useMouse() {const x = ref(0)const y = ref(0)const update = (event) => {x.value = event.pageXy.value = event.pageY}onMounted(() => window.addEventListener('mousemove', update))onUnmounted(() => window.removeEventListener('mousemove', update))return { x, y }
}
// 在组件中使用
<script setup>
import { useMouse } from './useMouse'// 调用组合式函数,解构响应式状态
const { x, y } = useMouse()
</script><template><div>鼠标位置:{{ x }}, {{ y }}</div>
</template>

3.9 toReftoRefs

Vue 3 中,toReftoRefs 是用于处理响应式对象属性的工具函数,它们的主要作用是将响应式对象的属性转换为 ref 对象,以保持响应性。它们的核心区别如下:

特性toReftoRefs
作用对象响应式对象的单个属性响应式对象的所有属性
返回值单个 ref 对象包含所有属性的 ref 对象的普通对象
适用场景需要保留对某个属性的响应式引用结构整个响应式对象,保持所有属性的响应性
是否处理不存在属性可为不存在的属性创建 ref【需谨慎】仅处理对象已存在的属性
  • toRef的使用:将响应式对象的某个属性转换为 ref

    import { reactive, toRef } from 'vue'const state = reactive({ name: 'Alice', age: 30 })
    const nameRef = toRef(state, 'name')// 修改 ref 会同步到原对象
    nameRef.value = 'Bob'
    console.log(state.name) // 输出: 'Bob'
    
    // 处理不存在的属性:若属性不存在,toRef 会创建一个可写的 ref,但不会自动添加到原对象const state = reactive({ name: 'Alice' })
    const ageRef = toRef(state, 'age') // 原对象无 age 属性// 修改 ref 的 value
    ageRef.value = 30
    console.log(state.age) // 输出: undefined 原对象未添加该属性
    
  • toRefs的使用:将响应式对象的所有属性转换为 ref

    import { reactive, toRefs } from 'vue'const state = reactive({ name: 'Alice', age: 30 })
    const stateRefs = toRefs(state)// 结构后仍保持响应性
    const { name, age } = stateRefs
    name.value = 'Bob'
    console.log(state.name) // 输出: 'Bob'
    
    // 处理嵌套对象
    const state = reactive({ info: { age: 30 } })
    const stateRefs = toRefs(state)// stateRefs.info 是 ref,其 value 是响应式对象
    stateRefs.info.value.age = 31 // 响应式更新
    

那么什么时候使用toRef,什么时候使用toRefs呢?

  • 单属性引用 → toRef
  • 多属性结构 → toRefs

4. 其它Composition API

4.1 shallowReactiveshallowRef

Vue3 中,shallowReactiveshallowRef 是用于浅层响应式处理的 API,适用于需要优化性能的场景。它们与标准 reactiveref 的关键区别在于仅对顶层数据做响应式处理,不递归追踪深层对象的变化。

  • shallowReactive :只对对象的第一层属性进行响应式处理,嵌套对象保持原始类型。当使用数据量大的对象,且仅需监听顶层字段变化的场景如配置对象、表单控件集合时使用。若需要响应嵌套对象的变化,需手动替换整个嵌套对象。

    import { shallowReactive, watchEffect } from 'vue'const state = shallowReactive({name: 'Alice',      // 响应式(顶层属性)profile: {          // ❌ 非响应式(嵌套对象)age: 25}
    })// 监听顶层属性变化
    watchEffect(() => {console.log('名字变化:', state.name) // ✅ 触发
    })// 监听嵌套属性变化
    watchEffect(() => {console.log('年龄变化:', state.profile.age) // ❌ 不触发
    })// 修改顶层属性 → 触发响应
    state.name = 'Bob'// 修改嵌套属性 → 不触发响应
    state.profile.age = 30
    
  • shallowRef :只处理基本数据类型的响应式,不进行对象的响应式处理。适合场景为存储大型对象,如 DOM 元素、复杂数据结构,避免深度响应式开销 || 需要整体替换值的场景,如分页数据更新。

    import { shallowRef, watchEffect } from 'vue'// 存储一个对象
    const userRef = shallowRef({name: 'Alice',profile: { age: 25 }
    })// 监听整个 .value 变化
    watchEffect(() => {console.log('用户变化:', userRef.value) // ✅ 触发(当 userRef.value 被替换时)
    })// 监听嵌套属性变化
    watchEffect(() => {console.log('年龄变化:', userRef.value.profile.age) // ❌ 不触发
    })// 修改嵌套属性 → 不触发响应
    userRef.value.profile.age = 30// 替换整个值 → 触发响应
    userRef.value = {name: 'Bob',profile: { age: 30 }
    }
    

4.2 readonlyshallowReadonly

Vue 3 中,readonlyshallowReadonly 是用于创建只读响应式对象的工具,适用于需要保护数据不被意外修改的场景,如传递 props、状态管理等。以下是它们的详细用法和区别:

  • readonly:递归地将整个对象及其嵌套属性变为只读。依然是响应式对象,但不可修改。

    import { reactive, readonly, watchEffect } from 'vue'const original = reactive({ name: 'Alice', profile: { age: 25 }
    })const readOnlyObj = readonly(original)// 尝试修改会触发警告(开发环境下)且操作无效
    readOnlyObj.name = 'Bob'          // ❌ 失败
    readOnlyObj.profile.age = 30      // ❌ 失败// 监听只读对象变化(仍会响应原始对象的修改)
    watchEffect(() => {console.log('只读对象的值:', readOnlyObj.name) // 原始对象修改时触发
    })// 修改原始对象 → 只读对象同步更新
    original.name = 'Bob'             // ✅ watchEffect 会触发
    
  • shallowReadonly:仅将对象的顶层属性变为只读,嵌套对象仍可变。顶层属性不可修改,但嵌套对象保持原始响应性。

    import { reactive, shallowReadonly } from 'vue'const original = reactive({ name: 'Alice', profile: { age: 25 }
    })const shallowReadOnlyObj = shallowReadonly(original)// 修改顶层属性 → 失败
    shallowReadOnlyObj.name = 'Bob'   // ❌ 失败// 修改嵌套属性 → 成功
    shallowReadOnlyObj.profile.age = 30 // ✅ 成功
    

4.3 toRawmarkRaw

Vue 3 中,toRawmarkRaw 是用于处理响应式对象和原始对象的工具函数,适用于需要绕过响应式系统或优化性能的场景。以下是它们的详细用法和核心区别:

  • toRaw:返回由 reactivereadonlyAPI 创建的响应式对象的原始非响应式对象。用于直接操作原始对象,避免响应式追踪的开销,如一次性批量修改数据。

    import { reactive, toRaw } from 'vue'const reactiveObj = reactive({ name: 'Alice', profile: { age: 25 } })
    const rawObj = toRaw(reactiveObj)// 修改原始对象 → 不会触发响应式更新
    rawObj.name = 'Bob'
    console.log(reactiveObj.name) // 输出: 'Bob'(数据已改,但不会触发视图更新)// 响应式对象仍可触发更新
    reactiveObj.name = 'Charlie' // ✅ 触发响应式更新
    
    const rawData = toRaw(reactiveData)
    rawData.items.push(...newItems) // 避免多次触发更新
    // 手动触发更新(如强制重新渲染)
    triggerRef(reactiveData)
    
  • makeRaw:标记一个对象,使其永远不会被转换为响应式对象。避免 Vue 对大型对象或第三方实例进行不必要的响应式处理。

    import { reactive, markRaw } from 'vue'// 标记对象为原始
    const staticData = markRaw({ config: { theme: 'dark' }, methods: { /* 复杂方法 */ }
    })// 即使被包裹在响应式对象中,staticData 仍保持原始
    const state = reactive({staticData, // ❌ 不会被代理dynamicData: { count: 0 } // ✅ 会被代理
    })// 修改 staticData 的嵌套属性 → 不会触发响应式
    state.staticData.config.theme = 'light'
    console.log(state.staticData.config.theme) // 'light'(数据已改,但无响应式)
    
特性toRawmarkRaw
作用对象响应式对象,如 reactiveref任何对象
返回值原始对象被标记的原始对象
修改原始对象的影响响应式对象数据同步更新,但不触发响应式追踪对象永远不会被 Vue 代理
适用场景临时操作原始数据永久禁止对象被响应式处理

4.4 customRef

Vue 3 中,customRef 允许开发者创建自定义的响应式引用ref,通过手动控制依赖追踪track和更新触发trigger,适用于需要精细控制响应式行为的场景,如防抖、异步操作等。customRef 接收一个工厂函数,该函数需返回包含 getset 方法的对象:

  • get():在访问值时调用,需手动调用 track() 追踪依赖。

  • set(newValue):在修改值时调用,需手动调用 trigger() 触发更新。

    <template><input v-model="text" /><div>Debounced Value: {{ text }}</div>
    </template><script setup>
    import { customRef } from 'vue'function debouncedRef(value, delay = 200) {let timeoutreturn customRef((track, trigger) => ({get() {track()return value},set(newValue) {clearTimeout(timeout)timeout = setTimeout(() => {value = newValuetrigger()}, delay)}}))
    }const text = debouncedRef('', 500)
    </script>
    

在实际开发中,有以下使用场景:

  • 防抖Ref:创建一个防抖 Ref,延迟触发更新。

    function debouncedRef(initialValue, delay = 200) {let timeoutreturn customRef((track, trigger) => {return {get() {track()return initialValue},set(newValue) {clearTimeout(timeout)timeout = setTimeout(() => {initialValue = newValuetrigger() // 延迟触发更新}, delay)}}})
    }// 使用
    const text = debouncedRef('', 500)
    
  • 异步数据Ref:在设置值时执行异步操作,完成后触发更新。

    function asyncRef(initialValue) {return customRef((track, trigger) => {let value = initialValuereturn {get() {track()return value},async set(newValue) {try {const result = await fetchData(newValue) // 假设异步请求value = resulttrigger()} catch (error) {console.error('请求失败:', error)}}}})
    }// 使用
    const data = asyncRef(null)
    

customRef 适用于需要手动控制响应式行为的场景,如防抖、异步更新、集成第三方库等。

4.5 provideinject

Vue 3 中,provideinject 用于实现跨组件层级的数据传递,尤其适合祖先组件与深层嵌套的后代组件之间的通信。以下是详细的使用指南和最佳实践:

  • provide:父组件提供数据,在祖先组件的 setup 函数中,使用 provide 提供数据。

    // ParentComponent.vue
    import { provide, ref } from 'vue'export default {setup() {const message = ref('Hello from parent')provide('message', message) // 提供响应式数据return { message }}
    }
    
  • inject:子组件注入数据,在后代组件的 setup 函数中,使用 inject 获取数据。

    // ChildComponent.vue
    import { inject } from 'vue'export default {setup() {const message = inject('message', '默认值') // 注入数据,可设置默认值return { message }}
    }
    

当然,也可以实现响应式数据传递,如下::

  • 提供响应式数据:确保提供的数据是响应式【refreactive】,以便子组件能检测到变化。

    // ParentComponent.vue
    const count = ref(0)
    provide('count', count)
    
  • 在子组件中修改数据:若需子组件修改数据,可在父组件中提供方法。

    // ParentComponent.vue
    const increment = () => count.value++
    provide('count', { count, increment })
    
    // ChildComponent.vue
    const { count, increment } = inject('count')
    

对于上述两个API的使用,需要注意以下问题:

  • 响应式数据必须显式提供:若提供非响应式数据,子组件无法检测变化。

    provide('staticData', { key: 'value' }) // ❌ 非响应式
    provide('dynamicData', ref({ key: 'value' })) // ✅ 响应式
    
  • 单向数据流:避免子组件直接修改注入的响应式数据,除非父组件明确允许。

    // ParentComponent.vue
    provide('data', readonly(data)) // 使用 readonly 保护数据
    

4.6 响应式数据的判断

  • isRef:检查一个值是否为一个Ref对象。
  • isReactive:检查一个值是否由reactive创建的响应式代理。
  • isReadonly:检查一个对象是否由readonly创建的只读代理。
  • isProxy:检查一个对象是否由reactivereadonly方法创建的代理。

5. Fragment组件

Vue 3 中,Fragment片段是一项重要的新特性,允许组件模板包含多个根节点而无需包裹在一个父容器中。

<!-- 合法模板(Vue 3-->
<template><h1>标题</h1><p>内容</p><button>按钮</button>
</template>

6. Teleport组件

Vue 3 中,<Teleport> 组件用于将模板内容渲染到DOM中的指定位置,常用于解决组件层级导致的样式或布局问题,如模态框、全局通知等。基础用法用法如下:

  • 定义目标容器:在 HTML 中添加目标容器,通常位于根节点附近。

    <!-- public/index.html -->
    <body><div id="app"></div><div id="modal-container"></div> <!-- Teleport 目标容器 -->
    </body>
    
  • 在组件中使用<Teleport>

    <!-- MyComponent.vue -->
    <template><button @click="showModal = true">打开模态框</button><Teleport to="#modal-container"><div v-if="showModal" class="modal"><h2>标题</h2><p>内容...</p><button @click="showModal = false">关闭</button></div></Teleport>
    </template><script setup>
    import { ref } from 'vue'
    const showModal = ref(false)
    </script>
    

动态目标与禁用撞状态:

  • 动态切换目标容器

    <Teleport :to="isMobile ? '#mobile-modal' : '#desktop-modal'"><!-- 内容 -->
    </Teleport>
    
  • 禁用Teleport:通过 :disabled 控制内容是否保留在原位置。

    <Teleport to="#modal-container" :disabled="forceInline"><!-- 当 forceInline 为 true 时,内容不传送 -->
    </Teleport>
    

Transition组件结合使用:

<Teleport to="#modal-container"><Transition name="fade"><div v-if="showModal" class="modal"><!-- 内容 --></div></Transition>
</Teleport><style>
.fade-enter-active, .fade-leave-active {transition: opacity 0.3s;
}
.fade-enter-from, .fade-leave-to {opacity: 0;
}
</style>

多内容传送与顺序:多个 Teleport 内容按代码顺序渲染到目标容器。

<Teleport to="#notifications"><div class="notification">通知 1</div>
</Teleport><Teleport to="#notifications"><div class="notification">通知 2</div>
</Teleport><!-- 目标容器显示顺序:通知 1 → 通知 2 -->

7. Suspense组件

Vue 3 中,<Suspense> 组件用于优雅地处理异步组件或异步数据的加载状态,允许开发者定义加载中和加载完成后的展示内容。以下是其详细使用指南和常见场景示例:

  • 基础用法:当加载异步组件时,展示加载状态。

    <template><Suspense><!-- 默认插槽:异步组件 --><template #default><AsyncComponent /></template><!-- 加载中状态 --><template #fallback><div>加载中...</div></template></Suspense>
    </template><script setup>
    import { defineAsyncComponent } from 'vue'// 定义异步组件(返回 Promise)
    const AsyncComponent = defineAsyncComponent(() =>import('./AsyncComponent.vue')
    )
    </script>
    
  • 处理异步数据:在组件 setup 中使用 async 函数处理异步数据。

    <template><Suspense><template #default><UserProfile :user="userData" /></template><template #fallback><div>加载用户数据...</div></template></Suspense>
    </template><script setup>
    import { ref } from 'vue'const userData = ref(null)// 异步获取数据(setup 函数支持 async)
    async function fetchUser() {const response = await fetch('/api/user')userData.value = await response.json()
    }await fetchUser() // 等待数据加载完成
    </script>
    
  • 错误处理

    • 捕获异步错误:使用 onErrorCaptured 钩子捕获错误并显示友好提示。

      <template><div v-if="error">{{ error.message }}</div><Suspense v-else><template #default><AsyncComponent /></template><template #fallback><div>加载中...</div></template></Suspense>
      </template><script setup>
      import { ref, onErrorCaptured } from 'vue'const error = ref(null)onErrorCaptured((err) => {error.value = errreturn false // 阻止错误继续向上传播
      })
      </script>
      
    • 重试机制:允许用户点击重试加载。

      <template><div v-if="error"><p>加载失败:{{ error.message }}</p><button @click="retry">重试</button></div><Suspense v-else><!-- ... --></Suspense>
      </template><script setup>
      const error = ref(null)
      const retry = () => {error.value = null// 重新执行异步操作(如重新加载组件或数据)
      }
      </script>
      
    • 动态切换异步组件:结合动态组件 <component :is="..."> 实现按需加载。

      <template><button @click="toggleComponent">切换组件</button><Suspense><template #default><component :is="currentComponent" /></template><template #fallback><div>加载组件中...</div></template></Suspense>
      </template><script setup>
      import { ref, defineAsyncComponent } from 'vue'const components = {A: defineAsyncComponent(() => import('./ComponentA.vue')),B: defineAsyncComponent(() => import('./ComponentB.vue'))
      }const currentComponent = ref('A')
      const toggleComponent = () => {currentComponent.value = currentComponent.value === 'A' ? 'B' : 'A'
      }
      </script>
      

8. Vue3中的其它变化

Vue3中,因为Vue不能使用,将原来的全局API转移到了应用实例app上,如下:

2.X全局API3.X全局API
Vue.config.xxxapp.config.xxx
Vue.config.productionTip移除
Vue.componentapp.component
Vue.directiveapp.directive
Vue.mixinapp.mixin
Vue.useapp.use
Vue.prototypeapp.config.globalProperties

除了上述全局API的转移,还有以下改变:

  • data选项始终被声明为一个函数。

  • 过度类名的更改

    Vue 2 类名Vue 3 类名说明
    .v-enter.v-enter-from进入动画的初始状态
    .v-leave.v-leave-from离开动画的初始状态
    .v-enter-to无变化进入动画的结束状态
    .v-leave-to无变化离开动画的结束状态
    .v-enter-active无变化进入动画的激活状态(过渡属性)
    .v-leave-active无变化离开动画的激活状态
  • 移除keyCode作为v-on的修饰符,同时也不再支持config.keyCodes

  • 移除v-on.native修饰符。

  • 移除过滤器filter

  • ......

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/32133.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

p5.js:模拟 n个彩色小球在一个3D大球体内部弹跳

向 豆包 提问&#xff1a;编写一个 p5.js 脚本&#xff0c;模拟 42 个彩色小球在一个3D大球体内部弹跳。每个小球都应留下一条逐渐消失的轨迹。大球体应缓慢旋转&#xff0c;并显示透明的轮廓线。请确保实现适当的碰撞检测&#xff0c;使小球保持在球体内部。 cd p5-demo copy…

javascript-es6 (六)

编程思想 面向过程 面向过程就是分析出解决问题所需要的步骤&#xff0c;然后用函数把这些步骤一步一步实现&#xff0c;使用的时候再一个一个的依次 调用就可以了 就是按照我们分析好了的步骤&#xff0c;按照步骤解决问题 面向对象 面向对象是把事务分解成为一个个对象&…

Linux第0节:Linux环境的搭建

一、Linux 环境的搭建方式 搭建方式主要有三种&#xff1a; 直接安装在物理机上。但是由于 Linux 桌面使用起来非常不友好&#xff08;不推荐&#xff09;。使用虚拟机软件, 将 Linux 搭建在虚拟机上。但是由于当前的虚拟机软件(如 VMWare 之类的)存在一些 bug , 会导致环境上…

计算机网络:计算机网络的概念

1.计算机网络&#xff1a;由若干个结点和链接这些的链路组成。 2.集线器&#xff08;Hub&#xff09;&#xff1a;可以把多个结点连接起来&#xff0c;组成一个计算机网络。 不能避免数据冲突的情况 3.交换机&#xff08;Switch&#xff09;:可以把多个结点连接起来&#x…

mysql的锁-->一篇读懂所有锁机制

目录 mysql的锁 概述&#xff1a;根据mysql锁的大类型可以分为 我们先来讲一下范围最大的全局锁 使用 为什么要使用全局锁&#xff1f; 使用全局锁进行备份的缺点 表级锁 表锁 1.共享读表锁的语法 2.排斥写表锁 元数据锁 意向锁 什么是意向锁 怎么产生意向锁 意向…

Vue 实现智能检测文字是否溢出,溢出显示省略号,鼠标悬浮显示全部【附封装组件完整代码+详细注释+粘贴即食】

一、场景需求 在项目中&#xff0c;经常会遇到文本内容超出容器的情况。为了提高用户体验&#xff0c;我希望在文字溢出时显示悬浮提示&#xff0c;未溢出时则不显示。 二、效果演示 三、实现原理 DOM宽度对比法&#xff1a;通过比较元素的scrollWidth&#xff08;实际内容宽…

用Deepseek写一个 HTML 和 JavaScript 实现一个简单的飞机游戏

大家好&#xff01;今天我将分享如何使用 HTML 和 JavaScript 编写一个简单的飞机游戏。这个游戏的核心功能包括&#xff1a;控制飞机移动、发射子弹、敌机生成、碰撞检测和得分统计。代码简洁易懂&#xff0c;适合初学者学习和实践。 游戏功能概述 玩家控制&#xff1a;使用键…

《Spring日志整合与注入技术:从入门到精通》

1.Spring与日志框架的整合 1.Spring与日志框架进行整合&#xff0c;日志框架就可以在控制台中&#xff0c;输出Spring框架运行过程中的一些重要的信息。 好处&#xff1a;方便了解Spring框架的运行过程&#xff0c;利于程序的调试。 Spring如何整合日志框架 Spring5.x整合log4j…

关于mybatis查询时,时间字段的映射问题

目录 1.mysql中&#xff0c;关于时间的两种类型 1.1 date 1.2 datetime 2.mybatis从mysql数据库查询出上述两种类型的字段后&#xff0c;映射到Java实体类时的问题 3.结语 1.mysql中&#xff0c;关于时间的两种类型 1.1 date 格式&#xff1a;2002-09-23 特点&#xff1a…

高效自动化测试:打造Python+Requests+Pytest+Allure+YAML的接口测试框架

一、背景 在快节奏的开发周期中&#xff0c;如何确保接口质量&#xff1f;自动化测试是关键。通过构建标准化、可复用的测试框架&#xff0c;能显著提升测试效率与准确性&#xff0c;为项目质量保驾护航[1][7]。 二、目标 ✅ 核心目标&#xff1a; ● 实现快速、高效的接口测试…

【鸿蒙开发】MongoDB入门

https://www.mongodb.com/try/download/community 下载MongoDB: var mongoose require("mongoose");// localhost 域名&#xff0c;代表本机 // 127.0.0.1 ip , 代码本机 mongoose.connect("mongodb://localhost:27017/jiaju").then(() > {console.l…

Linux中的TCP编程接口基本使用

TCP编程接口基本使用 本篇介绍 在UDP编程接口基本使用已经介绍过UDP编程相关的接口&#xff0c;本篇开始介绍TCP编程相关的接口。有了UDP编程的基础&#xff0c;理解TCP相关的接口会更加容易&#xff0c;下面将按照两个方向使用TCP编程接口&#xff1a; 基本使用TCP编程接口…

wireshark 如何关闭混杂模式 wireshark操作

Fiddler和Wireshark都是进行抓包的工具&#xff1a;所谓抓包就是将网络传输发送与接收的数据包进行截获、重发、编辑、转存等操作&#xff0c;也用来检查网络安全。抓包也经常被用来进行数据截取等。黑客常常会用抓包软件获取你非加密的上网数据&#xff0c;然后通过分析&#…

IDEA2024又一坑:连接Docker服务连不上,提示:Cannot run program “docker“: CreateProcess error=2

为新电脑安装了IDEA2024版&#xff0c;因为局域网中安装有Docker,所以这台电脑上没有安装&#xff0c;当运行时发现死活连不上Docker报&#xff1a;Cannot run program “docker“: CreateProcess error2 分析&#xff1a; Docker服务有问题 其它电脑都能连&#xff0c;排除 网…

文件包含漏洞第一关

一、什么是文件包含漏洞 1.文件包含漏洞概述 和SQL注入等攻击方式一样&#xff0c;文件包含漏洞也是一种注入型漏洞&#xff0c;其本质就是输入一段用户能够控制的脚本或者代码&#xff0c;并让服务端执行。 什么叫包含呢&#xff1f;以PHP为例&#xff0c;我们常常把可重复使…

网络安全事件响应--应急响应(windows)

应用系统日志 Windows主要有以下三类日志记录系统事件&#xff1a;应用程序日志、系统日志和安全日志。 系统和应用程序日志存储着故障排除信息&#xff0c;对于系统管理员更为有用。安全日志记录着事件审计信息&#xff0c;包括用户验证&#xff08;登录、远程访问等&#x…

C++蓝桥杯基础篇(九)

片头 嗨&#xff01;小伙伴们&#xff0c;大家好~ 今天我们将学习蓝桥杯基础篇&#xff08;十&#xff09;&#xff0c;学习函数相关知识&#xff0c;准备好了吗&#xff1f;咱们开始咯&#xff01; 一、函数基础 一个典型的函数定义包括以下部分&#xff1a;返回类型、函数名…

JVM内存结构笔记01-运行时数据区域

文章目录 前言运行时数据区域1.程序计数器定义特点总结 2.虚拟机栈2.1 定义局部变量表 ★操作数栈动态链接方法返回地址(方法出口) 2.2 栈内存溢出演示栈内存溢出 java.lang.StackOverflowError 2.3问题辨析1. 垃圾回收是否涉及栈内存&#xff1f;2. 栈内存分配越大越好吗&…

01-简单几步!在Windows上用llama.cpp运行DeepSeek-R1模型

1.llama.cpp介绍 Llama.cpp 是一个开源的、轻量级的项目&#xff0c;旨在实现 Meta 推出的开源大语言模型 Llama 的推理&#xff08;inference&#xff09;。Llama 是 Meta 在 2023 年开源的一个 70B 参数的高质量大语言模型&#xff0c;而 llama.cpp 是一个用 C 实现的轻量化…

对开源VLA sota π0的微调——如何基于各种开源数据集、以及你自己的私有数据集微调π0(含我司的微调实践)

前言 25年2.4日&#xff0c;几个月前推出π0的公司Physical Intelligence (π)宣布正式开源π0及π0-FAST&#xff0c;如之前所介绍的&#xff0c;他们对用超过 10,000 小时的机器人数据进行了预训练 该GitHub代码仓库「 π0及π0-FAST的GitHub地址&#xff1a;github.com/Ph…