本文最后更新于 297 天前,如有失效请评论区留言。
前言
导航组件是界面必须的内容,一般导航上面有Logo、菜单链接、登录等信息,另外还要实现暗黑主题和明亮主题的切换
这里用路由和状态管理插件来实现一个 Header 组件,最终的样式大概是这个样子,点击按钮能切换主题
安装依赖
路由 Router
npm install vue-router
安装 Pinia
npm install pinia
安装 vueuse
这个是基于组合式API的函数集合,简单说就是对特定功能封装好的一些函数
npm install @vueuse/core
依赖引入
注册 pinia
编写一个 pinia 示例
import { defineStore } from 'pinia'
import { useDark } from '@vueuse/core'
import { darkTheme } from 'naive-ui'
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core'
import type { BuiltInGlobalTheme } from 'naive-ui/es/themes/interface'
const breakpoints = useBreakpoints(breakpointsTailwind)
export const useAppStore = defineStore('app', {
state: () => ({
isDark: useDark(),
isMobile: breakpoints.smaller('sm')
}),
getters: {
naiveTheme(): BuiltInGlobalTheme | undefined {
return this.isDark ? darkTheme : undefined
}
},
actions: {
setIsMobile(isMobile: boolean) {
this.isMobile = isMobile
},
toggleDark() {
this.isDark = !this.isDark
}
},
persist: {
storage: localStorage
}
})
创建一个 pinia(根存储)并将其传递给应用程序
引入持久化插件 pinia-plugin-persistedstate
npm i pinia-plugin-persistedstate
import { createPinia } from 'pinia';
import { useAppStore } from './modules/app';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
export { useAppStore }
export default pinia;
注册 router
路由配置,这是一个简单的例子
import type { RouteRecordRaw } from 'vue-router';
const constantRoutes: RouteRecordRaw[] = [
{
path: '/login',
name: 'login',
component: () => import('@/views/login/index.vue'),
meta: { title: '登录' }
},
{
path: '/register',
name: 'register',
component: () => import('@/views/login/register.vue'),
meta: { title: '注册' }
},
{
path: '/',
name: 'Home',
component: () => import('@/views/home/index.vue'),
meta: {
title: '首页'
}
},
{
path: '/product/:id',
name: 'Product',
component: () => import('@/views/product/index.vue'),
meta: {
title: '商品详情'
}
},
{
path: '/order',
name: 'OrderDetail',
component: () => import('@/views/order/index.vue'),
meta: {
title: '订单详情'
}
}
// ...accessRoutes,
];
export default constantRoutes;
注册到 app 下
import { createRouter, createWebHistory } from 'vue-router';
import routers from './router.config'
const router = createRouter({
history: createWebHistory(),
routes: routers
});
export default router;
挂载到 app 实例
import { createApp } from 'vue'
import App from './App.vue'
import router from './router';
import piniaStore from './store';
// style
import '@/styles/index.less';
import '@/styles/reset.less'
import 'uno.css';
// 注册SVG图标
import 'virtual:svg-icons-register';
const app = createApp(App);
app.use(router);
app.use(piniaStore);
app.mount('#app');
unocss 增加快捷指令
unocss.config.ts 中添加一些 shortcuts 指令,如背景颜色,flex 布局等
import { defineConfig, presetAttributify, presetIcons, presetUno } from 'unocss';
export default defineConfig({
exclude: ['node_modules', '.git', '.github', '.husky', '.vscode', 'build', 'dist', 'mock', 'public', './stats.html'],
presets: [presetUno(), presetAttributify(), presetIcons()],
shortcuts: [
['wh-full', 'w-full h-full'],
['f-c-c', 'flex justify-center items-center'],
['flex-col', 'flex flex-col'],
['card-border', 'border border-solid border-light_border dark:border-dark_border'],
['auto-bg', 'bg-white dark:bg-dark'],
['auto-bg-hover', 'hover:bg-#eaf0f1 hover:dark:bg-#1b2429'],
['auto-bg-highlight', 'bg-#eaf0f1 dark:bg-#1b2429'],
['text-highlight', 'rounded-4 px-8 py-2 auto-bg-highlight'],
],
rules: [],
theme: {
colors: {
primary: 'var(--primary-color)',
primary_hover: 'var(--primary-color-hover)',
primary_pressed: 'var(--primary-color-pressed)',
primary_active: 'var(--primary-color-active)',
info: 'var(--info-color)',
info_hover: 'var(--info-color-hover)',
info_pressed: 'var(--info-color-pressed)',
info_active: 'var(--info-color-active)',
success: 'var(--success-color)',
success_hover: 'var(--success-color-hover)',
success_pressed: 'var(--success-color-pressed)',
success_active: 'var(--success-color-active)',
warning: 'var(--warning-color)',
warning_hover: 'var(--warning-color-hover)',
warning_pressed: 'var(--warning-color-pressed)',
warning_active: 'var(--warning-color-active)',
error: 'var(--error-color)',
error_hover: 'var(--error-color-hover)',
error_pressed: 'var(--error-color-pressed)',
error_active: 'var(--error-color-active)',
},
},
});
主题配置
style
这里需要对全局的样式做下控制,一些常规的样式重新进行调整,例如去掉 a 标签的下换线,li 标签的样式等
reset.less
html {
box-sizing: border-box;
}
*,
::before,
::after {
margin: 0;
padding: 0;
box-sizing: inherit;
}
a {
text-decoration: none;
color: #333;
}
a:hover,
a:link,
a:visited,
a:active {
text-decoration: none;
}
ol,
ul {
list-style: none;
}
input,
textarea {
outline: none;
border: none;
resize: none;
}
body {
font-size: 14px;
font-weight: 400;
}
index.html
在 body 中增加黑白背景色样式 dark:text-#e9e9e9 auto-bg
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue + TS</title>
</head>
<body class="dark:text-#e9e9e9 auto-bg">
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
app.vue
app.vue 中使用 naive 的 n-config-provider
组件,对组件的样式统一进行配置
<template>
<n-config-provider
:theme="appStore.isDark ? darkTheme : undefined"
>
<router-view />
</n-config-provider>
</template>
<script setup lang="ts">
import { darkTheme } from 'naive-ui'
import { useAppStore } from '@/store'
const appStore = useAppStore()
</script>
编写 Header 组件
Header/index.vue
<template>
<header
class="sticky top-0 z-20 w-full flex justify-center flex-col md:flex-row md:items-center gap-3 p-3 lg:px-4 md:h-16 auto-bg2"
>
<div class="flex items-center w-full gap-8 lg:w-[1200px]">
<div class="flex items-center gap-2">
<h1 class="mr-4 sm:mr-8 h-15 w-[210px] lg:mr-16 sm:h-[52px]">
<router-link to="/" class="h-15">Fakar</router-link>
</h1>
</div>
<div class="flex items-center gap-2 ml-auto">
<router-link
to="/order"
class="cursor-pointer inline-flex items-center whitespace-nowrap shrink-0 justify-center text-sm transition-colors disabled:pointer-events-none disabled:opacity-50 border border-solid border-#e4e4e7 shadow-sm font-medium hover:bg-#e4e4e7 hover:dark:bg-#222 h-8 px-3 py-2 rounded-full"
>
<SvgIcon name="svg-search" size="18px" :color="isDark ? '#fff' : '#111'" />
<span class="hidden sm:inline">订单查询</span>
</router-link>
<div
@click="toggleDark"
class="cursor-pointer inline-flex items-center whitespace-nowrap shrink-0 justify-center text-sm transition-colors disabled:pointer-events-none disabled:opacity-50 border border-solid border-#e4e4e7 shadow-sm font-medium hover:bg-#e4e4e7 hover:dark:bg-#222 h-8 px-3 py-2 rounded-full"
>
<SvgIcon :name="isDark ? 'svg-moon' : 'svg-sun'" size="20px" :color="isDark ? '#fff' : '#111'" />
</div>
<router-link
to="/login"
class="cursor-pointer inline-flex items-center whitespace-nowrap shrink-0 justify-center text-sm transition-colors disabled:pointer-events-none disabled:opacity-50 border border-solid border-#e4e4e7 shadow-sm font-medium hover:bg-#e4e4e7 hover:dark:bg-#222 h-8 px-3 py-2 rounded-full"
>
<SvgIcon name="svg-user" size="20px" :color="isDark ? '#fff' : '#111'" />
</router-link>
</div>
</div>
</header>
</template>
<script setup lang="ts">
import { useDark, useToggle } from '@vueuse/core'
import { useAppStore } from '@/store'
const appStore = useAppStore()
const isDark = useDark()
const toggleDark = () => {
appStore.toggleDark()
useToggle(isDark)()
}
</script>