测试开发前端篇之登录页

前提概述

该项目前端框架使用的是VUE3,UI组件采用ELement UI。具体技术点如下(以后也许会补充),适合测试人员学习,专业前端人员绕步🤣

  • Vue.js:一个流行的 JavaScript 前端框架,用于构建用户界面。

  • HTML/CSS:用于定义页面结构和样式的基本标记语言。

  • JavaScript:前端开发的主要编程语言。

  • Axios :用于进行 HTTP 请求和数据交互的 JavaScript 库。

  • Vuex:用于状态管理的 Vue.js 官方插件。

  • Vue Router:用于管理导航和路由的 Vue.js 官方插件。

  • Webpack:前端构建工具,用于打包和优化项目中的资源。

登录页页面构建

目录结构

vue_base
├─ .gitignore
├─ index.html
├─ package-lock.json
├─ package.json
├─ public
│  └─ favicon.ico
├─ README.md
├─ src
│  ├─ api
│  │  └─ api.js
│  ├─ App.vue
│  ├─ assets
│  │  ├─ css
│  │  │  └─ main.css
│  │  └─ logoA.png
│  ├─ components
│  │  └─ LoginBack.vue
│  ├─ main.js
│  ├─ router
│  │  └─ index.js
│  ├─ store
│  │  └─ index.js
│  ├─ views
│  │  ├─ Login.vue
│  └─ vite-env.d.ts
└─ vite.config.js
  • .gitignore:Git 版本控制系统忽略文件的配置,指定哪些文件和目录不需要纳入版本管理。

  • index.html:主页面 HTML 文件,该文件通常包含根 Vue 组件的挂载点。

  • package-lock.json:npm 包的精确版本锁定文件,用于确保在重复构建时安装相同版本的依赖包。

  • package.json:记录项目的元数据和依赖项,以及定义一些脚本命令和项目配置信息。

  • public:存放公共资源的目录,其中 favicon.ico 是网站的图标。

  • README.md:项目的说明文档,提供项目的介绍、使用方法等相关信息。

  • src:源代码的根目录,存放项目的主要代码。

    • api:存放后端 API 请求的封装代码或接口配置文件。

    • App.vue:根组件,作为项目的入口组件,负责渲染其他组件。

    • assets:存放项目使用的静态资源文件,如图片、样式表等。

      • css:存放 CSS 样式文件的目录,其中 main.css 是主要样式表。

      • logoA.png:项目使用的图片资源文件。

    • components:存放项目中的可复用组件。

      • LoginBack.vue:登录背景组件。

    • main.js:项目的入口文件,负责初始化 Vue 实例并挂载根组件。

    • router:存放 Vue Router 路由配置的目录。

      • index.js:路由配置文件,定义页面之间的导航规则。

    • store:存放 Vuex 状态管理的相关文件。

      • index.js:Vuex 的配置文件,定义全局状态和状态变更逻辑。

    • views:存放视图组件(页面级组件)。

      • Login.vue:登录页面组件。

全局公共的css样式

src\assets\css\main.css

.login_box .el-input__wrapper {
	/* 去除默认的背景和边框阴影 */
	background: none;
	box-shadow: none;
	/* 去除边框的圆角 */
	border-radius: 0px;
	/* 设置下边框的样式 */
	border-bottom: solid 1px #006b9d;
	/* 设置字体大小 */
	font-size: 15px;
}

.login_box .el-input__inner, 
.login_box .el-form-item__label
{
	color: #fff;
}

/* 登录时候账号密码填充的时候input背景变白解决 */
input:-webkit-autofill {  
	-webkit-text-fill-color: #ededed !important;  
	-webkit-box-shadow: 0 0 0px 1000px transparent  inset !important;  
	background-color:transparent;  
	background-image: none;  
	transition: background-color 50000s ease-in-out 0s;  
}  
input {  
	background-color:transparent;  
	  caret-color: #fff;  
}  

最后在mian.js中导入import './assets/css/main.css'

main.js文件

import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import api from './api/api.js'

// 1.导入路由对象所在的js文件
import router from './router'
import store from './store'

// 导入全局公共的css样式
import './assets/css/main.css'

