重新新建一个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);
相关推荐
崔庆才丨静觅34 分钟前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60611 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了1 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅2 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅2 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅2 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment2 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅3 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊3 小时前
jwt介绍
前端
爱敲代码的小鱼3 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax