Vue3 项目实战总结:路由、状态管理与工程化核心知识点

20 阅读8分钟

作为一名大三前端学习者,在开发 Vue3 实战项目的过程中,近段时间我重点深耕了 Vue3 框架核心能力——路由、状态管理、接口封装,这些都是企业级项目必备的技能,也是实习面试的高频考点。本文整合这段时间的学习与实战经验,结合通用 Vue3 项目场景,分享最实用的 Vue3 知识点,拒绝空谈理论,全是能直接落地的实战内容,适合前端新手参考学习。

一、Vue Router4 实战:路由守卫与懒加载(项目的“导航核心”)

在 Vue3 项目中,路由是页面跳转的核心,既要控制登录权限,也要优化首屏加载速度,这就用到了路由守卫和懒加载两个关键功能,适用于各类中后台、展示类项目。

1. 核心知识点

  • 路由懒加载:将路由组件按需加载,减小首屏打包体积,提升加载速度;
  • 路由守卫:控制路由跳转权限,实现登录拦截、页面访问限制,最常用全局前置守卫;
  • 路由传参:params、query 两种方式,适配不同场景(如传递详情ID、查询条件)。

2. 项目实战代码

(1)路由懒加载配置(必用)

之前直接导入所有路由组件,首屏加载很慢,改用懒加载后,首屏加载速度提升了40%,通用项目代码如下:

dex.js
import { createRouter, createWebHistory } from 'vue-router';

// 路由懒加载:访问对应路由时才加载组件
const Login = () => import('@/views/Login.vue');
const Home = () => import('@/views/Home.vue');
const ListPage = () => import('@/views/ListPage.vue'); // 列表页
const DetailPage = () => import('@/views/DetailPage.vue'); // 详情页

const routes = [
  { path: '/', redirect: '/home' },
  { path: '/login', component: Login },
  { path: '/home', component: Home },
  { path: '/list', component: ListPage },
  { path: '/detail/:id', component: DetailPage } // 动态路由传参
];

const router = createRouter({
  history: createWebHistory(),
  routes
});

export default router;

(2)全局前置守卫(登录拦截)

通用项目要求:未登录用户不能访问首页、列表等核心页面,必须跳转至登录页,代码如下:

// router/index.js
router.beforeEach((to, from, next) => {
  // 从localStorage获取token,判断是否登录
  const token = localStorage.getItem('token');
  // 白名单:不需要登录就能访问的页面(只有登录页)
  const whiteList = ['/login'];
  if (whiteList.includes(to.path)) {
    next(); // 放行
  } else {
    // 已登录放行,未登录跳登录页
    token ? next() : next('/login');
  }
});

3. 项目实战场景与面试点

  • 实战场景:访问详情页时,通过动态路由传递数据ID,在详情页接收并请求对应数据;登录后跳转至首页,未登录拦截所有核心页面,适用于各类需要权限控制的项目。
  • 面试高频问法:① 路由懒加载的实现方式和好处?② 如何实现Vue路由的登录拦截?③ 动态路由传参和query传参的区别?
  • 面试答案:懒加载用import动态导入,好处是减小首屏体积、提升加载速度;登录拦截用全局前置守卫,判断token是否存在;动态路由传参(params)参数在URL路径中,query传参在URL查询字符串中,前者刷新页面参数不丢失(需配置路由),后者会丢失。

二、Pinia 实战:Vue3 状态管理的最优解

在 Vue3 项目中,用户信息、全局主题、加载状态等需要在多个组件间共享的数据,用Pinia管理最简洁高效。之前尝试过Vuex,对比后发现Pinia更轻量、更适合Vue3,也更符合面试趋势,适用于各类需要全局状态共享的项目。

1. 核心知识点

  • Pinia 核心:defineStore(定义仓库)、state(状态)、actions(同步/异步操作)、getters(计算属性);
  • 优势:无mutations(简化代码)、天生支持TS、体积小(约1KB)、模块化无需额外配置;
  • 与Vuex区别:Pinia去掉mutations,actions可写异步,代码更简洁,TS支持更好,无需modules拆分模块。

2. 项目实战代码

(1)定义Pinia仓库(用户信息仓库)

// store/userStore.js
import { defineStore } from 'pinia';

// 定义仓库,第一个参数是仓库唯一标识,第二个参数是配置
export const useUserStore = defineStore('user', {
  state: () => ({
    token: localStorage.getItem('token') || '', // 用户token
    username: localStorage.getItem('username') || '', // 用户名
    role: localStorage.getItem('role') || 'user' // 用户角色
  }),
  actions: {
    // 同步设置用户信息
    setUserInfo(userInfo) {
      this.token = userInfo.token;
      this.username = userInfo.username;
      this.role = userInfo.role;
      // 持久化存储到localStorage,防止页面刷新丢失
      localStorage.setItem('token', userInfo.token);
      localStorage.setItem('username', userInfo.username);
      localStorage.setItem('role', userInfo.role);
    },
    // 异步退出登录(清除状态)
    async logout() {
      // 调用退出登录接口
      await $axios.post('/api/user/logout');
      // 清除仓库状态
      this.token = '';
      this.username = '';
      this.role = 'user';
      // 清除localStorage
      localStorage.removeItem('token');
      localStorage.removeItem('username');
      localStorage.removeItem('role');
    }
  },
  getters: {
    // 计算属性:判断是否为管理员
    isAdmin() {
      return this.role === 'admin';
    }
  }
});

(2)组件中使用Pinia

<template>
  <div class="user-info">
    <span>{{ username }}</span>
    <button @click="handleLogout" v-if="isAdmin">退出登录</button>
  </div>
</template>

<script setup>
import { useUserStore } from '@/store/userStore';
import { useRouter } from 'vue-router';

// 获取仓库实例
const userStore = useUserStore();
const router = useRouter();
// 解构仓库状态和方法(可直接使用)
const { username, isAdmin, logout } = userStore;

// 退出登录
const handleLogout = () => {
  logout();
  router.push('/login');
};
</script>

3. 项目实战场景与面试点

  • 实战场景:登录后存储用户信息,在导航栏显示用户名;管理员角色可查看更多操作按钮;退出登录时清除状态并跳转登录页,适用于各类有用户权限管理的项目。
  • 面试高频问法:① Pinia和Vuex的区别?② Pinia的actions为什么能写异步?③ 如何实现Pinia状态持久化?
  • 面试答案:Pinia无mutations、支持TS、体积小,Vuex有mutations、TS支持差、需modules;Pinia的actions无需像Vuex那样区分同步异步,可直接写async/await;状态持久化可结合localStorage(手动存储)或pinia-plugin-persistedstate插件。

三、Vue3 工程化:axios封装 + 跨域解决(项目的“接口核心”)

Vue3 项目中通常需要大量接口请求:登录、获取列表数据、上传文件、提交表单等,直接使用axios会导致代码冗余,跨域问题也会影响开发效率,因此封装axios和配置跨域是工程化的关键一步,适用于所有需要接口联调的Vue3项目。

1. 核心知识点

  • axios封装:统一配置baseURL、请求超时时间,通过请求/响应拦截器统一处理token、错误提示;
  • 跨域解决:开发环境用Vite配置proxy代理,生产环境由后端配置CORS;
  • 拦截器作用:请求拦截器添加token、统一请求头;响应拦截器统一处理错误、解析数据。

2. 项目实战代码

(1)axios封装

// api/request.js
import axios from 'axios';
import { ElMessage } from 'element-plus';
import router from '@/router';

// 创建axios实例
const service = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL, // 环境变量(区分开发/生产环境)
  timeout: 5000, // 超时时间
  headers: {
    'Content-Type': 'application/json'
  }
});

// 请求拦截器:添加token
service.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('token');
    if (token) {
      // 给所有请求添加Authorization请求头
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => {
    ElMessage.error('请求发起失败,请重试');
    return Promise.reject(error);
  }
);

// 响应拦截器:统一处理错误、解析数据
service.interceptors.response.use(
  (response) => {
    // 只返回响应体中的data部分,简化使用
    return response.data;
  },
  (error) => {
    // 统一错误提示
    const msg = error.response?.data?.msg || '请求失败,请稍后重试';
    ElMessage.error(msg);
    // 401未授权:token过期或未登录,跳登录页并清除状态
    if (error.response?.status === 401) {
      localStorage.removeItem('token');
      router.push('/login');
    }
    return Promise.reject(error);
  }
);

