Nuxt3重写博客(一):首页设计和Vue3导航封装

1,113 阅读18分钟

image.png

【成品镇楼图】

原来用的是vue技术栈的vuepress,早就想重构一波。奈何本来就很懒工作忙起来更是不想动弹,所以在腾讯云的服务器到期之后就博客就一直处于无法访问的状态。但现在有了不得不重构的理由,刚好最近还有一些nuxt3的积累。

那就,干吧!

VuePress 是一个基于 Vue 的静态网站生成器,非常适合用来构建技术文档或个人博客。

不想瞎搞的,强烈推荐这个。基本上随便丢个静态代理服务上都行(ngxin、各家云的serverless、甚至是OSS这种对象存储都行)

VuePress 2.x 是VuePress的最新主要版本,它带来了对Vue 3的支持以及一系列的性能改进和新特性。VuePress 2.x 的设计目标是保持简单,同时提供足够的灵活性和性能,以满足现代web开发的需求。下面是VuePress 2.x的一些关键特性和改进点:

对Vue 3的支持

  • Vue 3支持:VuePress 2.x 完全支持Vue 3,这意味着你可以在Markdown文件和VuePress主题/插件中使用Vue 3的新特性和改进。
  • Composition API:得益于Vue 3,你现在可以在VuePress项目中使用Composition API,为开发者提供了更灵活的代码组织方式。

性能改进

  • 更快的热重载(HMR):VuePress 2.x 优化了开发服务器的热重载性能,使得在开发过程中的页面刷新和更新更加迅速。
  • 更高效的构建:通过改进的webpack配置和Vue 3的编译器优化,VuePress 2.x 在构建站点时更加高效,尤其是对于大型项目。

新特性和改进

  • 默认主题改进:VuePress 2.x 对默认主题进行了改进,包括对移动设备的更好支持、暗黑模式、可配置的导航栏和侧边栏等。
  • 插件系统:VuePress 2.x 继续支持通过插件扩展功能,同时也对插件系统进行了改进,使得开发和使用插件更加简单直观。
  • Markdown扩展:VuePress 2.x 支持更多Markdown扩展,如自定义容器、代码块行高亮等,使得内容编写更加灵活。

2024-05-16 12:15:04 版本是v2.0.0-rc.9

精品调研😛

昨天在电脑跟前呆了好一会儿(绝对不是偷懒啊!),应该从哪里开始呢?这是一个问题?!

先去看看别人加怎么写的吧😎

作家专区·两栏

image-20240516111344819.png

这是一个为小说作者工作(码字/搬砖)、学习、交流的综合平台,提供了多种功能和服务。以下是一些简单的产品设计分析:

顶部导航栏

  • 首页:返回主页面。
  • 推荐:推荐的文章。
  • 资讯:提供文学相关的新闻和动态。
  • 活动:展示平台举办的活动信息。
  • 下载助手:提供下载桌面端应用的入口。

主横幅

  • 活动广告:展示当前的征文活动或其他重要活动的广告横幅。

右侧栏

  • 限时活动:推广桌面端助手,吸引用户下载。
  • 功能快捷入口
    • 作品管理:用于管理用户的文学作品。
    • 行业内参:提供行业内的资讯和参考资料。
    • 工作台:是用户的个人工作空间。
  • 作家资讯:提供与作家相关的新闻和信息。
  • 联系编辑:提供与编辑联系的渠道,帮助用户解决问题。
  • 作家福利:展示平台为作家提供的福利和优惠。

中央内容区

  • 推荐话题:展示当前热门话题,用户可以点击查看详细内容。
  • 推荐文章:推荐一些优质的文章,帮助用户发现有趣的内容。

页面布局

  • 左侧主要内容:主要是推荐话题和文章,吸引用户阅读和参与讨论。
  • 右侧辅助功能:提供一些快捷功能入口和资讯,方便用户使用。

设计风格

  • 简洁明了:整体设计简洁,信息布局合理,用户可以快速找到所需内容。
  • 视觉吸引:通过图片和颜色的搭配,吸引用户的注意力。

用户体验

  • 易于导航:清晰的导航栏和功能分区,用户可以轻松找到所需的功能和信息。
  • 互动性强:通过推荐话题和文章,鼓励用户参与讨论和阅读,增加平台的互动性。

严格来说这个不应该放在这里,因为他不是个专门的博客平台。但是我很喜欢这种设计,简洁明了功能布局合理。没有新闻类后台那样繁杂的功能,使用户能够一眼找到自己需要的内容。

掘金本金·三栏

image-20240516113015625.png

顶部导航栏

  • 导航项:包括首页、探索、资讯、活动、讲座、APP、排行榜等,提供了多种功能入口。
  • 搜索栏:用户可以通过搜索栏快速查找相关内容。
  • 用户功能:包括用户的个人中心和创作者中心,方便用户管理个人信息和创作内容。

左侧导航栏

  • 分类目录:包括大模型平台、关注、综合、后端、前端、Android、iOS、开发工具、代码人生、阅读、排行榜等,用户可以根据兴趣选择不同的分类。
  • 分类筛选:帮助用户快速找到感兴趣的内容。

中央内容区

  • 推荐和最新:展示推荐和最新的技术文章和讨论,吸引用户阅读和参与。
  • 文章列表:每篇文章显示标题、简介、作者、发布时间、阅读量、评论数等信息,帮助用户快速了解文章内容和热度。

右侧栏

  • 上好课:推广课程,吸引用户参加学习。
  • 文选榜:展示当前热门文章排行榜,帮助用户发现优质内容。
  • 广告和活动:展示一些广告和活动信息,如创意有奖征文、创作者训练营等。
  • 作家榜:展示活跃的创作者和他们的最新动态,增加社区互动。

页面布局

  • 左侧分类导航:帮助用户快速找到感兴趣的内容分类。
  • 中央内容区:主要展示文章和讨论,吸引用户阅读和参与。
  • 右侧辅助功能:提供课程推广、热门文章、广告和活跃创作者信息。

设计风格

  • 简洁清晰:整体设计简洁,信息布局合理,用户可以快速找到所需内容。
  • 功能丰富:提供了多种功能和服务,满足用户的不同需求。

用户体验

  • 易于导航:清晰的导航栏和分类目录,用户可以轻松找到所需的功能和信息。
  • 互动性强:通过推荐和最新文章、热门文章排行榜、活跃创作者榜等,鼓励用户参与讨论和阅读,增加平台的互动性。

突击提问,你点过左侧导航吗?除了综合(可能还有关注)你还点过哪个?[坏笑]

InfoQ·两栏

image-20240516114247664.png

顶部导航栏

  • 导航项:包括首页、技术社区、专题、视频、写作社区、资讯、研究中心、话题、活动等,提供了多种功能入口。
  • 搜索栏:用户可以通过搜索栏快速查找相关内容。
  • 用户功能:包括用户的个人中心和发布内容的入口,方便用户管理个人信息和发布内容。

主横幅

  • 主要新闻:展示当前的重要技术新闻和视频,吸引用户关注。

中央内容区

  • 推荐视频:展示一些推荐的视频内容,吸引用户观看。
  • 精品专题:展示一些精选的专题内容,帮助用户深入了解某一领域的技术。
  • 写作社区:展示用户在社区中的创作,鼓励用户参与和分享。
  • 最新文章:展示最新发布的技术文章,吸引用户阅读和讨论。

右侧栏

  • 精选大会:展示一些即将举行的技术大会,吸引用户参与。
  • 热门话题:展示当前热门的话题和讨论,帮助用户发现热门内容。
  • 广告和活动:展示一些广告和活动信息,如创意有奖征文等。

页面布局

  • 左侧主要内容:主要是推荐视频、精品专题和写作社区,吸引用户阅读和参与。
  • 右侧辅助功能:提供精选大会、热门话题和广告,增加用户的参与度。

设计风格

  • 简洁明了:整体设计简洁,信息布局合理,用户可以快速找到所需内容。
  • 视觉吸引:通过图片和视频的搭配,吸引用户的注意力。

用户体验

  • 易于导航:清晰的导航栏和分类目录,用户可以轻松找到所需的功能和信息。
  • 互动性强:通过推荐视频、精品专题、写作社区和热门话题,鼓励用户参与讨论和阅读,增加平台的互动性。

这个其实是个综合类平台,因为除了普通的图文技术文章之外他们还有技术新闻、专题、视频和社区互动以及各种演讲和活动。

我当年入坑fastify就是在这里看了一个21年的演讲十亿级 Node.js 网关的架构设计与工程实践

我的设计

img_v3_02au_1241db53-5f3a-4db3-853c-bfe802f5295g.jpg

顶部导航栏

  • Logo:位于左上角,便于用户识别网站品牌。(比如放上谁的照片?)
  • 导航:横向导航栏,包含主要功能入口,方便用户快速访问不同板块。
  • 登录状态和用户信息:位于右上角,方便用户查看和管理个人信息。

主内容区

  • 滚动推荐区:位于页面顶部,展示重要推荐内容,吸引用户注意力。
  • 活动卡片:展示多个活动卡片,提供当前的活动信息,鼓励用户参与。
  • 推荐文章、最新文章、关注的tag:通过标签切换展示不同类别的文章,方便用户根据兴趣选择阅读内容。
  • 文章列表:展示文章列表,用户可以浏览和点击阅读详细内容。

右侧栏

  • 用户卡片:展示用户信息,增强用户的个人体验。(这个估计最晚上,需要搞了扫码或者关注登录了才考虑)
  • 搜索热门词条:展示当前热搜词条,帮助用户了解热点话题。
  • 网站信息/流量情况:展示网站的相关信息和流量情况,增加透明度。

底部

  • 备案信息、网站地图:展示网站的备案信息和网站地图,增加网站的可信度和导航便利性。

页面布局

  • 左侧主内容:主要展示推荐内容和文章列表,吸引用户阅读和参与。
  • 右侧辅助功能:提供用户信息、热门词条和网站信息,增加用户的参与度和网站的透明度。

需求与开发

功能模块

  1. Logo、顶部导航栏和登录状态及用户信息
  2. 滚动推荐区
  3. 活动卡片
  4. 推荐文章、最新文章、关注的tag(标签切换)
  5. 文章列表
  6. 用户卡片
  7. 搜索热门词条
  8. 网站信息/流量情况
  9. 备案信息、网站地图

开发顺序

  1. 基础布局和样式

    • 顶部导航:实现基础布局和样式,包括Logo展示、导航栏和用户登录状态。
  2. 核心内容区

    • 滚动推荐区:实现推荐内容的滚动展示,吸引用户注意力。
    • 文章列表:实现文章列表的基础布局和数据展示,这是用户主要交互的内容区域。
  3. 次要内容区

    • 活动卡片:实现活动卡片的布局和内容展示。
    • 推荐文章、最新文章、关注的tag(标签切换) :实现标签切换功能,展示不同类别的文章。
    • 用户卡片:实现用户信息卡片的布局和数据展示。
  4. 搜索和热门词条

    • 搜索热门词条:实现搜索功能和热门词条展示。
  5. 辅助信息

    • 网站信息/流量情况:实现网站信息和流量情况的展示。
    • 备案信息、网站地图:实现备案信息和网站地图的展示。

导航设计

目前旧版本博客的导航如下

  • JavaScript
  • Nodejs
  • Python
  • Golang
  • 算法
  • 云计算
  • 人工智能

现在除了这些还有一些从0到1的项目实战以及开源项目文档,如果都使用一级导航就太长了必须要搞个二级导航出来。

初稿

