初学vue3+node搭建前后分离项目框架

为了扩展自己技术层面的知识,我逐步学习vue3+node相关技术依赖,逐步搭建前后分离项目框架。废话不多讲,开始我的学习历程

一、技术架构

1.前端

vue3+ElementPlus+socket.io

2.后端

node+express+socket.io+mongoDB

二、前端搭建

1.使用vue-cli去创建vue3项目基础架构(小白可以自学其他博主的搭建方式)

本人使用的语法糖是组合式api,根据个人自己习惯去写

2.做自己的项目肯定从登录开始,上才艺。

(1)做好路由守卫

  • 看一下接口返回的路由数据结构

小常识:

        一般路由会有鉴权;登录后会返回相应的路由此时要做缓存处理可以放到store里面也可以放到localStore中;放在Store中要做好刷新后数据丢失解决的方案,本人建议两者都用,刷新后可以使用localStorage进行补充到Store中。

  • 由于考虑到后期要增加菜单所以加入了动态菜单想法,请看关键代码

router/index.ts

import { RouteRecordRaw, createRouter, createWebHashHistory } from 'vue-router';
import { parse } from "zipson";
const modules = import.meta.glob("../views/**/**.vue"); // 用于引入动态路由
const { menus } = parse(localStorage.getItem("SPTIH_verify_info") || "") || { menus: [] };
const routes: Array<RouteRecordRaw> = [
    {
        path: "/404",
        name: "404",
        component: () => import("@/views/404/404.vue")
    },
    {
        path: "/login",
        name: "login",
        component: () => import("@/views/login/login.vue")
    },
    {
        path: "/demo",
        name: 'demo',
        component: () => import("@/views/demo/demo.vue")
    }
]

const router = createRouter({
    history: createWebHashHistory(),
    routes
})
let HomeRouter = {
    path: "/",
    redirect: '/home',
    component: () => import('@/views/page/index.vue'),
    children: [
        {
            path: '/home',
            name: "home",// 与页面name一致,则可渲染同名页面
            component: () => import('@/views/home/home.vue'),
        },
    ]
}
// 递归生成菜单路由
function ReconstructionRoutes(MenusRoutes: any) {
    let list: any = []
    if (MenusRoutes.length > 0) {
        MenusRoutes.forEach((item: any) => {
            let routeItem = {
                path: item.menuPath,
                name: item.code,
                // 使用/src是因为在引入动态路由的时候webpack或其他编译工具不能识别
                component: modules[`../views/${item.menuComponent}.vue`], // 引入动态路由写法
                children: []
            }
            if (item.children && item.children.length > 0) {
                routeItem.children = ReconstructionRoutes(routeItem.children)
            }
            list.push(routeItem)

        });
    }
    return list
}
// 重构路由
menus.forEach((menuItem: any) => {
    let routeItem: any = {
        path: menuItem.menuPath,
        name: menuItem.code,
        // 使用/src是因为在引入动态路由的时候webpack或其他编译工具不能识别
        component: modules[`../views/${menuItem.menuComponent}.vue`], // 引入动态路由写法
        children: []
    }
    if (menuItem.children && menuItem.children.length > 0) {
        routeItem.children = ReconstructionRoutes(routeItem.children)
    }
    HomeRouter.children.push(routeItem)
})
router.addRoute(HomeRouter)
export default router;

(2)菜单展示效果图

3.有同学要问你的菜单icon显示问题

        这个菜单icon想让他动态起来也不是很难,从数据结构中不难看出,本座存储的是字符串;为什么是字符串,给你们5分钟思考时间。

        菜单路由icon要求的是icon对象实例,将实例存储到数据库中也不是不行,首先是要转换成字符串在存储,用的时候在转成对象实例;在开发过程中耗费时间精力比较大,是可以提现你的开发能力,但是从时间成本上浪费了你大量时间。因此,存储成字符串让icon跟着项目走,就算换依赖或者在线地址也可以同步匹配。

  • 易用性:通俗易懂存储的图标标识,方便存储和使用
  • 解构性:菜单icon来源可以是在线也可以是ElementPlus-icon的可以将图标都导出到js文                 中,根据字符串比配对应的icon实例,存储到菜单路由数据结构中。
  • 原因解释完上关键代码,一个是导入icon的public.ts文件,本座将它放到了store中项目启动进行缓存也不用担心icon丢失;一个是动态渲染SideMenu.vue
import * as Icons from '@element-plus/icons-vue'
import { defineStore } from 'pinia'
// 定义类型
interface IconsStoreType {
    icons: any
}

export const useIconsStore = defineStore({
    id: 'iconStore',
    state: (): IconsStoreType => ({
        icons: Icons
    }),
})
<template>
  <el-menu
    class="el-menu-vertical-demo my-side-menu main_theme"
    background-color="#545c64"
    text-color="#fff"
    active-text-color="#ffd04b"
    :default-active="vmData.defActive"
    :collapse="isCollapse"
    :router="true"
    @open="handleOpen"
    @close="handleClose"
  >
    <template v-for="item in vmData.menuList" :key="item.menuPath">
      <el-sub-menu
        v-if="item.menuChild && item.menuChild.length > 0"
        :index="item.menuPath"
        :router="true"
      >
        <template #title>
          <template v-if="item.icon">
            <el-icon>
              <component :is="item.icon" style="width: 16px; height: 16px" />
            </el-icon>
            <span>{{ item.name }}</span>
          </template>
        </template>
      </el-sub-menu>
      <el-menu-item v-else :index="item.menuPath">
        <template v-if="item.icon">
          <el-icon>
            <component :is="item.icon" style="width: 16px; height: 16px" />
          </el-icon>
        </template>
        <template #title> {{ item.name }}</template>
      </el-menu-item>
    </template>
  </el-menu>
</template>

<script setup lang="ts">
import { ref, shallowRef } from "vue";
import { useRouter } from "vue-router";
import { icons } from "@/store/modules";
import { parse } from "zipson";
const router = useRouter();
const MenusIcons: any = icons;
const { menus } = parse(localStorage.getItem("SPTIH_verify_info") || "");
const vmData = ref({
  defActive: "",
  menuList: [
    {
      menuChild: [],
      menuPath: "",
      icon: "",
      name: "",
    },
  ],
});
defineProps<{
  isCollapse: Boolean;
}>();

// 初始化菜单
function initMenus() {
  if (menus.length > 0) {
    for (let i = 0; i < menus.length; i++) {
      for (let key in MenusIcons.value) {
        if (menus[i].icon && menus[i].icon == key) {
          menus[i].icon = shallowRef(MenusIcons.value[key]);
        }
      }
    }
    vmData.value.defActive = menus[0].menuPath;
    router.push(vmData.value.defActive);
  }
  vmData.value.menuList = menus;
}
initMenus();

// 打开菜单
function handleOpen() {}
// 关闭菜单
function handleClose() {}

/**************************************接口*************************************/
</script>

4.总结

前端这部分基础目录框架及package.json附到结尾,给同学们看一下,可以先创建基础框架练习,将数据写成mock形式把菜单等效果做出来。本座还没想好如何讲解后端,待我想想。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值