Skip to content

Commit 8f796dc

Browse files
committed
refactor(build): 重构构建和部署流程以支持动态路径
- 移除 package.json 中的多环境构建和预览命令,统一为单一构建和部署命令 - 优化 deploy.sh 脚本,去除子目录与根路径参数,实现统一构建流程 - 取消使用环境变量配置基础路径,改为采用相对路径('./')满足任意部署路径需求 - 新增 pathUtils 工具模块,动态获取基础路径和资源完整路径,自动适配多层子目录 - 修改代码中所有静态资源路径,统一通过 getAssetPath 函数获取,支持部署路径自动适配 - App.jsx 中路由 basename 使用 getRouterBasename 动态计算,替代硬编码环境变量路径 - main.jsx 注销遗留 Service Worker,避免影响应用更新和部署 - Vite 配置文件中 base 设为相对路径 './',简化部署环境要求和预览一致性维护
1 parent e0483c0 commit 8f796dc

10 files changed

Lines changed: 194 additions & 98 deletions

File tree

package.json

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
"dev": "vite",
88
"build": "vite build",
99
"clean": "rm -rf dist",
10-
"build:root": "npm run clean && VITE_BASE_PATH=/ vite build",
11-
"build:subdir": "npm run clean && vite build",
1210
"lint": "npm run lint:js && npm run lint:css",
1311
"lint:js": "eslint . --ext .js,.jsx",
1412
"lint:css": "stylelint \"**/*.css\"",
@@ -17,13 +15,10 @@
1715
"lint:css:fix": "stylelint \"**/*.css\" --fix",
1816
"format": "prettier --write \"**/*.{js,jsx,json,md,css}\"",
1917
"format:check": "prettier --check \"**/*.{js,jsx,json,md,css}\"",
20-
"preview": "vite preview",
21-
"preview:root": "npm run build:root && VITE_BASE_PATH=/ vite preview --port 4173",
22-
"preview:subdir": "npm run build:subdir && vite preview --port 4173",
18+
"preview": "vite preview --port 4173",
2319
"init": "node scripts/init.js",
2420
"update": "node scripts/update.js",
25-
"deploy:root": "./scripts/deploy.sh root",
26-
"deploy:subdir": "./scripts/deploy.sh",
21+
"deploy": "./scripts/deploy.sh",
2722
"prepare": "husky"
2823
},
2924
"bin": {

scripts/deploy.sh

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,15 @@
11
#!/bin/bash
22

33
# PPage 本地部署脚本
4-
# 用于将构建产出部署到 GitHub Pages
4+
# 用于将构建产出部署到 GitHub Pages(根路径部署)
55
#
66
# 使用方法:
7-
# 子目录部署(domain.com/ppage): ./scripts/deploy.sh
8-
# 根域名部署(domain.com): ./scripts/deploy.sh root
7+
# ./scripts/deploy.sh 或 npm run deploy
98

109
set -e
1110

1211
echo "🚀 开始构建和部署..."
1312

14-
# 检查部署类型参数
15-
DEPLOY_TYPE="${1:-subdir}"
16-
if [ "$DEPLOY_TYPE" = "root" ]; then
17-
echo "📍 部署类型: 根域名部署(/)"
18-
BUILD_COMMAND="npm run build:root"
19-
else
20-
echo "📍 部署类型: 子目录部署(/ppage/)"
21-
BUILD_COMMAND="npm run build:subdir"
22-
fi
23-
2413
# 0. 从 public/config.yml 读取配置
2514
echo "🔍 读取配置文件..."
2615

@@ -64,7 +53,7 @@ fi
6453

6554
# 2. 构建项目
6655
echo "📦 构建项目..."
67-
$BUILD_COMMAND
56+
npm run build
6857

6958
# 2.5. 生成 CNAME 文件(如果配置了自定义域名)
7059
if [ -n "$CUSTOM_DOMAIN" ]; then

src/App.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { Files } from './pages/Files'
1313
import { News } from './pages/News'
1414
import { DynamicDocumentPage } from './pages/DynamicDocumentPage'
1515
import { generateFolderConfigs } from './utils/folderScanner'
16+
import { getRouterBasename } from './utils/pathUtils'
1617

1718
function AppContent() {
1819
const siteConfig = useSiteConfig()
@@ -53,8 +54,8 @@ function AppContent() {
5354
}
5455
}, [siteConfig])
5556

