Vue笔记(八)

一、Pinia

(一)手动添加Piaia到Vue项目

1.安装Pinia:使用包管理器进行安装,在项目目录下运行 npm install pinia 或 yarn add pinia ,为项目引入Pinia状态管理库。
 
2.创建Pinia实例:在项目的JavaScript代码中,通常在 main.js 里创建Pinia实例。先从 pinia 库中导入 createPinia 函数,再调用该函数生成Pinia实例。这一实例用于管理整个项目的状态。
 
3.挂载Pinia实例到Vue应用:创建好Pinia实例后,通过 app.use(pinia) 方法将其挂载到Vue应用上,确保整个Vue项目都能使用Pinia进行状态管理。
 
4.使用注意事项:Pinia主要用于在Vue项目中进行状态管理,它能集中管理组件间共享的数据,提升开发效率和代码的可维护性。在使用时,要注意正确配置和引入相关依赖,按照规范的方式创建和使用状态、getters、actions等。

// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

// 创建Pinia实例
const pinia = createPinia()
const app = createApp(App)

// 挂载Pinia实例到Vue应用
app.use(pinia)
app.mount('#app')

 

(二)Pinia基本语法

 1.在Pinia中,通过 defineStore 函数来定义store。 defineStore 接收两个参数,第一个是store的唯一ID,第二个是一个配置对象。

import { defineStore } from 'pinia'

// 使用defineStore定义一个名为counterStore的store
const useCounterStore = defineStore('counter', {
  state: () => {
    return {
      count: 0
    }
  },
  getters: {
    doubleCount: (state) => state.count * 2
  },
  actions: {
    increment() {
      this.count++
    }
  }
})

 2.在组件中使用定义好的store,首先要引入对应的 useStore 函数,然后调用该函数获取store实例,进而访问和修改store中的状态、调用getters和actions。

<template>
  <div>
    <p>Count: {{ counterStore.count }}</p>
    <p>Double Count: {{ counterStore.doubleCount }}</p>
    <button @click="counterStore.increment">Increment</button>
  </div>
</template>

<script setup>
import { useCounterStore } from './stores/counterStore.js'

const counterStore = useCounterStore()
</script>
 

 3.state 是一个函数,返回一个对象,这个对象中的属性就是store的状态。状态是响应式的,当状态发生变化时,依赖它的组件会自动更新。

Getters(计算属性)

getters 用于对store中的状态进行计算和处理,类似于Vue组件中的计算属性。它接收 state 作为参数,可以返回一个计算后的值。

Actions(操作)

actions 用于定义可以修改状态的方法,也可以进行异步操作。在 actions 中可以通过 this 访问store的状态和其他方法。

(三)Pinia-action异步写法

 1.action不仅能同步修改状态,还可处理异步任务。通过在action中使用 async/await 语法,能让代码以更简洁、直观的方式处理异步操作,避免复杂的回调地狱。

2.与同步action的区别:同步action即时执行并修改状态,而异步action在异步操作完成后才更新状态。并且,异步action需处理异步操作可能出现的错误,增强代码的健壮性。
 
3.错误处理:使用 try...catch 块捕获异步操作中的错误,可在捕获错误后,根据不同错误类型进行相应处理

import { defineStore } from 'pinia'
// 模拟一个异步请求函数
const fetchData = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const success = Math.random() > 0.5;
            if (success) {
                resolve({ data: '异步请求成功的数据' });
            } else {
                reject(new Error('异步请求失败'));
            }
        }, 1000);
    });
}

const useAsyncStore = defineStore('asyncStore', {
    state: () => ({
        asyncData: null,
        error: null
    }),
    actions: {
        async fetchAsyncData() {
            try {
                const response = await fetchData();
                this.asyncData = response.data;
            } catch (error) {
                this.error = error.message;
            }
        }
    }
});

 在组件中使用这个带有异步action的store:

<template>
    <div>
        <button @click="fetchAsyncData">获取异步数据</button>
        <div v-if="asyncData">数据: {{ asyncData }}</div>
        <div v-if="error" style="color: red">错误: {{ error }}</div>
    </div>
</template>

<script setup>
import { useAsyncStore } from './stores/asyncStore.js';
const asyncStore = useAsyncStore();
const fetchAsyncData = async () => {
    await asyncStore.fetchAsyncData();
};
</script>
 

