Vue3-Day3

1. 插槽

插槽就是子组件预留的占位坑位,让父组件可以自定义插入 HTML / 组件内容,实现组件内容灵活复用。
简单比喻:
子组件是一个相框,<slot> 就是相框中间空白区域;父组件传入图片 / 文字,填进这个空白位置。

在Vue中,有三种插槽,对应不同的使用方式

  • 默认插槽(匿名插槽):该插槽一个组件只能存在一个
  • 具名插槽:子组件多处需要自定义内容,给每个 slot 起名字,父组件对应 v-slot:名字(简写 #名字)。
  • 作用域插槽:
    • 普通插槽:父只能把内容传给子;
    • 作用域插槽:子组件内部数据,反向传给父组件使用。
    • 适用场景:表格、列表循环,子循环列表,父自定义每一行渲染。
      子组件:给 slot 绑定自定义属性向外抛数据

子组件Card

<!-- components/Card.vue --> <template> <div class="card"> <h1> <!-- 默认插槽:一个组件只有一个默认插槽 --> <slot></slot> </h1> <h2> <!-- 具名插槽:需要多个插槽可以取名字 --> <slot name="name1"></slot> </h2> <h2> <slot name="name2"></slot> </h2> </div> </template> <script setup> </script>

子组件List

<template> <div> <div v-for="item in list" :key="item.id"> <!-- 子给插槽传数据:item ,别名row --> <!-- 作用域插槽 --> <slot :row="item"></slot> </div> </div> </template> <script setup> const list = [ { id:1, name:'张三' }, { id:2, name:'李四' } ] </script>

父组件

<script setup lang="ts"> import Card from './Card.vue' import List from './List.vue' </script> <template> <div> 父组件 <Card> <p>父组件使用子组件默认插槽</p> <template #name1> <p>父组件使用子组件具名插槽1</p> </template> <template #name2> <p>父组件使用子组件具名插槽2</p> </template> </Card> </div> <div> <List> <!-- 父组卷使用子组件作用域插槽 --> <template #default="{ row }"> <div>{{ row.id }} - {{ row.name }}</div> </template> </List> </div> </template>

2. defineOptions

  • 版本要求
    Vue ≥ 3.3 / Vite / Vue CLI 新版内置支持,无需导入,直接使用宏
    作用:在<script setup>内声明组件选项(name、inheritAttrs、props、emits 等)
  • 注意
    • 在Vite中,会自动命名name,但是推荐还是要写,不然会出现小问题
    • 命名了name后在Vue DevTools中可以看到自定义名称,而非匿名组件

常用于定义组件name

<script setup> // 直接定义组件名称,无需额外script块 defineOptions({ name: 'UserCard' }) </script> <template> <div>用户卡片</div> </template>

所有选项

<script setup> defineOptions({ // 组件名称(最重要,用于递归组件、devtools、keep-alive) name: 'TableList', // 是否继承父组件非props属性 inheritAttrs: false, // 自定义渲染函数(极少用) render: () => {}, // 静态props、emits(setup内优先用defineProps/defineEmits) props: [], emits: [], // 其他原生选项:components、directives 等不推荐在这里写 }) </script>

3. 动态组件

  • 作用:同一位置切换多个组件,不用 v-if/v-else 一堆判断,通过 is 属性指定要渲染的组件。
  • 每次切换组件
    • 旧组件执行 unmounted 销毁
    • 新组件重新 mounted 创建
    • 表单输入、滚动位置、接口数据全部丢失
    • 想要保留状态,就套上<KeepAlive>

3.1 实现组件切换

父组件 - Tab0

<script setup lang="ts"> import { ref } from 'vue' import Tab1 from './Tab1.vue' import Tab2 from './Tab2.vue' defineOptions({ name: 'Tab0' // 组件名称 }) // 初始值 const current = ref(Tab1); </script> <template> <button @click="current = Tab1">切换页面1</button> <button @click="current = Tab2">切换页面2</button> <!-- 通过:is绑定组件 --> <component :is="current" /> </template>

子组件 - Tab1

<script setup lang="ts"> defineOptions({ name: 'Tab1' }) </script> <template> <P>我是子组件1</P> </template>

子组件 - Tab2

<script setup lang="ts"> defineOptions({ name: 'Tab2' }) </script> <template> <P>我是子组件2</P> </template>

3.2 实现组件切换保留缓存

  • <keepAlive>可以实现缓存不活动的组件实例,不销毁,再次切换时直接复用,保留:表单值、滚动位置、接口缓存数据、定时器等。

基础用法(包裹动态组件):此时切换 Tab1 / Tab2,数据不会重置。

<KeepAlive> <component :is="current" /> </KeepAlive>

include(依赖组件 name):只缓存 TabOne、TabTwo,其他组件 每次切换销毁重建

<KeepAlive include="TabOne,TabTwo"> <component :is="current" /> </KeepAlive>

exclude:排除指定 name,不缓存

<KeepAlive exclude="TabTwo">

max:最大缓存实例数,超出数量后,最久没使用的组件自动销毁

<KeepAlive max="5">

子组件中缓存专属生命周期钩子(只有被 KeepAlive 缓存才触发):被缓存的组件切换时不会执行 onMounted / onUnmounted,只会走 activated / deactivated。

<script setup> import { onActivated, onDeactivated, onMounted } from "vue" // 只会首次创建执行 onMounted(() => { console.log("组件创建") }) // 每次切进来都执行 onActivated(() => { console.log("组件激活,刷新列表/重置查询条件") }) // 每次切走执行 onDeactivated(() => { console.log("组件失活,清除定时器") }) </script>

路由场景最常用(页面缓存)

<KeepAlive include="UserList,OrderPage"> <router-view /> </KeepAlive>

踩坑点:Vue Router4 内部重构了 渲染机制,路由视图是动态异步组件,不能直接作为 的子节点。
必须通过 v-slot 取出底层组件实例,再用 渲染,才能被 KeepAlive 正常捕获缓存。
官方标准插槽写法(现在必须用)

<router-view v-slot="{ Component }"> <KeepAlive include="UserList,OrderPage"> <component :is="Component" /> </KeepAlive> </router-view>

4. router

  • Vue Router 是 Vue.js 的官方路由管理器。

  • 单页应用的特点:页面跳转不会重新加载整个 HTML 页面,仅局部更新视图;

  • 作用:建立 URL 地址与页面组件之间的映射关系,根据当前 URL 渲染匹配的组件。

  • 用法

    1. 安装vue-router
      npm install vue-router@4
    2. 创建路由文件 src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router' import type { RouteRecordRaw } from 'vue-router' // 路由配置 const routes: RouteRecordRaw[] = [ {path: '/',redirect: '/home'}, {path: '/home',name: 'Home',component: () => import('@/views/Home.vue')}, {path: '/about',name: 'About',component: () => import('@/views/About.vue')} ] // 创建路由 const router = createRouter({ history: createWebHistory(), routes }) export default router
  1. main.ts中引入和注册
import router from './router' app.use(router)

就可以成功通过改变URL加载不同组件了