插槽、路由
一、插槽(solt)
在 Vue 2 中,插槽(slot) 是一种强大的功能,它允许你在父组件中定义内容并将这些内容传递给子组件的特定位置。插槽的主要目的是让你能够在组件的模板中留出占位符,以便将内容插入到这些占位符的位置。
1.1 概念
插槽是 Vue 组件中一种特性,它提供了一种将父组件的内容传递到子组件的方式。父组件可以通过 <slot></slot>
标签在子组件模板中指定插槽的位置,然后通过在父组件中传递内容来填充这些位置。
1.2 基本用法
假设我们有一个子组件 <Card>
,它包含了一个插槽来显示卡片的内容。子组件的代码如下:
<!-- Card.vue -->
<template>
<div class="card">
<h3 class="card-title">Card Title</h3>
<div class="card-content">
<slot></slot> <!-- 这是默认插槽 -->
</div>
</div>
</template>
<script>
export default {
name: 'Card'
}
</script>
父组件传递内容到子组件时,插槽中的内容会被替换为父组件提供的内容:
<!-- Parent.vue -->
<template>
<div>
<Card>
<p>This is the card content passed from the parent.</p>
</Card>
</div>
</template>
<script>
import Card from './Card.vue';
export default {
name: 'Parent',
components: {
Card
}
}
</script>
1.3 分类
1.3.1 默认插槽(Default Slot)
如上所示,当没有指定插槽名称时,使用的是默认插槽。默认插槽非常简单,父组件传入的任何内容都会插入到子组件中的 <slot></slot>
标签位置。
带有多个插槽的组件
如果你的组件需要多个插槽,你可以在子组件中定义多个插槽,并通过 `name` 属性来区分它们。
例子:
<!-- Card.vue -->
<template>
<div class="card">
<h3 class="card-title">Card Title</h3>
<div class="card-header">
<slot name="header"></slot> <!-- 带名字的插槽 -->
</div>
<div class="card-body">
<slot></slot> <!-- 默认插槽 -->
</div>
</div>
</template>
父组件传递内容时需要使用 slot
属性来指定插槽的名称:
<!-- Parent.vue -->
<template>
<div>
<Card>
<template v-slot:header>
<h4>Custom Header</h4>
</template>
<p>This is the card content passed from the parent.</p>
</Card>
</div>
</template>
1.3.2 具名插槽(Named Slots)
具名插槽是有 name
属性的插槽,可以让你在父组件中插入不同位置的内容。通过 v-slot
语法在父组件中为每个具名插槽指定内容。
语法:
<slot name="slotName"></slot>
父组件中使用具名插槽时,通过 v-slot:slotName
语法来填充具名插槽:
<!-- 父组件 -->
<Card>
<template v-slot:header>
<h4>Custom Header</h4>
</template>
<template v-slot:footer>
<p>Footer content</p>
</template>
<p>This is the body content.</p>
</Card>
1.3.3 作用域插槽(Scoped Slots)
作用域插槽(scoped slots)是 Vue 2 中提供的一个非常强大的功能,它允许父组件向插槽传递数据,这些数据可以在子组件内部进行处理,并通过作用域插槽的方式传递给父组件使用。
给solt,添加属性方式传递指定的值;
例子:
假设子组件要显示一个列表,并希望父组件根据列表的每一项来决定如何渲染它。
<!-- List.vue -->
<template>
<div>
<ul>
<li v-for="item in items" :key="item.id">
<slot :item="item"></slot> <!-- 作用域插槽 -->
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'List',
props: {
items: Array
}
}
</script>
父组件可以通过作用域插槽来访问传递的数据,并根据数据自定义渲染:
<!-- Parent.vue -->
<template>
<div>
<List :items="itemList">
<template v-slot:default="{ item }">
<p>{{ item.name }} - {{ item.price }}</p>
</template>
</List>
</div>
</template>
<script>
import List from './List.vue';
export default {
name: 'Parent',
components: {
List
},
data() {
return {
itemList: [
{ id: 1, name: 'Item 1', price: '10' },
{ id: 2, name: 'Item 2', price: '20' }
]
}
}
}
</script>
在上面的代码中,item
对象通过作用域插槽传递给父组件,父组件可以自由使用这些数据来进行自定义渲染。
父组件中使用作用域插槽
<template #default="slotProps">
<!-- slotProps 中包含子组件传递的数据 -->
<button @click="deleteItem(slotProps.id)">删除 {{ slotProps.name }}</button>
</template>
1.4 插槽的使用场景
- 自定义组件布局:父组件可以决定子组件的布局细节(例如,卡片的头部、内容和底部)。
- 灵活的组件设计:通过插槽,可以让子组件在设计时保持灵活,父组件可以根据不同的需求传递不同的内容。
- 提高组件复用性:通过插槽可以提高组件的复用性,不需要重新设计组件,而是通过插槽填充内容。
二、路由 router
Vue Router 是 Vue.js 的官方路由管理器,它允许你在单页面应用(SPA)中管理不同的页面或视图。
通过与 Vue 实例进行绑定来实现路由管理,实现前端的页面跳转、参数传递、嵌套路由等功能。
2.1 单页面应用(SPA)
单页应用程序: Single Page Application
用户访问的所有内容都加载在一个HTML页面中,
只有不同的视图或组件会根据URL变化进行更新,给用户带来更加流畅的交互体验。
单页面应用(SPA) 是一种通过动态加载内容来代替传统页面加载的技术,其特点是页面内容会在不重新加载整个页面的情况下动态更新。
Vue2中的SPA特点
组件化开发:Vue2中通过组件化来管理应用程序的各个部分。每个视图、模块或者功能通常被封装成一个组件,方便代码的复用和维护。
虚拟DOM:Vue2使用虚拟DOM来提高性能。每当数据变化时,Vue会先在虚拟DOM中进行比较,从而减少页面重新渲染的次数,提高响应速度。
路由管理:通过
vue-router
进行页面的路由管理,vue-router
能够实现URL的动态变化,使得单页面应用能够根据用户操作展现不同的内容和视图。状态管理:Vue2通常结合
Vuex
来进行全局状态管理,Vuex
通过集中式存储来管理所有组件的状态,避免了在多个组件间直接传递数据的问题。异步数据加载:SPA中常常使用Ajax或者Fetch来实现异步数据加载,当用户访问新的视图时,通过API请求加载相应的数据,而不是重新加载整个页面。
SPA与MPA(多页面应用)对比
特性 | 单页面应用(SPA) | 多页面应用(MPA) |
---|---|---|
页面加载 | 只有一个HTML页面,动态加载内容 | 每次请求都会加载一个新的HTML页面 |
路由 | 通过前端路由(如vue-router)控制页面的切换 | 每个页面都有独立的URL,刷新时重新加载 |
性能 | 初次加载时较慢,但后续交互速度快(无需重载页面) | 每次页面切换都需要重新加载整个页面 |
开发模式 | 组件化开发,前端逻辑和视图高度集成 | 页面之间通常是独立开发,前后端分离较多 |
用户体验 | 流畅的用户体验,没有明显的页面跳转感 | 用户每次点击链接都会看到明显的跳转和页面刷新 |
SEO | SEO需要额外配置(如使用服务端渲染或预渲染) | 每个页面都有独立的HTML,默认支持SEO |
技术栈 | 主要是前端技术(如Vue、React等) | 通常是前端与后端的完整开发栈(如PHP、Java、ASP.NET等) |
开发复杂度 | 对前端开发要求较高,需要处理路由、状态管理等 | 前端和后端开发相对独立,开发难度较低 |
状态管理 | 通常使用Vuex集中管理状态 | 状态管理通常通过后端处理 |
维护性 | 由于组件化,较易维护和重用代码 | 由于页面独立,维护较为复杂 |
2.2 路由
在 Vue 中,路由(vue-router)的核心功能是路径(URL)和组件之间的映射关系。通过路由配置,Vue 可以根据浏览器的 URL 路径,动态加载和渲染不同的组件,从而实现单页面应用(SPA)的页面切换。
2.2.1 使用过程
(1)安装 VueRouter模块
npm install vue-router
(2)引入并注册使用
import VueRouter from 'vue-router'
Vue.use(VueRouter)
- (3)配置路由
路由的核心是将 URL 路径映射到组件上。我们通过创建一个路由表(routes)来实现这个映射。
// 配置路由
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
// 创建路由实例
const router = new VueRouter({
routes // 等价于 routes: routes
})
- (4)在 Vue 实例中使用路由
new Vue({
el: '#app',
router, // 注入路由
render: h => h(App)
})
在 App.vue 中,我们通常使用 <router-view></router-view>
标签来显示匹配到的组件:
<template>
<div id="app">
<h1>Vue Router 示例</h1>
<nav>
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
</nav>
<router-view></router-view> <!-- 渲染匹配的路由组件 -->
</div>
</template>
- (5)路由链接与导航
router-link
:用于生成页面跳转的链接,它自动处理<a>
标签的行为,支持to
属性来定义跳转的路径。
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
router-view
:这是路由占位符,用于显示当前匹配的组件。
2.2.2 组件.vue 和 页面.vue对比
1. 组件分类概述
- 页面组件(Page Components):这些组件通常直接对应一个页面或视图,负责处理整个页面的内容和布局。它们通常较大,包含多个子组件,并且与路由路径密切相关。
- 复用组件(Reusable Components):这些组件通常是可复用的、较小的 UI 组件,提供特定的功能或视觉展示。例如,按钮、卡片、列表等组件,它们通常不直接与路由相关,而是可以在多个页面组件中重复使用。
2. 推荐的组件目录结构
为了清晰地管理和维护组件,通常会按页面组件和复用组件将 .vue
文件放置在不同的目录下。
src/
├── assets/ # 静态资源(图片、字体等)
├── components/ # 复用组件(UI 组件)
│ ├── Button.vue # 按钮组件
│ ├── Card.vue # 卡片组件
│ └── Modal.vue # 弹窗组件
├── views/ # 页面组件(每个视图对应一个路由)
│ ├── Home.vue # 首页组件
│ ├── About.vue # 关于页面组件
│ └── Profile.vue # 个人资料页面组件
├── router/ # 路由配置
│ └── index.js # 路由定义
└── App.vue # 根组件
3. 详细说明
a. 页面组件 (Page Components)
页面组件通常是与特定路由路径直接相关的 Vue 组件,代表单个页面。它们可能由多个复用组件组成,主要功能是呈现和管理页面的布局、数据、交互等。
- 路径映射:页面组件通常对应一个路由路径,例如
/home
、/about
等。 - 大小:页面组件通常较大,因为它们包含了多个子组件,负责协调页面内不同部分的内容和交互。
- 文件位置:页面组件放置在
src/views/
目录下,这个目录通常包含了项目中的所有页面。
// src/views/Home.vue
<template>
<div>
<h1>Welcome to Home Page</h1>
<MyCard /> <!-- 引入复用组件 -->
</div>
</template>
<script>
import MyCard from '@/components/Card.vue'
export default {
components: {
MyCard
}
}
</script>
b. 复用组件 (Reusable Components)
复用组件是小而独立的功能模块,通常用于在多个页面或地方复用。它们应当具有高度的可重用性和较低的耦合度。
- 功能:复用组件通常处理页面的一部分 UI 展示或特定功能,例如按钮、输入框、卡片、模态框等。
- 文件位置:这些组件通常放置在
src/components/
目录下。通过这种方式,它们可以很容易地被不同的页面组件引用。
// src/components/Card.vue
<template>
<div class="card">
<h2>{{ title }}</h2>
<p>{{ description }}</p>
</div>
</template>
<script>
export default {
props: {
title: String,
description: String
}
}
</script>
在页面组件中引用复用组件:
// src/views/Home.vue
<template>
<div>
<h1>Home Page</h1>
<Card title="Card Title" description="This is a reusable card" />
</div>
</template>
<script>
import Card from '@/components/Card.vue'
export default {
components: {
Card
}
}
</script>
c. 路由与页面组件的关系
在 Vue Router 中,页面组件是通过路径与 URL 映射的,因此页面组件通常与路由配置紧密关联。你可以在 router/index.js
文件中定义路由:
// src/router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
Vue.use(VueRouter)
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
const router = new VueRouter({
routes
})
export default router
4. 总结
- 页面组件:负责页面级别的布局和数据处理,通常对应一个路由,放在
views/
目录下。 - 复用组件:小型的、功能独立的组件,能够在多个页面中复用,放在
components/
目录下,可以根据功能细分为多个子目录。
2.3 路由模块
2.3.1 路由链接与导航
router-link
:用于生成页面跳转的链接,它自动处理<a>
标签的行为,支持to
属性来定义跳转的路径。
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
router-view
:这是路由占位符,用于显示当前匹配的组件。
router-link-active
:这是路由模糊匹配时,router-link
会添加的类名。
router-link-exact-active
:这是路由精确匹配时添加的类名。
2.3.2 路由参数
路由参数有两种:路径参数和查询参数。
a.路径参数
路径参数通过 :param
方式在路由路径中指定。例如:
const routes = [
{ path: '/user/:id', component: User }
]
配置导航链接to="/path/参数值"
在组件中,你可以通过 this.$route.params
获取路径参数:
export default {
computed: {
userId() {
return this.$route.params.id
}
}
}
- 路径参数:适用于表示资源的唯一标识符或主要信息。例如,用户的个人页面、文章详情页等。这类信息是页面内容的核心部分,通常是必需的。
- 示例:
to="/user/:id"
、/product/:productId
b.查询参数
查询参数使用 URL 的 ?
后跟 key=value
方式传递,例如 /user?id=123
.
你可以通过 this.$route.query
来访问查询参数:
this.$route.query.id // 获取查询参数 id
- 查询参数:适用于传递不影响路由匹配的额外信息,例如排序、过滤、分页等。这些信息通常不是必需的,而是用来改变页面内容的展示方式。
- 示例:
to="/search?q=vue&page=2"
、/products?category=electronics&sort=price
c.区别
路径参数:用于必须的、重要的信息,通常在 URL 中使用
:
来定义,访问时通过this.$route.params
获取。查询参数:用于附加的、可选的信息,通常通过 URL 的查询字符串传递,访问时通过
this.$route.query
获取。
特性 | 路径参数 | 查询参数 |
---|---|---|
定义位置 | 路由路径的一部分(通过 : 定义) |
URL 中的查询字符串(?key=value ) |
必选性 | 通常是必选的 | 可选的 |
URL 结构 | 更简洁、更语义化(如 /user/123 ) |
适用于附加额外信息(如 /search?q=vue ) |
参数顺序 | 顺序固定 | 顺序不固定,可以自由组合 |
访问方式 | 通过 this.$route.params 获取 |
通过 this.$route.query 获取 |
典型使用场景 | 资源标识(如用户、文章等) | 过滤、搜索、分页等 |
影响路由匹配 | 路由匹配时参数必须存在 | 查询参数不会影响路由匹配,只影响组件的渲染 |
2.3.3 在 Vue Router 中,路由重定向、路由 404 处理 和 路由模式 是常见的功能和配置选项。下面详细介绍这几个概念。
1. 路由重定向(Redirect)
路由重定向用于将访问某个路径的请求自动跳转到另一个路径,通常用于处理 URL 改变或某些路径的默认跳转。
配置重定向
你可以通过 redirect
配置选项来实现路由重定向
示例:
静态重定向(直接指定目标路径):
// 如果访问 `/home`,自动重定向到 `/`: const routes = [ { path: '/home', redirect: '/' } ]
动态重定向(可以根据条件动态决定跳转到哪里):
const routes = [ { path: '/old-home', redirect: to => { // 可以根据 to 对象中的信息进行动态重定向 if (to.query.redirectTo) { return to.query.redirectTo } return '/' } } ]
默认路由重定向
当用户访问根路径 /
时,通常我们会配置一个默认路由进行重定向:
const routes = [
{ path: '/', redirect: '/home' },
{ path: '/home', component: Home },
{ path: '/about', component: About }
]
2. 路由 404 处理
路由 404 处理是当用户访问一个不存在的路径时,跳转到一个错误页面或者其他合适的页面。通常,这种情况会在所有其他路由之后进行配置。
配置 404 路由
*
是一个通配符路径,表示匹配任何没有明确配置的路径。通常将 404 页面作为路由的最后一个配置项。
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
// 404 页面路由
{ path: '*', component: NotFound }
]
在上面的例子中,如果用户访问 /unknown-path
这样的路径,路由会自动匹配到 NotFound
页中。
例如
如果项目大型的时候,我们可以给用户一种友好的提示信息告知其在错误等页面中,并随之接下来就是的动态的倒计时告知其倒计时重定向跳转页面
或者 返回首页或热门链接 、搜索功能 、个性化推荐:如果可以追踪用户行为或用户历史
<template>
<div>
<h2>页面不存在</h2>
<p>您访问的页面不存在,请检查网址是否正确。</p>
</div>
</template>
<script>
export default {
name: 'NotFound'
}
</script>
3. 路由模式
Vue Router 提供了两种路由模式:哈希模式和历史模式。
哈希模式(默认模式)
在哈希模式下,URL 的路径会带有一个 #
符号。
http://example.com/#/home
这种模式的优点是兼容性好,特别是对于老旧的浏览器,且不需要服务器的额外配置,因为 #
后面的路径不会发送到服务器。只会在浏览器的前端处理。
在 Vue Router 中,哈希模式是默认模式,不需要特别配置:
const router = new VueRouter({
routes, // 路由表
mode: 'hash' // 默认为 hash
})
历史模式(HTML5 History API)
在历史模式下,URL 的路径不会包含 #
,而是直接显示路径。
http://example.com/home
这更符合现代网页的表现方式,路径看起来更干净,也便于 SEO。但使用历史模式时,要求服务器进行一些配置,否则刷新页面时会导致 404 错误。
在 Vue Router 中使用历史模式时,需要进行一些配置来确保服务器能够正确处理路径:
const router = new VueRouter({
routes, // 路由表
mode: 'history' // 使用 history 模式
})
Nginx服务器配置(历史模式)
当你使用历史模式时,需要确保你的服务器能够处理所有的路径,并将它们指向 index.html
,这样 Vue Router 才能接管路由控制。
不配置:为刷新时时通过 nginx 去访问静态资源的,明显这个路径是找不到,因为这个只是前端路由。
问题原因:
前端路由和 Nginx 配置冲突:前端路由(如 /h5/about)是由 JavaScript 控制的,它不会重新加载页面,而是通过浏览器控制路径的变化。
但是当你刷新页面,浏览器向服务器发送了一个 GET 请求,期望从服务器获取 /h5/about 路径的资源。
而如果 Nginx 没有正确配置,它会去文件系统中查找这个路径,结果找不到,导致返回 404 错误。
server {
location / {
try_files $uri $uri/ /index.html;
}
}
总结
- 路由重定向:通过
redirect
配置项可以实现路径跳转,支持静态和动态重定向。 - 路由 404 处理:使用
*
路径来匹配未定义的路径,通常设置为最后一个路由进行 404 页面跳转。 - 路由模式:Vue Router 提供哈希模式和历史模式两种选择。哈希模式使用
#
来保持路径,而历史模式提供更干净的 URL,但需要服务器进行额外配置。
这些配置使得 Vue Router 能够更加灵活地应对各种场景,帮助你更好地管理 SPA 应用的路由。
2.4 组件缓存 keep-alive
keep-alive
是一种常用于 Vue.js 和其他前端框架中的缓存策略,用于在组件之间切换时保持组件的状态,而不是每次切换时重新渲染。
1. Vue.js 中的 keep-alive
组件缓存
在 Vue.js 中,<keep-alive>
组件可以包裹需要缓存的组件。这样,当你切换不同的组件时,被包裹的组件不会被销毁,而是被保留在内存中,避免重新渲染,提升性能。
(1)常用于以下场景来提升性能:
- 切换页面时缓存内容:比如在多页面的单页应用(SPA)中,使用
keep-alive
来缓存切换的页面,避免重复加载。 - 减少不必要的重新渲染:避免组件因切换而重新渲染,从而节省性能开销。
<keep-alive>
<MyComponent v-if="isComponentVisible" />
</keep-alive>
(2) 工作原理
keep-alive
的工作原理是,在组件被隐藏时,它会保持组件的状态和生命周期。这样,当组件重新显示时,状态能够保留,避免重新加载、重新渲染,提升性能。
- 缓存:组件在
keep-alive
内部会被缓存,状态不被丢失。 - 销毁与复用:当组件切换时,Vue 会通过保存其内部状态来复用组件,而不是销毁后重新创建。
(3)主要属性:
include
:一个字符串或正则表达式,用来匹配要缓存的组件。exclude
:一个字符串或正则表达式,用来匹配不缓存的组件。max
:一个数字,表示最多缓存的组件实例数量。如果超过最大数,最先缓存的组件会被销毁。
<keep-alive :include="['MyComponent', 'AnotherComponent']">
<MyComponent v-if="isComponentVisible" />
<AnotherComponent v-if="isAnotherComponentVisible" />
</keep-alive>
include
和exclude
可以是字符串、正则或数组,用于控制哪些组件会被缓存。max
属性可以限制缓存的最大数量,如果组件超过缓存数量,Vue 会销毁最早缓存的组件。
<keep-alive :max="10">
<MyComponent />
</keep-alive>
2. 生命周期钩子
activated
:组件重新激活时调用。deactivated
:组件被缓存时调用(即从视图中移除时)。
示例:
export default {
data() {
return {
isComponentVisible: true,
};
},
activated() {
console.log('组件被激活');
},
deactivated() {
console.log('组件被缓存');
},
};
3. 与 Vue Router 配合
在 Vue Router 中,keep-alive
常与路由切换一起使用,缓存切换的路由组件。
<keep-alive>
<router-view></router-view>
</keep-alive>
结合 Vue Router 的 meta
配置,控制哪些路由组件需要缓存:
const routes = [
{
path: '/home',
component: Home,
meta: { keepAlive: true }, // 需要缓存
}
];
const router = new VueRouter({
routes,
});
4. 何时使用:
注意事项
- 内存占用:
keep-alive
会增加内存占用,因为缓存的组件实例会一直保留在内存中。所以需要合理使用,避免不必要的缓存。- 状态管理:对于需要缓存的组件,需要注意状态的管理和数据同步,避免旧数据残留。
- 适用于那些需要在不同状态之间切换的场景,如 tab 切换、页面切换、动态加载组件等。
- 在需要提高性能,减少不必要的重新渲染时非常有用。
keep-alive
在 Vue.js 中是一种非常有用的缓存策略,可以有效减少组件的重渲染,提升应用性能~ 需要注意的是,keep-alive
只对 Vue 组件有效,对于 HTML 元素或普通的 DOM 元素并不起作用哦~ (๑˃̵ᴗ˂̵)
2.5 嵌套路由
Vue Router 支持嵌套路由,可以将一个路由的组件作为子路由加载。父组件会包含一个 <router-view>
用来显示子路由的内容。
例如,父路由 /user
下有多个子路由:
const routes = [
{
path: '/user',
component: User,
children: [
{
path: 'page1',
component: UserPage1
},
{
path: 'page2',
component: UserPage2
}
]
}
]
在 User
组件中,你可以使用一个 router-view
来显示子组件:
<template>
<div>
<h2>User Page</h2>
<router-view></router-view> <!-- 渲染子路由 -->
</div>
</template>
2.6 路由守卫
在 Vue 2 中,路由守卫(Route Guards)用于在路由跳转之前或之后执行一些操作,常见的用途包括权限验证、页面加载、数据预处理等。
Vue Router 提供了多种类型的路由守卫,分为全局守卫、路由独享守卫和组件内守卫。
1. 全局守卫
全局守卫是指作用于整个路由导航过程的守卫
- beforeEach:在每次路由跳转之前触发
- beforeResolve:在路由解析完成后、导航确认之前触发
- afterEach:每次路由跳转完成之后触发
示例代码:
// 创建 Vue Router 实例
import VueRouter from 'vue-router';
import Vue from 'vue';
import Home from './components/Home.vue';
import About from './components/About.vue';
Vue.use(VueRouter);
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About }
];
const router = new VueRouter({
routes
});
// 全局前置守卫 - beforeEach
router.beforeEach((to, from, next) => {
console.log('beforeEach守卫');
// 如果需要权限验证,可以在这里判断是否有权限访问
if (to.path === '/about') {
// 假设需要用户已登录才能访问
const isAuthenticated = false; // 示例:假设用户未登录
if (isAuthenticated) {
next();
} else {
next('/'); // 重定向到首页
}
} else {
next();
}
});
// 全局后置守卫 - afterEach
router.afterEach((to, from) => {
console.log('afterEach守卫');
// 比如记录页面访问日志
});
// 路由实例挂载到 Vue 实例
new Vue({
router,
render: h => h(App)
}).$mount('#app');
2. 路由独享守卫
路由独享守卫是直接在路由配置对象上定义的守卫,它的执行仅限于特定的路由。
- beforeEnter:进入该路由之前的守卫
示例代码:
const routes = [
{
path: '/about',
component: About,
beforeEnter: (to, from, next) => {
console.log('beforeEnter守卫');
// 如果用户未登录,重定向到首页
const isAuthenticated = false; // 示例:假设用户未登录
if (isAuthenticated) {
next();
} else {
next('/'); // 重定向到首页
}
}
}
];
3. 组件内守卫
组件内守卫用于在路由跳转时进入组件或离开组件时执行的操作。它们只能在组件的生命周期内使用。
- beforeRouteEnter:进入该路由时触发
- beforeRouteUpdate:当前路由改变时触发(该守卫只在同一个组件内,路由参数改变时触发)
- beforeRouteLeave:离开该路由时触发
示例代码:
export default {
name: 'MyComponent',
beforeRouteEnter(to, from, next) {
console.log('beforeRouteEnter守卫');
next();
},
beforeRouteUpdate(to, from, next) {
console.log('beforeRouteUpdate守卫');
next();
},
beforeRouteLeave(to, from, next) {
console.log('beforeRouteLeave守卫');
next();
}
};
4. 路由守卫的 next()
方法
路由守卫中的 next()
方法决定了导航的流程:
next()
:表示放行,允许导航next(false)
:表示取消导航next('/')
:表示重定向到指定路由
小结
- 全局守卫:用于控制整个应用的路由行为。
- 路由独享守卫:用于针对特定路由的守卫d
- 组件内守卫:用于在组件内部控制进入和离开时的逻辑。
2.7 路由懒加载
为了提升性能,你可以使用路由懒加载,按需加载组件。通过 webpack
的代码拆分,路由组件会在用户访问时动态加载:
const User = () => import('./components/User.vue')
const routes = [
{ path: '/user', component: User }
]
这样,User
组件会被延迟加载,直到用户访问 /user
路径时才会被加载。
2.8 数据传递的方式
你提到的场景是:一个页面组件通过传递数据到其他页面组件的复用组件,而这种传值过程确实可能比较繁琐,尤其是当页面层级较深或组件之间的关系复杂时。
在 Vue 中,数据传递的方式主要有两种:
- 父子组件间通过
props
和emit
传值 - 跨组件(无直接父子关系)使用 Vuex 或事件总线(Event Bus)进行数据传递
1. 父子组件间通过 props
和 emit
传值
当你在一个父页面组件中通过 props 向子组件传递数据时,父组件与子组件的通信是直接的。但问题来了,如果传值需要跨越多个页面或者多个层级,那么中间的每个父组件都需要接收并传递这个数据,这就会导致数据传递过程非常繁琐。
示例:多层级的 props
传递
假设你有一个页面组件 PageA.vue
,它需要将数据传递给 PageB.vue
中的 Card.vue
组件。为了实现这个传递,PageA
需要将数据传递给它的子组件(假设是 PageB.vue
),而 PageB.vue
又需要将数据传递给它的子组件(假设是 Card.vue
):
<!-- PageA.vue -->
<template>
<div>
<PageB :data="pageData" />
</div>
</template>
<script>
import PageB from './PageB.vue'
export default {
components: {
PageB
},
data() {
return {
pageData: 'Hello from PageA'
}
}
}
</script>
<!-- PageB.vue -->
<template>
<div>
<Card :data="data" />
</div>
</template>
<script>
import Card from './Card.vue'
export default {
components: {
Card
},
props: {
data: String
}
}
</script>
<!-- Card.vue -->
<template>
<div>
<p>{{ data }}</p>
</div>
</template>
<script>
export default {
props: {
data: String
}
}
</script>
这种方式在页面或组件层级较深时,会导致每个父组件都需要添加 props
,并且不断将数据向下传递,导致代码冗长且难以维护。
2. 使用 Vuex (状态管理) 进行跨页面/组件的数据传递
为了解决这类繁琐的问题,Vue 推荐使用 Vuex 来进行全局状态管理,特别是在数据需要在多个页面或组件之间共享时。Vuex 可以将数据存储在一个集中式的 store 中,组件可以直接从 Vuex 中获取或修改数据,而不需要经过一层一层的传递。
示例:使用 Vuex 进行状态管理
安装并配置 Vuex:
首先,需要安装并配置 Vuex:
npm install vuex
创建 Vuex store:
在
src/store/index.js
中创建 store:import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { pageData: 'Hello from Vuex' }, mutations: { updatePageData(state, newData) { state.pageData = newData } }, actions: { updatePageData({ commit }, newData) { commit('updatePageData', newData) } } })
在组件中使用 Vuex:
现在,你可以直接在任何页面组件中使用 Vuex 中的数据,而不需要通过层层的
props
传递。PageA.vue:
<template> <div> <PageB /> </div> </template> <script> import PageB from './PageB.vue' export default { components: { PageB } } </script>
PageB.vue(从 Vuex 获取数据并传递给复用组件
Card.vue
):<template> <div> <Card :data="pageData" /> </div> </template> <script> import { mapState } from 'vuex' import Card from './Card.vue' export default { components: { Card }, computed: { ...mapState(['pageData']) // 直接从 Vuex 获取数据 } } </script>
Card.vue(展示从 Vuex 获取的
data
):<template> <div> <p>{{ data }}</p> </div> </template> <script> export default { props: { data: String } } </script>
全局共享状态:
使用 Vuex 后,你可以在任何页面或组件中通过
mapState
或this.$store.state
直接访问或修改 Vuex 中的数据,而无需传递props
。如果你需要修改数据,只需通过 mutations 或 actions 来进行修改,不必通过层层传递。
3. 事件总线(Event Bus)
虽然 Vuex 是管理全局状态的标准方式,但对于某些场景(例如跨越页面的简单数据传递),也可以使用 事件总线。通过事件总线,可以实现组件间的即时通信,适合在非父子组件间传递数据。
示例:使用事件总线传递数据
创建事件总线(Event Bus):
创建一个简单的事件总线,在
src/event-bus.js
文件中:import Vue from 'vue' export const EventBus = new Vue()
在发送数据的组件中触发事件:
// 在 PageA.vue 中触发事件 import { EventBus } from '@/event-bus' export default { mounted() { EventBus.$emit('data-from-pageA', 'Hello from PageA') } }
在接收数据的组件中监听事件:
// 在 Card.vue 中接收数据 import { EventBus } from '@/event-bus' export default { data() { return { receivedData: '' } }, created() { EventBus.$on('data-from-pageA', (data) => { this.receivedData = data }) }, beforeDestroy() { EventBus.$off('data-from-pageA') // 组件销毁时移除监听 } }
4. 总结
如果你在 Vue 项目中需要传递数据到其他页面的复用组件,可以选择以下方法:
- 使用
props
和emit
进行父子组件通信:适用于父子组件间的数据传递,但对于层级较深的组件会比较繁琐。 - 使用 Vuex 管理全局状态:适用于多个组件之间共享数据,不需要一层一层传递。
- 使用事件总线(Event Bus):适用于跨组件(无直接父子关系)传递简单数据。
对于复杂项目,通常推荐使用 Vuex 来集中管理状态和数据,以减少跨组件传值的复杂性。