Vue3文档精炼

我的 Vue3 文档

内置标签

component

动态组件

根据 currentTabComponent 的值判断是否要渲染组件

1
<component v-bind:is="currentTabComponent"></component>

keep-alive

每次切换新标签的时候,Vue 都会创建一个新的 currentTabComponent 实例。

1
2
3
4
<!-- 失活的组件将会被缓存!-->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>

slot⭐

子组件的占位符

在父组件引用的

1
2
3
4
5
6
7
8
9
10
<!-- 父组件 -->
<Button>
<div>这是默认插槽的内容</div>
</Button>

<!-- 子组件 -->
<template>
<div>子组件内容</div>
<slot></slot>
</template>

渲染结果

1
2
3
4
<div>
<div>子组件内容</div>
<div>这是默认插槽的内容</div>
</div>

默认插槽

当子组件只需要一个插槽,并且不需要区分不同部分的内容时,默认插槽是最佳选择

父组件

1
import Button from '@/components/button.vue'
1
2
3
4
5
<div class="box">
<Button> <!--引用子组件-->
<div>插槽内容,把这行div插到子组件的"<slot>"里</div>
</Button>
</div>

子组件

1
2
3
4
<template>
<div>子组件内容</div>
<slot></slot>
</template>

渲染结果

1
2
3
4
<div class="box">
<div>子组件内容</div>
<div>插槽内容,把这行div插到子组件的"<slot>"里</div>
</div>

具名插槽

父组件

1
2
3
4
5
6
7
8
9
10
<div class="box">
<Button> <!--引用子组件-->
<template #header> <!--插给header-->
<div>插槽内容1</div>
</template>
<template v-slot:footer> <!--插给footer-->
<div>插槽内容2</div>
</template>
</Button>
</div>

子组件

1
2
3
4
5
<template>
<slot name="header"></slot>
<div>子组件内容</div>
<slot name="footer"></slot>
</template>

渲染结果

1
2
3
4
5
<div class="box">
<div>插槽内容1</div>
<div>子组件内容</div>
<div>插槽内容2</div>
</div>

条件插槽

父组件

1
2
3
4
5
6
7
8
9
10
<div class="box">
<Button> <!--引用子组件-->
<template #header> <!--插给header-->
<div>插槽内容1</div>
</template>
<template v-slot:footer> <!--插给footer-->
<div>插槽内容2</div>
</template>
</Button>
</div>

子组件

1
2
3
4
5
6
7
8
9
10
11
<template>
<div v-if="$slots.header">
<div>标题</div>
<slot name="header"></slot>
</div>

<div v-if="$slots.footer">
<div>脚注</div>
<slot name="footer"></slot>
</div>
</template>

渲染结果

1
2
3
4
5
6
7
8
9
<div class="box">

<div>标题</div>
<div>插槽内容1</div>

<div>脚注</div>
<div>插槽内容2</div>

</div>

transition

1

transition-group

1

Props

defineProps⭐

子获取父的传值

子组件

接口

1
2
3
4
5
interface Props {
tag?: string
type?: 'primary' | 'success' | 'warning' | 'danger' | 'info'
useThrottle?: boolean
}
1
const props = defineProps<Props>()
1
2
3
4
5
6
7
8
9
<component
:is="props.tag || 'button'"
ref="_ref"
:type="props.nativeType || 'button'"
@click="
(e: MouseEvent) =>
props.useThrottle !== false ? handleBtneCLickThrottle(e) : handleBtnClick(e)">
<slot></slot>
</component>

父组件

1
<vr-... tag="a" type="primary" :use-throttle="true" />

withDefaults

defineProps 绑定默认值

接口

1
2
3
4
interface ...Props{
tag?: string | Component
size?: 'default' | 'large' | 'small'
}
1
2
3
4
withDefaults(defineProps<...Props>(),{
tag?: "button"
size?: default
})

Emits

defineEmits

子向父发射事件

接口 (函数签名类型)

1
2
3
export interface ...Emits {
(e: 'click', value: MouseEvent): void
}

子组件

1
const emits = defineEmits<...Emits>()
1
2
3
const handle...Click = (e: MouseEvent) => {
emits('click', e)//发射'click'事件 | 父组件通过@click响应到
}
1
<component @click="(e: MouseEvent) => handle...Click(e)">

父组件 用户使用组件

1
2
<vr-... @click="Click" />
//当接收到子组件发射过来的事件之后,触发父组件自己的'Click'函数

1.点击后 子组件的 @click 被触发,将一个 MouseEvent 类型事件传入 handle...Click 函数

2.emits('click', e) 向父组件发射 'click' 事件

3.父组件通过@click 响应到 'click' 事件, 开始执行父组件中自定义的 Click 事件

Slots

defineSlots

1
const slots = defineSlots()
1
2
3
const iconStyle = computed(() => ({
marginRight: slots.default ? '6px' : '0px',
}))

检测组件是否有默认插槽, 有则插入样式 marginRight: 6px

