vue3从0开始搭建的学习记录(二)

上期链接:vue3从0开始搭建的学习记录(一)-CSDN博客

这里先发现了vscode的扩展插件Vetur对vite+vue3并不友好,所以改成了使用Vue - Official

了解到vue3是需要使用ref()作为响应式声明的函数,引入了unplugin-auto-import依赖,以便自动添加常用的方法和函数到开发页面,而不用每个页面去引入了

npm i -D unplugin-auto-import

需要在vite.config.ts文件里加入要自动引入的一些方法和函数

使用resolve这里需要先引入nodejs的path的依赖,也可以用其他方案,这里就不说了,可以自己去查一下

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'

import AutoImport from 'unplugin-auto-import/vite'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    AutoImport({  
      imports: [  
        'vue',  
        'vue-router',  
      ],
      dts: resolve('./auto-imports.d.ts')
    }),
  ],
  resolve: {
    alias: {
      '@': resolve('src'),
    }
  }
})

 还需要在tsconfig.json文件里加入

"include": [
    "scripts/**/*.ts",
    "./**/*.ts",
    "./**/*.d.ts",
    "./**/*.tsx",
    "./**/*.vue",
    "auto-imports.d.ts", // 引入auto-import.d.ts文件
  ],

 下面是我的auto-imports.d.ts文件,需要放在根目录下,可以参考参考

/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
export {}
declare global {
  const EffectScope: typeof import('vue')['EffectScope']
  const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
  const computed: typeof import('vue')['computed']
  const createApp: typeof import('vue')['createApp']
  const createPinia: typeof import('pinia')['createPinia']
  const customRef: typeof import('vue')['customRef']
  const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
  const defineComponent: typeof import('vue')['defineComponent']
  const defineStore: typeof import('pinia')['defineStore']
  const effectScope: typeof import('vue')['effectScope']
  const getActivePinia: typeof import('pinia')['getActivePinia']
  const getCurrentInstance: typeof import('vue')['getCurrentInstance']
  const getCurrentScope: typeof import('vue')['getCurrentScope']
  const h: typeof import('vue')['h']
  const inject: typeof import('vue')['inject']
  const isProxy: typeof import('vue')['isProxy']
  const isReactive: typeof import('vue')['isReactive']
  const isReadonly: typeof import('vue')['isReadonly']
  const isRef: typeof import('vue')['isRef']
  const mapActions: typeof import('pinia')['mapActions']
  const mapGetters: typeof import('pinia')['mapGetters']
  const mapState: typeof import('pinia')['mapState']
  const mapStores: typeof import('pinia')['mapStores']
  const mapWritableState: typeof import('pinia')['mapWritableState']
  const markRaw: typeof import('vue')['markRaw']
  const nextTick: typeof import('vue')['nextTick']
  const onActivated: typeof import('vue')['onActivated']
  const onBeforeMount: typeof import('vue')['onBeforeMount']
  const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
  const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
  const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
  const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
  const onDeactivated: typeof import('vue')['onDeactivated']
  const onErrorCaptured: typeof import('vue')['onErrorCaptured']
  const onMounted: typeof import('vue')['onMounted']
  const onRenderTracked: typeof import('vue')['onRenderTracked']
  const onRenderTriggered: typeof import('vue')['onRenderTriggered']
  const onScopeDispose: typeof import('vue')['onScopeDispose']
  const onServerPrefetch: typeof import('vue')['onServerPrefetch']
  const onUnmounted: typeof import('vue')['onUnmounted']
  const onUpdated: typeof import('vue')['onUpdated']
  const provide: typeof import('vue')['provide']
  const reactive: typeof import('vue')['reactive']
  const readonly: typeof import('vue')['readonly']
  const ref: typeof import('vue')['ref']
  const resolveComponent: typeof import('vue')['resolveComponent']
  const setActivePinia: typeof import('pinia')['setActivePinia']
  const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
  const shallowReactive: typeof import('vue')['shallowReactive']
  const shallowReadonly: typeof import('vue')['shallowReadonly']
  const shallowRef: typeof import('vue')['shallowRef']
  const storeToRefs: typeof import('pinia')['storeToRefs']
  const toRaw: typeof import('vue')['toRaw']
  const toRef: typeof import('vue')['toRef']
  const toRefs: typeof import('vue')['toRefs']
  const toValue: typeof import('vue')['toValue']
  const triggerRef: typeof import('vue')['triggerRef']
  const unref: typeof import('vue')['unref']
  const useAttrs: typeof import('vue')['useAttrs']
  const useCssModule: typeof import('vue')['useCssModule']
  const useCssVars: typeof import('vue')['useCssVars']
  const useLink: typeof import('vue-router')['useLink']
  const useRoute: typeof import('vue-router')['useRoute']
  const useRouter: typeof import('vue-router')['useRouter']
  const useSlots: typeof import('vue')['useSlots']
  const watch: typeof import('vue')['watch']
  const watchEffect: typeof import('vue')['watchEffect']
  const watchPostEffect: typeof import('vue')['watchPostEffect']
  const watchSyncEffect: typeof import('vue')['watchSyncEffect']
}
// for type re-export
declare global {
  // @ts-ignore
  export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
  import('vue')
}