const app = createApp(App)
// 是将 httpDev 对象注册为 Vue 应用程序的全局属性
app.config.globalProperties.$api = api

for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    app.component(key, component)
  }

// 添加路由守卫
router.beforeEach((to, from, next) => {
  const token = sessionStorage.getItem('token');
  if (to.meta.requiresAuth) { // 页面需要登录权限
    if (token && token !== '') { // 登录状态有效
      console.log('Authenticated'); // 验证路由守卫被执行
      next();
    } else { // 未登录或登录状态无效
      console.log('Not Authenticated'); // 验证路由守卫被执行
      next({ name: 'login' }); // 重定向到登录页
    }
  } else {
    next();
  }
});


app.use(ElementPlus).use(router).use(store)
// 将路由对象挂载到vue实例中
app.mount('#app')

这段代码是一个Vue3的入口文件,主要功能如下:

  1. 导入了createApp函数和ElementPlus插件,以及相关的样式文件。

  2. 导入了根组件App.vueElementPlusIconsVue图标组件。

  3. 导入了api.js文件,用于处理后端API请求。

  4. 创建了Vue实例app

  5. 使用$apiapi对象注册为全局属性,方便在组件中使用。

  6. 遍历ElementPlusIconsVue对象并使用app.component()方法注册了所有的图标组件。

  7. 添加了路由守卫,通过beforeEach方法实现全局路由守卫功能,用于控制页面访问权限。

  8. 使用app.use()方法安装了ElementPlusrouterstore插件。

  9. 将路由对象挂载到Vue实例中。

  10. 通过app.mount('#app')将Vue实例挂载到id为app的DOM元素上。

整体来说,这段代码的作用是创建一个Vue应用实例,配置插件、路由和状态管理(Vuex),并将根组件挂载到DOM元素上。同时还实现了路由守卫功能,用于控制页面访问权限。

这里解释一下路由守卫功能:

路由守卫是在Vue Router中提供的一种机制,用于在路由导航过程中对路由进行拦截和控制。通过使用路由守卫,我们可以在用户访问页面之前、之后或在路由切换时执行一些逻辑,例如检查用户登录状态、权限验证、页面访问限制等。

在上述代码中,使用了beforeEach方法实现了全局的路由守卫。下面是路由守卫的详细解释:

  1. router.beforeEach((to, from, next) => {...}):使用beforeEach方法注册一个全局前置守卫。该方法接收一个回调函数作为参数,该回调函数会在每个路由导航之前被调用。

  2. (to, from, next):回调函数的参数分别是要进入的目标路由对象to,当前导航正要离开的路由对象from,以及一个next函数。

  3. const token = sessionStorage.getItem('token'):获取用户登录凭证(此处使用sessionStorage存储在浏览器会话期间),用于检查用户的登录状态。

  4. if (to.meta.requiresAuth) {...}:根据路由元信息(meta)判断该路由是否需要登录权限,如果需要则执行下面的逻辑。

  5. if (token && token !== '') {...}:检查用户是否已登录(即凭证存在且不为空),如果已登录则允许继续访问该页面。

  6. console.log('Authenticated')console.log('Not Authenticated'):分别在用户已登录和未登录的情况下打印相应的信息,用于验证路由守卫的执行。

  7. next()next({ name: 'login' })next函数允许导航继续进行。如果用户已登录,则调用next()允许访问目标路由;如果用户未登录或登录状态无效,则调用next({ name: 'login' })重定向到登录页。

  8. else:对于不需要登录权限的路由,直接调用next()允许访问。

总结起来,通过路由守卫可以在每次路由切换前检查用户登录状态,并根据需要进行权限验证和重定向操作。这有助于实现页面级别的访问控制和用户认证功能。

比如说项目页路由定义了:requiresAuth:true,则在没有登录的情况下直接访问项目页会重定向到登录页

{
    path: '/allProject',
	name: 'allProject',
	component: () => import('../views/AllProject.vue'),
	meta: {
		requiresAuth: true // 需要登录权限
		 }
},

根组件

<template>
  <router-view />
</template>

<script>
</script>