<template>
  <!-- 导航栏容器 -->
  <nav class="navbar">
    <!-- 导航栏左侧,通常用于展示品牌Logo或名称 -->
    <div class="navbar-left">
      <span class="brand-name">Logo</span> <!-- 品牌名称或Logo -->
    </div>
    <!-- 导航栏中间部分,用于展示导航链接 -->
    <div class="navbar-center">
      <!-- 导航链接的列表 -->
      <ul class="nav-links">
        <!-- 单个导航项,指向首页 -->
        <li><a href="#">首页</a></li>
        <!-- 下拉菜单的导航项 -->
        <li class="dropdown">
          <a href="#">二级菜单</a> 
          <!-- 下拉菜单内容 -->
          <div class="dropdown-content">
            <!-- 下拉菜单内的一个子菜单区块 -->
            <div class="dropdown-section">
              <h3>子菜单1</h3> <!-- 子菜单标题 -->
              <p>子菜单1的描述</p> <!-- 子菜单描述 -->
            </div>
            <div class="dropdown-section">
              <h3>子菜单2</h3> 
              <p>子菜单2的描述</p>
            </div>
          </div>
        </li>
        <!-- 其他普通的导航项 -->
        <li><a href="#">普通菜单</a></li>
        <!-- 特殊标记或图标的导航项 -->
        <li><a href="#">🍁实战</a></li>
      </ul>
    </div>
    <!-- 导航栏右侧,通常用于用户相关的操作,如登录、注册等 -->
    <div class="navbar-right">
      <button class="btn login">登录</button>
      <button class="btn trial">试用</button> 
    </div>
  </nav>
</template>
<style scoped>
.navbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 20px;
  background-color: #fff;
  border-bottom: 1px solid #eaeaea;
}

.navbar-left {
  display: flex;
  align-items: center;
}

.logo {
  width: 40px;
  height: 40px;
  margin-right: 10px;
}

.brand-name {
  font-size: 20px;
  font-weight: bold;
}

.navbar-center {
  flex-grow: 1;
}

.nav-links {
  display: flex;
  list-style: none;
  padding: 0;
  margin: 0;
}

.nav-links li {
  margin: 0 15px;
  position: relative;
}

.nav-links a {
  text-decoration: none;
  color: #333;
}

.nav-links .dropdown-content {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  background-color: #fff;
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
  z-index: 1;
}

.nav-links .dropdown:hover .dropdown-content {
  display: block;
}

.dropdown-section {
  padding: 10px;
}

.navbar-right {
  display: flex;
  align-items: center;
}

.btn {
  padding: 5px 10px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

.btn.login {
  background-color: #007bff;
  color: #fff;
  margin-right: 10px;
}

.btn.trial {
  background-color: #ff5722;
  color: #fff;
}
</style>

image-20240516125150072.png 有点导航的意思了,但是还需要改一下:

  • 下拉菜单应该有个适合的宽度
  • Logo这里不能只用文字
  • 是否可以下拉应该有提示
  • 组件化了之后,导航应该是用户传递进来的

完整组件代码

<template>
  <nav class="navbar">
    <div class="navbar-left">
      <slot name="logo">
        <img :src="logo.src" alt="Logo" class="logo" />
        <span class="brand-name">{{ logo.title }}</span>
      </slot>
    </div>
    <div class="navbar-center">
      <ul class="nav-links">
        <li
          v-for="(item, index) in items"
          :key="index"
          class="nav-item"
          @mouseover="showDropdown(index)"
          @mouseleave="hideDropdown(index)"
        >
          <a href="#">
            {{ item.title }}
            <span
              v-if="item.subItems && item.subItems.length"
              class="arrow"
              :class="{
                up: activeDropdown === index,
                down: activeDropdown !== index,
              }"
            ></span>
          </a>
          <div
            v-if="item.subItems && item.subItems.length"
            class="dropdown-content"
            :class="{ show: activeDropdown === index }"
          >
            <div
              v-for="(subItem, subIndex) in item.subItems"
              :key="subIndex"
              class="dropdown-section"
            >
              <h3>{{ subItem.title }}</h3>
              <p>{{ subItem.description }}</p>
            </div>
          </div>
        </li>
      </ul>
    </div>
    <div class="navbar-right">
      <slot name="login">
        <button class="btn login">登录</button>
        <button class="btn trial">注册</button>
      </slot>
    </div>
  </nav>
