Skip to content

Commit 766c1f8

Browse files
authored
Merge pull request tangly1024#3904 from tangly1024/release/4.9.4.2
Release/4.9.4.2
2 parents fa52365 + 9a803c1 commit 766c1f8

10 files changed

Lines changed: 252 additions & 69 deletions

File tree

lib/build/prefetch.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// lib/build/prefetch.js
2+
import pLimit from 'p-limit'
3+
import { fetchNotionPageBlocks } from '@/lib/db/notion/getPostBlocks'
4+
import {
5+
getDataFromCache,
6+
setDataToCache
7+
8+
} from '@/lib/cache/cache_manager'
9+
10+
11+
// lib/build/prefetch.js 中新增,或单独放 lib/build/buildUtils.js
12+
13+
/**
14+
* 获取优先预生成的页面
15+
* - 最新5篇(按发布时间倒序)
16+
* - 默认排序前5篇(allPages 原始顺序)
17+
* - 两者合并去重
18+
*/
19+
export function getPriorityPages(allPages) {
20+
const published = (allPages ?? []).filter(
21+
p => p.type === 'Post' && p.status === 'Published'
22+
)
23+
24+
// 默认排序前5
25+
const top5Default = published.slice(0, 5)
26+
27+
// 按发布时间最新5
28+
const top5Latest = [...published]
29+
.sort((a, b) => new Date(b.publishDate) - new Date(a.publishDate))
30+
.slice(0, 5)
31+
32+
// 合并去重
33+
const seen = new Set()
34+
return [...top5Default, ...top5Latest].filter(p => {
35+
if (seen.has(p.id)) return false
36+
seen.add(p.id)
37+
return true
38+
})
39+
}
40+
41+
/**
42+
* 预热所有页面的 block 数据
43+
* @param {*} allPages 页面列表
44+
* @param {*} concurrency 并发数
45+
*/
46+
export async function prefetchAllBlockMaps(allPages, concurrency = 8) {
47+
const limit = pLimit(concurrency)
48+
let hit = 0, fetched = 0, failed = 0
49+
50+
console.log(`[Prefetch] 开始预热 ${allPages.length} 个页面 block`)
51+
const start = Date.now()
52+
53+
await Promise.all(
54+
allPages.map(page =>
55+
limit(async () => {
56+
const cacheKey = `page_block_${page.id}`
57+
58+
// 已有缓存跳过
59+
if (await getDataFromCache(cacheKey)) {
60+
hit++
61+
return
62+
}
63+
64+
try {
65+
const block = await fetchNotionPageBlocks(page.id, 'prefetch')
66+
await setDataToCache(cacheKey, block, 1000 * 60 * 60 * 2) // 2小时
67+
fetched++
68+
} catch (e) {
69+
console.warn('[Prefetch Failed]', page.id, e.message)
70+
failed++
71+
}
72+
})
73+
)
74+
)
75+
76+
const elapsed = ((Date.now() - start) / 1000).toFixed(1)
77+
console.log(`[Prefetch] 完成: 命中缓存=${hit} 新拉取=${fetched} 失败=${failed} 耗时=${elapsed}s`)
78+
}

lib/build/staticPaths.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
// lib/build/staticPaths.js
3+
import { fetchGlobalAllData } from '@/lib/db/notion'
4+
import { prefetchAllBlockMaps } from '@/lib/build/prefetch'
5+
import { isExport } from '../utils/buildMode'
6+
7+
let _prefetchDone = false // 模块级标记,同一构建进程只预热一次
8+
9+
export async function getStaticPathsBase(filterFn) {
10+
if (!isExport()) {
11+
return { paths: [], fallback: 'blocking' }
12+
}
13+
14+
const { allPages } = await fetchGlobalAllData({ from: 'static-paths' })
15+
16+
// 只在第一个调用的路由文件里执行预热
17+
if (!_prefetchDone) {
18+
_prefetchDone = true
19+
// 预热全部,但优先处理最新5篇
20+
await prefetchAllBlockMaps(allPages)
21+
}
22+
23+
return {
24+
paths: allPages.filter(filterFn).map(pageToParams),
25+
fallback: false
26+
}
27+
}
28+
29+
// 把最新5篇单独导出,供各路由文件复用
30+
export async function getLatestSlugs(allPages, count = 5) {
31+
return [...allPages]
32+
.filter(p => p.type === 'Post' && p.status === 'Published')
33+
.sort((a, b) => new Date(b.publishDate) - new Date(a.publishDate))
34+
.slice(0, count)
35+
}
36+
37+
// 把最新5篇单独导出,供各路由文件复用
38+
export async function getLatestSlugs(allPages, count = 5) {
39+
return [...allPages]
40+
.filter(p => p.type === 'Post' && p.status === 'Published')
41+
.sort((a, b) => new Date(b.publishDate) - new Date(a.publishDate))
42+
.slice(0, count)
43+
}

