vue3通信大全(二)—— 路由传参与 teleport

3,727 阅读4分钟

vue3通信大全(一)—— 组件通信与provide - 掘金 (juejin.cn)

vue3通信大全(三)—— 全局状态管理库pinia,vuex - 掘金 (juejin.cn)

vue3通信大全(四)—— Slot /浏览器储存/window - 掘金 (juejin.cn)

前言

在 Vue 3 中,除了传统的组件间通信方式(如 props 下传、事件上冒泡、Vuex 等),我们还可以利用路由来实现不同组件间的通信。下面我将详细介绍如何在 Vue 3 中使用路由传参以及一些相关的实践。

Params作为参数

Params 参数是通过路径的一部分来传递的,通常用于表示动态的部分,例如 /users/:userId 中的 :userId 就是一个 param 参数。

使用方法:定义路由:

在 Vue Router 中定义带有 params 的路由非常简单,只需要在路径中使用占位符即可:

index.js文件中:

import { createWebHistory,createRouter } from "vue-router";
const routes = [{
    path: "/parent",
    name: "parent", 
    component: () => import("@/components/demo2/parent.vue"),
  },
  {
    path: "/child/:id", //传递的参数
    name: "child", // 命名路由
    component: () => import("@/components/demo2/child.vue"),
  }];
const router = createRouter({
  history: createWebHistory(),
  routes,
});
export default router;

当前页:

<template>
  <h1>
    父组件
  </h1>
<button @click="goSon">点击跳转</button>  
</template>

<script setup>
import {useRouter} from 'vue-router'
const router = useRouter()
const goSon=()=>{
  router.push({name:'child',params:{id: '123123'}})
}
</script>

目标页:

<template>
son: route.params = {{ route.params.id }}
</template>

<script setup>
import {useRoute}from 'vue-router' 
const route = useRoute()    //使用useRoute获取当前页面信息
</script>

当用户访问类似 /child/123 这样的 URL 时,:idd 的值会被自动提取出来,并且可以在这个组件中通过 route.params.id 获取到。而当我们如果需要从一个组件导航到另一个带有参数的路由,可以使用 router.push 方法,并且提供参数的值,从而实现传值。

Query 参数

Query 参数则是通过 URL 中的查询字符串来传递的,通常用于过滤、排序或者传递额外的信息,例如 /users?name=John 中的 name=John 就是一个 query 参数。

index文件:

import { createWebHistory,createRouter } from "vue-router";
const routes = [{
    path: "/parent",
    component: () => import("@/components/demo2/parent.vue"),
  },
  {
    path: "/child",   //query传值不需要使用name和修改path格式
    component: () => import("@/components/demo2/child.vue"),
  }];
const router = createRouter({
  history: createWebHistory(),
  routes,
});
export default router;

当前页:

 <template>
  <h1>
    父组件
  </h1>
<button @click="goSon">点击跳转</button>  
</template>

<script setup>
import {useRouter} from 'vue-router'
const router = useRouter()
const goSon=()=>{
  router.push({path:'/child',query:{id: '123123'}})
}
</script>

目标页:

<template>
son: route.query.id = {{ route.query.id }}
</template>

<script setup>
import {useRoute}from 'vue-router'
const route = useRoute()
</script>

使用query传参时,不需要我们在路由文件中修改目标页面的path路径而格式,而是在当前页面使用router.push(path:'xxx',query:{键名:键值}),在跳转到目标页面后,目标页面的url会在后面加上xxx?键名=键值。而在目标页面要取到这个值,也是使用useRoute获取到当前页面信息使用route.query.id得到对应的query。

teleport

使用 Teleport 组件的主要目的是为了能够将一个组件渲染到 DOM 树中的另一个位置,而不是直接在其父组件的 DOM 子树内。这在处理模态框、弹窗等需要跨越多个组件层级的 UI 元素时特别有用。

非常好的例子就是登录弹窗的动态使用Teleport来实现的,如果我们使用的是登入弹窗,那么不论在哪个页面,这个弹窗框的层级应该是最高的,同时应该脱离文档流不影响其下面的结构。我们在HTML中会使用Z-index属性,但是在vue组件化结构中是不生效的,因此Teleport便孕育而生。

index.html文件:

<div id="app">
    <!-- 应用的主要内容 -->
    <App></App>
  </div>

  <!-- Teleport 的目标容器 -->
  <div id="login-container"></div>

App.vue文件:

<template>
  <div>
    <button @click="showLogin = true">Login</button>
    这是之外的数据
    <teleport v-if="showLogin" to="#login-container">
      <div class="login-modal">
        <h1>登入</h1>
        <input type="text" placeholder="Username" v-model="username">
        <input type="password" placeholder="Password" v-model="password">
        <button @click="submitLogin">提交</button>
      </div>
    </teleport>
  </div>
</template>

<script setup>
import{ref}from 'vue'
     const showLogin=ref(false)
     const username=ref('')
     const password=ref('')
  const submitLogin=()=>{
    //登入逻辑
      showLogin.value = false; // Close the modal
    }
</script>

这里我们明显看到登入框弹出时,taleport并没有挤压其他的DOM结构。

为什么需要在#app内再放一个<App />

  • 分离关注点:
    • <App> 元素允许我们在 HTML 文件中定义应用程序的主要内容区域,而 main.js 中的 app.mount('#app') 则负责将 Vue 组件挂载到这个区域。
  • 保持 HTML 结构清晰:
    • 通过在 HTML 中定义 <App>,我们可以保留 HTML 文件的基本结构,使其易于阅读和维护。

换句话说我们也可以在#app内再放一个<App />但是这样做使得我们的登入的DOM结构不再清晰,这时不被优秀的程序设计所接受的。