</template><script setup lang="ts">
import { ref } from "vue";
import defaultLogoSrc from "../public/vue.png";
​
interface Logo {
  title: string;
  src: string;
}
​
interface SubItem {
  title: string;
  description: string;
}
​
interface NavItem {
  title: string;
  link: string;
  subItems?: SubItem[];
}
​
const props = withDefaults(
  defineProps<{
    items: NavItem[];
    logo?: Logo;
  }>(),
  {
    logo: () => {
      return {
        title: "自定义品牌",
        src: defaultLogoSrc,
      };
    },
  }
);
​
const activeDropdown = ref<number | null>(null);
​
const showDropdown = (index: number) => {
  activeDropdown.value = index;
};
​
const hideDropdown = (index: number) => {
  if (activeDropdown.value === index) {
    activeDropdown.value = null;
  }
};
​
</script><style scoped>
.navbar {
  position: fixed;
  top: 0;
  width: 100vw;
  height: 65px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 20px;
  background-color: #fff;
  border-bottom: 1px solid #eaeaea;
}
​
.navbar-left {
  display: flex;
  align-items: center;
}
​
.logo {
  width: 40px;
  height: 40px;
  margin-right: 10px;
}
​
.brand-name {
  font-size: 20px;
  font-weight: bold;
}
​
.navbar-center {
  display: flex;
}
​
.nav-links {
  display: flex;
  list-style: none;
  padding: 0;
  margin: 0;
}
​
.nav-item {
  margin: 0 15px;
  position: relative;
}
​
.nav-item a {
  text-decoration: none;
  color: #333;
  padding: 10px;
  display: flex;
  align-items: center;
}
​
.nav-item .arrow {
  margin-left: 5px;
  border: solid black;
  border-width: 0 2px 2px 0;
  display: inline-block;
  padding: 3px;
}
​
.nav-item .arrow.up {
  transform: rotate(-135deg);
  -webkit-transform: rotate(-135deg);
}
​
.nav-item .arrow.down {
  transform: rotate(45deg);
  -webkit-transform: rotate(45deg);
}
​
.nav-item .dropdown-content {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  background-color: #fff;
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
  z-index: 1;
  width: 200px;
  border: 1px solid #eaeaea;
}
​
.nav-item .dropdown-content.show {
  display: block;
}
​
.dropdown-section {
  padding: 10px;
  border-bottom: 1px solid #eaeaea;
}
​
.dropdown-section:last-child {
  border-bottom: none;
}
​
.nav-item a:hover {
  color: #007bff;
}
​
.dropdown-content h3 {
  margin: 0;
  font-size: 14px;
  font-weight: bold;
}
​
.dropdown-content p {
  margin: 0;
  font-size: 12px;
  color: #666;
}
​
.navbar-right {
  display: flex;
  align-items: center;
}
​
.btn {
  padding: 5px 10px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}
​
.btn.login {
  background-color: #007bff;
  color: #fff;
  margin-right: 10px;
}
​
.btn.trial {
  background-color: #ff5722;
  color: #fff;
}
</style>

特点

  1. 动态下拉菜单:导航项可以包含子菜单,通过鼠标悬停展开,提供了更多的导航选项而不占用额外空间。
  2. 可定制Logo和按钮:通过插槽允许自定义Logo和登录/注册按钮,提高了组件的可重用性和灵活性。
  3. 响应式设计:导航栏适应不同屏幕尺寸,保证用户体验。
  4. TypeScript支持:使用TypeScript进行类型注解,增强了代码的可读性和维护性。

关键技术

  • Composition API:使用Vue 3的Composition API(特别是ref来创建响应式变量),使得状态管理更加直观和灵活。
  • <script setup>语法糖:简化了组件的书写和组织方式,使得代码更加紧凑和易于理解。
  • 插槽(Slots) :提供内容分发的能力,允许用户自定义组件的特定部分。
  • 动态类和样式:使用Vue的动态类(:class)和样式(:style)绑定,根据组件的状态改变元素的外观和行为。
  • 事件处理:利用@mouseover@mouseleave事件监听器动态展示和隐藏下拉菜单。
  • CSS样式:通过<style scoped>定义组件局部样式,确保样式的封装性和组件的独立性。

插槽这里可以看下我另外一篇文章vue3 插槽一览和封装新闻卡片里面封装的新闻卡片就是给博客用的

在Nuxt3 Layouts中使用

文档传送门@ Nuxt3 Layouts

