我的一些未解决疑难

三葉Leaves Author

Nextra Issue

我很喜欢 nextra 的风格,所以我不想放弃它。如果有人能看到我下面提出的问题,希望能简单指导我怎样完成这个实践。不胜感激。

我的环境

nextra version:nextra v4
我的操作系统:windows11
我的编辑器:cursor
node version:v20.18.2

我选择组织文章结构的方式:

使用 content 目录,并且在 src 下。

我的需求

  1. 我需要有一个项目的亮点首页,能通过localhost:3000访问到,首页地址在src\app\page.tsx,首页不能用 md或者mdx文件写,因为这达不到我的要求。

  2. 我不止想拥有一个文档,而是多个独立文档,他们应当分别显示在导航栏。举例:

    • 导航栏的“商业计划书”对应 localhost:3000/docs/,点进去以后是一个有各种子目录层级的独立文档。在某个文件夹内存放所有该文档的 mdx 文件,比如在其中存放了glossary.mdx,就可通过 localhost:3000/docs/glossary访问。

    • 导航栏的“技术手册”对应 localhost:3000/tech/,点进去以后也是一个有各种子目录层级的独立文档。在某个文件夹内存放所有该文档的 mdx 文件,比如存放了api.mdx,就可通过 localhost:3000/tech/api 访问。

冲突点在于:
如果我使用 app/docs/[[...mdxPath]]/page.jsx 这样的结构,并且配置contentDirBasePath: 'docs',那我另一个独立文档又该怎么实现对 localhost:3000/tech 结尾的匹配呢?

我的尝试

1. 采用多重路由

操作:

我创建如下的目录结构:

1
2
3
4
app/
├── docs/[[...mdxPath]]/page.jsx # 处理 /docs/* 路径
└── tech/[[...mdxPath]]/page.jsx # 处理 /tech/* 路径
└── ......

并分别更改其中的 page.jsx

1
2
3
4
5
6
7
// app/docs/[[...mdxPath]]/page.jsx
import { generateStaticParamsFor, importPage } from 'nextra/pages'

// 指定使用 'docs' 作为内容源
export const generateStaticParams = generateStaticParamsFor('docs')

// 其余代码保持不变...
1
2
3
4
5
6
7
// app/tech/[[...mdxPath]]/page.jsx
import { generateStaticParamsFor, importPage } from 'nextra/pages'

// 指定使用 'tech' 作为内容源
export const generateStaticParams = generateStaticParamsFor('tech')

// 其余代码保持不变...

相应的组织目录结构:

1
2
3
4
5
6
7
content/
├── docs/ # 商业计划书内容
│ ├── index.mdx
│ └── glossary.mdx
└── tech/ # 技术手册内容
├── index.mdx
└── api.mdx

调整next.config.mjs

1
2
3
const withNextra = nextra({
contentDirBasePath: '/' // 保持根路径配置
})
结果:

控制台报错:

