基于 Nuxt3 + Obsidian 搭建个人博客

Nuxt是一个用Vue来编写的,可用来创建类型安全、高性能和生产级全栈 Web 应用程序和网站的全栈框架。后端是 Nitro,一个可以被单独使用的Web服务端框架。

作为一个全栈框架,不仅具备了比使用Vue开发SPA客户端更好的开发体验,还能享受服务端渲染带来的SEO优化,同时Node服务可以实现帮你实现更多的可能性

之所以基于Nuxt从零搭建,一是为了选全栈,二是发现了nuxt/content可以读取本地的markdown文件,三是选的模板达不到想要的效果,四是过程可以作为写文章的素材。

初始化Nuxt项目

使用nuxt/content初始化项目,因为这个插件是博客的核心插件,所以初始化时直接装上就可以

npx nuxi@latest init content-app -t content

选择npmpnpmyarn作为包管理器后,会生成项目目录,下载依赖,我选了npm

在这里插入图片描述

此时直接运行 npm run dev 就可以启动项目了

启动后会有如下页面:

在这里插入图片描述

app.vue作为页面的入口文件,里面只有一个简单对的 NuxtPage ,作用类似于 Vue 中的 RouterView ,实际上它就是 RouterView 的包装。

同样的包装还有:NuxtLinkNuxtImg 等等。 更多 Nuxt Component

<template><div><NuxtPage /></div>
</template>

它用来显示 pages/ 下的页面

pages/ 是Nuxt生成路由的一个约定目录,其内的文件会自动生成路由。

pages/a.vue 会生成 /a 路由

如果想接收url参数可以这样写: /pages/a/[id].vue/pages/a-[group]/[id].vue

在文件内使用这种方式来获取参数,总之 [xxx] 是它的路由规则

<script setup lang="ts">
const route = useRoute()
console.log(route.params.id, route.params.group)
</script>

如果想匹配改路径下的所有路由,可以这样写:/pages/a/[...slug].vue,此时可以再去看一下
route.params.slug 的值是什么。

查看更多关于 Nuxt Pages 👈

使用Pages不仅仅是简化了Router的配置,也是为了服务端渲染,更好的SEO。做博客的话,当然希望别人搜索某些关键字可以直接搜到自己的文章页面。而不是全部内容都在一个单页面内,由浏览器渲染。

所以自带的两个页面是怎么渲染的就清楚了。

你可以看到上面我并没有引入useRoute,但也可以在vue文件中直接使用,因为Nuxt提前做好了自动import 的配置,它自动导入组件、可组合项、辅助函数和 Vue API

项目根目录下的 components/, composables/ , utils/ 都可以直接使用,无需手动导入。

如果你想手动导入此类API,可以使用#imports, 如 import { ref, computed } from '#imports'

如果使用了第三方的包,也想支持自动导入,可以在 nuxt.config.ts 中配置。

export default defineNuxtConfig({imports: {presets: [{from: 'vue-i18n',imports: ['useI18n']}]}
})

同时,nuxt.config.ts还可以配置 sourcemap ,全局css引入:csstailwindcss ,以及 nuxt/content 等等。

Nuxt的文档和Vue的类似,都非常全面,读过一遍后会对这个框架有比较清晰的了解,所以起步阶段还是建议先读文档

NuxtContent

项目搭建好了,它默认加载了两个页面,也就是两个md文件,是/content目录下的 index.mdabout.md

这不是重点,因为我也不会在这个项目目录下管理我的文章。

NuxtContent 支持配置多种文件的来源,看一下官方的配置

