vue3+vite5+typescript+antDesign实战详解附代码,框架可以直接跑起来用,5分钟搞定一个自定义主题风格和登录权限的万能的SASS系统

webpack用了那么多年,试试vite的效果吧,亲测丝滑!

这里说下为啥用vue3不去用react,因为我们小组统一了框架语言用的vue(其他人不懂react没法接手代码)所以用了vue,这里我觉得给技术追求着画了一个牢笼,管理层生怕你强大,变得啥都懂,别人不懂,支棱起来了,哈哈哈哈,关于react框架的,后面有空咱们再写一篇哈,这里先写vue的。

关于typescript,大家知道,不管vue还是react,都必须要有,这里已经成为了两大主流框架的必备语言,可以减少在代码编写过程中一些潜在的bug。

关于为啥选择antDesign库,第一,免费,第二,团队一直在维护更新,第三,兼容性越来越好了,不管是react还是vue都可以用,当然react用得多一些。

1.搭建开发环境

我本人用的是node16版本,vite对node版本有要求,不能低于14。 初始化项目

js 复制代码
npm init vite@latest vite-vue3-ts

第一步,选择vue,

第二步,选择TypeScript

安装成功!找到这个目录cd vite-vue3-ts,并且安装依赖npm install,记住,node版本要大于14,不然跑不起来哈,我这里用的16

最后,运行项目npm run dev,很丝滑的看到了页面

2.引入# Ant Design of Vue组件库,开始编写万能的sass系统

官网地址:antdv.com/components/...

js 复制代码
npm install unplugin-vue-components -D

项目目录结构如下图,接下来我们开始一步一步搬砖了,package.json安装如下:

js 复制代码
{
  "name": "vite-vue3-ts",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "prettier": "^3.2.5",
    "sass": "^1.70.0",
    "vue": "^3.3.11",
    "vue-router": "^4.2.5"
  },
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^6.21.0",
    "@typescript-eslint/parser": "^6.21.0",
    "@vitejs/plugin-vue": "^4.5.2",
    "eslint": "^8.56.0",
    "eslint-config-prettier": "^9.1.0",
    "eslint-plugin-prettier": "^5.1.3",
    "eslint-plugin-vue": "^9.21.1",
    "typescript": "^5.2.2",
    "unplugin-vue-components": "^0.26.0",
    "vite": "^5.0.8",
    "vue-tsc": "^1.8.25"
  }
}

项目目录结构图如下:

vite.config.js文件这里进行vite相关的配置

js 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
import Components from 'unplugin-vue-components/vite';
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue(),
    Components({
      resolvers: [
        AntDesignVueResolver({
          importStyle: false, // css in js
        }),
      ],
    }),],
  resolve: {
    alias: {
      '@': resolve('./src'),
    },
    extensions: [ '.ts','.vue','.js']
  },
  base: './', // 打包路径
  server: {
    port: 4000, // 服务端口号
    open: true, // 服务启动时是否自动打开浏览器
    cors: true // 允许跨域
  }
})

tsconfig.json文件配置ts规范代码

