Vue 3 中 const 的指南
在 Vue 3 中,const 是 JavaScript 的关键字,但在 Vue 的响应式系统中,它有着特殊的使用方式和意义。下面我将详细解释 const 在 Vue 3 中的各种用法。
一、const 基础概念
1. const 的基本定义
定义 :const 是 JavaScript 中用于声明常量的关键字,表示这个标识符不能被重新赋值。
与 let 的区别:
const:声明常量,不可重新赋值let:声明变量,可以重新赋值
javascript
// 使用 const
const PI = 3.14159
// PI = 3.14 // ❌ 错误:不能重新赋值
// 使用 let
let count = 0
count = 1 // ✅ 正确:可以重新赋值
2. const 在 Vue 3 中的重要性
Vue 3 的 Composition API 鼓励使用 const 来声明响应式数据,因为:
- 提高代码可读性
- 防止意外重新赋值
- 配合响应式系统更安全
二、const 在 Vue 3 中的 6 种用法
1. 声明响应式数据(ref/reactive)
定义 :使用 const 声明通过 ref() 或 reactive() 创建的响应式数据。
3个示例:
示例1:使用 const 声明 ref
vue
<script setup>
import { ref } from 'vue'
// 基本类型使用 ref
const count = ref(0) // count 是常量,但 count.value 可以修改
const message = ref('Hello Vue 3')
// 修改值
const increment = () => {
count.value++ // ✅ 正确:修改 .value
// count = ref(1) // ❌ 错误:不能重新赋值
}
console.log(count.value) // 访问 .value
</script>
示例2:使用 const 声明 reactive
vue
<script setup>
import { reactive } from 'vue'
// 对象类型使用 reactive
const user = reactive({
name: '张三',
age: 25,
email: 'zhangsan@example.com'
})
// 修改属性
const updateUser = () => {
user.name = '李四' // ✅ 正确:可以修改属性
user.age = 30
// user = reactive({}) // ❌ 错误:不能重新赋值
}
</script>
示例3:混合使用
vue
<script setup>
import { ref, reactive } from 'vue'
// 混合声明
const isLoading = ref(false) // 布尔值
const userInfo = reactive({ // 对象
name: '张三',
scores: [90, 85, 88]
})
const apiUrl = 'https://api.example.com' // 真正的常量
// 使用方法
const fetchData = async () => {
isLoading.value = true
try {
const response = await fetch(apiUrl)
const data = await response.json()
Object.assign(userInfo, data)
} finally {
isLoading.value = false
}
}
</script>
2. 声明计算属性
定义 :使用 const 声明计算属性,这些属性会根据依赖的响应式数据自动更新。
3个示例:
示例1:基本计算属性
vue
<script setup>
import { ref, computed } from 'vue'
const price = ref(100)
const quantity = ref(2)
// 计算总价
const totalPrice = computed(() => {
return price.value * quantity.value
})
// 计算带税价格
const priceWithTax = computed(() => {
return totalPrice.value * 1.1
})
console.log(totalPrice.value) // 200
</script>
示例2:带 getter 和 setter 的计算属性
vue
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('张')
const lastName = ref('三')
// 可写的计算属性
const fullName = computed({
// getter
get() {
return `${firstName.value} ${lastName.value}`
},
// setter
set(newValue) {
const [first, last] = newValue.split(' ')
firstName.value = first || ''
lastName.value = last || ''
}
})
// 使用
fullName.value = '李 四' // 会调用 setter
console.log(firstName.value) // 李
console.log(lastName.value) // 四
</script>
示例3:基于对象属性的计算
vue
<script setup>
import { reactive, computed } from 'vue'
const user = reactive({
profile: {
basicInfo: {
age: 25,
height: 175
},
education: {
degree: '本科',
school: '清华大学'
}
}
})
// 计算是否成年
const isAdult = computed(() => {
return user.profile.basicInfo.age >= 18
})
// 计算用户简介
const userDescription = computed(() => {
return `${user.profile.education.degree},${user.profile.education.school}`
})
</script>
3. 声明方法/函数
定义 :使用 const 声明组件中的方法,这些方法不会被重新赋值。
3个示例:
示例1:普通方法
vue
<script setup>
import { ref } from 'vue'
const count = ref(0)
// 使用 const 声明方法
const increment = () => {
count.value++
}
const decrement = () => {
if (count.value > 0) {
count.value--
}
}
const reset = () => {
count.value = 0
}
</script>
示例2:异步方法
vue
<script setup>
import { ref } from 'vue'
const data = ref(null)
const loading = ref(false)
const error = ref(null)
// 异步方法
const fetchData = async (url) => {
loading.value = true
error.value = null
try {
const response = await fetch(url)
if (!response.ok) {
throw new Error(`HTTP错误: ${response.status}`)
}
data.value = await response.json()
} catch (err) {
error.value = err.message
} finally {
loading.value = false
}
}
// 使用方法
const loadUserData = () => {
fetchData('https://api.example.com/users')
}
</script>
示例3:带参数的方法
vue
<script setup>
import { reactive } from 'vue'
const formData = reactive({
username: '',
password: '',
rememberMe: false
})
// 表单处理方法
const handleSubmit = (event) => {
event.preventDefault()
console.log('提交数据:', formData)
// 这里可以调用API
}
const handleReset = () => {
formData.username = ''
formData.password = ''
formData.rememberMe = false
}
// 带默认参数的方法
const showMessage = (message = '默认消息', type = 'info') => {
console.log(`[${type}] ${message}`)
}
</script>
4. 声明组件引用(ref/template ref)
定义 :使用 const 声明模板引用,用于直接访问DOM元素或组件实例。
3个示例:
示例1:DOM元素引用
vue
<template>
<div>
<input ref="inputRef" type="text" />
<button @click="focusInput">聚焦输入框</button>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
// 声明模板引用
const inputRef = ref(null)
const focusInput = () => {
if (inputRef.value) {
inputRef.value.focus()
}
}
onMounted(() => {
// 组件挂载后自动聚焦
inputRef.value?.focus()
})
</script>
示例2:组件实例引用
vue
<!-- ParentComponent.vue -->
<template>
<div>
<ChildComponent ref="childRef" />
<button @click="callChildMethod">调用子组件方法</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
// 引用子组件实例
const childRef = ref(null)
const callChildMethod = () => {
if (childRef.value) {
childRef.value.sayHello() // 调用子组件方法
}
}
</script>
vue
<!-- ChildComponent.vue -->
<script setup>
// 子组件暴露方法
const sayHello = () => {
console.log('Hello from child component!')
}
// 使用 defineExpose 暴露方法
defineExpose({
sayHello
})
</script>
示例3:多个元素引用
vue
<template>
<div>
<input ref="inputs[0]" type="text" />
<input ref="inputs[1]" type="text" />
<input ref="inputs[2]" type="text" />
<button @click="focusNext">聚焦下一个输入框</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
// 使用数组存储多个引用
const inputs = ref([])
let currentIndex = 0
const focusNext = () => {
if (inputs.value[currentIndex]) {
inputs.value[currentIndex].blur()
}
currentIndex = (currentIndex + 1) % inputs.value.length
if (inputs.value[currentIndex]) {
inputs.value[currentIndex].focus()
}
}
</script>
5. 声明组件属性(props)
定义 :在 <script setup> 中使用 const 声明 props,通过 defineProps 定义。
3个示例:
示例1:基本属性定义
vue
<!-- UserCard.vue -->
<template>
<div class="user-card">
<h3>{{ name }}</h3>
<p>年龄: {{ age }}</p>
<p v-if="isAdmin">管理员</p>
</div>
</template>
<script setup>
// 使用 const 声明 props
const props = defineProps({
name: {
type: String,
required: true
},
age: {
type: Number,
default: 18
},
isAdmin: {
type: Boolean,
default: false
}
})
// 在逻辑中使用 props
const canEdit = props.isAdmin || props.age >= 18
</script>
示例2:使用 TypeScript 的类型标注
vue
<script setup lang="ts">
interface Props {
title: string
count?: number
items: string[]
onConfirm?: () => void
}
// 使用 TypeScript
const props = defineProps<Props>()
// 默认值
const { title = '默认标题', count = 0 } = props
</script>
示例3:解构 props
vue
<script setup>
// 解构 props
const {
name,
age = 20, // 提供默认值
isAdmin = false
} = defineProps({
name: String,
age: Number,
isAdmin: Boolean
})
// 直接使用解构后的变量
const userType = isAdmin ? '管理员' : '普通用户'
</script>
6. 声明上下文(emit/attrs/slots)
定义 :使用 const 声明组件的上下文,如 emit、attrs、slots。
3个示例:
示例1:使用 emit
vue
<!-- Counter.vue -->
<template>
<div>
<p>计数: {{ count }}</p>
<button @click="increment">增加</button>
<button @click="decrement">减少</button>
<button @click="reset">重置</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
// 定义 emit
const emit = defineEmits(['increment', 'decrement', 'reset'])
const count = ref(0)
// 触发事件
const increment = () => {
count.value++
emit('increment', count.value)
}
const decrement = () => {
if (count.value > 0) {
count.value--
emit('decrement', count.value)
}
}
const reset = () => {
count.value = 0
emit('reset')
}
</script>
示例2:使用 attrs 和 slots
vue
<!-- CustomButton.vue -->
<template>
<button v-bind="attrs" :class="buttonClass">
<slot>默认按钮</slot>
</button>
</template>
<script setup>
import { computed, useAttrs, useSlots } from 'vue'
// 获取 attrs 和 slots
const attrs = useAttrs()
const slots = useSlots()
// 根据是否有插槽添加样式
const buttonClass = computed(() => {
return {
'has-icon': slots.icon,
'has-text': slots.default
}
})
</script>
<style scoped>
.has-icon {
display: flex;
align-items: center;
gap: 8px;
}
</style>
示例3:完整的上下文使用
vue
<!-- Modal.vue -->
<template>
<div v-if="isVisible" class="modal">
<div class="modal-header">
<slot name="header">
<h2>{{ title }}</h2>
</slot>
<button @click="close">×</button>
</div>
<div class="modal-body">
<slot></slot>
</div>
<div class="modal-footer">
<slot name="footer">
<button @click="confirm">确认</button>
<button @click="cancel">取消</button>
</slot>
</div>
</div>
</template>
<script setup>
import { ref, useAttrs, useSlots } from 'vue'
// 定义 props
const props = defineProps({
title: {
type: String,
default: '提示'
},
visible: {
type: Boolean,
default: false
}
})
// 定义 emit
const emit = defineEmits(['update:visible', 'confirm', 'cancel'])
// 获取 attrs 和 slots
const attrs = useAttrs()
const slots = useSlots()
// 内部状态
const isVisible = ref(props.visible)
// 方法
const close = () => {
isVisible.value = false
emit('update:visible', false)
}
const confirm = () => {
emit('confirm')
close()
}
const cancel = () => {
emit('cancel')
close()
}
</script>
三、const 的最佳实践
1. 何时使用 const
✅ 应该使用 const 的情况:
- 响应式数据(
ref()、reactive()) - 计算属性(
computed()) - 方法/函数
- 组件引用
- 导入的模块
- 配置对象
- 不会重新赋值的变量
✅ 应该使用 let 的情况:
- 循环计数器
- 需要重新赋值的变量
- 条件语句中的临时变量
2. 代码示例对比
好的实践:
vue
<script setup>
import { ref, computed } from 'vue'
// ✅ 使用 const
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
const increment = () => {
count.value++
}
// ✅ 使用 let
let timer = null
const startTimer = () => {
timer = setInterval(() => {
count.value++
}, 1000)
}
</script>
不好的实践:
vue
<script setup>
import { ref } from 'vue'
// ❌ 错误的用法
let count = ref(0) // 可能被意外重新赋值
let increment = () => { // 方法不应该用 let
count.value++
}
</script>
3. 常见误区
误区1:const 声明的对象不能修改
javascript
// ✅ 可以修改对象属性
const user = { name: '张三' }
user.name = '李四' // 正确
// user = {} // 错误
// ✅ 可以修改数组元素
const numbers = [1, 2, 3]
numbers.push(4) // 正确
numbers[0] = 0 // 正确
// numbers = [] // 错误
误区2:const 声明的 ref 值不能修改
vue
<script setup>
import { ref } from 'vue'
const count = ref(0)
count.value = 1 // ✅ 正确:修改 .value
// count = ref(1) // ❌ 错误:重新赋值
</script>
四、高级技巧
1. 动态组件名
vue
<template>
<component :is="currentComponent" />
</template>
<script setup>
import { ref, shallowRef } from 'vue'
import ComponentA from './ComponentA.vue'
import ComponentB from './ComponentB.vue'
// 使用 shallowRef 避免不必要的深度响应
const currentComponent = shallowRef(ComponentA)
const switchComponent = () => {
currentComponent.value = currentComponent.value === ComponentA
? ComponentB
: ComponentA
}
</script>
2. 组合式函数
javascript
// useCounter.js
import { ref, computed } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
const reset = () => {
count.value = initialValue
}
const double = computed(() => count.value * 2)
return {
count,
increment,
decrement,
reset,
double
}
}
vue
<script setup>
import { useCounter } from './useCounter'
// 使用组合式函数
const { count, increment, double } = useCounter(10)
</script>
3. TypeScript 中的 const 断言
vue
<script setup lang="ts">
// 使用 as const 确保类型安全
const user = {
name: '张三',
age: 25
} as const // 所有属性都是只读的
// user.name = '李四' // ❌ 错误:不能修改
// 路由配置示例
const routes = [
{ path: '/', name: 'Home' },
{ path: '/about', name: 'About' }
] as const
</script>
五、总结
在 Vue 3 中,const 的使用建议:
- 总是使用
const声明响应式数据(除非真的需要重新赋值) - 使用
const声明方法和计算属性 - 在
<script setup>中优先使用const - 理解
const只是防止重新赋值,不防止修改对象属性
速记口诀:
Vue3 开发用 const,
响应数据不放过。
ref reactive computed,
函数方法也适合。
组件引用模板用,
属性定义不错过。
只有需要重赋值,
let 才能来替换。
通过合理使用 const,可以让你的 Vue 3 代码更加安全、可读和可维护。