<template> 标签内,我们定义了组件的模板内容。在这个例子中,只有一个 <router-view /> 标签,它是Vue Router提供的一个占位符,用于显示路由对应的组件内容。当路由切换时,对应的组件会被渲染到这个位置。

路由文件

src\router\index.js

// 1.导入createRouter、createWebHashHistory
import {
	createRouter, 
	createWebHashHistory,
	createWebHistory,
} from 'vue-router'

// 2.配置路由规则
const routes = [
	{
		// 指定页面访问的路径
		// 必须指定
		path: '/',
		// 指定该路由的名称
		name: 'root',
		// 定义当返回路径时,显示的组件
		// component: () => import('../views/Home.vue')
		// redirect: '/login'
		redirect: { name: 'login' } // 将 /home 重定向到名为 dashboard 的路由
	},
	{
		path: '/login',
		name: 'login',
		component: () => import('../views/Login.vue')
 	},
]

// 3.创建路由实例
const router = createRouter({
	// 设置路由模式
	// a.hash模式:会在url中带一个#,无需服务器进行额外配置、兼容性好、不利于SEO,搜索引擎不会抓取#后面的内容
	history: createWebHashHistory(),
	// b.HTML5模式:url中不带#,需要服务器的额外配置(nginx)、兼容性不好、利于SEO
	// history: createWebHistory(),
	
	// 指定路由规则数组
	// routes: routes
	routes
})

// 4.导出路由实例 
export default router

这段代码是在Vue应用中配置路由的代码。

首先,通过import语句导入了createRoutercreateWebHashHistorycreateWebHistory这三个函数。其中,createRouter用于创建路由实例,而createWebHashHistorycreateWebHistory则用于指定路由模式。

接下来,定义了一个routes数组,用于配置路由规则。每个路由规则是一个对象,包含以下属性:

  • path:指定页面访问的路径,例如//login

  • name:指定该路由的名称,可以在编程式导航中使用。

  • component:指定该路径对应的组件。这里使用了动态导入(import()),以支持懒加载。

然后,使用createRouter函数创建了路由实例。在创建路由实例时,使用了createWebHashHistory()函数来指定路由模式为hash模式。hash模式的URL中带有#符号,不需要服务器进行额外配置,兼容性较好,但不利于SEO。

最后,通过export default router语句将路由实例导出,以便在Vue应用的入口文件中将其与Vue应用关联。

总结起来,这段代码的作用就是配置了Vue应用的路由规则,并创建了一个路由实例,采用hash模式,最后将路由实例导出供其他地方使用。

封装接口请求

src\api\api.js

import axios from 'axios'

const base_url = 'http://xxx.x.xxx.xxx:xxxxx';
// 创建axios请求实例对象
const httpDev = axios.create({
  baseURL: base_url,	//定义公共的base url
  headers: {
    'Content-Type': 'application/json'
  },
  	// 定义校验响应状态码
  validateStatus: function(status) {
    return status >= 200 && status < 300;
  }
})

// 请求拦截器
httpDev.interceptors.request.use(
	function(config) {
      // 发送请求前判断是否需要鉴权
  console.log('请求拦截器:config', config)
  if (config.url !== '/login/' && config.url !== '/register/') {
    // 添加token
    const token = sessionStorage.getItem('token');
			if (token) {
				// Bearer
				config.headers['Authorization'] = 'JWT ' + token;
			}
  }
  return config;
  },
  function(error) {
    // 请求错误时做一些处理
    return Promise.reject(error)
  }
)

// 响应拦截器
httpDev.interceptors.response.use(
  function(response) {
    console.log('响应拦截器:response', response)
    // 响应数据之前做一些处理
    return response
  },
  function(error) {
    // 响应错误时做一些处理
    return Promise.reject(error)
  }
)

export default {
	// 登录接口
	loginApi(params) {
		return httpDev.post('/login/', params);
	},
	// 。。。
	
}

这段代码是使用axios库创建了一个HTTP请求对象,并定义了请求和响应的拦截器。

首先,通过import axios from 'axios'导入axios库。

接下来,定义了一个常量base_url,用于存储API的基本URL。你需要将它替换为实际的API服务器地址。

