一.Composition API
选项式 API 与 Composition API 属于两种心智模型,选项式就是我们所说的配置式,它通过前期的内容约定,结构规范保证程序健壮性及可读性。组合式显然比选项式灵活,除此以外,组合式实现了 UI 复用与状态逻辑复用的分离。
1.1常见的Composition API
-
setUp: 组合式API的入口
-
ref 和 reactive 有什么区别?
①基本类型 vs 对象类型ref:可以处理任何类型的值(基本类型和对象类型)
reactive:只能处理对象类型(Object, Array, Map, Set等)
②访问方式
ref:需要通过
.value
访问和修改值reactive:直接访问和修改属性
③ 模板中使用
ref:在模板中自动解包,不需要 .value
<div>{{ count }}</div> <!-- 不需要 count.value --
reactive:直接访问属性
<div>{{ state.count }}</div>
④ 解构/扩展
ref:保持响应性,可以单独传递
reactive:解构或扩展会失去响应性,需要使用 toRefs
保持响应性
⑤ 实现原理
ref:使用对象包装,通过 getter/setter 实现响应式
reactive:使用 Proxy 实现深层响应式
3. watchEffect 与 watch 的区别
懒执行副作用;
更加明确是应该由哪个状态触发侦听器重新执行;
可以访问所侦听状态的前一个值和当前值。
4. 生命周期
5. 异步组件
在大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件。换言之,我们的组件可能不再是同步导入或者组件需要等待 Promise
resolve
完成后才被渲染。这样的组件我们称为异步组件。
<script setup>
import { defineAsyncComponent } from 'vue'
const AdminPage = defineAsyncComponent(() =>
import('./components/AdminPageComponent.vue')
)
</script>
<template>
<AdminPage />
</template>
注意: 通常是和<Suspense>标签配合使用
<template>
<Suspense>
<!-- 默认插槽:显示异步组件 -->
<template #default>
<AsyncComponent />
</template>
<!-- 备用插槽:显示加载中的内容 -->
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
</template>
<script setup lang="ts">
import { defineAsyncComponent } from 'vue';
// 定义一个异步组件
const AsyncComponent = defineAsyncComponent(() =>
import('./AsyncComponent.vue')
);
</script>
6. 自定义指令
权限控制指令
app.directive('permission', (el, binding) => {
const { value } = binding
//const userRoles = getCurrentUserRoles() // 获取当前用户角色的方法
const userRoles = ['admin'] //模拟数据
if (value && value instanceof Array && value.length > 0) {
const hasPermission = userRoles.some(role => value.includes(role))
if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`需要指定权限,例如 v-permission="['admin','editor']"`)
}
}
)
<template>
<div class="container">
<button v-permission="['admin']">管理员按钮</button>
<button v-permission="['editor']">编辑者按钮</button>
</div>
</template>
指令的钩子会传递以下几种参数:
-
el
:指令绑定到的元素。这可以用于直接操作 DOM。 -
binding
:一个对象,包含以下属性。-
value
:传递给指令的值。例如在v-my-directive="1 + 1"
中,值是2
。 -
oldValue
:之前的值,仅在beforeUpdate
和updated
中可用。无论值是否更改,它都可用。 -
arg
:传递给指令的参数 (如果有的话)。例如在v-my-directive:foo
中,参数是"foo"
。 -
modifiers
:一个包含修饰符的对象 (如果有的话)。例如在v-my-directive.foo.bar
中,修饰符对象是{ foo: true, bar: true }
。 -
instance
:使用该指令的组件实例。 -
dir
:指令的定义对象。
-
-
vnode
:代表绑定元素的底层 VNode。 -
prevNode
:之前的渲染中代表指令所绑定元素的 VNode。仅在beforeUpdate
和updated
钩子中可用。
7. Teleport
该特性允许你将组件内的某个子组件挂载到任意 HTML 节点上,<Teleport>
只改变了渲染的 DOM 结构,它不会影响组件间的逻辑关系。也就是说,如果 <Teleport>
包含了一个组件,那么该组件始终和这个使用了 <teleport>
的组件保持逻辑上的父子关系。传入的 props 和触发的事件也会照常工作。
<Teleport>
接收一个 to prop 来指定传送的目标。to 的值可以是一个 CSS 选择器字符串(如:to=".container"
、to="#container
),也可以是一个 DOM 元素对象(如:to="body"
)。
<Teleport to="body">
<!-- 使用这个 modal 组件,传入 prop -->
<modal :show="isShow" @close="isShow = false">
<template #header>
<h3>custom header</h3>
</template>
</modal>
</Teleport>
8. 自定义Hooks
我们通过自定义 Hook,可以将组件的状态与 UI 实现分离,虽然这个 api 和早期的 mixin 非常像,但是他的设计思想实在先进太多。
-
import { Ref, readonly, ref } from 'vue' // 判断是否为数字 const isNumber = (value: unknown): value is number => typeof value === 'number' export interface UseCounterOptions { /** * Min count */ min?: number /** * Max count */ max?: number } export interface UseCounterActions { /** * Increment, default delta is 1 * @param delta number * @returns void */ inc: (delta?: number) => void /** * Decrement, default delta is 1 * @param delta number * @returns void */ dec: (delta?: number) => void /** * Set current value * @param value number | ((c: number) => number) * @returns void */ set: (value: number | ((c: number) => number)) => void /** * Reset current value to initial value * @returns void */ reset: () => void } export type ValueParam = number | ((c: number) => number) function getTargetValue(val: number, options: UseCounterOptions = {}) { const { min, max } = options let target = val if (isNumber(max)) { target = Math.min(max, target) } if (isNumber(min)) { target = Math.max(min, target) } return target } function useCounter( initialValue = 0, options: UseCounterOptions = {}, ): [Ref<number>, UseCounterActions] { const { min, max } = options const current = ref( getTargetValue(initialValue, { min, max, }), ) const setValue = (value: ValueParam) => { const target = isNumber(value) ? value : value(current.value) current.value = getTargetValue(target, { max, min, }) return current.value } const inc = (delta = 1) => { setValue(c => c + delta) } const dec = (delta = 1) => { setValue(c => c - delta) } const set = (value: ValueParam) => { setValue(value) } const reset = () => { setValue(initialValue) } return [ readonly(current), { inc, dec, set, reset, }, ] } export default useCounter