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

【Vue 3 】——setup、ref、watch

Vue 3 核心知识点全面解析

一、Vue 3 简介

Vue 3 是下一代前端框架 Vue.js 的重大版本更新,于 2020 年 9 月正式发布。它带来了许多令人兴奋的新特性和改进,旨在解决 Vue 2 在大型项目中的规模限制,并提供更好的性能。

Vue 3 的核心亮点:

  1. 更强的性能:重写了虚拟 DOM,优化了编译过程,使得打包体积更小,运行速度更快。
  2. 更好的 TypeScript 支持:Vue 3 的代码库完全使用 TypeScript 重写,提供了完美的类型推断。
  3. Composition API:引入了一套新的、更具弹性的 API,用于组织和复用代码逻辑,这是 Vue 3 最重要的特性之一。
  4. 新的内置组件:如 <Fragment><Teleport><Suspense> 等,用于处理更复杂的 UI 场景。
  5. 按需引入:支持更好的 Tree-Shaking,未使用的功能不会被打包到最终产物中。

二、创建 Vue 3 工程

官方推荐使用 Vitevue-cli 来创建项目。Vite 提供了更快的启动和热更新速度。

使用 Vite 创建项目:

# npm
npm create vite@latest my-vue-app -- --template vue# yarn
yarn create vite my-vue-app --template vue# pnpm
pnpm create vite my-vue-app --template vue

然后进入项目目录,安装依赖并运行:

cd my-vue-app
npm install
npm run dev

使用 Vue CLI 创建项目:

npm install -g @vue/cli
vue create my-vue-app
# 在提示中选择 Vue 3 预设
cd my-vue-app
npm run serve

三、编写 App 组件

创建项目后,主入口文件 main.js 会使用新的 createApp 工厂函数来初始化应用:

// main.js
import { createApp } from 'vue'
import App from './App.vue'const app = createApp(App)
app.mount('#app')

根组件 App.vue 是一个典型的单文件组件 (SFC):

<!-- App.vue -->
<template><div><h1>{{ title }}</h1><MyComponent /></div>
</template><script>
import MyComponent from './components/MyComponent.vue'export default {name: 'App',components: {MyComponent},data() {return {title: 'Welcome to My Vue 3 App!'}}
}
</script><style>
h1 {color: #42b883;
}
</style>

四、一个简单的效果

让我们实现一个简单的计数器组件,感受 Vue 3 的响应式特性。

<template><div><p>Count is: {{ count }}</p><button @click="increment">Increment</button></div>
</template><script>
import { ref } from 'vue'export default {setup() {const count = ref(0)const increment = () => {count.value++}return {count,increment}}
}
</script>

vscode同时修改多列:shift+alt拖动选择列,然后输入可以在多行输入相同内容

五、Options API 与 Composition API

这是 Vue 3 中两种组织组件代码的方式。

  • Options API (Vue 2 风格):通过不同的选项属性 (datamethodscomputedwatch生命周期 等) 来组织代码。逻辑关注点分散在各个选项中。
    export default {data() {return { count: 0 }},methods: {increment() { this.count++ }},mounted() {console.log('Component is mounted!')}}
  • Composition API:使用导入的函数来定义组件逻辑。它允许我们根据逻辑关注点而不是选项类型来组织代码,使得代码更易于阅读和维护,尤其是大型组件。
    import { ref, onMounted } from 'vue'export default {setup() {const count = ref(0)function increment() { count.value++ }onMounted(() => {console.log('Component is mounted!')})return { count, increment }}}

Composition API 的优势:

  • 更好的逻辑复用能力 (自定义 Composable 函数,类似 React Hooks)
  • 更灵活的代码组织
  • 更好的 TypeScript 支持

六、setup 概述

setup 是 Composition API 的入口和舞台。它是一个组件选项,在组件被创建之前、props 被解析之后执行。

1、数据

2、方法

return相当于把数据交出去

注意:

  • 执行时机:在 beforeCreate 生命周期之前执行,只执行一次。
  • this 的用法在 setup 内部,this 不是该活跃实例的引用,因为 setup 是在组件被完全初始化之前调用的。所有对数据和函数的访问都直接通过响应式引用和上下文对象。
  • 参数:它接收两个参数:
    1. props:响应式的 props 对象。
    2. context:一个普通 JavaScript 对象,暴露了三个组件属性:attrsslotsemit
