9787111724773

脚手架的升级与配置

# 安装 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)

优点:

  1. 缓存,原始数据不变的情况下,会立即返回;
  2. 书写统一,获取值的方式和 ref 一致;

缺点:

  1. 只能更新响应式数据的计算(比如日期 Date() 不会更新);
  2. 数据是只读的(可通过 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>

动态绑定 CSS