从vue2到vue3
一、脚手架
vue2: vue-cli 基于webpack
vue3:基于vite
二、TypeScript支持
Vue 3对TypeScript的支持进行了大量改进,包括更好的类型推断和更清晰的API设计,使得在TypeScript环境中使用Vue更加顺畅
三、状态存储
vue2: vuex
vue3:pinia 更简单
四、template限制
vue2: 要求有唯一根元素
vue3:没有唯一限制,可多个,解决了多个div嵌套的问题
五、script
vue2: <script>
vue3:<script setup>
在 Vue 3 单文件组件 (SFC) 中,<script setup>
和传统的 <script>
(选项式 API 写法)是两种不同的脚本编写方式,核心区别体现在语法风格、功能特性和使用场景上。以下是具体对比:
特性 | 传统 <script> (选项式 API) | <script setup> (组合式 API 语法糖) |
---|---|---|
语法风格 | 选项对象(data 、methods 等) | 顶层声明,自动暴露给模板 |
组件注册 | 需在 components 中显式注册 | 导入后自动注册 |
响应式 | 依赖 data 和 this | 依赖 ref 、reactive 等 API |
异步操作 | 需在生命周期中处理 | 支持顶层 await |
灵活性 | 结构固定,逻辑拆分较难 | 逻辑可自由组合、拆分(配合组合函数) |
1. 语法结构
传统 <script>
(选项式 API)
需要通过
export default
导出一个选项对象(如data
、methods
、computed
等)。数据和方法需要定义在特定选项中,模板通过选项名访问。
<template><button @click="increment">{{ count }}</button> </template><script> export default {data() {return { count: 0 } // 数据必须放在 data 中},methods: {increment() { this.count++ } // 方法必须放在 methods 中} } </script>
<script setup>
(组合式 API 语法糖)
无需
export default
,顶层声明的变量、函数会自动暴露给模板。直接使用 Vue 的组合式 API(如
ref
、reactive
)定义响应式数据,逻辑更灵活。<template><button @click="increment">{{ count }}</button> </template><script setup> import { ref } from 'vue' const count = ref(0) // 直接声明响应式数据 const increment = () => { count.value++ } // 直接定义函数 </script>
2. 组件注册
传统 <script>
导入的组件必须在
components
选项中注册后才能使用。vue
<script> import Child from './Child.vue' export default {components: { Child } // 必须显式注册 } </script>
<script setup>
导入的组件自动注册,可直接在模板中使用,无需额外配置。
vue
<script setup> import Child from './Child.vue' // 导入后直接可用 </script><template><Child /> </template>
3. 响应式处理
传统 <script>
- 依赖选项式 API 的固定结构(
data
返回响应式对象,methods
定义方法)。 - 通过
this
访问组件实例的数据和方法(this.count
、this.increment()
)。
<script setup>
- 依赖组合式 API(
ref
、reactive
等)手动创建响应式数据。 - 无需
this
,直接通过变量名访问(如count.value
),避免了this
指向问题。
4. 异步操作
传统 <script>
初始化异步操作需放在
created
或mounted
生命周期中,写法较繁琐。vue
<script> export default {data() { return { data: null } },async mounted() {this.data = await fetch('/api').then(res => res.json())} } </script>
<script setup>
支持顶层
await
,可直接在脚本顶层编写异步逻辑,更简洁。vue
<script setup> const data = await fetch('/api').then(res => res.json()) // 直接使用 await </script>
六、API
vue2: 选项式api
vue3:组合式api (兼容选项式api)
七、双向数据绑定的方式
vue2: 采用ES5的object.definePropert()对数据进行劫持,结合发布订阅和观察者模式进行的;
vue3:采用ES6的Proxy的数据代理来对数据进行代理,修复了vue2中对象和数组的属性添加修改的问题;
//vue3使用 ref() 函数来声明响应式状态,Ref 可以持有任何类型的值,包括深层嵌套的对象、数组或者 JavaScript 内置的数据结构
import { ref } from 'vue'const count = ref(0)
// reactive() 将使对象本身具有响应性,只能用于对象类型 (对象、数组和如 Map、Set 这样的集合类型)。
import { reactive } from 'vue'const state = reactive({ count: 0 })
八、生命周期钩子
vue3新增钩子:
onRenderTracked
: 调试工具中用于追踪组件渲染过程中的依赖关系。onRenderTriggered
: 当响应式依赖发生变更时触发,可用于调试性能问题。- setup:Vue 3 引入了
setup
函数,它会在组件实例创建之前执行,因此beforeCreate
和created
钩子不再必要。所有原本放在这两个钩子里的逻辑可以直接写在setup
函数内。
九、父组件访问子组件数据 ref
vue2: this.$refs.childComponent.message;
- 在父组件中,通过
ref="childComponent"
获取子组件实例。- 使用
this.$refs.childComponent.message
获取子组件的数据。
vue3:defineExpose
defineExpose
是 Vue 3 提供的一个仅能在<script setup>
中使用的函数,用来显式暴露组件内部的属性或方法,使得父组件可以通过ref
访问子组件的暴露内容。
//vue2
<!-- Parent.vue -->
<template><div><child ref="childComponent" /><button @click="getMessage">Get Message from Child</button><p>{{ childMessage }}</p></div>
</template><script>
import Child from './Child.vue';export default {components: {Child},data() {return {childMessage: ''};},methods: {getMessage() {this.childMessage = this.$refs.childComponent.message;}}
};
</script><!-- Child.vue -->
<template><div><p>{{ message }}</p></div>
</template><script>
export default {data() {return {message: 'Hello from Child'};}
};
</script>
//vue3
//子组件
<script setup>
import { ref } from 'vue';// 子组件内部的状态和方法
const count = ref(0);function increment() {count.value++;
}// 通过 defineExpose 暴露给父组件
defineExpose({count,increment
});
</script><template><div><p>计数器子组件:{{ count }}</p></div>
</template>//父组件
<script setup>
import { ref } from 'vue';
import Counter from './Counter.vue';// 通过 ref 获取子组件实例
const counterRef = ref(null);function callChildMethod() {counterRef.value.increment(); // 调用子组件的方法console.log('子组件计数值:', counterRef.value.count); // 访问子组件的暴露属性
}
</script><template><Counter ref="counterRef" /><button @click="callChildMethod">调用子组件方法</button>
</template>