引入vue-router

这里使用的是vue-router 4.X版本

先贴官网:入门 | Vue Router

npm install vue-router@4

main.ts 引入

import { createApp } from 'vue'
import App from './App.vue'

//引入vue-router
import router from './router'
const app= createApp(App)

app.use(router)

app.mount('#app')

修改app.vue

<template>
  <div>
    <!-- 使用router-view加入路由跳转组件 -->
    <router-view></router-view>
  </div>
  
</template>

<style scoped>

</style>

整体布局页面

在src目录下添加layout文件夹,分别加入index主页面,myMenu菜单组件,menuTab导航栏组件

index.vue文件

<template>
  <div>
    <div class="layout">
      <div class="headLine"></div>
      <div class="layout-mainbox">
        <div class="layout-menu">
          <myMenu :menuList="menuList" :collapse="collapse" @handleOpen="handleOpen" @handleClose="handleClose"></myMenu>
          <div class="menu-head">
            <el-button @click="collapse=!collapse">
              <el-icon>
                <DArrowLeft v-show="!collapse"/>
                <DArrowRight v-show="collapse"/>
              </el-icon>
            </el-button>
          </div>
        </div>
        <menuTab></menuTab>
        <router-view v-slot="{ Component }">
            <keep-alive>
              <component :is="Component" />
            </keep-alive>
          </router-view>
      </div>
    </div>
  </div>
</template>
<script setup lang="ts">
import menuTab from './menuTab.vue';
import myMenu from './myMenu.vue'

const collapse=ref(false)
const menuList=ref([{
  name:'单页面',
  code:'menu1',
  icon:'Eleme',
},{
  name:'菜单',
  code:'menu2',
  icon:'sd',
  children:[{
    name:'子菜单1',
    code:'sonmenu1',
    icon:'Edit',
    children:[{
      name:'子菜单1',
      code:'sonmenu3',
      icon:'asdd',
    }]
  }]
}])
const handleOpen = (key: string, keyPath: string[]) => {
  console.log(key, keyPath)
}
const handleClose = (key: string, keyPath: string[]) => {
  console.log(key, keyPath)
}
</script>
<style scoped lang="scss">
$headheight:50px;
.layout{
  height:100vh;
  width:100vw;
}
.headLine{
  width:100%;
  height:$headheight;
  border-bottom: 1px solid var(--el-border-color);
  box-sizing: border-box;
  }
.layout-mainbox{
  height:calc(100% - $headheight);
  width:100%;
  display: flex;
}
.layout-menu{
  height:100%;
  max-width:300px;
  .menu-head{
    height:$headheight;
    display:flex;
    justify-content:right;
    padding:15px 10px;
    box-sizing: border-box;
    border-right:1px solid var(--el-border-color);
    .el-button{
      padding:0 10px;
    }
  }
  .menu-main{
    height:calc(100% - $headheight);
  }
}


</style>

这里创建的是经典的管理系统,分为头部,左侧菜单树和右侧上部页签栏、下部页面主体

知识点:vue3组合式api、组件的引入、响应式变量声明和使用

菜单组件

myMenu.vue文件

<template>
  <template v-if="props.isChild">
    <template v-if="props.menuList.length>0">
      <template v-for="menu in props.menuList">
      <el-sub-menu v-if="menu.children&&menu.children.length>0" :index="menu.code" :key="menu.code">
        <template #title>
          <el-icon>
            <component :is="menu.icon" /></el-icon>
          <span>{{menu.name}}</span>
        </template>  
        <myMenu :isChild="true" :menuList="menu.children"></myMenu>     
      </el-sub-menu>
      <el-menu-item v-else :index="menu.code" :key="menu.code+'n'">
        <el-icon>
          <component :is="menu.icon" />
        </el-icon>
        <span>{{menu.name}}</span>
      </el-menu-item>
    </template>
    </template>
  </template>
  <template v-else>
    <el-menu
      default-active="2"
      class="menu-main"
      :collapse="props.collapse"
      @open="handleOpen"
      @close="handleClose"
    >
      <myMenu :isChild="true" :menuList="props.menuList"></myMenu> 
    </el-menu> 
  </template>
</template>
<script lang="ts" setup>
const props=defineProps(['menuList','isChild','collapse'])
const emit = defineEmits<{
  (e: 'handleOpen', key: string,keyPath:string[]): void
  (e: 'handleClose', key: string,keyPath:string[]): void
}>()
const handleOpen = (key: string, keyPath: string[]) => {
  console.log(key, keyPath)
  emit('handleOpen',key,keyPath)
}
const handleClose = (key: string, keyPath: string[]) => {
  console.log(key, keyPath)
  emit('handleClose',key,keyPath)
}
</script>
<style scoped lang="scss">

</style>

这里使用了递归嵌套组件来实现菜单

