Vite2+vue3+ts 使用 router,layout 搭建页面框架,并做页面自适应
vue3 对应使用的是 vue-router@4 版本, 有新的用,但是向下兼容
本文档源码:Lzq811/vite-vue-ts-eslint at vite2+vue3+ts使用vue-router搭建页面框架 (github.com)
1. 安装使用 vue-router@4
-
install
yarn add vue-router@4 # 一定要@4哦,不然会安装3版本导致不兼容vue3
在 src 下新建 pages 文件夹,并添加 login/index.vue 、 home/index.vue 文件,并随便初始化一下组件内容。
在 src 下新建 routes 文件夹,并添加 index.tsx 、 routesNames.tsx 文件
// index.tsx
import { createRouter, createWebHistory } from 'vue-router'
import * as RouteNames from './routesname'
interface RoutesItem {
path: string
component: any
name?: string
redirect?: string
}
const routes: Array<RoutesItem> = [
{
path: '/',
redirect: '/home',
component: () => import('@pages/home/index.vue'),
},
{
path: '/default',
name: RouteNames.HOME,
component: () => import('@pages/home/index.vue'),
},
{
path: '/home',
name: RouteNames.HOME,
component: () => import('@pages/home/index.vue'),
},
{
path: '/login',
name: RouteNames.LOGIN,
component: () => import('@pages/login/index.vue'),
},
]
// 配置路由
const router = createRouter({
history: createWebHistory(),
routes,
})
router.beforeEach((to, from) => {
const { path: toPath } = to
const { path: fromPath } = from
if (toPath === fromPath) {
return false
}
})
export default router
// routesNames.tsx
// 管理所有路由名称
export const LOGIN: string = 'LOGIN'
export const HOME: string = 'HOME'
-
在 Login 组件中 点击 跳转到 Home 页面
// Login.vue import { useRouter } from 'vue-router' const router = useRouter() const jumpTohome: any = (): void => { router.replace('/home') } // 执行jumpTohome方法就能跳转到home页面了
2. Home 页面布局
- 在 src/components 文件夹下创建文件 src/components/common/aside/index.vue 、 src/components/common/head/index.vue
// head.vue
<template>
<div>
<el-dropdown>
<el-icon style="margin-right: 15px"><setting /></el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>View</el-dropdown-item>
<el-dropdown-item>Add</el-dropdown-item>
<el-dropdown-item>Delete</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<span>Tom</span>
</div>
</template>
<script lang="ts" setup></script>
<style lang="less" scoped></style>
// asise.vue
<template>
<el-aside :width="asidewidth" class="aside-com">
<div class="logo-box">LOGO</div>
<el-button
type="primary"
@click="showCollapse"
:icon="isCollapse ? Fold : Expand"
></el-button>
<el-menu
class="menus-box"
active-text-color="#00A2D8"
text-color="#fff"
background-color="rgba(0,0,0,0)"
:default-openeds="['1', '3']"
:collapse="isCollapse"
>
<el-sub-menu v-for="item in Menus" :key="item.order" :index="item.order">
<template #title>
<el-icon :size="24" color="#fff">
<component :is="item.icon"></component>
</el-icon>
<span>{{ item.title }}</span>
</template>
<el-menu-item
v-for="menu in item.children"
:key="menu.index"
:index="menu.index"
>{{ menu.name }}</el-menu-item
>
</el-sub-menu>
</el-menu>
</el-aside>
</template>
<script lang="ts" setup>
import { Location, Fold, Expand } from '@element-plus/icons'
import { onMounted, ref } from 'vue'
const Menus = ref([
{
title: '基础配置',
order: 'base',
icon: Location,
children: [
{ name: '二级菜单1', index: '1-1' },
{ name: '二级菜单2', index: '1-2' },
],
},
{
title: '收发货管理',
order: 'manage',
icon: Location,
children: [
{ name: '二级菜单1', index: '2-1' },
{ name: '二级菜单2', index: '2-2' },
],
},
{
title: '运营监控',
order: 'control',
icon: Location,
children: [
{ name: '二级菜单1', index: '2-1' },
{ name: '二级菜单2', index: '2-2' },
],
},
])
const isCollapse = ref(false)
const asidewidth = ref('180px')
onMounted(() => {})
const handleOpen = (key: any, keyPath: any): void => {
console.log(key, keyPath)
}
const handleClose = (key: any, keyPath: any): void => {
console.log(key, keyPath)
}
const showCollapse = (): void => {
isCollapse.value = !isCollapse.value
asidewidth.value = !isCollapse.value ? '180px' : '60px'
}
</script>
<style lang="less">
.aside-com {
height: 100%;
box-sizing: border-box;
overflow: hidden;
transition: all 0.25s linear;
background-color: #1f2f48;
.logo-box {
width: 100%;
height: 60px;
background-color: rgba(255, 255, 255, 0.4);
text-align: center;
line-height: 60px;
}
.menus-box {
border: none;
transition: all 0.2s linear;
}
}
</style>
- 修改 home/index.vue
<template>
<el-container class="home-page">
<aside></aside>
<el-container>
<el-header class="head-com"><head></head></el-header>
<el-main> 主体内容 </el-main>
</el-container>
</el-container>
</template>
<script lang="ts" setup>
import Aside from '@components/common/aside/index.vue'
import Head from '@components/common/head/index.vue'
</script>
<style lang="less" scoped>
.home-page {
height: 100%;
}
.head-com {
width: 100%;
background-color: #1f2f48;
}
</style>
-
其中 __dirname找不到的解决办法
yarn add @types/node --save-dev
-
配置 alias 路径别名
// vite.config.ts resolve: { alias: { // 如何 __dirname 找不到 需要 yarn add @types/node --save-dev '@': path.resolve(__dirname, 'src'), '@api': path.resolve(__dirname, 'src/api'), '@pages': path.resolve(__dirname, 'src/pages'), '@routes': path.resolve(__dirname, 'src/routes'), '@components': path.resolve(__dirname, 'src/components') } },
3. 页面自适应
- 安装
yarn add postcss-plugin-px2rem
yarn add amfe-flexible
-
修改配置
// main.ts import 'amfe-flexible'
// vite.config.ts 一定要注意 postcss 的位置,不要写在了 preprocessorOptions 对象里面 css: { preprocessorOptions: { less: { javascriptEnabled: true, // 这样就能全局使用 src/assets/styles/base.less 定义的 变量 additionalData: `@import "${path.resolve(__dirname, 'src/assets/styles/reset.less')}";` } }, postcss: { plugins: [ require('postcss-plugin-px2rem')({ rootValue: 192, //换算基数, 默认100 ,这样的话把根标签的字体规定为1rem为50px,这样就可以从设计稿上量出多少个px直接在代码中写多上px了。 // unitPrecision: 5, //允许REM单位增长到的十进制数字。 //propWhiteList: [], //默认值是一个空数组,这意味着禁用白名单并启用所有属性。 // propBlackList: [], //黑名单 exclude: /(node_module)/, //默认false,可以(reg)利用正则表达式排除某些文件夹的方法,例如/(node_module)/ 。如果想把前端UI框架内的px也转换成rem,请把此属性设为默认值 // selectorBlackList: [], //要忽略并保留为px的选择器 // ignoreIdentifier: false, //(boolean/string)忽略单个属性的方法,启用ignoreidentifier后,replace将自动设置为true。 // replace: true, // (布尔值)替换包含REM的规则,而不是添加回退。 mediaQuery: false, //(布尔值)允许在媒体查询中转换px。 minPixelValue: 3 //设置要替换的最小像素值(3px会被转rem)。 默认 0 }) ] } }
查看页面的文字或者盒子大小是否已经从 px 单位 变成了 rem 单位。
本文的对应源码地址: Lzq811/vite-vue-ts-eslint at vite2+vue3+ts使用vue-router搭建页面框架 (github.com)