export default service;

(2)接口统一管理

将所有接口集中管理,便于维护,通用项目代码如下:

// api/list.js(列表相关接口)
import service from './request';

// 获取列表数据
export const getListData = (params) => {
  return service({
    method: 'GET',
    url: '/api/list',
    params // 分页、查询条件等参数
  });
};

// 上传文件
export const uploadFile = (data) => {
  return service({
    method: 'POST',
    url: '/api/upload',
    data,
    headers: {
      'Content-Type': 'multipart/form-data' // 文件上传需设置
    }
  });
};

// 组件中使用接口
import { getListData } from '@/api/list';
const getList = async () => {
  const res = await getListData({ page: 1, size: 10 });
  tableData.value = res.list;
};

(3)Vite配置跨域代理(开发环境)

// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src') // 路径别名,@指向src
    }
  },
  // 跨域代理配置
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000', // 后端接口地址
        changeOrigin: true, // 开启跨域
        rewrite: (path) => path.replace(/^/api/, '') // 去掉/api前缀
      }
    }
  }
});

3. 项目实战场景与面试点

  • 实战场景:所有接口请求统一使用封装后的axios,无需重复配置baseURL和token;开发环境通过代理解决跨域,顺利联调后端接口;文件上传、接口错误统一处理,提升开发效率,适用于各类需要接口联调的Vue3项目。
  • 面试高频问法:① 为什么要封装axios?② Vue项目中遇到跨域怎么解决?③ axios拦截器的作用是什么?
  • 面试答案:封装axios可以统一配置、减少重复代码、统一处理错误和token;跨域开发环境用proxy代理,生产环境后端配置CORS;请求拦截器添加token和请求头,响应拦截器统一处理错误、解析数据,避免每个请求都写重复逻辑。

四、Vue3 项目梳理:从功能到面试亮点

这5天的学习,核心是将Vue3的路由、状态管理、工程化能力落地到实战项目中,同时我也重新梳理了自己的Vue3项目,为后续实习面试做准备,整理了通用项目的核心亮点和难点解决方案,供大家参考。

1. 项目核心信息(面试必背)

  • 项目名称:Vue3 中后台实战项目
  • 技术栈:Vue3 + Vite + Pinia + Element Plus + axios + ECharts
  • 核心功能:用户登录、列表展示、详情查看、文件上传、数据查询、数据统计

2. 项目难点与解决方案(面试核心素材)

  • 难点1:跨域问题 → 解决方案:Vite配置proxy代理,生产环境后端配置CORS;
  • 难点2:用户状态共享 → 解决方案:用Pinia管理全局状态,结合localStorage实现持久化;
  • 难点3:接口请求冗余、错误处理繁琐 → 解决方案:封装axios,使用请求/响应拦截器统一处理;
  • 难点4:首屏加载慢 → 解决方案:路由懒加载,减小首屏打包体积。

3. 面试可突出的亮点

  • 工程化思维:封装axios、统一接口管理,规范项目结构,提升代码可维护性;
  • 问题解决能力:遇到跨域、状态管理等问题,能快速找到解决方案并落地;
  • 框架熟练度:熟练使用Vue3、Pinia、Vue Router等核心技术,贴合企业开发需求。

总结

这段学习,让我深刻体会到:Vue3框架的核心能力,最终都是为了“高效开发、规范项目、提升体验”。路由控制页面导航,Pinia管理全局状态,axios封装简化接口请求,这些技能不仅能让项目更规范,更是前端实习面试的“硬通货”。

作为大三前端学习者,我明白“实战是最好的老师”,把这些知识点落地到自己的Vue3实战项目中,不仅巩固了基础,也积累了宝贵的面试素材。后续我会继续学习Vue3性能优化、TypeScript等加分项,持续输出实战总结,为实习面试做好充分准备,也希望能帮到和我一样正在努力的前端新手。

后续会持续更新Vue3性能优化、项目踩坑、前端面试题相关内容,欢迎关注~