Vue框架


Vue3路由基础— vue router


前面已经介绍过了组件的部分,包括动态组件、插槽slot等,其中的一个关键就是数据的传递,要么通过自定义事件,要么通过ref引用,还有一个重要的就是全局注册axios,在组件的声明周期函数created接收页面的初始数据;自定义指令也可以简化一些固定的操作;作用域插槽也可以向使用者传递数据

简单了解了vue中的组件使用之后,接下来就是vue-router路由的分享了

前端路由

路由就是对应关系;【后端中Servlet的路径绑定】,路由分为前端路由和后端路由;后端路由指的是: 请求方式,请求地址和function处理函数之间的对应关系

在node.js中,express路由的使用例子

const express = require('express') //这里使用的时require的语法导入
//创建一个router实例
const router = express.Router()

//绑定
router.get('地址',function(){})
router.post('路径',funtion(){})

//导出router【没有ES6]
module.exports = router

相对应的,之前提过SPA指的是单页面应用程序;所有的组件的展示和切换都是在者唯一一个页面中完成,这个时候,在SPA项目中,不同的组件的切换需要通过前端路由来实现

前端路由就是 Hash地址与组件之间的对应关系; 比如一个简单的手机应用页面,点击下面的图标切换不同的内容,这就设计到组件的切换,需要使用路由【地址栏中以#开始的就是Hash地址】

前端路由的工作方式

  • 用户点击了页面上的路由链接【就是各种图标按钮】
  • 导致URL地址栏的Hash值发生了拜年话
  • 前端路由监听Hash地址的变化
  • 前端路由把当前Hash地址对应的组件渲染到浏览器中【Hash地址对应绑的是组件】
//除了#,还有其他的方式,比如通过参数直接从后端获取不同的页面
https://blog.csdn.net/a23452?type=lately ;比如CSDN这里就是通过type参数的不同值获取个人页面的不同页面

在这里插入图片描述

可以看到这里通过#/home绑定了home组件,当地址栏的地址变化,就会自动调用组件

实现简易的前端路由 created中window.onhashchange

  • 这里就以之前的vueTest3来实现一下:
export default {
  name: 'App',
  components: {
	LifeCircle,
	CosumKid,
	SlotTest,
	MyHome
  },
  • 之前分享动态组建的时候就提过,使用keep-alive避免销毁,同时component标签的is属性动态绑定名称;这里路由的实现就是依赖的component动态组件
<component :is='conName'></component>
  • 在组件的结构中声明了几个a超链接,通过点击超链接,切换不同Hash值
<a href="#/lifeCircle">LfCir</a>&nbsp;<a href="#/counsumeKid">CosuKid</a>

这里就是一个相对路径,这是资源路径,就是当前的URL加上后面的部分就是绝对路径

  • 在created生命周期函数中监听浏览器地址栏中Hash地址的变化,动态切换要展示的组件的名称
  created() {
  	window.onhashchange = () => {
		switch(location.hash) {
			case "#/lifeCircle":
				this.conName = 'LifeCircle'
				break
			case "#/counsumeKid":
				this.conName = 'CosumKid'
		}
	}
  }

所以前端路由就是基于动态组件加上Hash实现的,这里需要在Created函数中监听,使用的就是weindow.onhashchange,同时获取地址栏的Hash就是通过location.hash 【这里是原生的js的使劲按处理,hashchange就是一个地址的change事件,为onhanshchange属性绑定事件处理函数】

vue-router基本使用 router-link的hash地址不需要#

vue-router是vue.js给出的官方的路由解决方案,只能结合vue项目使用,简单管理SPA项目的组件的切换

【vue-router3.x只能结合vue2使用,vue4.x只能结合vue3使用】

  • 在项目中安装vue-router4

使用命令npm i vue-router@next -S 【axios和vue-router都-S】 可以在package.json中查看版本号: “vue-router”: “^4.0.13”

  • 定义路由的组件

就是定义几个需要切换的组件,这里就是用上面的例子中的LifeCirecle等组件

  • 声明路由衔接和占位符 router-link标签的to属性绑定路径router-view直接放上就可以声明占位符

vue-router中就不建议再使用普通的超链接标签a,而是使用< router-link>标签来声明路由链接,【这里会自动帮助渲染一个#】

并且使用《 router-view>声明路由占位符【这两个标签都是router提供的】,这里就相当于是组件的填充的位置,和之前的手动的component的功能相同,并且不需要手动进行绑定了

  <router-link to="/lifeCircle">LifCir</router-link>&nbsp;
  <router-link to="/counsumeKid">ConKis</router-link>


  <router-view></router-view>
  • 创建路由模块; 创建路由模块需要按需导入并且需要有一个实例的路由对象

    • 从vue-router按需导入两个方法
    • 导入需要使用路由控制的组件
    • 创建路由的实例对象
    • 向外共享实例对象
    • 再main.js导入挂载路由模块 【注意要导入组件,不然无法使用】 ==通过spa_app.use(router)==挂载 还有注意路由规则数组名称为routes,不是routers:happy: ----- 写错了就报错property undefined
    /*1.从vue-router中按需导入两个方法: 
     createRouter  创建路由的实例对象
     createWebHashHistory 指定路由的工作模式为hash模式
    */
    import {createRouter,createWebHashHistory} from 'vue-router'
    import LifeCircle from './components/life-circle.vue'
    import CosumKid from './components/consum-kid.vue'
    
    //创建路由的实例对象
    const router = createRouter({
    	//通过history属性指定路由的工作模式
    	history: createWebHashHistory(),
    	//通过routers数组,指定路由的规则【也就是进行组件和路径的绑定】
    	routes:[
    		//path代表的是hash地址,component为对应的组件
    		{path:'/lifeCircle',component:LifeCircle},
    		{path: '/counsumeKid',componet:CosumKid}
    	]
    })
    
    //将路由实例对象导出
    export default router
    
    
    -----------------在main.js中--------------------------
    import router from './router.js'
    
    //挂载router
    spa_app.use(router)
    
  • 导入并挂载路由模块

所以使用vue-router的关键: 首先就是使用router-link声明链接【用法和a一样,to属性,并且不需要#】,然后使用router-view占位【用处和component一样】,之后就是创建路由模块router.js — 主要就是创建一个router实例对象【对象中声明路由规则和路由模式】挂载到main.js中即可,use方法

vue-router的高级用法

路由重定向

【后端servlet的重定向和请求转发就是一次请求,访问多个,请求转发是内部,重定向相当于重新发起一次get请求】,路由重定向: 用户在访问地址A的时候,强制让用户跳转到地址C,从而展示特定的组件页面 — 直接通过路由规则的redirect属性,就可以指定路由地址,实现重定向

//routes中放的就是路由规则,每一个对象中,path是Hash地址【自动渲染#】,component是组件名
和这两个结点平级的就是redirect, redirct后面就是访问path重定向的地址


routes: [
		{path: '/lifeCircle',component: LifeCircle},
		{path: '/counsumeKid',component: CosumKid},
		{path: '/',redirect: '/lifeCircle'}
	]

这里就是如果访问相对路径为’/’,那么就会重定向到’/lifeCirecle’,也就是打开LifeCircle组件【在router-view位置】

路由高亮

router-link的链接如和设置高亮的效果,就是如果链接激活就是高亮的效果;可以通过两种方式将激活的路由链接进行高亮显示:

使用默认的高亮class类

被激活的路由链接,会默认应用一个router-link-active的类名,可以使用这个类名的选择器,为其设置高亮的效果

//在index.css中设置全局的样式,重写router-link-active的样式
.router-link-active{
	background-color: red;
	color: white;
	font-weight: bold;
}

在这里插入图片描述

路由高亮就是要让用户清晰看到当前展示的是哪个地址的页面,就类似于QQ页面标签点击下面的变大的功能【也就是路由的高亮】

使用自定义的高亮class类

在创建路由实例对象的时候,可以基于linkActiveClass属性,自定义路由链接被激活时应用的类名

//该属性和routes和history平级
const router = createRouter({
	history: createWebHashHistory(),
	linkActiveClass: 'router-active',
	routes: [
		{path: '/lifeCircle',component: LifeCircle},
		{path: '/counsumeKid',component: CosumKid},
		{path: '/',redirect: '/lifeCircle'}
	]
})

然后再在index.css中使用自定义的类名为激活的link添加样式

.router-active{
	background-color: red;
	color: white;
	font-weight: bold;
}

嵌套路由

通过路由实现组件的嵌套展示;比如在App中通过link展示几个子组件,在子组件中也可以通过link在嵌套,并且再次通过router-view进行占位

  • 首先就是声明子路由链接和子路由占位符

比如这里上面的声明的两个子组件LifeCirecle和CounsumKid;这里在ConsumKid中嵌套声明两个子组件SlotTest和MyHome

//第一步就是在CounmKid中声明子路由链接和占位符

<template>
	<div>
		ConsumKid子组件<br>
		X * 2 + 1的结果为 :<span ref='myspan' :style="{'background-color':color}">{{count * 2 + 1}}</span>
		<hr>
		
		<!-- 声明子路由链接 -->
		<router-link to="/counsumeKid/slotTest">SlotT</router-link>&nbsp;
		<router-link to="/counsumeKid/myHome">myHome</router-link>
		
		<!-- 声明子路由占位符 -->
		<router-view></router-view>
	</div>
</template>

这个时候点击界面就可以看到两个链接,控制台会给出warning, [Vue Router warn]: No match found for location with path “/counsumeKid/myHome” 因为这个时候只有路由链接,没有路由规则

  • 在父路由规则中,通过children属性嵌套声明子路由规则

在router.js中,导入需要渲染的两个组件,使用属性声明子路由规则

const router = createRouter({
	history: createWebHashHistory(),
	linkActiveClass: 'router-active',
	routes: [
		{path: '/lifeCircle',component: LifeCircle},
		// {path: '/counsumeKid',component: CosumKid}, 这里使用children声明
		{
			path: '/counsumeKid',
			component: CosumKid,
			children: [//该属性声明子路由规则
				{path:'/counsumeKid/slotTest',component: SlotTest},
				{path:'/counsumeKid/myHome',component: MyHome}
			]
		},
		{path: '/',redirect: '/lifeCircle'}
	]
})

这里子路径Hash地址,除了上面的写法,还可以使用资源路径的方式,比如直接slotTest;上面的是前台路径

嵌套路由的重定向

嵌套路由的重定向和之前的重定向没有太大的差别,就是把子规则中重新定义一个规则

routes: [
		{path: '/lifeCircle',component: LifeCircle},
		// {path: '/counsumeKid',component: CosumKid}, 这里使用children声明
		{
			path: '/counsumeKid',
			component: CosumKid,
			children: [//该属性声明子路由规则
				{path:'/counsumeKid/slotTest',component: SlotTest},
				{path:'/counsumeKid/myHome',component: MyHome},
				{path:'/',redirect:'myhome'}
			]
		},
		{path: '/',redirect: '/lifeCircle'}
	]

动态路由匹配

比如像C站注册了很多博主,每一个博主对应一个页面,如果按照之前的普通的路由规则的写法,那这个工作量太大了,复用性差,不符合软件开发的思想;这个时候,就应该使用动态的路由匹配

动态路由指的是:把Hash地址中可变的部分定义为参数项,提高路由规则的复用性,在vue-router中使用冒号: 来定义参数项

//将下面的路由规则合并
{path:'/movie/1',component: Movie},
{path:'/movie/2',component: Movie},
{path:'/movie/3',component: Movie},
    
-----------合并为一条--------------
{path:'/movie/:id',component:Movie}

这里的id就是动态的,访问不同的页面就可以获得不同的id值 ------点击不同的路由链接匹配的都是Movie组件

{path:'/counsumeKid/myHome/:id',component: MyHome
{path:'/',redirect:'myhome/:id'}
 
 
 <router-link to="/counsumeKid/myHome/1">myHome1</router-link>
 <router-link to="/counsumeKid/myHome/2">myHome2</router-link>
动态路由匹配获取参数值id----- $route.params

访问的都是同类型的组件,但是还是会有差异性,就是通过这个id来区分,比如博主的id,获取之后查询数据库然后根据不同的数据来渲染页面

通过动态路由匹配的方式渲染的组件中,可以使用$route.params对象访问到动态匹配的参数值

$route.params.XXX  --- 取得XXX参数值

这里可以直接在子组件中使用插值表达式

<p>MyHome组件 ---- {{count}}  {{$route.params.id}}</p>

这样就获取到了参数的值,切换时就会动态改变

简化传参 — props接收路由参数

为了简化路由参数的获取形式,允许props传参

  • 在定义路由规则时,加上结点props:true; 表示接收props传参,就可以在组件中通过props获取
  • 直接使用props接收参数 参数名就是XXX
{path:'/counsumeKid/myHome/:id',component: MyHome,props:true},

直接使用props接收Hash地址的参数【名称一致】
	export default {
		name:'MyHome',
		data() {
			return {
				count: 0,
			}
		},
		props:['id'],
	}

这样就可以便捷使用id这个prop, 比之前的$route.params方便许多

编程式导航

通过调用API实现导航的方式,叫做编程式导航,而通过点击链接实现导航的方式 — 声明式导航

普通网页中点击a链接,vue项目点击router-link都属于声明式导航

普通网页中点击location.href跳转到新页面的方式,属于编程式导航

vue-router编程式导航API

vue-router提供了很多编程式导航API,最常用的两个API分别是:

  • this.$router.push(“hash地址”) ----- 跳转到指定的Hash地址,展示对应的组件【和之前的获取参数的¥route.id有区别】
<hr>
<button type="button" @click="gotoMyhome(2)">导航到myHome2界面</button><br>

methods:{
			setColor() { //在子组件中定义了一个set方法,在另一个组件中点击按钮就可以执行该方法,那么就要使用组件的引用
				this.$refs.myspan.style.backgroundColor = 'pink'
			},
			gotoMyhome(id) {
				this.$router.push('/counsumeKid/myHome/' + id)
			} //这里不能使用资源路径,必须使用/开始的相对路径
		}
  • this.$router.go(数值 n) ------ 实现导航历史的前进、后退

这里传递值和之前的原生JS的是相同的,-1就是回退到上一个界面

<button type="button" class="btn btn-danger" @click="goBack">后退</button>

methods:{
			goBack() {
				this.$router.go(-1)
			}
		}

这里go(-1)就是后退到上一个页面中

命名路由 name指定跳转的路由,params指定路由参数

  • 声明式导航

通过name属性为路由规则定义名称 ----- 就是命名路由; 方式就是在路由规则对象中,使用name结点,和path,component,props、redirect等结点平级

routes: [
		{path: '/lifeCircle',component: LifeCircle},
		// {path: '/counsumeKid',component: CosumKid}, 这里使用children声明
		{
			path: '/counsumeKid',
			component: CosumKid,
			children: [//该属性声明子路由规则
				{path:'/counsumeKid/slotTest',component: SlotTest},
				{path:'/counsumeKid/myHome/:id',component: MyHome,props:true},
				{path:'/',redirect:'myhome/:id'}
			],
			props: true,
			name: 'conskid'
		},
		{path: '/',redirect: '/lifeCircle'}
	]

需要注意的是,命名路由的name具有唯一性,不能重复

这样对路由规则进行命名之后,就可以在router-link中进行使用,这里直接在to属性绑定的对象中进行属性的绑定

<router-link to="/counsumeKid">ConKis</router-link>


routes: [
		{path: '/lifeCircle',component: LifeCircle},
		// {path: '/counsumeKid',component: CosumKid}, 这里使用children声明
		{
			path: '/counsumeKid',  
之前这里就是通过的path来进行对应的,使用命名路由之后直接通过name进行匹配

--------------使用命名路由------------
 <router-link :to="{name:'conskid',params:{id:3}}">ConKis</router-link>

这里携带的参数也是一个JSON对象; 因为这里需要识别后面的对象,所以这里属于属性绑定,需要将:to, 不然就会当作字符串处理

  • 编程式导航

对于编程式导航,上面分析过,常用的就是this.$router.push(‘Hash地址’),或者使用this.¥router.go(); 对于编程式导航,也可以使用JSON对象使用命名路由;name为路由规则名,params为携带的参数

gotoMyhome(tid) {
				console.log(1)
				this.$router.push({name:'myHo',params:{id:tid}})
				// this.$router.push("/counsumeKid/myHome/" + tid)
			}

只要提供名称就可以匹配路由规则;和之前的非命名的路由的path匹配的效果是相同的;主要是可以避免path太长的情况

导航守卫

导航收尾可以控制路由的访问权限,防止一些恶意登录

在这里插入图片描述

从示意图可以看出来,导航守卫可以控制用户访问的界面

声明全局导航守卫 router.beforeEach(守卫方法)

这里的作用就和之前的SpringMvc的拦截器差不多,实际上,很多请求前台就应该拦截,不能放到后台增大工作量;全局导航守卫会拦截每个路由规则,对每一个路由进行访问权限的控制

调用路由实例对象的beforeEach函数,声明全局导航守卫

//创建路由对象
const router = createRouter({......})
                             
//调用路由实例对象的beforeEach函数,声明全局导航守卫,其中的声明的就是守卫函数,对于路由规则进行处理
router.beforeEach(fn)  //这里的fn就是守卫方法

这里在router.js中实现一下

router.beforeEach(() => {
	console.log("进行了拦截处理")
})

export default router

这样每次进行组件的切换(路由的切换)就都会触发这个全局的导航守卫

守卫方法的形参to、from、next

全局导航守卫会拦截每一个每个路由规则,to参数代表的是目标路由对象,from代表的当前的导航栏中的路由对象;next代表一个函数,表示执行

router.beforeEach((to) => {
	console.log("进行了拦截处理")
	console.log(to)
})

---------------这里就会打印要访问的路由对象-------------
{fullPath: '/counsumeKid/slotTest', path: '/counsumeKid/slotTest', query: {}, hash: '', name: undefined,}

这里的路由对象有fullpath,path,还有query,hash,name等属性

router.beforeEach((to,from) => {
	console.log("进行了拦截处理")
	console.log(from)
})

----------------打印当前页面【要离开】的路由对象---------------
{path: '/', name: undefined, params: {}, query: {}, hash: '',}

这里进去的时候就打印的不是/…, 而是/

对于next形参,如果守卫方法中不使用next形参,那么就会默认允许用户访问每一个路由,如果声明了next参数,就必须调用next()函数,不然不允许访问任何一个路由

router.beforeEach((to,from,next) => {
	console.log("进行了拦截处理")
	console.log(to)
})

比如这里就是点击链接,不会显示组件内容,但是导航守卫方法会执行

next函数3中调用方式
  • next() 直接放行
router.beforeEach((to,from,next) => {
	if(to.path === '/lifeCircle') {
		next()
	}
	else{
		next()
	}
})

这里就是不管访问的是哪一个页面,都会直接放行,拦截不起作用

  • next(false) 拦截,强制停留在当前页面,不放行
router.beforeEach((to,from,next) => {
	if(to.path === '/lifeCircle') {
        alert("没有权限访问!")
		next(false)
	}
	else{
		next()
	}
})

这里处理之后,访问Hash地址为/lifeCircle,就不能访问,其他的路径正常访问;

  • next(‘hash地址’) 强制跳转到某个固定的页面
//比如用户想要直接访问后台,强制跳转到登录页面  -- 这样就不需要后台的DispatcherServlet等进行处理了
router.beforeEach((to,from,next) => {
	if(to.path === '/lifeCircle') {
		next('/counsumeKid/slotTest')
	}
	else{
		next()
	}
})

这里当点击/lifeCircle链接的时候,就会自动跳转到’/counsumeKid/slotTest’这个组件展示

结合token控制后台访问的权限

img

token和session有区别,当服务器不同的时候,session就不能正常使用了,但是原理都是一样的,都是令牌机制

router.beforeEach((to,from,next) => {
	const token = localStorage.getItem('token') //读取token
	if(to.path === '/lifeCircle' && !token) {
		next('/counsumeKid/slotTest')
	}
	else{
		next()
	}
})

这里因为Application中的localstorage中没有token,所以会强制跳转到指定页面/counsumeKid/slotTest, 这里使用浏览器的在本地存储中手动添加token,就可以进行访问【手动添加token:xxx】,就可以正常跳转显示lifeCirecle组件

后台管理案例

这里就是简单的实现登录,然后呈现的页面就类似之前的SSM的后台系统这种简单的导航页面demo

首先就是初始化项目,使用那一连串的命令即可,同时安装axios和less还有vue-router

PS D:\Vueprogramm> npm init vite-app router-demo
Scaffolding project in D:\Vueprogramm\router-demo...

Done. Now run:

  cd router-demo
  npm install (or `yarn`)
  npm run dev (or `yarn dev`)

PS D:\Vueprogramm> cd router-demo
PS D:\Vueprogramm\router-demo> npm i

added 285 packages in 7s
PS D:\Vueprogramm\router-demo> npm i less -D

added 17 packages in 1s
PS D:\Vueprogramm\router-demo> npm i vue-router@next -S

added 2 packages in 1s
PS D:\Vueprogramm\router-demo> npm i axios -S

added 1 package in 1s
PS D:\Vueprogramm\router-demo> npm run dev

> router-demo@0.0.0 dev
> vite

[vite] Optimizable dependencies detected:
axios, vue, vue-router

  Dev server running at:

初始化项目

这里就删除项目初始的组件,同时删除相关的css的内容

  • index.css 设置全局的字体的大小,同时设置全局的高度height
:root {
  font-size: 13px;
}

html,
body,
#app {
  height: 100%;
}
  • main.js 导入bootstrap的css文件,同时要配置vue-router
import { createApp } from 'vue'
import App from './App.vue'
// 导入 bootstrap 样式表
import './assets/css/bootstrap.css'
// 导入全局自定义样式表
import './index.css'
import router from './router.js'

// 创建 app 实例
const spa_app = createApp(App)
// 挂载 app 实例

spa_app.use(router)
spa_app.mount('#app')
  • router.js文件 – 创建路由对象
import { createRouter,createWebHashHistory } from "vue-router"

const router = createRouter({
	history: createWebHashHistory(),
	routes:[
		
	]
})

//全局的导航守卫
router.beforeEach((to,from) =>{
	
})

export default router

封装MyLogin组件并进行路由配置

这个组件展示的是登录的界面,最开始登陆时显示的登录密码等【直接在bootstrap上取了部分样式】

<template>
  <div class="login-container">
    <div class="login-box">

      <!-- 头像区域 -->
      <div class="text-center avatar-box">
        <img src="../assets/logoin.jpg" class="img-thumbnail avatar" alt="">
      </div>

      <!-- 表单区域 -->
      <div class="form-login p-4">
        <!-- 登录名称 -->
        <div class="form-group form-inline">
          <label for="username">登录名称</label>
          <input type="text" class="form-control ml-2" id="username" placeholder="请输入登录名称" autocomplete="off">
        </div>
        <!-- 登录密码 -->
        <div class="form-group form-inline">
          <label for="password">登录密码</label>
          <input type="password" class="form-control ml-2" id="password" placeholder="请输入登录密码">
        </div>
        <!-- 登录和重置按钮 -->
        <div class="form-group form-inline d-flex justify-content-end">
          <button type="button" class="btn btn-secondary mr-2">重置</button>
          <button type="button" class="btn btn-primary">登录</button>
        </div>
      </div>

    </div>
  </div>
</template>

<script>
export default {
  name: 'MyLogin',
}
</script>

<style lang="less" scoped>
.login-container {
  background-color: #35495e;
  height: 100%;
  .login-box {
    width: 400px;
    height: 250px;
    background-color: #fff;
    border-radius: 3px;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    box-shadow: 0 0 6px rgba(255, 255, 255, 0.5);
    .form-login {
      position: absolute;
      bottom: 0;
      left: 0;
      width: 100%;
      box-sizing: border-box;
    }
  }
}

.form-control {
  flex: 1;
}

.avatar-box {
  position: absolute;
  width: 100%;
  top: -65px;
  left: 0;
  .avatar {
    width: 120px;
    height: 120px;
    border-radius: 50% !important;
    box-shadow: 0 0 6px #efefef;
  }
}
</style>

然后就是展示Login组件,在router.js中导入模块,并封装路由规则,这里使用重定向,当访问项目时默认跳转到该位置;类似之前的欢迎页面

import Login from './components/MyLogin.vue'

const router = createRouter({
	history: createWebHashHistory(),
	routes:[
		{path:'/login',component: Login},
		{path:'/',redirect:'/login'}
	]
})

-------------App.vue占位符----------
<template>
  <router-view></router-view>  <!-- 这里就会自动展示为整个页面 -->
</template>

这样每次进入项目,相当于就是点击了/,然后自动重定向显示Login组件

在这里插入图片描述

这样就展示了登录页面,这个页面做的十分见到那,就几个input就没有了,美化的最主要的就是CSS

模拟实现登录功能

在MyLogin组件中声明data数据,username和password,然后将数据与上面的文本框进行双向的数据绑定

  name: 'MyLogin',
  data() {
  	return {
		username: '',
		password: '',
	}
  },
}


        <div class="form-group form-inline">
          <label for="username">登录名称</label>
          <input type="text" class="form-control ml-2" id="username" placeholder="请输入登录名称" autocomplete="off" v-model.trim="username">
        </div>
        <!-- 登录密码 -->
        <div class="form-group form-inline">
          <label for="password">登录密码</label>
          <input type="password" class="form-control ml-2" id="password" placeholder="请输入登录密码" v-model="password">
        </div>

同时为登录按钮添加事件处理函数

  methods:{
	  onLoginClick() {
		  //判断用户名和密码是否正确
		  if(this.username === 'Cfeng' && this.password === '000000') {
			  //登录成功,跳转到后台主页
			  this.$router.push('/home')
			  //模拟存储token
			  return localStorage.setItem('token','Bearer 89y')
		  }
		  //登录失败,就会清除token
		  localStorage.removeItem('token')
	  }
  }

这里模拟了登录之后存取token的过程,登录成功就会获取到token存储到本地,否则移除本地的token【登录失败的时候会移除登录成功时的token】

封装MyHome组件并路由渲染

这个组件就是后台登录的页面,也就是#/home这个hash地址

<template>
  <div class="home-container">
    <!-- 头部组件 -->
	<my-header></my-header>
	
	<!-- 主体区域 -->
	<div class="home-main-box">
		<!-- 左边侧栏区域 -->
		<my-aside></my-aside>
		
		<!-- 右侧内容 -->
		<div class="home-main-body">
			
		</div>
	</div>
  </div>
</template>

<script>
// 头部区域组件
import MyHeader from './subcomponents/MyHeader.vue'
// 左侧边栏组件
import MyAside from './subcomponents/MyAside.vue'

export default {
  name: 'MyHome',
  // 注册组件
  components: {
    MyHeader,
    MyAside,
  },
}
</script>

<style lang="less" scoped>
.home-container {
  height: 100%;
  display: flex;
  flex-direction: column;

  .home-main-box {
    height: 100%;
    display: flex;
    .home-main-body {
      padding: 15px;
      flex: 1;
    }
  }
}
</style>

后台的主页就是通过两个私有的子组件组成的

  • MyHeader.vue

这个组件就是之前封装过多次,主要包含的就是logo和名称

<template>
  <div class="layout-header-container d-flex justify-content-between align-items-center p-3">
    <!-- 左侧 logo 和 标题区域 -->
    <div class="layout-header-left d-flex align-items-center user-select-none">
      <!-- logo -->
      <img class="layout-header-left-img" src="../../assets/Cfeng.png" alt="">
      <!-- 标题 -->
      <h4 class="layout-header-left-title ml-3">Cfeng后台管理系统</h4>
    </div>

    <!-- 右侧按钮区域 -->
    <div class="layout-header-right">
      <button type="button" class="btn btn-light">退出登录</button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'MyHeader',
}
</script>

<style lang="less" scoped>
.layout-header-container {
  height: 60px;
  border-bottom: 1px solid #eaeaea;
}

.layout-header-left-img {
  height: 50px;
}
</style>
  • MyAside.vue

这个组件封装的是左边的菜单栏部分; 就是一个简单的列表,但是最复杂的就是CSS的样式渲染:happy:

<template>
  <div class="layout-aside-container">
    <!-- 左侧边栏列表 -->
    <ul class="user-select-none menu">
      <li class="menu-item">用户管理</li>
      <li class="menu-item">权限管理</li>
      <li class="menu-item">商品管理</li>
      <li class="menu-item">订单管理</li>
      <li class="menu-item">系统设置</li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'MyAside',
}
</script>

