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()
来处理。
const
Object.assign(state, ajaxData);
使用 ref 可以直接替换:
let state = ref({ count: 0 });
state.value = ajaxData;
关于解构:
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}}
生命周期
setup()
开始创建组件之前,在beforeCreate()
和created()
之前执行
<template>
<h1>vue</h1>
</template>
<script lang="ts">
import {
defineComponent,
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onRenderTriggered,
onRenderTracked,
onBeforeUnmount,
onUnmounted
} from 'vue';
export default defineComponent({
name: 'App',
setup() {
console.log('开始创建组件');
onBeforeMount(() => {
console.log('挂载前');
});
onMounted(() => {
console.log('完成挂载');
});
onBeforeUpdate(() => {
console.log('更新前');
});
onUpdated(() => {
console.log('完成更新');
});
onRenderTriggered(event => {
console.log('状态触发');
});
onRenderTracked(event => {
console.log('状态跟踪');
});
onBeforeUnmount(() => {
console.log('卸载之前');
});
onUnmounted(() => {
console.log('卸载完成');
});
}
});
</script>
有趣的是在setup()
函数之后可以继续编写 Vue2 的生命周期函数,即 Vue2 的生命周期函数还是可用的,但是别混用
vue2 和 vue3 生命周期对比:
beforeCreate -> setup()
created -> setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
activated -> onActivated
deactivated -> onDeactivated
errorCaptured -> onErrorCaptured