知识点:vue3组合式api、组件的props参数传递和使用、组件的自我调用、事件的传递(Vue2的$emit())

页签组件

<template>
  <div>
    <el-breadcrumb separator=" ">
      <el-breadcrumb-item :class="{ 'active-page': $route.path === '/page/homepage' }" :to="{ path: '/homepage' }" >首页</el-breadcrumb-item>
      <!-- <draggable
        v-model="pageList"
        animation="300"
        ghost-class="ghostClass"
        @end="onEnd"
      > -->
        <el-breadcrumb-item
          @contextmenu.prevent.native="rightBtnClick($event,page)"
          :class="{ 'active-page': $route.path === page.path }"
          :to="{ path: page.path }"
          v-for="page in pageList"
          :key="page.code"
          >{{ page.name}}<el-icon
            v-if="element.path!=='/pages/homepage'"
            class="deleteIcon"
            @click.stop="deletePage(element)"
            :size="14"
        ><CircleCloseFilled/></el-icon>></el-breadcrumb-item>
      <!-- </draggable> -->
    </el-breadcrumb>
    <div class="rightbox" v-show="showRightBox" :style="`top:${rightBoxTop}px;left:${rightBoxLeft}px`">
      <div @click="deleteOthers">关闭其他标签</div>
      <div @click="deleteAll">关闭所有</div>
    </div>
  </div>
</template>
<script setup lang="ts">
import draggable from 'vuedraggable'
const rightBoxTop=ref(0)
const rightBoxLeft=ref(0)
const showRightBox=ref(false)
const currentPage=ref({})
const pageList=ref([])
function rightBtnClick(e,page) {
  if(e.button===2&&this.$route.path === page.path){
    if(e.clientY-this.originY||e.clientX-this.originx)return
    rightBoxTop.value=e.clientY
    rightBoxLeft.value=e.clientX
    showRightBox.value=true
    currentPage.value=page
  }
}
function deleteOthers() {
  
}
function deleteAll() {
  
}
function deletePage(page:object) {
  
}
function onEnd() {
  
}
</script>
<style lang="scss" scoped>
:deep(.el-breadcrumb) {
  margin: 8px 6px 6px 6px;
  display: flex;
  justify-content: left;
  align-items: center;
  .el-breadcrumb__item {
    padding:0;
    border-radius: 6px;
    text-align: left;
    user-select: none !important;
    cursor: pointer !important;
    border: 1px solid #e4e4e4;
    margin:0 2px;
    span {
      user-select: none !important;
      cursor: pointer !important;
      font-weight: 400;
    }
    .deleteIcon {
      margin-left: 4px;
      color:#e4e4e4;
    }
  }
  .el-breadcrumb__inner{
    padding: 10px 8px 10px 10px;
    height:100%;
    // width:100%;
    display: inline-block;
  }
  .el-breadcrumb__separator {
    display: none;
  }
  .active-page {
    border: 1px solid #66B1FFa0;
    // padding: 10px;
    background: #fff;
    .deleteIcon {
      margin-left: 4px;
      color:#66B1FFc0;
    }
    span {
      font-weight: bold !important;
    }
  }
}
  /*定义要拖拽元素的样式*/
  .ghostClass {
    display: inline-block;
    // background-color: #CCE4FF !important;
    opacity: 0.5;
  }
  /*右键菜单 */
  .rightbox{
  position: fixed;
  border-radius: 8px;
  border:2px solid #c2c2c2a0;
  box-shadow: 0 0 3px 3px #85858530;
  background: #fff;
  width:140px;
  padding:3px 0;
  transform: translate(10px,10px);
  z-index: 200;
  div{
    border-bottom: 1px solid #c2c2c2;
    height:20px;
    font-size:14px;
    color:#000;
    padding:6px 10px;
    user-select: none;
  }
  div:hover{
    background: #85858520;
    cursor: pointer;
  }
}
</style>

 原本想加上Vue2中的vuedraggble拖拽,结果发现有点问题所以先注释掉了,下次再调试他爸

路由表

简单页面布局画好后,开始配置路由

当然,需要在src下面创建一个空的页面文件做首页使用

src目录下创建文件夹router,加入index主文件和guard路由守卫文件(暂时只用到index)

因为目前是做纯前端页面,所以路由表直接先写死了,这里直接粘贴代码了,引入layout,使用的HTML5 模式

index.js

import {createRouter,createWebHistory } from 'vue-router'
import Layout from "../layout/index.vue";
const routes=[
  {
    path:'/page',
    component:Layout,
    redirect:'/page/homepage',
    children:[
      {
        path:'homepage',
        name:'homepage',
        component:()=>import('@/view/homePage/index.vue')
      }
    ]
  }
  
]
 const router = createRouter({
  history:createWebHistory(),
  routes:routes
})
export default router;

知识点:router4.x的几种路由模式

 简单效果图

后续的逻辑下次在记录

下期连接:vue3从0开始搭建的学习记录(三)-CSDN博客

  • 32
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值