From e9c32c4118aac6e60e910981fa2e41d5e9a6a5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E4=B8=80=E4=B9=8B?= Date: Sun, 24 Mar 2024 18:34:18 +0800 Subject: [PATCH] =?UTF-8?q?docusaurus=E6=8F=92=E4=BB=B6=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/dev/frontend/docusaurus插件开发.md | 229 ++++++++++++++++++ .../image-20240324133547293.png | Bin 0 -> 31583 bytes docs/dev/language/golang/code/io_test.go | 29 +++ docs/dev/language/golang/go-io.md | 104 +++++--- docs/dev/language/golang/go-slice.md | 4 +- docusaurus.config.ts | 6 +- .../src/index.ts | 5 - src/theme/Heading/index.tsx | 3 +- src/theme/Layout/Provider/antd.tsx | 7 +- 9 files changed, 340 insertions(+), 47 deletions(-) create mode 100644 docs/dev/frontend/docusaurus插件开发.md create mode 100644 docs/dev/frontend/img/docusaurus插件开发.assets/image-20240324133547293.png diff --git a/docs/dev/frontend/docusaurus插件开发.md b/docs/dev/frontend/docusaurus插件开发.md new file mode 100644 index 0000000..f631f15 --- /dev/null +++ b/docs/dev/frontend/docusaurus插件开发.md @@ -0,0 +1,229 @@ +# docusaurus 的插件开发与自定义主题 + +我为了本博客的一些需求,开发了一些 docusaurus 的插件,这里记录一下开发过程。其中踩到了一些坑,折腾了我好几天,结果回过头来一看是那么简单,希望对你能有所帮助。 + +## 创建插件 + +你需要创建一个类似下面的 npm 包: + +```json +{ + "name": "docusaurus-plugin-docs-info", + "version": "1.0.0", + "description": "", + "main": "src/index.ts", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@docusaurus/core": "^3.1.1", + "dayjs": "^1.11.10", + "reading-time": "^1.5.0" + } +} +``` + +目录结构就像这样: + +![image-20240324133547293](img/docusaurus%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91.assets/image-20240324133547293.png) + +其中源代码放在 src 目录下,theme 是相关主题,components 是组件 + +## 引用插件 + +创建完成插件后可以使用下面的方式进行引用 + +### 本地路径 + +可以通过本地相对路径来进行引用 + +```js + plugins: [ + ["./packages/docusaurus-plugin-docs-info", {}] + ], +``` + +### npm 包 + +也可以使用安装 npm 包的形式来引用 + +首先需要执行:`npm i ./packages/docusaurus-plugin-content-docs-ex`来安装包,然后再使用下方的配置进行配置 + +```json +plugins: [ + ["docusaurus-plugin-docs-info", {}] +], +``` + +## API + +你可以看官方文档了解更多:[Plugin Method References](https://www.docusaurus.cn/docs/api/plugin-methods),官方主要分为以下四个部分: + +- Lifecycle APIs:生命周期 API,当到达构建的某些阶段,docusaurus 会调用插件的生命周期 API,你可以在这些 API 中做一些事情,比如修改配置,添加一些内容等。 +- Extending infrastructure:扩展基础设施 +- I18n lifecycles:i18n 生命周期,一些 i18n 翻译相关的内容 +- Static methods:静态方法,主要是校验选项和校验主题两个方法 + +这里我简单的介绍一些常用的: + +### loadContent + +这个 API 可以用来预加载一些内容,比如读取文件,将某些数据预加载好,这样在后续的 API 中就可以使用这些内容了。 +例如官方的`docusaurus-plugin-content-docs`插件就是在此预加载好所有的文档数据,然后在`contentLoaded`中再添加路由的。 + +### contentLoaded + +这个 API 有两个参数,一个是 content,一个是 actions,content 就是 loadContent 返回的内容, +actions 里面包含了`addRoute`、`createData`、`setGlobalData`,3 个方法, +可以通过这三个方法添加数据和路由,这样就可以自定义一些页面了。 + +### getThemePath + +获取主题路径,在`contentLoaded`添加路由时,会要求填写一个组件:这里的组件填写的是一个字符串,你可以用`@site/`前缀表示主路径下的组件,也可以使用`@theme/`前缀表示`getThemePath`返回的路径下的组件。 + +```ts +export default function (context: LoadContext, options: any): Plugin { + const themePath = path.resolve(__dirname, "./theme"); + return { + name: "docusaurus-plugin-docs-info", + getThemePath() { + return themePath; + }, + }; +} +// 添加路由 +addRoute({ + path: "/timeline", + component: "@theme/Timeline", + modules: { + articles: pageData, + }, + exact: true, +}); +``` + +### injectHtmlTags + +在 body/head 中注入一些标签,比如添加一些统计代码,广告代码等。 + +### postBuild + +构建完成时调用,会有生成路由的信息,可以在这里做一些事情,比如生成 sitemap, rss 等。 + +### validateOptions + +校验选项,可以在这里校验一些配置是否正确。 + +## Hooks + +除了上面的 API 之外,还有一些 hooks 可以使用,很多都是官方内部的,文档中并没有说明,也是我翻阅源码看到的,这里我列举一些我用到的: + +### useGlobalData/PluginData + +用于获取在`contentLoaded`中设置的全局数据与插件数据,可以在组件中使用。 + +```ts +import useGlobalData, { + usePluginData, + useAllPluginInstancesData, +} from "@docusaurus/useGlobalData"; +``` + +### useDoc + +获取文档数据,可以获取到文档的标题、内容、路径等。 + +```ts +import { useDoc } from "@docusaurus/theme-common/internal"; +``` + +### useColorMode + +获取颜色模式,例如黑夜/白天模式。 + +```ts +import { useColorMode } from "@docusaurus/theme-common"; +``` + +## 自定义主题 + +如果你想要自定义你自己的主题,你可以使用`npm run swizzle --typescript`命令,这样可以让你替换插件的默认组件,来自定义你自己的样式。 + +官方文档:[Swizzling | Docusaurus](https://docusaurus.io/docs/swizzling) + +例如我就包装了原版的官方 docs 插件,然后自定义了主题,使我的博客 docs 稳定支持了创建时间与阅读时间。 + +## 使用包装器模式重制官方 docs 插件 + +虽然使用其它模式,例如再新写一个插件,也可以实现,但是使用包装器模式,可以更好的保持原有的插件的功能,只是在原有的基础上添加一些新的功能。 + +此处需要注意一个坑,如果使用包装器模式需要在`docusaurus.config.ts`中将原本的 docs 插件关闭掉,否则会产生冲突。 + +```ts + presets: [ + [ + "classic", + { + docs: false, + // docs: { + // routeBasePath: "/", + // sidebarPath: "./sidebars.ts", + // // Please change this to your repo. + // // Remove this to remove the "edit this page" links. + // editUrl: "https://github.com/codfrm/blog", + // showLastUpdateTime: true, + // }, + } satisfies Preset.Options, + ], + ], +``` + +引用时,在plugin单独引用: + +```ts + plugins: [ + [ + "docusaurus-plugin-content-docs-ex", + { + routeBasePath: "/", + sidebarPath: "./sidebars.ts", + // Please change this to your repo. + // Remove this to remove the "edit this page" links. + editUrl: "https://github.com/CodFrm/blog/edit/main", + showLastUpdateTime: true, + }, + ] + ] +``` + +然后就可以在插件中使用原有的 docs 插件,然后在原有的基础上添加一些新的功能。 + +```ts +export default async function pluginContentDocs( + context: LoadContext, + options: PluginOptions & { debug?: boolean } +): Promise> { + const ret = (await docsPlugin.call( + this, + context, + options + )) as Plugin; + const themePath = path.resolve(__dirname, "./theme"); + ret.getThemePath = () => { + return themePath; + }; + + const warpLoadContent = ret.loadContent; + ret.loadContent = async () => { + const ret = await warpLoadContent(); + // ....一些操作 + return ret; + } + + return ret; +} +``` + +更多详细的内容你可以看我的插件:[docusaurus-plugin-content-docs-ex](https://github.com/CodFrm/blog/tree/main/packages/docusaurus-plugin-content-docs-ex) \ No newline at end of file diff --git a/docs/dev/frontend/img/docusaurus插件开发.assets/image-20240324133547293.png b/docs/dev/frontend/img/docusaurus插件开发.assets/image-20240324133547293.png new file mode 100644 index 0000000000000000000000000000000000000000..29f0eda9f296f24794c2bf0ab28cc457be789e27 GIT binary patch literal 31583 zcmd42WmFtZ7d47I!QBQ(2n5#v!QI^g1b6q~Iyk{CxJ!`W?h=A)aCdjtuSuTw`+4ur zds%DN%rrgKr>d*#?6dc-Pz5=0R3t(q2nYyNNeK}p2nfg;;KhId2OQb%-!X)MK;bnP z7FLiH76vIe*qWGI8ACuwgeIuNYbf{QW@yDm3z$K|2|ls6M_cmtL`AW4h0dSv}|SJClRI(t!a=9>Iu)|RF0(0=ll|R z)9<-%pn+ZwnU&YI7Qzat(2C;bPW}96r) zRN_VO{e^AHUUv$y+%{94IlC}%C5lS?~U7FAu9;`ET)eXGM$B{ z2vBqA-8lYoSlAUCpy@zY41Ru)+FG?J50@AQa@i<)bu!X3u{d2g?YAp|SgcW%-S(07 zYMlFI3q>_;iXQBcVPsfD4D=GA!{|&bq93OO>hZl{_~#hX26=a>!o=NpaCiMzm0c+I zdJ$diY>Yl;ZL^7Vke`J7(6EhqVFwQaYVEnI9@n%h20h7Hy(NM(GqE|?@JLtq1^B*} z1~W!PsqT8BAsL?a=8&`6dy6g|Nnqi|xlx zz8>(nX7634!$%E%oY7bFu7M}0S{9lMAwI=k=RrquSn~p@djJ~uQrj%LhK9D-bMKZU0EMH-;`M%LA;Tgkr zcR-5v`-#TJ?7^+*vU*1*m6sUww9&XzC5W}ecLZz;! z$M^(Qr10s}#}_n+7EL&K_$bdEAx((c6FA$Kf|E3M=r-VoIl=6UPyDO&vyezU7=u8Y4a&i@L&@TC}5Lkm?PI8nUB1D0%X#BSX zlEN$)NL2o3iHI7Urz)H7lMLGHJ(cgQjlBL>lFIMssWBiP#3Y5;(JAeXIINz8vL4 zselLv^T{9HPga7$2(tiF3SkRO0`3M+p6cvVwNPS)a*NRB5VhdNh0gU5^t|i&*2BGy zXz>1w+1^)#VlU=ULMKlGGgw9719{o|Bl6nl^5`Z>viSIfh=jCws05{WuvD+qmQ-^> zB<&YP_kzpsrE+y3k7&w%o^z*jmU9;e);r9`)W`2Rf|WV0itE&K@ow?X@tOnX1H1!7 z#>U3V#$UJ0jQ!5Fx10wa;zw!tRi2A-iVaG-lq6MSis)45iXDWu>IR5DBv?pZ9)fLhe)m7>~G$GqJX2KXkH$rJP7t6Gn-SM~PnigxZfA+`rXQroIrsc*m3ch4}$uvvDPgYLpA8hW^O>$*9 z3hzuJbYl!*JYs~4PenGcthXLZe9`^VW>ajFe(by?@9xwJ(MsHk*~xCNAXR5+AxC;=#v}RyB=Q;qy(iCUa`DI#t3)+D9-hL`U4>7-Ef4n2|pS zohNmAGt70(bPZp|1pCN`vS+h9*S5?q#%XfZBlteur{;zmmT$Q9H5;<^fg@;0CwaDO z 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 QaPEqVf~I`AtXbgy0e^v*Z~y=R literal 0 HcmV?d00001 diff --git a/docs/dev/language/golang/code/io_test.go b/docs/dev/language/golang/code/io_test.go index 8ed3b2d..095c613 100644 --- a/docs/dev/language/golang/code/io_test.go +++ b/docs/dev/language/golang/code/io_test.go @@ -3,12 +3,41 @@ package code import ( "bufio" "bytes" + "compress/gzip" "crypto/md5" "crypto/sha1" "io" "testing" ) +type WrapReader struct { + len int + r io.Reader +} + +func (w *WrapReader) Read(p []byte) (int, error) { + n, err := w.r.Read(p) + w.len += n + return n, err +} + +func TestReader(t *testing.T) { + file := bytes.NewBuffer([]byte("hello world")) + wr := WrapReader{len: 0, r: file} + _, _ = io.Copy(io.Discard, &wr) + t.Logf("文件长度: %d\n", wr.len) +} + +func TestGzip(t *testing.T) { + file := bytes.NewBuffer([]byte("hello world")) + buf := bytes.NewBuffer(nil) + w := gzip.NewWriter(buf) + _, _ = io.Copy(w, file) + t.Logf("不Close文件长度: %d\n", buf.Len()) + w.Close() + t.Logf("压缩后文件长度: %d\n", buf.Len()) +} + func TestIOHash(t *testing.T) { file := bytes.NewBuffer([]byte("hello world")) md5 := md5.New() diff --git a/docs/dev/language/golang/go-io.md b/docs/dev/language/golang/go-io.md index 992510c..8f9508c 100644 --- a/docs/dev/language/golang/go-io.md +++ b/docs/dev/language/golang/go-io.md @@ -1,31 +1,71 @@ -# Golang IO相关操作 +# Golang IO 相关操作 -- io包提供了基础的io操作,几乎所有的io操作都是基于io.Reader和io.Writer接口的。 -- ioutil 包提供了一些方便的io操作函数,但是在go 1.16中已经被废弃,放进了io包。 -- bufio实现了缓冲io,可以提高io效率。 -- bytes 包中提供了Buffer类型,可以用来做io操作。 +- io 包提供了基础的 io 操作,几乎所有的 io 操作都是基于 io.Reader 和 io.Writer 接口的。 +- ~~ioutil 包提供了一些方便的 io 操作函数,但是在 go 1.16 中已经被废弃,放进了 io 包。~~ +- bufio 实现了缓冲 io,可以提高 io 效率。 +- bytes 包中提供了 Buffer 类型,可以用来做 io 操作。 ## 核心接口 -### Reader/Writer/Seeker +Go 中几乎所有的 io 操作都是围绕着 Reader/Writer/Seeker 这三个接口进行,这三个接口都是独立开来的,可以随机组合。 -Reader和Writer是io包中最重要的接口,它们定义了io操作的基本行为。像一些文件操作,网络操作等等,都是基于这两个接口的。 +Reader 和 Writer 是 io 包中最重要的接口,它们定义了 io 操作的基本行为。像一些文件操作,网络操作等等,都是基于这两个接口的。 -Seeker接口定义了Seek方法,可以在Reader/Writer中定位到指定的位置,文件操作中常用。 +Seeker 接口定义了 Seek 方法,可以在 Reader/Writer 中定位到指定的位置,文件操作中常用。 + +还有一个 Close 接口,用来关闭 io,也是经常使用,有这个接口的时候,我们可以使用 defer 来关闭,避免忘记,而且一般情况下都需要注意关闭,否则会产生内存泄漏之类的问题。 + +有很多操作也是需要执行完 Close 之后才能生效,例如 gzip 压缩,如果不 Close,可能会导致压缩文件不完整。 + +```go +func TestGzip(t *testing.T) { + file := bytes.NewBuffer([]byte("hello world")) + buf := bytes.NewBuffer(nil) + w := gzip.NewWriter(buf) + _, _ = io.Copy(w, file) + t.Logf("不Close文件长度: %d\n", buf.Len()) + w.Close() + t.Logf("压缩后文件长度: %d\n", buf.Len()) +} +``` + +### 基础使用 + +别看这三个接口简单,他们可以玩出很多花出来。例如对 Reader/Writer 进行包装,实现读取写入大小统计,来做流量统计时这很有用;标准库中也有很多方法,值得可以学习。 + +```go +type WrapReader struct { + len int + r io.Reader +} + +func (w *WrapReader) Read(p []byte) (int, error) { + n, err := w.r.Read(p) + w.len += n + return n, err +} + +func TestReader(t *testing.T) { + file := bytes.NewBuffer([]byte("hello world")) + wr := WrapReader{len: 0, r: file} + _, _ = io.Copy(io.Discard, &wr) + t.Logf("文件长度: %d\n", wr.len) +} +``` ## 常用函数 -下面我们来看一些常用的io操作函数,更多的函数可以查看官方文档,我只是列出一些我常用到的。 +下面我们来看一些常用的 io 操作函数,更多的函数可以查看官方文档,我只是列出一些我常用到的。 -- io.ReadeAll 读取所有数据,返回一个bytes -- io.MultiWriter/Reader 可以将多个Reader/Writer合并成一个 -- io.LimitReader 限制Reader的读取长度,读取n个长度后返回io.EOF -- io.TeeReader 读取数据的同时写入到另一个Writer中 +- io.ReadAll 读取所有数据,返回一个 bytes +- io.MultiWriter/Reader 可以将多个 Reader/Writer 合并成一个 +- io.LimitReader 限制 Reader 的读取长度,读取 n 个长度后返回 io.EOF +- io.TeeReader 读取数据的同时写入到另一个 Writer 中 - io.Pipe 创建同步内存管道 -- io.Copy/CopyN 复制Reader到Writer -- io.NopCloser 给一个io.Reader加上Close方法 -- bytes.NewBuffer 创建一个Buffer -- bufio.NewReader/Writer 创建一个缓冲Reader/Writer +- io.Copy/CopyN 复制 Reader 到 Writer +- io.NopCloser 给一个 io.Reader 加上 Close 方法 +- bytes.NewBuffer 创建一个 Buffer +- bufio.NewReader/Writer 创建一个缓冲 Reader/Writer ## 常见小坑 @@ -33,32 +73,32 @@ Seeker接口定义了Seek方法,可以在Reader/Writer中定位到指定的位 ### io.EOF -io.EOF是io包中定义的一个错误,表示读取到文件末尾。在读取文件时,当读取到文件末尾时,会返回io.EOF错误。 +io.EOF 是 io 包中定义的一个错误,表示读取到文件末尾。在读取文件时,当读取到文件末尾时,会返回 io.EOF 错误。 -请注意,当读取到文件末尾时,返回的数据可能不是nil,而是文件的最后一部分数据,需要注意处理。 +请注意,当读取到文件末尾时,返回的数据可能不是 nil,而是文件的最后一部分数据,需要注意处理。 ### 大文件的操作 -在处理大文件时,需要注意内存的使用,尽量使用io.Reader和io.Writer来处理文件,避免一次性读取整个文件到内存中。 +在处理大文件时,需要注意内存的使用,尽量使用 io.Reader 和 io.Writer 来处理文件,避免一次性读取整个文件到内存中。 ### bufio.Writer.Flush -在使用bufio.Writer时,需要注意Flush方法,因为bufio.Writer是带缓冲的,数据并不是实时写入到Writer中的,需要调用Flush方法来刷新缓冲区。 +在使用 bufio.Writer 时,需要注意 Flush 方法,因为 bufio.Writer 是带缓冲的,数据并不是实时写入到 Writer 中的,需要调用 Flush 方法来刷新缓冲区。 ### io.Pipe -io.Pipe是一个同步的内存管道,请注意在使用时,Reader与Writer必须在不同的goroutine中,且需要注意Reader与Writer的速度需要一致,否则会造成死锁或线程阻塞。 +io.Pipe 是一个同步的内存管道,请注意在使用时,Reader 与 Writer 必须在不同的 goroutine 中,且需要注意 Reader 与 Writer 的速度需要一致,否则会造成死锁或线程阻塞。 -并且需要使用Close方法来关闭Reader或Writer,否则会造成内存泄漏。 +并且需要使用 Close 方法来关闭 Reader 或 Writer,否则会造成内存泄漏。 ## 高级用例 -下面介绍一些高级用例,你也可以从中学习到一些高级的io操作技巧。 +下面介绍一些高级用例,你也可以从中学习到一些高级的 io 操作技巧。 -### 计算上传文件的hash +### 计算上传文件的 hash -下面的例子是计算上传文件的md5和sha1的hash值,我们使用io.TeeReader将文件内容同时写入到md5和sha1的hash计算器中。 -像我们在http上传文件时,可以在上传的同时计算文件的hash值,这样可以保证文件的完整性。同时也可以打开文件,再将文件的Writer放到io.Copy中,这样可以同时写入文件和计算hash值。 +下面的例子是计算上传文件的 md5 和 sha1 的 hash 值,我们使用 io.TeeReader 将文件内容同时写入到 md5 和 sha1 的 hash 计算器中。 +像我们在 http 上传文件时,可以在上传的同时计算文件的 hash 值,这样可以保证文件的完整性。同时也可以打开文件,再将文件的 Writer 放到 io.Copy 中,这样可以同时写入文件和计算 hash 值。 ```go func TestIOHash(t *testing.T) { @@ -74,7 +114,7 @@ func TestIOHash(t *testing.T) { ### 读取文件的部分内容 -下面的例子是读取文件的部分内容,我们使用io.LimitReader限制读取的长度,读取n个长度后返回io.EOF,这在解析文件头部信息时非常有用。 +下面的例子是读取文件的部分内容,我们使用 io.LimitReader 限制读取的长度,读取 n 个长度后返回 io.EOF,这在解析文件头部信息时非常有用。 ```go func TestLimitReader(t *testing.T) { @@ -87,9 +127,9 @@ func TestLimitReader(t *testing.T) { ### 大文件按行读取 -下面的例子是读取大文件的每一行,我们使用bufio.Reader来读取文件的每一行,这样可以避免一次性读取整个文件到内存中。 +下面的例子是读取大文件的每一行,我们使用 bufio.Reader 来读取文件的每一行,这样可以避免一次性读取整个文件到内存中。 -在读一些io比较慢的文件时(网络文件),也可以使用这种方式来读取文件。写文件也是类似的,可以使用bufio.Writer来写文件,提高磁盘io效率,避免频繁的io操作。 +在读一些 io 比较慢的文件时(网络文件),也可以使用这种方式来读取文件。写文件也是类似的,可以使用 bufio.Writer 来写文件,提高磁盘 io 效率,避免频繁的 io 操作。 ```go func TestBufIo(t *testing.T) { @@ -106,9 +146,9 @@ func TestBufIo(t *testing.T) { } ``` -### 使用Pipe将上传文件解密写入文件 +### 使用 Pipe 将上传文件解密写入文件 -有时候上传的文件是一个加密的,我们需要在上传的同时解密文件,然后同时写入到磁盘中,这时候可以使用io.Pipe来实现。 +有时候上传的文件是一个加密的,我们需要在上传的同时解密文件,然后同时写入到磁盘中,这时候可以使用 io.Pipe 来实现。 ```go diff --git a/docs/dev/language/golang/go-slice.md b/docs/dev/language/golang/go-slice.md index a9ef33b..7f01741 100644 --- a/docs/dev/language/golang/go-slice.md +++ b/docs/dev/language/golang/go-slice.md @@ -172,7 +172,7 @@ func ModifySlice(slice []int) { 在1.18之前,切片的扩容是原来的2倍,但是当容量超过1024时,每次容量变成原来的1.25倍,直到大于期望容量。在1.18后更换了新的机制: - +[src/runtime/slice.go#L267](https://github.com/golang/go/blob/27f41bb15391668fa8ba18561efe364bab9b8312/src/runtime/slice.go#L267) - 当新切片>旧切片\*2时,直接安装新切片容量计算 - 如果旧切片\<256,新切片容量为旧切片\*2 @@ -195,7 +195,7 @@ func TestCap(t *testing.T) { 至于实际的结果为什么没有和上述说的一样,可以看到,在`nextslicecap`计算出容量后续,还有对`newcap`的一系列操作,这是内存对齐的一系列计算。 - +[src/runtime/slice.go#L188](https://github.com/golang/go/blob/27f41bb15391668fa8ba18561efe364bab9b8312/src/runtime/slice.go#L188) 逻辑比较复杂,可以进入调试模式跟踪逻辑,这里就不多展开了 diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 2ae6b76..d67affc 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -4,8 +4,8 @@ import type * as Preset from "@docusaurus/preset-classic"; import docsToBlog from "./packages/docusaurus-plugin-docs-info/src"; const config: Config = { - title: "王一之", - tagline: "王一之的个人博客:分享知识,记录生活,认识朋友", + title: "一知", + tagline: "王一之的个人博客:分享知识,认识朋友", favicon: "img/favicon.ico", // Set the production url of your site here @@ -77,7 +77,7 @@ const config: Config = { // Replace with your project's social card image: "img/docusaurus-social-card.jpg", navbar: { - title: "王一之的博客", + title: "一知", logo: { alt: "Logo", src: "img/avatar.png", diff --git a/packages/docusaurus-plugin-content-docs-ex/src/index.ts b/packages/docusaurus-plugin-content-docs-ex/src/index.ts index 8fbc239..510ace7 100644 --- a/packages/docusaurus-plugin-content-docs-ex/src/index.ts +++ b/packages/docusaurus-plugin-content-docs-ex/src/index.ts @@ -36,11 +36,6 @@ export default async function pluginContentDocs( )) as Plugin; const isProd = process.env.NODE_ENV === "production"; - const themePath = path.resolve(__dirname, "./theme"); - ret.getThemePath = () => { - return themePath; - }; - const warpLoadContent = ret.loadContent; ret.loadContent = async () => { const ret = await warpLoadContent(); diff --git a/src/theme/Heading/index.tsx b/src/theme/Heading/index.tsx index 8f43205..983be35 100644 --- a/src/theme/Heading/index.tsx +++ b/src/theme/Heading/index.tsx @@ -9,12 +9,11 @@ export default function HeadingWrapper(props) { const syntheticTitle = useSyntheticTitle(); const doc = useDoc(); const detail = (doc.metadata as any).detail as Detail; - return ( <> {props.children} - {detail && !syntheticTitle && ( + {detail && !syntheticTitle && props.as.toString() === "h1" && doc.contentTitle==props.children && ( {children}