export default {props: {title: String},setup(props, context) {console.log(props.title) // 访问 propsconsole.log(context.attrs) // 等价于 this.$attrsconsole.log(context.slots) // 等价于 this.$slotsconsole.log(context.emit)  // 等价于 this.$emit// 在这里声明响应式数据、方法、计算属性、生命周期钩子等// 最后必须返回一个对象,该对象上的属性可以在模板中使用return { ... }}
}

七、setup 的返回值:return

setup 必须返回一个对象。这个对象包含所有你想要在模板中使用的属性(响应式数据、方法等)。这个对象会被合并到组件的实例上,模板可以直接访问它们。

setup() {const message = ref('Hello World!')const count = ref(0)function changeMessage() {message.value = 'Vue 3 is awesome!'}// 返回的对象可以在模板中使用return {message,count,changeMessage}
}

八、setup 与 Options API

setup 和传统的 Options API (data, methods 等) 可以共存。在 setup 中返回的属性会被暴露到 this 上,并且可以被 Options API 访问。

data可以用setup的数据

但setup不可以用data的数据

最好不要混合使用

export default {data() {return {dataCount: 1}},setup() {const setupCount = ref(100)return { setupCount }},mounted() {console.log(this.dataCount) // 1 (来自 Options API)console.log(this.setupCount) // 100 (来自 setup)}
}

然而,通常建议在同一个组件中只选择一种风格,以避免不必要的混淆。

九、setup 的语法糖 <script setup>

为了简化 setup 的使用,Vue 3 提供了 <script setup> 语法糖。这是编译时语法糖,代码更简洁。

传统写法 vs <script setup>

<!-- 传统写法 -->
<script>
import { ref } from 'vue'export default {setup() {const count = ref(0)return { count }}
}
</script>
<!-- 使用 <script setup> -->
<script setup>
import { ref } from 'vue'const count = ref(0)
// 任何顶层绑定 (变量、函数导入) 都可以直接在模板中使用
</script>

拓展:配置组件名字

一、多写一个script标签

二、下载一个插件

然后配置vite.config.ts

再设置name属性

<script setup> 中:

  • 不需要手动return模板需要使用的数据和方法。
  • 引入的组件可以直接在模板中使用,无需注册。
  • 使用 defineProps 和 defineEmits 来声明 props 和 emits
  • 使用 useSlots 和 useAttrs 来获取 slots 和 attrs
<script setup>
import { ref } from 'vue'
import MyComponent from './MyComponent.vue'// 定义 props 和 emits
const props = defineProps({title: String
})
const emit = defineEmits(['change'])const count = ref(0)function increment() {count.value++emit('change', count.value)
}
</script><template><h1>{{ title }}</h1><MyComponent /><button @click="increment">{{ count }}</button>
</template>

十、ref 创建:基本类型的响应式数据

ref 函数用于创建一个响应式的引用。它接收一个内部值(可以是基本类型或对象),返回一个响应式的、可更改的 ref 对象。这个对象只有一个 .value 属性,指向其内部值。

主要用于处理基本类型数据 (String, Number, Boolean, etc.)。

import { ref } from 'vue'const count = ref(0) // 创建一个值为 0 的响应式 ref
console.log(count.value) // 0count.value++ // 在 JavaScript 中需要通过 .value 来访问和修改
console.log(count.value) // 1

在模板中(<template>),Vue 会自动解包,无需使用 .value

在<script>里ref包裹的数据要.value

<template><div>{{ count }}</div> <!-- 直接使用,不需要 .value -->
</template>

十一、reactive 创建:对象类型的响应式数据

reactive 函数用于创建一个响应式的对象。它接收一个普通对象,返回该普通对象的响应式代理。

主要用于处理对象或数组等引用类型数据。

import { reactive } from 'vue'const state = reactive({count: 0,user: {name: 'Alice',age: 30},hobbies: ['reading', 'music']
})// 访问和修改
console.log(state.count) // 0 (直接访问属性,不需要 .value)
state.count++
state.user.name = 'Bob'
state.hobbies.push('coding')

小技巧:

选中按括号可以直接包裹

十二、ref 创建:对象类型的响应式数据

ref 也可以用来创建对象类型的响应式数据。当你使用 ref(null)ref({ ... }) 时,内部会调用 reactive 方法来使值具有响应性。

 

ref 里的对象 其实还是用reactive 实现的

使用ref定义需要.value

注意:reactive 返回的是原始对象的 Proxy,它们是不相等的: console.log(state === originalObject) // false

import { ref } from 'vue'const state = ref({count: 0,user: {name: 'Alice'}
})// 在 JS 中访问和修改需要使用 .value
console.log(state.value.count) // 0
state.value.count = 1
state.value.user.name = 'Bob'

在模板中同样会自动解包:

<template><div>{{ state.count }}</div> <!-- 自动解包,无需 .value -->
</template>

十三、ref 对比 reactive

特性refreactive
数据类型基本类型、对象类型仅对象类型 (Array, Object, Map, Set)
JS 中访问需要 .value直接访问
模板中访问自动解包,无需 .value直接访问
重新赋值可以用 .value 整体替换不能直接整体替换,会失去响应性
TypeScript需用 Ref<T> 类型直接用接口类型即可
适用场景基本类型数据、可能需要整体替换的引用数据不需要整体替换的复杂对象、JSON 数据

如何选择?

  • 推荐:优先使用 ref,因为它更统一,可以处理所有类型。
  • 当你确定一个对象永远不会被整体替换,只是想深度响应式地更新其属性时,可以使用 reactive

拓展:区别中的第一个

ref使用插件

在设置中√上就可以了

他就会自动补全value

区别中的第二个

不能重新分配对象,因为他就不是原来的那个对象了

解决方法

对于ref是可以直接修改的,因为.value就是响应式数据

十四、toRefs 与 toRef

不加toRefs,数据不会改变

正确写法

当你想解构一个响应式对象而不丢失其响应性时,这两个工具函数非常有用。

  • toRefs: 将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。
    import { reactive, toRefs } from 'vue'const state = reactive({count: 0,name: 'Vue'})// 直接解构会失去响应性!// let { count, name } = state// 使用 toRefs 解构,保持响应性let { count, name } = toRefs(state)// 现在 count 和 name 都是 ref,需要 .valuecount.value++ // state.count 也会变成 1
  • toRef: 为源响应式对象上的某个属性创建一个 ref。这个 ref 是响应式的,并且会保持和源属性的同步。
    import { reactive, toRef } from 'vue'const state = reactive({count: 0})const countRef = toRef(state, 'count')// countRef 是一个 ref,修改它的 .value 会更新 state.countcountRef.value++ console.log(state.count) // 1

toref是单独解构,torefs是全部解构

解构之后原来的reactive就会变成ref对象,就需要.value

<script setup> 中解构 props 的经典用法:

<script setup>
import { toRefs } from 'vue'const props = defineProps({title: String,user: Object
})// 解构 props,保持响应式连接
const { title, user } = toRefs(props)// 或者只解构其中一个
const titleRef = toRef(props, 'title')
</script>

十五、computed 计算属性

基本使用方法

2、computed是有缓存的,但是方法函数没有

全名打印了多次,但1只打印了一次,只有发生改变是才会重新计算

computed 函数用于创建计算属性,它接收一个 getter 函数,返回一个只读的响应式 ref 对象。

import { ref, computed } from 'vue'const count = ref(0)
const doubleCount = computed(() => count.value * 2)console.log(doubleCount.value) // 0
count.value++
console.log(doubleCount.value) // 2

可写的计算属性: 传入一个带有 getset 函数的对象。

const firstName = ref('John')
const lastName = ref('Doe')const fullName = computed({get() {return `${firstName.value} ${lastName.value}`},set(newValue) {[firstName.value, lastName.value] = newValue.split(' ')}
})fullName.value = 'Jane Smith' // 会触发 setter
console.log(firstName.value) // 'Jane'
console.log(lastName.value) // 'Smith'

十六、watch 监视

watch 函数用于在响应式数据变化时执行副作用操作。

情况一:监视单个 ref

不用.value,因为他监视的是这个响应式数据,不是值

import { ref, watch } from 'vue'const count = ref(0)watch(count, (newValue, oldValue) => {console.log(`count changed from ${oldValue} to ${newValue}`)
})

结束监视就去调他的返回值

情况二:监视ref定义的对象数据类型


实际开发中一班不管旧数据

拓展:

情况三:监视 reactive 对象

直接监视整个 reactive 对象会默认开启深度监视,且 newValueoldValue 会是同一个对象(Proxy)。

const state = reactive({ count: 0, user: { name: 'A' } })watch(state, (newValue, oldValue) => {// 深度监视已开启,state.user.name 变化也会触发console.log('state changed:', newValue)console.log(newValue === oldValue) // true (都是 Proxy)
})

情况四:监视 reactive 对象的某个属性

如果需要监视嵌套属性,需要使用 getter 函数。

错误写法:

正确写法

const state = reactive({ count: 0, user: { name: 'A' } })// 监视一个嵌套属性
watch(() => state.user.name,(newName, oldName) => {console.log(`Name changed: ${oldName} -> ${newName}`)}
)// 监视多个嵌套属性
watch([() => state.count, () => state.user.name],([newCount, newName], [oldCount, oldName]) => {console.log('Count or Name changed')}
)

情况五:立即执行与深度监视

watch 的第三个参数是一个配置对象:

  • immediate: true:在侦听器创建时立即触发回调。
  • deep: true:强制深度遍历源,即使它是 ref 或 getter 返回的对象。
const state = reactive({ user: { name: 'Unknown' } })watch(() => state.user,(newUser) => {console.log('User changed:', newUser)},{ immediate: true, deep: true } // 注意:这里监视的是 state.user 这个对象引用。// 如果不加 deep: true,修改 state.user.name 不会触发回调,因为对象引用没变。
)

十七、watchEffect

watchEffect立即执行传入的函数,同时会自动追踪该函数依赖的所有响应式属性,并在它们发生变化时重新运行该函数。

  • 无需明确指定监视源,自动收集依赖。
  • 立即执行
  • 没有 newValue 和 oldValue

import { ref, watchEffect } from 'vue'const count = ref(0)
const name = ref('Alice')watchEffect(() => {// 这个函数用到了 count 和 name,它们都是依赖console.log(`Effect triggered: Count is ${count.value}, Name is ${name.value}`)
})
// 立即打印: "Effect triggered: Count is 0, Name is Alice"count.value++ 
// 重新执行,打印: "Effect triggered: Count is 1, Name is Alice"

停止侦听器: watchwatchEffect 都会返回一个停止函数,调用它可以手动停止侦听。

const stop = watchEffect(() => { ... })// 当不再需要此侦听器时:
stop()

副作用清除: watchEffect 的回调函数可以接收一个 onCleanup 函数作为参数,用于注册清理回调。

watchEffect((onCleanup) => {const timer = setTimeout(() => {// 做一些异步操作}, 1000)// 在下次重新执行前,或停止侦听时,会先调用这个函数onCleanup(() => {clearTimeout(timer)})
})

watch vs watchEffect

  • watch:需要明确指定监视源,可以访问变化前后的值,惰性(除非设置 immediate)。
  • watchEffect:自动收集依赖,无法获取旧值,立即执行。常用于处理不需要知道前后值的副作用。

总结

Vue 3 的 Composition API 通过 ref, reactive, computed, watch, watchEffect 等函数,为我们提供了更强大、更灵活的方式来组织和复用组件逻辑。结合 <script setup> 语法糖,代码变得非常简洁和直观。理解这些核心概念是掌握 Vue 3 开发的关键。建议在实际项目中多多练习,以加深理解和运用。

http://www.dtcms.com/a/461452.html

相关文章:

  • 做期货网站违法的吗淄博市住房和城乡建设局网站
  • 使用feign进行远程调用出现的问题(文件服务参数接收为null)
  • 国自然·医工交叉热点|通用医学影像分割基础模型与数据库
  • React Native:关于react自定义css属性的位置
  • 对于el-table中自定义表头中添加el-popover会弹出两个的解决方案,分别针对固定列和非固定列来隐藏最后一个浮框。
  • 电子商务公司简介系统清理优化工具
  • 内网渗透实战:红队作战全解析
  • Verilog和FPGA的自学笔记4——多路选择器1(always语句)
  • 前端架构师,是架构什么
  • Coze源码分析-资源库-编辑数据库-后端源码-安全与错误处理
  • 制作专业网站餐厅网络推广方案
  • 掌握MyBatis Java API:高效操作数据库
  • 搭建网站 程序招工网站怎么做
  • 数据库设计_理论部分_设计方法设计过程
  • 【三维重建-算法解析】MVS(Multi-View Stereo,多视图立体)
  • 【GPT5系列】ChatGPT5 提示词工程指南
  • 61850协议GOOSE通信AB网通信
  • wordpress开启子站找公司做网站有什么好处
  • SpringBoot+Redis实现电商秒杀方案
  • 电子商务网站模板 html数据型网站
  • 【QT常用技术讲解】QSerialPort串口开发,包含文件发送功能
  • STM32 外设驱动模块【含代码】:SG90 舵机模块
  • 深圳城乡和住房建设局网站263企业邮箱官网登录
  • K8s概念基础(一)
  • 计算机视觉毕业设计选题该如何选?——根据自身情况合理选择
  • 返利网一类的网站怎么做深圳好看的网站建设哪家公司好
  • 2025-2031年全球 MT 插芯市场全景分析报告:技术演进、供需格局与投资前景
  • 优化的网站做域名跳转做网站现在什么尺寸合适
  • 北京网站建设中企云达电商平台项目运营策划方案
  • 符号主义对人工智能自然语言处理中深层语义分析的影响与启示