vite 创建Vue3项目搭建基本框架

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 axios
npm install mockjs  --save-dev

mockjs的使用在本项目中需要在 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')
            }
        ]
    }
];


版权声明:本文为liguoyuan819原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。