<style lang="less" scoped>
.layout-aside-container {
  width: 250px;
  height: 100%;
  border-right: 1px solid #eaeaea;
}

.menu {
  list-style-type: none;
  padding: 0;
  .menu-item {
    line-height: 50px;
    font-weight: bold;
    font-size: 14px;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
      Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
    &:hover {
      background-color: #efefef;
      cursor: pointer;
    }
    a {
      display: block;
      color: black;
      padding-left: 30px;
      &:hover {
        text-decoration: none;
      }
    }
  }
}

// 设置路由高亮效果
.router-link-active {
  background-color: #efefef;
  box-sizing: border-box;
  position: relative;
  // 伪元素实现路由高亮效果
  &::before {
    content: ' ';
    display: block;
    width: 4px;
    height: 100%;
    position: absolute;
    left: 0;
    top: 0;
    background-color: #42b983;
  }
}
</style>

路由渲染的过程和之前是相同的,导入即可

渲染Home.vue的基本结构,将子组件渲染到页面上

实现退出登录

退出登录首先就是要回退到登录的界面,同时要清空token值,直接为按钮绑定一个函数onLoginOut了

export default {
  name: 'MyHeader',
  methods:{
	  onLoginOut() {
		  //移除token
		  localStorage.removeItem('token')
		  //强制跳转到登录界面
		  this.$router.push('/login')
	  }
  }
}