Expose

defineExporse

暴露组件的内部状态和方法,供父组件通过 ref 访问

<script setup> 中,如果不使用 defineExpose,父组件无法通过 ref 访问子组件的任何内部状态或方法

1
2
3
4
5
6
defineExpose<ButtonInstance>({
ref: _ref,
disabled,
size,
type,
})

父组件可以通过 ref 访问这些暴露的属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<VrButton ref="buttonRef" />
</template>

<script setup>
import { ref } from 'vue'

const buttonRef = ref(null)

const checkButtonState = () => {
console.log('按钮是否禁用:', buttonRef.value.disabled)
console.log('按钮类型:', buttonRef.value.type)
}
</script>

专业名词

工具链

构建工具

  • ViteWebpack:

    这些是模块打包工具

    负责将你的 Vue 项目中的各种**资源(如 JavaScript、CSS、图片等)打包成浏览器可以理解的形式**。

    Vite 作为新一代的构建工具,以其快速的冷启动和热更新特性而受到欢迎。

编译器

  • Vue 编译器:

    Vue**单文件组件(SFC, Single File Components)需要被编译为普通 JavaScript**才能在浏览器中运行。Vue 提供了一个编译器来完成这项工作,它可以在构建时(通过构建工具配置)或运行时进行编译。

Linter 和 Formatter

  • ESLint, Prettier:

    这些工具帮助**保持代码风格的一致性捕获潜在的错误**。

    ESLint 是一个流行的 JavaScript linter,可以帮助你**发现代码中的问题**;

    Prettier 则是一个代码格式化工具,能够**自动格式化**你的代码以符合预定义的风格指南。

状态管理

  • Vuex, Pinia:

    Vuex 是 Vue.js 的状态管理模式+库。

    它充当应用中**所有组件的集中存储**,确保状态以一种可预测的方式发生变化。

路由管理

  • Vue Router:

    官方的路由管理器,与 Vue.js 核心深度集成。

    用于实现 SPA(单页面应用)中的**导航**功能。

单元测试

  • Jest, Mocha, Vue Test Utils:

    使用这些工具可以对 Vue 组件进行**单元测试**,确保各个部分按预期工作。

    Vue Test Utils 是由 Vue 官方提供的一个测试实用库,简化了 Vue 组件的测试过程。

开发服务器

  • 内置开发服务器:

    Vite 和 Webpack 都提供了内置的开发服务器,支持**热重载**(HMR, Hot Module Replacement)

    这意味着当你修改代码时,浏览器会自动刷新显示最新的更改,无需手动刷新整个页面。

CSS 预处理器

  • Sass, Less, Stylus:

    流行的 CSS 预处理器,允许你**用更强大的语法编写样式**,并且通过构建工具集成到 Vue 项目中。

SSR

简述

服务端渲染 (Server-Side Rendering)

相对传统客户端渲染(CSR, Client-Side Rendering)的 优势

  1. 更好的 SEO:服务器端直接返回完整的 HTML,搜索引擎可以轻松抓取页面内容。
  2. 更快的首屏加载:用户可以直接看到渲染好的 HTML,无需等待 JavaScript 加载完成。
  3. 更一致的用户体验:无论用户的设备性能如何,都可以快速看到页面内容。

原理

核心思想: 在服务器端生成 HTML,并将其发送到客户端

完整流程
用户访问一个 URL,请求服务器
服务器 render HTML,完整的 HTML 字符串客户端
用户立即可以看到页面内容(此时页面静态,无法响应用户的交互)
浏览器继续加载 Vue 应用的客户端 JS 文件(通常是 client-bundle.js), 这个文件包含了 Vue 的运行时代码以及应用的逻辑
Hydration 水合阶段一:客户端 JS 解析 HTML,将现有的 DOM 节点 **匹配 Vue 组件模板 **
Hydration 水合阶段二:Vue 在匹配的 DOM 节点上 绑定事件监听器(如 clickinput 等),使页面能够响应用户的操作。
Vue 重新 初始化组件状态(如 datacomputedmethods 等),并将其与 DOM 关联起来。如果服务端已经预取了数据(如通过 Vuex 或 Pinia),这些数据会被同步到客户端,确保状态一致性。
页面上的每个元素从此都被 Vue 接管,用户可以与页面进行交互

特点

  1. 高效利用现有 DOM
    • Hydration 不会重新创建 DOM 节点,而是直接复用服务器生成的 HTML。
    • 这样可以避免重复渲染,提升性能。
  2. 依赖于一致的 HTML 结构
    • 服务端生成的 HTML 必须与客户端的组件模板完全一致,否则 Hydration 会失败。
    • 如果服务端和客户端的 HTML 不一致,Vue 会抛出警告或错误。
  3. 渐进式增强
    • Hydration 是一种渐进式增强的技术:即使 JavaScript 加载失败,用户仍然可以看到静态页面内容(虽然没有交互性)。

使用