在使用 Vue 3 的 <script setup> 语法糖进行开发时,很多开发者会遇到一个常见问题:
"为什么我直接修改变量会报错?明明在模板里能用啊!"
本文将彻底讲清楚这个核心规则,并提供最佳实践建议。
🔥 核心规则
在 Vue 3 的 <script setup> 中:
所有需要被修改的变量都必须使用
ref()包装!无论该变量是否在模板中使用。
❓ 为什么会这样?
<script setup> 内部会对顶层绑定的变量进行只读处理 (类似 Object.freeze()),这是 Vue 3 的有意设计,目的包括:
- 防止意外修改:避免直接赋值导致响应式系统失效
- 性能优化:只读变量可被编译器更好地优化
- 明确意图 :通过
ref()显式声明"这是一个可变的响应式数据"
🚫 错误示例 vs ✅ 正确写法
❌ 错误:直接声明并修改普通变量
javascript
// 报错!TypeError: "xxx" is read-only
const noMoreData = false;
noMoreData = true; // ❌ 运行时错误!
✅ 正确:使用 ref() 包装
javascript
import { ref } from 'vue';
const noMoreData = ref(false);
noMoreData.value = true; // ✅ 完全正常
💡 注意:修改时要通过
.value,但在模板中使用时无需.value(Vue 自动解包)
📋 实际开发中的使用指南
| 变量类型 | 是否需要 ref() |
示例 |
|---|---|---|
| 模板中使用的数据 | ✅ 必须 | const list = ref([]) |
| 需要动态修改的状态 | ✅ 必须 | const loading = ref(false) |
| 分页/加载控制标志 | ✅ 必须 | const noMoreData = ref(false) |
| 常量(永不修改) | ❌ 不需要 | const PAGE_SIZE = 20 |
| 配置项/URL 基础路径 | ❌ 不需要 | const BASE_URL = 'https://api.example.com' |
| 工具函数 | ❌ 不需要 | const formatDate = (date) => {...} |
✅ 推荐代码结构
javascript
<script setup>
import { ref, computed } from 'vue'
// ✅ 需要 ref() 的变量(会变化 or 模板使用)
const userList = ref([])
const isLoading = ref(false)
const currentPage = ref(1)
const hasMore = ref(true)
// ❌ 不需要 ref() 的变量(常量/函数)
const PAGE_SIZE = 10
const API_BASE = '/api'
const formatTime = (timestamp) => {
return new Date(timestamp).toLocaleString()
}
// 计算属性(自动响应式,无需 ref)
const totalUsers = computed(() => userList.value.length)
</script>
⚠️ 常见误区提醒
-
误区1 :"只要不在模板里用,就可以不用
ref"→ 错!只要需要修改,就必须用
ref -
误区2 :"用
let声明就能修改"→ 错!
<script setup>中的let变量同样会被冻结 -
误区3 :"可以用
reactive代替ref"→ 对于基础类型(boolean/number/string),
reactive无效,必须用ref
✅ 总结
在 Vue 3 <script setup> 中,请牢记:
- 要修改?→ 用
ref()- 不修改?→ 普通变量即可
这不仅是语法要求,更是 Vue 3 响应式系统的设计哲学------让可变性显式化,让代码更安全、更清晰!
遵循这一原则,你将避免绝大多数"只读错误",写出更健壮的 Vue 3 应用!🚀