然后,通过axios.create()函数创建了一个名为httpDev的axios实例。在创建实例时,使用了一些配置:

  • baseURL:指定了公共的base URL,即API的基本URL。所有使用该实例发出的请求都会自动添加该base URL。

  • headers:指定了请求头中的Content-Typeapplication/json,表示请求体为JSON格式。

  • validateStatus:定义了校验响应状态码的函数,该函数判断状态码是否在200到300之间,如果是则返回true,否则返回false

接下来,定义了请求拦截器。通过httpDev.interceptors.request.use()可以注册一个请求拦截器,在发送请求前对请求进行处理。具体的处理逻辑包括:

  • 判断请求的URL是否为/login//register/,如果不是,则需要添加鉴权token。

  • 从sessionStorage中获取token,并在请求头中添加Authorization字段,值为JWT 加上token。

然后,定义了响应拦截器。通过httpDev.interceptors.response.use()可以注册一个响应拦截器,在处理响应数据之前对响应进行处理。这里简单地打印了响应数据。

最后,通过export default将一个包含登录接口等方法的对象导出,以便在其他地方使用。这些方法通过调用httpDev实例的各种方法来发送请求,例如httpDev.post('/login/', params)会发送一个POST请求到http://xxx.x.xxx.xxx:xxxxx/login/,参数为params

总结起来,这段代码通过axios库创建了一个HTTP请求对象,并定义了请求和响应的拦截器来处理请求和响应的一些通用逻辑,同时导出了一些接口方法供其他地方使用。

登录组件

src\views\Login.vue

<template>
	<LoginBack>
		<div class="login_box">
			<div class="logo">
        		<img src="../assets/logoA.png" alt="Logo">
      		</div>
			<el-form :model="loginForm" :rules="loginRules" ref="loginForm">
            """loginForm是一个数据对象,通过 :model="loginForm" 将其与 <el-form> 组件进行绑定。
               这样,在表单元素中使用 v-model 绑定某个字段时,该字段的值会从 form 对象中获取,
               并且当表单元素的值发生变化时,也会更新到 form 对象中的相应字段。
               所以,:model="form" 的作用是将表单元素与一个数据对象进行绑定,实现了从数据对象到表单元素的单向绑定。
               要实现双向绑定,需要在具体的表单元素上使用 v-model 指令。"""
				<el-form-item prop="username">
				  <el-input  v-model="loginForm.username" name="username" placeholder="体验账号:test" autocomplete="username">
					<template #prefix>
						<el-icon :size="24"><User /></el-icon>
					</template>
				  </el-input>
				</el-form-item>
				
				<el-form-item prop="password">
				  <el-input  v-model="loginForm.password" name="password" placeholder="密码:test123456" autocomplete="current-password" type="password">
					<template #prefix>
							<el-icon :size="24"><Lock /></el-icon>
					</template>
				  </el-input>
				</el-form-item>
				
				<el-form-item label="记住账号">
				  <el-switch v-model="loginForm.status" />
				</el-form-item>
				
				<el-form-item>
				  <el-button type="primary" style="width: 100% ; height: 40px;" @click="login">登录</el-button>
				</el-form-item>
				
				<div style="color: rgb(255, 255, 255);">没有账号?
				<span style="color: rgb(0, 85, 127); cursor: pointer; " @click="register">点击注册</span>
				</div>
			</el-form>
		</div>
	</LoginBack>
</template>

