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系统
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
最后,项目跑起来,如下: