Vue3现在已经开始变为Vue的默认版本,总结一下Vite+Vue3+Typescript搭建项目流程。
1.vite初始化项目
执行西面的命令
npm init vite@latest my-vue-app之后会出现选择语言版本

选择vue之后回车,选择vue-ts后回车初始化项目
进入my-vue-app项目根目录下,依次执行以下命令
npm install
npm run dev执行万npm run dev 之后,在浏览器中打开 http://localhost:3000/出现以下页面项目既初始化页面

2.开发登录页面
开发页面需要用到vue-router(路由),vuex(状态化管理)等,因此需要添加对应的依赖。本项目使用element-ui作为前端开发库,因此需要添加对应的版本库(由于element-ui不支持vue3,因此需要使用element-plus组件库),css处理使用了scss,也需要安装对应的依赖,请求接口使用了axios,没有后端接口,前端使用mockjs 模拟接口数据
执行一下代码,安装对应依赖库。
npm install vue-router vuex element-plus sass sass-loader axiosnpm install mockjs --save-devmockjs的使用在本项目中需要在 vite.config.ts文件中配置,同时需要安装插件vite-plugin-mock
npm install vite-plugin-mock --save-dev修改vite.config.ts,添加对应的配置
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { viteMockServe } from 'vite-plugin-mock'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(),
viteMockServe({
supportTs:false,
logger: false,
mockPath: "./mock/"
})
]
})在根目录下创建mock文件夹,在该文件夹下建立index.js文件,添加需要模拟的接口数据
export default [{
type:'get',
url:'/user/login',
response: () => {
return {isAuth:true}
}
},{
type:'get',
url:'/user/menu',
response: () => {
return {menusList:[{
id:'/sysManagent',
title:'系统管理',
subMenuList:[
{
id:'/userList',
title:'用户管理',
path:'/user/manage'
},
{
id:'/roleList',
title:'角色管理',
path:'/user/role'
},
{
id:'/permissionList',
title:'权限管理',
path:'/user/permission'
}
]
},{
id:'businessManagent',
title:'业务管理',
subMenuList:[
{
id:'/businessList',
title:'业务逻辑'
}
]
}],statusCode:200}
}
}]在src文件夹下建立文件夹router、store
在router文件夹下建立index.ts 文件,添加对应的登录页面(需要新建登录页面组件)
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
import Login from '../components/Login.vue'
const routes: Array<RouteRecordRaw> = [
{
path:'/',
redirect:'/login'
},
{
path:'/login',
component:Login
}
];
const router = createRouter({
history: createWebHistory(),
routes
})
router.beforeEach((to,from,next) => {
const isAuth = getStorage('isAuth',true);
if(to.path !== '/login' && !isAuth){
next({path:'/login'})
} else {
next();
}
})
export default router;在store文件夹下新建index.ts文件
import { createStore } from "vuex";
export default createStore({
state:{
isCollapse:false
},
mutations:{
COLLAPSE_CHANGE:(state)=>{
state.isCollapse = !state.isCollapse;
}
},
actions:{
collpaseChange:(context)=>{
context.commit('COLLAPSE_CHANGE');
}
}
})在main.ts 文件中引入路由、vuex,element组件
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElIcons from '@element-plus/icons-vue'
import './assets/css/global.scss'
import store from './store/index'
import autoLogout from './utils/autoLogout'
const app = createApp(App);
for(const name in ElIcons){
app.component(name,(ElIcons as any)[name]);
}
app.use(router).use(store).use(autoLogout).use(ElementPlus).mount('#app')在components文件下新建Login.vue组件
<template>
<div class="login-container">
<div class="login-box">
<el-form
ref="ruleFormRef"
:model="form"
:rules="rules"
label-width="80px"
class="login-form"
label-position="left"
>
<el-form-item label="用户名" prop="username">
<el-input
v-model="form.username"
placeholder="请输入用户名"
>
<template #prefix>
<el-icon><user /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input
v-model="form.password"
type="password"
placeholder="请输入密码"
>
<template #prefix>
<el-icon><stamp /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit(ruleFormRef)">
登录
</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script lang="ts">
import { FormInstance, FormRules } from 'element-plus'
import { defineComponent, reactive, ref } from 'vue'
import axios from 'axios'
import { useRouter } from 'vue-router'
import { setStorage } from '../utils/session'
export default defineComponent({
name: 'Login',
setup() {
const router = useRouter();
const ruleFormRef = ref<FormInstance>()
const form = reactive({
username: '',
password: '',
})
const rules = reactive<FormRules>({
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
})
const onSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (!valid) {
return;
}
axios.get('/user/login').then(res => {
const { data } = res;
if(data.isAuth) {
setStorage('isAuth', data.isAuth, true);
router.push({path:'/home'});
}
})
})
}
return {
form,
onSubmit,
ruleFormRef,
rules,
}
},
})
</script>
<style lang="scss" scoped>
.login-container {
background: linear-gradient(#7a9ad7, #3b5a94, #133064);
height: 100%;
}
.login-box {
width: 450px;
height: 300px;
background-color: #fff;
border-radius: 3px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.login-form {
position: absolute;
bottom: 25%;
width: 100%;
padding: 0 30px;
box-sizing: border-box;
}
</style>
3.登录之后的项目布局
登录之后系统进入到项目首页,由于项目是单页应用,需要定义基本布局组件,以后的页面内容放到布局组件的子组件中。
新建Home.vue组件
<template>
<el-container class="home-container">
<el-header>
<Header/>
</el-header>
<el-container>
<el-aside :class="{'collpase-width':store.state.isCollapse,'normal-width':!store.state.isCollapse}">
<Aside/>
</el-aside>
<el-main><router-view></router-view></el-main>
</el-container>
</el-container>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { useStore } from 'vuex'
import Aside from './aside/Aside.vue'
import Header from './header/Header.vue'
export default defineComponent({
name:'Home',
components:{
Aside,
Header
},
setup() {
const store = useStore();
return {
store
}
},
})
</script>
<style lang="scss" scoped>
.home-container {
height: 100%;
}
.el-header {
background-color: #373d41;
display: flex;
justify-content: space-between;
padding-left: 0%;
font-size: 20px;
color: #fff;
}
.el-aside {
background-color: #333744;
}
.el-main {
background-color: #eaedf1;
}
.normal-width{
width: 200px;
}
.collpase-width{
width: 65px;
}
</style>
整体分为头部组件,侧边栏组件和展示区,在新建aside和header文件夹,在这个下面新建对应的Aside.vue组件和Header.vue组件
Aside.vue组件
<template>
<div class="aside">
<div class="collpase-btn" @click="collpaseMenu">
<el-icon><fold /></el-icon>
</div>
<el-menu
active-text-color="#ffd04b"
background-color="#545c64"
class="el-menu-vertical-demo"
:default-active="router.path"
text-color="#fff"
:collapse="store.state.isCollapse"
@open="handleOpen"
@close="handleClose"
:router="true"
>
<el-sub-menu
:index="item.id"
v-for="item in menus.mensList"
:key="item.id"
>
<template #title>
<el-icon><grid /></el-icon>
<span>{{ item.title }}</span>
</template>
<el-menu-item :index="it.id" v-for="it in item.subMenuList">
{{ it.title }}
</el-menu-item>
</el-sub-menu>
</el-menu>
</div>
</template>
<script lang="ts">
import axios from 'axios'
import { defineComponent, onMounted, reactive } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useStore } from 'vuex'
export default defineComponent({
name: 'Aside',
setup() {
const store = useStore()
const router = useRoute()
const skipPage = useRouter();
const mensList: any = []
let menus = reactive({ mensList })
const handleOpen = (key: string, keyPath: string[]) => {}
const handleClose = (key: string, keyPath: string[]) => {}
const collpaseMenu = () => {
store.dispatch('collpaseChange')
}
onMounted(() => {
axios.get('/user/menu').then((res) => {
menus.mensList = res.data.menusList
skipPage.push({path:menus.mensList[0].subMenuList[0].id});
})
})
return {
handleOpen,
handleClose,
collpaseMenu,
store,
menus,
router
}
},
})
</script>
<style lang="scss" scoped>
.el-menu-item.is-active {
background-color: var(--el-menu-hover-bg-color);
}
.collpase-btn {
text-align: center;
width: 100%;
padding: 10px 0px;
cursor: pointer;
.el-icon {
color: white;
font-size: 24px;
}
}
</style>
Header.vue组件
<template>
<div class="header">
<div>vue管理系统</div>
<div>
<el-button type="info" @click="logout">退出登录</el-button>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { useRouter } from 'vue-router';
import { deleteStorage } from '../../utils/session'
export default defineComponent({
name:'Header',
setup() {
const router = useRouter();
const logout = () => {
deleteStorage('isAuth',true);
router.push({path:'/login'});
}
return {
logout
}
},
})
</script>
<style lang="scss" scoped>
.header{
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
div{
margin-left: 15px;
}
}
</style>
在router中配置Home组件的路由
const routes: Array<RouteRecordRaw> = [
{
path:'/',
redirect:'/login'
},
{
path:'/login',
component:Login
},
{
path:'/home',
component:() => import('../components/Home.vue'),
redirect:'/welcome',
children:[
{
path:'/welcome',
component:() => import('../components/Welcome.vue')
}
]
}
];