这个环节实现很简单

全局控制路由的访问权限

这里就是依靠全局的导航守卫

//全局的导航守卫
router.beforeEach((to,from,next) =>{
	//如果用户访问的是login,直接放行
	if(to.path === '/login') return next()
	//获取token值判断合法
	//访问其他的页面就要判断token
    const token = localStorage.getItem('token')
	if(!token) {
		return next('/login')
	}
	//存在token值,直接放行
	next()
})

这样就不可以直接从地址栏访问到后台的页面

将home左侧的菜单改为路由链接

将菜单改为路由链接,这样点击链接就可以直接跳转,并且将对应的组件通过路由占位符显示到右侧

//之前的aside边框
    <ul class="user-select-none menu">
      <li class="menu-item">用户管理</li>
      <li class="menu-item">权限管理</li>
      <li class="menu-item">商品管理</li>
      <li class="menu-item">订单管理</li>
      <li class="menu-item">系统设置</li>
    </ul>

--------将li中添加为路由链接---------------
    <ul class="user-select-none menu">
      <li class="menu-item">
		  <router-link to="/home/users">用户管理</router-link>
	  </li>
      <li class="menu-item">
		  <router-link to="/home/rights">权限管理</router-link>
	  </li>
      <li class="menu-item">
		  <router-link to="/home/goods">商品管理</router-link>
	  </li>
      <li class="menu-item">
		  <router-link to="/home/orders">订单管理</router-link>
	  </li>
      <li class="menu-item">
		  <router-link to="/home/settings">系统设置</router-link>
	  </li>
    </ul>


在myHOme中进行占位
<!-- 右侧内容 -->
		<div class="home-main-body">
			<router-view></router-view>
		</div>

之后在routers中绑定路由规则

const router = createRouter({
	history: createWebHashHistory(),
	routes:[
		{path:'/login',component: Login},
		{path:'/',redirect:'/login'}, //这里进行了重定向,所以进入项目就会自动访问展示Login组件
		{
			path:'/home',
			component: Home,
			redirect:'/home/users',
			children:[
				{path:'users',component:Users},
				{path:'rights',component:Rights},
				{path:'goods',component:Goods},
				{path:'settings',component:Settings},
				{path:'orders',component:Orders}
			]
		},
	]
})

绑定之后菜单栏就可以正常进行显示,这个easy,所以不阐释了

渲染用户列表数据

这里的用户管理的子组件还没有获取到任何的数据,这是需要请求后台从数据库中获取数据的; 这里直接在MyUsers组件中使用v-for命令进行渲染

<tbody>
      <tr v-for="(item,index) in userList" :key="item.id">
        <td>{{index + 1}}</td>
        <td>{{item.name}}</td>
        <td>{{item.age}}</td>
        <td>{{item.position}}</td>
        <td>详情</td>
      </tr>
    </tbody>

这里的数据直接使用的data定义,实际上是从数据库中获取的

这里点击详情需要达到的效果就是查看详细信息,跳转的组件和之前的页面平级 — 右侧主体区域

<td>
	<router-link :to="'/home/users' + item.id">详情</router-link>
</td>

然后导入详情页面,将其在children结点下声明路由规则 {path:‘users/:id’,component:UserDetails}

就可以正常实现跳转

在router中开启props传参

{path:'users/:id',component:UserDetails,props:true}

在详情页的组件中,使用props接收参数

export default {
  name: 'MyUserDetail',
  props:['id']
}

这样就可以具体的渲染用户的详情

详情页后退

这里就直接给按钮绑定一个点击事件,并使用router的go方法就可以正常后退

  methods:{
	  goBack() {
		  this.$router.go(-1)
	  }
  }

在这里插入图片描述

这样就简单完成了功能, 🎉

Logo

前往低代码交流专区

更多推荐