在Nuxt 3中使用布局(Layouts)是组织和共享页面布局的一种高效方式。布局可以定义应用的外观和结构,比如头部、尾部和侧边栏,这样你就不需要在每个页面中重复这些元素了。以下是在Nuxt 3项目中使用布局的基本步骤:

1. 创建布局文件

Nuxt 3自动识别layouts目录中的Vue文件作为应用的布局。首先,确保你在项目根目录下有一个layouts文件夹。如果没有,就创建一个。

例如,创建一个名为default.vue的布局文件,这将作为大多数页面的默认布局:

<!-- layouts/default.vue -->
<template>
  <div class="pc">
    <NavBar :items="navItems"> </NavBar>
    <main>
      <slot />
    </main>
  </div>
</template><script setup>
const navItems = [
  { title: "首页", link: "#" },
  {
    title: "编程语言",
    link: "#",
    subItems: [
      { title: "JavaScript", description: "轻松创建交互式网页" },
      { title: "Python", description: "广泛应用的高级编程语言" },
      { title: "Golang", description: "Google开发的静态强类型编译语言" },
    ],
  },
  {
    title: "技术领域",
    link: "#",
    subItems: [
      { title: "算法", description: "解决问题的方法与步骤" },
      { title: "云计算", description: "按需获取计算资源的服务" },
      { title: "人工智能", description: "模拟人类智能的技术" },
    ],
  },
  { title: "项目与实践", link: "#" },
  { title: "资源分享", link: "#" },
  { title: "社区交流", link: "#" },
  { title: "关于我", link: "#" },
];
</script><style>
/* 全局样式 */
:root {
  --pc-width: 1278px;
}
.pc {
  width: 100vw;
  height: 100vh;
  background: #f2f3f5;
}
.pc main {
  width: var(--pc-width);
  height: 100vh;
  margin: 0 auto;
  padding: 0 10px;
  padding-top: calc(65px + 10px);
  border: #666 solid 1px;
}
</style>

在这个布局中,<NuxtPage />是一个特殊的组件,用于渲染当前的页面组件。

2. 创建页面

pages目录下创建或修改Vue文件。Nuxt 3会自动将这些文件视为路由,并使用默认布局(layouts/default.vue),除非你指定了其他布局。

<!-- pages/index.vue -->
<template>
  <div>主页</div>
</template>

3. 修改app.vue以使用布局

如果你想为特定页面使用不同的布局,首先在layouts目录下创建一个新的布局文件,比如special.vue

<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>
<style>
* {
  border: none;
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}
</style>

在这个例子中,<NuxtLayout>是一个容器,它会根据当前页面使用的布局来渲染内容。<NuxtPage>是当前激活的页面组件。这种结构确保了你的应用中的每个页面都会被包裹在一个统一的布局中。

使用默认布局

如果你没有为某个页面特别指定布局,Nuxt 3会查找并使用名为default.vue的布局。因此,确保在layouts目录中有一个default.vue文件,这样它就会被<NuxtLayout>自动识别和使用。

指定不同的布局

如果你想为特定页面指定不同的布局,你需要在该页面的<script setup>中使用definePageMeta函数来指定布局名称:

<!-- pages/special.vue -->
<template>
  <div>This page uses a special layout!</div>
</template><script setup>
definePageMeta({
  layout: 'special'
})
</script>

在这个例子中,special.vue页面会使用layouts/special.vue布局(如果存在)而不是默认布局。

小结

通过在app.vue中使用<NuxtLayout><NuxtPage>组件,你可以为你的Nuxt 3应用定义一个全局的布局结构。这种方式简化了布局管理,并确保了布局的一致性和可重用性。通过创建不同的布局文件并在页面级别上指定它们,你可以轻松地为应用中的不同部分提供独特的布局。

总结

这是本系列专栏的开篇之作,后续预计有个十几张的样子。从基础开始使用Vue/Nuxt3技术栈开发一个个人博客项目,后续代码会上传开源社区。

本文的从几个作者常用的网站的借鉴设计的博客的首页,并按照功能设计了开发顺序。在这一章中,主要围绕这导航和布局等内容顺便复习了一下插槽、参数传递等vue3基本功能