当前位置: 首页 > news >正文

Vue3新特性实战:从入门到精通

Vue3新特性实战指南

引言

Vue.js 3.0带来了革命性的更新,不仅在性能上有显著提升,更在开发体验和功能特性上有重大突破。本文将通过实际代码示例,全面介绍Vue3的新特性,帮助你快速上手这些强大的功能。

目录

  1. Composition API
  2. Fragment组件
  3. Teleport传送门
  4. Suspense异步组件
  5. 响应式系统增强
  6. 组件通信新方式
  7. 全局API变化
  8. 生命周期钩子更新
  9. 模板语法改进
  10. 性能优化特性
  11. 迁移指南

1. Composition API

Composition API是Vue3最重要的新特性,它提供了一种全新的组件逻辑组织方式。

1.1 基本用法

<template> <div class="user-profile"> <h2>{{ user.name }}</h2> <p>{{ user.email }}</p> <button @click="updateUser">更新用户</button> </div> </template> <script setup> import { ref, reactive } from 'vue' // 使用ref创建响应式基本类型 const count = ref(0) // 使用reactive创建响应式对象 const user = reactive({ name: '张三', email: 'zhangsan@example.com' }) // 方法 const updateUser = () => { user.name = '李四' count.value++ } // 暴露给模板 </script> <style scoped> .user-profile { padding: 20px; border: 1px solid #ddd; } </style>

1.2 逻辑复用

// composables/useCounter.jsimport{ref,computed}from'vue'exportfunctionuseCounter(initialValue=0){constcount=ref(initialValue)constdouble=computed(()=>count.value*2)constincrement=()=>count.value++constdecrement=()=>count.value--constreset=()=>count.value=initialValuereturn{count,double,increment,decrement,reset}}
<!-- 使用组合式函数 --> <template> <div> <p>Count: {{ count }}</p> <p>Double: {{ double }}</p> <button @click="increment">+1</button> <button @click="decrement">-1</button> <button @click="reset">Reset</button> </div> </template> <script setup> import { useCounter } from './composables/useCounter' const { count, double, increment, decrement, reset } = useCounter(10) </script>

1.3 逻辑组合示例

<template> <div class="user-management"> <h2>用户管理</h2> <!-- 搜索 --> <input v-model="searchQuery" placeholder="搜索用户..." /> <!-- 筛选 --> <select v-model="filterRole"> <option value="">所有角色</option> <option value="admin">管理员</option> <option value="user">普通用户</option> </select> <!-- 用户列表 --> <div v-if="loading">加载中...</div> <div v-else-if="error">{{ error }}</div> <ul v-else> <li v-for="user in filteredUsers" :key="user.id"> {{ user.name }} - {{ user.role }} </li> </ul> </div> </template> <script setup> import { ref, computed, onMounted, onUnmounted } from 'vue' // 组合式函数:用户数据 function useUsers() { const users = ref([]) const loading = ref(false) const error = ref(null) const fetchUsers = async () => { loading.value = true error.value = null try { const response = await fetch('/api/users') users.value = await response.json() } catch (e) { error.value = e.message } finally { loading.value = false } } return { users, loading, error, fetchUsers } } // 组合式函数:搜索和筛选 function useSearch(items) { const searchQuery = ref('') const filterRole = ref('') const filteredItems = computed(() => { let result = items.value if (searchQuery.value) { result = result.filter(item => item.name.toLowerCase().includes(searchQuery.value.toLowerCase()) ) } if (filterRole.value) { result = result.filter(item => item.role === filterRole.value) } return result }) return { searchQuery, filterRole, filteredItems } } // 组合式函数:窗口尺寸 function useWindowSize() { const width = ref(window.innerWidth) const height = ref(window.innerHeight) const handleResize = () => { width.value = window.innerWidth height.value = window.innerHeight } onMounted(() => { window.addEventListener('resize', handleResize) }) onUnmounted(() => { window.removeEventListener('resize', handleResize) }) return { width, height } } // 组合所有逻辑 const { users, loading, error, fetchUsers } = useUsers() const { searchQuery, filterRole, filteredUsers } = useSearch(users) const { width, height } = useWindowSize() // 组件挂载时获取数据 onMounted(() => { fetchUsers() }) </script>

1.4 与Options API对比

Options API(Vue2风格)