<script>
import LoginBack from '../components/LoginBack.vue'
export default {
	data() {
		return {
			loginForm: {
				username: '',
				password: '',
				status: false
			},
			loginRules: {
				username: [
				{ required: true, message: '请输入用户名', trigger: 'blur' },
				{ min: 3, max: 20, message: '用户名长度应在3-20个字符之间', trigger: 'blur' }
				],
				password: [
				{ required: true, message: '请输入密码', trigger: 'blur' },
				{ min: 6, max: 16, message: '密码长度应在6-16个字符之间', trigger: 'blur' }
				]
			}
		}
	},
	// mounted() 是 Vue 组件生命周期钩子函数之一。当组件被挂载到 DOM 中时,mounted() 函数会被调用。
	mounted() {
		const savedUsername = localStorage.getItem('username');
		// localStorage 是一种 JavaScript 提供的用于在浏览器端存储数据的对象。通过使用 getItem() 方法并传入键名 "username",你可以获取之前在本地存储中存储的用户名的值。
		const savedPassword = localStorage.getItem('password');
		
		if (savedUsername && savedPassword) {
			this.loginForm.username = savedUsername;
			this.loginForm.password = savedPassword;
			this.loginForm.status = true; // 设置记住密码状态为开启
		} else {
			this.loginForm.status = false; // 设置记住密码状态为关闭
			}
		},
	components: {
    LoginBack
},
	methods: {
		// 构建登录接口请求
		async login() {
				try {
					const valid = await this.$refs.loginForm.validate();
					// valid 变量将包含 validate() 方法返回的结果,可能是一个布尔值或其他类型的验证结果,用于判断表单是否通过验证。
					if (valid) {
						// const response = await httpDev.post('/login/', this.loginForm);
						const response = await this.$api.loginApi(this.loginForm);
						if (response.status === 200) {
							this.token = response.data.token;
							sessionStorage.setItem('token', this.token);
							// sessionStorage.setItem('token', this.token); 是将 this.token 的值存储到浏览器的会话存储(session storage)中,使用名为 "token" 的键。
							if (this.loginForm.status) {
								localStorage.setItem('username', this.loginForm.username);
								localStorage.setItem('password', this.loginForm.password);
							} else {
								localStorage.removeItem('username');
								localStorage.removeItem('password');
							}
							sessionStorage.setItem('redirectedFromLogin', 'true');
							// 你可以在其他页面或组件中通过调用 sessionStorage.getItem('redirectedFromLogin') 
							// 来获取存储在会话存储中的这个标记值。如果返回的值是 'true',则表示用户是从登录页面重定向而来。
							this.$router.push({ name: 'allProject' });
						} else {
							alert("登录失败");
						}
					} else {
					alert("用户名和密码输入无效");
					}
			} catch(e) {
				console.error(e)
				this.showLoginFaildMessage()
			}	
			},
			register (){
				// ElMessage({
				// 	message: '暂未开放注册.',
				// 	type: 'warning',
				// })
			this.$router.push({ name: 'register'})
			},
			showLoginFaildMessage() {
				ElMessage.error('登录失败.')
			}
			}
	}
</script>

<style scoped>
	.login_box {
		width: 500px;
		height: 400px;
		/* vh代表视口高度的百分比,100vh等于视口高度的100% 
			使用100vh可以确保元素的高度始终与视口高度相匹配,无论视口的大小如何变化。
			这对于创建全屏背景、占满整个屏幕的布局或者垂直居中元素等场景非常有用。
		*/
		margin: calc((100vh - 200px) / 4 ) auto;
		
	}
	.logo{
		padding-bottom: 30px;
		text-align: center;
	}
</style>

这段代码是一个登录页面的 Vue 组件,主要实现以下功能:

  1. 在页面中引入了一个名为 LoginBack 的组件,用于显示背景图或者其他装饰效果。

  2. 定义了一个 loginForm 对象,用于存储用户输入的用户名、密码和记住账号的状态。

  3. 定义了 loginRules 对象,用于设置表单验证规则,包括用户名和密码的必填、长度限制等。

  4. 在组件的 mounted() 生命周期钩子函数中,通过 localStorage 获取存储在本地的用户名和密码,并将其赋值给 loginForm 对象。同时根据记住账号的状态设置开关按钮的选中状态。

  5. 定义了一个 login() 方法,用于处理登录按钮点击事件。在该方法中,首先通过 validate() 方法验证表单是否通过验证,如果通过,则发送登录请求;否则,弹出提示错误信息。

  6. 在登录请求成功后,将返回的 token 存储到会话存储中(sessionStorage),如果记住账号的状态为开启,则将用户名和密码存储到本地存储中(localStorage)。

  7. 定义了一个 register() 方法,用于处理注册按钮点击事件。在该方法中,通过路由跳转到注册页面。

  8. 定义了一个 showLoginFaildMessage() 方法,用于在登录失败时弹出错误提示信息。