56-
// 获取 Vite 配置的基础路径,支持子目录部署
57-
const basename = import.meta.env.BASE_URL || '/'
57+
// 使用自动计算的 basename,适配任意部署路径
58+
const basename = getRouterBasename()
5859

5960
return (
6061
<I18nProvider>

src/config/parser.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import yaml from 'js-yaml'
2+
import { getAssetPath } from '../utils/pathUtils'
23

34
/**
45
* 解析 YAML 配置文件
@@ -21,8 +22,9 @@ export function parseConfig(yamlContent) {
2122
*/
2223
export async function loadConfig() {
2324
try {
24-
// config.yml 是静态资源,始终从根路径访问,不需要 base 前缀
25-
const configPath = '/config.yml'
25+
// 使用 getAssetPath 获取正确的配置文件路径
26+
// 自动适配任意部署路径
27+
const configPath = getAssetPath('/config.yml')
2628
const response = await fetch(configPath)
2729
if (!response.ok) {
2830
throw new Error(`配置文件加载失败: ${response.statusText}`)

src/main.jsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,18 @@ import { createRoot } from 'react-dom/client'
33
import './index.css'
44
import App from './App.jsx'
55

6+
// 注销所有 Service Worker(清理遗留的 SW)
7+
if ('serviceWorker' in navigator) {
8+
navigator.serviceWorker.getRegistrations().then(registrations => {
9+
registrations.forEach(registration => {
10+
registration.unregister()
11+
console.log('Service Worker unregistered:', registration.scope)
12+
})
13+
})
14+
}
15+
616
createRoot(document.getElementById('root')).render(
717
<StrictMode>
818
<App />
9-
</StrictMode>,
19+
</StrictMode>
1020
)

src/utils/folderScanner.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
* 并为每个文件夹生成相应的文档中心配置
55
*/
66

7-
// 获取 base 路径(用于 fetch 请求)
8-
const base = import.meta.env.BASE_URL || '/'
7+
import { getAssetPath } from './pathUtils'
98

109
/**
1110
* 获取所有 Markdown 文件的 glob 模块
@@ -101,8 +100,8 @@ export function hasIndexFile(folderName) {
101100
*/
102101
export async function loadFolderIndex(folderName) {
103102
// 构建 index.md 路径
104-
// 注意:content 目录是静态资源,始终从根路径访问,不需要 base 前缀
105-
const indexPath = `/content/${folderName}/index.md`
103+
// 使用 getAssetPath 自动适配部署路径
104+
const indexPath = getAssetPath(`/content/${folderName}/index.md`)
106105

107106
try {
108107
const response = await fetch(indexPath)

src/utils/i18nMarkdown.js

Lines changed: 64 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,48 @@
33
* 支持根据语言代码加载对应的 Markdown 文件
44
*/
55

6+
import { getAssetPath } from './pathUtils'
7+
68
/**
79
* 加载多语言 Markdown 文件
810
* @param {string} basePath - 基础路径(不含语言后缀),如 '/content/pages/about-site'
911
* @param {string} language - 语言代码,如 'zh' 或 'en'
1012
* @param {string} fallbackLanguage - 后备语言代码,默认 'zh'
1113
* @returns {Promise<string|null>} Markdown 内容,如果加载失败返回 null
1214
*/
13-
export async function loadI18nMarkdown(basePath, language, fallbackLanguage = 'zh') {
15+
export async function loadI18nMarkdown(
16+
basePath,
17+
language,
18+
fallbackLanguage = 'zh'
19+
) {
1420
// 尝试加载指定语言的文件
1521
const paths = [
16-
`${basePath}.${language}.md`, // 如: /content/pages/about-site.zh.md
17-
`${basePath}.md`, // 如: /content/pages/about-site.md (无语言后缀)
18-
];
22+
`${basePath}.${language}.md`, // 如: /content/pages/about-site.zh.md
23+
`${basePath}.md`, // 如: /content/pages/about-site.md (无语言后缀)
24+
]
1925

2026
// 如果指定语言不是后备语言,也尝试后备语言
2127
if (language !== fallbackLanguage) {
22-
paths.push(`${basePath}.${fallbackLanguage}.md`);
28+
paths.push(`${basePath}.${fallbackLanguage}.md`)
2329
}
2430

2531
for (const path of paths) {
2632
try {
27-
const response = await fetch(path);
33+
// 使用 getAssetPath 自动适配部署路径
34+
const fullPath = getAssetPath(path)
35+
const response = await fetch(fullPath)
2836
if (response.ok) {
29-
const content = await response.text();
30-
console.log(`成功加载 Markdown: ${path}`);
31-
return content;
37+
const content = await response.text()
38+
console.log(`成功加载 Markdown: ${path}`)
39+
return content
3240
}
3341
} catch (error) {
34-
console.warn(`无法加载 ${path}:`, error.message);
42+
console.warn(`无法加载 ${path}:`, error.message)
3543
}
3644
}
3745

38-
console.warn(`所有路径都加载失败: ${paths.join(', ')}`);
39-
return null;
46+
console.warn(`所有路径都加载失败: ${paths.join(', ')}`)
47+
return null
4048
}
4149

4250
/**
@@ -46,18 +54,18 @@ export async function loadI18nMarkdown(basePath, language, fallbackLanguage = 'z
4654
* @returns {Promise<Object>} 语言代码到内容的映射
4755
*/
4856
export async function preloadI18nMarkdown(basePath, languages = ['zh', 'en']) {
49-
const result = {};
50-
57+
const result = {}
58+
5159
await Promise.all(
52-
languages.map(async (lang) => {
53-
const content = await loadI18nMarkdown(basePath, lang);
60+
languages.map(async lang => {
61+
const content = await loadI18nMarkdown(basePath, lang)
5462
if (content) {
55-
result[lang] = content;
63+
result[lang] = content
5664
}
5765
})
58-
);
59-
60-
return result;
66+
)
67+
68+
return result
6169
}
6270

6371
/**
@@ -67,8 +75,8 @@ export async function preloadI18nMarkdown(basePath, languages = ['zh', 'en']) {
6775
*/
6876
export function extractLanguageFromPath(path) {
6977
// 匹配 .zh.md, .en.md 等模式
70-
const match = path.match(/\.(zh|en)\.md$/);
71-
return match ? match[1] : null;
78+
const match = path.match(/\.(zh|en)\.md$/)
79+
return match ? match[1] : null
7280
}
7381

7482
/**
@@ -78,7 +86,7 @@ export function extractLanguageFromPath(path) {
7886
*/
7987
export function getBasePath(path) {
8088
// 移除语言后缀(如果存在)
81-
return path.replace(/\.(zh|en)\.md$/, '');
89+
return path.replace(/\.(zh|en)\.md$/, '')
8290
}
8391

8492
/**
@@ -88,60 +96,64 @@ export function getBasePath(path) {
8896
* 2. 如果没有当前语言版本,选择无语言后缀的文件(如 about.md)
8997
* 3. 如果都没有,使用后备语言版本
9098
* 4. 确保每个基础文件名只出现一次
91-
*
99+
*
92100
* @param {Array} files - 文件列表,每个文件包含 path 属性
93101
* @param {string} currentLanguage - 当前语言代码
94102
* @param {string} fallbackLanguage - 后备语言代码
95103
* @returns {Array} 过滤后的文件列表
96104
*/
97-
export function filterMarkdownByLanguage(files, currentLanguage = 'zh', fallbackLanguage = 'zh') {
105+
export function filterMarkdownByLanguage(
106+
files,
107+
currentLanguage = 'zh',
108+
fallbackLanguage = 'zh'
109+
) {
98110
// 按基础路径分组
99-
const fileGroups = new Map();
100-
111+
const fileGroups = new Map()
112+
101113
files.forEach(file => {
102-
const basePath = getBasePath(file.path);
103-
const lang = extractLanguageFromPath(file.path);
104-
114+
const basePath = getBasePath(file.path)
115+
const lang = extractLanguageFromPath(file.path)
116+
105117
if (!fileGroups.has(basePath)) {
106-
fileGroups.set(basePath, []);
118+
fileGroups.set(basePath, [])
107119
}
108-
120+
109121
fileGroups.get(basePath).push({
110122
...file,
111123
language: lang,
112-
basePath
113-
});
114-
});
115-
124+
basePath,
125+
})
126+
})
127+
116128
// 为每个基础路径选择最合适的版本
117-
const result = [];
118-
129+
const result = []
130+
119131
fileGroups.forEach((group, basePath) => {
120132
// 优先级:当前语言 > 无语言后缀 > 后备语言 > 其他
121-
let selected = null;
122-
133+
let selected = null
134+
123135
// 1. 尝试找到当前语言版本
124-
selected = group.find(f => f.language === currentLanguage);
125-
136+
selected = group.find(f => f.language === currentLanguage)
137+
126138
// 2. 如果没有,尝试找到无语言后缀版本
127139
if (!selected) {
128-
selected = group.find(f => f.language === null);
140+
selected = group.find(f => f.language === null)
129141
}
130-
142+
131143
// 3. 如果没有,尝试找到后备语言版本
132144
if (!selected && currentLanguage !== fallbackLanguage) {
133-
selected = group.find(f => f.language === fallbackLanguage);
145+
selected = group.find(f => f.language === fallbackLanguage)
134146
}
135-
147+
136148
// 4. 如果都没有,使用第一个可用的文件
137149
if (!selected && group.length > 0) {
138-
selected = group[0];
150+
selected = group[0]
139151
}
140-
152+
141153
if (selected) {
142-
result.push(selected);
154+
result.push(selected)
143155
}
144-
});
145-
146-
return result;
156+
})
157+
158+
return result
147159
}

src/utils/markdownIndex.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
import { getAllMarkdownModules } from './folderScanner'
7+
import { getAssetPath } from './pathUtils'
78

89
// 使用文件夹扫描器获取所有 Markdown 模块
910
const allModules = getAllMarkdownModules()
@@ -63,9 +64,9 @@ export async function loadAllMarkdownFiles() {
6364
for (const path in allModules) {
6465
try {
6566
// 将相对路径转换为绝对路径
66-
// 注意:content 目录是静态资源,始终从根路径访问,不需要 base 前缀
67+
// 使用 getAssetPath 自动适配部署路径
6768
const relativePath = path.replace('../..', '')
68-
const absolutePath = relativePath
69+
const absolutePath = getAssetPath(relativePath)
6970
const filename = path.split('/').pop()
7071

7172
// 提取文件夹名称
@@ -153,9 +154,9 @@ export async function loadFolderMarkdownFiles(folderName) {
153154

154155
try {
155156
// 将相对路径转换为绝对路径
156-
// 注意:content 目录是静态资源,始终从根路径访问,不需要 base 前缀
157+
// 使用 getAssetPath 自动适配部署路径
157158
const relativePath = path.replace('../..', '')
158-
const absolutePath = relativePath
159+
const absolutePath = getAssetPath(relativePath)
159160
const filename = path.split('/').pop()
160161

161162
const response = await fetch(absolutePath)

0 commit comments

Comments
 (0)