脚手架的升级与配置
# 安装 Vue
npm i vue
# 安装全局脚手架(这个是作者个人的脚手架
npm install -g create-preset
# 使用脚手架创建模板
preset init hello-vue3 --template vue3-ts-vite
- create-vite:
npm create vite
单组件的编写
数据的侦听
- 批量侦听
- 侦听效果清理
侦听:
// 侦听单个来源
function watch<T>(
source: WatchSource<T>, // 数据源
callback: WatchCallback<T>, // 回调函数
options?: WatchOptions // 侦听选项(可选)
): StopHandle
// 侦听多个来源
function watch<T>(
sources: WatchSource<T>[],
callback: WatchCallback<T[]>,
options?: WatchOptions
): StopHandle
参数说明:
// 数据源
type WatchSource<T> =
| Ref<T> // ref
| (() => T) // getter
| T extends object
? T
: never // 响应式对象
// 回调函数(参数无须定义)
type WatchCallback<T> = (
value: T, // 新值
oldValue: T, // 旧值
onCleanup: (cleanupFn: () => void) => void // 清理函数
) => void
// 侦听选项
interface WatchOptions extends WatchEffectOptions {
immediate?: boolean // 默认:false,是否立即执行侦听回调
deep?: boolean // 默认:false,是否进行深度侦听
flush?: 'pre' | 'post' | 'sync' // 默认:'pre',控制侦听回调的调用时机
onTrack?: (event: DebuggerEvent) => void // 在数据源被追踪时调用
onTrigger?: (event: DebuggerEvent) => void // 在侦听回调被触发时调用
once?: boolean // 默认:false (3.4+),回调函数仅运行一次
}
用法:
// 侦听 ref
const count = ref(42)
watch(count, (count, prevCount) => {
/* ... */
})
// 侦听响应式对象(侦听器会自动启用深层模式)
const state = reactive({ count: 42 })
watch(state, () => {
console.log('侦听整个 state:', state.count)
})
// 侦听 getter 函数
const state = reactive({ count: 42 })
watch(
() => state.count, // 箭头函数写法
(newValue, oldValue) => {
console.log('只侦听 count:', state.count)
}
)
// 停止侦听
state()
注:在 setup 中使用时在组件被卸载的时候也会一起停止(使用的是同步语句,非 setTimeout 等异步语句),无须手动停止侦听。另外如何启用了 immediate 选项,不能在第一次触发时执行。
watchEffect
export declare type WatchEffect = (onCleanup: OnCleanup) => void
export declare function watchEffect(
effect: WatchEffect,
options?: WatchOptionsBase
): WatchStopHandle
和 watch 区别:
- watch 可以访问侦听状态变化前后的值,而后者没有;
- watch 需要在属性改变的时候才执行,而后者会默认执行一次;
- watchPostEffect:watchEffect API 使用 flush: ‘post’ 选项时的别名
- watchEffect API 使用 flush: ‘sync’ 选项时的别名
数据的计算
默认情况下 computed() 创建的是只读属性,文中是对 ref 属性进行修改:
import { ref, computed } from 'vue'
const firstName = ref<string>('Bill')
const lastName = ref<string>('Gates')
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
// 修改 firstName,如果直接修改 fullName 会报错
setTimeout(() => {
firstName.value = 'immwind'
}, 2000)
优点:
- 缓存,原始数据不变的情况下,会立即返回;
- 书写统一,获取值的方式和 ref 一致;
缺点:
- 只能更新响应式数据的计算(比如日期 Date() 不会更新);
- 数据是只读的(可通过 set 更新数据);
应用场景
- 数据的拼接和计算:比如购物车列表和商品总额
- 复用组件的动态数据
- 获取多级对象的值
- 不同类型的数据转换:(使用 watch 时考虑能否用 computed 代替)
指令
内置指令别名:
- v-on 的别名是 @ ,使用 @click 等价于 v-on:click
- v-bind 的别名是 : ,使用 :src 等价于 v-bind:src
自定义指令
插槽
书内举的例子没有说明文件对应的位置,导致有点混淆。
父组件 home.vue:
<script setup>
import Child from '@/cp/Child.vue'
</script>
<template>
<Child>
<!-- 传给标题插槽 -->
<template #title>
<h1>这是标题</h1>
</template>
<!-- 传给作者插槽 -->
<template #author>
<h2>作者信息</h2>
</template>
<template #default />
</Child>
</template>
src/cp 目录下创建子组件 Child.vue:
<template>
<!-- 显示标题的插槽内容 -->
<div class="title">
<slot name="title" />
</div>
<!-- 显示作者的插槽内容 -->
<div class="author">
<slot name="author" />
</div>
<div class="default">
<slot>这是默认内容</slot>
</div>
<!-- 其他插槽内容放到这里 -->
<div class="content">
<slot />
</div>
</template>
CSS 样式与预处理器
最基础写法是在 Vue 中添加 <style /> 标签即可
:
<style>
/* CSS 代码 */
.author {
color: #ef0404;
}
</style>