lib/cache/cache_manager.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,8 @@ export async function delCacheData(key) {
141141

142142
function getCacheType() {
143143
if (hasRedis) return 'redis'
144-
if (isVercelEnv()) return 'vercel'
145144
if (isBuildPhase) return 'file'
145+
// if (isVercelEnv()) return 'vercel'
146146
return 'memory'
147147
}
148148

lib/db/SiteDataApi.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import {
2626
import { fetchPageFromNotion } from './notion/getNotionPost'
2727
import { processPostData } from '../utils/post'
2828
import { adapterNotionBlockMap } from '../utils/notion.util'
29-
import pLimit from 'p-limit'
29+
// import pLimit from 'p-limit'
3030

3131
export { getAllTags } from './notion/getAllTags'
3232
export { fetchPageFromNotion as getPost } from './notion/getNotionPost'

lib/utils/buildMode.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* 是否静态导出;或ISR动态站点
3+
*/
4+
function isExport() {
5+
return process.env.EXPORT === 'true'
6+
}
7+
8+
module.exports = { isExport }

lib/utils/pageId.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*/
77
function extractLangPrefix(str) {
88
const match = str.match(/^(.+?):/)
9-
if (match && match[1]) {
9+
if (match?.[1]) {
1010
return match[1]
1111
} else {
1212
return ''
@@ -22,7 +22,7 @@ function extractLangId(str) {
2222
// 使用正则表达式匹配字符串
2323
const match = str.match(/:\s*(.+)/)
2424
// 如果匹配成功,则返回匹配到的内容
25-
if (match && match[1]) {
25+
if (match?.[1]) {
2626
return match[1]
2727
} else {
2828
// 如果没有匹配到,则返回空字符串或者其他你想要返回的值
@@ -35,10 +35,11 @@ function extractLangId(str) {
3535
*/
3636

3737
function getShortId(uuid) {
38-
if (!uuid || uuid.indexOf('-') < 0) {
38+
if (!uuid?.includes('-')) {
3939
return uuid
4040
}
4141
return uuid.substring(14)
4242
}
4343

44+
4445
module.exports = { extractLangPrefix, extractLangId, getShortId }

next.config.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
const { THEME } = require('./blog.config')
2-
const fs = require('fs')
3-
const path = require('path')
2+
const fs = require('node:fs')
3+
const path = require('node:path')
44
const BLOG = require('./blog.config')
55
const { extractLangPrefix } = require('./lib/utils/pageId')
6+
const { isExport } = require('./lib/utils/buildMode')
67

78
// 打包时是否分析代码
89
const withBundleAnalyzer = require('@next/bundle-analyzer')({
@@ -18,8 +19,7 @@ const locales = (function () {
1819
const langs = [BLOG.LANG]
1920
if (BLOG.NOTION_PAGE_ID.indexOf(',') > 0) {
2021
const siteIds = BLOG.NOTION_PAGE_ID.split(',')
21-
for (let index = 0; index < siteIds.length; index++) {
22-
const siteId = siteIds[index]
22+
for (const siteId of siteIds) {
2323
const prefix = extractLangPrefix(siteId)
2424
// 如果包含前缀 例如 zh , en 等
2525
if (prefix) {
@@ -36,8 +36,8 @@ const locales = (function () {
3636
// eslint-disable-next-line no-unused-vars
3737
const preBuild = (function () {
3838
if (
39-
!process.env.npm_lifecycle_event === 'export' &&
40-
!process.env.npm_lifecycle_event === 'build'
39+
process.env.npm_lifecycle_event !== 'export' &&
40+
process.env.npm_lifecycle_event !== 'build'
4141
) {
4242
return
4343
}
@@ -81,7 +81,7 @@ function scanSubdirectories(directory) {
8181
*/
8282

8383
function getOutput() {
84-
if (process.env.EXPORT) return 'export'
84+
if (isExport()) return 'export'
8585
if (process.env.NEXT_BUILD_STANDALONE === 'true') return 'standalone'
8686
return undefined
8787
}
@@ -163,8 +163,7 @@ const nextConfig = {
163163
if (BLOG.NOTION_PAGE_ID.indexOf(',') > 0) {
164164
const siteIds = BLOG.NOTION_PAGE_ID.split(',')
165165
const langs = []
166-
for (let index = 0; index < siteIds.length; index++) {
167-
const siteId = siteIds[index]
166+
for (const siteId of siteIds) {
168167
const prefix = extractLangPrefix(siteId)
169168
// 如果包含前缀 例如 zh , en 等
170169
if (prefix) {

pages/[prefix]/[slug]/[...suffix].js

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import BLOG from '@/blog.config'
22
import { siteConfig } from '@/lib/config'
33
import { fetchGlobalAllData, resolvePostProps } from '@/lib/db/SiteDataApi'
4-
import { checkSlugHasMorThanTwoSlash, processPostData } from '@/lib/utils/post'
5-
import { idToUuid } from 'notion-utils'
4+
import { checkSlugHasMorThanTwoSlash } from '@/lib/utils/post'
65
import Slug from '..'
6+
import { isExport } from '@/lib/utils/buildMode'
7+
import { getPriorityPages, prefetchAllBlockMaps } from '@/lib/build/prefetch'
78

89
/**
910
* 根据notion的slug访问页面
@@ -15,32 +16,44 @@ const PrefixSlug = props => {
1516
return <Slug {...props} />
1617
}
1718

18-
/**
19-
* 编译渲染页面路径
20-
* @returns
21-
*/
19+
2220
export async function getStaticPaths() {
23-
if (!BLOG.isProd) {
21+
const from = 'slug-paths'
22+
const { allPages } = await fetchGlobalAllData({ from })
23+
24+
// Export 模式:全量预生成
25+
if (isExport()) {
26+
await prefetchAllBlockMaps(allPages)
2427
return {
25-
paths: [],
26-
fallback: true
28+
paths: allPages
29+
?.filter(row => checkSlugHasMorThanTwoSlash(row))
30+
.map(row => ({
31+
params: {
32+
prefix: row.slug.split('/')[0],
33+
slug: row.slug.split('/')[1],
34+
suffix: row.slug.split('/').slice(2)
35+
}
36+
})),
37+
fallback: false
2738
}
2839
}
2940

30-
const from = 'slug-paths'
31-
const { allPages } = await fetchGlobalAllData({ from })
32-
const paths = allPages
33-
?.filter(row => checkSlugHasMorThanTwoSlash(row))
34-
.map(row => ({
35-
params: {
36-
prefix: row.slug.split('/')[0],
37-
slug: row.slug.split('/')[1],
38-
suffix: row.slug.split('/').slice(2)
39-
}
40-
}))
41+
// ISR 模式:预生成最新10篇(仅三段以上路径格式)
42+
const tops = getPriorityPages(allPages)
43+
44+
await prefetchAllBlockMaps(tops)
45+
4146
return {
42-
paths: paths,
43-
fallback: true
47+
paths: tops
48+
.filter(p => checkSlugHasMorThanTwoSlash(p))
49+
.map(row => ({
50+
params: {
51+
prefix: row.slug.split('/')[0],
52+
slug: row.slug.split('/')[1],
53+
suffix: row.slug.split('/').slice(2)
54+
}
55+
})),
56+
fallback: 'blocking'
4457
}
4558
}
4659

@@ -53,6 +66,7 @@ export async function getStaticProps({
5366
params: { prefix, slug, suffix },
5467
locale
5568
}) {
69+
5670
const props = await resolvePostProps({
5771
prefix,
5872
slug,
@@ -62,13 +76,14 @@ export async function getStaticProps({
6276

6377
return {
6478
props,
65-
revalidate: process.env.EXPORT
79+
revalidate: isExport()
6680
? undefined
6781
: siteConfig(
6882
'NEXT_REVALIDATE_SECOND',
6983
BLOG.NEXT_REVALIDATE_SECOND,
7084
props.NOTION_CONFIG
71-
)
85+
),
86+
notFound: !props.post
7287
}
7388
}
7489

0 commit comments

Comments
 (0)