目录
[五、Props 与 Emits](#五、Props 与 Emits)
前言
在 Vue3 开发中,
<script setup>语法糖凭借其简洁、直观的写法,已经成为大多数开发者的首选。但当你加入一个新团队,或者维护一个老项目时,可能会遇到使用传统<script>写法的代码。面对
export default { setup() { ... } }这样的结构,很多习惯了语法糖的同学会感到困惑:为什么还要手动 return?props 怎么定义?生命周期还能用吗?本文将用对比学习 的方式,帮你快速掌握传统
<script>的核心写法,让你无缝切换两种开发模式。
一、最直观的对比:结构差异
1、语法糖写法(你熟悉的)
javascript
<script setup>
import { ref } from 'vue'
const count = ref(0)
const increment = () => count.value++
// 自动暴露给模板,无需 return
</script>
2、传统写法(公司项目常见)
javascript
<script>
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
const increment = () => count.value++
// 必须手动 return,模板才能使用
return { count, increment }
}
}
</script>
核心差异:语法糖自动 return 所有顶层绑定,传统写法需要你手动 return。
二、响应式数据的定义
| 场景 | <script setup> |
传统 <script> |
|---|---|---|
| ref 定义 | const num = ref(0) |
同上,但要 return |
| reactive 定义 | const state = reactive({}) |
同上,但要 return |
| 模板中使用 | 直接 {``{ num }} |
直接 {``{ num }} |
示例代码:
javascript
<!-- 传统写法 -->
<script>
import { ref, reactive } from 'vue'
export default {
setup() {
const count = ref(10)
const user = reactive({ name: 'Alice', age: 18 })
return { count, user }
}
}
</script>
<template>
<p>{{ count }}</p>
<p>{{ user.name }}</p>
</template>
三、方法的定义与使用
异步方法、普通方法写法完全一致,唯一区别仍然是 return。
javascript
<script>
import { ref } from 'vue'
import { getUserInfo } from '@/api/user'
export default {
setup() {
const data = ref(null)
const loading = ref(false)
const fetchData = async () => {
loading.value = true
const res = await getUserInfo()
data.value = res.data
loading.value = false
}
// 方法也需要 return
return { data, loading, fetchData }
}
}
</script>
四、生命周期钩子
生命周期钩子的用法完全一致 ,因为都是在
setup()函数内部调用。
javascript
<script>
import { onMounted, onUnmounted } from 'vue'
export default {
setup() {
onMounted(() => {
console.log('组件已挂载')
})
onUnmounted(() => {
console.log('组件即将卸载')
})
return {}
}
}
</script>
五、Props 与 Emits
这是最容易混淆的地方,对比来看更清晰。
1、语法糖写法
javascript
<script setup>
const props = defineProps(['title', 'id'])
const emit = defineEmits(['update', 'close'])
emit('update', 'new value')
</script>
2、传统写法
javascript
<script>
export default {
props: ['title', 'id'],
emits: ['update', 'close'],
setup(props, { emit }) {
emit('update', 'new value')
return {}
}
}
</script>
注意 :
props是响应式对象,不要解构它,否则会失去响应性。
六、完整示例对比
1、你习惯的写法(语法糖)
javascript
<script setup>
import { ref, onMounted } from 'vue'
import { getArticleList } from '@/api/article'
const articles = ref([])
const loading = ref(false)
const loadArticles = async () => {
loading.value = true
const res = await getArticleList()
articles.value = res.data
loading.value = false
}
onMounted(() => {
loadArticles()
})
</script>
2、公司项目的写法(传统)
javascript
<script>
import { ref, onMounted } from 'vue'
import { getArticleList } from '@/api/article'
export default {
setup() {
const articles = ref([])
const loading = ref(false)
const loadArticles = async () => {
loading.value = true
const res = await getArticleList()
articles.value = res.data
loading.value = false
}
onMounted(() => {
loadArticles()
})
// 关键:把所有要在模板中用的变量和函数都 return
return {
articles,
loading,
loadArticles
}
}
}
</script>
七、快速转换口诀
所有代码放进
setup() { } ,用到的变量/函数必须return出去props 单独写在
props选项中emits 单独写在
emits选项中生命周期钩子用法不变
八、常见错误与解决
| 错误现象 | 原因 | 解决方案 |
|---|---|---|
| 模板中显示 undefined | 变量没 return | 添加到 return 对象中 |
| props 不更新 | 解构了 props | 直接使用 props.xxx |
| emit 不生效 | 没声明 emits | 添加 emits: ['事件名'] |
| this 报错 | 想用 this.xxx | 直接用变量名,没有 this |
九、总结
| 特性 | <script setup> |
传统 <script> |
|---|---|---|
| 代码量 | 更少 | 稍多(需要 return) |
| 学习曲线 | 平缓 | 稍陡 |
| 组件实例 | 无 this | 无 this(setup 中也没有) |
| 适用场景 | 新项目、组件库 | 老项目、复杂配置 |
核心思想 :两种写法本质是一样的,传统写法只是把语法糖"自动做的事"变成了"手动做"。理解
setup()函数 +return这个机制,你就能轻松看懂任何传统写法的 Vue 组件。
写在最后
如果你刚接触传统写法,不用慌。先把
return { }里的内容当作"模板能用的东西清单",看不懂的代码就找这个清单。多写几次就能完全掌握。
希望这篇文章能帮助你快速适应公司项目的代码风格。如果有任何疑问,欢迎在评论区留言交流!