Vue3
组合式 API
vue3 新增setup()
语法,包括两个参数props
和context
。定义的变量和方法等都在setup()
里面。
编码技巧
- 使用组合式 API。按功能块写代码,避免将一堆 ref 变量写在一块
- 使用 setup 语法糖,即
<script setup>
编写组件
需要掌握的 API:
defineProps()
和defineEmits()
defineModel()
defineExpose()
defineOptions()
- 使用 ref 声明响应式变量,尽量避免使用 reactive
安装官方的 VScode 插件:Vue-Official,然后勾选上 Auto-complete Ref value with .value
就可以自动补全 .value
v-if
和v-for
不能在同一元素上使用,可以使用template
标签
<ul>
<template v-for="item in list" :key="item.id">
<li v-if="item.isActive">{{ item.name }}</li>
</template>
</ul>
ref、reactive
二者都能定义响应式变量。官方建议使用 ref() 作为声明响应式状态的主要 API。
ref
ref 可以定义基本类型和引用类型,将其变为响应式变量。
在 script 中使用时,变量名要带上.value
,在 template 中使用时不用。
<template>
<button v-for="(item, index) in user" :key="index" @click="clickName(index)">{{ index }} : {{ item }}</button>
<h1>name: {{ name }}</h1>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const user = ref<string[]>(['tom', 'jack', 'ivan']);
const name = ref<string>('');
const clickName = (index: number) => {
name.value = user.value[index];
};
</script>
如果将一个对象赋值给 ref,那么这个对象将通过 reactive()
转为具有深层次响应式的对象。这意味着如果对象中包含了嵌套的 ref,它们将被深层地解包。
若要避免这种深层次的转换,可以使用 shallowRef()
来替代。
下面示例中可以看出,通过 ref 包裹的对象,内部是通过reactive()
将其变为响应式对象的。
import { ref, reactive } from 'vue';
const msg = ref('Hello World!');
console.log(msg);
const foo = ref({ name: 'vue' });
console.log(foo);
const bar = reactive({ name: 'vue' });
console.log(bar);
要获取原始值可以通过 toRaw()
:
const list = ref([1, 2, 3]);
console.log(list.value); // Proxy
const raw = toRaw(list.value);
console.log(raw); // [1, 2, 3]
reactive
- 返回一个对象的响应式代理
- 不能用于原始类型,只能用于对象类型
- 使用时不带
.value
- 可以用
toRefs()
将其转换成ref
<template>
<button v-for="(item, index) in data.list" :key="index" @click="data.btnFun(index)">{{ index }} : {{ item }}</button>
<h1>{{ data.listName }}</h1>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
interface DataProps {
list: string[];
listName: string;
btnFun: (index: number) => void;
}
const data: DataProps = reactive({
list: ['first', 'second', 'third'],
listName: '',
btnFun: (index: number) => {
data.listName = data.list[index];
}
});
</script>
reactive 在重新分配对象时,会失去响应式。
let state = reactive({ count: 0 });
// 修改里面的属性没问题,依然是响应式
state.count = 1;
// 丢失响应性,上面的 { count: 0 } 引用将不再被追踪
state = { count: 1 };
// 即使用reactive包裹,依然会丢失响应式。这时就是一个新的响应式对象
state = reactive({ count: 1 });
在实际开发中,如果是通过接口返回的数据,不可能按照属性一个个的修改,如下所示:
const ajaxData = { count: 2, foo: 3, bar: 4 };
state.count = ajaxData.count;
state.foo = ajaxData.foo;
state.bar = ajaxData.bar;
// ...
这时可以通过使用 Object.assign()
来处理。
Object.assign(state, ajaxData);
使用 ref 可以直接替换:
let state = ref({ count: 0 });
state.value = ajaxData;
关于 reactive 的解构:
let person = reactive({ name: 'zgh', age: 18 });
// 这么解构会丢失响应性
let { name, age } = person;
name = 'lrx';
// 通过 toRefs() 解决
let { name, age } = toRefs(person);
name.value = 'lrx';
// 这时 person.name 的值也变为 lrx
在 template 中,可以使用{{name}}
,也可以使用{{person.name}}
计算属性
计算属性 computed()
,默认是只读的。使用 gettr 和 setter 可读可写。
import { ref, computed } from 'vue';
const firstName = ref('zhang');
const lastName = ref('san');
// 只读
const fullName = computed(() => firstName.value + lastName.value);
// 可读可写
const fullName = computed({
get() {
return firstName.value + ' ' + lastName.value;
},
set(newValue) {
const [n1, n2] = newValue.split(' ');
firstName.value = n1;
lastName.value = n2;
}
});
// 注意使用计算属性得到的是一个 ref
function changeFullName() {
fullName.value = 'Li si';
}
watch 监听
先导入import { watch } from 'vue'
。第一个参数是要监听的变量,第二个是回调函数。如果要监听多个变量,那么第一个参数传入数组
监听类型
- ref 定义的变量
const count = ref(0);
watch(count, newVal => {
console.log(newVal);
});
// 获取旧值
watch(count, (newVal, old) => {
console.log(newVal, old);
});
注意:监听 ref 定义的变量时,不用写.value
,因为用了.value
就是要监听对象的属性,是一个具体值,而不是一个响应式对象。
- 一个有返回值的函数
watch(
() => count.value,
newVal => {
console.log(newVal);
}
);
- 一个数组
const count = ref(0);
const num = ref(0);
// newVal可以解构赋值
watch([count, num], newVal => {
console.log(newVal); // [1, 0] ...
});
watch([count, () => num.value], newVal => {
console.log(newVal);
});
- reactive 定义的变量,不能直接监听,需要返回一个函数
const obj = reactive({ a: 1, b: 2 });
watch(
() => obj.a,
newVal => {
console.log(newVal);
}
);