Vue3的核心语法
OptionsAPI与CompositionAPI
Options API(选项式) 和 Composition API (组合式)是 Vue.js 中用于构建组件的两种不同方式。Options API
Options API
Options API 是 Vue 2 中的传统模式,并在 Vue 3 中继续得到支持。它通过在组件定义中使用选项(如 data
、methods
、computed
等)来组织代码,选项所定义的属性都会暴露在函数内部的 this
上,它会指向当前的组件实例
Composition API
Composition API 是 Vue 3 引入的新特性,允许开发者以函数的形式编写可重用的逻辑片段,并将这些片段组合在一起。
对比:
特性 | Options API | Composition API |
---|---|---|
代码组织方式 | 分散在 data 、methods 、computed 等选项中 |
使用 setup 函数,相关逻辑集中 |
复用性 | 较差,逻辑分散 | 较好,逻辑封装为函数,便于复用 |
维护性 | 随着组件复杂度增加,维护难度增大 | 代码集中,维护方便 |
学习难度 | 更直观,易于上手 | 学习曲线较陡 |
适用场景 | 小型项目、新手 | 大型项目、需要复用逻辑的 |
拉开序幕的setup
在 Vue 3 中,setup
函数是 Composition API 的核心入口,用于替代 Options API 中的 data
、methods
、computed
等选项。它在组件实例化之前执行,允许开发者以更灵活和模块化的方式组织组件逻辑。
setup
函数的作用
-
声明响应式状态
使用
ref
和reactive
创建响应式数据,这些数据可以在模板中直接使用。javascriptimport { ref } from 'vue'; export default { setup() { const count = ref(0); return { count }; } };
-
定义计算属性和侦听器
使用
computed
和watch
来创建计算属性和侦听器,从而实现更灵活的状态管理。javascriptimport { ref, computed, watch } from 'vue'; export default { setup() { const count = ref(0); const doubleCount = computed(() => count.value * 2); watch(count, (newVal, oldVal) => { console.log(`count changed from ${oldVal} to ${newVal}`); }); return { count, doubleCount }; } };
-
定义方法
在
setup
中定义方法,并在模板中调用这些方法。javascriptexport default { setup() { const count = ref(0); const increment = () => { count.value++; }; return { count, increment }; } };
-
使用生命周期钩子
虽然
setup
本身不是生命周期钩子,但可以在其中访问生命周期钩子函数(如onMounted
、onUnmounted
等)。javascriptimport { onMounted } from 'vue'; export default { setup() { onMounted(() => { console.log('Component is mounted!'); }); } };
-
逻辑复用和组合
通过自定义的组合函数(Composables),可以封装特定逻辑,并在多个组件中复用。
javascript// useCounter.js import { ref } from 'vue'; export function useCounter() { const count = ref(0); const increment = () => { count.value++; }; return { count, increment }; } // MyComponent.vue import { useCounter } from './useCounter'; export default { setup() { const { count, increment } = useCounter(); return { count, increment }; } };
-
与 Options API 的兼容性
虽然
setup
是 Vue 3 的新特性,但它可以与 Options API 共存。不过,为了保持代码的一致性,建议选择一种方式并坚持使用。
setup
函数的参数
setup
函数可以接收两个参数:props
和 context
。
props
:包含传递给组件的所有属性,是响应式的,不能直接解构。context
:包含attrs
、slots
和emit
,分别对应未声明为props
的属性、插槽内容和自定义事件。
示例
以下是一个更复杂的示例,展示如何在 setup
中综合使用响应式数据、计算属性、侦听器和生命周期钩子。
html
<template>
<div>
<h1>{{ message }}</h1>
<p>Count: {{ count }}</p>
<p>Double Count: {{ doubleCount }}</p>
<button @click="increment">Increment</button>
<button @click="resetCount">Reset Count</button>
</div>
</template>
<script>
import { ref, computed, watch, onMounted, onUnmounted } from 'vue';
export default {
props: {
message: String
},
setup(props) {
// 响应式数据
const count = ref(0);
// 计算属性
const doubleCount = computed(() => count.value * 2);
// 方法
const increment = () => {
count.value++;
};
const resetCount = () => {
count.value = 0;
};
// 侦听器
watch(count, (newVal, oldVal) => {
console.log(`Count changed from ${oldVal} to ${newVal}`);
});
// 生命周期钩子
onMounted(() => {
console.log('Component is mounted!');
});
onUnmounted(() => {
console.log('Component is unmounted!');
});
// 返回对象,暴露给模板
return {
message: props.message,
count,
doubleCount,
increment,
resetCount
};
}
};
</script>
- 响应式数据 :
count
是通过ref
创建的响应式数据。 - 计算属性 :
doubleCount
是基于count
的计算属性。 - 方法 :
increment
和resetCount
是定义的操作方法。 - 侦听器 :通过
watch
监听count
的变化,并在控制台输出变化信息。 - 生命周期钩子:在组件挂载和卸载时分别输出日志。
- 返回值 :
setup
返回的对象包含所有需要在模板中使用的数据和方法。
setup语法糖
setup
语法糖是 Vue 3 提供的一种更简洁的 setup
使用方式。它允许你直接在组件中使用 props
和 context
,而无需手动解构。
使用场景:
- 简化
props
的声明和使用 - 简化
context
的访问 - 与 TypeScript 的更好集成
基本用法:
js
<script setup> //关键
import { ref } from 'vue';
const count = ref(0);
const increment = () => count.value++;
</script>
<template>
<div>
<p>{{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
在上面的例子中,<script setup>
是 setup
语法糖的关键。它允许你在 <script>
标签中直接编写逻辑代码,而无需手动定义 setup
函数。
2. 声明 props
在 setup
语法糖中,props
可以直接在 <script>
标签中声明,而无需手动解构。
html
<script setup>
import { ref } from 'vue';
const props = defineProps({
title: String,
initialCount: {
type: Number,
default: 0
}
});
const count = ref(props.initialCount);
const increment = () => count.value++;
</script>
<template>
<div>
<h1>{{ title }}</h1>
<p>{{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
3.使用emit
setup
语法糖还支持直接使用 emit
,而无需手动解构 context
。
html
<script setup>
import { ref } from 'vue';
const props = defineProps({
title: String
});
const emit = defineEmits(['update:title']);
const updateTitle = () => {
emit('update:title', 'New Title');
};
</script>
<template>
<div>
<h1>{{ title }}</h1>
<button @click="updateTitle">Update Title</button>
</div>
</template>
4.使用 attrs
和 slots
setup
语法糖还支持直接访问 attrs
和 slots
,而无需手动解构 context
。
html
<script setup>
import { useSlots, useAttrs } from 'vue';
const slots = useSlots();
const attrs = useAttrs();
</script>
<template>
<div>
<p>Slots: {{ Object.keys(slots) }}</p>
<p>Attrs: {{ attrs }}</p>
</div>
</template>
ref
ref
是 Composition API 的核心功能之一,用于创建响应式数据引用。它允许开发者将基++本类型++ (如数字、字符串、布尔值)或++复杂类型++(如对象、数组)包装成响应式对象。
ref 基本概念
ref
是 Vue 3 提供的一个函数,用于创建一个响应式引用。它将一个值包装成一个响应式对象,该对象通过 .value
属性访问和修改其值。
js
import { ref } from 'vue';
const count = ref(0); // 创建一个响应式的数字
const name = ref('Vue 3'); // 创建一个响应式的字符串
const isVisible = ref(true); // 创建一个响应式的布尔值
在模板中使用 ref
时,Vue 会自动解包 .value
,因此可以直接使用。
html
<template>
<div>
<p>Count: {{ count }}</p>
<p>Name: {{ name }}</p>
<h2>当前和为:{{ sum }}</h2>
</div>
</template>
ref 的用法
1.基本用法:
ref
可以包装基本类型或复杂类型,但是必须通过 .value
访问和修改值。
js
console.log(count.value); // 输出 0
count.value++; // 修改值
console.log(count.value); // 输出 1
function changesum(){
sum.value +=1;
}
2.响应式对象和数组
ref
也可以用于对象和数组,Vue 会自动追踪其内部属性的变化。
javascript
const user = ref({ name: 'John', age: 30 });
console.log(user.value.name); // 输出 John
user.value.age = 31; // 修改对象属性
对于数组,可以直接使用数组方法(如 push
、pop
)。
javascript
const tasks = ref(['学习 Vue 3']);
tasks.value.push('完成项目');
3 .响应式对象的嵌套
如果 ref
包装的是一个对象,对象内部的属性也是响应式的,但需要通过 .value
访问。
javascript
const user = ref({ name: 'John', age: 30 });
console.log(user.value.name); // 输出 John
user.value.age = 31; // 修改对象属性
注意事项:
- 直接修改
ref
的.value
属性才会触发响应式更新。
reactive
reactive
是一个核心的响应式 API,用于创建响应式对象。它通过 ES6 的 Proxy
实现深层次的响应式代理,能够自动追踪对象属性的变化并触发视图更新。
基本用法:
reactive
主要用于处理对象或数组类型的响应式数据。它将一个普通对象转换为响应式对象,可以直接访问和修改其属性,而无需通过 .value
。
示例 1:基本用法
vue
<template>
<div>
<p>用户信息: {{ user.name }} - {{ user.age }}岁</p>
<button @click="incrementAge">增加年龄</button>
</div>
</template>
<script>
import { reactive } from 'vue';
export default {
setup() {
const user = reactive({
name: '张三',
age: 25,
});
const incrementAge = () => {
user.age++; // 直接修改属性,无需使用 .value
};
return { user, incrementAge };
}
};
</script>
在这个示例中,reactive
创建了一个包含用户信息的对象 user
。点击按钮时,user.age
直接被修改,视图也会自动更新。
reactive
支持深层嵌套对象的响应式更新,这使得它非常适合处理复杂的数据结构。
示例 2:嵌套对象与数组
vue
<template>
<div>
<p>用户信息: {{ user.name }} - {{ user.age }}岁</p>
<p>地址: {{ user.address.city }}, {{ user.address.zip }}</p>
<button @click="incrementAge">增加年龄</button>
<button @click="updateAddress">更新地址</button>
</div>
</template>
<script>
import { reactive } from 'vue';
export default {
setup() {
const user = reactive({
name: '张三',
age: 25,
address: {
city: '北京',
zip: '100000'
}
});
const incrementAge = () => {
user.age++;
};
const updateAddress = () => {
user.address.city = '上海';
user.address.zip = '200000';
};
return { user, incrementAge, updateAddress };
}
};
</script>
在这个示例中:
user
是一个包含嵌套对象address
的响应式对象。- 修改
user.age
或user.address
的属性时,视图会自动更新。
解析
- 响应式原理
reactive
使用 ES6 的Proxy
对象实现深层次的响应式代理。它会自动拦截对对象属性的get
和set
操作,从而实现依赖收集和更新触发。
reactive的弊端
ref 对比reactive
特性 | ref |
reactive |
---|---|---|
适用类型 | 基本类型(数字、字符串、布尔值),也可以用于对象和数组 | 对象、数组 |
访问方式 | 需要 .value |
直接访问 |
深度响应式 | 不支持 | 支持 |
性能 | 轻量级,适合简单数据 | 深度响应式,适合复杂数据 |
使用场景 | 简单数据、计数器、DOM 引用 | 复杂对象、表单数据、状态管理 |
toRefs 和 toRef
toRef 的介绍与用法
1. 作用
toRef
用于从一个响应式对象 中提取某个属性 ,并将其转换为一个独立 的 ref
对象。这个 ref
对象与原始属性保持响应式连接 ,即修改 ref
的值会同步更新原始属性,反之亦然。
2. 使用场景
- 当需要将响应式对象的某个属性单独提取出来时。
- 当需要将某个属性传递给组合式函数或子组件时。
3. 示例代码
javascript
import { reactive, toRef } from 'vue';
const state = reactive({
name: 'Alice',
age: 25
});
const nameRef = toRef(state, 'name'); // 提取 name 属性为 ref
console.log(nameRef.value); // 输出 Alice
nameRef.value = 'Bob'; // 修改 ref 的值
console.log(state.name); // 输出 Bob,原始属性也被更新
state.age = 30; // 修改原始属性
const ageRef = toRef(state, 'age');
console.log(ageRef.value); // 输出 30,ref 与原始属性同步[^36^]
toRefs的介绍与用法
1. 作用
toRefs
用于将一个响应式对象的所有属性转换为一个普通对象,其中每个属性都是一个 ref
对象。这些 ref
对象与原始属性保持响应式连接。

2. 使用场景
- 当需要解构响应式对象并在模板中使用其属性时,
toRefs
可以确保解构后的属性仍然保持响应式。 - 当需要将响应式对象的属性传递给子组件时,
toRefs
可以确保子组件接收到的属性是响应式的。
3. 示例代码
javascript
import { reactive, toRefs } from 'vue';
const state = reactive({
name: 'Alice',
age: 25
});
const stateRefs = toRefs(state); // 将所有属性转换为 ref
console.log(stateRefs.name.value); // 输出 Alice
console.log(stateRefs.age.value); // 输出 25
stateRefs.name.value = 'Bob'; // 修改 ref 的值
console.log(state.name); // 输出 Bob,原始属性也被更新
state.age = 30; // 修改原始属性
console.log(stateRefs.age.value); // 输出 30,ref 与原始属性同步[^37^]
4. 原理解释
toRefs
遍历响应式对象的所有属性,并为每个属性调用 toRef
,从而创建一个包含所有属性的 ref
对象的普通对象。
toRef 与toRefs 的区别
特性 | toRef |
toRefs |
---|---|---|
作用对象 | 单个属性 | 整个对象的所有属性 |
返回值 | 单个 ref 对象 |
包含所有属性的 ref 对象的普通对象 |
使用场景 | 提取单个属性并保持响应式连接 | 解构整个对象并保持响应式连接 |
灵活性 | 更灵活,适用于特定属性 | 适用于需要批量处理所有属性的场景 |
computed
在 Vue 3 中,computed
是一个非常重要的响应式特性,用于声明式地计算派生值。它基于依赖的响应式数据自动缓存计算结果,并且只有当依赖项发生变化时才会重新计算。
1. computed
的基本用法
computed
可以在 Vue 组件的 setup()
函数中通过 computed()
方法使用,也可以在选项式 API 中直接定义。
js
//CompositionAPI中的写法
import { ref, computed } from 'vue';
export default {
setup(){
const fiestName = ref('John');
const lastName = ref('Doa');
//定义一个 computed 属性
const fullName = computed(()=>{
return `${firstName.value} ${lastName.value}`
});
return {
firstName,
lastName,
fullName
};
}
};
在模板中可以直接使用fullName
:
html
<template>
<div>
<p>Full Name: {{ fullName }}</p>
<input v-model="firstName" placeholder="First Name">
<input v-model="lastName" placeholder="Last Name">
</div>
</template>
2. computed
的两种模式
computed
支持两种模式:getter-only (只读)和 writable(可读可写)。
这是最常见的模式,只提供一个 getter 函数,用于计算派生值。
javascript
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`;
});
如果需要让 computed
属性可写,可以传入一个对象,包含 get
和 set
方法。
js
const fullName = computed({
get() {
return `${firstName.value} ${lastName.value}`;
},
set(newValue) {
const names = newValue.split(' ');
firstName.value = names[0];
lastName.value = names[1];
}
});
在模板中,fullName
现在可以被赋值:
html
<input v-model="fullName" placeholder="Full Name">
完整示例:
html
<template>
<div class="person">
姓: <input type="text" v-model="firstName"><br><br>
名: <input type="text" v-model="lastName"><br><br>
全名:<span>{{ fullName }}</span>
<button @click="changFN">改全名为li-si</button>
</div>
</template>
js
<script setup lang="ts" name = "Person">
import {ref,computed} from 'vue';
let firstName = ref('zhang');
let lastName = ref('san');
// //这么定义的fullName是一个计算属性,且是只读的
// let fullName = computed(()=>{
// return firstName.value.slice(0,1).toUpperCase() +firstName.value.slice(1)+ '-' + lastName.value;
// })
//这么定义的fullName是一个计算属性,是可读可写的
let fullName = computed({
get(){
return firstName.value.slice(0,1).toUpperCase() +firstName.value.slice(1)+ '-' + lastName.value;
},
set(val){
const [str1,str2] = val.split('-');
firstName.value = str1;
lastName.value = str2;
console.log('set');
}
})
function changFN(){
fullName.value = 'li-si';
}
</script>
css
<style scoped>
.person{
background-color: rgb(5, 141, 141);
border-radius: 10px;
color: rgb(195, 202, 202);
box-shadow: 0 0 10px;
padding: 20px;
}
button{
margin: 10px;
}
</style>
3. 注意点
(1) ++缓存机制++
computed
是基于依赖的缓存。只有当依赖项发生变化时,才会重新计算。如果依赖项没有变化,即使手动调用 computed
,也不会重新计算,而是直接返回缓存值。
(2) ++避免将 computed
用于副作用操作++
computed
应该是纯函数,仅用于计算派生值。不要在 computed
中执行副作用操作(如 API 请求、修改数据等)。如果需要执行副作用操作,应该使用 watch
或 watchEffect
。
(3) ++依赖响应式数据++
computed
的依赖必须是响应式数据(如 ref
、reactive
或其他 computed
)。如果依赖非响应式数据,computed
将不会自动更新。
(4) 在 computed
中访问 this
在 Options API 中,可以直接通过 this
访问组件的上下文。但在 Composition API 中,computed
是一个独立的函数,不能直接访问 this
,需要通过 setup()
中的变量来访问。
(5) ++computed
的返回值++
computed
的返回值必须是一个值(如字符串、数字、对象等),不能返回函数或其他复杂结构。
(6) 避免过度使用 computed
虽然 computed
很强大,但过度使用可能会导致组件逻辑复杂化。对于简单的逻辑,直接在模板中使用表达式可能更清晰。