(四)Pinia-storeToRefs方法

1.  storeToRefs 是Pinia提供的工具函数,用于将store中的状态转换为响应式引用(refs)。在解构store状态时,能保持状态的响应性,避免丢失响应式导致组件无法随状态变化自动更新。
 
2.原理:它会遍历store的状态对象,为每个属性创建一个对应的ref对象,将这些ref对象包装在一个新对象中返回。在组件中解构这个返回对象,就能得到与store状态关联且保持响应性的ref。
 
3.与直接解构对比:直接解构store状态,如 const { count } = counterStore , count 失去响应性;而使用 storeToRefs ,即 const { count } = storeToRefs(counterStore) , count 是响应式的,组件能感知其变化并重新渲染。

// 定义store
import { defineStore } from 'pinia'
const useCounterStore = defineStore('counter', {
    state: () => ({
        count: 0
    }),
    actions: {
        increment() {
            this.count++
        }
    }
})
 
 

  
<!-- 组件中使用storeToRefs -->
<template>
    <div>
        <p>Count: {{ count }}</p>
        <button @click="counterStore.increment">Increment</button>
    </div>
</template>

<script setup>
import { useCounterStore, storeToRefs } from './stores/counterStore.js'
const counterStore = useCounterStore()
const { count } = storeToRefs(counterStore)
</script>

(五)Pinia持久化

 1.Pinia持久化是指将Pinia store中的状态数据保存下来,在页面刷新、关闭浏览器再打开等情况下,数据依然存在,不会丢失。这样可以提升用户体验,保持应用状态的连贯性。
 
2.实现方式:通常借助插件来实现,如 pinia-plugin-persistedstate 插件。该插件能将store状态存储在浏览器的本地存储(Local Storage)、会话存储(Session Storage)或其他存储机制中。

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import App from './App.vue'

const pinia = createPinia()
// 使用插件
pinia.use(piniaPluginPersistedstate)

const app = createApp(App)
app.use(pinia)
app.mount('#app')
import { defineStore } from 'pinia'

const useCounterStore = defineStore('counter', {
    state: () => ({
        count: 0
    }),
    // 开启持久化,默认存储在本地存储
    persist: true 
})
 

 

二、Vue3项目大事件

(一)项目介绍和pnpm创建项目

1.pnpm是一种快速、节省磁盘空间的包管理器。相比npm和yarn,它利用硬链接和内容寻址存储机制,能更高效地管理项目依赖,减少磁盘占用,提升安装速度,并且在多项目开发时能共享依赖,降低维护成本。
 
2.使用pnpm创建项目:需先确保已安装pnpm,可在官网下载对应安装包完成安装。在命令行中,使用 pnpm create vue@latest 命令来创建Vue项目。创建过程中,会提示设置项目名称、选择包管理器(此时默认选择pnpm)、是否使用TypeScript、是否安装Vue Router和Pinia等,可根据项目需求进行选择。
 
3.创建Vue项目:在命令行输入以下命令:

bash   
pnpm create vue@latest

4.根据提示依次输入:

项目名称(例如 big-events-project )。
 选择包管理器时,默认选择 pnpm 。
 对于是否使用TypeScript、是否安装Vue Router和Pinia等问题,按需求输入 y 或 n 进行选择。
例如,若希望项目使用TypeScript且安装Vue Router和Pinia,依次选择 y 确认。 安装完成后,进入项目目录:

bash   
cd big-events-project

(二)ESlint配合Prettier完成代码风格配置


ESlint与Prettier的作用

1. ESlint:主要用于发现并报告JavaScript代码中的问题,确保代码符合一定的规范和最佳实践,还能检测潜在的错误,提升代码质量。
 
2.Prettier:专注于代码格式化,统一代码风格,自动调整代码的排版,如缩进、换行、空格等,使团队成员编写的代码格式一致,增强代码可读性。
 
3.两者配合的优势:ESlint侧重代码质量和规范检查,Prettier负责格式化代码,两者结合能在保证代码质量的同时,让代码拥有统一美观的风格,减少因代码风格不一致引发的冲突,提高开发效率。

配置步骤

1.安装相关依赖:包括ESlint、Prettier以及它们的Vue插件和相关配置文件。
 
2.初始化ESlint配置:使用命令生成基础配置文件,根据项目需求修改规则。
 
3.配置Prettier:创建配置文件,设置格式化规则,让其与ESlint协同工作。
 
4.解决冲突:由于两者对代码的处理方式有重叠,需配置插件解决潜在冲突,确保代码检查和格式化顺利进行。

 
5.安装依赖:在项目根目录下执行命令:

bash   
pnpm add eslint eslint-plugin-vue @vue/eslint-config-prettier @vue/eslint-config-standard prettier eslint-plugin-prettier eslint-plugin-standard -D

初始化ESlint配置:运行命令生成配置文件,按提示选择配置选项:

bash   
npx eslint --init

创建Prettier配置文件:在项目根目录创建 .prettierrc.json 文件,添加如下配置:

 
json   
{
  "semi": true,
  "singleQuote": true,
  "trailingComma": "es5"
}
 


 
配置ESlint与Prettier协同工作:在 .eslintrc.js 文件中,添加或修改配置:
 


module.exports = {
  // 其他配置项...
  extends: ['plugin:vue/vue3-essential', '@vue/standard', '@vue/prettier']
};


 

(三)基于husky的代码检查工作流程

 

1.husky是一个Git钩子工具,它允许开发者在Git操作(如commit、push等)的特定阶段执行自定义脚本。在Vue3大事件项目中,利用husky可实现自动化的代码检查,保证提交到仓库的代码质量,避免低质量代码进入版本库。
 
2.基于husky的代码检查工作流原理:在项目中配置husky后,当执行Git操作时,husky会检测是否有对应的钩子脚本。若有,就执行该脚本,例如在 pre-commit 钩子中,可以运行ESlint等代码检查工具,检查暂存区的代码是否符合规范,若不符合则阻止提交,开发者需修改代码后再次提交。

配置步骤

1.安装husky:在项目中使用包管理器安装husky,它会在项目中创建必要的文件和目录结构,用于管理钩子脚本。
 
2.的钩子文件模板。
 
3.添加钩子脚本:在husky生成的钩子文件中,添加实际执行代码检查的脚本命令,通常是运行ESlint检查暂存区文件的命令。

安装husky:在项目根目录下,使用pnpm(若项目使用pnpm管理依赖)安装husky,命令如下:
 
bash
  
pnpm add husky -D

激活husky:运行以下命令激活husky,它会在项目根目录下创建 .husky 目录,并在其中生成一些初始文件:
 
bash
  
npx husky install

添加 pre-commit 钩子脚本:在 .husky 目录下找到 pre-commit 文件(若不存在可手动创建),添加以下内容:
 
bash
  
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

pnpm lint

假设项目中 pnpm lint 命令已配置为运行ESlint进行代码检查。这样,每次执行 git commit 时,husky会先运行 pnpm lint 命令检查代码,若代码检查不通过,提交会被阻止。

(四)调整目录

如果在某个组件(如 Home.vue )中原来引用 user.js 的方式是 import { getUserInfo } from '@/user.js'; ,移动文件后需要更新为 import { getUserInfo } from '@/api/user.js';

<!-- Home.vue -->
<template>
  <div>
    <!-- 组件内容 -->
  </div>
</template>

<script setup>
// 更新前引用路径
// import { getUserInfo } from '@/user.js';
// 更新后引用路径
import { getUserInfo } from '@/api/user.js';
// 后续使用getUserInfo函数的代码
</script>

 

(五)VueRouter4路由语法解析

 1.基本概念:VueRouter4是Vue.js官方的路由管理器,用于构建单页面应用(SPA)的路由系统。它能让页面在不刷新的情况下实现不同视图间的切换,提升用户体验。在Vue3大事件项目里,借助VueRouter4实现页面导航、组件展示切换等功能。

路由定义与配置

1. 创建路由实例:通过 createRouter 函数创建路由实例,该函数接收一个配置对象,包含路由模式(如 history 模式用于去除URL中的 # )、路由规则数组等关键信息。
 
2.定义路由规则:在路由规则数组中,每个对象代表一条路由规则。包含 path (路由路径)、 name (路由名称,方便引用)、 component (对应路径要渲染的组件)等属性。例如,定义首页路由, path 设为 / , component 指定为首页组件。

路由导航

1.声明式导航:在模板中使用 <router-link> 组件进行导航。通过设置 to 属性指定目标路由的路径或名称,点击该组件会触发路由切换,如 <router-link to="/home">首页</router-link> 。
 
2.编程式导航:在JavaScript代码中使用 router.push 或 router.replace 方法进行导航。 router.push 会向历史记录中添加一条新记录, router.replace 则替换当前历史记录,常用于登录成功后跳转到特定页面等场景。

动态路由

在路由路径中使用冒号( : )定义动态参数。比如 /user/:id ,其中 :id 就是动态参数,可用于根据不同用户ID展示对应信息。在组件中通过 $route.params 获取动态参数的值。

嵌套路由

用于构建具有层级结构的页面布局,在父路由的 children 属性中定义子路由规则。每个子路由同样包含 path 、 name 、 component 等属性,子路由的路径是相对于父路由的。
 
代码示例

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '@/views/Home.vue'
import UserDetail from '@/views/UserDetail.vue'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home
    },
    {
      path: '/user/:id',
      name: 'UserDetail',
      component: UserDetail
    },
    {
      path: '/parent',
      name: 'Parent',
      component: () => import('@/views/Parent.vue'),
      children: [
        {
          path: 'child',
          name: 'Child',
          component: () => import('@/views/Child.vue')
        }
      ]
    }
  ]
})

export default router
 
<!-- 在模板中使用声明式导航 -->
<template>
  <div>
    <router-link to="/">首页</router-link>
    <router-link to="/user/123">用户详情</router-link>
    <router-link to="/parent/child">子页面</router-link>
    <router-view></router-view>
  </div>
</template>
// 在组件中使用编程式导航
import { useRouter } from 'vue-router'

export default {
  setup() {
    const router = useRouter()
    const goToUserDetail = () => {
      router.push('/user/456')
    }
    return {
      goToUserDetail
    }
  }
}
 

 

// 在UserDetail.vue组件中获取动态参数
import { useRoute } from 'vue-router'

export default {
  setup() {
    const route = useRoute()
    const userId = route.params.id
    return {
      userId
    }
  }
}

 

<!-- 在组件模板中使用编程式导航对应的方法 -->
<template>
  <button @click="goToUserDetail">跳转到用户详情</button>
</template>

(六)ElementPlus按需引入

 ElementPlus是基于Vue 3的桌面端组件库。在项目中按需引入,而非全部引入,能有效减少项目体积,提升加载速度。按需引入借助 unplugin-vue-components 和 unplugin-auto-import 插件实现,它们可自动导入所需组件和函数,避免手动逐个引入的繁琐操作。

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

export default defineConfig({
  plugins: [
    vue(),
    AutoImport({
      resolvers: [ElementPlusResolver()]
    }),
    Components({
      resolvers: [ElementPlusResolver()]
    })
  ]
})

(七)Pinia构建用户仓库和持久化

 使用Pinia构建用户仓库,用于集中管理与用户相关的状态数据,如用户登录信息、权限等。通过持久化存储用户仓库数据,可在页面刷新或关闭后重新打开时,保留用户状态,提升用户体验。持久化借助 pinia-plugin-persistedstate 插件实现。

import { defineStore } from 'pinia'

const useUserStore = defineStore('user', {
  state: () => ({
    userInfo: null,
    token: ''
  }),
  actions: {
    setUserInfo(info) {
      this.userInfo = info
    },
    setToken(token) {
      this.token = token
    }
  },
  persist: true
})

export default useUserStore

 在组件中使用用户仓库:在组件的 <script setup> 中

import { useUserStore } from '@/stores/user.js'

const userStore = useUserStore()
const login = () => {
  const newUserInfo = { name: 'test', age: 18 }
  const newToken = '123456'
  userStore.setUserInfo(newUserInfo)
  userStore.setToken(newToken)
}

(八)数据交互--请求工具设计

// 创建request.js文件
import axios from 'axios'

// 创建axios实例
const request = axios.create({
  baseURL: 'https://api.example.com', // 基础URL
  timeout: 5000 // 超时时间
})

// 请求拦截器
request.interceptors.request.use(config => {
  // 在发送请求前做些什么,如添加token
  const token = localStorage.getItem('token')
  if (token) {
    config.headers['Authorization'] = `Bearer ${token}`
  }
  return config
}, error => {
  return Promise.reject(error)
})

// 响应拦截器
request.interceptors.response.use(response => {
  return response.data
}, error => {
  // 处理响应错误,如统一错误提示
  console.error('请求错误', error)
  return Promise.reject(error)
})