总体来说,这段代码实现了一个简单的登录页面,包括表单验证、记住账号功能和路由跳转。用户输入用户名和密码后,点击登录按钮可以发送登录请求,并根据登录结果进行相应处理。

这里着重解释一下记住密码的逻辑实现与账户密码规则校验的实现

loginForm: {
	username: '',
	password: '',
	status: false
},

loginForm 对象中定义了一个 status 字段,用于表示记住密码的状态,初始值为 false,即未选中记住密码。

// mounted() 是 Vue 组件生命周期钩子函数之一。当组件被挂载到 DOM 中时,mounted() 函数会被调用。
mounted() {
	const savedUsername = localStorage.getItem('username');
	// localStorage 是一种 JavaScript 提供的用于在浏览器端存储数据的对象。通过使用 getItem() 方法并传入键名 "username",你可以获取之前在本地存储中存储的用户名的值。
	const savedPassword = localStorage.getItem('password');
		
	if (savedUsername && savedPassword) {
		this.loginForm.username = savedUsername;
	    this.loginForm.password = savedPassword;
		this.loginForm.status = true; // 设置记住密码状态为开启
	} else {
		this.loginForm.status = false; // 设置记住密码状态为关闭
		}
	},

在页面加载时的 mounted() 生命周期钩子函数中,首先通过 localStorage.getItem() 方法获取之前存储在本地存储中的用户名和密码。

如果之前有存储的用户名和密码,则将其赋值给 loginForm 对象中的 usernamepassword 字段,并将 status 字段设置为 true,即默认选中记住密码。

如果之前没有存储的用户名和密码,则将 status 字段设置为 false,即默认不选中记住密码。

