【Nuxt 实战】02-集成 Pinia、新增导航栏、暗黑模式

前言

通过 Nuxt 实战系列的上节《【Nuxt 实战】01-初始化项目、新增组件库、图标库》,我们完成对 Nuxt 项目的初始化,并增加 UI 组件库和图标库。

这一小节,我们在 Nuxt 项目中集成 Pinia增加错误页面新增导航栏实现暗黑效果增加一个全局插件

集成 Pinia

安装 Pinia

在 Nuxt 项目中集成 Pinia 非常简单,直接在 Nuxt Modules 页面搜索 Pinia 找到下图中的两个模块,把它们添加到 nuxt.config.ts 文件中的 modules 模块中。

在项目命令行终端输入 pnpm add pinia @pinia/nuxt pinia-plugin-persistedstate

然后把它们添加到 nuxt.config.ts 文件中,关于更多 @pinia/nuxt 的配置,可以参考 Pinia 官网中针对 Nuxt 的配置说明。

ts 复制代码
modules: [
  [
    '@pinia/nuxt',
    {
      autoImports: [
        // 自动引入 `defineStore(), storeToRefs()`
        'defineStore',
        'storeToRefs'
      ]
    }
  ],
  'pinia-plugin-persistedstate/nuxt'
]

应用 Pinia

在项目根目录新建 store 目录,然后在其文件夹中新增 user.ts 文件,例如新增如下内容。

ts 复制代码
export const useUser = defineStore('user', {
  state: () => ({
    isLogin: false,
    hello: false,
    userName: '小熊'
  }),
  actions: {
    login() {
      this.isLogin = true
    },
    logout() {
      this.isLogin = false
      this.hello = false
    }
  },
  persist: true
})

接下来模拟登录和退出登录的效果,实现对 Pinia 的应用。

在项目根目录中创建 pages/login/index.vue 页面。

ts 复制代码
<template>
  <div @click="handleLogin">登录</div>
</template>

<script setup lang="ts">
import { useUser } from '~/store/user'

const router = useRouter()
const store = useUser()
const { isLogin } = storeToRefs(store)
const handleLogin = () => {
  store.login()
  router.push('/')
}
if (isLogin.value) {
  router.push('/')
}
</script>

修改项目根目录中的 pages/index.vue 文件中的内容。

ts 复制代码
<template>
  <div>Index Page</div>
  <NuxtLink to="/mine">个人中心</NuxtLink>
  <div @click="handleExit">退出登录</div>
</template>

<script setup lang="ts">
import { ElNotification } from 'element-plus'
import { useUser } from '~/store/user'

const router = useRouter()
const store = useUser()
const { isLogin, hello, userName } = storeToRefs(store)
console.log('object', isLogin.value)
if (!isLogin.value) {
  navigateTo('/login')
}
if (!hello.value) {
  ElNotification({
    title: '你好' + userName.value,
    message: '欢迎光临,前端庄园技术社区'
  })
  hello.value = true
}

const handleExit = () => {
  store.logout()
  router.push('/login')
}
</script>

实现效果如下,右侧弹框中成功读取存储在 store 中的用户信息。
代码提交记录

增加错误页面

在项目根目录新建 error.vue 页面,当页面发生错误的时候,如找不到页面的 404 错误,或者服务端报错等信息,可以在此页面中展示相关信息。

ts 复制代码
<template>
  <div class="min-h-screen bg-gray-100 flex flex-col items-center justify-center p-4">
    <div class="bg-white rounded-lg shadow-xl p-8 max-w-md w-full text-center">
      <Icon name="lucide:circle-alert" class="text-red-500 mb-4" size="80" />
      <h1 class="text-4xl font-bold text-gray-800 mb-2">{{ error?.statusCode }}</h1>
      <p class="text-xl text-gray-600 mb-8">{{ error?.message }}</p>
      <div>
        <el-button type="warning" plain round @click="retry">重试</el-button>
        <el-button round @click="handleError">返回首页</el-button>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
const props = defineProps({
  error: Object
})
console.log(props.error)
const retry = () => (window.location.href = props.error!.url)
const handleError = () => clearError({ redirect: '/' })
</script>

例如,在浏览器页面访问 http://localhost:3000/article 一个不存在的页面,此时就会显示相关的错误信息。
代码提交记录

新增导航栏

在项目根目录中的 components 文件夹下新建 NavBar.vue 导航栏文件。

ts 复制代码
<template>
  <div class="border-b-1 border-zinc-200">
    <div class="flex justify-between items-center max-w-[1200px] m-auto h-[60px]">
      <div class="h-[40px] flex items-center cursor-pointer">
        <img src="/assets/images/logo.png" class="w-full h-full" />
      </div>
      <div class="pr-2 flex items-center">
        <UButton icon="lucide:sun" size="xl" variant="ghost" class="cursor-pointer" color="neutral" @click="handleLight" />
        <UButton icon="lucide:moon" size="xl" variant="ghost" class="cursor-pointer" color="neutral" @click="handleDark" />
        <UButton icon="ion:extension-puzzle-outline" size="xl" variant="ghost" class="cursor-pointer" color="neutral" />
        <UButton icon="ion:notifications" size="xl" variant="ghost" class="cursor-pointer" color="neutral" />
        <UButton icon="logos:github-icon" size="xl" variant="ghost" class="cursor-pointer" color="neutral" to="https://www.baidu.com" target="_black" />

        <UDropdownMenu :items="items" class="w-48">
          <UAvatar src="https://github.com/benjamincanac.png" class="ml-3 cursor-pointer" />
        </UDropdownMenu>
      </div>
    </div>
  </div>