export default request

 

 

(九)路由的设计和配置

 

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '@/views/Home.vue'
import Login from '@/views/Login.vue'
import Dashboard from '@/views/Dashboard.vue'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home
    },
    {
      path: '/login',
      name: 'Login',
      component: Login
    },
    {
      path: '/dashboard',
      name: 'Dashboard',
      component: Dashboard,
      meta: { requiresAuth: true } // 需要登录才能访问
    }
  ]
})

// 全局前置守卫
router.beforeEach((to, from, next) => {
  const isLoggedIn = localStorage.getItem('token')
  if (to.meta.requiresAuth &&!isLoggedIn) {
    next('/login')
  } else {
    next()
  }
})

export default router

三、注册登录

(一)静态结构--基本切换

 搭建注册登录页面的静态结构,使用HTML和CSS构建页面布局,包含用户名、密码输入框,注册/登录按钮等元素。利用Vue的动态组件和状态管理实现注册和登录页面的基本切换功能,通过一个标志变量控制显示哪个页面,增强用户操作的便捷性。

<template>
  <div>
    <button @click="showLogin = true">登录</button>
    <button @click="showLogin = false">注册</button>
    <component :is="showLogin? 'LoginComponent' : 'RegisterComponent'"></component>
  </div>
</template>

<script setup>
import LoginComponent from '@/components/LoginComponent.vue'
import RegisterComponent from '@/components/RegisterComponent.vue'
import { ref } from 'vue'

const showLogin = ref(true)
</script>
<!-- LoginComponent.vue -->
<template>
  <div>
    <h2>登录</h2>
    <input type="text" placeholder="用户名">
    <input type="password" placeholder="密码">
    <button>登录</button>
  </div>
</template>
<!-- RegisterComponent.vue -->
<template>
  <div>
    <h2>注册</h2>
    <input type="text" placeholder="用户名">
    <input type="password" placeholder="密码">
    <input type="password" placeholder="确认密码">
    <button>注册</button>
  </div>
</template>

 

 

(二)表单校验

 为确保用户输入数据的准确性和合法性,对注册登录表单进行校验。使用 vee-validate 库,定义校验规则,如用户名长度、密码强度、确认密码一致性等。在表单提交时,根据校验结果决定是否允许提交,同时在页面上实时显示校验错误信息,引导用户正确输入。

# 安装vee-validate
npm install vee-validate@next
<template>
  <div>
    <h2>注册</h2>
    <Form @submit="onSubmit">
      <Field name="username" :rules="['required', 'alpha_num', 'between:3,20']">
        <template #default="{ field }">
          <input v-bind="field" type="text" placeholder="用户名">
          <ErrorMessage name="username" />
        </template>
      </Field>
      <Field name="password" :rules="['required', 'length:6,20']">
        <template #default="{ field }">
          <input v-bind="field" type="password" placeholder="密码">
          <ErrorMessage name="password" />
        </template>
      </Field>
      <Field name="confirmPassword" :rules="['required', 'confirmed:password']">
        <template #default="{ field }">
          <input v-bind="field" type="password" placeholder="确认密码">
          <ErrorMessage name="confirmPassword" />
        </template>
      </Field>
      <button type="submit">注册</button>
    </Form>
  </div>
</template>

<script setup>
import { Form, Field, ErrorMessage } from 'vee-validate'
import { ref } from 'vue'

const onSubmit = (values) => {
  console.log('提交的数据', values)
}
</script>

四、注册预校验--封装api实现注册功能

 在注册功能中,进行预校验,如检查用户名是否已存在,提高注册的成功率。封装注册API,将注册请求的逻辑封装在一个函数中,方便在不同组件中调用。在注册时,调用封装的API,处理注册成功或失败的情况,如保存用户信息、提示用户注册结果。

// api/user.js
import request from '@/utils/request'

