Compare commits
10 Commits
ffebcb1099
...
0d686e09a1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d686e09a1 | ||
|
|
a515ecae39 | ||
|
|
cce2791c3f | ||
|
|
1967e05ec1 | ||
|
|
f84e030c1e | ||
|
|
2aa978451d | ||
|
|
14d4e6e94b | ||
|
|
33f9f8f0c8 | ||
|
|
a87c93acd6 | ||
|
|
a2d19f5045 |
32
README.md
32
README.md
@ -60,27 +60,25 @@
|
|||||||
- 执行 pnpm newpost '文章标题' 创建新文章,并在 src/content/posts/ 目录中编辑
|
- 执行 pnpm newpost '文章标题' 创建新文章,并在 src/content/posts/ 目录中编辑
|
||||||
- 参考官方指南将博客部署至 Vercel, Netlify,Cloudflare Pages, GitHub Pages 等
|
- 参考官方指南将博客部署至 Vercel, Netlify,Cloudflare Pages, GitHub Pages 等
|
||||||
|
|
||||||
### ⚠️ Hexo 迁移 Astro 方法
|
### Vercel 自动部署
|
||||||
|
|
||||||
> `Hexo` 的部署、使用、自动化部署等方法 完全适用于 `Astro`,只需要将 `Hexo` 博客的 `src/_posts/` 目录下的文章文件迁移至 `Astro` 目录下的 `src/content/blog/` 目录下即可,然后自定义 `src/config.ts` 配置文件去自定义博客。<br> 此时,你已成功迁移 Hexo 博客至 Astro 博客!
|
|
||||||
|
|
||||||
### 自动部署
|
|
||||||
|
|
||||||
**Vercel 部署:**
|
|
||||||
|
|
||||||
[](https://vercel.com/new/clone?repository-url=https://github.com/uxiaohan/vhAstro-Theme)
|
[](https://vercel.com/new/clone?repository-url=https://github.com/uxiaohan/vhAstro-Theme)
|
||||||
|
|
||||||
|
### Cloudflare Pages 自动部署
|
||||||
|
|
||||||
|
[](https://dash.cloudflare.com/?to=/:account/workers-and-pages/create/deploy-to-workers&repository=https://github.com/uxiaohan/vhAstro-Theme)
|
||||||
|
|
||||||
### 使用命令拉取模板
|
### 使用命令拉取模板
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# pnpm
|
# 使用 pnpm
|
||||||
pnpm create astro@latest --template uxiaohan/vhAstro-Theme
|
pnpm create astro@latest --template uxiaohan/vhAstro-Theme astro-blog
|
||||||
|
# 或者 yarn
|
||||||
# yarn
|
yarn create astro --template uxiaohan/vhAstro-Theme astro-blog
|
||||||
yarn create astro --template uxiaohan/vhAstro-Theme
|
# 或者 npm
|
||||||
|
npm create astro@latest -- --template uxiaohan/vhAstro-Theme astro-blog
|
||||||
# npm
|
# 进入项目目录
|
||||||
npm create astro@latest -- --template uxiaohan/vhAstro-Theme
|
cd astro-blog
|
||||||
```
|
```
|
||||||
|
|
||||||
### 本地开发
|
### 本地开发
|
||||||
@ -96,6 +94,10 @@ pnpm build
|
|||||||
pnpm newpost '文章标题'
|
pnpm newpost '文章标题'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### ⚠️ Hexo 迁移 Astro 方法
|
||||||
|
|
||||||
|
> 将 `Hexo` 博客的 `src/_posts/` 目录下的文章文件,复制到 `Astro` 的 `src/content/blog/` 目录下即可,然后自定义 `src/config.ts` 配置文件去自定义博客。<br>⚠️ `Hexo` 的部署、使用、自动化部署等方法 完全适用于 `Astro` 博客!<br>🎉 此时,你已成功迁移 Hexo 博客至 Astro 博客!
|
||||||
|
|
||||||
## 🍬 特色页面
|
## 🍬 特色页面
|
||||||
|
|
||||||
### 友情链接
|
### 友情链接
|
||||||
|
|||||||
14
package.json
14
package.json
@ -12,13 +12,13 @@
|
|||||||
"ondev": "astro preferences enable devToolbar"
|
"ondev": "astro preferences enable devToolbar"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/mdx": "^4.2.3",
|
"@astrojs/mdx": "^4.2.6",
|
||||||
"@astrojs/rss": "^4.0.11",
|
"@astrojs/rss": "^4.0.11",
|
||||||
"@astrojs/sitemap": "^3.3.0",
|
"@astrojs/sitemap": "^3.4.0",
|
||||||
"@swup/astro": "^1.6.0",
|
"@swup/astro": "^1.6.0",
|
||||||
"aplayer": "^1.10.1",
|
"aplayer": "^1.10.1",
|
||||||
"astro": "^5.6.0",
|
"astro": "^5.7.12",
|
||||||
"overlayscrollbars": "^2.11.1",
|
"overlayscrollbars": "^2.11.2",
|
||||||
"vanilla-lazyload": "^19.1.3",
|
"vanilla-lazyload": "^19.1.3",
|
||||||
"vh-plugin": "^1.2.2"
|
"vh-plugin": "^1.2.2"
|
||||||
},
|
},
|
||||||
@ -26,10 +26,10 @@
|
|||||||
"@playform/compress": "^0.1.9",
|
"@playform/compress": "^0.1.9",
|
||||||
"@types/dplayer": "^1.25.5",
|
"@types/dplayer": "^1.25.5",
|
||||||
"@types/nprogress": "^0.2.3",
|
"@types/nprogress": "^0.2.3",
|
||||||
"@waline/client": "^3.5.6",
|
"@waline/client": "^3.5.7",
|
||||||
"cheerio": "^1.0.0",
|
"cheerio": "^1.0.0",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"less": "^4.2.2",
|
"less": "^4.3.0",
|
||||||
"mdast-util-to-string": "^4.0.0",
|
"mdast-util-to-string": "^4.0.0",
|
||||||
"reading-time": "^1.5.0",
|
"reading-time": "^1.5.0",
|
||||||
"rehype-katex": "^7.0.1",
|
"rehype-katex": "^7.0.1",
|
||||||
@ -37,7 +37,7 @@
|
|||||||
"remark-directive": "^4.0.0",
|
"remark-directive": "^4.0.0",
|
||||||
"remark-math": "^6.0.0",
|
"remark-math": "^6.0.0",
|
||||||
"remark-sectionize": "^2.1.0",
|
"remark-sectionize": "^2.1.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.3",
|
||||||
"unist-util-visit": "^5.0.0"
|
"unist-util-visit": "^5.0.0"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
|
|||||||
@ -15,8 +15,7 @@ if (!articleName) {
|
|||||||
const ArticleContent = `---
|
const ArticleContent = `---
|
||||||
title: "${articleName.replace(/"/g, '\\"')}"
|
title: "${articleName.replace(/"/g, '\\"')}"
|
||||||
categories: 分类
|
categories: 分类
|
||||||
tags:
|
tags: ['标签']
|
||||||
- 标签
|
|
||||||
id: "${articleID.slice(0, 16)}"
|
id: "${articleID.slice(0, 16)}"
|
||||||
date: ${dayjs().format('YYYY-MM-DD HH:mm:ss')}
|
date: ${dayjs().format('YYYY-MM-DD HH:mm:ss')}
|
||||||
cover: "封面图URL (为空默认随机内置封面 /public/assets/images/banner)"
|
cover: "封面图URL (为空默认随机内置封面 /public/assets/images/banner)"
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
---
|
---
|
||||||
import { Image } from "astro:assets";
|
|
||||||
import { getDescription, fmtTime } from "@/utils/index";
|
import { getDescription, fmtTime } from "@/utils/index";
|
||||||
const { post } = Astro.props;
|
const { post } = Astro.props;
|
||||||
// 获取文章的摘要
|
// 获取文章的摘要
|
||||||
@ -11,16 +10,14 @@ const ARTICLE_COVER: string = await getCover(post.data.cover);
|
|||||||
import "./ArticleCard.less";
|
import "./ArticleCard.less";
|
||||||
---
|
---
|
||||||
|
|
||||||
<article class="vh-article-item vh-animation vh-animation-init">
|
<article class={`vh-article-item vh-animation vh-animation-init vh-article-link${post.data.top ? " active" : ""}`}>
|
||||||
<a class={`vh-article-link${post.data.top ? " active" : ""}`} href={`/article/${post.data.id}`}>
|
<section class="vh-article-banner"><img src="/assets/images/lazy-loading.webp" data-vh-lz-src={ARTICLE_COVER} alt={post.data.title} /></section>
|
||||||
<section class="vh-article-banner"><Image src="/assets/images/lazy-loading.webp" data-vh-lz-src={ARTICLE_COVER} alt={post.data.title} width="1" height="1" /></section>
|
<header>
|
||||||
<section class="vh-article-desc">
|
<h3><a class={`vh-article-cat vh-cat-${post.data.categories}`} href={`/categories/${post.data.categories}`}>{post.data.categories}</a><time>{fmtTime(post.data.date)}</time></h3>
|
||||||
<header>
|
<h1 class="title"><a class="vh-ellipsis" href={`/article/${post.data.id}`}>{post.data.title}</a></h1>
|
||||||
<span class={`vh-article-cat vh-cat-${post.data.categories}`}>{post.data.categories}</span>
|
</header>
|
||||||
<h2 class="title vh-ellipsis">{post.data.title}</h2>
|
<h2 class="vh-article-excerpt vh-ellipsis line-2">{description}</h2>
|
||||||
</header>
|
<h4 class="vh-article-taglist vh-ellipsis">
|
||||||
<p class="vh-article-excerpt vh-ellipsis line-2">{description}</p>
|
{post.data.tags.map((tag: string) => <a href={`/tag/${tag}`}>{tag}</a>)}
|
||||||
<footer>{fmtTime(post.data.date)}</footer>
|
</h4>
|
||||||
</section>
|
|
||||||
</a>
|
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@ -16,7 +16,10 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&>.vh-article-item {
|
&>.vh-article-item {
|
||||||
|
position: relative;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: initial;
|
height: initial;
|
||||||
min-height: max-content;
|
min-height: max-content;
|
||||||
@ -25,173 +28,207 @@
|
|||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&>a.vh-article-link {
|
&:hover {
|
||||||
position: relative;
|
&>.vh-article-banner {
|
||||||
box-sizing: border-box;
|
&>img {
|
||||||
display: flex;
|
transform: scale(1.08);
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
height: max-content;
|
|
||||||
min-height: 100%;
|
|
||||||
border-radius: 0.618rem;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: background 0.18s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
&>.vh-article-banner {
|
|
||||||
&>img {
|
|
||||||
transform: scale(1.08);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
&::before {
|
&::before {
|
||||||
content: "置顶";
|
content: "置顶";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 1.6rem;
|
top: 1.6rem;
|
||||||
left: 0.66rem;
|
left: 0.66rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 0.18rem 1rem;
|
padding: 0.18rem 1rem;
|
||||||
width: max-content;
|
width: max-content;
|
||||||
height: max-content;
|
height: max-content;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-radius: 0.66rem;
|
border-radius: 0.66rem;
|
||||||
transform: rotate(-45deg);
|
transform: rotate(-45deg);
|
||||||
/* 确保元素有半透明背景 */
|
/* 确保元素有半透明背景 */
|
||||||
background: #00000033;
|
background: #00000033;
|
||||||
box-shadow: 0 2rem 1rem rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2rem 1rem rgba(0, 0, 0, 0.1);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
/* 高斯模糊效果 */
|
/* 高斯模糊效果 */
|
||||||
backdrop-filter: blur(6px);
|
backdrop-filter: blur(6px);
|
||||||
-webkit-backdrop-filter: blur(6px);
|
-webkit-backdrop-filter: blur(6px);
|
||||||
/* 兼容旧版浏览器 */
|
/* 兼容旧版浏览器 */
|
||||||
}
|
|
||||||
|
|
||||||
&>.vh-article-banner {
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: rgba(0, 0, 0, 0.06);
|
|
||||||
z-index: 1;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
&>.vh-article-banner {
|
&>.vh-article-banner {
|
||||||
flex-shrink: 0;
|
&::before {
|
||||||
position: relative;
|
content: '';
|
||||||
box-sizing: border-box;
|
position: absolute;
|
||||||
width: 100%;
|
top: 0;
|
||||||
height: 12rem;
|
left: 0;
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
&>img {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
background: rgba(0, 0, 0, 0.06);
|
||||||
|
z-index: 1;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
&>.vh-article-banner {
|
||||||
|
flex-shrink: 0;
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
height: 12rem;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&>img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&>header {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0.88rem 1.166rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.66rem;
|
||||||
|
width: 100%;
|
||||||
|
height: max-content;
|
||||||
|
|
||||||
|
&>h3 {
|
||||||
|
display: flex;
|
||||||
|
align-self: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
height: max-content;
|
||||||
|
|
||||||
|
&>.vh-article-cat {
|
||||||
|
flex-shrink: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: max-content;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 700;
|
||||||
|
/* 默认渐变色起始 */
|
||||||
|
--gradient-color-1: #a32cc4;
|
||||||
|
/* 默认渐变色中间 */
|
||||||
|
--gradient-color-2: #b65fcf;
|
||||||
|
/* 默认渐变色结束 */
|
||||||
|
--gradient-color-3: #e39ff6;
|
||||||
|
background: linear-gradient(118deg, var(--gradient-color-1), var(--gradient-color-2) 26%, var(--gradient-color-3));
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
color: transparent;
|
||||||
|
|
||||||
|
&.vh-cat-Code {
|
||||||
|
/* 默认渐变色起始 */
|
||||||
|
--gradient-color-1: #f48600;
|
||||||
|
/* 默认渐变色中间 */
|
||||||
|
--gradient-color-2: #ffa12e;
|
||||||
|
/* 默认渐变色结束 */
|
||||||
|
--gradient-color-3: #ffe9c9;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.vh-cat-Daily {
|
||||||
|
/* 默认渐变色起始 */
|
||||||
|
--gradient-color-1: #6270ee;
|
||||||
|
/* 默认渐变色中间 */
|
||||||
|
--gradient-color-2: #2b90d9;
|
||||||
|
/* 默认渐变色结束 */
|
||||||
|
--gradient-color-3: #37dede;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.vh-cat-Software {
|
||||||
|
/* 默认渐变色起始 */
|
||||||
|
--gradient-color-1: #006633;
|
||||||
|
/* 默认渐变色中间 */
|
||||||
|
--gradient-color-2: #00cc66;
|
||||||
|
/* 默认渐变色结束 */
|
||||||
|
--gradient-color-3: #66ff99;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.vh-cat-Share {
|
||||||
|
/* 默认渐变色起始 */
|
||||||
|
--gradient-color-1: rgb(240, 45, 133);
|
||||||
|
/* 默认渐变色中间 */
|
||||||
|
--gradient-color-2: #f95ba5;
|
||||||
|
/* 默认渐变色结束 */
|
||||||
|
--gradient-color-3: #f4bdd0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&>time {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-top: auto;
|
||||||
|
font-size: 0.805rem;
|
||||||
|
font-weight: 400;
|
||||||
|
color: var(--vh-font-color);
|
||||||
|
width: max-content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&>.title {
|
||||||
&>.vh-article-desc {
|
|
||||||
flex: 1;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 1.188rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: max-content;
|
||||||
min-height: max-content;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&>header {
|
&>a {
|
||||||
display: flex;
|
font-size: 1.06rem;
|
||||||
flex-direction: column;
|
font-weight: 600;
|
||||||
gap: 0.36rem;
|
transition: color 0.18s;
|
||||||
|
|
||||||
&>.vh-article-cat {
|
&:hover {
|
||||||
box-sizing: border-box;
|
color: var(--vh-main-color);
|
||||||
width: max-content;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
font-weight: 700;
|
|
||||||
/* 默认渐变色起始 */
|
|
||||||
--gradient-color-1: #a32cc4;
|
|
||||||
/* 默认渐变色中间 */
|
|
||||||
--gradient-color-2: #b65fcf;
|
|
||||||
/* 默认渐变色结束 */
|
|
||||||
--gradient-color-3: #e39ff6;
|
|
||||||
background: linear-gradient(118deg, var(--gradient-color-1), var(--gradient-color-2) 26%, var(--gradient-color-3));
|
|
||||||
background-clip: text;
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
color: transparent;
|
|
||||||
|
|
||||||
&.vh-cat-Code {
|
|
||||||
/* 默认渐变色起始 */
|
|
||||||
--gradient-color-1: #f48600;
|
|
||||||
/* 默认渐变色中间 */
|
|
||||||
--gradient-color-2: #ffa12e;
|
|
||||||
/* 默认渐变色结束 */
|
|
||||||
--gradient-color-3: #ffe9c9;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.vh-cat-Daily {
|
|
||||||
/* 默认渐变色起始 */
|
|
||||||
--gradient-color-1: #6270ee;
|
|
||||||
/* 默认渐变色中间 */
|
|
||||||
--gradient-color-2: #2b90d9;
|
|
||||||
/* 默认渐变色结束 */
|
|
||||||
--gradient-color-3: #37dede;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.vh-cat-Software {
|
|
||||||
/* 默认渐变色起始 */
|
|
||||||
--gradient-color-1: #006633;
|
|
||||||
/* 默认渐变色中间 */
|
|
||||||
--gradient-color-2: #00cc66;
|
|
||||||
/* 默认渐变色结束 */
|
|
||||||
--gradient-color-3: #66ff99;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.vh-cat-Share {
|
|
||||||
/* 默认渐变色起始 */
|
|
||||||
--gradient-color-1: rgb(240, 45, 133);
|
|
||||||
/* 默认渐变色中间 */
|
|
||||||
--gradient-color-2: #f95ba5;
|
|
||||||
/* 默认渐变色结束 */
|
|
||||||
--gradient-color-3: #f4bdd0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&>.title {
|
|
||||||
font-size: 1.06rem;
|
|
||||||
font-weight: 600;
|
|
||||||
line-height: 1.36;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&>.vh-article-excerpt {
|
&>.vh-article-excerpt {
|
||||||
margin: 0.88rem 0;
|
box-sizing: border-box;
|
||||||
width: 100%;
|
padding: 0 1.166rem;
|
||||||
height: max-content;
|
width: 100%;
|
||||||
color: var(--vh-font-66);
|
height: max-content;
|
||||||
font-size: 0.805rem;
|
color: var(--vh-font-66);
|
||||||
letter-spacing: 0.88px;
|
font-size: 0.805rem;
|
||||||
line-height: 1.28rem;
|
font-weight: 400;
|
||||||
|
letter-spacing: 0.88px;
|
||||||
|
line-height: 1.28rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
&>.vh-article-taglist {
|
||||||
|
margin-top: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0.66rem 1.166rem 1rem;
|
||||||
|
width: 100%;
|
||||||
|
height: max-content;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&>a {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-right: 0.88rem;
|
||||||
|
color: var(--vh-font);
|
||||||
|
font-size: 0.688rem;
|
||||||
|
font-weight: 400;
|
||||||
|
transition: color 0.18s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--vh-main-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
&>footer {
|
&::before {
|
||||||
margin-top: auto;
|
content: "#";
|
||||||
font-size: 0.805rem;
|
}
|
||||||
color: var(--vh-font-color);
|
|
||||||
|
&:nth-last-of-type(1) {
|
||||||
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
// 静态图片
|
// 静态图片
|
||||||
// Svg 组件
|
// Svg 组件
|
||||||
import Svg from "@/components/Svg/Svg.astro";
|
import Svg from "@/components/Svg/Svg.astro";
|
||||||
import { Image } from "astro:assets";
|
|
||||||
// 时间处理
|
// 时间处理
|
||||||
import { fmtTime } from "@/utils/index";
|
import { fmtTime } from "@/utils/index";
|
||||||
// 获取用户配置数据
|
// 获取用户配置数据
|
||||||
@ -14,7 +13,7 @@ import { getCategories, getTags, getRecommendArticles, getCountInfo } from "@/ut
|
|||||||
// 分类列表
|
// 分类列表
|
||||||
const categories = getCategories();
|
const categories = getCategories();
|
||||||
// 热门标签
|
// 热门标签
|
||||||
const tags = getTags().slice(0, 16);
|
const tags = getTags();
|
||||||
// 获取网站统计数据
|
// 获取网站统计数据
|
||||||
const CountInfo = getCountInfo();
|
const CountInfo = getCountInfo();
|
||||||
// 最新文章
|
// 最新文章
|
||||||
@ -30,7 +29,7 @@ import "./Aside.less";
|
|||||||
{
|
{
|
||||||
AsideShow.WebSitesShow && (
|
AsideShow.WebSitesShow && (
|
||||||
<section class="vh-aside-item user">
|
<section class="vh-aside-item user">
|
||||||
<Image class="vh-aside-avatar" src="/assets/images/lazy-loading.webp" data-vh-lz-src={Avatar} alt={Author} width="1" height="1" />
|
<img class="vh-aside-avatar" src="/assets/images/lazy-loading.webp" data-vh-lz-src={Avatar} alt={Author} />
|
||||||
<span class="vh-aside-auther">{Author}</span>
|
<span class="vh-aside-auther">{Author}</span>
|
||||||
<p class="vh-aside-motto">{Motto}</p>
|
<p class="vh-aside-motto">{Motto}</p>
|
||||||
<section class="vh-aside-links">
|
<section class="vh-aside-links">
|
||||||
|
|||||||
@ -138,11 +138,11 @@ aside.vh-aside {
|
|||||||
|
|
||||||
&>.count {
|
&>.count {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 1rem 0 0;
|
padding: 0.88rem 0 0.66rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 0.66rem;
|
gap: 0.28rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: var(--vh-white-color);
|
background: var(--vh-white-color);
|
||||||
border-radius: var(--vh-main-radius);
|
border-radius: var(--vh-main-radius);
|
||||||
@ -205,6 +205,10 @@ aside.vh-aside {
|
|||||||
p {
|
p {
|
||||||
padding: 0.28rem 0;
|
padding: 0.28rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +273,9 @@ aside.vh-aside {
|
|||||||
gap: 0.68rem 1rem;
|
gap: 0.68rem 1rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: max-content;
|
height: max-content;
|
||||||
overflow: hidden;
|
max-height: 18.88rem;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
&>a {
|
&>a {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@ -1,13 +1,11 @@
|
|||||||
---
|
---
|
||||||
// 静态图片
|
|
||||||
import { Image } from "astro:assets";
|
|
||||||
// 基础 样式
|
// 基础 样式
|
||||||
import "./Footer.less";
|
import "./Footer.less";
|
||||||
---
|
---
|
||||||
|
|
||||||
<footer class="vh-footer">
|
<footer class="vh-footer">
|
||||||
<main>
|
<main>
|
||||||
<p><Image src="/assets/images/footer/ing.svg" alt="韩小韩" width="1" height="1" /><span><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> <path fill="#fff" d="M269.4 2.9C265.2 1 260.7 0 256 0s-9.2 1-13.4 2.9L54.3 82.8c-22 9.3-38.4 31-38.3 57.2c.5 99.2 41.3 280.7 213.6 363.2c16.7 8 36.1 8 52.8 0C454.7 420.7 495.5 239.2 496 140c.1-26.2-16.3-47.9-38.3-57.2L269.4 2.9zM144 221.3c0-33.8 27.4-61.3 61.3-61.3c16.2 0 31.8 6.5 43.3 17.9l7.4 7.4 7.4-7.4c11.5-11.5 27.1-17.9 43.3-17.9c33.8 0 61.3 27.4 61.3 61.3c0 16.2-6.5 31.8-17.9 43.3l-82.7 82.7c-6.2 6.2-16.4 6.2-22.6 0l-82.7-82.7c-11.5-11.5-17.9-27.1-17.9-43.3z"></path></svg><cite>稳定运行</cite><em class="web_time"></em></span><a href="https://analytics.vvhan.com" target="_blank" rel="noopener noreferrer"><Image width="1" height="1" alt="HanAnalytics" src="/assets/images/footer/hananalytics.svg" /></a></p>
|
<p><img src="/assets/images/footer/ing.svg" alt="韩小韩" /><span><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> <path fill="#fff" d="M269.4 2.9C265.2 1 260.7 0 256 0s-9.2 1-13.4 2.9L54.3 82.8c-22 9.3-38.4 31-38.3 57.2c.5 99.2 41.3 280.7 213.6 363.2c16.7 8 36.1 8 52.8 0C454.7 420.7 495.5 239.2 496 140c.1-26.2-16.3-47.9-38.3-57.2L269.4 2.9zM144 221.3c0-33.8 27.4-61.3 61.3-61.3c16.2 0 31.8 6.5 43.3 17.9l7.4 7.4 7.4-7.4c11.5-11.5 27.1-17.9 43.3-17.9c33.8 0 61.3 27.4 61.3 61.3c0 16.2-6.5 31.8-17.9 43.3l-82.7 82.7c-6.2 6.2-16.4 6.2-22.6 0l-82.7-82.7c-11.5-11.5-17.9-27.1-17.9-43.3z"></path></svg><cite>稳定运行</cite><em class="web_time"></em></span><a href="https://analytics.vvhan.com" target="_blank" rel="noopener noreferrer"><img alt="HanAnalytics" src="/assets/images/footer/hananalytics.svg" /></a></p>
|
||||||
<p><a href="https://astro.build/" target="_blank" rel="noopener noreferrer"><Image width="1" height="1" alt="Astro" src="/assets/images/footer/astro.svg" /></a><a href="https://github.com/uxiaohan/vhAstro-Theme" target="_blank" rel="noopener noreferrer"><Image width="1" height="1" alt="vhAstro-Theme" src="/assets/images/footer/theme.svg" /></a><a href="/sitemap-index.xml" target="_blank"><Image width="1" height="1" alt="sitemap" src="/assets/images/footer/sitemap.svg" /></a><a href="/rss.xml" target="_blank"><Image width="1" height="1" alt="rss" src="/assets/images/footer/rss.svg" /></a><a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer"><Image width="1" height="1" alt="icp" src="/assets/images/footer/icp.svg" /></a></p>
|
<p><a href="https://astro.build/" target="_blank" rel="noopener noreferrer"><img alt="Astro" src="/assets/images/footer/astro.svg" /></a><a href="https://github.com/uxiaohan/vhAstro-Theme" target="_blank" rel="noopener noreferrer"><img alt="vhAstro-Theme" src="/assets/images/footer/theme.svg" /></a><a href="/sitemap-index.xml" target="_blank"><img alt="sitemap" src="/assets/images/footer/sitemap.svg" /></a><a href="/rss.xml" target="_blank"><img alt="rss" src="/assets/images/footer/rss.svg" /></a><a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer"><img alt="icp" src="/assets/images/footer/icp.svg" /></a></p>
|
||||||
</main>
|
</main>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@ -10,8 +10,9 @@ const WebTitle = Title || SiteName;
|
|||||||
const SiteCover = Site + Cover;
|
const SiteCover = Site + Cover;
|
||||||
const WebCover = PageCover || SiteCover;
|
const WebCover = PageCover || SiteCover;
|
||||||
// Schema.org 结构化数据(JSON-LD)
|
// Schema.org 结构化数据(JSON-LD)
|
||||||
const WebSiteData = { "@context": "https://schema.org", "@type": "WebSite", name: WebTitle, url: canonicalURL, image: { "@type": "ImageObject", url: SiteCover } };
|
const WebSiteData = { "@context": "https://schema.org", "@type": "WebSite", url: canonicalURL, name: WebTitle, description: Description, inLanguage: "zh-CN", image: WebCover, publisher: { "@type": "Organization", name: WebTitle, logo: { "@type": "ImageObject", url: SiteCover } } };
|
||||||
const ArticleData = { "@context": "https://schema.org", "@type": "BlogPosting", mainEntityOfPage: { "@type": "WebPage", "@id": canonicalURL }, headline: WebTitle, image: [WebCover], author: { "@type": "Person", name: Author, url: Site }, publisher: { "@type": "Organization", name: SiteName, logo: { "@type": "ImageObject", url: SiteCover } } };
|
const ArticleData = { "@context": "https://schema.org", "@type": "BlogPosting", mainEntityOfPage: { "@type": "WebPage", "@id": canonicalURL }, headline: WebTitle, description: Description, image: [WebCover], datePublished: new Date().toISOString().replace("Z", `+08:00`), dateModified: new Date().toISOString().replace("Z", `+08:00`), author: { "@type": "Person", name: Author, url: Site }, publisher: { "@type": "Organization", name: SiteName, logo: { "@type": "ImageObject", url: SiteCover } }, keywords: (Keywords || ["Astro", "分享", "博客"]).join(", ") };
|
||||||
|
|
||||||
// 基础 样式
|
// 基础 样式
|
||||||
import "@/styles/Base.less";
|
import "@/styles/Base.less";
|
||||||
---
|
---
|
||||||
|
|||||||
@ -8,5 +8,7 @@ import "./MainHeader.less";
|
|||||||
<img src="/assets/images/lazy-loading.webp" data-vh-lz-src={SITE_INFO.Avatar} alt="avatar" />
|
<img src="/assets/images/lazy-loading.webp" data-vh-lz-src={SITE_INFO.Avatar} alt="avatar" />
|
||||||
</div>
|
</div>
|
||||||
<h3 class="auther">{SITE_INFO.Author || "-"}</h3>
|
<h3 class="auther">{SITE_INFO.Author || "-"}</h3>
|
||||||
|
{Array.isArray(SITE_INFO.TypeWriteList) && SITE_INFO.TypeWriteList.length > 0 && (
|
||||||
<p class="desc"></p>
|
<p class="desc"></p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -3,7 +3,23 @@ import SITE_INFO from "@/config";
|
|||||||
import "./Reward.less";
|
import "./Reward.less";
|
||||||
---
|
---
|
||||||
|
|
||||||
<section class="vh-reward">
|
{
|
||||||
<p>喜欢这篇文章嘛,觉得文章不错的话,奖励奖励我!</p>
|
SITE_INFO.Reward.AliPay && SITE_INFO.Reward.WeChat && (
|
||||||
<div class="reward-list"><span class="alipay"><img src={SITE_INFO.Reward.AliPay} alt="支付宝打赏" />支付宝</span><span class="wechat"><img src={SITE_INFO.Reward.WeChat} alt="微信打赏" /> 微信</span></div>
|
<section class="vh-reward">
|
||||||
</section>
|
<p>喜欢这篇文章嘛,觉得文章不错的话,奖励奖励我!</p>
|
||||||
|
<div class="reward-list">
|
||||||
|
{SITE_INFO.Reward.AliPay && (
|
||||||
|
<span class="alipay">
|
||||||
|
<img src={SITE_INFO.Reward.AliPay} alt="支付宝打赏" />
|
||||||
|
支付宝
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{SITE_INFO.Reward.WeChat && (
|
||||||
|
<span class="wechat">
|
||||||
|
<img src={SITE_INFO.Reward.WeChat} alt="微信打赏" /> 微信
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@ -122,6 +122,12 @@ export default {
|
|||||||
// 微信收款码
|
// 微信收款码
|
||||||
WeChat: '/assets/images/wechat.webp'
|
WeChat: '/assets/images/wechat.webp'
|
||||||
},
|
},
|
||||||
|
// 访问网页 自动推送到搜索引擎
|
||||||
|
SeoPush: {
|
||||||
|
enable: false,
|
||||||
|
serverApi: '',
|
||||||
|
paramsName: 'url'
|
||||||
|
},
|
||||||
// 页面阻尼滚动速度
|
// 页面阻尼滚动速度
|
||||||
ScrollSpeed: 666
|
ScrollSpeed: 666
|
||||||
}
|
}
|
||||||
@ -10,7 +10,7 @@ const blog = defineCollection({
|
|||||||
date: z.coerce.date(),
|
date: z.coerce.date(),
|
||||||
updated: z.coerce.date().optional(),
|
updated: z.coerce.date().optional(),
|
||||||
categories: z.string(),
|
categories: z.string(),
|
||||||
tags: z.array(z.union([z.string(), z.number()])),
|
tags: z.array(z.union([z.string(), z.number()])).optional(),
|
||||||
id: z.union([z.string(), z.number()]),
|
id: z.union([z.string(), z.number()]),
|
||||||
cover: z.string().optional(),
|
cover: z.string().optional(),
|
||||||
recommend: z.boolean().optional(),
|
recommend: z.boolean().optional(),
|
||||||
|
|||||||
@ -68,42 +68,39 @@ top: true
|
|||||||
|
|
||||||
## 🚀 使用方法
|
## 🚀 使用方法
|
||||||
|
|
||||||
|
### 使用 Github 模板
|
||||||
|
|
||||||
- 使用此模板 [生成新仓库或 Fork 此仓库](https://github.com/new?template_name=vhAstro-Theme&template_owner=uxiaohan)
|
- 使用此模板 [生成新仓库或 Fork 此仓库](https://github.com/new?template_name=vhAstro-Theme&template_owner=uxiaohan)
|
||||||
- 进行本地开发,Clone 新的仓库,执行 `pnpm install` 以安装依赖
|
- 进行本地开发,Clone 新的仓库,执行 `pnpm install` 以安装依赖
|
||||||
- 若未安装 pnpm,执行 `npm install -g pnpm`
|
- 若未安装 [pnpm](https://pnpm.io),执行 `npm install -g pnpm`
|
||||||
- 通过配置文件 `src/config.ts` 自定义博客
|
- 通过配置文件 `src/config.ts` 自定义博客
|
||||||
- 执行 pnpm newpost '文章标题' 创建新文章,并在 src/content/posts/ 目录中编辑
|
- 执行 pnpm newpost '文章标题' 创建新文章,并在 src/content/posts/ 目录中编辑
|
||||||
- 参考官方指南将博客部署至 Vercel, Netlify,Cloudflare Pages, GitHub Pages 等
|
- 参考官方指南将博客部署至 Vercel, Netlify,Cloudflare Pages, GitHub Pages 等
|
||||||
- 部署前需编辑 `astro.config.mjs` 中的站点设置。
|
|
||||||
|
|
||||||
### ⚠️ Hexo 迁移 Astro 方法
|
### Vercel 自动部署
|
||||||
|
|
||||||
> `Hexo` 的部署、使用、自动化部署等方法 完全适用于 `Astro`,只需要将 `Hexo` 博客的 `src/_posts/` 目录下的文章文件迁移至 `Astro` 目录下的 `src/content/blog/` 目录下即可,然后自定义 `src/config.ts` 配置文件去自定义博客。<br> 此时,你已成功迁移 Hexo 博客至 Astro 博客!
|
::btn[Vercel 一键部署]{link="https://vercel.com/new/clone?repository-url=https://github.com/uxiaohan/vhAstro-Theme"}
|
||||||
|
|
||||||
|
### Cloudflare Pages 自动部署
|
||||||
|
|
||||||
## 📄 文章格式
|
::btn[Cloudflare Pages 一键部署]{link="https://dash.cloudflare.com/?to=/:account/workers-and-pages/create/deploy-to-workers&repository=https://github.com/uxiaohan/vhAstro-Theme" type="warning"}
|
||||||
|
|
||||||
```md
|
其他更多部署方式(简简单单闭眼部署),请参考 [Astro 官方文档](https://docs.astro.build/en/guides/integrations-guide/cloudflare/)。
|
||||||
---
|
|
||||||
title: 标题
|
### 使用命令拉取模板
|
||||||
categories: 分类
|
|
||||||
tags:
|
```bash
|
||||||
- 标签1
|
# 使用 pnpm
|
||||||
- 标签2
|
pnpm create astro@latest --template uxiaohan/vhAstro-Theme astro-blog
|
||||||
id: 文章ID
|
# 或者 yarn
|
||||||
date: 文章创建日期
|
yarn create astro --template uxiaohan/vhAstro-Theme astro-blog
|
||||||
updated: 文章更新日期
|
# 或者 npm
|
||||||
cover: "封面图URL (为空默认随机内置封面 /public/assets/images/banner)"
|
npm create astro@latest -- --template uxiaohan/vhAstro-Theme astro-blog
|
||||||
recommend: false # 是否推荐文章
|
# 进入项目目录
|
||||||
top: false # 是否置顶文章
|
cd astro-blog
|
||||||
hide: false # 是否隐藏文章
|
|
||||||
<!-- 页面独有 -->
|
|
||||||
type: "links" # 页面类型
|
|
||||||
comment: false # 关闭页面评论(默认开启)
|
|
||||||
---
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 💻 命令
|
### 本地开发
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 安装依赖
|
# 安装依赖
|
||||||
@ -116,6 +113,16 @@ pnpm build
|
|||||||
pnpm newpost '文章标题'
|
pnpm newpost '文章标题'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### ⚠️ Hexo 迁移 Astro 方法
|
||||||
|
|
||||||
|
:::note{type="success"}
|
||||||
|
将 `Hexo` 博客的 `src/_posts/` 目录下的文章文件,复制到 `Astro` 的 `src/content/blog/` 目录下即可,然后自定义 `src/config.ts` 配置文件去自定义博客。
|
||||||
|
|
||||||
|
⚠️ `Hexo` 的部署、使用、自动化部署等方法 完全适用于 `Astro` 博客!
|
||||||
|
|
||||||
|
🎉 此时,你已成功迁移 Hexo 博客至 Astro 博客!
|
||||||
|
:::
|
||||||
|
|
||||||
## 🍬 特色页面
|
## 🍬 特色页面
|
||||||
|
|
||||||
### 友情链接
|
### 友情链接
|
||||||
@ -201,6 +208,29 @@ export default {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 📄 文章格式
|
||||||
|
|
||||||
|
```md
|
||||||
|
---
|
||||||
|
title: 标题
|
||||||
|
categories: 分类
|
||||||
|
tags:
|
||||||
|
- 标签1
|
||||||
|
- 标签2
|
||||||
|
id: 文章ID
|
||||||
|
date: 文章创建日期
|
||||||
|
updated: 文章更新日期
|
||||||
|
cover: "封面图URL (为空默认随机内置封面 /public/assets/images/banner)"
|
||||||
|
recommend: false # 是否推荐文章
|
||||||
|
top: false # 是否置顶文章
|
||||||
|
hide: false # 是否隐藏文章
|
||||||
|
<!-- 页面独有 -->
|
||||||
|
type: "links" # 页面类型
|
||||||
|
comment: false # 关闭页面评论(默认开启)
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## ✅ Lighthouse
|
## ✅ Lighthouse
|
||||||
|
|
||||||

|

|
||||||
@ -628,9 +658,9 @@ $\cup$、$\cap$、$\in$、$\notin$、$\ni$、$\subset$、$\subseteq$、$\supset$
|
|||||||
::vhLivePhoto{photo="https://static.vvhan.com/img/2.webp" video="https://static.vvhan.com/img/2.mp4"}
|
::vhLivePhoto{photo="https://static.vvhan.com/img/2.webp" video="https://static.vvhan.com/img/2.mp4"}
|
||||||
```
|
```
|
||||||
|
|
||||||
::vhLivePhoto{photo="/public/assets/livephoto/1.webp" video="/public/assets/livephoto/1.mp4" type="y"}
|
::vhLivePhoto{photo="/assets/livephoto/1.webp" video="/assets/livephoto/1.mp4" type="y"}
|
||||||
|
|
||||||
::vhLivePhoto{photo="/public/assets/livephoto/2.webp" video="/public/assets/livephoto/2.mp4"}
|
::vhLivePhoto{photo="/assets/livephoto/2.webp" video="/assets/livephoto/2.mp4"}
|
||||||
|
|
||||||
### Music 组件
|
### Music 组件
|
||||||
|
|
||||||
|
|||||||
77
src/content/blog/从Hexo到Astro博客1分钟迁移指南.md
Normal file
77
src/content/blog/从Hexo到Astro博客1分钟迁移指南.md
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
---
|
||||||
|
title: "从Hexo到Astro博客1分钟迁移指南"
|
||||||
|
categories: Share
|
||||||
|
tags: ["Hexo", "Astro", "静态博客", "迁移"]
|
||||||
|
id: "hexo-to-astro-1-minute-migration-guide"
|
||||||
|
date: 2025-04-07 17:20:46
|
||||||
|
cover: "https://i0.wp.com/uxiaohan.github.io/v2/2025/04/1744018738123.webp"
|
||||||
|
---
|
||||||
|
|
||||||
|
:::note{type="info"}
|
||||||
|
🎉 本文将介绍如何将 Hexo 博客迁移到 Astro 博客,只需要 1 分钟即可完成!
|
||||||
|
|
||||||
|
作为 `Hexo` 的长期用户,我在 2024 年开始尝试 `Astro`,`Hexo` 基于模板引擎(EJS/Swig)的字符串拼接,而 `Astro` 采用基于 `Vite` 的组件化构建体系。`Hexo` 在 `hexo generate` 时完成全量渲染,`Astro` 在 `astro build` 时执行 `SSG`(静态站点生成)+ `ISR`(增量静态再生)。
|
||||||
|
|
||||||
|
`Astro` 的组件化架构、按需加载,构建速度、`Vite` 驱动,热更新速度快等现代化开发体验 深深吸引了我!
|
||||||
|
:::
|
||||||
|
|
||||||
|
### 项目结构对比
|
||||||
|
|
||||||
|
| Hexo | Astro | 说明 |
|
||||||
|
| ------------ | ---------------- | ------------ |
|
||||||
|
| \_config.yml | src/config.ts | 配置文件 |
|
||||||
|
| source | src/content/blog | 主内容目录 |
|
||||||
|
| themes | src/components | 组件目录 |
|
||||||
|
| public | public | 静态资源目录 |
|
||||||
|
|
||||||
|
## 迁移步骤详解
|
||||||
|
|
||||||
|
### 1. 初始化 Astro 项目
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 创建美妙的 Astro 项目
|
||||||
|
npm create astro@latest --template uxiaohan/vhAstro-Theme astro-blog
|
||||||
|
# 进入项目目录
|
||||||
|
cd astro-blog
|
||||||
|
# 安装依赖
|
||||||
|
npm install
|
||||||
|
# 本地预览
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### 迁移文章内容
|
||||||
|
|
||||||
|
:::note{type="success"}
|
||||||
|
将 `Hexo` 博客的 `src/_posts/` 目录下的文章文件,复制到 `Astro` 的 `src/content/blog/` 目录下即可,然后自定义 `src/config.ts` 配置文件去自定义博客。
|
||||||
|
|
||||||
|
⚠️ `Hexo` 的部署、使用、自动化部署等方法 完全适用于 `Astro` 博客!
|
||||||
|
|
||||||
|
🎉 此时,你已成功迁移 Hexo 博客至 Astro 博客!
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Vercel 自动部署
|
||||||
|
|
||||||
|
::btn[Vercel 一键部署]{link="https://vercel.com/new/clone?repository-url=https://github.com/uxiaohan/vhAstro-Theme"}
|
||||||
|
|
||||||
|
### Cloudflare Pages 自动部署
|
||||||
|
|
||||||
|
::btn[Cloudflare Pages 一键部署]{link="https://dash.cloudflare.com/?to=/:account/workers-and-pages/create/deploy-to-workers&repository=https://github.com/uxiaohan/vhAstro-Theme" type="warning"}
|
||||||
|
|
||||||
|
其他更多部署方式(简简单单闭眼部署),请参考 [Astro 官方文档](https://docs.astro.build/en/guides/integrations-guide/cloudflare/)。
|
||||||
|
|
||||||
|
### 迁移后性能对比
|
||||||
|
|
||||||
|
| 指标 | Hexo | Astro | 提升 |
|
||||||
|
| --------------- | ----- | ----- | ---- |
|
||||||
|
| 构建时间 | 12.3s | 5.1s | 58%↑ |
|
||||||
|
| Lighthouse 性能 | 89 | 100 | 12%↑ |
|
||||||
|
| 页面大小 | 145KB | 23KB | 84%↓ |
|
||||||
|
|
||||||
|
### 总结
|
||||||
|
|
||||||
|
迁移到 Astro 后,我的技术博客实现了:
|
||||||
|
|
||||||
|
- ✅ 构建速度提升 2.4 倍
|
||||||
|
- ✅ 页面性能评分全满分
|
||||||
|
- ✅ 开发体验现代化
|
||||||
|
- ✅ 扩展能力显著增强
|
||||||
@ -3,8 +3,6 @@ const { frontmatter } = Astro.props;
|
|||||||
// 页面 Info
|
// 页面 Info
|
||||||
import SITE_CONFIG from "@/config";
|
import SITE_CONFIG from "@/config";
|
||||||
const { Description, Title } = SITE_CONFIG;
|
const { Description, Title } = SITE_CONFIG;
|
||||||
// Aside组件
|
|
||||||
import Aside from "@/components/Aside/Aside.astro";
|
|
||||||
// 公共 Layout
|
// 公共 Layout
|
||||||
import Layout from "@/layouts/Layout/Layout.astro";
|
import Layout from "@/layouts/Layout/Layout.astro";
|
||||||
// 评论组件
|
// 评论组件
|
||||||
|
|||||||
@ -3,8 +3,6 @@ const { frontmatter } = Astro.props;
|
|||||||
// 页面 Info
|
// 页面 Info
|
||||||
import SITE_CONFIG from "@/config";
|
import SITE_CONFIG from "@/config";
|
||||||
const { Description, Title } = SITE_CONFIG;
|
const { Description, Title } = SITE_CONFIG;
|
||||||
// Aside组件
|
|
||||||
import Aside from "@/components/Aside/Aside.astro";
|
|
||||||
// 公共 Layout
|
// 公共 Layout
|
||||||
import Layout from "@/layouts/Layout/Layout.astro";
|
import Layout from "@/layouts/Layout/Layout.astro";
|
||||||
// 评论组件
|
// 评论组件
|
||||||
|
|||||||
@ -7,8 +7,6 @@ import SITE_CONFIG from "@/config";
|
|||||||
const { Description } = SITE_CONFIG;
|
const { Description } = SITE_CONFIG;
|
||||||
// 公共 Layout
|
// 公共 Layout
|
||||||
import Layout from "@/layouts/Layout/Layout.astro";
|
import Layout from "@/layouts/Layout/Layout.astro";
|
||||||
// Aside组件
|
|
||||||
import Aside from "@/components/Aside/Aside.astro";
|
|
||||||
// 文章列表组件
|
// 文章列表组件
|
||||||
import Archive from "@/components/Archive/Archive.astro";
|
import Archive from "@/components/Archive/Archive.astro";
|
||||||
---
|
---
|
||||||
|
|||||||
@ -56,9 +56,9 @@ import "@/styles/ArticleBase.less";
|
|||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<Content />
|
<Content />
|
||||||
<nav class="tag-list">
|
<section class="tag-list">
|
||||||
{
|
{
|
||||||
post.data.tags.map((i: any) => (
|
(post.data.tags || []).map((i: any) => (
|
||||||
<a href={`/tag/${i}`}>
|
<a href={`/tag/${i}`}>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
@ -69,11 +69,11 @@ import "@/styles/ArticleBase.less";
|
|||||||
</a>
|
</a>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</nav>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer>
|
||||||
<!-- 打赏组件 -->
|
<!-- 打赏组件 -->
|
||||||
<Reward />
|
{SITE_CONFIG.Reward && (SITE_CONFIG.Reward.AliPay || SITE_CONFIG.Reward.WeChat) && <Reward />}
|
||||||
<!-- 版权©️信息 -->
|
<!-- 版权©️信息 -->
|
||||||
<Copyright site={Site} id={post.data.id} title={post.data.title} sitename={Title} time={fmtTime(post.data.date, "YYYY-MM-DD A")} auther={Author} />
|
<Copyright site={Site} id={post.data.id} title={post.data.title} sitename={Title} time={fmtTime(post.data.date, "YYYY-MM-DD A")} auther={Author} />
|
||||||
<!-- 底部谷歌广告 -->
|
<!-- 底部谷歌广告 -->
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
import type { APIRoute } from 'astro';
|
import type { APIRoute } from 'astro';
|
||||||
|
|
||||||
const getRobotsTxt = (sitemapURL: URL) => `User-agent: *
|
const getRobotsTxt = (sitemapURL: URL) => `User-agent: *
|
||||||
Disallow: /MineWeb/
|
Allow: /
|
||||||
Disallow: /assets/
|
|
||||||
Disallow: /*?*
|
|
||||||
|
|
||||||
Sitemap: ${sitemapURL.href}`;
|
Sitemap: ${sitemapURL.href}`;
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { getCollection } from "astro:content";
|
|||||||
export async function getStaticPaths(): Promise<any> {
|
export async function getStaticPaths(): Promise<any> {
|
||||||
const posts = await getCollection("blog");
|
const posts = await getCollection("blog");
|
||||||
let tagList: any = [];
|
let tagList: any = [];
|
||||||
posts.forEach(post => (tagList = [...tagList, ...post.data.tags]));
|
posts.forEach(post => (tagList = [...tagList, ...(post.data.tags || [])]));
|
||||||
return [...new Set(tagList)].map(tags => ({ params: { tags } }));
|
return [...new Set(tagList)].map(tags => ({ params: { tags } }));
|
||||||
}
|
}
|
||||||
// 获取分类列表
|
// 获取分类列表
|
||||||
@ -15,8 +15,6 @@ import SITE_CONFIG from "@/config";
|
|||||||
const { Description } = SITE_CONFIG;
|
const { Description } = SITE_CONFIG;
|
||||||
// 公共 Layout
|
// 公共 Layout
|
||||||
import Layout from "@/layouts/Layout/Layout.astro";
|
import Layout from "@/layouts/Layout/Layout.astro";
|
||||||
// Aside组件
|
|
||||||
import Aside from "@/components/Aside/Aside.astro";
|
|
||||||
// 文章列表组件
|
// 文章列表组件
|
||||||
import Archive from "@/components/Archive/Archive.astro";
|
import Archive from "@/components/Archive/Archive.astro";
|
||||||
---
|
---
|
||||||
|
|||||||
@ -1,3 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* @Author: Han
|
||||||
|
* @Date: 2025-04-07 11:31:34
|
||||||
|
* @LastEditors: Han
|
||||||
|
* @LastEditTime: 2025-04-21 14:32:19
|
||||||
|
* @Description:
|
||||||
|
*
|
||||||
|
*/
|
||||||
import SITE_INFO from "@/config";
|
import SITE_INFO from "@/config";
|
||||||
import { LoadScript } from "@/utils/index";
|
import { LoadScript } from "@/utils/index";
|
||||||
declare const twikoo: any;
|
declare const twikoo: any;
|
||||||
@ -25,6 +33,7 @@ const WalineFn = async (commentDOM: string, walineInit: any) => {
|
|||||||
"https://registry.npmmirror.com/@waline/emojis/1.3.0/files/tieba/tieba_awkward.png",
|
"https://registry.npmmirror.com/@waline/emojis/1.3.0/files/tieba/tieba_awkward.png",
|
||||||
"https://registry.npmmirror.com/@waline/emojis/1.3.0/files/tieba/tieba_sleep.png",
|
"https://registry.npmmirror.com/@waline/emojis/1.3.0/files/tieba/tieba_sleep.png",
|
||||||
],
|
],
|
||||||
|
requiredMeta: ['nick', 'mail'],
|
||||||
imageUploader: async (file: any) => {
|
imageUploader: async (file: any) => {
|
||||||
const body = new FormData();
|
const body = new FormData();
|
||||||
body.append('file', file);
|
body.append('file', file);
|
||||||
|
|||||||
@ -1,4 +0,0 @@
|
|||||||
import { $POST } from '@/utils/index'
|
|
||||||
export default async () => {
|
|
||||||
await $POST('https://vh-api.4ce.cn/api/seoPush', { url: window.location.href.replace(/\/$/, '') }, { 'Content-Type': 'application/json; charset=utf-8' })
|
|
||||||
}
|
|
||||||
@ -36,7 +36,7 @@ import GoogleAdInit from "@/scripts/GoogleAd";
|
|||||||
// Han Analytics 统计
|
// Han Analytics 统计
|
||||||
import HanAnalyticsInit from "@/scripts/HanAnalytics";
|
import HanAnalyticsInit from "@/scripts/HanAnalytics";
|
||||||
// 谷歌 SEO 推送
|
// 谷歌 SEO 推送
|
||||||
import GoogleSEOInit from "@/scripts/GoogleSeoPush";
|
import SeoPushInit from "@/scripts/SeoPush";
|
||||||
// SmoothScroll 滚动优化
|
// SmoothScroll 滚动优化
|
||||||
import SmoothScroll from "@/scripts/Smoothscroll";
|
import SmoothScroll from "@/scripts/Smoothscroll";
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ const indexInit = async (only: boolean = true) => {
|
|||||||
// Google 广告
|
// Google 广告
|
||||||
GoogleAdInit();
|
GoogleAdInit();
|
||||||
// 谷歌 SEO 推送
|
// 谷歌 SEO 推送
|
||||||
GoogleSEOInit();
|
SeoPushInit();
|
||||||
// 文章评论初始化
|
// 文章评论初始化
|
||||||
checkComment() && commentInit(checkComment(), commentLIst)
|
checkComment() && commentInit(checkComment(), commentLIst)
|
||||||
// Han Analytics 统计
|
// Han Analytics 统计
|
||||||
|
|||||||
6
src/scripts/SeoPush.ts
Normal file
6
src/scripts/SeoPush.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import SITE_INFO from '@/config'
|
||||||
|
import { $POST } from '@/utils/index'
|
||||||
|
export default async () => {
|
||||||
|
if (!SITE_INFO.SeoPush.enable) return;
|
||||||
|
await $POST(SITE_INFO.SeoPush.serverApi, { [SITE_INFO.SeoPush.paramsName]: window.location.href.replace(/\/$/, '') }, { 'Content-Type': 'application/json; charset=utf-8' })
|
||||||
|
}
|
||||||
@ -2,7 +2,8 @@ import SITE_INFO from '@/config';
|
|||||||
export default () => {
|
export default () => {
|
||||||
const writeDom = document.querySelector('.header-main>.desc');
|
const writeDom = document.querySelector('.header-main>.desc');
|
||||||
if (!writeDom) return;
|
if (!writeDom) return;
|
||||||
const TypeWriteList = SITE_INFO.TypeWriteList;
|
const TypeWriteList: any = SITE_INFO.TypeWriteList;
|
||||||
|
if (!Array.isArray(TypeWriteList) || !TypeWriteList.length) return writeDom.remove();
|
||||||
let TypeWriteListIndex = 0;
|
let TypeWriteListIndex = 0;
|
||||||
let index = 0;
|
let index = 0;
|
||||||
let isDeleting = false;
|
let isDeleting = false;
|
||||||
|
|||||||
@ -25,10 +25,15 @@ article.vh-article-main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&>.article-meta {
|
&>.article-meta {
|
||||||
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.88rem;
|
gap: 0.88rem;
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
|
width: 100%;
|
||||||
|
height: 0.98rem;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
|
||||||
&>.article-meta-item {
|
&>.article-meta-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -40,6 +45,7 @@ article.vh-article-main {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
height: 0.888rem;
|
height: 0.888rem;
|
||||||
width: auto;
|
width: auto;
|
||||||
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
&>time {
|
&>time {
|
||||||
|
|||||||
@ -50,8 +50,6 @@
|
|||||||
// 隐藏滚动条
|
// 隐藏滚动条
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
// 平滑滚动
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
// 去除下划线
|
// 去除下划线
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
// 消除边框
|
// 消除边框
|
||||||
|
|||||||
@ -25,7 +25,7 @@ const getCategoriesList = async (categories: string) => {
|
|||||||
// 获取标签下的文章列表
|
// 获取标签下的文章列表
|
||||||
const getTagsList = async (tags: string) => {
|
const getTagsList = async (tags: string) => {
|
||||||
const posts = await getCollection("blog");
|
const posts = await getCollection("blog");
|
||||||
const articleList = posts.filter((i: any) => i.data.tags.includes(tags)).sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());;
|
const articleList = posts.filter((i: any) => (i.data.tags || []).map((_i: any) => (String(_i))).includes(tags)).sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
|
||||||
return fmtArticleList(articleList);
|
return fmtArticleList(articleList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ const getCountInfo = () => {
|
|||||||
// 获取文章标签
|
// 获取文章标签
|
||||||
const getTags = () => {
|
const getTags = () => {
|
||||||
const tagList = posts.reduce((acc: any, i: any) => {
|
const tagList = posts.reduce((acc: any, i: any) => {
|
||||||
i.data.tags.forEach((tag: string) => {
|
(i.data.tags || []).forEach((tag: string) => {
|
||||||
acc[tag] = (acc[tag] || 0) + 1;
|
acc[tag] = (acc[tag] || 0) + 1;
|
||||||
});
|
});
|
||||||
return acc;
|
return acc;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user