import { resolve } from "node:path";export default defineNuxtConfig({content: {sources: {// overwrite default source AKA `content` directorycontent: {driver: 'fs',prefix: '/docs', // All contents inside this source will be prefixed with `/docs`base: resolve(__dirname, 'content')},// Additional sourcesfa: {prefix: '/fa', // All contents inside this source will be prefixed with `/fa`driver: 'fs',// ...driverOptionsbase: resolve(__dirname, 'content-fa') // Path for source directory},github: {prefix: '/blog', // Prefix for routes used to query contentsdriver: 'github', // Driver used to fetch contents (view unstorage documentation)repo: "<owner>/<repo>",branch: "main",dir: "content", // Directory where contents are located. It could be a subdirectory of the repository.// Imagine you have a blog inside your content folder. You can set this option to `content/blog` with the prefix option to `/blog` to avoid conflicts with local files.},}}
})

可以看到不仅能用content目录,还能用content-fa目录,还能用github拉取。

而目录下base配置传入的是一个文件夹路径,所以我们这里直接写上自己经常用来写文章的一个目录绝对路径就可以了。

在目录下所有文章的改动会被监听,也就是在写文章时有什么改动会实时更新在本地服务的页面上,非常方便。

我建议在一个比较高级别的目录下分出几个单独的文件夹,往博客上发的全都放在一个比如blog的目录下,其他可能不是文章的,就还是该咋写咋写。

blog下的文章,有时候也不一定都能当天写完,nuxt/content支持使用 .- 作为文件的前缀时忽略此文章,不会被处理。Obsidian不支持使用.作为前缀,所以我用的-

blog下的文章可以随意划分文件夹,blog上不受影响,因为博客上的文章其实还是要根据tagcategory用代码逻辑划分,和本地的文件夹没什么关系。

所以此时我的配置就是这样的:

content: {sources: {obsidian: {prefix: '/obsidian', // All contents inside this source will be prefixed with `/fa`driver: 'fs',// ...driverOptionsbase: `/xxx/xxx/notion/blog` // Path for source directory},}
}

要想正常的使用本地文件,还需要做一些改动。

之前在Obsidian写东西,文件都是中文名,而content在处理中文名时会自动忽略,我搜了下各种issue 其他语言也存在这种问题。好在content处理前后分别给了一个钩子函数,不然这博客直接夭折了

在这里写一个处理函数 /server/plugins/content.ts (没有的目录要手动新建)

// @ts-ignore
export default defineNitroPlugin((nitroApp) => {nitroApp.hooks.hook('content:file:beforeParse', (file: { body: string }) => {// 匹配markdown文件内的元信息const match = file.body.match(/---\n([\s\S]+?)\n---\n([\s\S]*)/);if (match) {let frontMatter = match[1];const mainContent = match[2];// 如果不包含_path字段, 则使用title字段和一个前缀来生成_pathif (!frontMatter.includes('_path:')) {// 提取 title 字段的值const titleMatch = frontMatter.match(/title:\s*(.+)/);if (titleMatch && titleMatch.length > 1) {const titleValue = titleMatch[1].trim();const pathValue = `/post/${titleValue}`;// 将 _path 插入到 front-matter 中frontMatter = `_path: ${pathValue}\n` + frontMatter;} else {return;}}// 重新组合文件内容const newContent = `---\n${frontMatter}\n---\n${mainContent}`;file.body = newContent;}// 如果页面内没有 _path 属性, 则自动添加为 /blog/ + 文件名});
})

content拿到本地文件后,会编译一遍,然后放在 .nuxt 缓存,其处理后的文件内容,带有一个 _path 属性,这个属性就是页面上对应的文章的地址。

前面说中文地址会丢掉,也是这个_path出了问题,因为它默认是自己根据文件名生成的。

所以这里处理逻辑就是,处理原始内容前,给原始内容加上一个_path。当原始内容里带了_path,它就会优先用设置好的,不自动生成。

所以你如果愿意手动给自己的每个md文件都手动加上一个_path的话,也可以不用这个钩子。

以前还要注意一个问题:const pathValue = /post 这行代码,相当于写死了每篇文章的前缀是 /post

所以说 pages/ 下必须要有这样的结构 /pages/post/[...slug].vue

如果你想更改前缀,请手动同步更新这两处

这样,我们无需在项目中增加文章目录,就实现了从本地直接拉取文章文件。

Obsidian的小伙伴,可以用Linter这个插件格式化YAML属性 ,如datelastmodtitle,这三个是自动生成而且都是必用的。tagscategory 会用作分类和筛选。

在这里插入图片描述

那文章有了,在哪里点进去呢?

content 提供了 queryContent 来查询内容, 你可以这样来查询:

const contentQuery = queryContent('post')
// 总文章数 去除了被忽略的文章
const count = await queryContent('post').count()const contentQuery2 = queryContent('post')
// 按时间倒序以及分页后的数据
const pages await contentQuery2.sort({ date: -1 }).skip(skip).limit(pageSize.value).find()

skip limit sort 用这几个就能完成大部分操作,同时还支持 where 过滤内容,实现更多的分类查询功能。

关于queryContent的更多API👈

到这里,基于本地文件有了,也没破坏本地的写作流程,各种分类、搜索功能有了,作为一个最简博客已经五脏俱全。

后续我会继续分享我自己的博客建设过程。

最后一步就是发布到服务器

打包和部署

如果此时你没有初始化git,可以先初始化一下。

然后我要先说一个Nuxt和其他Node服务的不同点:

它打包后的文件内已经包含了node_modules ,也就是说打包后的output文件就是它的完全体,不需要再 npm install

而博客的文章,是本地某个目录下写的,用的别的软件如Obsidian 来管理。

这样就有三个问题:

1️⃣:打包要在本地打。因为博客文件在本地,其实也是在.nuxt内,但我们也不会把.nuxt 传到git上去。所以只能在本地打完了再传上去。

2️⃣:要修改.gitignore。这里要去掉两个 一个是 node_modules 一个是 dist

我前期因为只去了 node_modules 落下了 dist,还让我一度怀疑是Nuxt的重大bug。因为我在服务器配了giteaaction,git push上去时,需要把output整个都扔上去

而扔上去之后因为某些node_module插件少了dist目录,会导致有些导出引用不到,我在排查pm2日志时懒得cat整个errlog,直接用的pm2 logs Blog --lines 30,恰好没发现dist丢了的问题。

然后还去github不停的搜问题,还真被我搜出一些 vitenuxt 的早期bug,误以为现在还存在。于是就用重新npm install的方式暂时解决了问题。

直到我近几天才发现报错路径上有个dist,然后猛然想起 .gitignore里忽略里dist。我滴个老天爷。

3️⃣:要配个软件来写markdown。如果已经有了还好,但是可能有人用的vscode之类的东西来写md,我不确定方不方便管理 front matter

有了以上几点,基本就是本地写文章,写完跑一下build,然后push上去就完成部署了

是不是还挺丝滑的?比项目内管理MD文件要舒服很多吧

结语

以上就是用Nuxt搭建一个可以集成本地文件的博客的最简起手流程了

其中我也是踩了不少的坑,这里分享给大家!

后续关于个人博客的建设也会一直更新,欢迎关注~~

有任何问题也欢迎私信交流

更多实战内容,欢迎访问我的博客

👏👏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/854.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode刷题笔记—栈与队列

栈与队列 栈与队列是非常重要的基础数据结构&#xff0c;本文汇总了《代码随想录》和《Leetcode101》中关于栈与队列的练习题及其题解&#xff0c;旨在帮助读者更深入地理解相关概念和解题思路。如有疏漏或错误&#xff0c;恳请批评指正。 文章目录 栈与队列1. 栈[232. 用栈实…

MongoDB如何使用

1.简单介绍 MongoDB是一个开源、高性能、无模式的文档型数据库&#xff0c;当初的设计就是用于简化开发和方便扩展&#xff0c;是NoSQL数据库产品中的一种。是最 像关系型数据库&#xff08;MySQL&#xff09;的非关系型数据库。 MongoDB是一个基于分布式文件存储的数据库由C语…

二、BIO、NIO编程与直接内存、零拷贝

一、网络通信 1、什么是socket&#xff1f; Socket 是应用层与 TCP/IP 协议族通信的中间软件抽象层&#xff0c;它是一组接口&#xff0c;一般由操作 系统提供。客户端连接上一个服务端&#xff0c;就会在客户端中产生一个 socket 接口实例&#xff0c;服务端每接受 一个客户端…

git flow流程拆解实践指导

常听人说到git flow,但实际开发过程中是如何落地的? 现在让我们按实际工作中的步骤进行拆解,大家完全可以不用通读,当遇到相应流程步骤时能用上本说明进行查阅参考即可,希望对于推进git flow流程的实际落地起到一些积极的作用. 目录 正常版本开发 开始一个特性开发提测一个版…

Ollama私有化部署大语言模型LLM

目录 一、Ollama介绍 二、安装Ollama 1、标准安装 2、国内加速 三、升级Ollama版本 四、使用Ollama 1、启动ollama服务 systemctl start ollama.service ollama serve 2、使用ollama命令 ollama run 运行模型 ollama ps 查看正在运行的模型 ollama list 查看(本地)…

Matlab一些使用技巧

代码分段 两个百分号就可以实现代码的分段&#xff0c;不同段之间会以不同的背景色显示&#xff0c;方便调试 如下&#xff1a; %% 腐蚀 stlen TimeWidth*Fs/50; %线性算子的长度&#xff0c;1/100的脉宽&#xff0c;对应0.5us&#xff0c;15个采样点 stlen 100; SE strel…

改进萤火虫算法之七:基于自适应机制的萤火虫算法(Adaptive Firefly Algorithm, AFA)

基于自适应机制的萤火虫算法(Adaptive Firefly Algorithm, AFA)是一种结合了萤火虫算法与自适应调整机制的优化算法。 一、基本原理 萤火虫算法是一种基于群体智能的优化算法,其灵感来源于自然界中萤火虫通过闪光进行信息交互和相互吸引的行为。而基于自适应机制的萤火虫算法…

RabbitMQ基础(简单易懂)

RabbitMQ高级篇请看&#xff1a; RabbitMQ高级篇-CSDN博客 目录 什么是RabbitMQ&#xff1f; MQ 的核心概念 1. RabbitMQ 的核心组件 2. Exchange 的类型 3. 数据流向说明 如何安装RabbitQueue&#xff1f; WorkQueue&#xff08;工作队列&#xff09;&#xff1a; Fa…

VScode python 远程调试

https://zhuanlan.zhihu.com/p/564709397 VScode python 远程调试 launch.json 改变conda环境&#xff0c;直接在右下角选择

RuoYi Cloud项目解读【四、项目配置与启动】

四、项目配置与启动 当上面环境全部准备好之后&#xff0c;接下来就是项目配置。需要将项目相关配置修改成当前相关环境。 1 后端配置 1.1 数据库 创建数据库ry-cloud并导入数据脚本ry_2024xxxx.sql&#xff08;必须&#xff09;&#xff0c;quartz.sql&#xff08;可选&…

【深度学习】布匹寻边:抓边误差小于3px【附完整链接】

布匹寻边 项目简介 布匹寻边是指布料裁剪过程中&#xff0c;通过AI寻边技术自动识别布匹的边缘&#xff0c;将检测到的边缘信息输出&#xff0c;确保裁剪的准确性&#xff0c;减少浪费&#xff0c;并提高生产效率。 项目需求 将打满针眼的布匹边缘裁剪掉&#xff0c;且误差小…

LKT4304新一代算法移植加密芯片,守护物联网设备和云服务安全

凌科芯安作为一家在加密芯片领域深耕18年的企业&#xff0c;主推的LKT4304系列加密芯片集成了身份认证、算法下载、数据保护和完整性校验等多方面安全防护功能&#xff0c;可以为客户的产品提供一站式解决方案&#xff0c;并且在调试和使用过程提供全程技术支持&#xff0c;针对…

晨辉面试抽签和评分管理系统之六:面试答题倒计时

晨辉面试抽签和评分管理系统&#xff08;下载地址:www.chenhuisoft.cn&#xff09;是公务员招录面试、教师资格考试面试、企业招录面试等各类面试通用的考生编排、考生入场抽签、候考室倒计时管理、面试考官抽签、面试评分记录和成绩核算的面试全流程信息化管理软件。提供了考生…

2025封禁指定国家ip-安装xtables-addons记录

如何安装和使用 安装lux仓库(该仓库包含xtables-addons所需的依赖环境) # wget http://repo.iotti.biz/CentOS/7/noarch/lux-release-7-1.noarch.rpm # rpm -ivh lux-release-7-1.noarch.rpm 安装xtables-addons。注意&#xff1a;必须先安装kmod-xtables-addons&#xff0c;再…

使用RSyslog将Nginx Access Log写入Kafka

个人博客地址&#xff1a;使用RSyslog将Nginx Access Log写入Kafka | 一张假钞的真实世界 环境说明 CentOS Linux release 7.3.1611kafka_2.12-0.10.2.2nginx/1.12.2rsyslog-8.24.0-34.el7.x86_64.rpm 创建测试Topic $ ./kafka-topics.sh --zookeeper 192.168.72.25:2181/k…

[离线数仓] 总结二、Hive数仓分层开发

接 [离线数仓] 总结一、数据采集 5.8 数仓开发之ODS层 ODS层的设计要点如下: (1)ODS层的表结构设计依托于从业务系统同步过来的数据结构。 (2)ODS层要保存全部历史数据,故其压缩格式应选择压缩比率,较高的,此处选择gzip。 CompressedStorage - Apache Hive - Apac…

MySQL的小问题

编码问题 不管官方使用什么编码&#xff1a;latin1、gbk、utf8、utfmb4。统一使用utfmb4 MySQL中的utf8并不是utf-8&#xff0c;它省略了一个字节&#xff0c;只是用三个字节存储所有的符号&#xff0c;utfmb4才是utf-8 远程登录问题&#xff1a; MySQL官方默认没有启动远程…

一些计算机零碎知识随写(25年1月)-1

我原以为世界上有技术的那批人不会那么闲&#xff0c;我错了&#xff0c;被脚本真实了。 今天正隔着画画呢&#xff0c;手机突然弹出几条安全告警通知。 急忙打开服务器&#xff0c;发现问题不简单&#xff0c;直接关服务器重装系统..... 首先&#xff0c;不要认为小网站&…

golang OpcUaClient

实现功能 package mainimport ("fmt""log""opcuaclient/util/plugin/client/opcclient""os""os/signal""syscall" )func main() {OPCUATest()// 监听操作系统信号&#xff0c;阻塞直到接收到信号quit : make(chan…

【微服务】面试 1、概述和服务发现

微服务面试题 课程内容架构 Spring Cloud 部分 服务注册&#xff1a;重点讲解&#xff08;Nacos&#xff09;和&#xff08;Eureka&#xff09;&#xff0c;这是微服务架构中实现服务发现与注册的关键组件&#xff0c;确保服务间能够相互定位与通信。负载均衡&#xff1a;涵盖…