export const register = (data) => {
  return request.post('/register', data)
}
 <template>
  <div>
    <h2>注册</h2>
    <Form @submit="handleSubmit">
      <Field name="username" :rules="['required', 'alpha_num', 'between:3,20']">
        <template #default="{ field }">
          <input v-bind="field" type="text" placeholder="用户名">
          <ErrorMessage name="username" />
        </template>
      </Field>
      <Field name="password" :rules="['required', 'length:6,20']">
        <template #default="{ field }">
          <input v-bind="field" type="password" placeholder="密码">
          <ErrorMessage name="password" />
        </template>
      </Field>
      <Field name="confirmPassword" :rules="['required', 'confirmed:password']">
        <template #default="{ field }">
          <input v-bind="field" type="password" placeholder="确认密码">
          <ErrorMessage name="confirmPassword" />
        </template>
      </Field>
      <button type="submit">注册</button>
    </Form>
  </div>
</template>

<script setup>
import { Form, Field, ErrorMessage } from 'vee-validate'
import { register } from '@/api/user.js'
import { ref } from 'vue'
import { useRouter } from 'vue-router'

const router = useRouter()
const handleSubmit = async (values) => {
  try {
    const response = await register(values)
    if (response.success) {
      // 注册成功,保存用户信息,跳转到首页
      localStorage.setItem('token', response.data.token)
      router.push('/')
    } else {
      console.error('注册失败', response.message)
    }
  } catch (error) {
    console.error('注册请求错误', error)
  }
}
</script>

五、登录校验和登录请求

 登录校验用于确保用户输入的登录信息准确无误,常见校验包括用户名或邮箱格式、密码长度等。登录请求则是将用户输入信息发送到后端服务器进行验证,验证通过后获取相关授权信息(如token),实现用户登录功能。校验和请求功能需协同,校验不通过时阻止请求发送,保障数据交互准确性。

<template>
  <div>
    <h2>登录</h2>
    <form @submit.prevent="handleLogin">
      <label for="username">用户名:</label>
      <input type="text" id="username" v-model="formData.username" required>
      <label for="password">密码:</label>
      <input type="password" id="password" v-model="formData.password" required minlength="6">
      <button type="submit">登录</button>
    </form>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { login } from '@/api/user.js';

const formData = ref({
  username: '',
  password: ''
});

const handleLogin = async () => {
  if (!formData.value.username ||!formData.value.password) {
    alert('用户名和密码不能为空');
    return;
  }
  try {
    const response = await login(formData.value);
    if (response.success) {
      localStorage.setItem('token', response.data.token);
      // 登录成功,跳转到首页
    } else {
      alert('登录失败,请检查用户名和密码');
    }
  } catch (error) {
    console.error('登录请求出错', error);
  }
};
</script>
 
 
// api/user.js
import request from '@/utils/request';

export const login = (data) => {
  return request.post('/login', data);
};
 

六、首页

(一)layout架子分析和登录访问拦截

首页 layout 架子分析旨在确定首页整体布局结构,包括导航栏、侧边栏、内容区域等组件的划分与布局方式。登录访问拦截是利用路由守卫机制,判断用户是否登录。未登录用户尝试访问首页等受保护页面时,将其重定向到登录页面,保障页面访问安全性,防止非法访问。

// router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import Home from '@/views/Home.vue';
import Login from '@/views/Login.vue';

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home,
      meta: { requiresAuth: true }
    },
    {
      path: '/login',
      name: 'Login',
      component: Login
    }
  ]
});

router.beforeEach((to, from, next) => {
  const token = localStorage.getItem('token');
  if (to.meta.requiresAuth &&!token) {
    next('/login');
  } else {
    next();
  }
});

export default router;
 
 
<!-- Home.vue -->
<template>
  <div class="home-layout">
    <header>导航栏</header>
    <aside>侧边栏</aside>
    <main>内容区域</main>
  </div>
</template>

<script setup>
// 首页组件逻辑
</script>

<style scoped>
.home-layout {
  display: flex;
}
header {
  width: 100%;
  height: 60px;
  background-color: #333;
  color: white;
  text-align: center;
}
aside {
  width: 200px;
  background-color: #f0f0f0;
}
main {
  flex: 1;
  background-color: #fff;
}
</style>

(二)用户基本信息渲染和退出

用户基本信息渲染是从存储(如本地存储、后端接口获取)中读取用户相关信息,如用户名、头像等,并在首页展示。退出功能则是清除用户登录状态相关信息(如token),使用户登出系统,同时更新页面状态,如隐藏用户信息展示区域、跳转到登录页面等

<template>
  <div class="home-layout">
    <header>
      <div v-if="userInfo">
        <span>{{ userInfo.username }}</span>
        <button @click="handleLogout">退出</button>
      </div>
    </header>
    <aside>侧边栏</aside>
    <main>内容区域</main>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { useRouter } from 'vue-router';

