重新新建一个vue3项目

从头搭建一个vue3项目 记录一下自己的遇到的问题以及记录一下自己的学到的东西

栽一棵树最好的时间是现在。

PS:怕作者哪天把文章删了,所以就把自己写的步骤都copy下来了。

参考文章:Vue3+ Vite + Element-Plus + TypeScript 从0到1搭建企业级后台管理系统

新建项目

js 复制代码
//查询node版本 
node -v  // 16.17.0
npm -v //8.15.0

Axios

遇到的问题

解决办法:

在tsconfig.json中加入

js 复制代码
{
  "compilerOptions": {
    // 加入下面这三行代码是为了使import
    "module": "esnext",
    "target": "es5",  // 或其他目标版本
    "outDir": "./dist",
    }
}

路由vue-router

安装vue-router

js 复制代码
npm install vue-router@next

创建路由

新建 src/router/index.ts

js 复制代码
// src/router/index.ts
import { createRouter, createWebHashHistory, RouteRecordRaw ,createWebHistory } from 'vue-router';

export const Layout = () => import('@/layout/index.vue');

// 静态路由
export const constantRoutes: RouteRecordRaw[] = [
  {
    path: '/redirect',
    component: Layout,
    meta: { hidden: true },
    children: [
      {
        path: '/redirect/:path(.*)',
        component: () => import('@/views/redirect/index.vue')
      }
    ]
  },

  {
    path: '/login',
    component: () => import('@/views/login/index.vue'),
    meta: { hidden: true }
  },

  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [
      {
        path: 'dashboard',
        component: () => import('@/views/dashboard/index.vue'),
        name: 'Dashboard',
        meta: { title: 'dashboard', icon: 'homepage', affix: true }
      }
    ]
  }
];

/**
 * 创建路由
 */
const router = createRouter({
  history: createWebHistory(),
  routes: constantRoutes as RouteRecordRaw[],
  // 刷新时,滚动条位置还原
  scrollBehavior: () => ({ left: 0, top: 0 })
});

/**
 * 重置路由
 */
export function resetRouter() {
  router.replace({ path: '/login' });
  location.reload();
}

export default router;

全局注册

js 复制代码
//引入路由
import router from "@/router";
import App from './App.vue'
createApp(App).use(router).mount('#app')

路由守卫

js 复制代码
//路由守卫
import type { RouteRecordRaw } from "vue-router";
import NProgress from "@/utils/nprogress";
import router from "@/router";
import { useUserStoreHook, useUserStore } from "@/store";
// 储存键值对的地方
import { ROLE_ROOT } from "@/constants";

export function setupPermission() {
  const whiteList = ["/login"]; // 无需登录的页面

  router.beforeEach(async (to, from, next) => {
    NProgress.start();
    console.log(to);
    try {
      // 在这里这两个的区别是什么啊 啊啊啊
      console.log("===", useUserStoreHook().isLoggedIn);
      console.log("===", useUserStore().isLoggedIn);
      console.log(whiteList.includes(to.path));

      // 使用 store 暴露的登录态,便于后续扩展(如基于过期时间等)
      const isLoggedIn = useUserStore().isLoggedIn;
      // 未登录处理
      if (!isLoggedIn) {
        if (whiteList.includes(to.path)) {
          next();
        } else {
          next(`/login?redirect=${encodeURIComponent(to.fullPath)}`);
          NProgress.done();
        }
        return;
      }

      // 已登录且访问登录页,重定向到首页
      if (to.path === "/login") {
        next({ path: "/" });
        return;
      }

      // 已登录用户的正常访问
      // const permissionStore = usePermissionStore();
      // const userStore = useUserStore();

      // 路由未生成则生成
      //   if (!permissionStore.isDynamicRoutesGenerated) {
      //     if (!userStore.userInfo?.roles?.length) {
      //       await userStore.getUserInfo();
      //     }

      //     const dynamicRoutes = await permissionStore.generateRoutes();
      //     dynamicRoutes.forEach((route: RouteRecordRaw) => {
      //       router.addRoute(route);
      //     });

      //     next({ ...to, replace: true });
      //     return;
      //   }

      // 检查路由是否存在
      if (to.matched.length === 0) {
        next("/404");
        return;
      }

      // 设置页面标题
      const title = (to.params.title as string) || (to.query.title as string);
      if (title) {
        to.meta.title = title;
      }

      next();
    } catch (error) {
      console.error("❌ Route guard error:", error);
      // 出错时清理状态并重定向到登录页
      //   try {
      //     await useUserStore().resetAllState();
      //   } catch (resetError) {
      //     console.error("❌ Failed to reset user state:", resetError);
      //   }
      next("/login");
      NProgress.done();
    }
  });

  router.afterEach(() => {
    NProgress.done();
  });
}
setupPermission();
/** 判断是否有权限 */
export function hasAuth(
  value: string | string[],
  type: "button" | "role" = "button"
) {
  const { roles, perms } = useUserStore().userInfo;

  // 超级管理员 拥有所有权限
  if (type === "button" && roles.includes(ROLE_ROOT)) {
    return true;
  }

  const auths = type === "button" ? perms : roles;
  return typeof value === "string"
    ? auths.includes(value)
    : value.some((perm) => auths.includes(perm));
}

自定义指令

自定义指令

案例

新建一个directive / highlight /index.ts

js 复制代码
// 自定义指令
import type { Directive } from "vue";
export const highlight: Directive = {
  mounted: (el) =>{
    console.log('elel',el);
    
    return  el.classList.add("is-highlight")
  },
};

新建 directive/ index.ts

js 复制代码
import { App } from "vue";
import { highlight } from "./permission";

// 注册一个全局自定义指令 `v-highlight`
export function setDirectiveFn(app: App<Element>) {
  app.directive("highlight", highlight);
}

main.ts中注册

js 复制代码
import {setDirectiveFn} from "@/directive";
setDirectiveFn(app);
相关推荐
岁月宁静5 小时前
深度定制:在 Vue 3.5 应用中集成流式 AI 写作助手的实践
前端·vue.js·人工智能
心易行者6 小时前
10天!前端用coze,后端用Trae IDE+Claude Code从0开始构建到平台上线
前端
saadiya~6 小时前
ECharts 实时数据平滑更新实践(含 WebSocket 模拟)
前端·javascript·echarts
fruge7 小时前
前端三驾马车(HTML/CSS/JS)核心概念深度解析
前端·css·html
百锦再7 小时前
Vue Scoped样式混淆问题详解与解决方案
java·前端·javascript·数据库·vue.js·学习·.net
烛阴7 小时前
Lua 模块的完整入门指南
前端·lua
浪里行舟8 小时前
国产OCR双雄对决?PaddleOCR-VL与DeepSeek-OCR全面解析
前端·后端
znhy@1238 小时前
CSS易忘属性
前端·css
瓜瓜怪兽亚8 小时前
前端基础知识---Ajax
前端·javascript·ajax
AI智能研究院8 小时前
(四)从零学 React Props:数据传递 + 实战案例 + 避坑指南
前端·javascript·react.js