js 复制代码
{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "skipLibCheck": true,

    "moduleResolution": "node",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "preserve",

    /* Linting */
    "strict": false,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "noFallthroughCasesInSwitch": false,
    "allowSyntheticDefaultImports": true,
    "baseUrl": "./",
    "paths": {
      "@": ["src"],
      "@/*": ["src/*"]
    },
  },
  "include": [ "src/**/*.ts","src/**/*.d.ts","src/**/*.tsx","src/**/*.vue","src/*.vue","src/**/*.js"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

vite-env.d.ts文件代码加入如下,方便识别vue代码

js 复制代码
/// <reference types="vite/client" />
declare module "*.vue" {

    import type { DefineComponent } from "vue";
    
    import Vue from "vue";
    
    const component: DefineComponent<{}, {}, any> | Vue;
    
    export default component;
    
}

项目的入口文件main.ts文件,引入组件库,图标库和路由,代码如下,

js 复制代码
import { createApp } from 'vue';
import './style.css';
import App from './App.vue';
import router from '@/router/index';
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/reset.css';
import * as Icons from '@ant-design/icons-vue'
const app = createApp(App)

// 全局注册图标组件
for (const i in Icons) {
    app.component(i, Icons[i])
  }
app.use(router).use(Antd).mount('#app')

路由文件如下

js 复制代码
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'

const routes: Array<RouteRecordRaw> = [
  {
    path: '/home',
    name: 'Home',
    component: () => import('@/views/home/index.vue'),
  },
  {
    path: '/card',
    name: 'Card',
    component: () => import('@/views/card/index.vue'),
  },
  {
    path: '/table',
    name: 'Table',
    component: () => import('@/views/table/index.vue'),
  },
  {
    path: '/login',
    name: 'login',
    component: () => import('@/views/login/index.vue')
  },

  { path: '/', redirect: { name: 'login' } }
]

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

export default router

App.vue文件如下

js 复制代码
<template>
  <a-config-provider
    :theme="{
      algorithm: theme.darkAlgorithm,
      components: {
        Radio: {
          colorPrimary: '#00b96b',
        },
      },
      token: {
          colorPrimary: '#1890ff',
      },
    }"
    :locale="locale"
  >
    <ErrorBoundary>
      <Menu/>
    </ErrorBoundary>  
  </a-config-provider>
</template>
<script setup lang="ts">
import {ref} from 'vue';
//边缘错误
import ErrorBoundary from '@/components/ErrorBoundary.vue';
//menu
import Menu from '../src/views/menu/index.vue';
//定制主题
import { theme } from 'ant-design-vue';
// const { darkAlgorithm, compactAlgorithm } = theme;
// const { useToken } = theme;
// const { token } = useToken();
//国际化  
import zhCN from 'ant-design-vue/es/locale/zh_CN';
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
dayjs.locale('zh-cn');
const locale = ref(zhCN);
</script>
<style scoped>

</style>

这里我用到了边缘错误组件ErrorBoundary.vue,包在最外层,方便给到错误提示,防止白屏,

js 复制代码
<template>
    <div  class="error-box" v-if="hasError">
      <!-- 错误信息展示或其他处理 -->
      {{ '发生错误,请联系技术支持。'+ error.message }}
    </div>
    <div v-else>
      <!-- 正常渲染内容 -->
      <slot></slot>
    </div>
  </template>
<script setup>
import { onErrorCaptured ,ref} from 'vue';
const hasError = ref(false);
const error = ref(null);
// 在组件实例化时设置 onErrorCaptured 钩子
onErrorCaptured((err, vm, info) => {
  // 处理错误,可以将错误信息记录到日志或进行其他处理
  console.error('Error captured in child component:', err);
  console.log('Component instance:', vm);
  console.log('Error info:', info);
  hasError.value = true;
  error.value = err;
  return false;
});
</script>
<style scoped>
.error-box{
  width: 80vw;
  margin: 30vh auto;
  text-align: center;
  font-size: 2vw;
}
</style>

menu菜单文件,menu/index.vue

js 复制代码
<template>
    <a-layout :style="MenuStyle">
      <a-layout-header class="header">
        <div class="logo"/>
        <a-menu
          v-model:selectedKeys="selectedKeys1"
          theme="dark"
          mode="horizontal"
          :style="{ lineHeight: '64px' }"
        >
          <a-menu-item key="1">首页</a-menu-item>
          <a-menu-item key="2">产品页</a-menu-item>
          <a-menu-item key="3">关于我们</a-menu-item>
        </a-menu>
      </a-layout-header>
      <a-layout-content style="padding: 0 50px">
        <a-layout style="padding: 24px 0;">
          <a-layout-sider width="15vw">
            <a-menu
              v-model:selectedKeys="selectedKeys2"
              v-model:openKeys="openKeys"
              mode="inline"
              style="height: 80vh; overflow-y:scroll;"
            > 
             
              <a-sub-menu :key="menu.id" v-for="menu in MenuList">
                <template #title>
                  <span>
                    <component :is="menu.icon"></component>
                    {{ menu.label }}
                  </span>
                </template>
                <a-menu-item :key="child.id" v-for="child in menu.children"><a :href="child.path">{{ child.label }}</a></a-menu-item>
              </a-sub-menu>
            </a-menu>
          </a-layout-sider>
          <a-layout-content :style="{ padding: '0 24px', minHeight: '280px' }">
            <RouterView />
          </a-layout-content>
        </a-layout>
      </a-layout-content>
      <a-layout-footer style="text-align: center">
        宇宙第一超级好用系统
      </a-layout-footer>
    </a-layout>
  </template>
  <script lang="ts" setup>
  import { ref } from 'vue';
  import MenuList from './menuList.tsx';
  const MenuStyle = ref({
    width: '100vw',
    height:'100vh'
  })
  const selectedKeys1 = ref<string[]>(['1']);
  const selectedKeys2 = ref<string[]>(['1']);
  const openKeys = ref<string[]>(['sub1']);
  console.log(openKeys.value,selectedKeys1.value,selectedKeys2.value,'openKeys,selectedKeys1,selectedKeys2')
  </script>
  <style scoped>
  #components-layout-demo-top-side .logo {
    float: left;
    width: 120px;
    height: 31px;
    margin: 16px 24px 16px 0;
  }
  
  .ant-row-rtl #components-layout-demo-top-side .logo {
    float: right;
    margin: 16px 0 16px 24px;
  }
  /* 隐藏滚动条 */
    ::-webkit-scrollbar {
    display: none;
    }

  </style>
  

menuList.tsx文件用来单独存放配置的菜单栏

js 复制代码
const MenuList = [
    {
        id:"sub1",
        label:"subnav 1",
        icon:"UserOutlined",
        children:[
        {
            id:"1",
            label:"option1",
            path:"#/table",
            children:[],
        },
        {
            id:"2",
            label:"option2",
            path:"#/card",
            children:[],
        },
        {
            id:"3",
            label:"option3",
            path:"#/home",
            children:[],
        },
        {
            id:"4",
            label:"option4",
            path:"#/home",
            children:[],
        },
    ],
    },
    {
        id:"sub2",
        label:"subnav 2",
        icon:"LaptopOutlined",
        children:[
            {
                id:"5",
                label:"option5",
                path:"#/home",
                children:[],
            },
            {
                id:"6",
                label:"option6",
                path:"#/home",
                children:[],
            },
            {
                id:"7",
                label:"option7",
                path:"#/home",
                children:[],
            },
            {
                id:"8",
                label:"option8",
                path:"#/home",
                children:[],
            },
        ],
    },
    {
        id:"sub3",
        label:"subnav 3",
        icon:"NotificationOutlined",
        children:[
            {
                id:"9",
                label:"option9",
                path:"#/home",
                children:[],
            },
            {
                id:"10",
                label:"option10",
                path:"#/home",
                children:[],
            },
            {
                id:"11",
                label:"option11",
                path:"#/home",
                children:[],
            },
            {
                id:"12",
                label:"option12",
                path:"#/home",
                children:[],
            },
        ],
    },
]

export default MenuList

最后,项目跑起来,如下:

3.附github地址:github.com/xiongqianhu...

相关推荐
Martin -Tang26 分钟前
vite和webpack的区别
前端·webpack·node.js·vite
迷途小码农零零发27 分钟前
解锁微前端的优秀库
前端
王解1 小时前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js
我不当帕鲁谁当帕鲁1 小时前
arcgis for js实现FeatureLayer图层弹窗展示所有field字段
前端·javascript·arcgis
那一抹阳光多灿烂1 小时前
工程化实战内功修炼测试题
前端·javascript
放逐者-保持本心,方可放逐2 小时前
微信小程序=》基础=》常见问题=》性能总结
前端·微信小程序·小程序·前端框架
毋若成4 小时前
前端三大组件之CSS,三大选择器,游戏网页仿写
前端·css
红中马喽4 小时前
JS学习日记(webAPI—DOM)
开发语言·前端·javascript·笔记·vscode·学习
Black蜡笔小新5 小时前
网页直播/点播播放器EasyPlayer.js播放器OffscreenCanvas这个特性是否需要特殊的环境和硬件支持
前端·javascript·html
秦jh_6 小时前
【Linux】多线程(概念,控制)
linux·运维·前端