1
2
Error: Page "/docs/[[...mdxPath]]/page" is missing param "/docs" in "generateStaticParams()", which is required with "output: export" config.  
at DevServer.renderToResponseWithComponentsImpl (file://D:\Projects\sanyeyun_docs\node_modules\next\dist\server\base-server.js:1186:49)

我不知道该怎么办,于是我删除了next.config.mjs里的以下内容:

1
2
3
4
5
6
   const nextConfig = {
-    // output: 'export', 删除这行
     images: {
       unoptimized: true
     }
   }

再次启动开发服务器,还是有如下报错:

1
2
3
4
5
6
7
8
9
10
11
12
13
- error [nextra] Error while loading { pathSegments: [] } Error: Cannot find module './undefined'
at webpackContextResolve (D:\Projects\sanyeyun_docs\.next\server\app\docs\[[...mdxPath]]\page.js:139:11)
at webpackContext (D:\Projects\sanyeyun_docs\.next\server\app\docs\[[...mdxPath]]\page.js:134:11)
at async Page (src\app\docs\[[...mdxPath]]\page.jsx:16:17)
14 | export default async function Page(props) {
15 | const params = await props.params
> 16 | const result = await importPage(params.mdxPath)
| ^
17 | const { default: MDXContent, toc, metadata } = result
18 | return (
19 | <Wrapper toc={toc} metadata={metadata}> {
code: 'MODULE_NOT_FOUND'
}

2. 采用单层路由,但是调整 content 目录里的结构

操作
  1. 我把 app 目录调整成如下结构:
1
2
3
4
5
app/
├── [[...mdxPath]]/page.jsx # 让[[...mdxPath]]直接在根目录
├── page.tsx
├── layout.jsx
├── ......
  1. 并且调整了 content 目录下的结构:
1
2
3
4
5
6
7
content/
├── docs/ # 商业计划书内容
│ ├── index.mdx
│ └── glossary.mdx
└── tech/ # 技术手册内容
├── index.mdx
└── api.mdx
  1. 并且把 contentDirBasePath 值更改为 '/'

然后我现在尝试启动开发服务器,发现有报错如下,导致启动不了:

1
[Error: You cannot define a route with the same specificity as a optional catch-all route ("/" and "/[[...mdxPath]]").]  

于是我删除了 src\app\page.tsx ,并且新建了一个 src\content\index.mdx来暂时替代首页。
我新建了 src\content\_meta.js,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default {
    index: {
        display: 'hidden',
    },
    docs:   {
        type: 'page',
        title: '商业计划书',
        display: 'children'
    },
    tech: {
        type: 'page',
        title: '技术手册',
        display: 'children'
    },
  }

这次似乎很接近的我想要的样子了,但是有两点的难以接受:

  1. 我不再有自定义的首页,因为首页是用 mdx 写的。
  2. 如果之后我想独立配置 tech 文件的 layout.jsx ,这似乎就不能实现,因为 content 目录下似乎只能解析 mdx 文件。

I really like the style of nextra, so I don’t want to give it up. If anyone sees my question below, I hope they can provide guidance on how to complete this practice. Thank you very much.

My Environment

  • Nextra version: nextra v4
  • My operating system: Windows 11
  • My editor: Cursor
  • Node version: v20.18.2

My Approach to Organizing Article Structure

I use the content directory, located under src.

My Requirements

  1. I need a project landing page, accessible via localhost:3000. This landing page is located at src\app\page.tsx. It cannot be written using md or mdx files, as that does not meet my requirements.

  2. I want to have multiple independent documentation sets, each displayed in the navigation bar. For example:

    • The “Business Plan” in the navigation bar corresponds to localhost:3000/docs/, which leads to an independent documentation set with various subdirectories. If a folder contains all the mdx files for this documentation, such as glossary.mdx, it should be accessible via localhost:3000/docs/glossary.

    • The “Technical Manual” in the navigation bar corresponds to localhost:3000/tech/, which also leads to an independent documentation set with various subdirectories. If a folder contains all the mdx files for this documentation, such as api.mdx, it should be accessible via localhost:3000/tech/api.

    The conflict arises:

    If I use a structure like app/docs/[[...mdxPath]]/page.jsx and configure contentDirBasePath: 'docs', how can I implement the matching for localhost:3000/tech for my other independent documentation set?

My Attempts

1. Using Multiple Routes

Actions:

I created the following directory structure:

1
2
3
4
app/
├── docs/[[...mdxPath]]/page.jsx # Handles /docs/* paths
└── tech/[[...mdxPath]]/page.jsx # Handles /tech/* paths
└── ......

And modified the page.jsx files as follows:

1
2
3
4
5
6
7
// app/docs/[[...mdxPath]]/page.jsx
import { generateStaticParamsFor, importPage } from 'nextra/pages'

// Specifies 'docs' as the content source
export const generateStaticParams = generateStaticParamsFor('docs')

// Rest of the code remains unchanged...
1
2
3
4
5
6
7
// app/tech/[[...mdxPath]]/page.jsx
import { generateStaticParamsFor, importPage } from 'nextra/pages'

// Specifies 'tech' as the content source
export const generateStaticParams = generateStaticParamsFor('tech')

// Rest of the code remains unchanged...

Correspondingly, the content directory structure is organized as:

1
2
3
4
5
6
7
content/
├── docs/ # Business Plan content
│ ├── index.mdx
│ └── glossary.mdx
└── tech/ # Technical Manual content
├── index.mdx
└── api.mdx

Modified next.config.mjs:

1
2
3
const withNextra = nextra({
contentDirBasePath: '/' // Keeps the root path configuration
})
Results:

The console throws an error:

Bash

1
2
Error: Page "/docs/[[...mdxPath]]/page" is missing param "/docs" in "generateStaticParams()", which is required with "output: export" config.
at DevServer.renderToResponseWithComponentsImpl (file://D:\Projects\sanyeyun_docs\node_modules\next\dist\server\base-server.js:1186:49)

I didn’t know what to do, so I removed the following from next.config.mjs:

1
2
3
4
5
6
const nextConfig = {
// output: 'export', // Removed this line
images: {
unoptimized: true
}
}

Restarting the development server still results in the following error:

1
2
3
4
5
6
7
8
9
10
11
12
13
- error [nextra] Error while loading { pathSegments: [] } Error: Cannot find module './undefined'
at webpackContextResolve (D:\Projects\sanyeyun_docs\.next\server\app\docs\[[...mdxPath]]\page.js:139:11)
at webpackContext (D:\Projects\sanyeyun_docs\.next\server\app\docs\[[...mdxPath]]\page.js:134:11)
at async Page (src\app\docs\[[...mdxPath]]\page.jsx:16:17)
14 | export default async function Page(props) {
15 | const params = await props.params
> 16 | const result = await importPage(params.mdxPath)
| ^
17 | const { default: MDXContent, toc, metadata } = result
18 | return (
19 | <Wrapper toc={toc} metadata={metadata}> {
code: 'MODULE_NOT_FOUND'
}

2. Using a Single-Layer Route but Adjusting the Structure in the content Directory

Actions:
  1. I adjusted the app directory to the following structure:
1
2
3
4
5
app/
├── [[...mdxPath]]/page.jsx # [[...mdxPath]] directly under the root
├── page.tsx
├── layout.jsx
├── ......
  1. And adjusted the structure under the content directory:
1
2
3
4
5
6
7
content/
├── docs/ # Business Plan content
│ ├── index.mdx
│ └── glossary.mdx
└── tech/ # Technical Manual content
├── index.mdx
└── api.mdx
  1. And changed the contentDirBasePath value to '/'.

Now, when I try to start the development server, I encounter the following error, which prevents it from starting:

1
[Error: You cannot define a route with the same specificity as a optional catch-all route ("/" and "/[[...mdxPath]]").]

So, I removed src\app\page.tsx and created a new src\content\index.mdx to temporarily replace the landing page.

I created a new src\content\_meta.js with the following content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default {
index: {
display: 'hidden',
},
docs: {
type: 'page',
title: 'Business Plan',
display: 'children'
},
tech: {
type: 'page',
title: 'Technical Manual',
display: 'children'
},
}

This seems to be very close to what I want, but there are two unacceptable points:

  1. I no longer have a custom landing page because the landing page is written in mdx.
  2. If I want to independently configure the layout.jsx for the tech files in the future, it seems impossible because the content directory can only parse mdx files.

关于重定向规则的配置

我的 crain.html 中包含如下内容:

1
<link rel="stylesheet" href="./css/index.css">

我有两个需求:

  1. 用户访问crain.oksanye.com的时候,自动代理到oksanye.com/friend/crain.html,并且 crain.html 中引用的文件,比如上面所示的 css 文件也要正确加载。(这一条是为了美化url )
  2. 用户访问oksanye.com/crain的时候,自动代理到oksanye.com/friend/crain.html,并且和上面一样, crain.html 中引用的文件,比如上面所示的 css 文件也要正确加载。(这一条是为了简化 url )
    我使用 netlify 托管我的网站。试图达成上述目的时,我在 netlify.toml 中写下了如下重定向规则:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[[redirects]]
from = "crain.oksanye.com"
to = "friend/crain.html"
status = 200
force = true

[[redirects]]
from = "crain.oksanye.com/*"
to = "friend/:splat"
status = 200
force = true

[[redirects]]
from = "/crain"
to = "/friend/crain.html"
status = 200
force = true

[[redirects]]
from = "/crain/*"
to = "/friend/:splat"
status = 200
force = true

现在实测情况是,访问crain.oksanye.com或者oksanye.com/crain的时候,crain.html均能被正常加载;但是crain.html中引用的文件(比如之前提到的 css 文件),仅使用crain.oksanye.com访问时能正常加载,而使用oksanye.com/crain访问时,加载异常,后台返回404
经过我排查,访问crain.oksanye.com的时候,自动代理到了crain.html,又因为 crain.html中引用了./css/index.css,所以后台可以看到 有关于 css 文件的请求 url 为https://crain.oksanye.com/css/index.css,这正好匹配到了上面 netlify.toml 中的第二条规则,故能正常加载;
而访问oksanye.com/crain的时候,先匹配到了规则3,所以代理转发到了crain.html,然后crain.html依然对引用的 css 文件发出请求,但后台可以看到请求 url 为https://oksanye.com/css/index.css,并不是预期的https://oksanye.com/crain/css/index.css。这另我很困惑,明明用户访问的是oksanye.com/crain,那么由oksanye.com/crain得到的crain.html包含的引用难道不应该附属到oksanye.com/crain后面,就像预期的oksanye.com/crain/css/index.css这样吗?最终,由于没有匹配到任何规则(尤其没匹配到是我期望的netlify.toml文件中的第四条规则,因为我期望中crain.html发出的请求是https://oksanye.com/crain/css/index.css,这样才能匹配到toml中的规则4),而https://oksanye.com/css/index.css这个地方本身也没有任何文件,所以这个请求返回了404。

Systemd和Docker的冲突

试图使用命令重启系统时,报错信息

1
2
3
4
5
6
7
8
9
10
11
12
13
root@ubuntuMC1:/# reboot
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down
Failed to talk to init daemon: Host is down

root@ubuntuMC1:/# shutdown -r now
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down
Failed to talk to init daemon: Host is down

试图使用systemd服务设置开机自启动应用时,已经写好.service文件,试图更新配置文件时报错

1
2
3
root@ubuntuMC1:/etc/systemd/system# systemctl daemon-reload
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down

关于rc*.d文件的困惑

这个问题在我试图设置开机自启动应用时遇到了

如上图所示,我的etc文件夹内的rc*.d十分奇怪,这和大多数教程中出现的不一样,我并不能理解其的含义。

Cron的启动运行悖论

我试图使用 cron 让某个任务在系统重启后自动执行(也就是开机自启),所以我在 crontab 里写了一条包含 @reboot 的规则。
但是 cron 本身在系统开机后就不会自启,那我还怎么实现开机自启的策略呢(不要告诉我用 rc.d 什么之类的,因为上一条困惑就和 rc.d 有关。

  • 标题: 我的一些未解决疑难
  • 作者: 三葉Leaves
  • 创建于 : 2024-09-25 00:00:00
  • 更新于 : 2025-03-17 15:28:13
  • 链接: https://blog.oksanye.com/b844e6cf51f5/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论