</template>

<script setup>
const colorMode = useColorMode()
const currentMode = ref('')
currentMode.value = colorMode.preference
// colorMode.value = 'light'
console.log('colorMode', colorMode)

const handleLight = () => {
  colorMode.preference = 'light'
  colorMode.value = 'light'
}

const handleDark = () => {
  colorMode.preference = 'dark'
  colorMode.value = 'dark'
}

const items = ref([
  [
    {
      label: 'Profile',
      icon: 'i-heroicons-user'
    },
    {
      label: 'Billing',
      icon: 'i-heroicons-credit-card'
    },
    {
      label: 'Settings',
      icon: 'i-heroicons-cog',
      kbds: [',']
    },
    {
      label: 'Keyboard shortcuts',
      icon: 'i-heroicons-computer-desktop'
    }
  ],
  [
    {
      label: 'Team',
      icon: 'i-heroicons-users'
    },
    {
      label: 'Invite users',
      icon: 'i-heroicons-user-plus',
      children: [
        [
          {
            label: 'Email',
            icon: 'i-heroicons-envelope'
          },
          {
            label: 'Message',
            icon: 'i-heroicons-chat-bubble-left'
          }
        ],
        [
          {
            label: 'More',
            icon: 'i-heroicons-plus-circle'
          }
        ]
      ]
    },
    {
      label: 'New team',
      icon: 'i-heroicons-plus',
      kbds: ['meta', 'n']
    }
  ],
  [
    {
      label: 'GitHub',
      icon: 'i-simple-icons-github',
      to: 'https://github.com/nuxt/ui',
      target: '_blank'
    },
    {
      label: 'Support',
      icon: 'i-heroicons-lifebuoy',
      to: '/components/dropdown-menu'
    },
    {
      label: 'API',
      icon: 'i-heroicons-cloud',
      disabled: true
    }
  ],
  [
    {
      label: 'Logout',
      icon: 'i-heroicons-arrow-right-on-rectangle',
      kbds: ['shift', 'meta', 'q']
    }
  ]
])
</script>

值得注意的是,其中 UDropdownMenu 组件使用的是 Nuxt UI 中的 DropdownMenu 组件。

而实现暗黑效果 使用的是 Nuxt Modules 中的 @nuxtjs/color-mode 模块,安装方法和上述介绍的安装 Nuxt 模块相同,参考文档安装即可。

图标使用的是 icones 官网中的图标,具体使用方法在上一节文章《【Nuxt 实战】01-初始化项目、新增组件库、图标库》中已经介绍过。

新增后的导航栏目前的效果是这样的,后期再继续优化。
代码提交记录

新增全局插件

封装插件

在项目根目录 plugins 文件夹内新增 toast.ts 文件,Nuxt 会自动识别这是一个插件,我们就可以在全局使用此插件实现对应的业务。

我们针对 Nuxt UI 中的 Toast 组件封装一个全局插件,用于在页面中使用。

ts 复制代码
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.hook('app:created', (vueApp) => {
    vueApp.config.globalProperties.$toast = (title: string, description?: string, color: string = 'success') => {
      const toast = useToast()
      toast.add({ title, description, color: color })
    }
  })
})

使用插件

值得注意的是,使用 Nuxt UI 中的 Toast 组件,首先需要在 app.vue 文件中引用 App 组件,这样我们就可以配置 Toast 组件的位置。

ts 复制代码
<template>
  <UApp :toaster="{ position: 'top-right', duration: 3000 }">
    <NuxtLayout>
      <NuxtPage></NuxtPage>
    </NuxtLayout>
  </UApp>
</template>

pages/index.vue 文件中使用插件。

ts 复制代码
<template>
  <div>Index Page</div>
  <NuxtLink to="/mine" class="text-4xl font-bold text-yellow-700">个人中心</NuxtLink>
  <div @click="handleExit">退出登录</div>
</template>

<script setup lang="ts">
useHead({
  title: '首页'
})

const ins = getCurrentInstance()
const router = useRouter()
const store = useUser()
const { isLogin, hello, userName } = storeToRefs(store)
const appConfig = useAppConfig()

if (!isLogin.value) {
  navigateTo('/login')
}
if (!hello.value) {
  ins?.proxy?.$toast('你好' + userName.value, appConfig.helloMessage)
  hello.value = true
}

const handleExit = () => {
  store.logout()
  router.push('/login')
  ins?.proxy?.$toast('退出登录成功', '', 'info')
}
</script>

代码提交记录

结束语

本节,我们实现了在项目中集成 Pinia、新增导航栏并实现暗黑模式,以及新建错误页面、新增插件并应用在项目中,接下来就是实现用户登录、新增 TipTap 编辑器、连接数据库等操作。

相关推荐
y先森43 分钟前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy43 分钟前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189111 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿2 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡3 小时前
commitlint校验git提交信息
前端
虾球xz3 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇3 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒3 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员4 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐4 小时前
前端图像处理(一)
前端