.*?<\/div><\/div>
.*?<\/div><\/div><\/div><\/div>/g
- return content.replace(regexExp, '')
+ try {
+ fs.mkdirSync('./public/rss', { recursive: true })
+ fs.writeFileSync('./public/rss/feed.xml', feed.rss2())
+ fs.writeFileSync('./public/rss/atom.xml', feed.atom1())
+ fs.writeFileSync('./public/rss/feed.json', feed.json1())
+ } catch (error) {
+ // 在vercel运行环境是只读的,这里会报错;
+ // 但在vercel编译阶段、或VPS等其他平台这行代码会成功执行
+ // RSS被高频词访问将大量消耗服务端资源,故作为静态文件
}
}
diff --git a/lib/sitemap.xml.js b/lib/sitemap.xml.js
index 0e057610..7522951d 100644
--- a/lib/sitemap.xml.js
+++ b/lib/sitemap.xml.js
@@ -3,8 +3,6 @@ import fs from 'fs'
import BLOG from '@/blog.config'
export async function generateSitemapXml({ allPages }) {
- fs.mkdirSync('./public', { recursive: true })
-
const urls = [{
loc: `${BLOG.LINK}`,
lastmod: new Date().toISOString().split('T')[0],
@@ -31,9 +29,13 @@ export async function generateSitemapXml({ allPages }) {
})
})
const xml = createSitemapXml(urls)
- fs.writeFileSync('./public/sitemap.xml', xml)
+ try {
+ fs.writeFileSync('sitemap.xml', xml)
+ fs.writeFileSync('./public/sitemap.xml', xml)
+ } catch (error) {
+ console.warn('无法写入文件', error)
+ }
}
-
function createSitemapXml(urls) {
let urlsXml = ''
urls.forEach(u => {
@@ -46,7 +48,7 @@ function createSitemapXml(urls) {
})
return `
- {
+ let isMobile = false
+ if (!isBrowser()) {
+ return isMobile
+ }
+
+ // 这个判断会引发 TypeError: navigator.userAgentData.mobile is undefined 问题,导致博客无法正常工作
+ // if (!isMobile && navigator.userAgentData.mobile) {
+ // isMobile = true
+ // }
+
+ if (!isMobile && (/Mobi|Android|iPhone/i.test(navigator.userAgent))) {
+ isMobile = true
+ }
+
+ if (/Android|iPhone|iPad|iPod/i.test(navigator.platform)) {
+ isMobile = true
+ }
+
+ if (typeof window.orientation !== 'undefined') {
+ isMobile = true
+ }
+
+ return isMobile
+}
diff --git a/pages/_app.js b/pages/_app.js
index 41feba71..e6749281 100644
--- a/pages/_app.js
+++ b/pages/_app.js
@@ -24,6 +24,7 @@ import smoothscroll from 'smoothscroll-polyfill'
import AOS from 'aos'
import 'aos/dist/aos.css' // You can also use for styles
+import { isMobile } from '@/lib/utils'
const Ackee = dynamic(() => import('@/components/Ackee'), { ssr: false })
const Gtag = dynamic(() => import('@/components/Gtag'), { ssr: false })
@@ -57,7 +58,9 @@ const MyApp = ({ Component, pageProps }) => {
useEffect(() => {
AOS.init()
- smoothscroll.polyfill()
+ if (isMobile()) {
+ smoothscroll.polyfill()
+ }
}, [])
return (
diff --git a/pages/index.js b/pages/index.js
index 663462fd..542500fb 100644
--- a/pages/index.js
+++ b/pages/index.js
@@ -5,7 +5,6 @@ import * as ThemeMap from '@/themes'
import { useGlobal } from '@/lib/global'
import { generateRss } from '@/lib/rss'
import { generateRobotsTxt } from '@/lib/robots.txt'
-import { generateSitemapXml } from '@/lib/sitemap.xml'
const Index = props => {
const { theme } = useGlobal()
const ThemeComponents = ThemeMap[theme]
@@ -44,14 +43,12 @@ export async function getStaticProps() {
}
}
+ // 生成robotTxt
+ generateRobotsTxt()
// 生成Feed订阅
if (JSON.parse(BLOG.ENABLE_RSS)) {
generateRss(props?.latestPosts || [])
}
- // 生成robotTxt
- generateRobotsTxt()
- // 生成sitemap.xml
- generateSitemapXml({ allPages: props.allPages })
delete props.allPages
diff --git a/pages/sitemap.xml.js b/pages/sitemap.xml.js
new file mode 100644
index 00000000..b17b3e45
--- /dev/null
+++ b/pages/sitemap.xml.js
@@ -0,0 +1,60 @@
+// pages/sitemap.xml.js
+import { getServerSideSitemap } from 'next-sitemap'
+import { getGlobalNotionData } from '@/lib/notion/getNotionData'
+import BLOG from '@/blog.config'
+
+export const getServerSideProps = async (ctx) => {
+ const { allPages } = await getGlobalNotionData({ from: 'rss' })
+ const defaultFields = [
+ {
+ loc: `${BLOG.LINK}`,
+ lastmod: new Date().toISOString().split('T')[0],
+ changefreq: 'daily',
+ priority: '0.7'
+ }, {
+ loc: `${BLOG.LINK}/archive`,
+ lastmod: new Date().toISOString().split('T')[0],
+ changefreq: 'daily',
+ priority: '0.7'
+ }, {
+ loc: `${BLOG.LINK}/category`,
+ lastmod: new Date().toISOString().split('T')[0],
+ changefreq: 'daily',
+ priority: '0.7'
+ }, {
+ loc: `${BLOG.LINK}/feed`,
+ lastmod: new Date().toISOString().split('T')[0],
+ changefreq: 'daily',
+ priority: '0.7'
+ }, {
+ loc: `${BLOG.LINK}/search`,
+ lastmod: new Date().toISOString().split('T')[0],
+ changefreq: 'daily',
+ priority: '0.7'
+ }, {
+ loc: `${BLOG.LINK}/tag`,
+ lastmod: new Date().toISOString().split('T')[0],
+ changefreq: 'daily',
+ priority: '0.7'
+ }
+ ]
+ const postFields = allPages?.map(post => {
+ return {
+ loc: `${BLOG.LINK}/${post.slug}`,
+ lastmod: new Date(post?.date?.start_date || post?.createdTime).toISOString().split('T')[0],
+ changefreq: 'daily',
+ priority: '0.7'
+ }
+ })
+ const fields = defaultFields.concat(postFields)
+
+ // 缓存
+ ctx.res.setHeader(
+ 'Cache-Control',
+ 'public, max-age=3600, stale-while-revalidate=59'
+ )
+
+ return getServerSideSitemap(ctx, fields)
+}
+
+export default () => { }
diff --git a/themes/example/components/BlogListScroll.js b/themes/example/components/BlogListScroll.js
index d70e4bc5..b3d7f3f0 100644
--- a/themes/example/components/BlogListScroll.js
+++ b/themes/example/components/BlogListScroll.js
@@ -78,5 +78,5 @@ export const BlogListScroll = props => {
- );
+ )
}
diff --git a/themes/hexo/LayoutBase.js b/themes/hexo/LayoutBase.js
index 701daa71..066c4404 100644
--- a/themes/hexo/LayoutBase.js
+++ b/themes/hexo/LayoutBase.js
@@ -1,6 +1,6 @@
import CommonHead from '@/components/CommonHead'
-import { useEffect, useState } from 'react'
-
+import { useCallback, useEffect, useState } from 'react'
+import throttle from 'lodash.throttle'
import Footer from './components/Footer'
import JumpToTopButton from './components/JumpToTopButton'
import SideRight from './components/SideRight'
@@ -42,23 +42,20 @@ const LayoutBase = props => {
>
)
const { onLoading } = useGlobal()
+ const throttleMs = 200
+ const scrollListener = useCallback(throttle(() => {
+ const targetRef = document.getElementById('wrapper')
+ const clientHeight = targetRef?.clientHeight
+ const scrollY = window.pageYOffset
+ const fullHeight = clientHeight - window.outerHeight
+ let per = parseFloat(((scrollY / fullHeight) * 100).toFixed(0))
+ if (per > 100) per = 100
+ const shouldShow = scrollY > 100 && per > 0
- const scrollListener = () => {
- requestAnimationFrame(() => {
- const targetRef = document.getElementById('wrapper')
- const clientHeight = targetRef?.clientHeight
- const scrollY = window.pageYOffset
- const fullHeight = clientHeight - window.outerHeight
- let per = parseFloat(((scrollY / fullHeight) * 100).toFixed(0))
- if (per > 100) per = 100
- const shouldShow = scrollY > 100 && per > 0
-
- if (shouldShow !== showFloatButton) {
- switchShow(shouldShow)
- }
- // changePercent(per)
- })
- }
+ if (shouldShow !== showFloatButton) {
+ switchShow(shouldShow)
+ }
+ }, throttleMs))
useEffect(() => {
document.addEventListener('scroll', scrollListener)
return () => document.removeEventListener('scroll', scrollListener)
diff --git a/themes/hexo/components/Announcement.js b/themes/hexo/components/Announcement.js
index 391fd3a8..677cb467 100644
--- a/themes/hexo/components/Announcement.js
+++ b/themes/hexo/components/Announcement.js
@@ -7,7 +7,7 @@ const Announcement = ({ post, className }) => {
const { locale } = useGlobal()
if (post?.blockMap) {
return