const userInfo = ref(JSON.parse(localStorage.getItem('userInfo')));
const router = useRouter();

const handleLogout = () => {
  localStorage.removeItem('token');
  localStorage.removeItem('userInfo');
  userInfo.value = null;
  router.push('/login');
};
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/968237.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何将3DMAX中的3D文件转换为AutoCAD中的2D图形?

大家好,今天我们来探讨一下如何将3DMAX中的3D文件转换为AutoCAD中的2D图形。无论是出于设计交流、施工准备还是其他实际需求,这种转换在工程设计领域都是一项非常实用的技能。接下来,我将为大家详细介绍几种实现这一转换的方法,帮助大家轻松跨越3D与2D设计之间的鸿沟。让我…

javaEE-11.javaScript入门

目录 一.什么是javaScript 二.快速实现 三.JS引入方式 1.行内引入: 2.内部引入: 3.外部引入: 四.基础语法 1.变量 变量命名规则: 2.数据类型 3.运算符 五.JS对象 1.数组 创建数组: 2.操作数组 3.函数 函数注意事项: 函数参数: 4.对象 1.使用字面量 创建对象:…

机器学习 - 进一步理解最大似然估计和高斯分布的关系

一、高斯分布得到的是一个概率吗&#xff1f; 高斯分布&#xff08;也称为正态分布&#xff09;描述的是随机变量在某范围内取值的概率分布情况。其概率密度函数&#xff08;PDF&#xff09;为&#xff1a; 其中&#xff0c;μ 是均值&#xff0c;σ 是标准差。 需要注意的是…

SaaS+AI应用架构:业务场景、智能体、大模型、知识库、传统工具系统

SaaSAI应用架构&#xff1a;业务场景、智能体、大模型、知识库、传统工具系统 大家好&#xff0c;我是汤师爷~ 在SaaS与AI应用的演进过程中&#xff0c;合理的架构设计至关重要。本节将详细介绍其五个核心层次&#xff1a; 业务场景层&#xff1a;发现和确定业务场景智能体层…

三、k8s pod详解

pod详解的相关的基础知识和初始化容器&#xff0c;以及私有化的镜像仓库*。 pod进阶&#xff1a;pod的状态&#xff0c;pod的探针 pod的详解&#xff1a; pod是k8s集群管理的最小单位&#xff0c;最小的资源组件&#xff0c;也是最小化运行容器的资源对象。 容器运行在pod里…

OpenCV 相机标定流程指南

OpenCV 相机标定流程指南 前置准备标定流程结果输出与验证建议源代码 OpenCV 相机标定流程指南 https://docs.opencv.org/4.x/dc/dbb/tutorial_py_calibration.html https://learnopencv.com/camera-calibration-using-opencv/ 前置准备 制作标定板&#xff1a;生成高精度棋…

【Windows】PowerShell 缓存区大小调节

PowerShell 缓存区大小调节 方式1 打开powershell 窗口属性调节方式2&#xff0c;修改 PowerShell 配置文件 方式1 打开powershell 窗口属性调节 打开 CMD&#xff08;按 Win R&#xff0c;输入 cmd&#xff09;。右键标题栏 → 选择 属性&#xff08;Properties&#xff09;…

127,【3】 buuctf [NPUCTF2020]ReadlezPHP

进入靶场 吓我一跳 查看源码 点击 审计 <?php// 定义一个名为 HelloPhp 的类&#xff0c;该类可能用于执行与日期格式化相关的操作 class HelloPhp {// 定义一个公共属性 $a&#xff0c;用于存储日期格式化的模板public $a;// 定义一个公共属性 $b&#xff0c;用于存储…

问题:通过策略模式+工厂模式+模板方法模式实现ifelse优化

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 示例&#xff1a;商城系统有会员系统&#xff0c;不同会员有不同优惠程度&#xff0c;普通会员不优惠&#xff1b;黄金会员打8折&#xff1b;白金会员优惠50元&#xff0c;再打7折&#xff1b; 问题描…

Android Studio2024版本安装环境SDK、Gradle配置