export default { data() { return { users: [], searchQuery: '', filterRole: '', loading: false, error: null } }, computed: { filteredUsers() { let result = this.users if (this.searchQuery) { result = result.filter(user => user.name.includes(this.searchQuery) ) } if (this.filterRole) { result = result.filter(user => user.role === this.filterRole) } return result } }, methods: { async fetchUsers() { // 数据获取逻辑 } }, mounted() { this.fetchUsers() } }

Composition API优势

  • ✅ 逻辑相关代码可以放在一起
  • ✅ 逻辑可以轻松提取为可复用函数
  • ✅ 类型推断更好
  • ✅ 更好的Tree-shaking

2. Fragment组件

2.1 多根节点支持

Vue3支持组件有多个根节点,无需额外的div包裹。

<!-- UserProfile.vue --> <template> <header> <h1>{{ title }}</h1> </header> <main> <p>{{ content }}</p> </main> <footer> <small>&copy; 2023</small> </footer> </template> <script setup> defineProps({ title: String, content: String }) </script> <style scoped> header, main, footer { padding: 1rem; } </style>
<!-- 使用组件 --> <template> <UserProfile title="我的标题" content="这里是内容" /> </template>

2.2 动态多根节点

<template> <div> <header v-if="$slots.header"> <slot name="header" /> </header> <main> <slot /> </main> <footer v-if="$slots.footer"> <slot name="footer" /> </footer> </div> </template>

3. Teleport传送门

Teleport允许我们将组件渲染到DOM的任意位置。

3.1 模态框示例

<!-- Modal.vue --> <template> <Teleport to="body"> <div v-if="isOpen" class="modal-overlay" @click="closeModal"> <div class="modal-content" @click.stop> <header> <h3>{{ title }}</h3> <button @click="closeModal">&times;</button> </header> <main> <slot /> </main> <footer> <button @click="closeModal">关闭</button> <button @click="confirm">确认</button> </footer> </div> </div> </Teleport> </template> <script setup> import { computed } from 'vue' const props = defineProps({ isOpen: Boolean, title: String }) const emit = defineEmits(['close', 'confirm']) const closeModal = () => emit('close') const confirm = () => emit('confirm') </script> <style scoped> .modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; } .modal-content { background: white; border-radius: 8px; max-width: 500px; width: 100%; } </style>

3.2 通知组件

<!-- Notification.vue --> <template> <Teleport to="#notifications"> <transition-group name="notification" tag="div" class="notifications"> <div v-for="notif in notifications" :key="notif.id" :class="['notification', notif.type]" @click="removeNotification(notif.id)" > {{ notif.message }} </div> </transition-group> </Teleport> </template> <script setup> import { ref } from 'vue' const notifications = ref([]) const addNotification = (message, type = 'info', duration = 3000) => { const id = Date.now() notifications.value.push({ id, message, type }) if (duration > 0) { setTimeout(() => { removeNotification(id) }, duration) } } const removeNotification = (id) => { const index = notifications.value.findIndex(n => n.id === id) if (index > -1) { notifications.value.splice(index, 1) } } // 暴露方法给父组件 defineExpose({ addNotification }) // 事件总线或全局状态管理 </script> <style scoped> .notifications { position: fixed; top: 20px; right: 20px; z-index: 9999; } .notification { padding: 1rem; margin-bottom: 10px; border-radius: 4px; color: white; cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,0.2); } .notification.info { background: #2196F3; } .notification.success { background: #4CAF50; } .notification.warning { background: #FF9800; } .notification.error { background: #F44336; } .notification-enter-active, .notification-leave-active { transition: all 0.3s ease; } .notification-enter-from, .notification-leave-to { opacity: 0; transform: translateX(100%); } </style>
<!-- index.html --><body><divid="app"></div><divid="notifications"></div></body>

4. Suspense异步组件

Suspense让我们优雅地处理异步组件的加载状态。

4.1 基本用法

<!-- SuspenseExample.vue --> <template> <Suspense> <!-- 异步组件 --> <template #default> <AsyncUserProfile /> </template> <!-- 加载中显示的组件 --> <template #fallback> <div class="loading">加载中...</div> </template> </Suspense> </template> <script setup> import { defineAsyncComponent } from 'vue' // 方式1:使用defineAsyncComponent const AsyncUserProfile = defineAsyncComponent(() => import('./AsyncUserProfile.vue') ) // 方式2:在组件内部使用 // AsyncUserProfile.vue </script> <style scoped> .loading { padding: 2rem; text-align: center; font-size: 1.2rem; } </style>

4.2 异步组件加载配置

<script setup> import { defineAsyncComponent } from 'vue' const AsyncUserProfile = defineAsyncComponent({ // 工厂函数 loader: () => import('./AsyncUserProfile.vue'), // 加载时组件 loadingComponent: LoadingSpinner, // 加载失败组件 errorComponent: ErrorComponent, // 在显示loadingComponent之前的延迟时间(毫秒) delay: 200, // 如果提供了timeout,并且加载时间超过此值,将显示错误组件 timeout: 3000 }) </script>

4.3 多个异步依赖

<template> <Suspense> <template #default> <div> <AsyncUserProfile /> <AsyncUserPosts /> <AsyncUserSettings /> </div> </template> <template #fallback> <div class="loading-container"> <div class="spinner"></div> <p>正在加载用户数据...</p> </div> </template> </Suspense> </template> <style scoped> .loading-container { display: flex; flex-direction: column; align-items: center; padding: 2rem; } .spinner { width: 40px; height: 40px; border: 4px solid #f3f3f3; border-top: 4px solid #3498db; border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } </style>

5. 响应式系统增强

5.1 ref vs reactive

import{ref,reactive,toRefs}from'vue'// ref:适用于基本类型和对象constcount=ref(0)constuser=ref({name:'John',age:30})// 访问时需要.valueconsole.log(count.value)// 0count.value++// reactive:仅适用于对象conststate=reactive({count:0,user:{name:'John',age:30}})// 访问时不需要.valueconsole.log(state.count)// 0state.count++

5.2 toRef和toRefs

<template> <div> <p>{{ name }}</p> <p>{{ age }}</p> <button @click="updateName">更新姓名</button> </div> </template> <script setup> import { reactive, toRef, toRefs } from 'vue' const state = reactive({ name: '张三', age: 25 }) // toRef:创建单个ref const name = toRef(state, 'name') // toRefs:将整个对象转换为refs const { age } = toRefs(state) const updateName = () => { name.value = '李四' } </script>

5.3 computed

import{ref,computed}from'vue'constfirstName=ref('John')constlastName=ref('Doe')// 只读计算属性constfullName=computed(()=>{return`${firstName.value}${lastName.value}`})// 可写计算属性constfullNameWritable=computed({get(){return`${firstName.value}${lastName.value}`},set(value){[firstName.value,lastName.value]=value.split(' ')}})fullNameWritable.value='Jane Smith'console.log(firstName.value)// 'Jane'console.log(lastName.value)// 'Smith'

5.4 watch和watchEffect

import{ref,watch,watchEffect}from'vue'constcount=ref(0)// watch:监听特定响应式引用watch(count,(newValue,oldValue)=>{console.log(`Count changed from${oldValue}to${newValue}`)})// watchEffect:自动收集依赖watchEffect(()=>{console.log(`Count is:${count.value}`)})// 监听多个源constfirstName=ref('John')constlastName=ref('Doe')watch([firstName,lastName],([newFirst,newLast],[oldFirst,oldLast])=>{console.log(`Name changed from${oldFirst}${oldLast}to${newFirst}${newLast}`)})

6. 组件通信新方式

6.1 defineProps和defineEmits

<!-- ChildComponent.vue --> <template> <div> <p>从父组件接收到的消息:{{ message }}</p> <button @click="sendMessage">发送消息给父组件</button> </div> </template> <script setup> // 方式1:使用字符串数组 // defineProps(['message']) // 方式2:使用对象定义(推荐) const props = defineProps({ message: { type: String, required: true, default: '默认消息' }, count: { type: Number, default: 0 } }) // 方式3:TypeScript类型定义 // const props = defineProps<{ message: string, count?: number }>() const emit = defineEmits(['updateMessage', 'childClick']) // 或者使用对象定义 // const emit = defineEmits({ // updateMessage: (value: string) => { // return typeof value === 'string' // }, // childClick: () => true // }) const sendMessage = () => { emit('updateMessage', '来自子组件的消息') emit('childClick') } </script>

6.2 defineExpose

<!-- ChildComponent.vue --> <template> <div> <p>{{ content }}</p> </div> </template> <script setup> import { ref } from 'vue' const content = ref('子组件内容') const count = ref(0) const increment = () => { count.value++ } const getMessage = () => { return content.value } // 暴露给父组件 defineExpose({ content, count, increment, getMessage }) </script>
<!-- 父组件 --> <template> <div> <ChildComponent ref="childRef" /> <button @click="callChildMethod">调用子组件方法</button> </div> </template> <script setup> import { ref, onMounted } from 'vue' import ChildComponent from './ChildComponent.vue' const childRef = ref(null) onMounted(() => { console.log(childRef.value.content) // 访问子组件数据 childRef.value.increment() // 调用子组件方法 }) const callChildMethod = () => { const message = childRef.value.getMessage() console.log('子组件消息:', message) } </script>

7. 全局API变化

7.1 createApp

// Vue2constapp=newVue({router,store,render:h=>h(App)})// Vue3constapp=createApp(App)app.use(router)app.use(store)app.mount('#app')

7.2 全局属性

// Vue2Vue.prototype.$http=axios// Vue3constapp=createApp(App)app.config.globalProperties.$http=axios
<!-- 组件内使用 --> <script setup> import { getCurrentInstance } from 'vue' const { proxy } = getCurrentInstance() // 访问全局属性 console.log(proxy.$http) </script>

7.3 配置选项

constapp=createApp(App)// 全局配置app.config.errorHandler=(err,vm,info)=>{console.error('Global error:',err,info)}app.config.warnHandler=(msg,vm,trace)=>{console.warn('Global warning:',msg,trace)}app.config.globalProperties.customProperty='Custom Value'

8. 生命周期钩子更新

8.1 新的钩子

<script setup> import { onMounted, onUpdated, onUnmounted, onBeforeMount, onBeforeUpdate, onBeforeUnmount, onErrorCaptured, onActivated, onDeactivated, onRenderTracked, onRenderTriggered } from 'vue' // 组件挂载前 onBeforeMount(() => { console.log('Before Mount') }) // 组件挂载后 onMounted(() => { console.log('Mounted') }) // 组件更新前 onBeforeUpdate(() => { console.log('Before Update') }) // 组件更新后 onUpdated(() => { console.log('Updated') }) // 组件卸载前 onBeforeUnmount(() => { console.log('Before Unmount') }) // 组件卸载后 onUnmounted(() => { console.log('Unmounted') }) // 捕获后代组件错误 onErrorCaptured((err, instance, info) => { console.error('Error captured:', err, info) return false // 阻止错误传播 }) // 调试:跟踪渲染 onRenderTracked(({ key, target, type }) => { console.log('Render tracked:', key, type) }) // 调试:触发渲染 onRenderTriggered(({ key, target, type }) => { console.log('Render triggered:', key, type) }) </script>

9. 模板语法改进

9.1 多v-model

<!-- 父组件 --> <template> <UserForm v-model:name="userName" v-model:email="userEmail" v-model:age="userAge" /> </template> <script setup> import { ref } from 'vue' import UserForm from './UserForm.vue' const userName = ref('张三') const userEmail = ref('zhangsan@example.com') const userAge = ref(25) </script>
<!-- UserForm.vue --> <template> <div> <input type="text" :value="name" @input="$emit('update:name', $event.target.value)" placeholder="姓名" /> <input type="email" :value="email" @input="$emit('update:email', $event.target.value)" placeholder="邮箱" /> <input type="number" :value="age" @input="$emit('update:age', $event.target.value)" placeholder="年龄" /> </div> </template> <script setup> defineProps({ name: String, email: String, age: Number }) defineEmits(['update:name', 'update:email', 'update:age']) </script>

9.2 v-memo指令

<template> <div v-for="item in items" :key="item.id" v-memo="[item.id, item.selected]"> <p>ID: {{ item.id }}</p> <p>Name: {{ item.name }}</p> <p>Selected: {{ item.selected }}</p> </div> </template> <script setup> import { ref } from 'vue' const items = ref([ { id: 1, name: 'Item 1', selected: false }, { id: 2, name: 'Item 2', selected: true }, // ... ]) // 只有当 item.id 或 item.selected 改变时,才会重新渲染 </script>

9.3 新的指令修饰符

<!-- 事件修饰符 --> <button @click.stop="handleClick">点击</button> <form @submit.prevent="handleSubmit">提交</form> <input @keyup.enter="handleEnter" /> <button @click.once="handleClickOnce">只执行一次</button> <!-- 鼠标修饰符 --> <button @click.left="handleLeftClick">左键</button> <button @click.right="handleRightClick">右键</button> <button @click.middle="handleMiddleClick">中键</button> <!-- 精确修饰符 --> <button @click.ctrl.exact="handleCtrlClick">只有Ctrl</button>

10. 性能优化特性

10.1 静态节点提升

<template> <div> <!-- 这个div只会在编译时创建一次 --> <div class="static">静态内容</div> <!-- 这个div会在数据变化时重新创建 --> <div>{{ dynamicContent }}</div> </div> </template>

10.2 PatchFlags

// Vue3编译时自动优化// 例如:<div class="red">{{ msg }}</div>// 编译为:h('div',{class:'red',patchFlag:1// 文本内容会变化},msg)

10.3 事件缓存

<template> <button @click="handleClick">点击</button> </template> <script setup> // handleClick函数会被缓存,不会每次渲染都重新创建 const handleClick = () => { console.log('Clicked') } </script>

11. 迁移指南

11.1 主要变化对比

Vue2Vue3说明
new Vue()createApp()创建应用实例
Vue.prototypeapp.config.globalProperties全局属性
$childrenref访问子组件
过滤器computed/methods过滤器被移除
data必须为函数data必须为函数仍然是函数
生命周期onXxx形式组合式API生命周期
多根节点不支持多根节点支持Fragment支持
mixinscomposables更好的逻辑复用
只有一个v-model支持多个v-model灵活性更高

11.2 迁移步骤

  1. 升级依赖

    {"dependencies":{"vue":"^3.0.0"}}
  2. 使用@vue/compiler-sfc

    // vite.config.jsimport{defineConfig}from'vite'importvuefrom'@vitejs/plugin-vue'exportdefaultdefineConfig({plugins:[vue()]})
  3. 逐步迁移组件

    • 从Options API迁移到Composition API
    • 使用新的生命周期钩子
    • 更新组件通信方式

11.3 兼容性检查

# 安装迁移辅助工具npminstall@vue/compat# 配置兼容性// vite.config.jsexportdefault{plugins:[vue({template:{compilerOptions:{// 启用兼容性模式 isCompat:true}}})]}

总结

Vue3的新特性为我们带来了:

  1. 更强的逻辑复用能力- Composition API让代码组织更灵活
  2. 更好的TypeScript支持- 类型推断更准确
  3. 更灵活的开发方式- Fragment、Teleport等特性
  4. 更好的性能- 编译时优化、响应式系统重构
  5. 更现代的架构- 模块化设计、Tree-shaking支持
http://www.jsqmd.com/news/122520/

相关文章:

  • springboot + vue
  • NAS网络存储:办公与数据管理的理想之选 - myqiye
  • 嵌入式第三十九篇——linux系统编程——信号通信、共享内存
  • Electron 文件系统与数据存储
  • 论文网站
  • LangFlow实战案例分享:构建多步骤推理AI代理全流程
  • 商用净水器大揭秘:流量大、服务好的品牌推荐 - 工业推荐榜
  • LangFlow多语言支持能力测试报告
  • 基于单片机的恒温储物箱设计(蓝牙RX TX反了)(有完整资料)
  • 论文AI率飙高?这2款降AI率工具亲测有效,轻松通过AI率检测!
  • Spark数据验证框架:单元测试的完整方法论
  • 计算机毕业设计|基于springboot + vue英语学习系统(源码+数据库+文档)
  • 最小函数值(minval)(信息学奥赛一本通- P1370)
  • Electron 网络与外部通信
  • 人工智能驱动下钓鱼攻击的演化机制与防御对策研究
  • 支持信创环境的FTP有哪些?一文读懂安全效率全面升级的方案
  • LangFlow与LangChain协同工作原理深度剖析
  • 还在手动处理Open-AutoGLM误报?掌握这5个自动化校准技巧效率提升10倍
  • 基于Java+SSM+Flask毕业论文管理系统(源码+LW+调试文档+讲解等)/毕业论文管理/论文管理系统/毕业论文工具/论文进度管理/学术论文软件/论文写作助手/毕业设计环境/学生论文平台
  • 16.2 对齐方法论:FineTune与RAG两大技术路径
  • Electron 安全性
  • 为什么你的Open-AutoGLM加密配置总失败?这7个坑你必须避开
  • 日历订阅机制滥用:新型钓鱼与恶意软件投递渠道分析
  • 跨语言桥梁:C# 如何优雅地调用 Python 策略 (Python.NET)—— QuantConnect/Lean 源码分析系列四
  • 基于婚礼邀请的钓鱼攻击:社会工程模式、技术实现与防御机制研究
  • 为什么顶尖团队都在用Open-AutoGLM处理多弹窗?真相令人震惊!
  • 【SPIE出版 | EI检索】2026年电力电子与电能变换国际学术会议(ICPEPC 2026)
  • 汇编语言全接触-41.虚拟设备驱动程序初步
  • 从合规到实战:企业安全建设的合规检查落地指南与风险规避策略
  • 红娘系统源码技术剖析JAVA:从用户匹配到消息推送的核心实现