if (response.status === 200) {
	this.token = response.data.token;
	sessionStorage.setItem('token', this.token);
	// sessionStorage.setItem('token', this.token); 是将 this.token 的值存储到浏览器的会话存储(session storage)中,使用名为 "token" 的键。
	if (this.loginForm.status) {
		localStorage.setItem('username', this.loginForm.username);
		localStorage.setItem('password', this.loginForm.password);
	} else {
		localStorage.removeItem('username');
		localStorage.removeItem('password');
	}
	sessionStorage.setItem('redirectedFromLogin', 'true');
	// 你可以在其他页面或组件中通过调用 sessionStorage.getItem('redirectedFromLogin') 
	// 来获取存储在会话存储中的这个标记值。如果返回的值是 'true',则表示用户是从登录页面重定向而来。
	this.$router.push({ name: 'allProject' });

在登录成功后的 login() 方法中,首先判断用户是否选中记住密码的状态。如果是,则通过 localStorage.setItem() 方法将当前输入的用户名和密码存储到本地存储中,并在后续登录时自动填充。如果否,则通过 localStorage.removeItem() 方法从本地存储中移除之前存储的用户名和密码。

<el-form :model="loginForm" :rules="loginRules" ref="loginForm">
	<el-form-item prop="username">
			<el-input  v-model="loginForm.username" name="username" placeholder="体验账号:test" autocomplete="username">
			<template #prefix>
				<el-icon :size="24"><User /></el-icon>
			</template>
			</el-input>
	</el-form-item>		
	<el-form-item prop="password">
			<el-input  v-model="loginForm.password" name="password" placeholder="密码:test123456" autocomplete="current-password" type="password">
			<template #prefix>
				<el-icon :size="24"><Lock /></el-icon>
			</template>
			</el-input>
	</el-form-item>
	<el-form-item label="记住账号">
			<el-switch v-model="loginForm.status" />
	</el-form-item>		
	<el-form-item>
			<el-button type="primary" style="width: 100% ; height: 40px;" @click="login">登录</el-button>
	</el-form-item>	
	<div style="color: rgb(255, 255, 255);">没有账号?
		<span style="color: rgb(0, 85, 127); cursor: pointer; " @click="register">点击注册</span>
	</div>
</el-form>
  • :model="loginForm":这个属性将 loginForm 对象与表单进行双向数据绑定。通过该属性,输入框中的内容会自动更新到 loginForm 对象中,同时 loginForm 对象中的数据也可以自动填充到表单中。

  • :rules="loginRules":这个属性将 loginRules 对象中定义的表单验证规则应用于对应的表单项。通过设定验证规则,可以确保用户输入的数据符合预期的要求。

  • ref="loginForm":这个属性为表单指定一个唯一的引用名称,通过该引用可以在代码中获取到表单的实例对象。通过别的方式你可以触发表单的校验和提交操作等。

  • prop="username":这个属性指定了该表单项对应的字段名称,以便在验证规则中进行匹配,并在表单验证时进行检查。在示例中,prop="username" 表示该表单项验证规则在 loginRules 对象中使用 username 字段进行匹配。

loginRules: {
	username: [
	{ required: true, message: '请输入用户名', trigger: 'blur' },
	{ min: 3, max: 20, message: '用户名长度应在3-20个字符之间', trigger: 'blur' }
	],
	password: [
	{ required: true, message: '请输入密码', trigger: 'blur' },
	{ min: 6, max: 16, message: '密码长度应在6-16个字符之间', trigger: 'blur' }
	]
}
  1. loginRules 对象中定义了两个字段:usernamepassword,分别对应用户名和密码的验证规则数组。

  2. 在验证规则数组中,使用了 required 规则表示必填项,minmax 规则表示长度限制。

  3. trigger: 'blur',表示在输入框失去焦点时会触发相应字段的验证。当用户离开该字段的输入框时,系统会检查其内容是否符合验证规则,并展示相应的错误提示信息。

const valid = await this.$refs.loginForm.validate();
		// valid 变量将包含 validate() 方法返回的结果,可能是一个布尔值或其他类型的验证结果,用于判断表单是否通过验证。
		if (valid) { ...}

this.$refs.loginForm.validate() 是一个调用表单实例的 validate() 方法,用于对表单进行验证。

在Vue.js中,通过给表单设置 ref 属性,在组件中可以使用 $refs 对象来引用对应的DOM元素或组件实例。在这里,loginForm 是表单的 ref 属性值,通过 this.$refs.loginForm 可以获取到对应表单的实例对象。

通过调用 validate() 方法,可以触发对表单的验证。该方法会根据表单项的验证规则(通过 :rules 指定)对表单数据进行验证,并返回一个验证结果。

  • 如果表单项都通过了验证,则 validate() 方法返回 true,表示表单验证通过。

  • 如果表单项有任何一个未通过验证,则 validate() 方法返回 false,表示表单验证未通过。

一般情况下,validate() 方法会自动触发表单项的验证,并显示相应的错误提示信息。如果希望手动调用表单验证,可以使用 this.$refs.loginForm.validate() 进行验证,并根据返回值判断表单是否通过验证,从而执行相应的逻辑操作。

补充说明

localStorage和sessionStorage是浏览器提供的Web存储机制,用于在客户端存储数据。

  1. localStorage:

    • 数据持久性:localStorage中存储的数据没有过期时间,一直保留在客户端,除非主动删除或清除缓存。

    • 存储容量:一般来说,每个域名的localStorage存储容量为5MB左右。

    • 共享性:同源策略限制,localStorage只在同一个域名下共享。

  2. sessionStorage:

    • 数据会话性:sessionStorage中存储的数据只在当前会话(当前窗口或标签页)有效。当会话结束(窗口或标签页关闭)时,数据也会被清除。

    • 存储容量:类似localStorage,一般每个域名的sessionStorage存储容量也为5MB左右。

    • 共享性:同源策略限制,sessionStorage只在同一个域名下共享。

使用方法相似:

// 存储数据
localStorage.setItem('key', 'value');
sessionStorage.setItem('key', 'value');

// 获取数据
const valueFromLocalStorage = localStorage.getItem('key');
const valueFromSessionStorage = sessionStorage.getItem('key');

// 删除数据
localStorage.removeItem('key');
sessionStorage.removeItem('key');

// 清除所有数据
localStorage.clear();
sessionStorage.clear();

使用localStorage或sessionStorage可以方便地在客户端存储数据,用于简单的持久化需求或会话性存储需求,如保存用户的登录状态、本地缓存数据等。请注意,在敏感信息或重要数据的场景下,应该采用其他更安全的手段,如使用服务器端存储。