一、软件版本&#xff0c;安装包附上 &#x1f449;android-studio-2024.1.2.12-windows.exe&#x1f448; &#x1f449;百度网盘Android Studio安装包&#x1f448; &#xff08;若下载连链接失效可去百度网盘链接下载&#xff09; 二、软件安装过程 ​ ​ ​ 三、准备运行…

Leetcode - 149双周赛

目录 一、3438. 找到字符串中合法的相邻数字二、3439. 重新安排会议得到最多空余时间 I三、3440. 重新安排会议得到最多空余时间 II四、3441. 变成好标题的最少代价 一、3438. 找到字符串中合法的相邻数字 题目链接 本题有两个条件&#xff1a; 相邻数字互不相同两个数字的的…

使用 meshgrid函数绘制网格点坐标的原理与代码实现

使用 meshgrid 绘制网格点坐标的原理与代码实现 在 MATLAB 中&#xff0c;meshgrid 是一个常用函数&#xff0c;用于生成二维平面网格点的坐标矩阵。本文将详细介绍如何利用 meshgrid 函数生成的矩阵绘制网格点的坐标&#xff0c;并给出具体的代码实现和原理解析。 实现思路 …

【AI赋能】蓝耘智算平台实战指南:3步构建企业级DeepSeek智能助手

蓝耘智算平台实战指南&#xff1a;3步构建企业级DeepSeek智能助手 引言&#xff1a;AI大模型时代的算力革命 在2025年全球AI技术峰会上&#xff0c;DeepSeek-R1凭借其开源架构与实时推理能力&#xff0c;成为首个通过图灵测试的中文大模型。该模型在语言理解、跨模态交互等维…

Mac(m1)本地部署deepseek-R1模型

1. 下载安装ollama 直接下载软件&#xff0c;下载完成之后&#xff0c;安装即可&#xff0c;安装完成之后&#xff0c;命令行中可出现ollama命令 2. 在ollama官网查看需要下载的模型下载命令 1. 在官网查看deepseek对应的模型 2. 选择使用电脑配置的模型 3. copy 对应模型的安…

第七节 文件与流

基本的输入输出&#xff08;iostream&#xff09; C标准库提供了一组丰富的输入/输出功能&#xff0c;C的I/O发生在流中&#xff0c;流是字节序列。如果字节流是从设备&#xff08;键盘、磁盘驱动器、网络连接等&#xff09;流向内存&#xff0c;叫做输入操作。如果字节流是从…

网络安全溯源 思路 网络安全原理

网络安全背景 网络就是实现不同主机之间的通讯。网络出现之初利用TCP/IP协议簇的相关协议概念&#xff0c;已经满足了互连两台主机之间可以进行通讯的目的&#xff0c;虽然看似简简单单几句话&#xff0c;就描述了网络概念与网络出现的目的&#xff0c;但是为了真正实现两台主机…

内网ip网段记录

1.介绍 常见的内网IP段有&#xff1a; A类&#xff1a; 10.0.0.0/8 大型企业内部网络&#xff08;如 AWS、阿里云&#xff09; 10.0.0.0 - 10.255.255.255 B类&#xff1a;172.16.0.0/12 中型企业、学校 172.16.0.0 - 172.31.255.255 C类&#xff1a;192.168.0.0/16 家庭…

SQL Server 逻辑查询处理阶段及其处理顺序

在 SQL Server 中&#xff0c;查询的执行并不是按照我们编写的 SQL 语句的顺序进行的。相反&#xff0c;SQL Server 有自己的一套逻辑处理顺序&#xff0c;这个顺序决定了查询的执行方式和结果集的生成。了解这些处理阶段和顺序对于优化查询性能和调试复杂查询非常重要。 SQL …

四、OSG学习笔记-基础图元

前一章节&#xff1a; 三、OSG学习笔记-应用基础-CSDN博客https://blog.csdn.net/weixin_36323170/article/details/145514021 代码&#xff1a;CuiQingCheng/OsgStudy - Gitee.com 一、绘制盒子模型 下面一个简单的 demo #include<windows.h> #include<osg/Node&…

性格测评小程序03搭建用户管理

目录 1 创建数据源2 搭建后台3 开通权限4 搭建启用禁用功能最终效果总结 性格测评小程序我们期望是用户先进行注册&#xff0c;注册之后使用测评功能。这样方便留存用户的联系信息&#xff0c;日后还可以推送对应的相关活动促进应用的活跃。实现这个功能我们要先创建数据源&…