Vue3+Vite+ts 从零开发前端 – 7 – 组件编写(2) – 封装 Header 组件
本文最后更新于 297 天前,如有失效请评论区留言。

前言

导航组件是界面必须的内容,一般导航上面有Logo、菜单链接、登录等信息,另外还要实现暗黑主题和明亮主题的切换

这里用路由和状态管理插件来实现一个 Header 组件,最终的样式大概是这个样子,点击按钮能切换主题

Pasted image 20240229210748

Pasted image 20240229210806

安装依赖

路由 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>
版权声明:除特殊说明,博客文章均为Gavin原创,依据CC BY-SA 4.0许可证进行授权,转载请附上出处链接及本声明。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