")])],-1),Cs=s("li",null,[s("p",null,"详情:"),s("p",null,"允许替换 DocSearch 按钮和弹窗内的默认文字。")],-1),bs=s("p",null,"参考:",-1),ms={href:"https://docsearch.algolia.com/docs/api/#translations",target:"_blank",rel:"noopener noreferrer"},Es=e(`类型: Record<string, DocsearchPluginOptions>
详情:
在不同 locales 下对该插件进行不同的配置。
该插件的所有其他选项都可以在 locale 中进行配置。
示例:
export default {
+ plugins: [
+ docsearchPlugin ({
+ appId: '<APP_ID>' ,
+ apiKey: '<API_KEY>' ,
+ indexName: '<INDEX_NAME>' ,
+ locales: {
+ '/' : {
+ placeholder: 'Search Documentation' ,
+ translations: {
+ button: {
+ buttonText: 'Search Documentation' ,
+ },
+ },
+ },
+ '/zh/' : {
+ placeholder: '搜索文档' ,
+ translations: {
+ button: {
+ buttonText: '搜索文档' ,
+ },
+ },
+ },
+ },
+ }),
+ ],
+}
+
`,3),_s=s("h3",{id:"indexbase",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#indexbase"},[s("span",null,"indexBase")])],-1),gs=s("li",null,[s("p",null,[n("类型: "),s("code",null,"string")])],-1),As=s("p",null,"详情:",-1),Bs=s("p",null,"搜索索引基础路径。",-1),fs=s("p",null,[n("如果你需要把你的站点部署到不同的域名上,你不需要把它们全都提交到 Docsearch 上来分别生成搜索索引。你可以选择其中一个域名作为 "),s("em",null,"索引域名"),n(" ,并且仅将 "),s("em",null,"索引域名"),n(" 提交到 DocSearch 上来爬去搜索索引。然后,你就可以在不同的部署域名下复用索引。")],-1),Fs=s("em",null,"索引域名",-1),xs=e(' ',3),ks={href:"https://docsearch.algolia.com/docs/styling",target:"_blank",rel:"noopener noreferrer"},Ss=e(`:root {
+ --docsearch-primary-color : rgb ( 84 , 104 , 255 );
+ --docsearch-text-color : rgb ( 28 , 30 , 33 );
+ --docsearch-spacing : 12px ;
+ --docsearch-icon-stroke-width : 1.4 ;
+ --docsearch-highlight-color : var ( --docsearch-primary-color );
+ --docsearch-muted-color : rgb ( 150 , 159 , 175 );
+ --docsearch-container-background : rgba ( 101 , 108 , 133 , 0.8 );
+ --docsearch-logo-color : rgba ( 84 , 104 , 255 );
+
+ /* modal */
+ --docsearch-modal-width : 560px ;
+ --docsearch-modal-height : 600px ;
+ --docsearch-modal-background : rgb ( 245 , 246 , 247 );
+ --docsearch-modal-shadow : inset 1px 1px 0 0 rgba ( 255 , 255 , 255 , 0.5 ), 0 3px
+ 8px 0 rgba ( 85 , 90 , 100 , 1 );
+
+ /* searchbox */
+ --docsearch-searchbox-height : 56px ;
+ --docsearch-searchbox-background : rgb ( 235 , 237 , 240 );
+ --docsearch-searchbox-focus-background : #fff ;
+ --docsearch-searchbox-shadow : inset 0 0 0 2px var ( --docsearch-primary-color );
+
+ /* hit */
+ --docsearch-hit-height : 56px ;
+ --docsearch-hit-color : rgb ( 68 , 73 , 80 );
+ --docsearch-hit-active-color : #fff ;
+ --docsearch-hit-background : #fff ;
+ --docsearch-hit-shadow : 0 1px 3px 0 rgb ( 212 , 217 , 225 );
+
+ /* key */
+ --docsearch-key-gradient : linear-gradient (
+ -225deg ,
+ rgb ( 213 , 219 , 228 ) 0% ,
+ rgb ( 248 , 248 , 248 ) 100%
+ );
+ --docsearch-key-shadow : inset 0 -2px 0 0 rgb ( 205 , 205 , 230 ), inset 0 0 1px 1px
+ #fff , 0 1px 2px 1px rgba ( 30 , 35 , 90 , 0.4 );
+
+ /* footer */
+ --docsearch-footer-height : 44px ;
+ --docsearch-footer-background : #fff ;
+ --docsearch-footer-shadow : 0 -1px 0 0 rgb ( 224 , 227 , 232 ), 0 -3px 6px 0 rgba ( 69 , 98 , 155 , 0.12 );
+}
+
提示
该组件主要用于主题开发。在大多数情况下你不需要直接使用该组件。
`,5);function Ps(Is,Ns){const r=c("NpmBadge"),a=c("ExternalLinkIcon"),p=c("RouteLink");return t(),D("div",null,[y,l(r,{package:"@vuepress/plugin-docsearch"}),s("p",null,[n("将 "),s("a",u,[n("Algolia DocSearch"),l(a)]),n(" 集成到 VuePress 中,为你的文档网站提供搜索功能。")]),h,s("p",null,[n("你需要 "),s("a",v,[n("提交你的网站 URL"),l(a)]),n(" 来加入 DocSearch 项目。当你的索引成功创建后, DocSearch 团队会将 "),C,n(" 和 "),b,n(" 发送到你的邮箱。接下来,你就可以配置该插件,在 VuePress 中启用 DocSearch 了。")]),s("p",null,[n("或者,你也可以 "),s("a",m,[n("运行你自己的爬虫"),l(a)]),n(" 来创建索引,然后使用你自己的 "),E,n(", "),_,n(" 和 "),g,n(" 来配置该插件。")]),A,s("div",B,[f,s("p",null,[n("如果你使用的不是默认主题,或者在使用 Docsearch 的时候遇到了任何问题,你也可以检查上述的爬虫配置示例,然后前往 "),s("a",F,[n("Algolia Crawler"),l(a)]),n(" 仓库,在你项目侧边栏中的 Editor 页面中修改你的配置。")])]),x,k,s("ul",null,[S,P,I,s("li",null,[N,s("ul",null,[s("li",null,[s("a",R,[n("DocSearch > Options > apiKey"),l(a)])])])])]),w,s("ul",null,[O,T,U,s("li",null,[z,s("ul",null,[s("li",null,[s("a",L,[n("DocSearch > Options > indexName"),l(a)])])])])]),Y,s("ul",null,[K,j,q,s("li",null,[M,s("ul",null,[s("li",null,[s("a",V,[n("DocSearch > Options > appId"),l(a)])])])])]),W,s("ul",null,[X,H,s("li",null,[Q,s("ul",null,[s("li",null,[s("a",J,[n("DocSearch > Options > searchParameters"),l(a)])]),s("li",null,[s("a",$,[n("Algolia > Search API Parameters"),l(a)])])])])]),G,s("ul",null,[Z,ss,ns,s("li",null,[ls,s("ul",null,[s("li",null,[s("a",as,[n("DocSearch > Options > placeholder"),l(a)])])])])]),es,s("ul",null,[ps,os,cs,s("li",null,[rs,s("ul",null,[s("li",null,[s("a",is,[n("DocSearch > Options > disableUserPersonalization"),l(a)])])])])]),ts,s("ul",null,[Ds,ds,s("li",null,[ys,s("ul",null,[s("li",null,[s("a",us,[n("DocSearch > Options > initialQuery"),l(a)])])])])]),hs,s("ul",null,[vs,Cs,s("li",null,[bs,s("ul",null,[s("li",null,[s("a",ms,[n("DocSearch > Options > translations"),l(a)])])])])]),Es,s("ul",null,[s("li",null,[n("参考: "),s("ul",null,[s("li",null,[l(p,{to:"/guide/i18n.html"},{default:o(()=>[n("指南 > 多语言支持")]),_:1})])])])]),_s,s("ul",null,[gs,s("li",null,[s("p",null,[n("默认值: "),l(p,{to:"/zh/config.html#base"},{default:o(()=>[n("base")]),_:1})])]),s("li",null,[As,Bs,fs,s("p",null,[n("如果你不同部署域名下的 "),l(p,{to:"/zh/config.html#base"},{default:o(()=>[n("base")]),_:1}),n(" 是不一样的,你就需要将这个配置设置成 "),Fs,n(" 的 "),l(p,{to:"/zh/config.html#base"},{default:o(()=>[n("base")]),_:1}),n(" ,这样其他的部署域名就可以正确复用索引了。")])])]),xs,s("p",null,[n("你可以通过 "),s("a",ks,[n("@docsearch/css"),l(a)]),n(" 提供的 CSS 变量来自定义样式:")]),Ss])}const ws=i(d,[["render",Ps],["__file","docsearch.html.vue"]]),Os=JSON.parse('{"path":"/zh/plugins/docsearch.html","title":"docsearch","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"获取搜索索引","slug":"获取搜索索引","link":"#获取搜索索引","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"apiKey","slug":"apikey","link":"#apikey","children":[]},{"level":3,"title":"indexName","slug":"indexname","link":"#indexname","children":[]},{"level":3,"title":"appId","slug":"appid","link":"#appid","children":[]},{"level":3,"title":"searchParameters","slug":"searchparameters","link":"#searchparameters","children":[]},{"level":3,"title":"placeholder","slug":"placeholder","link":"#placeholder","children":[]},{"level":3,"title":"disableUserPersonalization","slug":"disableuserpersonalization","link":"#disableuserpersonalization","children":[]},{"level":3,"title":"initialQuery","slug":"initialquery","link":"#initialquery","children":[]},{"level":3,"title":"translations","slug":"translations","link":"#translations","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]},{"level":3,"title":"indexBase","slug":"indexbase","link":"#indexbase","children":[]},{"level":3,"title":"injectStyles","slug":"injectstyles","link":"#injectstyles","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]},{"level":2,"title":"组件","slug":"组件","link":"#组件","children":[{"level":3,"title":"Docsearch","slug":"docsearch-1","link":"#docsearch-1","children":[]}]}],"git":{"updatedTime":1706627619000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/docsearch.md"}');export{ws as comp,Os as data};
diff --git a/assets/extending-a-theme-01-Cqco1E4_.js b/assets/extending-a-theme-01-Cqco1E4_.js
new file mode 100644
index 0000000000..0372478e41
--- /dev/null
+++ b/assets/extending-a-theme-01-Cqco1E4_.js
@@ -0,0 +1 @@
+const e="/ecosystem/images/cookbook/extending-a-theme-01.png";export{e as _};
diff --git a/assets/extending.html-BhjvhAeX.js b/assets/extending.html-BhjvhAeX.js
new file mode 100644
index 0000000000..f23518ae7a
--- /dev/null
+++ b/assets/extending.html-BhjvhAeX.js
@@ -0,0 +1,65 @@
+import{_ as p}from"./extending-a-theme-01-Cqco1E4_.js";import{_ as o,r as t,o as c,c as r,b as n,d as s,a as l,e}from"./app-BcH8wZQx.js";const i={},D=e(`VuePress 默认主题有着大量的用户,因此我们对它进行了一些便于继承的设计,以便用户轻松进行定制化。
默认主题的 Layout
布局提供了一些插槽:
navbar
navbar-before
navbar-after
sidebar
sidebar-top
sidebar-bottom
page
page-top
page-bottom
page-content-top
page-content-bottom
在它们的帮助下,你可以很容易地添加或替换内容。下面通过一个示例来介绍一下如何使用布局插槽来继承默认主题。
首先,创建一个客户端配置文件 .vuepress/client.ts
:
import { defineClientConfig } from 'vuepress/client'
+import Layout from './layouts/Layout.vue'
+
+export default defineClientConfig ({
+ layouts: {
+ Layout ,
+ },
+})
+
接下来,创建 .vuepress/layouts/Layout.vue
,并使用由默认主题的 Layout
布局提供的插槽:
< script setup >
+import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
+</ script >
+
+< template >
+ < ParentLayout >
+ < template # page-bottom >
+ < div class = "my-footer" > This is my custom page footer </ div >
+ </ template >
+ </ ParentLayout >
+</ template >
+
+< style lang = "css" >
+.my-footer {
+ text-align : center ;
+}
+</ style >
+
此时默认的 Layout
布局已经被你的本地布局覆盖,将会在除了首页外的所有页面添加一个自定义的页脚:
布局插槽十分实用,但有时候你可能会觉得它不够灵活。默认主题同样提供了替换单个组件的能力。
',14),d={href:"https://github.com/vuepress/ecosystem/tree/main/themes/theme-default/src/client/components",target:"_blank",rel:"noopener noreferrer"},y=n("code",null,"@theme",-1),C={href:"https://v2.vuepress.vuejs.org/zh/reference/plugin-api.html#alias",target:"_blank",rel:"noopener noreferrer"},v=n("code",null,"HomeFooter.vue",-1),m=n("code",null,"@theme/HomeFooter.vue",-1),u=e(`接下来,如果你想要替换 HomeFooter.vue
组件,只需要在配置文件 .vuepress/config.ts
中覆盖这个别名即可:
import { defaultTheme } from '@vuepress/theme-default'
+import { getDirname , path } from 'vuepress/utils'
+import { defineUserConfig } from 'vuepress'
+
+const __dirname = getDirname ( import . meta . url )
+
+export default defineUserConfig ({
+ theme: defaultTheme (),
+ alias: {
+ '@theme/HomeFooter.vue' : path . resolve (
+ __dirname ,
+ './components/MyHomeFooter.vue' ,
+ ),
+ },
+})
+
除了在 .vuepress/config.ts
和 .vuepress/client.ts
中直接扩展默认主题以外,你可以通过继承默认主题来开发一个你自己的主题:
import { defaultTheme , type DefaultThemeOptions } from '@vuepress/theme-default'
+import type { Theme } from 'vuepress/core'
+import { getDirname , path } from 'vuepress/utils'
+
+const __dirname = getDirname ( import . meta . url )
+
+export const childTheme = ( options : DefaultThemeOptions ): Theme => {
+ return {
+ name: 'vuepress-theme-child' ,
+ extends: defaultTheme ( options ),
+
+ // 在子主题的客户端配置文件中覆盖布局
+ // 注意,你在发布到 NPM 之前会将 TS 构建为 JS ,因此这里需要设置为 JS 文件的路径
+ clientConfigFile: path . resolve ( __dirname , './client.js' ),
+
+ // 覆盖组件别名
+ alias: {
+ '@theme/HomeFooter.vue' : path . resolve (
+ __dirname ,
+ './components/MyHomeFooter.vue' ,
+ ),
+ },
+ }
+}
+
`,5);function b(h,E){const a=t("ExternalLinkIcon");return c(),r("div",null,[D,n("p",null,[s("默认主题将所有 "),n("a",d,[s("非全局的组件"),l(a)]),s(" 都注册了一个带 "),y,s(" 前缀的 "),n("a",C,[s("alias"),l(a)]),s(" 。例如,"),v,s(" 的别名是 "),m,s(" 。")]),u])}const F=o(i,[["render",b],["__file","extending.html.vue"]]),_=JSON.parse('{"path":"/zh/themes/default/extending.html","title":"继承","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"布局插槽","slug":"布局插槽","link":"#布局插槽","children":[]},{"level":2,"title":"组件替换","slug":"组件替换","link":"#组件替换","children":[]},{"level":2,"title":"开发一个子主题","slug":"开发一个子主题","link":"#开发一个子主题","children":[]}],"git":{"updatedTime":1706625181000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/themes/default/extending.md"}');export{F as comp,_ as data};
diff --git a/assets/extending.html-dM0a4Pnp.js b/assets/extending.html-dM0a4Pnp.js
new file mode 100644
index 0000000000..40e280a80f
--- /dev/null
+++ b/assets/extending.html-dM0a4Pnp.js
@@ -0,0 +1,66 @@
+import{_ as o}from"./extending-a-theme-01-Cqco1E4_.js";import{_ as p,r as t,o as c,c as i,b as n,d as s,a,e as l}from"./app-BcH8wZQx.js";const r={},d=l(`VuePress default theme is widely used by users, so it is designed to be extendable, allowing users to make their own customization with ease.
Default theme's Layout
provides some slots:
navbar
navbar-before
navbar-after
sidebar
sidebar-top
sidebar-bottom
page
page-top
page-bottom
page-content-top
page-content-bottom
With the help of them, you can add or replace content easily. Here comes an example to introduce how to extend default theme with layout slots.
Firstly, create a client config file .vuepress/client.ts
:
import { defineClientConfig } from 'vuepress/client'
+import Layout from './layouts/Layout.vue'
+
+export default defineClientConfig ({
+ layouts: {
+ Layout ,
+ },
+})
+
Next, create the .vuepress/layouts/Layout.vue
, and make use of the slots that provided by the Layout
of default theme:
< script setup >
+import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
+</ script >
+
+< template >
+ < ParentLayout >
+ < template # page-bottom >
+ < div class = "my-footer" > This is my custom page footer </ div >
+ </ template >
+ </ ParentLayout >
+</ template >
+
+< style lang = "css" >
+.my-footer {
+ text-align : center ;
+}
+</ style >
+
Then the default Layout
layout has been overridden by your own local layout, which will add a custom footer to every normal pages in default theme (excluding homepage):
The layout slots are useful, but sometimes you might find it's not flexible enough. Default theme also provides the ability to replace a single component.
',14),D={href:"https://v2.vuepress.vuejs.org/plugin-api.html#alias",target:"_blank",rel:"noopener noreferrer"},y={href:"https://github.com/vuepress/ecosystem/tree/main/themes/theme-default/src/client/components",target:"_blank",rel:"noopener noreferrer"},m=n("code",null,"@theme",-1),u=n("code",null,"HomeFooter.vue",-1),v=n("code",null,"@theme/HomeFooter.vue",-1),C=l(`Then, if you want to replace the HomeFooter.vue
component, just override the alias in your config file .vuepress/config.ts
:
import { defaultTheme } from '@vuepress/theme-default'
+import { getDirname , path } from 'vuepress/utils'
+import { defineUserConfig } from 'vuepress'
+
+const __dirname = getDirname ( import . meta . url )
+
+export default defineUserConfig ({
+ theme: defaultTheme (),
+ alias: {
+ '@theme/HomeFooter.vue' : path . resolve (
+ __dirname ,
+ './components/MyHomeFooter.vue' ,
+ ),
+ },
+})
+
Instead of extending the default theme directly in .vuepress/config.ts
and .vuepress/client.ts
, you can also develop your own theme extending the default theme:
import { defaultTheme , type DefaultThemeOptions } from '@vuepress/theme-default'
+import type { Theme } from 'vuepress/core'
+import { getDirname , path } from 'vuepress/utils'
+
+const __dirname = getDirname ( import . meta . url )
+
+export const childTheme = ( options : DefaultThemeOptions ): Theme => {
+ return {
+ name: 'vuepress-theme-child' ,
+ extends: defaultTheme ( options ),
+
+ // override layouts in child theme's client config file
+ // notice that you would build ts to js before publishing to npm,
+ // so this should be the path to the js file
+ clientConfigFile: path . resolve ( __dirname , './client.js' ),
+
+ // override component alias
+ alias: {
+ '@theme/HomeFooter.vue' : path . resolve (
+ __dirname ,
+ './components/MyHomeFooter.vue' ,
+ ),
+ },
+ }
+}
+
`,5);function h(b,g){const e=t("ExternalLinkIcon");return c(),i("div",null,[d,n("p",null,[s("Default theme has registered "),n("a",D,[s("alias"),a(e)]),s(" for every "),n("a",y,[s("non-global components"),a(e)]),s(" with a "),m,s(" prefix. For example, the alias of "),u,s(" is "),v,s(".")]),C])}const F=p(r,[["render",h],["__file","extending.html.vue"]]),_=JSON.parse('{"path":"/themes/default/extending.html","title":"Extending","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Layout Slots","slug":"layout-slots","link":"#layout-slots","children":[]},{"level":2,"title":"Components Replacement","slug":"components-replacement","link":"#components-replacement","children":[]},{"level":2,"title":"Developing a Child Theme","slug":"developing-a-child-theme","link":"#developing-a-child-theme","children":[]}],"git":{"updatedTime":1706625181000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"themes/default/extending.md"}');export{F as comp,_ as data};
diff --git a/assets/external-link-icon.html-5DQfPplT.js b/assets/external-link-icon.html-5DQfPplT.js
new file mode 100644
index 0000000000..1cab802904
--- /dev/null
+++ b/assets/external-link-icon.html-5DQfPplT.js
@@ -0,0 +1,28 @@
+import{_ as c,r as s,o as t,c as r,a as l,b as n,d as a,w as d,e}from"./app-BcH8wZQx.js";const D={},u=n("h1",{id:"external-link-icon",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#external-link-icon"},[n("span",null,"external-link-icon")])],-1),v=e(`该插件已经集成到默认主题中。
npm i -D @vuepress/plugin-external-link-icon@next
+
import { externalLinkIconPlugin } from '@vuepress/plugin-external-link-icon'
+
+export default {
+ plugins: [
+ externalLinkIconPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
export default {
+ plugins: [
+ externalLinkIconPlugin ({
+ locales: {
+ '/' : {
+ openInNewWindow: 'open in new window' ,
+ },
+ '/zh/' : {
+ openInNewWindow: '在新窗口打开' ,
+ },
+ },
+ }),
+ ],
+}
+
`,8),h=e(`类型: boolean
详情:
是否在当前页面的外部链接的后面添加外部链接图标。
你可以通过 CSS 变量来自定义外部链接图标的样式:
:root {
+ --external-link-icon-color : #aaa ;
+}
+
提示
该组件主要用于主题开发。在大多数情况下你不需要直接使用该组件。
`,10);function m(y,k){const i=s("NpmBadge"),o=s("ExternalLinkIcon"),p=s("RouteLink");return t(),r("div",null,[u,l(i,{package:"@vuepress/plugin-external-link-icon"}),n("p",null,[a("该插件会为你 Markdown 内容中的外部链接添加一个图标,即 "),l(o)]),v,n("ul",null,[n("li",null,[a("参考: "),n("ul",null,[n("li",null,[l(p,{to:"/guide/i18n.html"},{default:d(()=>[a("指南 > 多语言支持")]),_:1})])])])]),h])}const x=c(D,[["render",m],["__file","external-link-icon.html.vue"]]),C=JSON.parse('{"path":"/zh/plugins/external-link-icon.html","title":"external-link-icon","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"externalLinkIcon","slug":"externallinkicon","link":"#externallinkicon","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]},{"level":2,"title":"组件","slug":"组件","link":"#组件","children":[{"level":3,"title":"ExternalLinkIcon","slug":"externallinkicon-1","link":"#externallinkicon-1","children":[]}]}],"git":{"updatedTime":1707223547000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/external-link-icon.md"}');export{x as comp,C as data};
diff --git a/assets/external-link-icon.html-C61esLvb.js b/assets/external-link-icon.html-C61esLvb.js
new file mode 100644
index 0000000000..450f45617e
--- /dev/null
+++ b/assets/external-link-icon.html-C61esLvb.js
@@ -0,0 +1,28 @@
+import{_ as p,r as s,o as c,c as r,a as e,b as n,d as l,w as d,e as a}from"./app-BcH8wZQx.js";const u={},D=n("h1",{id:"external-link-icon",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#external-link-icon"},[n("span",null,"external-link-icon")])],-1),h=a(`This plugin has been integrated into the default theme.
npm i -D @vuepress/plugin-external-link-icon@next
+
import { externalLinkIconPlugin } from '@vuepress/plugin-external-link-icon'
+
+export default {
+ plugins: [
+ externalLinkIconPlugin ({
+ // options
+ }),
+ ],
+}
+
Type: Record<string, { openInNewWindow: string }>
Details:
The a11y text of the external link icon in different locales.
If this option is not specified, it will fallback to default text.
Example:
export default {
+ plugins: [
+ externalLinkIconPlugin ({
+ locales: {
+ '/' : {
+ openInNewWindow: 'open in new window' ,
+ },
+ '/zh/' : {
+ openInNewWindow: '在新窗口打开' ,
+ },
+ },
+ }),
+ ],
+}
+
`,8),m=a(`You can customize the style of the external link icon via CSS variables:
:root {
+ --external-link-icon-color : #aaa ;
+}
+
TIP
This component is mainly used for theme development. You don't need to use it directly in most cases.
`,10);function v(y,k){const i=s("NpmBadge"),o=s("ExternalLinkIcon"),t=s("RouteLink");return c(),r("div",null,[D,e(i,{package:"@vuepress/plugin-external-link-icon"}),n("p",null,[l("This plugin will add an icon to the external link in your markdown content, i.e. "),e(o)]),h,n("ul",null,[n("li",null,[l("Also see: "),n("ul",null,[n("li",null,[e(t,{to:"/guide/i18n.html"},{default:d(()=>[l("Guide > I18n")]),_:1})])])])]),m])}const b=p(u,[["render",v],["__file","external-link-icon.html.vue"]]),g=JSON.parse('{"path":"/plugins/external-link-icon.html","title":"external-link-icon","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"externalLinkIcon","slug":"externallinkicon","link":"#externallinkicon","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]},{"level":2,"title":"Components","slug":"components","link":"#components","children":[{"level":3,"title":"ExternalLinkIcon","slug":"externallinkicon-1","link":"#externallinkicon-1","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/external-link-icon.md"}');export{b as comp,g as data};
diff --git a/assets/frontmatter.html-B4EhEZLV.js b/assets/frontmatter.html-B4EhEZLV.js
new file mode 100644
index 0000000000..fc797c0c17
--- /dev/null
+++ b/assets/frontmatter.html-B4EhEZLV.js
@@ -0,0 +1 @@
+import{_ as l,r as n,o as r,c as o,b as e,d as t,a as s,w as p,e as a}from"./app-BcH8wZQx.js";const c={},m=a(' ',4),d=e("li",null,[e("p",null,[t("Type: "),e("code",null,'"always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never"')])],-1),h=e("li",null,[e("p",null,[t("Default: "),e("code",null,'"daily"')])],-1),u=e("p",null,"Details:",-1),f=a(' ',2);function _(g,y){const i=n("RouteLink");return r(),o("div",null,[m,e("ul",null,[d,h,e("li",null,[u,e("p",null,[t("Page default update frequency. This will override "),s(i,{to:"/plugins/sitemap/config.html#changefreq"},{default:p(()=>[t("changefreq")]),_:1}),t(" in Plugin Options.")])])]),f])}const k=l(c,[["render",_],["__file","frontmatter.html.vue"]]),q=JSON.parse('{"path":"/plugins/sitemap/frontmatter.html","title":"Frontmatter","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"sitemap","slug":"sitemap","link":"#sitemap","children":[{"level":3,"title":"sitemap.changefreq","slug":"sitemap-changefreq","link":"#sitemap-changefreq","children":[]},{"level":3,"title":"sitemap.priority","slug":"sitemap-priority","link":"#sitemap-priority","children":[]}]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"plugins/sitemap/frontmatter.md"}');export{k as comp,q as data};
diff --git a/assets/frontmatter.html-BagSk6YQ.js b/assets/frontmatter.html-BagSk6YQ.js
new file mode 100644
index 0000000000..572cba1078
--- /dev/null
+++ b/assets/frontmatter.html-BagSk6YQ.js
@@ -0,0 +1,51 @@
+import{_ as s,o as n,c as e,e as a}from"./app-BcH8wZQx.js";const l={},i=a(`你可以通过配置每个页面的 Frontmatter,来对每个 Feed 项目生成进行单独的控制。
默认情况下,所有文章均会被添加至 feed 流。如果你想在 feed 中移除特定页面,你可以在 frontmatter 中设置 feed: false
。
由 VuePress 自动生成,默认为页面的 h1 内容
页面描述
页面的发布日期
该页面是否是文章
如果此项设置为 false
,则该页不会包含在最终的 feed 中。
页面版权信息
页面的封面/分享图,需为完整链接或绝对链接。
Feed 项目的标题
Feed 项目的描述
Feed 项目的内容
类型:FeedAuthor[] | FeedAuthor
Feed 项目的作者
FeedAuthor 格式 interface FeedAuthor {
+ /**
+ * 作者名字
+ */
+ name ?: string
+
+ /**
+ * 作者邮件
+ */
+ email ?: string
+
+ /**
+ * 作者网站
+ *
+ * @description json format only
+ */
+ url ?: string
+
+ /**
+ * 作者头像
+ *
+ * @description json format only
+ */
+ avatar ?: string
+}
+
类型:FeedContributor[] | FeedContributor
Feed 项目的贡献者
FeedContributor 格式 interface FeedContributor {
+ /**
+ * 作者名字
+ */
+ name ?: string
+
+ /**
+ * 作者邮件
+ */
+ email ?: string
+
+ /**
+ * 作者网站
+ *
+ * @description json format only
+ */
+ url ?: string
+
+ /**
+ * 作者头像
+ *
+ * @description json format only
+ */
+ avatar ?: string
+}
+
Feed 项目的标识符,用于标识 Feed 项目。
你应该确保每个 Feed 项目有全局唯一的 guid。
`,46),t=[i];function r(p,c){return n(),e("div",null,t)}const d=s(l,[["render",r],["__file","frontmatter.html.vue"]]),u=JSON.parse('{"path":"/zh/plugins/feed/frontmatter.html","title":"Frontmatter 配置","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"添加与移除","slug":"添加与移除","link":"#添加与移除","children":[]},{"level":2,"title":"读取的 Frontmatter 信息","slug":"读取的-frontmatter-信息","link":"#读取的-frontmatter-信息","children":[{"level":3,"title":"title","slug":"title","link":"#title","children":[]},{"level":3,"title":"description","slug":"description","link":"#description","children":[]},{"level":3,"title":"date","slug":"date","link":"#date","children":[]},{"level":3,"title":"article","slug":"article","link":"#article","children":[]},{"level":3,"title":"copyright","slug":"copyright","link":"#copyright","children":[]},{"level":3,"title":"cover / image / banner","slug":"cover-image-banner","link":"#cover-image-banner","children":[]}]},{"level":2,"title":"Frontmatter 选项","slug":"frontmatter-选项","link":"#frontmatter-选项","children":[{"level":3,"title":"feed.title","slug":"feed-title","link":"#feed-title","children":[]},{"level":3,"title":"feed.description","slug":"feed-description","link":"#feed-description","children":[]},{"level":3,"title":"feed.content","slug":"feed-content","link":"#feed-content","children":[]},{"level":3,"title":"feed.author","slug":"feed-author","link":"#feed-author","children":[]},{"level":3,"title":"feed.contributor","slug":"feed-contributor","link":"#feed-contributor","children":[]},{"level":3,"title":"feed.guid","slug":"feed-guid","link":"#feed-guid","children":[]}]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/feed/frontmatter.md"}');export{d as comp,u as data};
diff --git a/assets/frontmatter.html-BfVSOut1.js b/assets/frontmatter.html-BfVSOut1.js
new file mode 100644
index 0000000000..cb79d2e984
--- /dev/null
+++ b/assets/frontmatter.html-BfVSOut1.js
@@ -0,0 +1,60 @@
+import{_ as r,r as o,o as c,c as d,a as s,b as l,d as n,w as a,e as i}from"./app-BcH8wZQx.js";const u={},h=l("h1",{id:"frontmatter",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#frontmatter"},[l("span",null,"Frontmatter")])],-1),D=l("h2",{id:"所有页面",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#所有页面"},[l("span",null,"所有页面")])],-1),v=l("p",null,"本章节中的 Frontmatter 会在所有类型的页面中生效。",-1),m=l("h3",{id:"externallinkicon",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#externallinkicon"},[l("span",null,"externalLinkIcon")])],-1),b=l("li",null,[l("p",null,[n("类型: "),l("code",null,"boolean")])],-1),_=l("p",null,"详情:",-1),g=l("p",null,"参考:",-1),y=l("h3",{id:"navbar",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#navbar"},[l("span",null,"navbar")])],-1),k=l("li",null,[l("p",null,[n("类型: "),l("code",null,"boolean")])],-1),f=l("li",null,[l("p",null,"详情:"),l("p",null,"是否在当前页面展示导航栏。"),l("p",null,"如果你在主题配置中禁用了导航栏,那么该 Frontmatter 将不会生效。")],-1),x=l("p",null,"参考:",-1),C=i(`类型: string
详情:
为当前页面添加额外的类名。
示例:
---
+pageClass : custom-page-class
+---
+
然后你可以在 .vuepress/styles/index.scss
文件中为这个页面添加自定义样式:
.theme-container.custom-page-class {
+ /* 页面样式 */
+}
+
`,5),E=i(`本章节中的 Frontmatter 只会在首页中生效。
`,5),L=i(`类型: string
详情:
首页图片的 URL 。
示例:
---
+# Public 文件路径
+heroImage : /images/hero.png
+# URL
+heroImage : https://vuejs.org/images/logo.png
+---
+
`,3),A=l("p",null,"参考:",-1),z={href:"https://v2.vuepress.vuejs.org/zh/guide/assets.html#public-%E6%96%87%E4%BB%B6",target:"_blank",rel:"noopener noreferrer"},B=l("h3",{id:"heroimagedark",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#heroimagedark"},[l("span",null,"heroImageDark")])],-1),F=l("li",null,[l("p",null,[n("类型: "),l("code",null,"string")])],-1),N=l("li",null,[l("p",null,"详情:"),l("p",null,"在夜间模式中使用的首页图片的 URL 。"),l("p",null,"如果你想在夜间模式中使用不同的首页图片,就可以使用该配置项。")],-1),I=l("p",null,"参考:",-1),w=l("li",null,[l("a",{href:"#heroimage"},"默认主题 > Frontmatter > heroImage")],-1),P=i('类型: string
详情:
首页图片的 alt
属性。
如果不设置,则默认使用 heroText 。
',3),S=l("li",null,[l("p",null,[n("类型: "),l("code",null,"number")])],-1),H=l("li",null,[l("p",null,[n("默认值: "),l("code",null,"280")])],-1),V=l("p",null,"详情:",-1),T=l("p",null,[n("首页图片 "),l("code",null," "),n(" 标签的 "),l("code",null,"height"),n(" 属性。")],-1),M=l("p",null,"当你的首页图片高度小于默认值时,你可能需要减小该属性。",-1),R={href:"https://web.dev/cls/",target:"_blank",rel:"noopener noreferrer"},U=l("h3",{id:"herotext",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#herotext"},[l("span",null,"heroText")])],-1),j=l("li",null,[l("p",null,[n("类型: "),l("code",null,"string | null")])],-1),G=l("p",null,"详情:",-1),O=l("p",null,"首页的大标题。",-1),J={href:"https://v2.vuepress.vuejs.org/zh/reference/config.html#title",target:"_blank",rel:"noopener noreferrer"},q=l("p",null,[n("设置为 "),l("code",null,"null"),n(" 来禁用首页大标题。")],-1),K=l("h3",{id:"tagline",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#tagline"},[l("span",null,"tagline")])],-1),Q=l("li",null,[l("p",null,[n("类型: "),l("code",null,"string | null")])],-1),W=l("p",null,"详情:",-1),X=l("p",null,"首页的标语。",-1),Y={href:"https://v2.vuepress.vuejs.org/zh/reference/config.html#description",target:"_blank",rel:"noopener noreferrer"},Z=l("p",null,[n("设置为 "),l("code",null,"null"),n(" 来禁用首页标语。")],-1),$=i(`Array <{
+ text : string
+ link : string
+ type ?: 'primary' | 'secondary'
+}>
+
---
+actions :
+ - text : 快速上手
+ link : /zh/guide/getting-started.html
+ type : primary
+ - text : 项目简介
+ link : /zh/guide/introduction.html
+ type : secondary
+---
+
Array <{
+ title : string
+ details : string
+}>
+
---
+features :
+ - title : 简洁至上
+ details : 以 Markdown 为中心的项目结构,以最少的配置帮助你专注于写作。
+ - title : Vue 驱动
+ details : 享受 Vue 的开发体验,可以在 Markdown 中使用 Vue 组件,又可以使用 Vue 来开发自定义主题。
+ - title : 高性能
+ details : VuePress 会为每个页面预渲染生成静态的 HTML,同时,每个页面被加载的时候,将作为 SPA 运行。
+---
+
本章节中的 Frontmatter 只会在普通页面中生效。
`,17),ll=l("li",null,[l("p",null,[n("类型: "),l("code",null,"boolean")])],-1),nl=l("li",null,[l("p",null,"详情:"),l("p",null,[n("是否在本页面中启用 "),l("em",null,"编辑此页"),n(" 链接。")])],-1),sl=l("p",null,"参考:",-1),el=l("h3",{id:"editlinkpattern",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#editlinkpattern"},[l("span",null,"editLinkPattern")])],-1),al=l("li",null,[l("p",null,[n("类型: "),l("code",null,"string")])],-1),il=l("li",null,[l("p",null,"详情:"),l("p",null,[n("本页面中 "),l("em",null,"编辑此页"),n(" 链接的 Pattern 。")])],-1),tl=l("p",null,"参考:",-1),ol=l("h3",{id:"lastupdated",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#lastupdated"},[l("span",null,"lastUpdated")])],-1),pl=l("li",null,[l("p",null,[n("类型: "),l("code",null,"boolean")])],-1),rl=l("li",null,[l("p",null,"详情:"),l("p",null,[n("是否在本页面中启用 "),l("em",null,"最近更新时间戳"),n(" 。")])],-1),cl=l("p",null,"参考:",-1),dl=l("h3",{id:"contributors",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#contributors"},[l("span",null,"contributors")])],-1),ul=l("li",null,[l("p",null,[n("类型: "),l("code",null,"boolean")])],-1),hl=l("li",null,[l("p",null,"详情:"),l("p",null,[n("是否在本页面中启用 "),l("em",null,"贡献者列表"),n(" 。")])],-1),Dl=l("p",null,"参考:",-1),vl=l("h3",{id:"sidebar",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#sidebar"},[l("span",null,"sidebar")])],-1),ml=l("li",null,[l("p",null,[n("类型: "),l("code",null,"false | 'auto' | SidebarConfigArray | SidebarConfigObject")])],-1),bl=l("li",null,[l("p",null,"详情:"),l("p",null,"配置本页面的侧边栏。")],-1),_l=l("p",null,"参考:",-1),gl=l("h3",{id:"sidebardepth",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#sidebardepth"},[l("span",null,"sidebarDepth")])],-1),yl=l("li",null,[l("p",null,[n("类型: "),l("code",null,"number")])],-1),kl=l("li",null,[l("p",null,"详情:"),l("p",null,"配置本页面的侧边栏深度。")],-1),fl=l("p",null,"参考:",-1),xl=i(`---
+# NavLink
+prev :
+ text : Get Started
+ link : /guide/getting-started.html
+
+# NavLink - 外部 URL
+prev :
+ text : GitHub
+ link : https://github.com
+
+# 字符串 - 页面文件路径
+prev : /guide/getting-started.md
+
+# 字符串 - 页面文件相对路径
+prev : ../../guide/getting-started.md
+---
+
`,5);function Cl(El,Ll){const p=o("NpmBadge"),e=o("RouteLink"),t=o("ExternalLinkIcon");return c(),d("div",null,[h,s(p,{package:"@vuepress/theme-default"}),D,v,m,l("ul",null,[b,l("li",null,[_,l("p",null,[n("由"),s(e,{to:"/zh/plugins/external-link-icon.html#externallinkicon"},{default:a(()=>[n("@vuepress/plugin-external-link-icon")]),_:1}),n(" 提供。")])]),l("li",null,[g,l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/config.html#themeplugins-externallinkicon"},{default:a(()=>[n("默认主题 > 配置 > themePlugins.externalLinkIcon")]),_:1})])])])]),y,l("ul",null,[k,f,l("li",null,[x,l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/config.html#navbar"},{default:a(()=>[n("默认主题 > 配置 > navbar")]),_:1})])])])]),C,l("ul",null,[l("li",null,[n("参考: "),l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/styles.html#style-%E6%96%87%E4%BB%B6"},{default:a(()=>[n("默认主题 > 样式 > Style 文件")]),_:1})])])])]),E,l("ul",null,[L,l("li",null,[A,l("ul",null,[l("li",null,[l("a",z,[n("指南 > 静态资源 > Public 文件"),s(t)])])])])]),B,l("ul",null,[F,N,l("li",null,[I,l("ul",null,[w,l("li",null,[s(e,{to:"/zh/themes/default/config.html#colormode"},{default:a(()=>[n("默认主题 > 配置 > colorMode")]),_:1})])])])]),P,l("ul",null,[S,H,l("li",null,[V,T,M,l("p",null,[n("需要注意的是,首页图片的高度同样受到了 CSS 的约束。设置这个属性主要是为了减少由加载首页图片引起的 "),l("a",R,[n("累积布局偏移 (CLS)"),s(t)]),n(" 。")])])]),U,l("ul",null,[j,l("li",null,[G,O,l("p",null,[n("如果不设置,则默认使用站点 "),l("a",J,[n("title"),s(t)]),n(" 。")]),q])]),K,l("ul",null,[Q,l("li",null,[W,X,l("p",null,[n("如果不设置,则默认使用站点 "),l("a",Y,[n("description"),s(t)]),n(" 。")]),Z])]),$,l("ul",null,[ll,nl,l("li",null,[sl,l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/config.html#editlink"},{default:a(()=>[n("默认主题 > 配置 > editLink")]),_:1})])])])]),el,l("ul",null,[al,il,l("li",null,[tl,l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/config.html#editlinkpattern"},{default:a(()=>[n("默认主题 > 配置 > editLinkPattern")]),_:1})])])])]),ol,l("ul",null,[pl,rl,l("li",null,[cl,l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/config.html#lastupdated"},{default:a(()=>[n("默认主题 > 配置 > lastUpdated")]),_:1})])])])]),dl,l("ul",null,[ul,hl,l("li",null,[Dl,l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/config.html#contributors"},{default:a(()=>[n("默认主题 > 配置 > contributors")]),_:1})])])])]),vl,l("ul",null,[ml,bl,l("li",null,[_l,l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/config.html#sidebar"},{default:a(()=>[n("默认主题 > 配置 > sidebar")]),_:1})])])])]),gl,l("ul",null,[yl,kl,l("li",null,[fl,l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/config.html#sidebardepth"},{default:a(()=>[n("默认主题 > 配置 > sidebarDepth")]),_:1})])])])]),xl])}const zl=r(u,[["render",Cl],["__file","frontmatter.html.vue"]]),Bl=JSON.parse('{"path":"/zh/themes/default/frontmatter.html","title":"Frontmatter","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"所有页面","slug":"所有页面","link":"#所有页面","children":[{"level":3,"title":"externalLinkIcon","slug":"externallinkicon","link":"#externallinkicon","children":[]},{"level":3,"title":"navbar","slug":"navbar","link":"#navbar","children":[]},{"level":3,"title":"pageClass","slug":"pageclass","link":"#pageclass","children":[]}]},{"level":2,"title":"首页","slug":"首页","link":"#首页","children":[{"level":3,"title":"home","slug":"home","link":"#home","children":[]},{"level":3,"title":"heroImage","slug":"heroimage","link":"#heroimage","children":[]},{"level":3,"title":"heroImageDark","slug":"heroimagedark","link":"#heroimagedark","children":[]},{"level":3,"title":"heroAlt","slug":"heroalt","link":"#heroalt","children":[]},{"level":3,"title":"heroHeight","slug":"heroheight","link":"#heroheight","children":[]},{"level":3,"title":"heroText","slug":"herotext","link":"#herotext","children":[]},{"level":3,"title":"tagline","slug":"tagline","link":"#tagline","children":[]},{"level":3,"title":"actions","slug":"actions","link":"#actions","children":[]},{"level":3,"title":"features","slug":"features","link":"#features","children":[]},{"level":3,"title":"footer","slug":"footer","link":"#footer","children":[]},{"level":3,"title":"footerHtml","slug":"footerhtml","link":"#footerhtml","children":[]}]},{"level":2,"title":"普通页面","slug":"普通页面","link":"#普通页面","children":[{"level":3,"title":"editLink","slug":"editlink","link":"#editlink","children":[]},{"level":3,"title":"editLinkPattern","slug":"editlinkpattern","link":"#editlinkpattern","children":[]},{"level":3,"title":"lastUpdated","slug":"lastupdated","link":"#lastupdated","children":[]},{"level":3,"title":"contributors","slug":"contributors","link":"#contributors","children":[]},{"level":3,"title":"sidebar","slug":"sidebar","link":"#sidebar","children":[]},{"level":3,"title":"sidebarDepth","slug":"sidebardepth","link":"#sidebardepth","children":[]},{"level":3,"title":"prev","slug":"prev","link":"#prev","children":[]},{"level":3,"title":"next","slug":"next","link":"#next","children":[]}]}],"git":{"updatedTime":1706625181000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/themes/default/frontmatter.md"}');export{zl as comp,Bl as data};
diff --git a/assets/frontmatter.html-CTfK53N8.js b/assets/frontmatter.html-CTfK53N8.js
new file mode 100644
index 0000000000..0855745a69
--- /dev/null
+++ b/assets/frontmatter.html-CTfK53N8.js
@@ -0,0 +1,60 @@
+import{_ as r,r as o,o as c,c as d,a as s,b as e,d as l,w as a,e as t}from"./app-BcH8wZQx.js";const u={},h=e("h1",{id:"frontmatter",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#frontmatter"},[e("span",null,"Frontmatter")])],-1),m=e("h2",{id:"all-pages",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#all-pages"},[e("span",null,"All Pages")])],-1),D=e("p",null,"Frontmatter in this section will take effect in all types of pages.",-1),g=e("h3",{id:"externallinkicon",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#externallinkicon"},[e("span",null,"externalLinkIcon")])],-1),v=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),f=e("p",null,"Details:",-1),b=e("p",null,"Also see:",-1),y=e("h3",{id:"navbar",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#navbar"},[e("span",null,"navbar")])],-1),_=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),k=e("li",null,[e("p",null,"Details:"),e("p",null,"Show navbar on this page or not."),e("p",null,"If you disable navbar in theme config, this frontmatter will not take effect.")],-1),x=e("p",null,"Also see:",-1),C=t(`---
+pageClass : custom-page-class
+---
+
Then you can customize styles of this page in .vuepress/styles/index.scss
file:
.theme-container.custom-page-class {
+ /* page styles */
+}
+
`,5),E=t(`Frontmatter in this section will only take effect in home pages.
Type: boolean
Details:
Specify whether the page is homepage or a normal page.
If you don't set this frontmatter or set it to false
, the page would be a normal page .
Example:
---
+# public file path
+heroImage : /images/hero.png
+# url
+heroImage : https://vuejs.org/images/logo.png
+---
+
`,8),T={href:"https://v2.vuepress.vuejs.org/guide/assets.html#public-files",target:"_blank",rel:"noopener noreferrer"},A=e("h3",{id:"heroimagedark",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#heroimagedark"},[e("span",null,"heroImageDark")])],-1),w=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),S=e("li",null,[e("p",null,"Details:"),e("p",null,"Specify the url of hero image to be used in dark mode."),e("p",null,"You can make use of this option if you want to use different heroImage config in dark mode.")],-1),L=e("p",null,"Also see:",-1),I=e("li",null,[e("a",{href:"#heroimage"},"Default Theme > Frontmatter > heroImage")],-1),N=t(' ',3),P=e("li",null,[e("p",null,[l("Type: "),e("code",null,"number")])],-1),F=e("li",null,[e("p",null,[l("Default: "),e("code",null,"280")])],-1),H=e("p",null,"Details:",-1),j=e("p",null,[l("Specify the "),e("code",null,"height"),l(" attribute of the hero "),e("code",null," "),l(" tag.")],-1),B=e("p",null,"You may need to reduce this value if the height of your hero image is less than the default value.",-1),V={href:"https://web.dev/cls/",target:"_blank",rel:"noopener noreferrer"},M=e("h3",{id:"herotext",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#herotext"},[e("span",null,"heroText")])],-1),G=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string | null")])],-1),R=e("p",null,"Details:",-1),U=e("p",null,"Specify the the hero text.",-1),O={href:"https://v2.vuepress.vuejs.org/reference/config.html#title",target:"_blank",rel:"noopener noreferrer"},Y=e("p",null,[l("Set to "),e("code",null,"null"),l(" to disable hero text.")],-1),z=e("h3",{id:"tagline",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#tagline"},[e("span",null,"tagline")])],-1),J=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string | null")])],-1),q=e("p",null,"Details:",-1),K=e("p",null,"Specify the the tagline.",-1),Q={href:"https://v2.vuepress.vuejs.org/reference/config.html#description",target:"_blank",rel:"noopener noreferrer"},W=e("p",null,[l("Set to "),e("code",null,"null"),l(" to disable tagline.")],-1),X=t(`Array <{
+ text : string
+ link : string
+ type ?: 'primary' | 'secondary'
+}>
+
---
+actions :
+ - text : Get Started
+ link : /guide/getting-started.html
+ type : primary
+ - text : Introduction
+ link : /guide/introduction.html
+ type : secondary
+---
+
Array <{
+ title : string
+ details : string
+}>
+
---
+features :
+ - title : Simplicity First
+ details : Minimal setup with markdown-centered project structure helps you focus on writing.
+ - title : Vue-Powered
+ details : Enjoy the dev experience of Vue, use Vue components in markdown, and develop custom themes with Vue.
+ - title : Performant
+ details : VuePress generates pre-rendered static HTML for each page, and runs as an SPA once a page is loaded.
+---
+
Frontmatter in this section will only take effect in normal pages.
`,17),Z=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),$=e("li",null,[e("p",null,"Details:"),e("p",null,[l("Enable the "),e("em",null,"edit this page"),l(" link in this page or not.")])],-1),ee=e("p",null,"Also see:",-1),le=e("h3",{id:"editlinkpattern",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#editlinkpattern"},[e("span",null,"editLinkPattern")])],-1),se=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),ne=e("li",null,[e("p",null,"Details:"),e("p",null,[l("Specify the pattern of the "),e("em",null,"edit this page"),l(" link of this page.")])],-1),ae=e("p",null,"Also see:",-1),te=e("h3",{id:"lastupdated",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#lastupdated"},[e("span",null,"lastUpdated")])],-1),ie=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),oe=e("li",null,[e("p",null,"Details:"),e("p",null,[l("Enable the "),e("em",null,"last updated timestamp"),l(" in this page or not.")])],-1),pe=e("p",null,"Also see:",-1),re=e("h3",{id:"contributors",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#contributors"},[e("span",null,"contributors")])],-1),ce=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),de=e("li",null,[e("p",null,"Details:"),e("p",null,[l("Enable the "),e("em",null,"contributors list"),l(" in this page or not.")])],-1),ue=e("p",null,"Also see:",-1),he=e("h3",{id:"sidebar",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#sidebar"},[e("span",null,"sidebar")])],-1),me=e("li",null,[e("p",null,[l("Type: "),e("code",null,"false | 'auto' | SidebarConfigArray | SidebarConfigObject")])],-1),De=e("li",null,[e("p",null,"Details:"),e("p",null,"Configure the sidebar of this page.")],-1),ge=e("p",null,"Also see:",-1),ve=e("h3",{id:"sidebardepth",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#sidebardepth"},[e("span",null,"sidebarDepth")])],-1),fe=e("li",null,[e("p",null,[l("Type: "),e("code",null,"number")])],-1),be=e("li",null,[e("p",null,"Details:"),e("p",null,"Configure the sidebar depth of this page.")],-1),ye=e("p",null,"Also see:",-1),_e=t(`Type: NavLink | string
Details:
Specify the link of the previous page.
If you don't set this frontmatter, the link will be inferred from the sidebar config.
To configure the prev link manually, you can set this frontmatter to a NavLink
object or a string:
A NavLink
object should have a text
field and a link
field. A string should be the path to the target page file. It will be converted to a NavLink
object, whose text
is the page title, and link
is the page route path. Example:
---
+# NavLink
+prev :
+ text : Get Started
+ link : /guide/getting-started.html
+
+# NavLink - external url
+prev :
+ text : GitHub
+ link : https://github.com
+
+# string - page file path
+prev : /guide/getting-started.md
+
+# string - page file relative path
+prev : ../../guide/getting-started.md
+---
+
Type: NavLink | string
Details:
Specify the link of the next page.
If you don't set this frontmatter, the link will be inferred from the sidebar config.
The type is the same as prev frontmatter.
`,5);function ke(xe,Ce){const p=o("NpmBadge"),n=o("RouteLink"),i=o("ExternalLinkIcon");return c(),d("div",null,[h,s(p,{package:"@vuepress/theme-default"}),m,D,g,e("ul",null,[v,e("li",null,[f,e("p",null,[l("Provided by "),s(n,{to:"/plugins/external-link-icon.html#externallinkicon"},{default:a(()=>[l("@vuepress/plugin-external-link-icon")]),_:1}),l(".")])]),e("li",null,[b,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#themeplugins-externallinkicon"},{default:a(()=>[l("Default Theme > Config Reference > themePlugins.externalLinkIcon")]),_:1})])])])]),y,e("ul",null,[_,k,e("li",null,[x,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#navbar"},{default:a(()=>[l("Default Theme > Config > navbar")]),_:1})])])])]),C,e("ul",null,[e("li",null,[l("Also see: "),e("ul",null,[e("li",null,[s(n,{to:"/themes/default/styles.html#style-file"},{default:a(()=>[l("Default Theme > Styles > Style File")]),_:1})])])])]),E,e("ul",null,[e("li",null,[l("Also see: "),e("ul",null,[e("li",null,[e("a",T,[l("Guide > Assets > Public Files"),s(i)])])])])]),A,e("ul",null,[w,S,e("li",null,[L,e("ul",null,[I,e("li",null,[s(n,{to:"/themes/default/config.html#colormode"},{default:a(()=>[l("Default Theme > Config > colorMode")]),_:1})])])])]),N,e("ul",null,[P,F,e("li",null,[H,j,B,e("p",null,[l("Notice that the height is also constrained by CSS. This attribute is to reduce "),e("a",V,[l("Cumulative Layout Shift (CLS)"),s(i)]),l(" that caused by the loading of the hero image.")])])]),M,e("ul",null,[G,e("li",null,[R,U,e("p",null,[l("This will fallback to the site "),e("a",O,[l("title"),s(i)]),l(".")]),Y])]),z,e("ul",null,[J,e("li",null,[q,K,e("p",null,[l("This will fallback to the site "),e("a",Q,[l("description"),s(i)]),l(".")]),W])]),X,e("ul",null,[Z,$,e("li",null,[ee,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#editlink"},{default:a(()=>[l("Default Theme > Config > editLink")]),_:1})])])])]),le,e("ul",null,[se,ne,e("li",null,[ae,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#editlinkpattern"},{default:a(()=>[l("Default Theme > Config > editLinkPattern")]),_:1})])])])]),te,e("ul",null,[ie,oe,e("li",null,[pe,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#lastupdated"},{default:a(()=>[l("Default Theme > Config > lastUpdated")]),_:1})])])])]),re,e("ul",null,[ce,de,e("li",null,[ue,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#contributors"},{default:a(()=>[l("Default Theme > Config > contributors")]),_:1})])])])]),he,e("ul",null,[me,De,e("li",null,[ge,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#sidebar"},{default:a(()=>[l("Default Theme > Config > sidebar")]),_:1})])])])]),ve,e("ul",null,[fe,be,e("li",null,[ye,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#sidebardepth"},{default:a(()=>[l("Default Theme > Config > sidebarDepth")]),_:1})])])])]),_e])}const Te=r(u,[["render",ke],["__file","frontmatter.html.vue"]]),Ae=JSON.parse('{"path":"/themes/default/frontmatter.html","title":"Frontmatter","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"All Pages","slug":"all-pages","link":"#all-pages","children":[{"level":3,"title":"externalLinkIcon","slug":"externallinkicon","link":"#externallinkicon","children":[]},{"level":3,"title":"navbar","slug":"navbar","link":"#navbar","children":[]},{"level":3,"title":"pageClass","slug":"pageclass","link":"#pageclass","children":[]}]},{"level":2,"title":"Home Page","slug":"home-page","link":"#home-page","children":[{"level":3,"title":"home","slug":"home","link":"#home","children":[]},{"level":3,"title":"heroImage","slug":"heroimage","link":"#heroimage","children":[]},{"level":3,"title":"heroImageDark","slug":"heroimagedark","link":"#heroimagedark","children":[]},{"level":3,"title":"heroAlt","slug":"heroalt","link":"#heroalt","children":[]},{"level":3,"title":"heroHeight","slug":"heroheight","link":"#heroheight","children":[]},{"level":3,"title":"heroText","slug":"herotext","link":"#herotext","children":[]},{"level":3,"title":"tagline","slug":"tagline","link":"#tagline","children":[]},{"level":3,"title":"actions","slug":"actions","link":"#actions","children":[]},{"level":3,"title":"features","slug":"features","link":"#features","children":[]},{"level":3,"title":"footer","slug":"footer","link":"#footer","children":[]},{"level":3,"title":"footerHtml","slug":"footerhtml","link":"#footerhtml","children":[]}]},{"level":2,"title":"Normal Page","slug":"normal-page","link":"#normal-page","children":[{"level":3,"title":"editLink","slug":"editlink","link":"#editlink","children":[]},{"level":3,"title":"editLinkPattern","slug":"editlinkpattern","link":"#editlinkpattern","children":[]},{"level":3,"title":"lastUpdated","slug":"lastupdated","link":"#lastupdated","children":[]},{"level":3,"title":"contributors","slug":"contributors","link":"#contributors","children":[]},{"level":3,"title":"sidebar","slug":"sidebar","link":"#sidebar","children":[]},{"level":3,"title":"sidebarDepth","slug":"sidebardepth","link":"#sidebardepth","children":[]},{"level":3,"title":"prev","slug":"prev","link":"#prev","children":[]},{"level":3,"title":"next","slug":"next","link":"#next","children":[]}]}],"git":{"updatedTime":1706625181000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"themes/default/frontmatter.md"}');export{Te as comp,Ae as data};
diff --git a/assets/frontmatter.html-CnhtcLfI.js b/assets/frontmatter.html-CnhtcLfI.js
new file mode 100644
index 0000000000..64c132200e
--- /dev/null
+++ b/assets/frontmatter.html-CnhtcLfI.js
@@ -0,0 +1,51 @@
+import{_ as e,o as s,c as n,e as a}from"./app-BcH8wZQx.js";const l={},i=a(`You can control each feed item generation by setting page frontmatter.
By default, all articles are added to the feed stream. Set feed: false
in frontmatter to remove a page from feed.
Automatically generated by VuePress, defaults to the h1 content of the page
Description of the page
Date when the page was published
Whether the page is an article
If this is set to false
, the page will not be included in the final feed.
Page copyright information
Image used as page cover , should be full link or absolute link.
The title of the feed item
Description of the feed item
The content of the feed item
Type: FeedAuthor[] | FeedAuthor
The author of the feed item
FeedAuthor format interface FeedAuthor {
+ /**
+ * Author name
+ */
+ name ?: string
+
+ /**
+ * Author email
+ */
+ email ?: string
+
+ /**
+ * Author site
+ *
+ * @description json format only
+ */
+ url ?: string
+
+ /**
+ * Author avatar
+ *
+ * @description json format only
+ */
+ avatar ?: string
+}
+
Type: FeedContributor[] | FeedContributor
Contributors to feed item
FeedContributor format interface FeedContributor {
+ /**
+ * Author name
+ */
+ name ?: string
+
+ /**
+ * Author email
+ */
+ email ?: string
+
+ /**
+ * Author site
+ *
+ * @description json format only
+ */
+ url ?: string
+
+ /**
+ * Author avatar
+ *
+ * @description json format only
+ */
+ avatar ?: string
+}
+
The identifier of feed item, used to identify the feed item.
You should ensure every feed has a unique guid.
`,46),t=[i];function o(r,p){return s(),n("div",null,t)}const d=e(l,[["render",o],["__file","frontmatter.html.vue"]]),u=JSON.parse('{"path":"/plugins/feed/frontmatter.html","title":"Frontmatter Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Additions and Removals","slug":"additions-and-removals","link":"#additions-and-removals","children":[]},{"level":2,"title":"Frontmatter Information","slug":"frontmatter-information","link":"#frontmatter-information","children":[{"level":3,"title":"title","slug":"title","link":"#title","children":[]},{"level":3,"title":"description","slug":"description","link":"#description","children":[]},{"level":3,"title":"date","slug":"date","link":"#date","children":[]},{"level":3,"title":"article","slug":"article","link":"#article","children":[]},{"level":3,"title":"copyright","slug":"copyright","link":"#copyright","children":[]},{"level":3,"title":"cover / image / banner","slug":"cover-image-banner","link":"#cover-image-banner","children":[]}]},{"level":2,"title":"Frontmatter Options","slug":"frontmatter-options","link":"#frontmatter-options","children":[{"level":3,"title":"feed.title","slug":"feed-title","link":"#feed-title","children":[]},{"level":3,"title":"feed.description","slug":"feed-description","link":"#feed-description","children":[]},{"level":3,"title":"feed.content","slug":"feed-content","link":"#feed-content","children":[]},{"level":3,"title":"feed.author","slug":"feed-author","link":"#feed-author","children":[]},{"level":3,"title":"feed.contributor","slug":"feed-contributor","link":"#feed-contributor","children":[]},{"level":3,"title":"feed.guid","slug":"feed-guid","link":"#feed-guid","children":[]}]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/feed/frontmatter.md"}');export{d as comp,u as data};
diff --git a/assets/frontmatter.html-D3fZMJ8G.js b/assets/frontmatter.html-D3fZMJ8G.js
new file mode 100644
index 0000000000..a90d1eaa01
--- /dev/null
+++ b/assets/frontmatter.html-D3fZMJ8G.js
@@ -0,0 +1 @@
+import{_ as l,r as n,o,c as r,b as e,d as t,a as s,w as p,e as a}from"./app-BcH8wZQx.js";const c={},m=a(' ',4),h=e("li",null,[e("p",null,[t("类型:"),e("code",null,'"always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never"')])],-1),d=e("li",null,[e("p",null,[t("默认值:"),e("code",null,'"daily"')])],-1),u=e("p",null,"详情:",-1),f=a('类型:number
默认值:0.5
详情:
页面优先级,范围 0
至 1
。
',2);function _(g,y){const i=n("RouteLink");return o(),r("div",null,[m,e("ul",null,[h,d,e("li",null,[u,e("p",null,[t("页面默认更新频率。它会覆盖插件选项中的 "),s(i,{to:"/zh/plugins/sitemap/config.html#changefreq"},{default:p(()=>[t("changefreq")]),_:1}),t(" 选项。")])])]),f])}const x=l(c,[["render",_],["__file","frontmatter.html.vue"]]),q=JSON.parse('{"path":"/zh/plugins/sitemap/frontmatter.html","title":"Frontmatter","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"sitemap","slug":"sitemap","link":"#sitemap","children":[{"level":3,"title":"sitemap.changefreq","slug":"sitemap-changefreq","link":"#sitemap-changefreq","children":[]},{"level":3,"title":"sitemap.priority","slug":"sitemap-priority","link":"#sitemap-priority","children":[]}]}],"git":{"updatedTime":1706625181000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/sitemap/frontmatter.md"}');export{x as comp,q as data};
diff --git a/assets/getter.html-C8_KZMfI.js b/assets/getter.html-C8_KZMfI.js
new file mode 100644
index 0000000000..5d5b386196
--- /dev/null
+++ b/assets/getter.html-C8_KZMfI.js
@@ -0,0 +1,91 @@
+import{_ as s,o as n,c as e,e as a}from"./app-BcH8wZQx.js";const l={},t=a(`You can take full control of feed items generation by setting getter
in the plugin options.
Type: (page: Page) => string
Item title getter
Type: (page: Page) => string
Item link getter
Type: (page: Page) => string | undefined
Item description getter
TIP
Due to Atom support HTML in summary, so you can return HTML content here if possible, but the content must start with mark html:
.
Type: (page: Page) => string
Item content getter
Type: (page: Page) => FeedAuthor[]
Item author getter.
The getter should return an empty array when author information is missing.
FeedAuthor format interface FeedAuthor {
+ /**
+ * Author name
+ */
+ name ?: string
+
+ /**
+ * Author email
+ */
+ email ?: string
+
+ /**
+ * Author site
+ *
+ * @description json format only
+ */
+ url ?: string
+
+ /**
+ * Author avatar
+ *
+ * @description json format only
+ */
+ avatar ?: string
+}
+
Type: (page: Page) => FeedCategory[] | undefined
Item category getter.
FeedCategory format interface FeedCategory {
+ /**
+ * Category Name
+ */
+ name : string
+
+ /**
+ * A string that identifies a categorization taxonomy
+ *
+ * @description rss format only
+ */
+ domain ?: string
+
+ /**
+ * the categorization scheme via a URI
+ *
+ * @description atom format only
+ */
+ scheme ?: string
+}
+
Type: (page: Page) => FeedEnclosure | undefined
Item enclosure getter.
FeedEnclosure format interface FeedEnclosure {
+ /**
+ * Enclosure link
+ */
+ url : string
+
+ /**
+ * what its type is
+ *
+ * @description should be a standard MIME Type, rss format only
+ */
+ Type : string
+
+ /**
+ * Size in bytes
+ *
+ * @description rss format only
+ */
+ length ?: number
+}
+
Type: (page: Page) => Date | undefined
Item release date getter
Type: (page: Page) => Date
Item last update date getter
Type: (page: Page) => string
Item Image Getter
Ensure it's returning a full URL
Type: (page: Page) => FeedContributor[]
Item Contributor Getter
The getter should return an empty array when contributor information is missing.
FeedContributor format interface FeedContributor {
+ /**
+ * Author name
+ */
+ name ?: string
+
+ /**
+ * Author email
+ */
+ email ?: string
+
+ /**
+ * Author site
+ *
+ * @description json format only
+ */
+ url ?: string
+
+ /**
+ * Author avatar
+ *
+ * @description json format only
+ */
+ avatar ?: string
+}
+
Type: (page: Page) => string | undefined
Item copyright getter
`,46),i=[t];function p(r,c){return n(),e("div",null,i)}const d=s(l,[["render",p],["__file","getter.html.vue"]]),u=JSON.parse('{"path":"/plugins/feed/getter.html","title":"Feed Getter","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"getter.title","slug":"getter-title","link":"#getter-title","children":[]},{"level":2,"title":"getter.link","slug":"getter-link","link":"#getter-link","children":[]},{"level":2,"title":"getter.description","slug":"getter-description","link":"#getter-description","children":[]},{"level":2,"title":"getter.content","slug":"getter-content","link":"#getter-content","children":[]},{"level":2,"title":"getter.author","slug":"getter-author","link":"#getter-author","children":[]},{"level":2,"title":"getter.category","slug":"getter-category","link":"#getter-category","children":[]},{"level":2,"title":"getter.enclosure","slug":"getter-enclosure","link":"#getter-enclosure","children":[]},{"level":2,"title":"getter.publishDate","slug":"getter-publishdate","link":"#getter-publishdate","children":[]},{"level":2,"title":"getter.lastUpdateDate","slug":"getter-lastupdatedate","link":"#getter-lastupdatedate","children":[]},{"level":2,"title":"getter.image","slug":"getter-image","link":"#getter-image","children":[]},{"level":2,"title":"getter.contributor","slug":"getter-contributor","link":"#getter-contributor","children":[]},{"level":2,"title":"getter.copyright","slug":"getter-copyright","link":"#getter-copyright","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/feed/getter.md"}');export{d as comp,u as data};
diff --git a/assets/getter.html-COH5xBAr.js b/assets/getter.html-COH5xBAr.js
new file mode 100644
index 0000000000..93a0bbce25
--- /dev/null
+++ b/assets/getter.html-COH5xBAr.js
@@ -0,0 +1,91 @@
+import{_ as s,o as n,c as e,e as a}from"./app-BcH8wZQx.js";const l={},i=a(`你可以通过控制插件选项中的 getter
来完全控制 Feed 项目的生成。
类型:(page: Page) => string
项目标题获取器
类型:(page: Page) => string
项目链接获取器
类型:(page: Page) => string | undefined
项目描述获取器
提示
因为 Atom 在摘要中支持 HTML,所以如果可能的话,你可以在这里返回 HTML 内容,但内容必须以标记 html:
开头。
类型:(page: Page) => string
项目内容获取器
类型:(page: Page) => FeedAuthor[]
项目作者获取器。
FeedAuthor 格式 interface FeedAuthor {
+ /**
+ * 作者名字
+ */
+ name ?: string
+
+ /**
+ * 作者邮件
+ */
+ email ?: string
+
+ /**
+ * 作者网站
+ *
+ * @description json format only
+ */
+ url ?: string
+
+ /**
+ * 作者头像
+ *
+ * @description json format only
+ */
+ avatar ?: string
+}
+
类型:(page: Page) => FeedCategory[] | undefined
项目分类获取器。
FeedCategory 格式 interface FeedCategory {
+ /**
+ * 分类名称
+ */
+ name : string
+
+ /**
+ * 标识分类法的字符串
+ *
+ * @description rss format only
+ */
+ domain ?: string
+
+ /**
+ * URI 标识的分类 scheme
+ *
+ * @description atom format only
+ */
+ scheme ?: string
+}
+
类型:(page: Page) => FeedEnclosure | undefined
项目附件获取器。
FeedEnclosure 格式 interface FeedEnclosure {
+ /**
+ * Enclosure 地址
+ */
+ url : string
+
+ /**
+ * 类型
+ *
+ * @description 应为一个标准的 MIME 类型,rss format only
+ */
+ type : string
+
+ /**
+ * 按照字节数计算的大小
+ *
+ * @description rss format only
+ */
+ length ?: number
+}
+
类型:(page: Page) => Date | undefined
项目发布日期获取器
项目最后更新日期获取器
类型:(page: Page) => string
项目图片获取器
类型:(page: Page) => FeedContributor[]
项目贡献者获取器
FeedContributor 格式 interface FeedContributor {
+ /**
+ * 作者名字
+ */
+ name ?: string
+
+ /**
+ * 作者邮件
+ */
+ email ?: string
+
+ /**
+ * 作者网站
+ *
+ * @description json format only
+ */
+ url ?: string
+
+ /**
+ * 作者头像
+ *
+ * @description json format only
+ */
+ avatar ?: string
+}
+
类型:(page: Page) => string | undefined
项目版权获取器
`,46),t=[i];function p(r,c){return n(),e("div",null,t)}const d=s(l,[["render",p],["__file","getter.html.vue"]]),u=JSON.parse('{"path":"/zh/plugins/feed/getter.html","title":"Feed 获取器","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"getter.title","slug":"getter-title","link":"#getter-title","children":[]},{"level":2,"title":"getter.link","slug":"getter-link","link":"#getter-link","children":[]},{"level":2,"title":"getter.description","slug":"getter-description","link":"#getter-description","children":[]},{"level":2,"title":"getter.content","slug":"getter-content","link":"#getter-content","children":[]},{"level":2,"title":"getter.author","slug":"getter-author","link":"#getter-author","children":[]},{"level":2,"title":"getter.category","slug":"getter-category","link":"#getter-category","children":[]},{"level":2,"title":"getter.enclosure","slug":"getter-enclosure","link":"#getter-enclosure","children":[]},{"level":2,"title":"getter.publishDate","slug":"getter-publishdate","link":"#getter-publishdate","children":[]},{"level":2,"title":"getter.lastUpdateDate","slug":"getter-lastupdatedate","link":"#getter-lastupdatedate","children":[]},{"level":2,"title":"getter.image","slug":"getter-image","link":"#getter-image","children":[]},{"level":2,"title":"getter.contributor","slug":"getter-contributor","link":"#getter-contributor","children":[]},{"level":2,"title":"getter.copyright","slug":"getter-copyright","link":"#getter-copyright","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/feed/getter.md"}');export{d as comp,u as data};
diff --git a/assets/git.html-D5W-c3Ut.js b/assets/git.html-D5W-c3Ut.js
new file mode 100644
index 0000000000..86e9e3aedb
--- /dev/null
+++ b/assets/git.html-D5W-c3Ut.js
@@ -0,0 +1,30 @@
+import{_ as r,r as n,o as c,c as d,a,b as s,d as e,w as i,e as o}from"./app-BcH8wZQx.js";const u={},h=s("h1",{id:"git",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#git"},[s("span",null,"git")])],-1),m=s("p",null,"This plugin will collect git information of your pages, including the created and updated time, the contributors, etc.",-1),g=o(`This plugin is mainly used to develop themes. You won't need to use it directly in most cases.
npm i -D @vuepress/plugin-git@next
+
import { gitPlugin } from '@vuepress/plugin-git'
+
+export default {
+ plugins: [
+ gitPlugin ({
+ // options
+ }),
+ ],
+}
+
`,5),D={href:"https://git-scm.com/book/en/Git-Basics-Getting-a-Git-Repository",target:"_blank",rel:"noopener noreferrer"},y={href:"https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---depthltdepthgt",target:"_blank",rel:"noopener noreferrer"},v=o(`WARNING
This plugin will significantly slow down the speed of data preparation, especially when you have a lot of pages. You can consider disabling this plugin in dev
mode to get better development experience.
---
+gitInclude :
+ - relative/path/to/file1
+ - relative/path/to/file2
+---
+
This plugin will add a git
field to page data.
After using this plugin, you can get the collected git information in page data:
import type { GitPluginPageData } from '@vuepress/plugin-git'
+import { usePageData } from 'vuepress/client'
+
+export default {
+ setup () {
+ const page = usePageData < GitPluginPageData >()
+ console . log ( page . value . git )
+ },
+}
+
interface GitContributor {
+ name : string
+ email : string
+ commits : number
+}
+
`,24);function b(f,C){const p=n("NpmBadge"),l=n("RouteLink"),t=n("ExternalLinkIcon");return c(),d("div",null,[h,a(p,{package:"@vuepress/plugin-git"}),m,s("p",null,[e("The "),a(l,{to:"/default-theme/config.html#lastupdated"},{default:i(()=>[e("lastUpdated")]),_:1}),e(" and "),a(l,{to:"/default-theme/config.html#contributors"},{default:i(()=>[e("contributors")]),_:1}),e(" of default theme is powered by this plugin.")]),g,s("p",null,[e("This plugin requires your project to be inside a "),s("a",D,[e("Git Repository"),a(t)]),e(", so that it can collect information from the commit history.")]),s("p",null,[e("You should ensure all commits are available when building your site. For example, CI workflows usually clone your repository with "),s("a",y,[e("--depth 1"),a(t)]),e(" to avoid fetching all commits, so you should disable the behavior to make this plugin work properly in CI.")]),v])}const E=r(u,[["render",b],["__file","git.html.vue"]]),x=JSON.parse('{"path":"/plugins/git.html","title":"git","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Git Repository","slug":"git-repository","link":"#git-repository","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"createdTime","slug":"createdtime","link":"#createdtime","children":[]},{"level":3,"title":"updatedTime","slug":"updatedtime","link":"#updatedtime","children":[]},{"level":3,"title":"contributors","slug":"contributors","link":"#contributors","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"gitInclude","slug":"gitinclude","link":"#gitinclude","children":[]}]},{"level":2,"title":"Page Data","slug":"page-data","link":"#page-data","children":[{"level":3,"title":"git.createdTime","slug":"git-createdtime","link":"#git-createdtime","children":[]},{"level":3,"title":"git.updatedTime","slug":"git-updatedtime","link":"#git-updatedtime","children":[]},{"level":3,"title":"git.contributors","slug":"git-contributors","link":"#git-contributors","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/git.md"}');export{E as comp,x as data};
diff --git a/assets/git.html-E1oGmecX.js b/assets/git.html-E1oGmecX.js
new file mode 100644
index 0000000000..0582f110f1
--- /dev/null
+++ b/assets/git.html-E1oGmecX.js
@@ -0,0 +1,30 @@
+import{_ as c,r as a,o as r,c as d,a as n,b as e,d as s,w as t,e as p}from"./app-BcH8wZQx.js";const u={},D=e("h1",{id:"git",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#git"},[e("span",null,"git")])],-1),h=e("p",null,"该插件会收集你的页面的 Git 信息,包括创建和更新时间、贡献者等。",-1),m=p(`该插件主要用于开发主题,大部分情况下你不需要直接使用它。
npm i -D @vuepress/plugin-git@next
+
import { gitPlugin } from '@vuepress/plugin-git'
+
+export default {
+ plugins: [
+ gitPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
`,5),g={href:"https://git-scm.com/book/en/Git-Basics-Getting-a-Git-Repository",target:"_blank",rel:"noopener noreferrer"},v={href:"https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---depthltdepthgt",target:"_blank",rel:"noopener noreferrer"},b=p(`注意
该插件会显著降低准备数据的速度,特别是在你的页面数量很多的时候。你可以考虑在 dev
模式下禁用该插件来获取更好的开发体验。
类型: boolean
默认值: true
详情:
是否收集页面的创建时间。
类型: boolean
默认值: true
详情:
是否收集页面的更新时间。
类型: boolean
默认值: true
详情:
是否收集页面的贡献者。
---
+gitInclude :
+ - relative/path/to/file1
+ - relative/path/to/file2
+---
+
该插件会向页面数据中添加一个 git
字段。
在使用该插件后,可以在页面数据中获取该插件收集到的 Git 信息:
import type { GitPluginPageData } from '@vuepress/plugin-git'
+import { usePageData } from 'vuepress/client'
+
+export default {
+ setup () {
+ const page = usePageData < GitPluginPageData >()
+ console . log ( page . value . git )
+ },
+}
+
interface GitContributor {
+ name : string
+ email : string
+ commits : number
+}
+
`,24);function y(C,f){const o=a("NpmBadge"),l=a("RouteLink"),i=a("ExternalLinkIcon");return r(),d("div",null,[D,n(o,{package:"@vuepress/plugin-git"}),h,e("p",null,[s("默认主题的 "),n(l,{to:"/zh/default-theme/config.html#lastupdated"},{default:t(()=>[s("lastUpdated")]),_:1}),s(" 和 "),n(l,{to:"/zh/default-theme/config.html#contributors"},{default:t(()=>[s("contributors")]),_:1}),s(" 就是由该插件支持的。")]),m,e("p",null,[s("该插件要求你的项目在 "),e("a",g,[s("Git 仓库"),n(i)]),s(" 下,这样它才能从提交历史记录中收集信息。")]),e("p",null,[s("在构建站点时,你应该确保所有的提交记录是可以获取到的。举例来说, CI 工作流通常会在克隆你的仓库时添加 "),e("a",v,[s("--depth 1"),n(i)]),s(" 参数来避免拉取全部的提交记录,因此你需要禁用这个功能,以便该插件在 CI 可以中正常使用。")]),b])}const k=c(u,[["render",y],["__file","git.html.vue"]]),x=JSON.parse('{"path":"/zh/plugins/git.html","title":"git","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"Git 仓库","slug":"git-仓库","link":"#git-仓库","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"createdTime","slug":"createdtime","link":"#createdtime","children":[]},{"level":3,"title":"updatedTime","slug":"updatedtime","link":"#updatedtime","children":[]},{"level":3,"title":"contributors","slug":"contributors","link":"#contributors","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"gitInclude","slug":"gitinclude","link":"#gitinclude","children":[]}]},{"level":2,"title":"页面数据","slug":"页面数据","link":"#页面数据","children":[{"level":3,"title":"git.createdTime","slug":"git-createdtime","link":"#git-createdtime","children":[]},{"level":3,"title":"git.updatedTime","slug":"git-updatedtime","link":"#git-updatedtime","children":[]},{"level":3,"title":"git.contributors","slug":"git-contributors","link":"#git-contributors","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/git.md"}');export{k as comp,x as data};
diff --git a/assets/google-analytics.html-7Ld7n2El.js b/assets/google-analytics.html-7Ld7n2El.js
new file mode 100644
index 0000000000..f5cadf1144
--- /dev/null
+++ b/assets/google-analytics.html-7Ld7n2El.js
@@ -0,0 +1,26 @@
+import{_ as i,r as o,o as r,c,a as n,b as s,d as e,e as a}from"./app-BcH8wZQx.js";const p={},d=s("h1",{id:"google-analytics",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#google-analytics"},[s("span",null,"google-analytics")])],-1),u={href:"https://analytics.google.com/",target:"_blank",rel:"noopener noreferrer"},g={href:"https://developers.google.com/analytics/devguides/collection/gtagjs",target:"_blank",rel:"noopener noreferrer"},D={href:"https://support.google.com/analytics/answer/10089681",target:"_blank",rel:"noopener noreferrer"},h=a(`npm i -D @vuepress/plugin-google-analytics@next
+
import { googleAnalyticsPlugin } from '@vuepress/plugin-google-analytics'
+
+export default {
+ plugins: [
+ googleAnalyticsPlugin ({
+ // options
+ }),
+ ],
+}
+
`,4),v={href:"https://support.google.com/analytics/answer/9234069",target:"_blank",rel:"noopener noreferrer"},y=s("code",null,"page_view",-1),m=s("code",null,"first_visit",-1),_=s("p",null,[e("So if you only want to collect some basic data of your site, you don't need to do anything else except setting the "),s("a",{href:"#id"},"Measurement ID"),e(" correctly.")],-1),b=s("code",null,"gtag()",-1),f=s("code",null,"window",-1),C={href:"https://developers.google.com/analytics/devguides/collection/ga4/events",target:"_blank",rel:"noopener noreferrer"},k=s("h2",{id:"options",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#options"},[s("span",null,"Options")])],-1),E=s("h3",{id:"id",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#id"},[s("span",null,"id")])],-1),x=s("li",null,[s("p",null,[e("Type: "),s("code",null,"string")])],-1),A=s("p",null,"Details:",-1),X=s("p",null,[e("The Measurement ID of Google Analytics 4, which should start with "),s("code",null,"'G-'"),e(".")],-1),w={href:"https://support.google.com/analytics/answer/9539598",target:"_blank",rel:"noopener noreferrer"},I=s("li",null,[s("p",null,"Example:")],-1),G=a(`export default {
+ plugins: [
+ googleAnalyticsPlugin ({
+ id: 'G-XXXXXXXXXX' ,
+ }),
+ ],
+}
+
`,2),N=s("li",null,[s("p",null,[e("Type: "),s("code",null,"boolean")])],-1),F=s("p",null,"Details:",-1),T=s("code",null,"true",-1),V={href:"https://support.google.com/analytics/answer/7201382",target:"_blank",rel:"noopener noreferrer"},P=s("li",null,[s("p",null,"Example:")],-1),S=a(`export default {
+ plugins: [
+ googleAnalyticsPlugin ({
+ id: 'G-XXXXXXXXXX' ,
+ debug: true ,
+ }),
+ ],
+}
+
`,1);function B(M,U){const t=o("NpmBadge"),l=o("ExternalLinkIcon");return r(),c("div",null,[d,n(t,{package:"@vuepress/plugin-google-analytics"}),s("p",null,[e("Integrate "),s("a",u,[e("Google Analytics"),n(l)]),e(" into VuePress.")]),s("p",null,[e("This plugin will import "),s("a",g,[e("gtag.js"),n(l)]),e(" for "),s("a",D,[e("Google Analytics 4"),n(l)]),e(".")]),h,s("p",null,[e("Google Analytics will "),s("a",v,[e("automatically collect some events"),n(l)]),e(", such as "),y,e(", "),m,e(", etc.")]),_,s("p",null,[e("After using this plugin, the global "),b,e(" function is available on the "),f,e(" object, and you can use it for "),s("a",C,[e("custom events reporting"),n(l)]),e(".")]),k,E,s("ul",null,[x,s("li",null,[A,X,s("p",null,[e("You can follow the instructions "),s("a",w,[e("here"),n(l)]),e(' to find your Measurement ID. Notice the difference between Google Analytics 4 Measurement ID (i.e. "G-" ID) and Universal Analytics Tracking ID (i.e. "UA-" ID).')])]),I]),G,s("ul",null,[N,s("li",null,[F,s("p",null,[e("Set to "),T,e(" to enable sending events to DebugView. "),s("a",V,[e("See more information on DebugView"),n(l)]),e(".")])]),P]),S])}const O=i(p,[["render",B],["__file","google-analytics.html.vue"]]),R=JSON.parse('{"path":"/plugins/google-analytics.html","title":"google-analytics","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[{"level":3,"title":"Reporting Events","slug":"reporting-events","link":"#reporting-events","children":[]}]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"id","slug":"id","link":"#id","children":[]},{"level":3,"title":"debug","slug":"debug","link":"#debug","children":[]}]}],"git":{"updatedTime":1707223547000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/google-analytics.md"}');export{O as comp,R as data};
diff --git a/assets/google-analytics.html-ydZOWnMr.js b/assets/google-analytics.html-ydZOWnMr.js
new file mode 100644
index 0000000000..1b24ff3500
--- /dev/null
+++ b/assets/google-analytics.html-ydZOWnMr.js
@@ -0,0 +1,26 @@
+import{_ as t,r as o,o as c,c as p,a as e,b as s,d as n,e as a}from"./app-BcH8wZQx.js";const r={},d=s("h1",{id:"google-analytics",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#google-analytics"},[s("span",null,"google-analytics")])],-1),u={href:"https://analytics.google.com/",target:"_blank",rel:"noopener noreferrer"},g={href:"https://developers.google.com/analytics/devguides/collection/gtagjs",target:"_blank",rel:"noopener noreferrer"},D={href:"https://support.google.com/analytics/answer/10089681",target:"_blank",rel:"noopener noreferrer"},h=a(`npm i -D @vuepress/plugin-google-analytics@next
+
import { googleAnalyticsPlugin } from '@vuepress/plugin-google-analytics'
+
+export default {
+ plugins: [
+ googleAnalyticsPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
`,4),v={href:"https://support.google.com/analytics/answer/9234069",target:"_blank",rel:"noopener noreferrer"},y=s("code",null,"page_view",-1),_=s("code",null,"first_visit",-1),m=s("p",null,[n("因此,如果你只是想收集站点的一些基础数据,你只需要正确设置 "),s("a",{href:"#id"},"Measurement ID"),n(" ,不需要再额外做其他事情。")],-1),b=s("code",null,"gtag()",-1),C=s("code",null,"window",-1),f={href:"https://developers.google.com/analytics/devguides/collection/ga4/events",target:"_blank",rel:"noopener noreferrer"},k=s("h2",{id:"选项",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#选项"},[s("span",null,"选项")])],-1),E=s("h3",{id:"id",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#id"},[s("span",null,"id")])],-1),x=s("li",null,[s("p",null,[n("类型: "),s("code",null,"string")])],-1),A=s("p",null,"详情:",-1),X=s("p",null,[n("Google Analytics 4 的 Measurement ID ,应以 "),s("code",null,"'G-'"),n(" 开头。")],-1),w={href:"https://support.google.com/analytics/answer/9539598",target:"_blank",rel:"noopener noreferrer"},G=s("li",null,[s("p",null,"示例:")],-1),I=a(`export default {
+ plugins: [
+ googleAnalyticsPlugin ({
+ id: 'G-XXXXXXXXXX' ,
+ }),
+ ],
+}
+
`,2),N=s("li",null,[s("p",null,[n("类型: "),s("code",null,"boolean")])],-1),F=s("p",null,"详情:",-1),V=s("code",null,"true",-1),P={href:"https://support.google.com/analytics/answer/7201382",target:"_blank",rel:"noopener noreferrer"},B=s("li",null,[s("p",null,"示例:")],-1),M=a(`export default {
+ plugins: [
+ googleAnalyticsPlugin ({
+ id: 'G-XXXXXXXXXX' ,
+ debug: true ,
+ }),
+ ],
+}
+
`,1);function z(T,j){const i=o("NpmBadge"),l=o("ExternalLinkIcon");return c(),p("div",null,[d,e(i,{package:"@vuepress/plugin-google-analytics"}),s("p",null,[n("将 "),s("a",u,[n("Google Analytics"),e(l)]),n(" 集成到 VuePress 中。")]),s("p",null,[n("该插件会通过引入 "),s("a",g,[n("gtag.js"),e(l)]),n(" 来启用 "),s("a",D,[n("Google Analytics 4"),e(l)]),n(" 。")]),h,s("p",null,[n("Google Analytics 会 "),s("a",v,[n("自动收集部分事件"),e(l)]),n(" ,比如 "),y,n(", "),_,n(" 等。")]),m,s("p",null,[n("在引入该插件之后,一个全局的 "),b,n(" 函数会被挂载到 "),C,n(" 对象上,你可以使用它进行 "),s("a",f,[n("自定义事件的上报"),e(l)]),n(" 。")]),k,E,s("ul",null,[x,s("li",null,[A,X,s("p",null,[n("你可以通过 "),s("a",w,[n("这里"),e(l)]),n(' 的指引来找到你的 Measurement ID 。注意区分 Google Analytics 4 的 Measurement ID (即 "G-" 开头的 ID) 和 Universal Analytics 的 Tracking ID (即 "UA-" 开头的 ID)。')])]),G]),I,s("ul",null,[N,s("li",null,[F,s("p",null,[n("设置为 "),V,n(" 可以向 DebugView 发送事件。"),s("a",P,[n("了解更多关于 DebugView 的信息"),e(l)]),n(" 。")])]),B]),M])}const S=t(r,[["render",z],["__file","google-analytics.html.vue"]]),U=JSON.parse('{"path":"/zh/plugins/google-analytics.html","title":"google-analytics","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[{"level":3,"title":"上报事件","slug":"上报事件","link":"#上报事件","children":[]}]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"id","slug":"id","link":"#id","children":[]},{"level":3,"title":"debug","slug":"debug","link":"#debug","children":[]}]}],"git":{"updatedTime":1707223547000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/google-analytics.md"}');export{S as comp,U as data};
diff --git a/assets/guide.html-0r3vMXAF.js b/assets/guide.html-0r3vMXAF.js
new file mode 100644
index 0000000000..5d97c3170b
--- /dev/null
+++ b/assets/guide.html-0r3vMXAF.js
@@ -0,0 +1,218 @@
+import{_ as e,r as t,o as c,c as r,b as o,d as s,a,w as l,e as p}from"./app-BcH8wZQx.js";const i={},D=p(`With @vuepress/plugin-blog
, you can easily bring blog feature into your theme.
The plugin filters all pages using filter
option to drop pages you don't want.
By default, all pages generating from Markdown files but not homepage are considered as articles.
You can fully customize pages to collect through option filter
, which accepts a function (page: Page) => boolean
.
You should set getInfo
option with a function accepting Page
as argument and returning an object containing the info you want.
The plugin will collect all the info you want and write them to routeMeta
field of each page, so you will be able to get this information through Composition API later.
Demo import { blogPlugin } from '@vuepress/plugin-blog'
+
+export default {
+ name: 'vuepress-theme-xxx' ,
+ plugins: [
+ blogPlugin ({
+ filter : ({ filePathRelative , frontmatter }) => {
+ // drop those pages which is NOT generated from file
+ if (! filePathRelative ) return false
+
+ // drop those pages in \`archives\` directory
+ if ( filePathRelative . startsWith ( 'archives/' )) return false
+
+ // drop those pages which do not use default layout
+ if ( frontmatter . home || frontmatter . layout ) return false
+
+ return true
+ },
+
+ getInfo : ({ frontmatter , git = {}, data = {} }) => {
+ // getting page info
+ const info : Record < string , any > = {
+ author: frontmatter . author || '' ,
+ categories: frontmatter . categories || [],
+ date: frontmatter . date || git . createdTime || null ,
+ tags: frontmatter . tags || [],
+ excerpt: data . excerpt || '' ,
+ }
+
+ return info
+ },
+ }),
+ // other plugins ...
+ ],
+}
+
Basically, you would want 2 types of collection in your blog:
Category:
"Category" means grouping articles with their labels.
For example, each article may have their "categories" and "tags".
Type:
"Type" means identifying articles with conditions.
For example, you may want to describe some of your articles as diary.
After understanding description of these 2 types, you can set category
and type
options, each accepts an array, and each element represents a configuration.
Let's start with 2 examples here.
Imagine you are setting tags for each articles with tag
field in page frontmatter. You want a tag mapping page in /tag/
with TagMap
layout, and group each tag list with tagName in /tag/tagName
with TagList
layout, you probably need a configuration like this:
import { blogPlugin } from '@vuepress/plugin-blog'
+
+export default {
+ name: 'vuepress-theme-xxx' ,
+ plugins: [
+ blogPlugin ({
+ // other options ...
+ category: [
+ {
+ key: 'tag' ,
+ getter : ({ frontmatter }) => frontmatter . tag || [],
+ path: '/tag/' ,
+ layout: 'TagMap' ,
+ frontmatter : () => ({ title: 'Tag page' }),
+ itemPath: '/tag/:name/' ,
+ itemLayout: 'TagList' ,
+ itemFrontmatter : ( name ) => ({ title: \`Tag \${ name } \` }),
+ },
+ ],
+ }),
+ // other plugins ...
+ ],
+}
+
Also, you may want to star some of your articles, and display them to visitors. When you are setting star: true
in frontmatter to mark them, you probably need a configuration like this to display them in /star/
path with StarList
layout:
import { blogPlugin } from '@vuepress/plugin-blog'
+
+export default {
+ name: 'vuepress-theme-xxx' ,
+ plugins: [
+ blogPlugin ({
+ // other options ...
+ type: [
+ {
+ key: 'star' ,
+ filter : ({ frontmatter }) => frontmatter . star ,
+ path: '/star/' ,
+ layout: 'StarList' ,
+ frontmatter : () => ({ title: 'Star page' }),
+ },
+ ],
+ }),
+ // other plugins ...
+ ],
+}
+
`,18),y=p(`When generating each page, the plugin will set following information under frontmatter.blog
:
interface BlogFrontmatterOptions {
+ /** Current type of the page */
+ type : 'category' | 'type'
+ /** Unique key under current category or tag */
+ key : string
+ /**
+ * Current category name
+ *
+ * @description Only available in category item page
+ */
+ name ?: string
+}
+
So you can invoke useBlogCategory()
and useBlogType()
directly, and the result will be the category or type bind to current route.
Also, you can pass key
you want as argument, then you will get information bind to that key.
So with node side settings above, you can get information about "tag" and "star" in client side:
TagMap
layout:
< script setup lang = "ts" >
+import { useBlogCategory } from '@vuepress/plugin-blog'
+import { RouteLink } from 'vuepress/client'
+
+const categoryMap = useBlogCategory ( 'tag' )
+</ script >
+
+< template >
+ < div >
+ < h1 > Tag page </ h1 >
+ < ul >
+ < li v-for = " ({ items , path }, name ) in categoryMap . map " >
+ < RouteLink : key = " name " : to = " path " class = "category" >
+ {{ name }}
+ < span class = "category-num" >
+ {{ items . length }}
+ </ span >
+ </ RouteLink >
+ </ li >
+ </ ul >
+ </ div >
+</ template >
+
TagList
layout:
< script setup lang = "ts" >
+import { useBlogCategory } from '@vuepress/plugin-blog'
+
+const categoryMap = useBlogCategory ( 'tag' )
+</ script >
+
+< template >
+ < div >
+ < h1 > Tag page </ h1 >
+ < div class = "category-wrapper" >
+ < RouteLink
+ v-for = " ({ items , path }, name ) in categoryMap . map "
+ : key = " name "
+ : to = " path "
+ class = "category"
+ >
+ {{ name }}
+ < span class = "category-num" >
+ {{ items . length }}
+ </ span >
+ </ RouteLink >
+ </ div >
+ < div class = "article-wrapper" v-if = " categoryMap . currentItems " >
+ < div v-if = " ! categoryMap . currentItems . length " > Nothing in here. </ div >
+ < article
+ v-for = " { info , path } in categoryMap . currentItems "
+ class = "article"
+ @ click = " $router . push ( path ) "
+ >
+ < header class = "title" >
+ {{
+ ( isTimeline
+ ? \` \${ new Date ( info . date ). toLocaleDateString () } : \`
+ : '' ) + info . title
+ }}
+ </ header >
+ < hr />
+ < div class = "article-info" >
+ < span v-if = " info . author " class = "author"
+ > Author: {{ info . author }} </ span
+ >
+ < span v-if = " info . date && ! isTimeline " class = "date"
+ > Date: {{ new Date ( info . date ). toLocaleDateString () }} </ span
+ >
+ < span v-if = " info . category " class = "category"
+ > Category: {{ info . category . join ( ', ' ) }} </ span
+ >
+ < span v-if = " info . tag " class = "tag"
+ > Tag: {{ info . tag . join ( ', ' ) }} </ span
+ >
+ </ div >
+ < div v-if = " info . excerpt " class = "excerpt" v-html = " info . excerpt " / >
+ </ article >
+ </ div >
+ </ div >
+</ template >
+
StarList
layout:
< script setup lang = "ts" >
+import { useBlogType } from '@vuepress/plugin-blog/client'
+import { RouteLink } from 'vuepress/client'
+
+import ArticleList from '../components/ArticleList.vue'
+import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
+
+const stars = useBlogType ( 'star' )
+</ script >
+
+< template >
+ < div class = "article-wrapper" v-if = " stars . items " >
+ < div v-if = " ! stars . items . length " > Nothing in here. </ div >
+ < article
+ v-for = " { info , path } in stars . items "
+ class = "article"
+ @ click = " $router . push ( path ) "
+ >
+ < header class = "title" >
+ {{
+ ( isTimeline ? \` \${ new Date ( info . date ). toLocaleDateString () } : \` : '' ) +
+ info . title
+ }}
+ </ header >
+ < hr />
+ < div class = "article-info" >
+ < span v-if = " info . author " class = "author" > Author: {{ info . author }} </ span >
+ < span v-if = " info . date && ! isTimeline " class = "date"
+ > Date: {{ new Date ( info . date ). toLocaleDateString () }} </ span
+ >
+ < span v-if = " info . category " class = "category"
+ > Category: {{ info . category . join ( ', ' ) }} </ span
+ >
+ < span v-if = " info . tag " class = "tag" > Tag: {{ info . tag . join ( ', ' ) }} </ span >
+ </ div >
+ < div v-if = " info . excerpt " class = "excerpt" v-html = " info . excerpt " / >
+ </ article >
+ </ div >
+</ template >
+
`,12),d=p(`This plugin adds native i18n support, so your settings will be automatically applied to each language.
For example, if user has the following locales' config, and you are setting the "star" example above:
export default {
+ locales: {
+ '/' : {
+ lang: 'en-US' ,
+ },
+ '/zh/' : {
+ lang: 'zh-CN' ,
+ },
+ },
+}
+
Then /zh/star/
and /star/
will both be available, and only articles under the correct locale will appear.
This plugin provides a built-in excerpt generator, which can be enabled by setting excerpt
option to true
.
Excerpt introduction
An excerpt is an HTML fragment that is used to display a short description of an article in the blog list, so the excerpt has the following restrictions:
It doesn't support any unknown tags (including all Vue components) and Vue syntax, so these contents will be removed when generating. If you have custom components (non-Vue components), set isCustomElement
option. Since the snippet is an HTML fragment, you will not be able to import any images via relative paths or aliases, they will be removed directly. If you want to keep images, please use absolute path based on .vuepress/public
or full URL to ensure they can be accessed in other places. The excerpt generator will try to find a valid excerpt separator from markdown contents, if it finds one, it will use content before the separator. The separator is default <!-- more -->
, and you can customize it by setting excerptSeparator
option.
If it cannot find a valid separator, it will parse content from the beginning of markdown file, and stop till its length reaches a preset value. The value is default 300
, and you can customize it by setting excerptLength
option.
To choose which page should generate excerpt, you can use excerptFilter
option.
Example
Normally you may want to use frontmatter.description
if users set them, so you can let filter function return false
if frontmatter.description
is not empty.
`,12);function C(u,v){const n=t("RouteLink");return c(),r("div",null,[D,o("p",null,[s("See, setting these 2 types is easy. For full options, please see "),a(n,{to:"/plugins/blog/config.html#blog-category-config"},{default:l(()=>[s("Category Config")]),_:1}),s(" and "),a(n,{to:"/plugins/blog/config.html#blog-type-config"},{default:l(()=>[s("Type Config")]),_:1}),s(".")]),y,o("p",null,[s("For return types, please see "),a(n,{to:"/plugins/blog/config.html#composition-api"},{default:l(()=>[s("Composition API Return Types")]),_:1}),s(".")]),d])}const g=e(i,[["render",C],["__file","guide.html.vue"]]),b=JSON.parse('{"path":"/plugins/blog/guide.html","title":"Guide","lang":"en-US","frontmatter":{"title":"Guide","icon":"lightbulb"},"headers":[{"level":2,"title":"Collecting Articles","slug":"collecting-articles","link":"#collecting-articles","children":[]},{"level":2,"title":"Gathering Info","slug":"gathering-info","link":"#gathering-info","children":[]},{"level":2,"title":"Customizing Categories and Types","slug":"customizing-categories-and-types","link":"#customizing-categories-and-types","children":[]},{"level":2,"title":"Using Composition API in Client-side","slug":"using-composition-api-in-client-side","link":"#using-composition-api-in-client-side","children":[]},{"level":2,"title":"I18n Support","slug":"i18n-support","link":"#i18n-support","children":[]},{"level":2,"title":"Generating Excerpt","slug":"generating-excerpt","link":"#generating-excerpt","children":[]}],"git":{"updatedTime":1708365679000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/blog/guide.md"}');export{g as comp,b as data};
diff --git a/assets/guide.html-B28TH4EC.js b/assets/guide.html-B28TH4EC.js
new file mode 100644
index 0000000000..518c6b63c6
--- /dev/null
+++ b/assets/guide.html-B28TH4EC.js
@@ -0,0 +1,9 @@
+import{_ as e,o as s,c as a,e as l}from"./app-BcH8wZQx.js";const n={},o=l(`本插件会为你的网站自动生成 Sitemap。为了使插件正常工作,你需要将部署的域名传递给插件的 hostname
选项。如果你想在开发服务器中预览,请配置 devServer
选项。
插件会自动根据页面的 Git 的时间戳生成页面的最后更新时间,同时会根据站点的多语言配置声明页面的其他语言版本替代地址。
默认情况下,所有除 404 页面以外的网站链接均会被添加进 Sitemap。
如果你希望在 VuePress 项目页面之外,添加其他页面链接到 Sitemap,请将它们变成数组传入插件的 extraUrls
选项。
如果你需要排除一些页面路径,你可以将它们变成数组传入到插件的 excludePaths
选项。你也可以在对应页面的 frontmatter 中,设置 sitemap
为 false
。
你还可以通过插件的 sitemapFilename
选项控制输出的地址,此地址相对于输出目录,默认为 sitemap.xml
。
页面默认的更新周期是 daily
(每天),如果你希望修改全部的页面周期,请在插件选项中设置 changefreq
。你也可以在页面的 frontmatter 中设置 sitemap.changefreq
,页面具有更高的优先级。
合法的频率有:
"always"
"hourly"
"daily"
"weekly"
"monthly"
"yearly"
"never"
你可以在插件中设置 priority
以提供一个默认值。同时你可以通过 frontmatter 中的 sitemap.priority
来为每个页面设置优先级。可接受的值为 0
到 1
的浮点数。
你可以通过插件的 modifyTimeGetter
来返回一个 ISO 字符串格式的时间,默认会通过 Git 插件生成。
以下是一个基于文件最后修改时间的例子。
// 基于文件最后修改时间
+({
+ modifyTimeGetter : ( page , app ) =>
+ fs . statSync ( app . dir . source ( page . filePathRelative )). mtime . toISOString ();
+})
+
网站地图 (Sitemap) 提供搜索引擎优化 (SEO):
为搜索引擎爬虫提供可以浏览整个网站的链接; 为搜索引擎爬虫提供一些链接,指向动态页面或者采用其他方法比较难以到达的页面; 如果访问者试图访问网站所在域内并不存在的 URL,那么这个访问者就会被转到“无法找到文件”的错误页面,而网站地图可以作为导航页。 网站地图通过使所有页面可被找到来增强搜索引擎优化的效果。
大部分搜索引擎只跟踪页面内有限数量的链接,因此当网站非常大的时候,网站地图对于使搜索引擎和访问者可以访问网站中的所有内容就变得必不可少了。
Sitemaps 是站点管理员向搜索引擎爬虫公布站点可被抓取页面的协议,sitemap 文件内容必须遵循 XML 格式的定义。每个 URL 可以包含更新的周期和时间、URL 在整个站点中的优先级。这样可以让搜索引擎更佳有效的抓取网站内容。
同步配置 robots.txt
由于 Sitemap 面向搜索引擎,配合此插件使用时,你最好保证你在 .vuepress/public
文件夹下放置了有效的 robots.txt
,以允许搜索引擎收录。一个最简单的 robots.txt 如下 (允许所有搜索引擎访问所有路径)
User-agent: *
+
+Allow: /
+
`,26),t=[o];function p(i,c){return s(),a("div",null,t)}const r=e(n,[["render",p],["__file","guide.html.vue"]]),m=JSON.parse('{"path":"/zh/plugins/sitemap/guide.html","title":"指南","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"控制 Sitemap 链接","slug":"控制-sitemap-链接","link":"#控制-sitemap-链接","children":[]},{"level":2,"title":"输出位置","slug":"输出位置","link":"#输出位置","children":[]},{"level":2,"title":"更新周期","slug":"更新周期","link":"#更新周期","children":[]},{"level":2,"title":"优先级","slug":"优先级","link":"#优先级","children":[]},{"level":2,"title":"修改时间获取","slug":"修改时间获取","link":"#修改时间获取","children":[]},{"level":2,"title":"Sitemap 介绍","slug":"sitemap-介绍","link":"#sitemap-介绍","children":[]}],"git":{"updatedTime":1706723991000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/sitemap/guide.md"}');export{r as comp,m as data};
diff --git a/assets/guide.html-B9bfJpxN.js b/assets/guide.html-B9bfJpxN.js
new file mode 100644
index 0000000000..c56efba6ca
--- /dev/null
+++ b/assets/guide.html-B9bfJpxN.js
@@ -0,0 +1 @@
+import{_ as s,r as a,o as r,c as i,b as e,d as t,a as l,w as n,e as c}from"./app-BcH8wZQx.js";const h={},p=c('插件可为你生成以下三种格式的 feed 文件:
请按照需要生成的格式,在插件选项中设置 atom
, json
或 rss
为 true
。
为了正确生成 Feed 链接,你需要在插件选项中设置 hostname
。
',7),u={href:"/zh/atom.xml",target:"_blank",rel:"noopener noreferrer"},_={href:"/zh/rss.xml",target:"_blank",rel:"noopener noreferrer"},f=c('如果你想在开发服务器中预览 Feed,你需要在插件选项中设置 devServer: true
。如果你没有使用默认的 http://localhost:{port}
,你还需要设置 devHostname
。
你可以通过设置 channel
选项来自自定义 Feed 频道的各项信息。
我们推荐进行如下设置:
将建立 Feed 的日期转换为 ISOString 写入到 channel.pubDate
中 通过 channel.ttl
中设置内容的更新周期(单位: 分钟) 通过 channel.copyright
设置版权信息 通过 channel.author
设置频道作者。 ',5),m=e("h2",{id:"feed-生成",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#feed-生成"},[e("span",null,"Feed 生成")])],-1),g=e("p",null,"默认情况下,所有文章均会被添加至 feed 流。",-1),x=e("code",null,"feed",-1),k=e("code",null,"getter",-1),v=e("h3",{id:"多语言配置",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#多语言配置"},[e("span",null,"多语言配置")])],-1),F=e("p",null,"插件会针对每个语言生成单独的 Feed。",-1),b=e("p",null,[t("你可以通过插件选项中的 "),e("code",null,"locales"),t(" 分别对不同语言提供不同的默认设置。")],-1);function z(S,N){const d=a("ExternalLinkIcon"),o=a("RouteLink");return r(),i("div",null,[p,e("p",null,[t("当你在浏览器中打开 Feed 文件时,我们会通过 xsl 模板将 atom 和 rss feed xml 魔法般地转换为可读的 html。你可以查看本站的 "),e("a",u,[t("atom"),l(d)]),t(" 和 "),e("a",_,[t("rss"),l(d)]),t(" feed 作为案例!")]),f,e("p",null,[t("详细的选项及其默认值详见 "),l(o,{to:"/zh/plugins/feed/channel.html"},{default:n(()=>[t("配置 → 频道设置")]),_:1})]),m,g,e("p",null,[t("你可以在 frontmatter 中配置 "),x,t(" 和其他选项控制每个页面的 Feed 项目内容,详见 "),l(o,{to:"/zh/plugins/feed/frontmatter.html"},{default:n(()=>[t("Frontmatter 选项")]),_:1}),t(" 了解它们如何被转换。")]),e("p",null,[t("你可以通过配置插件选项中的 "),k,t(" 完全控制 Feed 项目的生成逻辑。 详细的选项及其默认值详见 "),l(o,{to:"/zh/plugins/feed/getter.html"},{default:n(()=>[t("配置 → Feed 获取器")]),_:1})]),v,F,b])}const R=s(h,[["render",z],["__file","guide.html.vue"]]),V=JSON.parse('{"path":"/zh/plugins/feed/guide.html","title":"指南","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]},{"level":2,"title":"可读的预览","slug":"可读的预览","link":"#可读的预览","children":[]},{"level":2,"title":"频道设置","slug":"频道设置","link":"#频道设置","children":[]},{"level":2,"title":"Feed 生成","slug":"feed-生成","link":"#feed-生成","children":[{"level":3,"title":"多语言配置","slug":"多语言配置","link":"#多语言配置","children":[]}]}],"git":{"updatedTime":1706674769000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/feed/guide.md"}');export{R as comp,V as data};
diff --git a/assets/guide.html-BYBHO6SP.js b/assets/guide.html-BYBHO6SP.js
new file mode 100644
index 0000000000..c15a0543aa
--- /dev/null
+++ b/assets/guide.html-BYBHO6SP.js
@@ -0,0 +1,9 @@
+import{_ as e,o as t,c as a,e as s}from"./app-BcH8wZQx.js";const i={},o=s(`This plugin will automatically generate a Sitemap for your site. To let this plugin work, you need to pass the deployed domain name to the hostname
option of the plugin. If you want to preview in devServer, set devServer
options.
The plugin will automatically generate the last update time of the page based on the Git timestamp of the page, and will also declare the alternative links of the page in other languages according to the locales' config.
By default, all site links except 404 page will be added to the Sitemap.
To add other pages to the Sitemap outside the VuePress project page, please turn them into an array and pass to the extraUrls
plugin option.
If you don't want certain pages to appear in the sitemap, you can turn their paths into an array and pass to the excludePaths
plugin option, or set sitemap
to false
in the frontmatter of the corresponding page.
You can also control the output link through the sitemapFilename
option of the plugin, the link is relative to output directory. By default, the plugin will use sitemap.xml
.
The default update cycle of the page is daily
(every day). To modify the entire page cycle, please set changefreq
in the plugin options. You can also set sitemap.changefreq
in the frontmatter of the page. Note that page has a higher priority.
The legal frequencies are:
"always"
"hourly"
"daily"
"weekly"
"monthly"
"yearly"
"never"
You can set priority
in the plugin to provide a default value. At the same time you can set the priority for each page through sitemap.priority
in frontmatter. Acceptable values are floating point numbers from 0
to 1
.
You can use option modifyTimeGetter
to return a time in ISO string format, which is generated by the Git plugin by default.
The following is an example based on the last modification time of a file.
// Based on file last modified time
+({
+ modifyTimeGetter : ( page , app ) =>
+ fs . statSync ( app . dir . source ( page . filePathRelative )). mtime . toISOString ();
+})
+
Sitemaps provide SEO (Search Engine Optimization):
Provide search engine spiders with links of the entire site; Provide some links for search engine spiders to dynamic pages or pages that are difficult to reach by other methods; If a visitor attempts to access a URL that does not exist within the site's domain, the visitor will be directed to a "file not found" error page, and the sitemap can be used as a navigation page. A sitemap enhances SEO by making all pages findable.
Most search engines only follow a limited number of links within a page, so when the site is very large, a sitemap becomes essential to make everything on the site accessible to search engines and visitors.
Sitemaps is a protocol for site administrators to publish pages that can be crawled on a site to search engine spiders. The content of sitemap files must follow the definition in XML format. Each URL can contain the update period and last update time, the priority of the URL across the site. This allows search engines to crawl site content better and more efficiently.
Together with robots.txt
Sitemap is basically used by search engines, when using this plugin, you'd better ensure that you have a valid robots.txt
in the .vuepress/public
directory to allow search engines spiders to visit your site. The simplest robots.txt is as follows (allow all search engines to access all paths)
User-agent: *
+
+Allow: /
+
`,26),n=[o];function l(p,r){return t(),a("div",null,n)}const d=e(i,[["render",l],["__file","guide.html.vue"]]),h=JSON.parse('{"path":"/plugins/sitemap/guide.html","title":"Guide","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Control Sitemap Link","slug":"control-sitemap-link","link":"#control-sitemap-link","children":[]},{"level":2,"title":"Output Location","slug":"output-location","link":"#output-location","children":[]},{"level":2,"title":"Change Frequency","slug":"change-frequency","link":"#change-frequency","children":[]},{"level":2,"title":"Priority","slug":"priority","link":"#priority","children":[]},{"level":2,"title":"Modify Time","slug":"modify-time","link":"#modify-time","children":[]},{"level":2,"title":"Sitemap Intro","slug":"sitemap-intro","link":"#sitemap-intro","children":[]}],"git":{"updatedTime":1706723991000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/sitemap/guide.md"}');export{d as comp,h as data};
diff --git a/assets/guide.html-BrsCyCB1.js b/assets/guide.html-BrsCyCB1.js
new file mode 100644
index 0000000000..dbc1e5a502
--- /dev/null
+++ b/assets/guide.html-BrsCyCB1.js
@@ -0,0 +1,23 @@
+import{_ as r,r as l,o as c,c as p,b as e,d as s,a as o,w as i,e as n}from"./app-BcH8wZQx.js";const d={},h=n('Make your VuePress site a Progressive Web Application (PWA).
',3),u={href:"https://developers.google.com/web/tools/workbox/modules/workbox-build",target:"_blank",rel:"noopener noreferrer"},f={href:"https://github.com/yyx990803/register-service-worker",target:"_blank",rel:"noopener noreferrer"},g={class:"custom-container warning"},y=e("p",{class:"custom-container-title"},"WARNING",-1),m=n(`A PWA uses a Service Worker (SW for short) to cache and proxy site content.
To make your website fully compliant with PWA, a Web app manifests file is needed, and your pwa should satisfy the installability specification.
You can set manifest
option to customize the manifest file, or provide a manifest.webmanifest
or manifest.json
in public folder. The former has higher priority.
The plugin will automatically generate manifest.webmanifest
for you and add manifest link declaration in each page, while you should still at least set a valid icon through manifest.icons
or other icon related options in the PWA plugin.
WARNING
The installability specification requires at least one valid icon to be declared in the manifest.
So if you do not configure manifest.icons
, visitors can only enjoy the offline accessibility brought by the Service Worker cache, while cannot install your site as a PWA.
Besides the plugin does not process anything in the manifest by default, but outputs them as-is. This means that if you plan to deploy to a subdirectory, you should append the URL prefix to manifest Urls yourself. If everything you need is all under base
directory, you can set appendBase: true
in plugin options to let the plugin append base
to any links in manifest.
To better control what the Service Worker can pre-cache, the plugin provides related options for cache control.
By default, the plugin will pre-cache all js
css
files, and only homepage and 404 html are cached. The plugin will also cache font files (woff, woff2, eot, ttf, otf) and SVG icons.
If your site has only a few important images, and want them displayed in offline mode, you can cache site images by setting cacheImage: true
.
We recognize images by file extension. Any files ending with .png
, .jpg
, .jpeg
, .gif
, .bmp
, .webp
will be regarded as images.
If you have small sites, and would like to make document fully offline available, you can set cacheHTML
to true
to cache all HTML files.
Why only home and 404 page been cached by default?
Though VuePress generates HTML files through SSG for all pages, these files are mainly used for SEO and allow you to directly configure the backend without SPA Visit any link.
VuePress is essentially an SPA. This means that you only need to cache the home page and enter from the home page to access all pages normally. Therefore, not caching other HTML by default can effectively reduce the cache size (40% smaller in size) and speed up the SW update speed.
But this also has the disadvantage. If the user enters the site directly from a non-home page, the HTML file for the first page still needs to be loaded from the internet. Also, in offline environment, users can only enter through the homepage and then navigate to the corresponding page by themselves. If they directly access a link, an inaccessible prompt will appear.
To prevent large files from being included in the pre-cache list, any files > 2 MB or images > 1 MB will be omitted. You can customize these limits with maxSize
and maxImageSize
options (in KB unit).
We provide the update
option to control how users receive updates.
The default value of the update
option is "available"
, which means that when new content available, the new SW will be installed and its resources will be fetched silently in the background. A pop-up window appears once the new SW is ready, and users can choose whether to refresh immediately to view new content. This means users are reading old content before a new SW is ready.
If your project is still in building stage, and you want to alert the user that he may be reading outdated content, you can set this to "hint"
. This allows users to be notified that new content has been published within seconds after visiting docs. But the negative effect of this is that if the user chooses to update before the new SW is ready, he will need to get all the resources of the page from the internet before the new SW installs and controls the page.
If your docs are stable, or you're hosting a blog and don't care much about users receiving the latest version right away, you can set this to "disabled"
, which means that the new SW will be installed completely silently in the background and start waiting, when all pages controlled by old SW are all closed, the new SW will start to take control and provide users with new content during next visit. This setting can prevent users from being disturbed during the visit.
To speed up user access under weak or no network conditions through SW, but also want users to always access new content, you can set this option to "force"
. This means any old SW will be removed as soon as a new SW is detected, and all pages are refreshed to ensure the user is browsing the latest content. The biggest disadvantage is that all users will experience unexpected sudden refresh within seconds after reentering an updated site.
When new content is detected (new SW detected), a update found popup appears; and when the new content is ready, an update ready popup appears.
If you are not satisfied with the default popup content, you can use your own component. Import PwaFoundPopup
or PwaReadyPopup
from @vuepress/plugin-pwa/client
and use its slot to customize the popup content, then pass the component path to foundComponent
or readyComponent
option:
< script setup lang = "ts" >
+import { PwaFoundPopup } from '@vuepress/plugin-pwa/client'
+</ script >
+< template >
+ < PwaFoundPopup v-slot = " { found , refresh } " >
+ < div v-if = " found " >
+ New content is found.
+ < button @ click = " refresh " > Refresh </ button >
+ </ div >
+ </ PwaFoundPopup >
+</ template >
+
< script setup lang = "ts" >
+import { PwaReadyPopup } from '@vuepress/plugin-pwa/client'
+</ script >
+< template >
+ < PwaReadyPopup v-slot = " { isReady , reload } " >
+ < div v-if = " isReady " >
+ New content is ready.
+ < button @ click = " reload " > Apply </ button >
+ </ div >
+ </ PwaReadyPopup >
+</ template >
+
`,31),b=e("code",null,"generateSwConfig",-1),v=e("code",null,"workbox-build",-1),D=e("h2",{id:"further-reading",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#further-reading"},[e("span",null,"Further Reading")])],-1),w=e("p",null,"For more details, please see:",-1),_={href:"https://web.dev/progressive-web-apps/",target:"_blank",rel:"noopener noreferrer"},C={href:"https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps",target:"_blank",rel:"noopener noreferrer"},k={href:"https://w3c.github.io/manifest/",target:"_blank",rel:"noopener noreferrer"},W=e("hr",{class:"footnotes-sep"},null,-1),S={class:"footnotes"},P={class:"footnotes-list"},x={id:"footnote1",class:"footnote-item"},E=e("p",null,[e("strong",null,"PWA introduction")],-1),T=e("p",null,"PWA, full name Progressive Web app. PWA standard is stipulated by W3C.",-1),A=e("p",null,"It allows sites to install the site as an App on supported platform through a browser that supports this feature.",-1),q={href:"https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps",target:"_blank",rel:"noopener noreferrer"},F=e("a",{href:"#footnote-ref1",class:"footnote-backref"},"↩︎",-1),I=n('',1),M={id:"footnote3",class:"footnote-item"},z=e("p",null,[e("strong",null,"Manifest File")],-1),R=e("p",null,"The manifest file uses the JSON format and is responsible for declaring various information of the PWA, such as name, description, icon, and shortcut actions.",-1),N=e("p",null,"In order for your site to be registered as a PWA, you need to meet the basic specifications of the manifest to make the browser consider the site as an installable PWA and allow users to install it.",-1),B={class:"custom-container tip"},L=e("p",{class:"custom-container-title"},"TIP",-1),O={href:"https://developer.mozilla.org/en-US/docs/Web/Manifest",target:"_blank",rel:"noopener noreferrer"},G={href:"https://w3c.github.io/manifest/",target:"_blank",rel:"noopener noreferrer"},U=e("a",{href:"#footnote-ref3",class:"footnote-backref"},"↩︎",-1),V=n('',4);function H(j,Y){const t=l("ExternalLinkIcon"),a=l("RouteLink");return c(),p("div",null,[h,e("p",null,[s("This plugin uses "),e("a",u,[s("workbox-build"),o(t)]),s(" to generate service worker file, and uses "),e("a",f,[s("register-service-worker"),o(t)]),s(" to register service worker.")]),e("div",g,[y,e("p",null,[s("If you enabled this plugin once and you want to disable it, you might need "),o(a,{to:"/plugins/remove-pwa.html"},{default:i(()=>[s("`@vuepress/plugin-remove-pwa")]),_:1}),s(" to remove the existing service worker.")])]),m,e("p",null,[s("The plugin also provides other PWA-related options, such as Microsoft tile icon and color settings, Apple icon and so on. If you are an advanced user, you can also set "),b,s(" to configure "),v,s(". Check "),o(a,{to:"/plugins/pwa/config.html#options"},{default:i(()=>[s("Plugin options")]),_:1}),s(" for more details.")]),D,w,e("ul",null,[e("li",null,[e("a",_,[s("Google PWA"),o(t)])]),e("li",null,[e("a",C,[s("MDN PWA"),o(t)])]),e("li",null,[e("a",k,[s("W3C Manifest Specification"),o(t)])])]),W,e("section",S,[e("ol",P,[e("li",x,[E,T,A,e("p",null,[s("See "),e("a",q,[s("https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps"),o(t)]),s(" for details. "),F])]),I,e("li",M,[z,R,N,e("div",B,[L,e("p",null,[s("For Manifest standards and specifications, please see "),e("a",O,[s("MDN Web app manifests"),o(t)]),s(" and "),e("a",G,[s("W3C Manifest"),o(t)]),s(".")])]),U]),V])])])}const K=r(d,[["render",H],["__file","guide.html.vue"]]),Q=JSON.parse('{"path":"/plugins/pwa/guide.html","title":"Guide","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Intro","slug":"intro","link":"#intro","children":[]},{"level":2,"title":"Web App Manifests","slug":"web-app-manifests","link":"#web-app-manifests","children":[]},{"level":2,"title":"Cache Control","slug":"cache-control","link":"#cache-control","children":[{"level":3,"title":"Default cache","slug":"default-cache","link":"#default-cache","children":[]},{"level":3,"title":"Image Cache","slug":"image-cache","link":"#image-cache","children":[]},{"level":3,"title":"HTML Cache","slug":"html-cache","link":"#html-cache","children":[]},{"level":3,"title":"Size Control","slug":"size-control","link":"#size-control","children":[]}]},{"level":2,"title":"Update Control","slug":"update-control","link":"#update-control","children":[{"level":3,"title":"Popups","slug":"popups","link":"#popups","children":[]}]},{"level":2,"title":"Other Options","slug":"other-options","link":"#other-options","children":[]},{"level":2,"title":"Further Reading","slug":"further-reading","link":"#further-reading","children":[]}],"git":{"updatedTime":1708397875000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/pwa/guide.md"}');export{K as comp,Q as data};
diff --git a/assets/guide.html-CWAzJQUt.js b/assets/guide.html-CWAzJQUt.js
new file mode 100644
index 0000000000..1069668718
--- /dev/null
+++ b/assets/guide.html-CWAzJQUt.js
@@ -0,0 +1 @@
+import{_ as s,r as l,o as d,c,b as e,d as t,a as n,w as a,e as r}from"./app-BcH8wZQx.js";const h={},u=r('The plugin can generate feed files in the following three formats for you:
Please set atom
, json
or rss
to true
in the plugin options according to the formats you want to generate.
To correctly generate feed links, you need to set hostname
in the plugin options,
',7),f={href:"/atom.xml",target:"_blank",rel:"noopener noreferrer"},p={href:"/rss.xml",target:"_blank",rel:"noopener noreferrer"},g=r('If you want to preview your feed in devServer, set devServer: true
in plugin options. You may also need to set devHostname
if you are not using the default http://localhost:{port}
.
You can customize the feed channel information by setting the channel
option.
We recommend the following settings:
Convert the date of creating the feed to ISOString and write it into channel.pubDate
The update period of the content set in channel.ttl
(unit: minutes) Set copyright information via channel.copyright
Set the channel author via channel.author
. ',5),m=e("h2",{id:"feed-generation",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#feed-generation"},[e("span",null,"Feed Generation")])],-1),_=e("p",null,"By default, all articles are added to the feed stream.",-1),v=e("code",null,"feed",-1),b=e("code",null,"getter",-1),x=e("h3",{id:"i18n-config",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#i18n-config"},[e("span",null,"I18n Config")])],-1),k=e("p",null,"The plugin generates separate feeds for each language.",-1),w=e("p",null,[t("You can provide different settings for different languages via "),e("code",null,"locales"),t(" in the plugin options.")],-1);function y(S,C){const i=l("ExternalLinkIcon"),o=l("RouteLink");return d(),c("div",null,[u,e("p",null,[t("When you open the feed file in browser, we magically convert atom and rss feed xml to human readable html via xsl template. Check "),e("a",f,[t("atom"),n(i)]),t(" and "),e("a",p,[t("rss"),n(i)]),t(" feed of this site as an example!")]),g,e("p",null,[t("For detailed options and their default values, see "),n(o,{to:"/plugins/feed/channel.html"},{default:a(()=>[t("Channel Config")]),_:1})]),m,_,e("p",null,[t("You can set "),v,t(" and other options in page frontmatter to control contents of feed item. See "),n(o,{to:"/plugins/feed/frontmatter.html"},{default:a(()=>[t("Frontmatter Config")]),_:1}),t(" for how they are converted.")]),e("p",null,[t("You can take full control of feed items generation by configuring the "),b,t(" in the plugin options. For detailed options and their default values, see "),n(o,{to:"/plugins/feed/getter.html"},{default:a(()=>[t("Configuration → Feed Getter")]),_:1}),t(".")]),x,k,w])}const I=s(h,[["render",y],["__file","guide.html.vue"]]),N=JSON.parse('{"path":"/plugins/feed/guide.html","title":"Guide","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Readable Preview","slug":"readable-preview","link":"#readable-preview","children":[]},{"level":2,"title":"Channel settings","slug":"channel-settings","link":"#channel-settings","children":[]},{"level":2,"title":"Feed Generation","slug":"feed-generation","link":"#feed-generation","children":[{"level":3,"title":"I18n Config","slug":"i18n-config","link":"#i18n-config","children":[]}]}],"git":{"updatedTime":1706674769000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/feed/guide.md"}');export{I as comp,N as data};
diff --git a/assets/guide.html-DBN010b_.js b/assets/guide.html-DBN010b_.js
new file mode 100644
index 0000000000..f705faeddf
--- /dev/null
+++ b/assets/guide.html-DBN010b_.js
@@ -0,0 +1,215 @@
+import{_ as e,r as t,o as c,c as r,b as o,d as s,a,w as l,e as p}from"./app-BcH8wZQx.js";const D={},i=p(`使用 @vuepress/plugin-blog
,你可以轻松地将博客功能引入主题。
起步时,插件会首选过滤并选择那些需要作为文章的页面。这将剔除你不想要的页面,并在后续处理中排除它们。
默认情况下,所有从 Markdown 文件生成但不是主页的页面,都将被视作文章。
你可能需要设置 filter
选项来完全自定义要收集的页面。 filter
接受一个形状为 (page: Page) => boolean
的函数。
接着,你应该设置 getInfo
选项为一个接受 Page
作为参数并返回包含所需信息的对象的函数。这样稍后,你可以从组合 API 中获取这些信息。
案例 import { blogPlugin } from '@vuepress/plugin-blog'
+
+export default {
+ name: 'vuepress-theme-xxx' ,
+ plugins: [
+ blogPlugin ({
+ filter : ({ filePathRelative , frontmatter }) => {
+ // 舍弃那些不是从 Markdown 文件生成的页面
+ if (! filePathRelative ) return false
+
+ // 舍弃 \`archives\` 文件夹的页面
+ if ( filePathRelative . startsWith ( 'archives/' )) return false
+
+ // 舍弃那些没有使用默认布局的页面
+ if ( frontmatter . home || frontmatter . layout ) return false
+
+ return true
+ },
+
+ getInfo : ({ frontmatter , git = {}, data = {} }) => {
+ // 获取页面信息
+ const info : Record < string , any > = {
+ author: frontmatter . author || '' ,
+ categories: frontmatter . categories || [],
+ date: frontmatter . date || git . createdTime || null ,
+ tags: frontmatter . tags || [],
+ excerpt: data . excerpt || '' ,
+ }
+
+ return info
+ },
+ }),
+ // 其他插件 ...
+ ],
+}
+
基本上,你的博客中需要两种“类型”:
了解这两种类型的描述后,你可以设置 category
和 type
选项,它们都接受一个数组,每个元素代表一个配置。
让我们从此处 2 个例子开始。
假设你想为每篇文章设置标签,并且你正在通过 frontmatter.tag
设置它们。同时,你想要在 /tag/
中使用 TagMap
布局的标签页面,并在/tag/标签名称
中使用 TagList
布局对标签按名称进行分组,你可能需要这样的配置:
import { blogPlugin } from '@vuepress/plugin-blog'
+
+export default {
+ name: 'vuepress-theme-xxx' ,
+ plugins: [
+ blogPlugin ({
+ // 其他配置 ...
+ category: [
+ {
+ key: 'tag' ,
+ getter : ({ frontmatter }) => frontmatter . tag || [],
+ path: '/tag/' ,
+ layout: 'TagMap' ,
+ frontmatter : () => ({ title: '标签页' }),
+ itemPath: '/tag/:name/' ,
+ itemLayout: 'TagList' ,
+ itemFrontmatter : ( name ) => ({ title: \` \${ name } 标签\` }),
+ },
+ ],
+ }),
+ // 其他插件 ...
+ ],
+}
+
此外,你可能希望为你的一些文章加注星标,并将其展示给访问者。当你在 frontmatter 中设置 star: true
来标记它们时,你可能需要这样的配置来在 /star/
路径中以 StarList
布局显示它们:
import { blogPlugin } from '@vuepress/plugin-blog'
+
+export default {
+ name: 'vuepress-theme-xxx' ,
+ plugins: [
+ blogPlugin ({
+ // 其他配置 ...
+ type: [
+ {
+ key: 'star' ,
+ filter : ({ frontmatter }) => frontmatter . star ,
+ path: '/star/' ,
+ layout: 'StarList' ,
+ frontmatter : () => ({ title: '星标文章' }),
+ },
+ ],
+ }),
+ // 其他插件 ...
+ ],
+}
+
`,16),y=p(`当生成每个页面时,插件将在 frontmatter.blog
中设置如下信息
interface BlogFrontmatterOptions {
+ /** 当前页面的类型 */
+ type : 'category' | 'type'
+ /** 在当前分类或类别下全局唯一的 key */
+ key : string
+ /**
+ * 当前的分类名称
+ *
+ * @description 仅在分类子项目页面中可用
+ */
+ name ?: string
+}
+
所以你可以直接调用 useBlogCategory()
和 useBlogType()
,结果将是当前路由绑定的类别或类型。
此外,你可以通过传递所需的 key
作为参数,来将获得绑定到该 key
的信息。
对于上方的 Node 配置而言,你可以在客户端通过如下方式获取 tag 和 star 的信息:
TagMap
布局:
< template >
+ < div >
+ < h1 > Tag page </ h1 >
+ < ul >
+ < li v-for = " ({ items , path }, name ) in categoryMap . map " >
+ < RouteLink : key = " name " : to = " path " class = "category" >
+ {{ name }}
+ < span class = "category-num" >
+ {{ items . length }}
+ </ span >
+ </ RouteLink >
+ </ li >
+ </ ul >
+ </ div >
+</ template >
+< script setup lang = "ts" >
+import { useBlogCategory } from '@vuepress/plugin-blog'
+import { RouteLink } from 'vuepress/client'
+
+const categoryMap = useBlogCategory ( 'tag' )
+</ script >
+
TagList
布局:
< template >
+ < div >
+ < h1 > Tag page </ h1 >
+ < div class = "category-wrapper" >
+ < RouteLink
+ v-for = " ({ items , path }, name ) in categoryMap . map "
+ : key = " name "
+ : to = " path "
+ class = "category"
+ >
+ {{ name }}
+ < span class = "category-num" >
+ {{ items . length }}
+ </ span >
+ </ RouteLink >
+ </ div >
+ < div class = "article-wrapper" v-if = " categoryMap . currentItems " >
+ < div v-if = " ! categoryMap . currentItems . length " > Nothing in here. </ div >
+ < article
+ v-for = " { info , path } in categoryMap . currentItems "
+ class = "article"
+ @ click = " $router . push ( path ) "
+ >
+ < header class = "title" >
+ {{
+ ( isTimeline
+ ? \` \${ new Date ( info . date ). toLocaleDateString () } : \`
+ : '' ) + info . title
+ }}
+ </ header >
+ < hr />
+ < div class = "article-info" >
+ < span v-if = " info . author " class = "author"
+ > Author: {{ info . author }} </ span
+ >
+ < span v-if = " info . date && ! isTimeline " class = "date"
+ > Date: {{ new Date ( info . date ). toLocaleDateString () }} </ span
+ >
+ < span v-if = " info . category " class = "category"
+ > Category: {{ info . category . join ( ', ' ) }} </ span
+ >
+ < span v-if = " info . tag " class = "tag"
+ > Tag: {{ info . tag . join ( ', ' ) }} </ span
+ >
+ </ div >
+ < div v-if = " info . excerpt " class = "excerpt" v-html = " info . excerpt " / >
+ </ article >
+ </ div >
+ </ div >
+</ template >
+< script setup lang = "ts" >
+import { useBlogCategory } from '@vuepress/plugin-blog'
+import { RouteLink } from 'vuepress/client'
+
+const categoryMap = useBlogCategory ( 'tag' )
+</ script >
+
StarList
布局:
< template >
+ < div class = "article-wrapper" v-if = " stars . items " >
+ < div v-if = " ! stars . items . length " > Nothing in here. </ div >
+ < article
+ v-for = " { info , path } in stars . items "
+ class = "article"
+ @ click = " $router . push ( path ) "
+ >
+ < header class = "title" >
+ {{
+ ( isTimeline ? \` \${ new Date ( info . date ). toLocaleDateString () } : \` : '' ) +
+ info . title
+ }}
+ </ header >
+ < hr />
+ < div class = "article-info" >
+ < span v-if = " info . author " class = "author" > Author: {{ info . author }} </ span >
+ < span v-if = " info . date && ! isTimeline " class = "date"
+ > Date: {{ new Date ( info . date ). toLocaleDateString () }} </ span
+ >
+ < span v-if = " info . category " class = "category"
+ > Category: {{ info . category . join ( ', ' ) }} </ span
+ >
+ < span v-if = " info . tag " class = "tag" > Tag: {{ info . tag . join ( ', ' ) }} </ span >
+ </ div >
+ < div v-if = " info . excerpt " class = "excerpt" v-html = " info . excerpt " / >
+ </ article >
+ </ div >
+</ template >
+< script setup lang = "ts" >
+import { useBlogType } from '@vuepress/plugin-blog/client'
+
+import ArticleList from '../components/ArticleList.vue'
+import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
+
+const stars = useBlogType ( 'star' )
+</ script >
+
`,12),C=p(`该插件添加了原生多语言支持,因此你的设置将自动应用于每种语言。
例如,如果用户进行了以下 locales 配置,并且你正在设置上面的“star”示例:
export default {
+ locales: {
+ '/' : {
+ lang: 'en-US' ,
+ },
+ '/zh/' : {
+ lang: 'zh-CN' ,
+ },
+ },
+}
+
那么 /zh/star/
和 /star/
都将可用,并且只会显示对应语言下的文章。
这个插件提供了一个内置的摘要生成器,可以通过将 excerpt
选项设置为 true
来启用。
摘要介绍
摘要是一个 HTML 片段,被用于在博客列表中显示文章的简短描述,所以摘要有如下限制:
摘要不支持任何未知标签以及 Vue 语法,所以此类内容会在生成时被移除。如果你有自定义组件 (非 Vue 组件),请配置 isCustomElement
选项。 由于摘要是一个 HTML 片段,所以你将无法通过相对路径或别名引入任何图片,这些图片会被直接移除。如果你想要保留图片,请使用基于 .vuepress/public
的绝对路径或完整路径以确保它们可以在其他地址被访问。 摘要生成器将尝试从 Frontmatter 内容中找到有效的摘要分隔符,如果找到,它将使用分隔符之前的内容,分隔符默认为 <!-- more -->
,并且你可以通过 excerptSeparator
选项来自定义它。
如果找不到有效的分隔符,它将从 Markdown 文件的开头开始解析内容,直到长度达到预设值时停止。该值默认为 300
,你可以通过设置 excerptLength
选项来自定义它。
要选择哪个页面应该生成摘要,你可以使用 excerptFilter
选项。
示例
通常,如果用户设置了 frontmatter.description
,你可能希望使用它们,因此如果 frontmatter.description
不为空,你可以让过滤器函数返回 false
。
`,12);function d(v,u){const n=t("RouteLink");return c(),r("div",null,[i,o("p",null,[s("看,设置这两种类型很容易。有关完整选项,请参阅 "),a(n,{to:"/zh/plugins/blog/config.html#%E5%8D%9A%E5%AE%A2%E5%88%86%E7%B1%BB%E9%85%8D%E7%BD%AE"},{default:l(()=>[s("博客分类配置")]),_:1}),s(" 和 "),a(n,{to:"/zh/plugins/blog/config.html#%E5%8D%9A%E5%AE%A2%E7%B1%BB%E5%9E%8B%E9%85%8D%E7%BD%AE"},{default:l(()=>[s("博客分类配置")]),_:1}),s("。")]),y,o("p",null,[s("有关返回类型,请参阅 "),a(n,{to:"/zh/plugins/blog/config.html#%E5%8F%AF%E7%BB%84%E5%90%88%E5%BC%8F-API"},{default:l(()=>[s("Composition API 返回类型")]),_:1}),s("。")]),C])}const E=e(D,[["render",d],["__file","guide.html.vue"]]),b=JSON.parse('{"path":"/zh/plugins/blog/guide.html","title":"指南","lang":"zh-CN","frontmatter":{"title":"指南","icon":"lightbulb"},"headers":[{"level":2,"title":"收集文章并生成信息","slug":"收集文章并生成信息","link":"#收集文章并生成信息","children":[]},{"level":2,"title":"自定义类别和类型","slug":"自定义类别和类型","link":"#自定义类别和类型","children":[]},{"level":2,"title":"在客户端使用组合 API","slug":"在客户端使用组合-api","link":"#在客户端使用组合-api","children":[]},{"level":2,"title":"多语言支持","slug":"多语言支持","link":"#多语言支持","children":[]},{"level":2,"title":"摘要生成","slug":"摘要生成","link":"#摘要生成","children":[]}],"git":{"updatedTime":1708365679000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/blog/guide.md"}');export{E as comp,b as data};
diff --git a/assets/guide.html-DTgdayH3.js b/assets/guide.html-DTgdayH3.js
new file mode 100644
index 0000000000..bed464e444
--- /dev/null
+++ b/assets/guide.html-DTgdayH3.js
@@ -0,0 +1,37 @@
+import{_ as c,r as s,o as d,c as p,b as e,d as t,a as n,w as o,e as l}from"./app-BcH8wZQx.js";const g={},u=e("h1",{id:"guide",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#guide"},[e("span",null,"Guide")])],-1),h={href:"https://ogp.me/",target:"_blank",rel:"noopener noreferrer"},y={href:"https://www.w3.org/TR/json-ld-api/",target:"_blank",rel:"noopener noreferrer"},m=l(`The plugin works out of the box. Without any config, it will extract information from the page content as much as possible to complete the necessary tags required by OGP and JSON-LD.
By default, the plugin will read the site config and page frontmatter to automatically generate tags as much as possible. Such as site name, page title, page type, writing date, last update date, and article tags are all automatically generated.
The following are the <meta>
tags and their values that will be injected into <head>
by default:
The following are the <meta>
tags and their value injected into <head>
by default to satisfy OGP:
Meta Name Value og:url
options.hostname
+ path
og:site_name
siteConfig.title
og:title
page.title
og:description
page.frontmatter.description
|| auto generated (when autoDescription
is true
in plugin options)og:type
"article"
og:image
options.hostname
+ page.frontmatter.image
||first image in page || fallbackImage
in plugin optionsog:updated_time
page.git.updatedTime
og:locale
page.lang
og:locale:alternate
Other languages in siteData.locales
twitter:card
"summary_large_image"
(only available when image found)twitter:image:alt
page.title
(only available when image found)article:author
page.frontmatter.author
|| options.author
article:tag
page.frontmatter.tags
|| page.frontmatter.tag
article:published_time
page.frontmatter.date
|| page.git.createdTime
article:modified_time
page.git.updatedTime
Property Name Value @context
"https://schema.org"
@type
"NewsArticle"
headline
page.title
image
image in page || options.hostname
+ page.frontmatter.image
|| siteFavIcon
in plugin options datePublished
page.frontmatter.date
|| page.git.createdTime
dateModified
page.git.updatedTime
author
page.frontmatter.author
|| options.author
You can configure the head
option in the page's frontmatter to add specific tags to the page <head>
to enhance SEO. For example:
---
+head :
+ - - meta
+ - name : keywords
+ content : SEO plugin
+---
+
Will automatically inject <meta name="keywords" content="SEO plugin" />
.
The plugin also gives you full control over the build logic.
For most pages, there are basically only two types: articles and website, so the plugin provides the isArticle
option to allow you to provide logic for identifying articles.
The option accepts a function in the format (page: Page) => boolean
, by default all non-home pages generated from Markdown files are treated as articles.
TIP
If a page does fit into the "unpopular" genre like books, music, etc., you can handle them by setting the three options below.
You can use the plugin options ogp
to pass in a function to modify the default OGP object to your needs and return it.
function ogp (
+ /** OGP Object inferred by plugin */
+ ogp : SeoContent ,
+ /** Page Object */
+ page : Page ,
+ /** VuePress App */
+ app : App ,
+): SeoContent
+
`,22),D=l(`For example, if you are using a third-party theme and set a banner
in frontmatter for each article according to the theme requirements, then you can pass in the following ogp
:
seoPlugin ({
+ ogp : ( ogp , page ) => ({
+ ... ogp ,
+ 'og:image' : page . frontmatter . banner || ogp [ 'og:image' ],
+ }),
+})
+
Like OGP, you can use the plugin options jsonLd
to pass in a function to modify the default JSON-LD object to your needs and return it.
function jsonLd (
+ /** JSON-LD Object inferred by plugin */
+ jsonLD : ArticleSchema | BlogPostingSchema | WebPageSchema ,
+ /** Page Object */
+ page : Page ,
+ /** VuePress App */
+ app : App ,
+): ArticleSchema | BlogPostingSchema | WebPageSchema
+
If you are deploying your content to different sites, or same content under different URLs, you may need to set canonical
option to provide a "Canonical Link" for your page. You can either set a string which will be appended before page route link, or adding a custom function (page: Page) => string | null
to return a canonical link if necessary.
Example
If your sites are deployed under docs directory in example.com
, but available in:
http://example.com/docs/xxx
https://example.com/docs/xxx
http://www.example.com/docs/xxx
https://www.example.com/docs/xxx
(primary)To let search engine results always be the primary choice, you may need to set canonical
to https://www.example.com/docs/
, so that search engine will know that the fourth URL is preferred to be indexed.
Sometimes you may need to fit other protocols or provide the corresponding SEO tags in the format provided by other search engines. In this case, you can use the customHead
option, whose type is:
function customHead (
+ /** Head tag config */
+ head : HeadConfig [],
+ /** Page Object */
+ page : Page ,
+ /** VuePress App */
+ app : App ,
+): void
+
You should modify the head
array in this function directly.
S earch e ngine optimization (SEO) is the process of improving the quality and quantity of site traffic to a site or a web page from search engines. SEO targets unpaid traffic (known as "natural" or "organic" results) rather than direct traffic or paid traffic. Unpaid traffic may originate from different kinds of searches, including image search, video search, academic search, news search, and industry-specific vertical search engines.
As an internet marketing strategy, SEO considers how search engines work, the computer-programmed algorithms that dictate search engine behavior, what people search for, the actual search terms or keywords typed into search engines, and which search engines are preferred by their targeted audience. SEO is performed because a site will receive more visitors from a search engine when sites rank higher on the search engine results page (SERP). These visitors can then potentially be converted into customers.
`,16),f={href:"https://ogp.me/",target:"_blank",rel:"noopener noreferrer"},b=e("strong",null,"O",-1),v=e("strong",null,"G",-1),x=e("strong",null,"Pr",-1),C=e("p",null,[t("This plugin perfectly supports this protocol and will automatically generate "),e("code",null," "),t(" tags that conform to the protocol.")],-1),k={href:"https://www.w3.org/TR/json-ld-api/",target:"_blank",rel:"noopener noreferrer"},E=e("p",null,'This plugin will generate "NewsArticle" scheme for article pages.',-1),_={href:"https://www.w3.org/TR/rdfa-primer/",target:"_blank",rel:"noopener noreferrer"},w={href:"https://schema.org/",target:"_blank",rel:"noopener noreferrer"},O=e("p",null,"Schema definition site for structural markup",-1),S=e("h2",{id:"related-tools",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#related-tools"},[e("span",null,"Related Tools")])],-1),P={href:"https://search.google.com/test/rich-results",target:"_blank",rel:"noopener noreferrer"};function T(A,j){const a=s("ExternalLinkIcon"),i=s("RouteLink"),r=s("ProjectLink");return d(),p("div",null,[u,e("p",null,[t("This plugin will make your site fully support "),e("a",h,[t("Open Content Protocol OGP"),n(a)]),t(" and "),e("a",y,[t("JSON-LD 1.1"),n(a)]),t(" to enhance the SEO of the site.")]),m,e("p",null,[t("For detailed parameter structure, see "),n(i,{to:"/plugins/seo/config.html"},{default:o(()=>[t("Config")]),_:1}),t(".")]),D,e("ul",null,[e("li",null,[e("p",null,[e("a",f,[t("Open Content Protocol OGP"),n(a)]),t(" ("),b,t("pen "),v,t("raph "),x,t("otocol)")]),C]),e("li",null,[e("p",null,[e("a",k,[t("JSON-LD 1.1"),n(a)])]),E]),e("li",null,[e("p",null,[e("a",_,[t("RDFa 1.1"),n(a)])]),e("p",null,[t("RDFa mainly marks HTML structure. This is what the plugin cannot support. "),n(r,{type:"theme",name:"hope"},{default:o(()=>[t("vuepress-theme-hope")]),_:1}),t(" uses this feature to pass Google's rich media structure test. You can consider using it.")])]),e("li",null,[e("p",null,[e("a",w,[t("Schema.Org"),n(a)])]),O])]),S,e("p",null,[t("You can use "),e("a",P,[t("Google Rich Media Structure Test Tool"),n(a)]),t(" to test this site.")])])}const L=c(g,[["render",T],["__file","guide.html.vue"]]),q=JSON.parse('{"path":"/plugins/seo/guide.html","title":"Guide","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Out of Box","slug":"out-of-box","link":"#out-of-box","children":[{"level":3,"title":"Default OGP Generation","slug":"default-ogp-generation","link":"#default-ogp-generation","children":[]},{"level":3,"title":"Default JSON-LD Generation","slug":"default-json-ld-generation","link":"#default-json-ld-generation","children":[]}]},{"level":2,"title":"Setting Tags Directly","slug":"setting-tags-directly","link":"#setting-tags-directly","children":[]},{"level":2,"title":"Customize Generation","slug":"customize-generation","link":"#customize-generation","children":[{"level":3,"title":"Page Type","slug":"page-type","link":"#page-type","children":[]},{"level":3,"title":"OGP","slug":"ogp","link":"#ogp","children":[]},{"level":3,"title":"JSON-LD","slug":"json-ld","link":"#json-ld","children":[]}]},{"level":2,"title":"Canonical Link","slug":"canonical-link","link":"#canonical-link","children":[{"level":3,"title":"Customize head Tags","slug":"customize-head-tags","link":"#customize-head-tags","children":[]}]},{"level":2,"title":"SEO Introduction","slug":"seo-introduction","link":"#seo-introduction","children":[]},{"level":2,"title":"Related Documents","slug":"related-documents","link":"#related-documents","children":[]},{"level":2,"title":"Related Tools","slug":"related-tools","link":"#related-tools","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"plugins/seo/guide.md"}');export{L as comp,q as data};
diff --git a/assets/guide.html-TN47Spm1.js b/assets/guide.html-TN47Spm1.js
new file mode 100644
index 0000000000..03e36747ac
--- /dev/null
+++ b/assets/guide.html-TN47Spm1.js
@@ -0,0 +1,23 @@
+import{_ as r,r as l,o as c,c as i,b as s,d as e,a as o,w as p,e as a}from"./app-BcH8wZQx.js";const d={},f=a('将你的 VuePress 站点变成渐进式网络应用程序 (PWA)。
',3),u={href:"https://developers.google.com/web/tools/workbox/modules/workbox-build",target:"_blank",rel:"noopener noreferrer"},h={href:"https://github.com/yyx990803/register-service-worker",target:"_blank",rel:"noopener noreferrer"},D={class:"custom-container warning"},y=s("p",{class:"custom-container-title"},"注意",-1),v=a(`A PWA uses a Service Worker (SW for short) to cache and proxy site content.
一个 PWA 使用 Service Worker (简称 SW) 来获取并托管网站内容。
为了使你的网站符合 PWA 的要求,一个网络 App 清单文件是必要的,并且你的 PWA 应满足可安装性要求。
你可以通过设置 manifest
选项来自定义 manifest 文件,或者在 public 文件夹中提供 manifest.webmanifest
或 manifest.json
。前者优先级更高。
插件会自动为你生成 manifest.webmanifest
,并在每个页面的 <head>
中添加清单链接声明,但是 你至少应该通过 manifest.icons
或 PWA 插件中的其他选项设置一个有效的图标。
注意
可安装性规范要求 manifest 中至少声明一个有效的图标。
所以如果你不配置 manifest.icons
,访问者只能享受到 Service Worker 缓存带来的离线可访问性,而并不能作为 PWA 进行安装。
此外,该插件默认不处理清单中的任何内容,而是按原样输出。 这意味着,如果你计划部署到子目录,则应自行将 URL 前缀附加到自己的清单 Urls 中。如果你需要的所有东西都在 base 文件夹下,你可以在插件选项中设置 appendBase: true
让插件将 base
自动附加到任何地址。
为了更好的控制 Service Worker 可以预缓存的内容,插件提供了相关的缓存控制选项。
默认情况下插件会预缓存所有的 JS 和 CSS 文件,但仅缓存主页和 404 页面的 HTML。插件同时还会缓存字体文件 (woff, woff2, eot, ttf, otf) 和 SVG 图标。
如果你的站点只有少量重要图片,并希望它们在离线模式下显示,你可以通过设置 cacheImage
选项为 true
来缓存站点图片。
我们通过文件后缀名识别图片,任何以 .png
, .jpg
, .jpeg
, .gif
, .bmp
, .webp
结尾的文件都会视为图片。
当你网站体积不大,并且希望文档完全离线可用时,你可以通过设置 cacheHTML
为 true
来缓存所有 HTML 页面。
为什么默认不缓存非主页和 404 页面
虽然说 VuePress 为所有的页面通过 SSG 生成了 HTML 文件,但是这些文件主要用于 SEO,并能够让你在后端不做 SPA 配置的情况下能够直接访问任何链接。
VuePress 本质上是一个 SPA。这意味着你只需要缓存主页并从主页进入即可正常访问所有页面。所以默认不缓存其他 HTML 能够有效减小缓存大小 (可以缩减大约 40% 的体积),加快 SW 更新速度。
但是这样做也有缺点,如果用户直接从非主页进入网站,首个页面的 HTML 文件仍需要从互联网加载。同时离线环境下,用户只能通过主页进入再自行导航到对应页面,直接访问某个链接会出现无法访问的提示。
为了防止在预缓存列表中包含大文件,任何 > 2 MB 的文件或 > 1 MB 的图片都将被忽略。 你可以通过 maxSize
和 maxImageSize
来自定义大小限制 (单位为 KB)。
我们提供 update
选项控制用户如何接收更新。
update
选项的默认值是 "available"
,这意味着当网站内容更新后,新的 SW 会在后台静默安装,并在安装结束后弹窗提示用户新内容就绪。用户可以自主选择是否立即刷新查看新内容。这意味在新 SW 就绪前用户会访问旧版本网站。
如果你的文档仍在建设期,希望尽早提示用户他可能在阅读已过时的内容,你可以将其设置为 "hint"
。这样用户在进入文档后数秒内就可以收到新内容已发布的通知。但这样做的负面效果是如果用户在新 SW 就绪前选择更新,那么他将在新 SW 安装并接管页面前,需要从互联网获取页面的全部资源。
如果你的文档很稳定,或者你在托管博客,不太关心用户立即接收到最新版本,你可以将其设置为 "disabled"
,这意味着新的 SW 将在后台完全静默安装并在安装后等待,当旧版本 SW 控制的页面全部关闭后,新 SW 将再下次访问接管并提供用户新内容。此设置可以避免用户在访中被弹窗打扰。
如果你希望通过 SW 来加速用户在弱网或无网条件下的访问,但同时希望用户时刻访问新内容,你可以将此选项设置为 "force"
。这意味着检测到新 SW 后旧 SW 将会被立刻销毁并且页面会被刷新以确保用户浏览最新内容。最大的缺点就是致新 SW 发布后,用户在重新进入网站后的几秒内会遇到预期之外的突然刷新,并且他们将必须通过互联网访问文档并完全重新安装最新的 SW。
当检测到新内容 (检测到新的 SW) 时,更新提示弹窗将会出现;当新内容就绪时,更新就绪弹窗将会出现。
如果你对默认的弹窗不满意,你可以自行编写组件更换。从 @vuepress/plugin-pwa/client
中导入 PwaFoundPopup
或 PwaReadyPopup
并使用其 slot 来自定义弹窗内容,然后将组件路径传递给 foundComponent
或 readyComponent
选项。
< script setup lang = "ts" >
+import { PwaFoundPopup } from '@vuepress/plugin-pwa/client'
+</ script >
+< template >
+ < PwaFoundPopup v-slot = " { found , refresh } " >
+ < div v-if = " found " >
+ 已找到新内容
+ < button @ click = " refresh " > 刷新 </ button >
+ </ div >
+ </ PwaFoundPopup >
+</ template >
+
< script setup lang = "ts" >
+import { PwaReadyPopup } from '@vuepress/plugin-pwa/client'
+</ script >
+< template >
+ < PwaReadyPopup v-slot = " { isReady , reload } " >
+ < div v-if = " isReady " >
+ 新内容已就绪
+ < button @ click = " reload " > 应用 </ button >
+ </ div >
+ </ PwaReadyPopup >
+</ template >
+
`,32),g=s("code",null,"generateSwConfig",-1),m=s("code",null,"workbox-build",-1),_=s("h2",{id:"相关阅读",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#相关阅读"},[s("span",null,"相关阅读")])],-1),b=s("p",null,"更多内容,请详见:",-1),C={href:"https://web.dev/progressive-web-apps/",target:"_blank",rel:"noopener noreferrer"},k={href:"https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps",target:"_blank",rel:"noopener noreferrer"},S={href:"https://w3c.github.io/manifest/",target:"_blank",rel:"noopener noreferrer"},W=s("hr",{class:"footnotes-sep"},null,-1),P={class:"footnotes"},w={class:"footnotes-list"},E={id:"footnote1",class:"footnote-item"},x=s("p",null,[s("strong",null,"PWA 介绍")],-1),A=s("p",null,"PWA 全称 Progressive Web app,即渐进式网络应用程序,标准由 W3C 规定。",-1),q=s("p",null,"它允许网站通过支持该特性的浏览器将网站作为 App 安装在对应平台上。",-1),F={href:"https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps",target:"_blank",rel:"noopener noreferrer"},M=s("a",{href:"#footnote-ref1",class:"footnote-backref"},"↩︎",-1),z=a('',1),L={id:"footnote3",class:"footnote-item"},N=s("p",null,[s("strong",null,"清单文件")],-1),R=s("p",null,"清单文件使用 JSON 格式,负责声明 PWA 各项信息,如名称、描述、图标、快捷动作等。",-1),T=s("p",null,"为了使你的站点能够被注册为 PWA,你需要满足 manifest 基本的规范,才能使浏览器认为该网站为一个可安装的 PWA 并允许用户安装它。",-1),B={class:"custom-container tip"},H=s("p",{class:"custom-container-title"},"提示",-1),V={href:"https://developer.mozilla.org/zh-CN/docs/Web/Manifest",target:"_blank",rel:"noopener noreferrer"},O={href:"https://w3c.github.io/manifest/",target:"_blank",rel:"noopener noreferrer"},G=s("a",{href:"#footnote-ref3",class:"footnote-backref"},"↩︎",-1),I=a('',2),j={id:"footnote6",class:"footnote-item"},J=s("p",null,[s("strong",null,"SEO"),e(": "),s("strong",null,"S"),e("earch "),s("strong",null,"E"),e("ngine "),s("strong",null,"O"),e("ptimization,搜索引擎增强,")],-1),U={href:"https://mister-hope.com/code/website/html/definition/seo.html",target:"_blank",rel:"noopener noreferrer"},K=s("a",{href:"#footnote-ref6",class:"footnote-backref"},"↩︎",-1),Q=s("li",{id:"footnote7",class:"footnote-item"},[s("p",null,[s("strong",null,"SPA"),e(": "),s("strong",null,"S"),e("ingle "),s("strong",null,"P"),e("age "),s("strong",null,"A"),e("pplication, 单页应用")]),s("p",null,[e("大多只有主页,并使用 history mode 处理路由,而不是真的在页面之间导航。 "),s("a",{href:"#footnote-ref7",class:"footnote-backref"},"↩︎")])],-1);function X(Y,Z){const n=l("ExternalLinkIcon"),t=l("RouteLink");return c(),i("div",null,[f,s("p",null,[e("此插件使用 "),s("a",u,[e("workbox-build"),o(n)]),e(" 生成 Service Worker 文件,并使用 "),s("a",h,[e("register-service-worker"),o(n)]),e(" 注册 Service Worker。")]),s("div",D,[y,s("p",null,[e("如果你启用过该插件,并想要禁用它,你可能需要 "),o(t,{to:"/zh/plugins/remove-pwa.html"},{default:p(()=>[e("`@vuepress/plugin-remove-pwa")]),_:1}),e(" 来移除现有的 Service Worker 。")])]),v,s("p",null,[e("插件还提供了其他 PWA 相关选项,比如微软磁贴图标与颜色设置,苹果图标等。 如果你是一个高级用户,你也可以设置 "),g,e(" 来配置 "),m,e("。查看 "),o(t,{to:"/zh/plugins/pwa/config.html#%E9%80%89%E9%A1%B9"},{default:p(()=>[e("插件选项")]),_:1}),e(" 了解更多细节。")]),_,b,s("ul",null,[s("li",null,[s("a",C,[e("Google PWA"),o(n)])]),s("li",null,[s("a",k,[e("MDN PWA"),o(n)])]),s("li",null,[s("a",S,[e("W3C Manifest 规范"),o(n)])])]),W,s("section",P,[s("ol",w,[s("li",E,[x,A,q,s("p",null,[e("访问 "),s("a",F,[e("https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps"),o(n)]),e(" 查看详情。 "),M])]),z,s("li",L,[N,R,T,s("div",B,[H,s("p",null,[e("Manifest 的标准与规范,请详见 "),s("a",V,[e("MDN 网络 App 清单"),o(n)]),e(" 和 "),s("a",O,[e("W3C Manifest"),o(n)])])]),G]),I,s("li",j,[J,s("p",null,[e("详见 "),s("a",U,[e("SEO 介绍"),o(n)]),e(),K])]),Q])])])}const ss=r(d,[["render",X],["__file","guide.html.vue"]]),es=JSON.parse('{"path":"/zh/plugins/pwa/guide.html","title":"指南","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"网络 App 清单","slug":"网络-app-清单","link":"#网络-app-清单","children":[]},{"level":2,"title":"缓存控制","slug":"缓存控制","link":"#缓存控制","children":[{"level":3,"title":"默认缓存","slug":"默认缓存","link":"#默认缓存","children":[]},{"level":3,"title":"图片缓存","slug":"图片缓存","link":"#图片缓存","children":[]},{"level":3,"title":"HTML 缓存","slug":"html-缓存","link":"#html-缓存","children":[]},{"level":3,"title":"大小控制","slug":"大小控制","link":"#大小控制","children":[]}]},{"level":2,"title":"更新控制","slug":"更新控制","link":"#更新控制","children":[{"level":3,"title":"更新提示弹窗","slug":"更新提示弹窗","link":"#更新提示弹窗","children":[]}]},{"level":2,"title":"其他选项","slug":"其他选项","link":"#其他选项","children":[]},{"level":2,"title":"相关阅读","slug":"相关阅读","link":"#相关阅读","children":[]}],"git":{"updatedTime":1708397875000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/plugins/pwa/guide.md"}');export{ss as comp,es as data};
diff --git a/assets/guide.html-hicwSV0-.js b/assets/guide.html-hicwSV0-.js
new file mode 100644
index 0000000000..b1d3a15d19
--- /dev/null
+++ b/assets/guide.html-hicwSV0-.js
@@ -0,0 +1,37 @@
+import{_ as p,r as a,o as r,c as i,b as e,d as s,a as n,w as l,e as o}from"./app-BcH8wZQx.js";const D={},g=e("h1",{id:"指南",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#指南"},[e("span",null,"指南")])],-1),h=e("code",null,"",-1),y={href:"https://ogp.me/",target:"_blank",rel:"noopener noreferrer"},u={href:"https://www.w3.org/TR/json-ld-api/",target:"_blank",rel:"noopener noreferrer"},m=o(`插件开箱即用,在不做任何配置的情况下,会尽可能通过页面内容,提取对应的信息补全 OGP 与 JSON-LD 所需的必要标签。
默认情况下,插件会读取站点配置、主题配置与页面的 frontmatter 来尽可能自动生成。诸如站点名称,页面标题,页面类型,写作日期,最后更新日期,文章标签均会自动生成。
属性名称 值 og:url
options.hostname
+ path
og:site_name
siteConfig.title
og:title
page.title
og:description
page.frontmatter.description
|| 自动生成 (当插件选项中的 autoDescription
为 true
时)og:type
"article"
og:image
options.hostname
+ page.frontmatter.image
|| 页面的第一张图片|| 插件选项的 fallbackImage
og:updated_time
page.git.updatedTime
og:locale
page.lang
og:locale:alternate
siteData.locales
包含的其他语言twitter:card
"summary_large_image"
(仅在找到图片时)twitter:image:alt
page.title
(仅在找到图片时)article:author
page.frontmatter.author
|| options.author
article:tag
page.frontmatter.tags
|| page.frontmatter.tag
article:published_time
page.frontmatter.date
|| page.git.createdTime
article:modified_time
page.git.updatedTime
属性名 值 @context
"https://schema.org"
@type
"NewsArticle"
headline
page.title
image
页面中的图片|| options.hostname
+ page.frontmatter.image
datePublished
page.frontmatter.date
|| page.git.createdTime
dateModified
page.git.updatedTime
author
page.frontmatter.author
|| options.author
你可以在页面的 frontmatter 中配置 head
选项,自主添加特定标签到页面 <head>
以增强 SEO。
如:
---
+head :
+ - - meta
+ - name : keywords
+ content : SEO plugin
+---
+
会自动注入 <meta name="keywords" content="SEO plugin" />
。
本插件也支持你完全控制生成逻辑。
对于大多数页面,基本只有文章和网页两种类型,所以插件提供了 isArticle
选项让你提供辨别文章的逻辑。
选项接受一个 (page: Page) => boolean
格式的函数,默认情况下从 Markdown 文件生成的非主页页面都会被视为文章。
提示
如果某个网页的确符合图书、音乐之类的“冷门”类型,你可以通过设置下方三个选项处理它们。
你可以使用插件选项的 ogp
传入一个函数来按照你的需要修改默认 OGP 对象并返回。
function ogp (
+ /** 插件推断的 OGP 信息 */
+ ogp : SeoContent ,
+ /** 页面对象 */
+ page : Page ,
+ /** VuePress App */
+ app : App ,
+): SeoContent
+
`,21),v=o(`比如你在使用某个第三方主题,并按照主题要求为每篇文章在 Front Matter 中设置了 banner
,那你可以传入这样的 ogp
:
seoPlugin ({
+ ogp : ( ogp , page ) => ({
+ ... ogp ,
+ 'og:image' : page . frontmatter . banner || ogp [ 'og:image' ],
+ }),
+})
+
同 OGP,你可以使用插件选项的 jsonLd
传入一个函数来按照你的需要修改默认 JSON-LD 对象并返回。
function jsonLd (
+ /** 由插件推断出的 JSON-LD 对象 */
+ jsonLD : ArticleSchema | BlogPostingSchema | WebPageSchema ,
+ /** 页面对象 */
+ page : Page ,
+ /** VuePress App */
+ app : App ,
+): ArticleSchema | BlogPostingSchema | WebPageSchema
+
如果你将内容部署到不同的站点,或不同 URL 下的相同内容,你可能需要设置 canonical
选项为你的页面提供 “规范链接”。 你可以设置一个字符串,这样它会附加在页面路由链接之前,或者添加一个自定义函数 (page: Page) => string | null
返回规范链接。
例子
如果你的站点部署在 example.com
的 docs 文件夹下,但同时在下列网址中可用:
http://example.com/docs/xxx
https://example.com/docs/xxx
http://www.example.com/docs/xxx
https://www.example.com/docs/xxx
(首选)要让搜索引擎结果始终是首选,你可能需要将 canonical
设置为 https://www.example.com/docs/
,以便搜索引擎知道首选第四个 URL 作为索引结果。
有些时候你可能需要符合其他协议或按照其他搜索引擎提供的格式提供对应的 SEO 标签,此时你可以使用 customHead
选项,其类型为:
function customHead (
+ /** head 标签配置 */
+ head : HeadConfig [],
+ /** 页面对象 */
+ page : Page ,
+ /** VuePress App */
+ app : App ,
+): void
+
你应该直接修改传入的 head
参数。
搜索引擎优化 (S earch E ngine O ptimization),是一种透过了解搜索引擎的运作规则来调整网站,以及提高目的网站在有关搜索引擎内排名的方式。由于不少研究发现,搜索引擎的用户往往只会留意搜索结果最前面的几个条目,所以不少网站都希望透过各种形式来影响搜索引擎的排序,让自己的网站可以有优秀的搜索排名。 所谓“针对搜索引擎作最优化的处理”,是指为了要让网站更容易被搜索引擎接受。搜索引擎会将网站彼此间的内容做一些相关性的资料比对,然后再由浏览器将这些内容以最快速且接近最完整的方式,呈现给搜索者。搜索引擎优化就是通过搜索引擎的规则进行优化,为用户打造更好的用户体验,最终的目的就是做好用户体验。
`,15),x={href:"https://ogp.me/",target:"_blank",rel:"noopener noreferrer"},b=e("strong",null,"O",-1),C=e("strong",null,"G",-1),_=e("strong",null,"Pr",-1),E=e("p",null,[s("本插件完美支持该协议,会自动生成符合该协议的 "),e("code",null," "),s(" 标签。")],-1),f={href:"https://www.w3.org/TR/json-ld-api/",target:"_blank",rel:"noopener noreferrer"},k=e("p",null,"本插件会为文章类页面生成 NewsArticle 类标签。",-1),w={href:"https://www.w3.org/TR/rdfa-primer/",target:"_blank",rel:"noopener noreferrer"},A={href:"https://schema.org/",target:"_blank",rel:"noopener noreferrer"},O=e("p",null,"结构标记的 Schema 定义站点",-1),P=e("h2",{id:"相关工具",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#相关工具"},[e("span",null,"相关工具")])],-1),S={href:"https://search.google.com/test/rich-results",target:"_blank",rel:"noopener noreferrer"};function B(F,L){const t=a("ExternalLinkIcon"),c=a("RouteLink"),d=a("ProjectLink");return r(),i("div",null,[g,e("p",null,[s("本插件会通过向网站 "),h,s(" 注入标签,让你的网站完全支持 "),e("a",y,[s("开放内容协议 OGP"),n(t)]),s(" 和 "),e("a",u,[s("JSON-LD 1.1"),n(t)]),s(",以全面增强站点的搜索引擎优化性。")]),m,e("p",null,[s("详细的参数结构详见 "),n(c,{to:"/zh/plugins/seo/config.html"},{default:l(()=>[s("配置")]),_:1}),s("。")]),v,e("ul",null,[e("li",null,[e("p",null,[e("a",x,[s("开放内容协议 OGP"),n(t)]),s(" ("),b,s("pen "),C,s("raph "),_,s("otocal)")]),E]),e("li",null,[e("p",null,[e("a",f,[s("JSON-LD 1.1"),n(t)])]),k]),e("li",null,[e("p",null,[e("a",w,[s("RDFa 1.1"),n(t)])]),e("p",null,[s("RDFa 主要标记 HTML 结构。这是插件无法支持的内容,"),n(d,{type:"theme",name:"hope",path:"/zh/"},{default:l(()=>[s("vuepress-theme-hope")]),_:1}),s(" 使用了这一功能通过了谷歌的富媒体结构测试。你可以考虑搭配使用。")])]),e("li",null,[e("p",null,[e("a",A,[s("Schema.Org"),n(t)])]),O])]),P,e("p",null,[s("你可以使用 "),e("a",S,[s("Google 富媒体结构测试工具"),n(t)]),s(" 测试本站点。")])])}const j=p(D,[["render",B],["__file","guide.html.vue"]]),q=JSON.parse('{"path":"/zh/plugins/seo/guide.html","title":"指南","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"开箱即用","slug":"开箱即用","link":"#开箱即用","children":[{"level":3,"title":"默认的 OGP 生成逻辑","slug":"默认的-ogp-生成逻辑","link":"#默认的-ogp-生成逻辑","children":[]},{"level":3,"title":"默认的 JSON-LD 生成逻辑","slug":"默认的-json-ld-生成逻辑","link":"#默认的-json-ld-生成逻辑","children":[]}]},{"level":2,"title":"直接添加 head 标签","slug":"直接添加-head-标签","link":"#直接添加-head-标签","children":[]},{"level":2,"title":"自定义生成过程","slug":"自定义生成过程","link":"#自定义生成过程","children":[{"level":3,"title":"页面类型","slug":"页面类型","link":"#页面类型","children":[]},{"level":3,"title":"OGP","slug":"ogp","link":"#ogp","children":[]},{"level":3,"title":"JSON-LD","slug":"json-ld","link":"#json-ld","children":[]}]},{"level":2,"title":"规范链接","slug":"规范链接","link":"#规范链接","children":[{"level":3,"title":"自定义 head 标签","slug":"自定义-head-标签","link":"#自定义-head-标签","children":[]}]},{"level":2,"title":"SEO 介绍","slug":"seo-介绍","link":"#seo-介绍","children":[]},{"level":2,"title":"相关文档","slug":"相关文档","link":"#相关文档","children":[]},{"level":2,"title":"相关工具","slug":"相关工具","link":"#相关工具","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/plugins/seo/guide.md"}');export{j as comp,q as data};
diff --git a/assets/index.html-4J_SM0OC.js b/assets/index.html-4J_SM0OC.js
new file mode 100644
index 0000000000..6966322477
--- /dev/null
+++ b/assets/index.html-4J_SM0OC.js
@@ -0,0 +1 @@
+import{_ as a,r as o,o as n,c as s,a as r,b as e}from"./app-BcH8wZQx.js";const c={},l=e("h1",{id:"主题",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#主题"},[e("span",null,"主题")])],-1);function m(i,h){const t=o("Catalog");return n(),s("div",null,[l,r(t,{level:1})])}const _=a(c,[["render",m],["__file","index.html.vue"]]),p=JSON.parse('{"path":"/zh/themes/","title":"主题","lang":"zh-CN","frontmatter":{},"headers":[],"git":{"updatedTime":1708364547000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/themes/README.md"}');export{_ as comp,p as data};
diff --git a/assets/index.html-B9g5ZyHl.js b/assets/index.html-B9g5ZyHl.js
new file mode 100644
index 0000000000..89907f59f2
--- /dev/null
+++ b/assets/index.html-B9g5ZyHl.js
@@ -0,0 +1,11 @@
+import{_ as n,r as a,o as l,c as o,a as i,b as s,e as p}from"./app-BcH8wZQx.js";const c={},t=s("h1",{id:"seo",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#seo"},[s("span",null,"seo")])],-1),r=p(`npm i -D @vuepress/plugin-seo@next
+
import { seoPlugin } from '@vuepress/plugin-seo'
+
+export default {
+ plugins: [
+ seoPlugin ({
+ // options
+ }),
+ ],
+}
+
`,3);function d(u,m){const e=a("NpmBadge");return l(),o("div",null,[t,i(e,{package:"@vuepress/plugin-seo"}),r])}const v=n(c,[["render",d],["__file","index.html.vue"]]),g=JSON.parse('{"path":"/plugins/seo/","title":"seo","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/seo/README.md"}');export{v as comp,g as data};
diff --git a/assets/index.html-BA6JzZr4.js b/assets/index.html-BA6JzZr4.js
new file mode 100644
index 0000000000..c9614febe7
--- /dev/null
+++ b/assets/index.html-BA6JzZr4.js
@@ -0,0 +1,11 @@
+import{_ as n,r as e,o as l,c as p,a as i,b as s,e as o}from"./app-BcH8wZQx.js";const c={},t=s("h1",{id:"pwa",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#pwa"},[s("span",null,"pwa")])],-1),r=o(`npm i -D @vuepress/plugin-pwa@next
+
import { pwaPlugin } from '@vuepress/plugin-pwa'
+
+export default {
+ plugins: [
+ pwaPlugin ({
+ // options
+ }),
+ ],
+}
+
`,3);function d(u,m){const a=e("NpmBadge");return l(),p("div",null,[t,i(a,{package:"@vuepress/plugin-pwa"}),r])}const v=n(c,[["render",d],["__file","index.html.vue"]]),g=JSON.parse('{"path":"/plugins/pwa/","title":"pwa","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]}],"git":{"updatedTime":1708365979000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/pwa/README.md"}');export{v as comp,g as data};
diff --git a/assets/index.html-BCYzS3zE.js b/assets/index.html-BCYzS3zE.js
new file mode 100644
index 0000000000..f72501364a
--- /dev/null
+++ b/assets/index.html-BCYzS3zE.js
@@ -0,0 +1,11 @@
+import{_ as n,r as a,o as l,c as o,a as i,b as s,e as p}from"./app-BcH8wZQx.js";const c={},r=s("h1",{id:"seo",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#seo"},[s("span",null,"seo")])],-1),t=p(`npm i -D @vuepress/plugin-seo@next
+
import { seoPlugin } from '@vuepress/plugin-seo'
+
+export default {
+ plugins: [
+ seoPlugin ({
+ // 选项
+ }),
+ ],
+}
+
`,3);function d(u,m){const e=a("NpmBadge");return l(),o("div",null,[r,i(e,{package:"@vuepress/plugin-seo"}),t])}const v=n(c,[["render",d],["__file","index.html.vue"]]),h=JSON.parse('{"path":"/zh/plugins/seo/","title":"seo","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/seo/README.md"}');export{v as comp,h as data};
diff --git a/assets/index.html-BNqR2Pam.js b/assets/index.html-BNqR2Pam.js
new file mode 100644
index 0000000000..3353b332b8
--- /dev/null
+++ b/assets/index.html-BNqR2Pam.js
@@ -0,0 +1,11 @@
+import{_ as e,r as n,o as l,c as i,a as p,b as s,e as t}from"./app-BcH8wZQx.js";const o={},c=s("h1",{id:"sitemap",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#sitemap"},[s("span",null,"sitemap")])],-1),r=t(`npm i -D @vuepress/plugin-sitemap@next
+
import { sitemapPlugin } from '@vuepress/plugin-sitemap'
+
+export default {
+ plugins: [
+ sitemapPlugin ({
+ // options
+ }),
+ ],
+}
+
`,3);function d(m,u){const a=n("NpmBadge");return l(),i("div",null,[c,p(a,{package:"@vuepress/plugin-sitemap"}),r])}const v=e(o,[["render",d],["__file","index.html.vue"]]),g=JSON.parse('{"path":"/plugins/sitemap/","title":"sitemap","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/sitemap/README.md"}');export{v as comp,g as data};
diff --git a/assets/index.html-BPyBIGBN.js b/assets/index.html-BPyBIGBN.js
new file mode 100644
index 0000000000..e46970385d
--- /dev/null
+++ b/assets/index.html-BPyBIGBN.js
@@ -0,0 +1 @@
+import{_ as t,r as o,o as a,c as n,a as l}from"./app-BcH8wZQx.js";const s={};function c(r,_){const e=o("Catalog");return a(),n("div",null,[l(e)])}const m=t(s,[["render",c],["__file","index.html.vue"]]),p=JSON.parse('{"path":"/tools/","title":"Tools","lang":"en-US","frontmatter":{"title":"Tools"},"headers":[],"git":{},"filePathRelative":null}');export{m as comp,p as data};
diff --git a/assets/index.html-BskDfEji.js b/assets/index.html-BskDfEji.js
new file mode 100644
index 0000000000..e0a1c73fef
--- /dev/null
+++ b/assets/index.html-BskDfEji.js
@@ -0,0 +1 @@
+import{_ as a,r as o,o as n,c as s,a as l,b as e}from"./app-BcH8wZQx.js";const r={},c=e("h1",{id:"plugins",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#plugins"},[e("span",null,"Plugins")])],-1);function i(m,p){const t=o("Catalog");return n(),s("div",null,[c,l(t,{level:1})])}const _=a(r,[["render",i],["__file","index.html.vue"]]),u=JSON.parse('{"path":"/plugins/","title":"Plugins","lang":"en-US","frontmatter":{},"headers":[],"git":{"updatedTime":1708364547000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/README.md"}');export{_ as comp,u as data};
diff --git a/assets/index.html-Bwmip8jk.js b/assets/index.html-Bwmip8jk.js
new file mode 100644
index 0000000000..4c5b766e86
--- /dev/null
+++ b/assets/index.html-Bwmip8jk.js
@@ -0,0 +1,11 @@
+import{_ as n,r as e,o as l,c as p,a as i,b as s,e as o}from"./app-BcH8wZQx.js";const c={},r=s("h1",{id:"pwa",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#pwa"},[s("span",null,"pwa")])],-1),t=o(`npm i -D @vuepress/plugin-pwa@next
+
import { pwaPlugin } from '@vuepress/plugin-pwa'
+
+export default {
+ plugins: [
+ pwaPlugin ({
+ // 选项
+ }),
+ ],
+}
+
`,3);function d(u,m){const a=e("NpmBadge");return l(),p("div",null,[r,i(a,{package:"@vuepress/plugin-pwa"}),t])}const v=n(c,[["render",d],["__file","index.html.vue"]]),h=JSON.parse('{"path":"/zh/plugins/pwa/","title":"pwa","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]}],"git":{"updatedTime":1708365979000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/pwa/README.md"}');export{v as comp,h as data};
diff --git a/assets/index.html-Bxz8_Cii.js b/assets/index.html-Bxz8_Cii.js
new file mode 100644
index 0000000000..c41fd8c6e7
--- /dev/null
+++ b/assets/index.html-Bxz8_Cii.js
@@ -0,0 +1 @@
+import{_ as t,r as o,o as a,c as n,a as l}from"./app-BcH8wZQx.js";const s={};function c(r,_){const e=o("Catalog");return a(),n("div",null,[l(e)])}const m=t(s,[["render",c],["__file","index.html.vue"]]),p=JSON.parse('{"path":"/zh/tools/","title":"Tools","lang":"en-US","frontmatter":{"title":"Tools"},"headers":[],"git":{},"filePathRelative":null}');export{m as comp,p as data};
diff --git a/assets/index.html-C-dQ67mk.js b/assets/index.html-C-dQ67mk.js
new file mode 100644
index 0000000000..1587f3ed47
--- /dev/null
+++ b/assets/index.html-C-dQ67mk.js
@@ -0,0 +1,11 @@
+import{_ as n,r as a,o as l,c as i,a as p,b as s,e as o}from"./app-BcH8wZQx.js";const c={},r=s("h1",{id:"feed",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#feed"},[s("span",null,"feed")])],-1),t=o(`npm i -D @vuepress/plugin-feed@next
+
import { feedPlugin } from '@vuepress/plugin-feed'
+
+export default {
+ plugins: [
+ feedPlugin ({
+ // 选项
+ }),
+ ],
+}
+
`,3);function d(u,m){const e=a("NpmBadge");return l(),i("div",null,[r,p(e,{package:"@vuepress/plugin-feed"}),t])}const v=n(c,[["render",d],["__file","index.html.vue"]]),h=JSON.parse('{"path":"/zh/plugins/feed/","title":"feed","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/feed/README.md"}');export{v as comp,h as data};
diff --git a/assets/index.html-CGjoXFU5.js b/assets/index.html-CGjoXFU5.js
new file mode 100644
index 0000000000..be50a216dc
--- /dev/null
+++ b/assets/index.html-CGjoXFU5.js
@@ -0,0 +1 @@
+import{_ as t,r as o,o as a,c as n,a as l}from"./app-BcH8wZQx.js";const r={};function c(s,_){const e=o("Catalog");return a(),n("div",null,[l(e)])}const d=t(r,[["render",c],["__file","index.html.vue"]]),m=JSON.parse('{"path":"/tools/helper/node/","title":"Node","lang":"en-US","frontmatter":{"title":"Node"},"headers":[],"git":{},"filePathRelative":null}');export{d as comp,m as data};
diff --git a/assets/index.html-CJsfyJgK.js b/assets/index.html-CJsfyJgK.js
new file mode 100644
index 0000000000..3460fe8643
--- /dev/null
+++ b/assets/index.html-CJsfyJgK.js
@@ -0,0 +1,9 @@
+import{_ as a,r as n,o as l,c as t,a as o,b as s,e as p}from"./app-BcH8wZQx.js";const i={},c=s("h1",{id:"默认主题",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#默认主题"},[s("span",null,"默认主题")])],-1),r=s("h2",{id:"使用方法",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#使用方法"},[s("span",null,"使用方法")])],-1),d=p(`安装默认主题:
npm i -D @vuepress/theme-default@next
+
在配置文件中指定主题:
import { defaultTheme } from '@vuepress/theme-default'
+
+export default {
+ theme: defaultTheme ({
+ // 在这里添加主题配置
+ }),
+}
+
`,4);function m(u,h){const e=n("NpmBadge");return l(),t("div",null,[c,r,o(e,{package:"@vuepress/theme-default"}),d])}const D=a(i,[["render",m],["__file","index.html.vue"]]),b=JSON.parse('{"path":"/zh/themes/default/","title":"默认主题","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]}],"git":{"updatedTime":1707124819000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/themes/default/README.md"}');export{D as comp,b as data};
diff --git a/assets/index.html-CVjqGs6U.js b/assets/index.html-CVjqGs6U.js
new file mode 100644
index 0000000000..d59418c8ef
--- /dev/null
+++ b/assets/index.html-CVjqGs6U.js
@@ -0,0 +1,11 @@
+import{_ as a,r as e,o as l,c as o,a as i,b as s,e as p}from"./app-BcH8wZQx.js";const c={},r=s("h1",{id:"blog",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#blog"},[s("span",null,"blog")])],-1),t=p(`npm i -D @vuepress/plugin-blog@next
+
import { blogPlugin } from '@vuepress/plugin-blog'
+
+export default {
+ plugins: [
+ blogPlugin ({
+ // 选项
+ }),
+ ],
+}
+
`,3);function d(u,m){const n=e("NpmBadge");return l(),o("div",null,[r,i(n,{package:"@vuepress/plugin-blog"}),t])}const v=a(c,[["render",d],["__file","index.html.vue"]]),b=JSON.parse('{"path":"/zh/plugins/blog/","title":"blog","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]}],"git":{"updatedTime":1707125652000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/blog/README.md"}');export{v as comp,b as data};
diff --git a/assets/index.html-C_SQhwFY.js b/assets/index.html-C_SQhwFY.js
new file mode 100644
index 0000000000..bcabbb3c4f
--- /dev/null
+++ b/assets/index.html-C_SQhwFY.js
@@ -0,0 +1 @@
+import{_ as a,r as o,o as n,c as s,a as r,b as e}from"./app-BcH8wZQx.js";const c={},l=e("h1",{id:"插件",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#插件"},[e("span",null,"插件")])],-1);function i(m,d){const t=o("Catalog");return n(),s("div",null,[l,r(t,{level:1})])}const h=a(c,[["render",i],["__file","index.html.vue"]]),p=JSON.parse('{"path":"/zh/plugins/","title":"插件","lang":"zh-CN","frontmatter":{},"headers":[],"git":{"updatedTime":1708364547000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/README.md"}');export{h as comp,p as data};
diff --git a/assets/index.html-DHEV9XEA.js b/assets/index.html-DHEV9XEA.js
new file mode 100644
index 0000000000..0661541f6a
--- /dev/null
+++ b/assets/index.html-DHEV9XEA.js
@@ -0,0 +1 @@
+import{_ as a,r as n,o as h,c as p,a as l,b as e,w as s,d as t}from"./app-BcH8wZQx.js";const u={},c=e("h1",{id:"vuepress-helper",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#vuepress-helper"},[e("span",null,"@vuepress/helper")])],-1),d=e("p",null,"此包为 VuePress 开发者提供辅助函数。",-1),i=e("p",null,[e("code",null,"@vuepress/helper"),t(": Node.js 一侧的辅助函数。")],-1),_=e("code",null,"@vuepress/helper/client",-1),m=e("code",null,"@vuepress/helper/shared",-1);function f(v,N){const r=n("NpmBadge"),o=n("RouteLink");return h(),p("div",null,[c,l(r,{package:"@vuepress/helper"}),d,e("ul",null,[e("li",null,[i,e("ul",null,[e("li",null,[l(o,{to:"/zh/tools/helper/node/bundler.html"},{default:s(()=>[t("打包器相关")]),_:1})]),e("li",null,[l(o,{to:"/zh/tools/helper/node/page.html"},{default:s(()=>[t("页面相关")]),_:1})])])]),e("li",null,[e("p",null,[l(o,{to:"/zh/tools/helper/client.html"},{default:s(()=>[_]),_:1}),t(": 客户端一侧的辅助函数。")])]),e("li",null,[e("p",null,[l(o,{to:"/zh/tools/helper/shared.html"},{default:s(()=>[m]),_:1}),t(": Node.js 和客户端共享的辅助函数。")])])])])}const z=a(u,[["render",f],["__file","index.html.vue"]]),g=JSON.parse('{"path":"/zh/tools/helper/","title":"@vuepress/helper","lang":"zh-CN","frontmatter":{},"headers":[],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/tools/helper/README.md"}');export{z as comp,g as data};
diff --git a/assets/index.html-DKZkuLx8.js b/assets/index.html-DKZkuLx8.js
new file mode 100644
index 0000000000..204edb0e57
--- /dev/null
+++ b/assets/index.html-DKZkuLx8.js
@@ -0,0 +1 @@
+import{_ as a,r as o,o as s,c as n,a as r,b as e}from"./app-BcH8wZQx.js";const c={},m=e("h1",{id:"themes",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themes"},[e("span",null,"Themes")])],-1);function l(i,h){const t=o("Catalog");return s(),n("div",null,[m,r(t,{level:1})])}const _=a(c,[["render",l],["__file","index.html.vue"]]),p=JSON.parse('{"path":"/themes/","title":"Themes","lang":"en-US","frontmatter":{},"headers":[],"git":{"updatedTime":1708364547000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"themes/README.md"}');export{_ as comp,p as data};
diff --git a/assets/index.html-DO7rBdZF.js b/assets/index.html-DO7rBdZF.js
new file mode 100644
index 0000000000..8922d5f1cd
--- /dev/null
+++ b/assets/index.html-DO7rBdZF.js
@@ -0,0 +1 @@
+import{_ as e,o as t,c as o}from"./app-BcH8wZQx.js";const r={};function i(m,n){return t(),o("div")}const a=e(r,[["render",i],["__file","index.html.vue"]]),c=JSON.parse('{"path":"/","title":"Home","lang":"en-US","frontmatter":{"home":true,"title":"Home","heroImage":"/images/hero.png","actions":[{"text":"Themes","link":"./themes/","type":"primary"},{"text":"Plugins","link":"./plugins/","type":"primary"}],"footer":"MIT Licensed | Copyright © 2018-present VuePress Community"},"headers":[],"git":{"updatedTime":1707215383000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"README.md"}');export{a as comp,c as data};
diff --git a/assets/index.html-DVXwq_-A.js b/assets/index.html-DVXwq_-A.js
new file mode 100644
index 0000000000..138586858f
--- /dev/null
+++ b/assets/index.html-DVXwq_-A.js
@@ -0,0 +1,11 @@
+import{_ as a,r as e,o as l,c as o,a as i,b as s,e as p}from"./app-BcH8wZQx.js";const c={},t=s("h1",{id:"blog",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#blog"},[s("span",null,"blog")])],-1),r=p(`npm i -D @vuepress/plugin-blog@next
+
import { blogPlugin } from '@vuepress/plugin-blog'
+
+export default {
+ plugins: [
+ blogPlugin ({
+ // options
+ }),
+ ],
+}
+
`,3);function d(u,m){const n=e("NpmBadge");return l(),o("div",null,[t,i(n,{package:"@vuepress/plugin-blog"}),r])}const D=a(c,[["render",d],["__file","index.html.vue"]]),v=JSON.parse('{"path":"/plugins/blog/","title":"blog","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]}],"git":{"updatedTime":1707125652000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/blog/README.md"}');export{D as comp,v as data};
diff --git a/assets/index.html-N_oWok3e.js b/assets/index.html-N_oWok3e.js
new file mode 100644
index 0000000000..da8390935e
--- /dev/null
+++ b/assets/index.html-N_oWok3e.js
@@ -0,0 +1 @@
+import{_ as e,o as t,c as o}from"./app-BcH8wZQx.js";const r={};function a(i,m){return t(),o("div")}const s=e(r,[["render",a],["__file","index.html.vue"]]),c=JSON.parse('{"path":"/zh/","title":"首页","lang":"zh-CN","frontmatter":{"home":true,"title":"首页","heroImage":"/images/hero.png","actions":[{"text":"主题","link":"./themes/","type":"primary"},{"text":"插件","link":"./plugins/","type":"primary"}],"footer":"MIT 协议 | 版权所有 © 2018-至今 VuePress 社区"},"headers":[],"git":{"updatedTime":1707215383000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/README.md"}');export{s as comp,c as data};
diff --git a/assets/index.html-PL0FrDDR.js b/assets/index.html-PL0FrDDR.js
new file mode 100644
index 0000000000..e86ff76fef
--- /dev/null
+++ b/assets/index.html-PL0FrDDR.js
@@ -0,0 +1 @@
+import{_ as a,r,o as i,c as p,a as l,b as e,w as s,d as t}from"./app-BcH8wZQx.js";const d={},u=e("h1",{id:"vuepress-helper",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#vuepress-helper"},[e("span",null,"@vuepress/helper")])],-1),h=e("p",null,"This package is a helper utility for VuePress developers.",-1),c=e("p",null,[e("code",null,"@vuepress/helper"),t(": Node.js side helper utilities.")],-1),_=e("code",null,"@vuepress/helper/client",-1),m=e("code",null,"@vuepress/helper/shared",-1);function f(v,g){const n=r("NpmBadge"),o=r("RouteLink");return i(),p("div",null,[u,l(n,{package:"@vuepress/helper"}),h,e("ul",null,[e("li",null,[c,e("ul",null,[e("li",null,[l(o,{to:"/tools/helper/node/bundler.html"},{default:s(()=>[t("Bundler Related")]),_:1})]),e("li",null,[l(o,{to:"/tools/helper/node/page.html"},{default:s(()=>[t("Page Related")]),_:1})])])]),e("li",null,[e("p",null,[l(o,{to:"/tools/helper/client.html"},{default:s(()=>[_]),_:1}),t(": Client side helper utilities.")])]),e("li",null,[e("p",null,[l(o,{to:"/tools/helper/shared.html"},{default:s(()=>[m]),_:1}),t(": Utilities that are both available at Node.js side or Client.")])])])])}const N=a(d,[["render",f],["__file","index.html.vue"]]),k=JSON.parse('{"path":"/tools/helper/","title":"@vuepress/helper","lang":"en-US","frontmatter":{},"headers":[],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"tools/helper/README.md"}');export{N as comp,k as data};
diff --git a/assets/index.html-bT4s4bUW.js b/assets/index.html-bT4s4bUW.js
new file mode 100644
index 0000000000..482571ee73
--- /dev/null
+++ b/assets/index.html-bT4s4bUW.js
@@ -0,0 +1,11 @@
+import{_ as e,r as n,o as l,c as i,a as p,b as s,e as t}from"./app-BcH8wZQx.js";const o={},c=s("h1",{id:"sitemap",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#sitemap"},[s("span",null,"sitemap")])],-1),r=t(`npm i -D @vuepress/plugin-sitemap@next
+
import { sitemapPlugin } from '@vuepress/plugin-sitemap'
+
+export default {
+ plugins: [
+ sitemapPlugin ({
+ // 选项
+ }),
+ ],
+}
+
`,3);function d(m,u){const a=n("NpmBadge");return l(),i("div",null,[c,p(a,{package:"@vuepress/plugin-sitemap"}),r])}const v=e(o,[["render",d],["__file","index.html.vue"]]),h=JSON.parse('{"path":"/zh/plugins/sitemap/","title":"sitemap","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/sitemap/README.md"}');export{v as comp,h as data};
diff --git a/assets/index.html-d1K66q2T.js b/assets/index.html-d1K66q2T.js
new file mode 100644
index 0000000000..9189898fab
--- /dev/null
+++ b/assets/index.html-d1K66q2T.js
@@ -0,0 +1,11 @@
+import{_ as n,r as a,o as l,c as i,a as p,b as s,e as o}from"./app-BcH8wZQx.js";const c={},t=s("h1",{id:"feed",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#feed"},[s("span",null,"feed")])],-1),r=o(`npm i -D @vuepress/plugin-feed@next
+
import { feedPlugin } from '@vuepress/plugin-feed'
+
+export default {
+ plugins: [
+ feedPlugin ({
+ // options
+ }),
+ ],
+}
+
`,3);function d(u,m){const e=a("NpmBadge");return l(),i("div",null,[t,p(e,{package:"@vuepress/plugin-feed"}),r])}const v=n(c,[["render",d],["__file","index.html.vue"]]),g=JSON.parse('{"path":"/plugins/feed/","title":"feed","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/feed/README.md"}');export{v as comp,g as data};
diff --git a/assets/index.html-fi5f_VJY.js b/assets/index.html-fi5f_VJY.js
new file mode 100644
index 0000000000..e088a4b64a
--- /dev/null
+++ b/assets/index.html-fi5f_VJY.js
@@ -0,0 +1 @@
+import{_ as t,r as o,o as a,c as n,a as l}from"./app-BcH8wZQx.js";const r={};function c(s,_){const e=o("Catalog");return a(),n("div",null,[l(e)])}const d=t(r,[["render",c],["__file","index.html.vue"]]),m=JSON.parse('{"path":"/zh/tools/helper/node/","title":"Node","lang":"en-US","frontmatter":{"title":"Node"},"headers":[],"git":{},"filePathRelative":null}');export{d as comp,m as data};
diff --git a/assets/index.html-gvC-_Ya4.js b/assets/index.html-gvC-_Ya4.js
new file mode 100644
index 0000000000..c1c102bbeb
--- /dev/null
+++ b/assets/index.html-gvC-_Ya4.js
@@ -0,0 +1,9 @@
+import{_ as a,r as n,o as l,c as t,a as o,b as e,e as i}from"./app-BcH8wZQx.js";const c={},p=e("h1",{id:"theme-default",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#theme-default"},[e("span",null,"theme-default")])],-1),r=e("h2",{id:"usage",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#usage"},[e("span",null,"Usage")])],-1),d=i(`Install @vuepress/theme-default
:
npm install @vuepress/theme-default@next
+
import { defaultTheme } from '@vuepress/theme-default'
+
+export default {
+ theme: defaultTheme ({
+ // set theme config here
+ }),
+}
+
`,3);function m(u,h){const s=n("NpmBadge");return l(),t("div",null,[p,r,o(s,{package:"@vuepress/theme-default"}),d])}const D=a(c,[["render",m],["__file","index.html.vue"]]),f=JSON.parse('{"path":"/themes/default/","title":"theme-default","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]}],"git":{"updatedTime":1707124819000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"themes/default/README.md"}');export{D as comp,f as data};
diff --git a/assets/locale.html-D1TGwjKd.js b/assets/locale.html-D1TGwjKd.js
new file mode 100644
index 0000000000..36565393f4
--- /dev/null
+++ b/assets/locale.html-D1TGwjKd.js
@@ -0,0 +1,21 @@
+import{_ as i,r as o,o as p,c,b as e,d as l,a,w as s,e as t}from"./app-BcH8wZQx.js";const d={},r=e("h1",{id:"locale-config",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#locale-config"},[e("span",null,"Locale Config")])],-1),u=e("p",null,"These options configure locale-related texts.",-1),h=e("p",null,"If your site is served in a different language besides English, you should set these options per locale to provide translations.",-1),g=e("h2",{id:"repolabel",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#repolabel"},[e("span",null,"repoLabel")])],-1),m=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),f=e("p",null,"Details:",-1),b=e("p",null,"Specify the repository label of your project.",-1),_=e("p",null,[l("This will be used as the text of the "),e("em",null,"repository link"),l(", which will be displayed as the last item of the navbar.")],-1),D=e("h2",{id:"selectlanguagetext",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#selectlanguagetext"},[e("span",null,"selectLanguageText")])],-1),y=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),v=e("p",null,"Details:",-1),x=e("p",null,[l("Specify the text of the "),e("em",null,"select language menu"),l(".")],-1),k=e("em",null,"select language menu",-1),C=t(' ',3),w=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),T=e("p",null,"Details:",-1),E=e("p",null,"Specify the name of the language of a locale.",-1),L=e("strong",null,"only take effect inside",-1),S=e("em",null,"select language menu",-1),N=e("li",null,[e("p",null,"Example:")],-1),F=t(`export default {
+ locales: {
+ '/' : {
+ lang: 'en-US' ,
+ },
+ '/zh/' : {
+ lang: 'zh-CN' ,
+ },
+ },
+ theme: defaultTheme ({
+ locales: {
+ '/' : {
+ selectLanguageName: 'English' ,
+ },
+ '/zh/' : {
+ selectLanguageName: '简体中文' ,
+ },
+ },
+ }),
+}
+
`,12),I=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),A=e("li",null,[e("p",null,[l("Default: "),e("code",null,"'TIP'")])],-1),R=e("p",null,"Details:",-1),U=e("h2",{id:"warning",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#warning"},[e("span",null,"warning")])],-1),B=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),M=e("li",null,[e("p",null,[l("Default: "),e("code",null,"'WARNING'")])],-1),P=e("p",null,"Details:",-1),V=e("h2",{id:"danger",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#danger"},[e("span",null,"danger")])],-1),z=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),H=e("li",null,[e("p",null,[l("Default: "),e("code",null,"'DANGER'")])],-1),W=e("p",null,"Details:",-1),G=t(' ',5),j=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),J=e("li",null,[e("p",null,[l("Default: "),e("code",null,"'open in new window'")])],-1),O=e("p",null,"Details:",-1),q=e("code",null,"sr-only",-1),K=e("p",null,"This is mainly for a11y purpose.",-1),Q=e("p",null,"Also see:",-1),X=e("h2",{id:"togglecolormode",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#togglecolormode"},[e("span",null,"toggleColorMode")])],-1),Y=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),Z=e("li",null,[e("p",null,[l("Default: "),e("code",null,"'toggle color mode'")])],-1),$=e("li",null,[e("p",null,"Details:"),e("p",null,"Title text for the color mode toggle button."),e("p",null,"This is mainly for a11y purpose.")],-1),ee=e("p",null,"Also see:",-1),le=t('Type: string
Default: 'toggle sidebar'
Details:
Title text for sidebar toggle button.
This is mainly for a11y purpose.
',2);function ne(ae,se){const n=o("RouteLink");return p(),c("div",null,[r,u,h,g,e("ul",null,[m,e("li",null,[f,b,_,e("p",null,[l("If you don't set this option explicitly, it will be automatically inferred from the "),a(n,{to:"/themes/default/config.html#repo"},{default:s(()=>[l("repo")]),_:1}),l(" option.")])])]),D,e("ul",null,[y,e("li",null,[v,x,e("p",null,[l("The "),k,l(" will appear next to the repository button in the navbar when you set multiple "),a(n,{to:"/themes/default/config.html#locales"},{default:s(()=>[l("locales")]),_:1}),l(" in your site config.")])])]),C,e("ul",null,[w,e("li",null,[T,E,e("p",null,[l("This option will "),L,l(" the "),a(n,{to:"/themes/default/config.html#locales"},{default:s(()=>[l("locales")]),_:1}),l(" of your theme config. It will be used as the language name of the locale, which will be displayed in the "),S,l(".")])]),N]),F,e("ul",null,[I,A,e("li",null,[R,e("p",null,[l("Specify the default title of the tip "),a(n,{to:"/themes/default/markdown.html#custom-containers"},{default:s(()=>[l("custom containers")]),_:1}),l(".")])])]),U,e("ul",null,[B,M,e("li",null,[P,e("p",null,[l("Specify the default title of the warning "),a(n,{to:"/themes/default/markdown.html#custom-containers"},{default:s(()=>[l("custom containers")]),_:1}),l(".")])])]),V,e("ul",null,[z,H,e("li",null,[W,e("p",null,[l("Specify the default title of the danger "),a(n,{to:"/themes/default/markdown.html#custom-containers"},{default:s(()=>[l("custom containers")]),_:1}),l(".")])])]),G,e("ul",null,[j,J,e("li",null,[O,e("p",null,[l("Specify the "),q,l(" text of the "),a(n,{to:"/plugins/external-link-icon.html#externallinkicon"},{default:s(()=>[l("ExternalLinkIcon")]),_:1}),l(".")]),K]),e("li",null,[Q,e("ul",null,[e("li",null,[a(n,{to:"/themes/default/plugin.html#themeplugins-externallinkicon"},{default:s(()=>[l("Default Theme > Plugin Config > themePlugins.externalLinkIcon")]),_:1})])])])]),X,e("ul",null,[Y,Z,$,e("li",null,[ee,e("ul",null,[e("li",null,[a(n,{to:"/themes/default/config.html#colormodeswitch"},{default:s(()=>[l("Default Theme > Config > colorModeSwitch")]),_:1})])])])]),le])}const ie=i(d,[["render",ne],["__file","locale.html.vue"]]),oe=JSON.parse('{"path":"/themes/default/locale.html","title":"Locale Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"repoLabel","slug":"repolabel","link":"#repolabel","children":[]},{"level":2,"title":"selectLanguageText","slug":"selectlanguagetext","link":"#selectlanguagetext","children":[]},{"level":2,"title":"selectLanguageAriaLabel","slug":"selectlanguagearialabel","link":"#selectlanguagearialabel","children":[]},{"level":2,"title":"selectLanguageName","slug":"selectlanguagename","link":"#selectlanguagename","children":[]},{"level":2,"title":"navbarLabel","slug":"navbarlabel","link":"#navbarlabel","children":[]},{"level":2,"title":"pageNavbarLabel","slug":"pagenavbarlabel","link":"#pagenavbarlabel","children":[]},{"level":2,"title":"editLinkText","slug":"editlinktext","link":"#editlinktext","children":[]},{"level":2,"title":"lastUpdatedText","slug":"lastupdatedtext","link":"#lastupdatedtext","children":[]},{"level":2,"title":"contributorsText","slug":"contributorstext","link":"#contributorstext","children":[]},{"level":2,"title":"tip","slug":"tip","link":"#tip","children":[]},{"level":2,"title":"warning","slug":"warning","link":"#warning","children":[]},{"level":2,"title":"danger","slug":"danger","link":"#danger","children":[]},{"level":2,"title":"notFound","slug":"notfound","link":"#notfound","children":[]},{"level":2,"title":"backToHome","slug":"backtohome","link":"#backtohome","children":[]},{"level":2,"title":"openInNewWindow","slug":"openinnewwindow","link":"#openinnewwindow","children":[]},{"level":2,"title":"toggleColorMode","slug":"togglecolormode","link":"#togglecolormode","children":[]},{"level":2,"title":"toggleSidebar","slug":"togglesidebar","link":"#togglesidebar","children":[]}],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"themes/default/locale.md"}');export{ie as comp,oe as data};
diff --git a/assets/locale.html-mlytCd5l.js b/assets/locale.html-mlytCd5l.js
new file mode 100644
index 0000000000..5ff754e9f3
--- /dev/null
+++ b/assets/locale.html-mlytCd5l.js
@@ -0,0 +1,21 @@
+import{_ as c,r as o,o as p,c as d,b as l,d as e,a as n,w as s,e as t}from"./app-BcH8wZQx.js";const r={},u=l("h1",{id:"语言配置",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#语言配置"},[l("span",null,"语言配置")])],-1),h=l("p",null,"这些选项用于配置与语言相关的文本。",-1),g=l("p",null,"如果你的站点是以英语以外的其他语言提供服务的,你应该为每个语言设置这些选项来提供翻译。",-1),_=l("h2",{id:"repolabel",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#repolabel"},[l("span",null,"repoLabel")])],-1),b=l("li",null,[l("p",null,[e("类型: "),l("code",null,"string")])],-1),m=l("p",null,"详情:",-1),v=l("p",null,"项目仓库的标签。",-1),D=l("p",null,[e("它将被用作 "),l("em",null,"仓库链接"),e(" 的文字。"),l("em",null,"仓库链接"),e(" 将会显示为导航栏的最后一个元素。")],-1),x=l("h2",{id:"selectlanguagetext",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#selectlanguagetext"},[l("span",null,"selectLanguageText")])],-1),f=l("li",null,[l("p",null,[e("类型: "),l("code",null,"string")])],-1),E=l("p",null,"详情:",-1),k=l("p",null,[l("em",null,"选择语言菜单"),e(" 的文字。")],-1),y={href:"https://v2.vuepress.vuejs.org/zh/config.html#locales",target:"_blank",rel:"noopener noreferrer"},C=l("em",null,"选择语言菜单",-1),w=t('类型: string
详情:
选择语言菜单 的 aria-label
属性。
它主要是为了站点的可访问性 (a11y) 。
',3),L=l("li",null,[l("p",null,[e("类型: "),l("code",null,"string")])],-1),A=l("p",null,"详情:",-1),N=l("p",null,"Locale 的语言名称。",-1),z=l("em",null,"选择语言菜单",-1),T=l("li",null,[l("p",null,"示例:")],-1),F=t(`export default {
+ locales: {
+ '/' : {
+ lang: 'en-US' ,
+ },
+ '/zh/' : {
+ lang: 'zh-CN' ,
+ },
+ },
+ theme: defaultTheme ({
+ locales: {
+ '/' : {
+ selectLanguageName: 'English' ,
+ },
+ '/zh/' : {
+ selectLanguageName: '简体中文' ,
+ },
+ },
+ }),
+}
+
类型:null | string
详情:
导航栏中主导航 aria-label
属性的值。
类型: string
默认值: 'Edit this page'
详情:
编辑此页 链接的文字。
类型: string
默认值: 'Last Updated'
详情:
最近更新时间戳 标签的文字。
类型: string
默认值: 'Contributors'
详情:
贡献者列表 标签的文字。
`,12),B=l("li",null,[l("p",null,[e("类型: "),l("code",null,"string")])],-1),I=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"'TIP'")])],-1),S=l("p",null,"详情:",-1),R=l("h2",{id:"warning",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#warning"},[l("span",null,"warning")])],-1),M=l("li",null,[l("p",null,[e("类型: "),l("code",null,"string")])],-1),U=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"'WARNING'")])],-1),V=l("p",null,"详情:",-1),W=l("h2",{id:"danger",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#danger"},[l("span",null,"danger")])],-1),H=l("li",null,[l("p",null,[e("类型: "),l("code",null,"string")])],-1),P=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"'DANGER'")])],-1),G=l("p",null,"详情:",-1),j=t('类型: string
默认值: 'Back to home'
详情:
404 页面中 返回首页 链接的文字。
',5),J=l("li",null,[l("p",null,[e("类型: "),l("code",null,"string")])],-1),O=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"'open in new window'")])],-1),q=l("p",null,"详情:",-1),K=l("code",null,"sr-only",-1),Q=l("p",null,"它主要是为了站点的可访问性 (a11y) 。",-1),X=l("p",null,"参考:",-1),Y=l("h2",{id:"togglecolormode",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#togglecolormode"},[l("span",null,"toggleColorMode")])],-1),Z=l("li",null,[l("p",null,[e("类型: "),l("code",null,"string")])],-1),$=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"'toggle color mode'")])],-1),ll=l("li",null,[l("p",null,"详情:"),l("p",null,"切换颜色模式按钮的标题文字。"),l("p",null,"它主要是为了站点的可访问性 (a11y) 。")],-1),el=l("p",null,"参考:",-1),nl=t('类型: string
默认值: 'toggle sidebar'
详情:
切换侧边栏按钮的标题文字。
它主要是为了站点的可访问性 (a11y) 。
',2);function al(sl,tl){const a=o("RouteLink"),i=o("ExternalLinkIcon");return p(),d("div",null,[u,h,g,_,l("ul",null,[b,l("li",null,[m,v,D,l("p",null,[e("如果你不明确指定该配置项,它将会根据 "),n(a,{to:"/zh/themes/default/config.html#repo"},{default:s(()=>[e("repo")]),_:1}),e(" 配置项自动推断。")])])]),x,l("ul",null,[f,l("li",null,[E,k,l("p",null,[e("如果你在站点配置中设置了多个 "),l("a",y,[e("locales"),n(i)]),e(" ,那么 "),C,e(" 就会显示在导航栏中仓库按钮的旁边。")])])]),w,l("ul",null,[L,l("li",null,[A,N,l("p",null,[e("该配置项 "),l("strong",null,[e("仅能在主题配置的 "),n(a,{to:"/zh/themes/default/config.html#locales"},{default:s(()=>[e("locales")]),_:1}),e(" 的内部生效")]),e(" 。它将被用作 locale 的语言名称,展示在 "),z,e(" 内。")])]),T]),F,l("ul",null,[B,I,l("li",null,[S,l("p",null,[e("Tip "),n(a,{to:"/zh/themes/default/markdown.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%B9%E5%99%A8"},{default:s(()=>[e("自定义容器")]),_:1}),e(" 的默认标题。")])])]),R,l("ul",null,[M,U,l("li",null,[V,l("p",null,[e("Warning "),n(a,{to:"/zh/themes/default/markdown.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%B9%E5%99%A8"},{default:s(()=>[e("自定义容器")]),_:1}),e(" 的默认标题。")])])]),W,l("ul",null,[H,P,l("li",null,[G,l("p",null,[e("Danger "),n(a,{to:"/zh/themes/default/markdown.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%B9%E5%99%A8"},{default:s(()=>[e("自定义容器")]),_:1}),e(" 的默认标题。")])])]),j,l("ul",null,[J,O,l("li",null,[q,l("p",null,[n(a,{to:"/zh/plugins/external-link-icon.html#externallinkicon"},{default:s(()=>[e("ExternalLinkIcon")]),_:1}),e(". 链接内的 "),K,e(" 文字。")]),Q]),l("li",null,[X,l("ul",null,[l("li",null,[n(a,{to:"/zh/themes/default/plugin.html#themeplugins-externallinkicon"},{default:s(()=>[e("默认主题 > 插件配置 > themePlugins.externalLinkIcon")]),_:1})])])])]),Y,l("ul",null,[Z,$,ll,l("li",null,[el,l("ul",null,[l("li",null,[n(a,{to:"/zh/themes/default/config.html#colormodeswitch"},{default:s(()=>[e("默认主题 > 配置 > colorModeSwitch")]),_:1})])])])]),nl])}const il=c(r,[["render",al],["__file","locale.html.vue"]]),cl=JSON.parse('{"path":"/zh/themes/default/locale.html","title":"语言配置","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"repoLabel","slug":"repolabel","link":"#repolabel","children":[]},{"level":2,"title":"selectLanguageText","slug":"selectlanguagetext","link":"#selectlanguagetext","children":[]},{"level":2,"title":"selectLanguageAriaLabel","slug":"selectlanguagearialabel","link":"#selectlanguagearialabel","children":[]},{"level":2,"title":"selectLanguageName","slug":"selectlanguagename","link":"#selectlanguagename","children":[]},{"level":2,"title":"navbarLabel","slug":"navbarlabel","link":"#navbarlabel","children":[]},{"level":2,"title":"pageNavbarLabel","slug":"pagenavbarlabel","link":"#pagenavbarlabel","children":[]},{"level":2,"title":"editLinkText","slug":"editlinktext","link":"#editlinktext","children":[]},{"level":2,"title":"lastUpdatedText","slug":"lastupdatedtext","link":"#lastupdatedtext","children":[]},{"level":2,"title":"contributorsText","slug":"contributorstext","link":"#contributorstext","children":[]},{"level":2,"title":"tip","slug":"tip","link":"#tip","children":[]},{"level":2,"title":"warning","slug":"warning","link":"#warning","children":[]},{"level":2,"title":"danger","slug":"danger","link":"#danger","children":[]},{"level":2,"title":"notFound","slug":"notfound","link":"#notfound","children":[]},{"level":2,"title":"backToHome","slug":"backtohome","link":"#backtohome","children":[]},{"level":2,"title":"openInNewWindow","slug":"openinnewwindow","link":"#openinnewwindow","children":[]},{"level":2,"title":"toggleColorMode","slug":"togglecolormode","link":"#togglecolormode","children":[]},{"level":2,"title":"toggleSidebar","slug":"togglesidebar","link":"#togglesidebar","children":[]}],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/themes/default/locale.md"}');export{il as comp,cl as data};
diff --git a/assets/markdown.html-BU-_rhjs.js b/assets/markdown.html-BU-_rhjs.js
new file mode 100644
index 0000000000..729e726a7d
--- /dev/null
+++ b/assets/markdown.html-BU-_rhjs.js
@@ -0,0 +1,50 @@
+import{_ as d,r as e,o as r,c as u,a,b as s,d as n,w as l,e as c}from"./app-BcH8wZQx.js";const D={},m=s("h1",{id:"markdown",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#markdown"},[s("span",null,"Markdown")])],-1),v=s("h2",{id:"custom-containers",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#custom-containers"},[s("span",null,"Custom Containers")])],-1),b=c(`Usage:
::: < type > [ title ]
+[ content ]
+:::
+
The type
is required, and the title
and content
are optional.
Supported type
:
`,4),y=s("li",null,[s("code",null,"tip")],-1),g=s("li",null,[s("code",null,"warning")],-1),h=s("li",null,[s("code",null,"danger")],-1),_=s("li",null,[s("code",null,"details")],-1),k=s("ul",null,[s("li",null,[s("code",null,"code-group")]),s("li",null,[s("code",null,"code-group-item")])],-1),C=s("li",null,[s("p",null,"Example 1 (default title):")],-1),E=c(`Input
::: tip
+This is a tip
+:::
+
+::: warning
+This is a warning
+:::
+
+::: danger
+This is a dangerous warning
+:::
+
+::: details
+This is a details block
+:::
+
Output
DANGER
This is a dangerous warning
This is a details block
Example 2 (custom title): Input
::: danger STOP
+Danger zone, do not proceed
+:::
+
+::: details Click me to view the code
+
+\`\`\`ts
+console . log ( 'Hello, VuePress!' )
+\`\`\`
+
+:::
+
Output
STOP
Danger zone, do not proceed
Click me to view the code console . log ( 'Hello, VuePress!' )
+
Example 3 (code group alias): Input
:::: code-group
+::: code-group-item FOO
+
+\`\`\`ts
+const foo = 'foo'
+\`\`\`
+
+:::
+
+::: code-group-item BAR
+
+\`\`\`ts
+const bar = 'bar'
+\`\`\`
+
+:::
+::::
+
Output
`,17),f=s("div",{class:"language-typescript line-numbers-mode","data-ext":"ts","data-title":"ts"},[s("pre",{class:"shiki dark-plus",style:{"background-color":"#1E1E1E",color:"#D4D4D4"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#569CD6"}},"const"),s("span",{style:{color:"#4FC1FF"}}," foo"),s("span",{style:{color:"#D4D4D4"}}," = "),s("span",{style:{color:"#CE9178"}},"'foo'")]),n(`
+`),s("span",{class:"line"})])]),s("div",{class:"line-numbers","aria-hidden":"true"},[s("div",{class:"line-number"})])],-1),x=s("div",{class:"language-typescript line-numbers-mode","data-ext":"ts","data-title":"ts"},[s("pre",{class:"shiki dark-plus",style:{"background-color":"#1E1E1E",color:"#D4D4D4"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#569CD6"}},"const"),s("span",{style:{color:"#4FC1FF"}}," bar"),s("span",{style:{color:"#D4D4D4"}}," = "),s("span",{style:{color:"#CE9178"}},"'bar'")]),n(`
+`),s("span",{class:"line"})])]),s("div",{class:"line-numbers","aria-hidden":"true"},[s("div",{class:"line-number"})])],-1);function w(F,T){const t=e("NpmBadge"),i=e("RouteLink"),o=e("CodeGroupItem"),p=e("CodeGroup");return r(),u("div",null,[m,a(t,{package:"@vuepress/theme-default"}),v,s("ul",null,[s("li",null,[b,s("ul",null,[y,g,h,_,s("li",null,[n("Alias of "),a(i,{to:"/themes/default/components.html#codegroup"},{default:l(()=>[n("CodeGroup")]),_:1}),n(" and "),a(i,{to:"/themes/default/components.html#codegroupitem"},{default:l(()=>[n("CodeGroupItem")]),_:1}),n(": "),k])])]),C]),E,a(p,null,{default:l(()=>[a(o,{title:"FOO"},{default:l(()=>[f]),_:1}),a(o,{title:"BAR"},{default:l(()=>[x]),_:1})]),_:1})])}const O=d(D,[["render",w],["__file","markdown.html.vue"]]),A=JSON.parse('{"path":"/themes/default/markdown.html","title":"Markdown","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Custom Containers","slug":"custom-containers","link":"#custom-containers","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"themes/default/markdown.md"}');export{O as comp,A as data};
diff --git a/assets/markdown.html-WIfWTHgC.js b/assets/markdown.html-WIfWTHgC.js
new file mode 100644
index 0000000000..42bd1b2f81
--- /dev/null
+++ b/assets/markdown.html-WIfWTHgC.js
@@ -0,0 +1,50 @@
+import{_ as d,r as e,o as r,c as u,a as n,b as s,w as a,d as l,e as p}from"./app-BcH8wZQx.js";const D={},m=s("h1",{id:"markdown",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#markdown"},[s("span",null,"Markdown")])],-1),v=s("h2",{id:"自定义容器",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#自定义容器"},[s("span",null,"自定义容器")])],-1),b=p(`使用:
::: < type > [ title ]
+[ content ]
+:::
+
type
是必需的, title
和 content
是可选的。
支持的 type
有:
`,4),y=s("li",null,[s("code",null,"tip")],-1),g=s("li",null,[s("code",null,"warning")],-1),h=s("li",null,[s("code",null,"danger")],-1),_=s("li",null,[s("code",null,"details")],-1),k=s("ul",null,[s("li",null,[s("code",null,"code-group")]),s("li",null,[s("code",null,"code-group-item")])],-1),C=s("li",null,[s("p",null,"示例 1 (默认标题):")],-1),E=p(`输入
::: tip
+这是一个提示
+:::
+
+::: warning
+这是一个警告
+:::
+
+::: danger
+这是一个危险警告
+:::
+
+::: details
+这是一个 details 标签
+:::
+
输出
这是一个 details 标签
输入
::: danger STOP
+危险区域,禁止通行
+:::
+
+::: details 点击查看代码
+
+\`\`\`ts
+console . log ( '你好,VuePress!' )
+\`\`\`
+
+:::
+
输出
点击查看代码 console . log ( '你好,VuePress!' )
+
输入
:::: code-group
+::: code-group-item FOO
+
+\`\`\`ts
+const foo = 'foo'
+\`\`\`
+
+:::
+
+::: code-group-item BAR
+
+\`\`\`ts
+const bar = 'bar'
+\`\`\`
+
+:::
+::::
+
输出
`,17),f=s("div",{class:"language-typescript line-numbers-mode","data-ext":"ts","data-title":"ts"},[s("pre",{class:"shiki dark-plus",style:{"background-color":"#1E1E1E",color:"#D4D4D4"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#569CD6"}},"const"),s("span",{style:{color:"#4FC1FF"}}," foo"),s("span",{style:{color:"#D4D4D4"}}," = "),s("span",{style:{color:"#CE9178"}},"'foo'")]),l(`
+`),s("span",{class:"line"})])]),s("div",{class:"line-numbers","aria-hidden":"true"},[s("div",{class:"line-number"})])],-1),x=s("div",{class:"language-typescript line-numbers-mode","data-ext":"ts","data-title":"ts"},[s("pre",{class:"shiki dark-plus",style:{"background-color":"#1E1E1E",color:"#D4D4D4"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#569CD6"}},"const"),s("span",{style:{color:"#4FC1FF"}}," bar"),s("span",{style:{color:"#D4D4D4"}}," = "),s("span",{style:{color:"#CE9178"}},"'bar'")]),l(`
+`),s("span",{class:"line"})])]),s("div",{class:"line-numbers","aria-hidden":"true"},[s("div",{class:"line-number"})])],-1);function w(F,N){const c=e("NpmBadge"),i=e("RouteLink"),o=e("CodeGroupItem"),t=e("CodeGroup");return r(),u("div",null,[m,n(c,{package:"@vuepress/theme-default"}),v,s("ul",null,[s("li",null,[b,s("ul",null,[y,g,h,_,s("li",null,[n(i,{to:"/zh/themes/default/components.html#codegroup"},{default:a(()=>[l("CodeGroup")]),_:1}),l(" 和 "),n(i,{to:"/zh/themes/default/components.html#codegroupitem"},{default:a(()=>[l("CodeGroupItem")]),_:1}),l(" 的别名: "),k])])]),C]),E,n(t,null,{default:a(()=>[n(o,{title:"FOO"},{default:a(()=>[f]),_:1}),n(o,{title:"BAR"},{default:a(()=>[x]),_:1})]),_:1})])}const G=d(D,[["render",w],["__file","markdown.html.vue"]]),O=JSON.parse('{"path":"/zh/themes/default/markdown.html","title":"Markdown","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"自定义容器","slug":"自定义容器","link":"#自定义容器","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/themes/default/markdown.md"}');export{G as comp,O as data};
diff --git a/assets/medium-zoom.html-BzW9Wo-K.js b/assets/medium-zoom.html-BzW9Wo-K.js
new file mode 100644
index 0000000000..b601e684b1
--- /dev/null
+++ b/assets/medium-zoom.html-BzW9Wo-K.js
@@ -0,0 +1,31 @@
+import{_ as p,r as o,o as c,c as t,a as e,b as s,d as n,e as a}from"./app-BcH8wZQx.js";const r={},d=s("h1",{id:"medium-zoom",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#medium-zoom"},[s("span",null,"medium-zoom")])],-1),m={href:"https://github.com/francoischalifour/medium-zoom#readme",target:"_blank",rel:"noopener noreferrer"},u=a(`该插件已经集成到默认主题中。
npm i -D @vuepress/plugin-medium-zoom@next
+
import { mediumZoomPlugin } from '@vuepress/plugin-medium-zoom'
+
+export default {
+ plugins: [
+ mediumZoomPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
`,10),D=s("li",null,[s("p",null,[n("类型: "),s("code",null,"Object")])],-1),h=s("li",null,[s("p",null,"详情:"),s("p",null,"medium-zoom 的配置项。")],-1),v=s("p",null,"参考:",-1),y={href:"https://github.com/francoischalifour/medium-zoom#options",target:"_blank",rel:"noopener noreferrer"},b=a(`你可以通过 zoomOptions 对大部分的缩放样式进行自定义,不过作为补充,该插件同样提供了一些 CSS 变量:
:root {
+ --medium-zoom-z-index : 100 ;
+ --medium-zoom-bg-color : #ffffff ;
+ --medium-zoom-opacity : 1 ;
+}
+
`,5),C=s("p",null,"详情:",-1),_=s("code",null,"Zoom",-1),g={href:"https://github.com/francoischalifour/medium-zoom#methods",target:"_blank",rel:"noopener noreferrer"},f=s("p",null,"该插件会在切换路由进入当前页面时使图片支持缩放。但如果你要动态添加新图片,那么你可能就需要这个方法来让这些新图片也支持缩放。",-1),z=s("p",null,[n("该插件在 "),s("code",null,"Zoom"),n(" 实例上额外添加了一个 "),s("code",null,"refresh"),n(" 方法,它将使用 "),s("a",{href:"#selector"},"selector"),n(" 作为默认参数,先调用 "),s("code",null,"zoom.detach()"),n(" 再调用 "),s("code",null,"zoom.attach()"),n(" ,便于你快速刷新当前页面图片的缩放状态。")],-1),k=s("li",null,[s("p",null,"示例:")],-1),E=a(`import { nextTick } from 'vue'
+import { useMediumZoom } from '@vuepress/plugin-medium-zoom/client'
+
+export default {
+ setup () {
+ const zoom = useMediumZoom ()
+
+ // ... 进行了一些操作,在当前页面添加了新的图片
+
+ // 此时你可能需要手动调用 \`refresh\` 来让这些新图片支持缩放
+ nextTick (() => {
+ zoom . refresh ()
+ })
+ },
+}
+
`,1);function x(A,F){const i=o("NpmBadge"),l=o("ExternalLinkIcon");return c(),t("div",null,[d,e(i,{package:"@vuepress/plugin-medium-zoom"}),s("p",null,[n("将 "),s("a",m,[n("medium-zoom"),e(l)]),n(" 集成到 VuePress 中,为图片提供可缩放的功能。")]),u,s("ul",null,[D,h,s("li",null,[v,s("ul",null,[s("li",null,[s("a",y,[n("medium-zoom > Options"),e(l)])])])])]),b,s("ul",null,[s("li",null,[C,s("p",null,[n("返回该插件使用的 "),_,n(" 实例,便于你直接使用实例上的 "),s("a",g,[n("methods"),e(l)]),n(" 。")]),f,z]),k]),E])}const B=p(r,[["render",x],["__file","medium-zoom.html.vue"]]),N=JSON.parse('{"path":"/zh/plugins/medium-zoom.html","title":"medium-zoom","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]},{"level":3,"title":"zoomOptions","slug":"zoomoptions","link":"#zoomoptions","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]},{"level":2,"title":"Composition API","slug":"composition-api","link":"#composition-api","children":[{"level":3,"title":"useMediumZoom","slug":"usemediumzoom","link":"#usemediumzoom","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/medium-zoom.md"}');export{B as comp,N as data};
diff --git a/assets/medium-zoom.html-DnJbC-Lw.js b/assets/medium-zoom.html-DnJbC-Lw.js
new file mode 100644
index 0000000000..ad98e13ee9
--- /dev/null
+++ b/assets/medium-zoom.html-DnJbC-Lw.js
@@ -0,0 +1,31 @@
+import{_ as t,r as o,o as p,c,a as n,b as s,d as e,e as a}from"./app-BcH8wZQx.js";const r={},d=s("h1",{id:"medium-zoom",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#medium-zoom"},[s("span",null,"medium-zoom")])],-1),m={href:"https://github.com/francoischalifour/medium-zoom#readme",target:"_blank",rel:"noopener noreferrer"},u=a(`This plugin has been integrated into the default theme.
npm i -D @vuepress/plugin-medium-zoom@next
+
import { mediumZoomPlugin } from '@vuepress/plugin-medium-zoom'
+
+export default {
+ plugins: [
+ mediumZoomPlugin ({
+ // options
+ }),
+ ],
+}
+
`,10),h=s("li",null,[s("p",null,[e("Type: "),s("code",null,"Object")])],-1),D=s("li",null,[s("p",null,"Details:"),s("p",null,"Options for medium-zoom.")],-1),y=s("p",null,"Also see:",-1),v={href:"https://github.com/francoischalifour/medium-zoom#options",target:"_blank",rel:"noopener noreferrer"},g=a(`You can customize most of the zoom styles via zoomOptions , while this plugin also provides some CSS variables for additional customization:
:root {
+ --medium-zoom-z-index : 100 ;
+ --medium-zoom-bg-color : #ffffff ;
+ --medium-zoom-opacity : 1 ;
+}
+
`,5),b=s("p",null,"Details:",-1),C=s("code",null,"Zoom",-1),f={href:"https://github.com/francoischalifour/medium-zoom#methods",target:"_blank",rel:"noopener noreferrer"},_=s("p",null,"This plugin will make images zoomable after navigating to current page. But if you are going to add new images dynamically, you may need this method to make those new images zoomable, too.",-1),z=s("p",null,[e("This plugin adds an extra "),s("code",null,"refresh"),e(" method on the "),s("code",null,"Zoom"),e(" instance, which will call "),s("code",null,"zoom.detach()"),e(" then "),s("code",null,"zoom.attach()"),e(" with the "),s("a",{href:"#selector"},"selector"),e(" as the default parameter. It will help you to refresh the zoomable images for current page.")],-1),k=s("li",null,[s("p",null,"Example:")],-1),x=a(`import { nextTick } from 'vue'
+import { useMediumZoom } from '@vuepress/plugin-medium-zoom/client'
+
+export default {
+ setup () {
+ const zoom = useMediumZoom ()
+
+ // ... do something to add new images in current page
+
+ // then you may need to call \`refresh\` manually to make those new images zoomable
+ nextTick (() => {
+ zoom . refresh ()
+ })
+ },
+}
+
`,1);function E(A,w){const i=o("NpmBadge"),l=o("ExternalLinkIcon");return p(),c("div",null,[d,n(i,{package:"@vuepress/plugin-medium-zoom"}),s("p",null,[e("Integrate "),s("a",m,[e("medium-zoom"),n(l)]),e(" into VuePress, which can provide the ability to zoom images.")]),u,s("ul",null,[h,D,s("li",null,[y,s("ul",null,[s("li",null,[s("a",v,[e("medium-zoom > Options"),n(l)])])])])]),g,s("ul",null,[s("li",null,[b,s("p",null,[e("Returns the "),C,e(" instance that used by this plugin, so that you can use the instance "),s("a",f,[e("methods"),n(l)]),e(" directly.")]),_,z]),k]),x])}const B=t(r,[["render",E],["__file","medium-zoom.html.vue"]]),T=JSON.parse('{"path":"/plugins/medium-zoom.html","title":"medium-zoom","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]},{"level":3,"title":"zoomOptions","slug":"zoomoptions","link":"#zoomoptions","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]},{"level":2,"title":"Composition API","slug":"composition-api","link":"#composition-api","children":[{"level":3,"title":"useMediumZoom","slug":"usemediumzoom","link":"#usemediumzoom","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/medium-zoom.md"}');export{B as comp,T as data};
diff --git a/assets/nprogress.html-DLQf86XW.js b/assets/nprogress.html-DLQf86XW.js
new file mode 100644
index 0000000000..1fe258fd97
--- /dev/null
+++ b/assets/nprogress.html-DLQf86XW.js
@@ -0,0 +1,11 @@
+import{_ as o,r as n,o as p,c as t,a,b as s,d as e,e as i}from"./app-BcH8wZQx.js";const c={},d=s("h1",{id:"nprogress-plugin",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#nprogress-plugin"},[s("span",null,"nprogress Plugin")])],-1),u={href:"https://github.com/rstacruz/nprogress",target:"_blank",rel:"noopener noreferrer"},g=i(`This plugin has been integrated into the default theme.
npm i -D @vuepress/plugin-nprogress@next
+
import { nprogressPlugin } from '@vuepress/plugin-nprogress'
+
+export default {
+ plugins: [ nprogressPlugin ()],
+}
+
You can customize the style of the progress bar via CSS variables:
:root {
+ --nprogress-color : #29d ;
+ --nprogress-z-index : 1031 ;
+}
+
`,7);function D(h,m){const l=n("NpmBadge"),r=n("ExternalLinkIcon");return p(),t("div",null,[d,a(l,{package:"@vuepress/plugin-nprogress"}),s("p",null,[e("Integrate "),s("a",u,[e("nprogress"),a(r)]),e(" into VuePress, which can provide a progress bar when navigating to another page.")]),g])}const y=o(c,[["render",D],["__file","nprogress.html.vue"]]),b=JSON.parse('{"path":"/plugins/nprogress.html","title":"nprogress","lang":"en-US","frontmatter":{"title":"nprogress"},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/nprogress.md"}');export{y as comp,b as data};
diff --git a/assets/nprogress.html-DoGUnVXK.js b/assets/nprogress.html-DoGUnVXK.js
new file mode 100644
index 0000000000..866b997ed9
--- /dev/null
+++ b/assets/nprogress.html-DoGUnVXK.js
@@ -0,0 +1,11 @@
+import{_ as o,r as e,o as p,c as i,a,b as s,d as n,e as t}from"./app-BcH8wZQx.js";const c={},d=s("h1",{id:"nprogress-插件",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#nprogress-插件"},[s("span",null,"nprogress 插件")])],-1),D={href:"https://github.com/rstacruz/nprogress",target:"_blank",rel:"noopener noreferrer"},u=t(`该插件已经集成到默认主题中。
npm i -D @vuepress/plugin-nprogress@next
+
import { nprogressPlugin } from '@vuepress/plugin-nprogress'
+
+export default {
+ plugins: [ nprogressPlugin ()],
+}
+
你可以通过 CSS 变量来自定义进度条的样式:
:root {
+ --nprogress-color : #29d ;
+ --nprogress-z-index : 1031 ;
+}
+
`,7);function m(g,h){const l=e("NpmBadge"),r=e("ExternalLinkIcon");return p(),i("div",null,[d,a(l,{package:"@vuepress/plugin-nprogress"}),s("p",null,[n("将 "),s("a",D,[n("nprogress"),a(r)]),n(" 集成到 VuePress 中,在切换到另一个页面时会展示进度条。")]),u])}const y=o(c,[["render",m],["__file","nprogress.html.vue"]]),b=JSON.parse('{"path":"/zh/plugins/nprogress.html","title":"nprogress","lang":"zh-CN","frontmatter":{"title":"nprogress"},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/nprogress.md"}');export{y as comp,b as data};
diff --git a/assets/page.html-DnZT7-UX.js b/assets/page.html-DnZT7-UX.js
new file mode 100644
index 0000000000..72ff115dbc
--- /dev/null
+++ b/assets/page.html-DnZT7-UX.js
@@ -0,0 +1,77 @@
+import{_ as s,o as n,c as a,e as l}from"./app-BcH8wZQx.js";const e={},p=l(`These functions generate common information for your pages.
Get the excerpt of the page.
export interface PageExcerptOptions {
+ /**
+ * Excerpt separator
+ *
+ * @default " <!-- more --> "
+ */
+ separator ?: string
+
+ /**
+ * Length of excerpt
+ *
+ * @description Excerpt length will be the minimal possible length reaching this value
+ *
+ * @default 300
+ */
+ length ?: number
+
+ /**
+ * Tags which is considered as custom elements
+ *
+ * @description This is used to determine whether a tag is a custom element since all unknown tags are removed in excerpt.
+ */
+ isCustomElement ?: ( tagName : string ) => boolean
+
+ /**
+ * Whether keep page title (first h1) in excerpt
+ *
+ * @default false
+ */
+ keepPageTitle ?: boolean
+
+ /**
+ * Whether preserve tags like line numbers and highlight lines for code blocks
+ *
+ * @default false
+ */
+ keepFenceDom ?: boolean
+}
+
+export const getPageExcerpt : (
+ app : App ,
+ page : Page ,
+ options ?: PageExcerptOptions ,
+) => string
+
Get plain text of the page.
export interface PageTextOptions {
+ /**
+ * Whether convert text to single line content
+ *
+ * @default false
+ */
+ singleLine ?: boolean
+
+ /**
+ * Length of text
+ *
+ * @description Text length will be the minimal possible length reaching this value
+ *
+ * @default 300
+ */
+ length ?: number
+
+ /**
+ * Tags to be removed
+ *
+ * @description Table and code blocks are removed by default.
+ *
+ * @default ['table', 'pre']
+ */
+ removedTags ?: string []
+}
+
+export const getPageText : (
+ app : App ,
+ page : Page ,
+ options ?: PageTextOptions ,
+) => string
+
`,8),i=[p];function o(c,t){return n(),a("div",null,i)}const d=s(e,[["render",o],["__file","page.html.vue"]]),v=JSON.parse('{"path":"/tools/helper/node/page.html","title":"Page Related","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"getPageExcerpt","slug":"getpageexcerpt","link":"#getpageexcerpt","children":[]},{"level":2,"title":"getPageText","slug":"getpagetext","link":"#getpagetext","children":[]}],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"tools/helper/node/page.md"}');export{d as comp,v as data};
diff --git a/assets/page.html-OOTVDdF_.js b/assets/page.html-OOTVDdF_.js
new file mode 100644
index 0000000000..e1dc62471c
--- /dev/null
+++ b/assets/page.html-OOTVDdF_.js
@@ -0,0 +1,77 @@
+import{_ as s,o as n,c as a,e as l}from"./app-BcH8wZQx.js";const e={},p=l(`这些函数为你的页面生成常见信息。
获取页面摘要。
export interface PageExcerptOptions {
+ /**
+ * 摘要分隔符
+ *
+ * @default " <!-- more --> "
+ */
+ separator ?: string
+
+ /**
+ * 摘要的长度
+ *
+ * @description 摘要的长度会尽可能的接近这个值
+ *
+ * @default 300
+ */
+ length ?: number
+
+ /**
+ * 被认为是自定义元素的标签
+ *
+ * @description 用于判断一个标签是否是自定义元素,因为在摘要中,所有的未知标签都会被移除。
+ */
+ isCustomElement ?: ( tagName : string ) => boolean
+
+ /**
+ * 是否保留页面标题 (第一个 h1)
+ *
+ * @default false
+ */
+ keepPageTitle ?: boolean
+
+ /**
+ * 是否保留代码块的标签,诸如行号和高亮行
+ *
+ * @default false
+ */
+ keepFenceDom ?: boolean
+}
+
+export const getPageExcerpt : (
+ app : App ,
+ page : Page ,
+ options ?: PageExcerptOptions ,
+) => string
+
获取页面纯文本。
export interface PageTextOptions {
+ /**
+ * 是否将文字转换成单行内容
+ *
+ * @default false
+ */
+ singleLine ?: boolean
+
+ /**
+ * 文字的长度
+ *
+ * @description 文字的长度会尽可能的接近这个值
+ *
+ * @default 300
+ */
+ length ?: number
+
+ /**
+ * 需要移除的标签
+ *
+ * @description 默认情况下表格和代码块会被移除
+ *
+ * @default ['table', 'pre']
+ */
+ removedTags ?: string []
+}
+
+export const getPageText : (
+ app : App ,
+ page : Page ,
+ options ?: PageTextOptions ,
+) => string
+
`,8),i=[p];function o(c,r){return n(),a("div",null,i)}const d=s(e,[["render",o],["__file","page.html.vue"]]),v=JSON.parse('{"path":"/zh/tools/helper/node/page.html","title":"页面相关","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"getPageExcerpt","slug":"getpageexcerpt","link":"#getpageexcerpt","children":[]},{"level":2,"title":"getPageText","slug":"getpagetext","link":"#getpagetext","children":[]}],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/tools/helper/node/page.md"}');export{d as comp,v as data};
diff --git a/assets/palette.html-BQkheaO_.js b/assets/palette.html-BQkheaO_.js
new file mode 100644
index 0000000000..71c6bee6f9
--- /dev/null
+++ b/assets/palette.html-BQkheaO_.js
@@ -0,0 +1,39 @@
+import{_ as p,r as n,o,c as r,a as l,b as s,d as e,e as t}from"./app-BcH8wZQx.js";const c={},d=s("h1",{id:"palette",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#palette"},[s("span",null,"palette")])],-1),u=t(`Provide palette support for your theme.
This plugin is mainly used to develop themes, and has been integrated into the default theme. You won't need to use it directly in most cases.
For theme authors, this plugin will help you to provide styles customization for users.
npm i -D @vuepress/plugin-palette@next
+
import { palettePlugin } from '@vuepress/plugin-palette'
+
+export default {
+ plugins: [
+ palettePlugin ({
+ // options
+ }),
+ ],
+}
+
This plugin will provide a @vuepress/plugin-palette/palette
(palette file) and a @vuepress/plugin-palette/style
(style file) to be imported in your theme styles.
`,8),y={href:"https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties",target:"_blank",rel:"noopener noreferrer"},h={href:"https://sass-lang.com/documentation/variables",target:"_blank",rel:"noopener noreferrer"},v={href:"http://lesscss.org/features/#variables-feature",target:"_blank",rel:"noopener noreferrer"},m={href:"https://stylus-lang.com/docs/variables.html",target:"_blank",rel:"noopener noreferrer"},D=t(`The style file is used for overriding the default styles or adding extra styles, so it's likely to be imported at the end of your theme styles.
Use this plugin in your theme, assuming you are using SASS:
export default {
+ // ...
+ plugins: [ palettePlugin ({ preset: 'sass' })],
+}
+
Import the plugin's palette file where your theme needs to use the corresponding variables, such as in the Layout.vue
file:
< template >
+ < h1 class = "palette-title" > Hello, Palette! </ h1 >
+</ template >
+
+< style lang = "scss" >
+/* import variables from the plugin's palette file */
+@import '@vuepress/plugin-palette/palette' ;
+
+/* set default value for variables */
+$color : red !default ;
+
+/* use variables in your styles */
+.palette-title {
+ color : $color ;
+}
+</ style >
+
Then users can customize variables in .vuepress/styles/palette.scss
:
Import the plugin's style file after your theme's styles, for example, in the clientConfigFile
:
// import your theme's style file
+import 'path/to/your/theme/style'
+// import the plugin's style file
+import '@vuepress/plugin-palette/style'
+
Then users can add extra styles in .vuepress/styles/index.scss
and override the default styles of your theme:
h1 {
+ font-size : 2.5rem ;
+}
+
Type: 'css' | 'sass' | 'less' | 'stylus'
Default: 'css'
Details:
Set preset for other options.
If you don't need advanced customization of the plugin, it's recommended to only set this option and omit other options.
Type: string
Default:
css: '.vuepress/styles/palette.css'
sass: '.vuepress/styles/palette.scss'
less: '.vuepress/styles/palette.less'
stylus: '.vuepress/styles/palette.styl'
Details:
File path of the user palette file, relative to source directory.
The default value depends on the preset option.
The file is where users define style variables, and it's recommended to keep the default file path as a convention.
Type: string
Default:
css: 'styles/palette.css'
sass: 'styles/palette.scss'
less: 'styles/palette.less'
stylus: 'styles/palette.styl'
Details:
File path of the generated palette temp file, relative to temp directory.
The default value depends on the preset option.
You should import the palette file via '@vuepress/plugin-palette/palette'
alias, so you don't need to change this option in most cases.
Type: string
Default:
css: '.vuepress/styles/index.css'
sass: '.vuepress/styles/index.scss'
less: '.vuepress/styles/index.less'
stylus: '.vuepress/styles/index.styl'
Details:
File path of the user style file, relative to source directory.
The default value depends on the preset option.
The file is where users override default styles or add extra styles, and it's recommended to keep the default file path as a convention.
Type: string
Default:
css: 'styles/index.css'
sass: 'styles/index.scss'
less: 'styles/index.less'
stylus: 'styles/index.styl'
Details:
File path of the generated style temp file, relative to temp directory.
The default value depends on the preset option.
You should import the style file via '@vuepress/plugin-palette/style'
alias, so you don't need to change this option in most cases.
Type: (filePath: string) => string
Default:
css: (filePath) => \`@import '\${filePath}';\\n\`
sass: (filePath) => \`@forward 'file:///\${filePath}';\\n\`
less: (filePath) => \`@import '\${filePath}';\\n\`
stylus: (filePath) => \`@require '\${filePath}';\\n\`
Details:
Function to generate import code.
The default value depends on the preset option.
This option is used for generating tempPaletteFile and tempStyleFile , and you don't need to change this option in most cases.
`,27);function f(g,b){const i=n("NpmBadge"),a=n("ExternalLinkIcon");return o(),r("div",null,[d,l(i,{package:"@vuepress/plugin-palette"}),u,s("p",null,[e("The palette file is used for defining style variables, so it's likely to be imported at the beginning of your theme styles. For example, users can define "),s("a",y,[e("CSS variables"),l(a)]),e(", "),s("a",h,[e("SASS variables"),l(a)]),e(", "),s("a",v,[e("LESS variables"),l(a)]),e(" or "),s("a",m,[e("Stylus variables"),l(a)]),e(" in the palette, and then you can use those variables in your theme styles.")]),D])}const x=p(c,[["render",f],["__file","palette.html.vue"]]),k=JSON.parse('{"path":"/plugins/palette.html","title":"palette","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Palette and Style","slug":"palette-and-style","link":"#palette-and-style","children":[]},{"level":2,"title":"Usage","slug":"usage-1","link":"#usage-1","children":[{"level":3,"title":"Usage of Palette","slug":"usage-of-palette","link":"#usage-of-palette","children":[]},{"level":3,"title":"Usage of Style","slug":"usage-of-style","link":"#usage-of-style","children":[]}]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"preset","slug":"preset","link":"#preset","children":[]},{"level":3,"title":"userPaletteFile","slug":"userpalettefile","link":"#userpalettefile","children":[]},{"level":3,"title":"tempPaletteFile","slug":"temppalettefile","link":"#temppalettefile","children":[]},{"level":3,"title":"userStyleFile","slug":"userstylefile","link":"#userstylefile","children":[]},{"level":3,"title":"tempStyleFile","slug":"tempstylefile","link":"#tempstylefile","children":[]},{"level":3,"title":"importCode","slug":"importcode","link":"#importcode","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/palette.md"}');export{x as comp,k as data};
diff --git a/assets/palette.html-DdZN7ryj.js b/assets/palette.html-DdZN7ryj.js
new file mode 100644
index 0000000000..3f1de18075
--- /dev/null
+++ b/assets/palette.html-DdZN7ryj.js
@@ -0,0 +1,39 @@
+import{_ as i,r as n,o,c,a as l,b as e,d as s,e as p}from"./app-BcH8wZQx.js";const r={},d=e("h1",{id:"palette",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#palette"},[e("span",null,"palette")])],-1),u=p(`为你的主题提供调色板功能。
该插件主要用于开发主题,并且已经集成到默认主题中。大部分情况下你不需要直接使用它。
对于主题作者,该插件可以帮助你提供用户自定义样式的能力。
npm i -D @vuepress/plugin-palette@next
+
import { palettePlugin } from '@vuepress/plugin-palette'
+
+export default {
+ plugins: [
+ palettePlugin ({
+ // 配置项
+ }),
+ ],
+}
+
该插件会提供一个 @vuepress/plugin-palette/palette
(调色板文件)和一个 @vuepress/plugin-palette/style
(样式文件),用于在你的主题样式中引入。
`,8),y={href:"https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties",target:"_blank",rel:"noopener noreferrer"},v={href:"https://sass-lang.com/documentation/variables",target:"_blank",rel:"noopener noreferrer"},h={href:"http://lesscss.org/features/#variables-feature",target:"_blank",rel:"noopener noreferrer"},D={href:"https://stylus-lang.com/docs/variables.html",target:"_blank",rel:"noopener noreferrer"},m=p(`样式文件用于覆盖默认样式或添加额外样式,因此它一般会在你主题样式的末尾引入。
在你的主题中使用该插件,假设你使用 SASS 作为 CSS 预处理器:
export default {
+ // ...
+ plugins: [ palettePlugin ({ preset: 'sass' })],
+}
+
在你主题需要使用对应变量的地方引入该插件的调色板文件,比如在 Layout.vue
中:
< template >
+ < h1 class = "palette-title" > 你好,调色板! </ h1 >
+</ template >
+
+< style lang = "scss" >
+/* 从该插件的调色板中引入变量 */
+@import '@vuepress/plugin-palette/palette' ;
+
+/* 设置变量的默认值 */
+$color : red !default ;
+
+/* 在你的样式中使用变量 */
+.palette-title {
+ color : $color ;
+}
+</ style >
+
然后,用户就可以在 .vuepress/styles/palette.scss
中自定义变量:
在你主题的样式之后引入该插件的样式文件,比如在 clientConfigFile
中:
// 引入你主题本身的样式文件
+import 'path/to/your/theme/style'
+// 引入该插件的样式文件
+import '@vuepress/plugin-palette/style'
+
然后,用户就可以在 .vuepress/styles/index.scss
中添加额外样式,并可以覆盖你主题本身的样式:
h1 {
+ font-size : 2.5rem ;
+}
+
`,27);function b(g,f){const t=n("NpmBadge"),a=n("ExternalLinkIcon");return o(),c("div",null,[d,l(t,{package:"@vuepress/plugin-palette"}),u,e("p",null,[s("调色板文件用于定义样式变量,因此它一般会在你主题样式的开头引入。举例来说,用户可以在调色板中定义 "),e("a",y,[s("CSS 变量"),l(a)]),s(" 、 "),e("a",v,[s("SASS 变量"),l(a)]),s(" 、 "),e("a",h,[s("LESS 变量"),l(a)]),s(" 或 "),e("a",D,[s("Stylus 变量"),l(a)]),s(" ,然后你可以在你的主题样式中使用这些变量。")]),m])}const E=i(r,[["render",b],["__file","palette.html.vue"]]),x=JSON.parse('{"path":"/zh/plugins/palette.html","title":"palette","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"调色板和样式","slug":"调色板和样式","link":"#调色板和样式","children":[]},{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[{"level":3,"title":"使用调色板","slug":"使用调色板","link":"#使用调色板","children":[]},{"level":3,"title":"使用样式","slug":"使用样式","link":"#使用样式","children":[]}]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"preset","slug":"preset","link":"#preset","children":[]},{"level":3,"title":"userPaletteFile","slug":"userpalettefile","link":"#userpalettefile","children":[]},{"level":3,"title":"tempPaletteFile","slug":"temppalettefile","link":"#temppalettefile","children":[]},{"level":3,"title":"userStyleFile","slug":"userstylefile","link":"#userstylefile","children":[]},{"level":3,"title":"tempStyleFile","slug":"tempstylefile","link":"#tempstylefile","children":[]},{"level":3,"title":"importCode","slug":"importcode","link":"#importcode","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/palette.md"}');export{E as comp,x as data};
diff --git a/assets/photo-swipe.html-CBMdUOD5.js b/assets/photo-swipe.html-CBMdUOD5.js
new file mode 100644
index 0000000000..33fea55b1a
--- /dev/null
+++ b/assets/photo-swipe.html-CBMdUOD5.js
@@ -0,0 +1,145 @@
+import{_ as i,r as n,o as c,c as t,a as l,b as s,d as a,e}from"./app-BcH8wZQx.js";const r={},D=s("h1",{id:"photo-swipe",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#photo-swipe"},[s("span",null,"photo-swipe")])],-1),d=e(`此插件会使页面正文内的图片在点击时进入浏览模式浏览。
npm i -D @vuepress/plugin-photo-swipe@next
+
import { photoSwipePlugin } from '@vuepress/plugin-photo-swipe'
+
+export default {
+ plugins: [
+ photoSwipePlugin ({
+ // 选项
+ }),
+ ],
+}
+
在图片预览模式中,你可以:
左右滑动按顺序浏览页面内其他的图片 查看图片的描述 对图片进行缩放 全屏浏览图片 下载图片 分享图片 提示
除了点击右上角的 "×" 退出浏览模式外,在上下滚动超过一定距离后,会自动退出图片浏览模式。 在移动端,或使用 PC 触控板,你可以使用平移、缩放手势在浏览模式中平移、缩放图片。 类型:string | string[]
默认值:".theme-default-content :not(a) > img:not([no-view])"
详情:图片选择器 类型:boolean
默认值:true
详情:是否在滚动时关闭当前图片。 类型:number
默认值:800
详情:
操作页面 DOM 的延时,单位 ms。
提示
如果你使用的主题有切换动画,建议配置此选项为 切换动画时长 + 200
。
内置支持语言 简体中文 (zh-CN)繁体中文 (zh-TW)英文(美国) (en-US)德语 (de-DE)德语(澳大利亚) (de-AT)俄语 (ru-RU)乌克兰语 (uk-UA)越南语 (vi-VN)葡萄牙语(巴西) (pt-BR)波兰语 (pl-PL)法语 (fr-FR)西班牙语 (es-ES)斯洛伐克 (sk-SK)日语 (ja-JP)土耳其语 (tr-TR)韩语 (ko-KR)芬兰语 (fi-FI)印尼语 (id-ID)荷兰语 (nl-NL) `,22),y={href:"http://photoswipe.com/",target:"_blank",rel:"noopener noreferrer"},v=s("code",null,"photo-swipe",-1),u=e(`import { definePhotoSwipeConfig } from '@vuepress/plugin-photo-swipe/client'
+
+definePhotoSwipeConfig ({
+ // 在此设置 photoswipe 选项
+})
+
+export default {}
+
你可以通过 API 来调用 photoswipe。
createPhotoSwipe
允许你以编程的方式查看图片链接:
< script setup lang = "ts" >
+import { onMounted , onUnmounted } from 'vue' ;
+import { createPhotoSwipe } from "@vuepress/plugin-photo-swipe/client" ;
+
+let state : PhotoSwipeState | null = null ;
+
+const openPhotoSwipe = ( index : number ) => {
+ state ?. open ( index - 1 );
+};
+
+onMounted ( async () => {
+ // 通过图片链接创建一个新的 photoswipe 实例
+ state = await createPhotoSwipe (
+ [
+ 'https://exmaple.com/image1.png'
+ 'https://exmaple.com/image2.png'
+ 'https://exmaple.com/image3.png'
+ ],
+ {
+ // photoswipe 选项
+ }
+ );
+});
+
+onUnmounted (() => {
+ state ?. destroy ()
+})
+</ script >
+
+< template >
+ < button v-for = " i in 3 " @ click = " openPhotoSwipe ( i ) " > open photo {{ i }} </ button >
+</ template >
+
registerPhotoSwipe
允许你为给定的图片元素注册 photoswipe:
< script setup lang = "ts" >
+import { onMounted , onUnmounted } from 'vue'
+import { registerPhotoSwipe } from '@vuepress/plugin-photo-swipe/client'
+
+let destroy : () => void | null = null
+
+onMounted ( async () => {
+ await nextTick ()
+
+ const images = Array . from ( document . querySelectorAll ( 'img' ))
+
+ // 通过图片元素创建一个新的 photoswipe 实例
+ state = await registerPhotoSwipe ( images , {
+ // photoswipe 选项
+ })
+})
+
+onUnmounted (() => {
+ destroy ?.()
+})
+</ script >
+
你可以通过 CSS 变量来自定义部分样式:
:root {
+ --photo-swipe-bullet : #fff ;
+ --photo-swipe-bullet-active : #3eaf7c ;
+}
+
`,10);function C(m,b){const p=n("NpmBadge"),o=n("ExternalLinkIcon");return c(),t("div",null,[D,l(p,{package:"@vuepress/plugin-photo-swipe"}),d,s("p",null,[a("传递给 "),s("a",y,[v,l(o)]),a(" 的额外选项。")]),u])}const g=i(r,[["render",C],["__file","photo-swipe.html.vue"]]),E=JSON.parse('{"path":"/zh/plugins/photo-swipe.html","title":"photo-swipe","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]},{"level":3,"title":"scrollToClose","slug":"scrolltoclose","link":"#scrolltoclose","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"photoswipe","slug":"photoswipe","link":"#photoswipe","children":[]}]},{"level":2,"title":"客户端配置","slug":"客户端配置","link":"#客户端配置","children":[{"level":3,"title":"definePhotoSwipeConfig","slug":"definephotoswipeconfig","link":"#definephotoswipeconfig","children":[]}]},{"level":2,"title":"API","slug":"api","link":"#api","children":[]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]}],"git":{"updatedTime":1708274662000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/plugins/photo-swipe.md"}');export{g as comp,E as data};
diff --git a/assets/photo-swipe.html-DCSQki0o.js b/assets/photo-swipe.html-DCSQki0o.js
new file mode 100644
index 0000000000..adb799cee3
--- /dev/null
+++ b/assets/photo-swipe.html-DCSQki0o.js
@@ -0,0 +1,145 @@
+import{_ as o,r as n,o as i,c as t,a as l,b as s,d as c,e as a}from"./app-BcH8wZQx.js";const r={},d=s("h1",{id:"photo-swipe",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#photo-swipe"},[s("span",null,"photo-swipe")])],-1),D=a(`This plugin will make the pictures in the body of the page enter the preview mode when clicked.
npm i -D @vuepress/plugin-photo-swipe@next
+
import { photoSwipePlugin } from '@vuepress/plugin-photo-swipe'
+
+export default {
+ plugins: [
+ photoSwipePlugin ({
+ // options
+ }),
+ ],
+}
+
In preview mode, you can:
Swipe left and right to preview other pictures on the page in order View the description of the picture Zoom in and zoom out the picture View pictures in full screen Download pictures Share pictures TIP
Besides clicking "×" in the upper right corner to exit the preview mode, scrolling up and down more than a certain distance will also exit preview mode. On mobile devices, or using the PC trackpad, you can use pan and zoom gestures to pan and zoom in the preview mode. Type: string | string[]
Default: ".theme-default-content :not(a) > img:not([no-view])"
Details: Image selector Type: boolean
Default: true
Details: Whether close the current image when scrolling. Built-in Supported Languages Simplified Chinese (zh-CN)Traditional Chinese (zh-TW)English (United States) (en-US)German (de-DE)German (Australia) (de-AT)Russian (ru-RU)Ukrainian (uk-UA)Vietnamese (vi-VN)Portuguese (Brazil) (pt-BR)Polish (pl-PL)French (fr-FR)Spanish (es-ES)Slovak (sk-SK)Japanese (ja-JP)Turkish (tr-TR)Korean (ko-KR)Finnish (fi-FI)Indonesian (id-ID)Dutch (nl-NL)Type: string | false
Details: Image selector for the current page, or false
to disable photo-swipe in current page.
`,22),y={href:"http://photoswipe.com/",target:"_blank",rel:"noopener noreferrer"},u=s("code",null,"photo-swipe",-1),v=a(`import { definePhotoSwipeConfig } from '@vuepress/plugin-photo-swipe/client'
+
+definePhotoSwipeConfig ({
+ // set photoswipe options here
+})
+
+export default {}
+
You can also call photoswipe with apis.
createPhotoSwipe
allows you to programmatically view images links with PhotoSwipe:
< script setup lang = "ts" >
+import { onMounted , onUnmounted } from 'vue' ;
+import { createPhotoSwipe } from "@vuepress/plugin-photo-swipe/client" ;
+
+let state : PhotoSwipeState | null = null ;
+
+const openPhotoSwipe = ( index : number ) => {
+ state ?. open ( index - 1 );
+};
+
+onMounted ( async () => {
+ // create a new photoswipe instance with image links
+ state = await createPhotoSwipe (
+ [
+ 'https://exmaple.com/image1.png'
+ 'https://exmaple.com/image2.png'
+ 'https://exmaple.com/image3.png'
+ ],
+ {
+ // photoswipe options
+ }
+ );
+});
+
+onUnmounted (() => {
+ state ?. destroy ()
+})
+</ script >
+
+< template >
+ < button v-for = " i in 3 " @ click = " openPhotoSwipe ( i ) " > open photo {{ i }} </ button >
+</ template >
+
registerPhotoSwipe
allows you to register photoswipe for the given image elements:
< script setup lang = "ts" >
+import { onMounted , onUnmounted } from 'vue'
+import { registerPhotoSwipe } from '@vuepress/plugin-photo-swipe/client'
+
+let destroy : () => void | null = null
+
+onMounted ( async () => {
+ await nextTick ()
+
+ const images = Array . from ( document . querySelectorAll ( 'img' ))
+
+ // create a new photoswipe instance on image elements
+ state = await registerPhotoSwipe ( images , {
+ // photoswipe options
+ })
+})
+
+onUnmounted (() => {
+ destroy ?.()
+})
+</ script >
+
You can customize the style via CSS variables:
:root {
+ --photo-swipe-bullet : #fff ;
+ --photo-swipe-bullet-active : #3eaf7c ;
+}
+
`,10);function m(C,h){const e=n("NpmBadge"),p=n("ExternalLinkIcon");return i(),t("div",null,[d,l(e,{package:"@vuepress/plugin-photo-swipe"}),D,s("p",null,[c("Options passed to "),s("a",y,[u,l(p)])]),v])}const g=o(r,[["render",m],["__file","photo-swipe.html.vue"]]),E=JSON.parse('{"path":"/plugins/photo-swipe.html","title":"photo-swipe","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]},{"level":3,"title":"scrollToClose","slug":"scrolltoclose","link":"#scrolltoclose","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[]},{"level":2,"title":"Client Config","slug":"client-config","link":"#client-config","children":[{"level":3,"title":"definePhotoSwipeConfig","slug":"definephotoswipeconfig","link":"#definephotoswipeconfig","children":[]}]},{"level":2,"title":"API","slug":"api","link":"#api","children":[]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]}],"git":{"updatedTime":1708274662000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"plugins/photo-swipe.md"}');export{g as comp,E as data};
diff --git a/assets/plugin.html-Bx-Qu2io.js b/assets/plugin.html-Bx-Qu2io.js
new file mode 100644
index 0000000000..b4696b6a70
--- /dev/null
+++ b/assets/plugin.html-Bx-Qu2io.js
@@ -0,0 +1,10 @@
+import{_ as o,r as u,o as a,c as p,b as l,d as e,a as s,w as t,e as i}from"./app-BcH8wZQx.js";const c={},d=i(`你可以通过 themePlugins
设置默认主题使用的插件。
默认主题使用了一些插件,如果你确实不需要该插件,你可以选择禁用它。在禁用插件之前,请确保你已了解它的用途。
import { defaultTheme } from '@vuepress/theme-default'
+
+export default {
+ theme: defaultTheme ({
+ themePlugins: {
+ // 在这里自定义主题插件
+ },
+ }),
+}
+
`,5),h=l("li",null,[l("p",null,[e("类型: "),l("code",null,"boolean")])],-1),r=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"true")])],-1),m=l("p",null,"详情:",-1),g=l("h2",{id:"themeplugins-backtotop",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-backtotop"},[l("span",null,"themePlugins.backToTop")])],-1),_=l("li",null,[l("p",null,[e("类型: "),l("code",null,"BackToTopPluginOptions | boolean")])],-1),v=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"true")])],-1),k=l("p",null,"详情:",-1),b=l("p",null,"支持对象格式以作为插件选项。",-1),f=l("h2",{id:"themeplugins-container",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-container"},[l("span",null,"themePlugins.container")])],-1),D=l("li",null,[l("p",null,[e("类型: "),l("code",null,"Record")])],-1),y=l("p",null,"详情:",-1),x=i("ContainerType
类型为:
tip
warning
danger
details
codeGroup
codeGroupItem
",2),P=l("p",null,"参考:",-1),C=l("h2",{id:"themeplugins-copycode",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-copycode"},[l("span",null,"themePlugins.copyCode")])],-1),z=l("li",null,[l("p",null,[e("类型: "),l("code",null,"CopyCodePluginOptions | boolean")])],-1),E=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"true")])],-1),T=l("p",null,"详情:",-1),A=l("p",null,"支持对象格式以作为插件选项。",-1),B=l("h2",{id:"themeplugins-externallinkicon",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-externallinkicon"},[l("span",null,"themePlugins.externalLinkIcon")])],-1),L=l("li",null,[l("p",null,[e("类型: "),l("code",null,"boolean")])],-1),N=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"true")])],-1),w=l("p",null,"详情:",-1),O=l("h2",{id:"themeplugins-git",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-git"},[l("span",null,"themePlugins.git")])],-1),R=l("li",null,[l("p",null,[e("类型: "),l("code",null,"boolean")])],-1),S=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"true")])],-1),V=l("p",null,"详情:",-1),F=l("h2",{id:"themeplugins-mediumzoom",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-mediumzoom"},[l("span",null,"themePlugins.mediumZoom")])],-1),H=l("li",null,[l("p",null,[e("类型: "),l("code",null,"boolean")])],-1),I=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"true")])],-1),G=l("p",null,"详情:",-1),M=l("h2",{id:"themeplugins-nprogress",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-nprogress"},[l("span",null,"themePlugins.nprogress")])],-1),Z=l("li",null,[l("p",null,[e("类型: "),l("code",null,"boolean")])],-1),J=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"true")])],-1),j=l("p",null,"详情:",-1),q=l("h2",{id:"themeplugins-seo",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-seo"},[l("span",null,"themePlugins.seo")])],-1),K=l("li",null,[l("p",null,[e("类型: "),l("code",null,"SeoPluginOptions | boolean")])],-1),Q=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"true")])],-1),U=l("p",null,"详情:",-1),W=l("p",null,"支持对象格式以作为插件选项。",-1),X=l("h2",{id:"themeplugins-sitemap",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-sitemap"},[l("span",null,"themePlugins.sitemap")])],-1),Y=l("li",null,[l("p",null,[e("类型: "),l("code",null,"SitemapPluginOptions | boolean")])],-1),$=l("li",null,[l("p",null,[e("默认值: "),l("code",null,"true")])],-1),ll=l("p",null,"详情:",-1),el=l("p",null,"支持对象格式以作为插件选项。",-1);function nl(sl,tl){const n=u("RouteLink");return a(),p("div",null,[d,l("ul",null,[h,r,l("li",null,[m,l("p",null,[e("是否启用 "),s(n,{to:"/zh/plugins/active-header-links.html"},{default:t(()=>[e("@vuepress/plugin-active-header-links")]),_:1}),e(" 。")])])]),g,l("ul",null,[_,v,l("li",null,[k,l("p",null,[e("是否启用 "),s(n,{to:"/zh/plugins/back-to-top.html"},{default:t(()=>[e("@vuepress/plugin-back-to-top")]),_:1}),e(" 。")]),b])]),f,l("ul",null,[D,l("li",null,[y,l("p",null,[e("是否启用由 "),s(n,{to:"/zh/plugins/container.html"},{default:t(()=>[e("@vuepress/plugin-container")]),_:1}),e(" 支持的自定义容器。")]),x]),l("li",null,[P,l("ul",null,[l("li",null,[s(n,{to:"/zh/themes/default/markdown.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%B9%E5%99%A8"},{default:t(()=>[e("默认主题 > Markdown > 自定义容器")]),_:1})])])])]),C,l("ul",null,[z,E,l("li",null,[T,l("p",null,[e("是否启用 "),s(n,{to:"/zh/plugins/copy-code.html"},{default:t(()=>[e("@vuepress/plugin-copy-code")]),_:1}),e(" 。")]),A])]),B,l("ul",null,[L,N,l("li",null,[w,l("p",null,[e("是否启用 "),s(n,{to:"/zh/plugins/external-link-icon.html"},{default:t(()=>[e("@vuepress/plugin-external-link-icon")]),_:1}),e(" 。")])])]),O,l("ul",null,[R,S,l("li",null,[V,l("p",null,[e("是否启用 "),s(n,{to:"/zh/plugins/git.html"},{default:t(()=>[e("@vuepress/plugin-git")]),_:1}),e(" 。")])])]),F,l("ul",null,[H,I,l("li",null,[G,l("p",null,[e("是否启用 "),s(n,{to:"/zh/plugins/medium-zoom.html"},{default:t(()=>[e("@vuepress/plugin-medium-zoom")]),_:1}),e(" 。")])])]),M,l("ul",null,[Z,J,l("li",null,[j,l("p",null,[e("是否启用 "),s(n,{to:"/zh/plugins/nprogress.html"},{default:t(()=>[e("@vuepress/plugin-nprogress")]),_:1}),e(" 。")])])]),q,l("ul",null,[K,Q,l("li",null,[U,l("p",null,[e("是否启用 "),s(n,{to:"/zh/plugins/seo/"},{default:t(()=>[e("@vuepress/plugin-seo")]),_:1}),e(" 。")]),W])]),X,l("ul",null,[Y,$,l("li",null,[ll,l("p",null,[e("是否启用 "),s(n,{to:"/zh/plugins/sitemap/"},{default:t(()=>[e("@vuepress/plugin-sitemap")]),_:1}),e(" 。")]),el])])])}const ol=o(c,[["render",nl],["__file","plugin.html.vue"]]),ul=JSON.parse('{"path":"/zh/themes/default/plugin.html","title":"插件配置","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"themePlugins.activeHeaderLinks","slug":"themeplugins-activeheaderlinks","link":"#themeplugins-activeheaderlinks","children":[]},{"level":2,"title":"themePlugins.backToTop","slug":"themeplugins-backtotop","link":"#themeplugins-backtotop","children":[]},{"level":2,"title":"themePlugins.container","slug":"themeplugins-container","link":"#themeplugins-container","children":[]},{"level":2,"title":"themePlugins.copyCode","slug":"themeplugins-copycode","link":"#themeplugins-copycode","children":[]},{"level":2,"title":"themePlugins.externalLinkIcon","slug":"themeplugins-externallinkicon","link":"#themeplugins-externallinkicon","children":[]},{"level":2,"title":"themePlugins.git","slug":"themeplugins-git","link":"#themeplugins-git","children":[]},{"level":2,"title":"themePlugins.mediumZoom","slug":"themeplugins-mediumzoom","link":"#themeplugins-mediumzoom","children":[]},{"level":2,"title":"themePlugins.nprogress","slug":"themeplugins-nprogress","link":"#themeplugins-nprogress","children":[]},{"level":2,"title":"themePlugins.seo","slug":"themeplugins-seo","link":"#themeplugins-seo","children":[]},{"level":2,"title":"themePlugins.sitemap","slug":"themeplugins-sitemap","link":"#themeplugins-sitemap","children":[]}],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/themes/default/plugin.md"}');export{ol as comp,ul as data};
diff --git a/assets/plugin.html-vTOM614H.js b/assets/plugin.html-vTOM614H.js
new file mode 100644
index 0000000000..701f07c8d1
--- /dev/null
+++ b/assets/plugin.html-vTOM614H.js
@@ -0,0 +1,10 @@
+import{_ as o,r as u,o as a,c as p,b as e,d as l,a as s,w as t,e as i}from"./app-BcH8wZQx.js";const c={},d=i(`You can configure the plugins that used by default theme with themePlugins
.
Default theme is using some plugins by default. You can disable a plugin if you really do not want to use it. Make sure you understand what the plugin is for before disabling it.
import { defaultTheme } from '@vuepress/theme-default'
+
+export default {
+ theme: defaultTheme ({
+ themePlugins: {
+ // customize theme plugins here
+ },
+ }),
+}
+
`,5),h=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),r=e("li",null,[e("p",null,[l("Default: "),e("code",null,"true")])],-1),m=e("p",null,"Details:",-1),g=e("h2",{id:"themeplugins-backtotop",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themeplugins-backtotop"},[e("span",null,"themePlugins.backToTop")])],-1),_=e("li",null,[e("p",null,[l("Type: "),e("code",null,"BackToTopPluginOptions | boolean")])],-1),b=e("li",null,[e("p",null,[l("Default: "),e("code",null,"true")])],-1),f=e("p",null,"Details:",-1),v=e("p",null,"Object value is supported as plugin options.",-1),D=e("h2",{id:"themeplugins-container",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themeplugins-container"},[e("span",null,"themePlugins.container")])],-1),y=e("li",null,[e("p",null,[l("Type: "),e("code",null,"Record")])],-1),k=e("p",null,"Details:",-1),C=i("ContainerType
type is:
tip
warning
danger
details
codeGroup
codeGroupItem
",2),P=e("p",null,"Also see:",-1),x=e("h2",{id:"themeplugins-externallinkicon",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themeplugins-externallinkicon"},[e("span",null,"themePlugins.externalLinkIcon")])],-1),T=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),E=e("li",null,[e("p",null,[l("Default: "),e("code",null,"true")])],-1),w=e("p",null,"Details:",-1),O=e("h2",{id:"themeplugins-copycode",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themeplugins-copycode"},[e("span",null,"themePlugins.copyCode")])],-1),z=e("li",null,[e("p",null,[l("Type: "),e("code",null,"CopyCodePluginOptions | boolean")])],-1),L=e("li",null,[e("p",null,[l("Default: "),e("code",null,"true")])],-1),N=e("p",null,"Details:",-1),S=e("p",null,"Object value is supported as plugin options.",-1),j=e("h2",{id:"themeplugins-git",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themeplugins-git"},[e("span",null,"themePlugins.git")])],-1),A=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),B=e("li",null,[e("p",null,[l("Default: "),e("code",null,"true")])],-1),R=e("p",null,"Details:",-1),V=e("h2",{id:"themeplugins-mediumzoom",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themeplugins-mediumzoom"},[e("span",null,"themePlugins.mediumZoom")])],-1),F=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),H=e("li",null,[e("p",null,[l("Default: "),e("code",null,"true")])],-1),I=e("p",null,"Details:",-1),M=e("h2",{id:"themeplugins-nprogress",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themeplugins-nprogress"},[e("span",null,"themePlugins.nprogress")])],-1),G=e("li",null,[e("p",null,[l("Type: "),e("code",null,"boolean")])],-1),Y=e("li",null,[e("p",null,[l("Default: "),e("code",null,"true")])],-1),Z=e("p",null,"Details:",-1),J=e("h2",{id:"themeplugins-seo",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themeplugins-seo"},[e("span",null,"themePlugins.seo")])],-1),U=e("li",null,[e("p",null,[l("Type: "),e("code",null,"SeoPluginOptions | boolean")])],-1),q=e("li",null,[e("p",null,[l("Default: "),e("code",null,"true")])],-1),K=e("p",null,"Details:",-1),Q=e("p",null,"Object value is supported as plugin options.",-1),W=e("h2",{id:"themeplugins-sitemap",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themeplugins-sitemap"},[e("span",null,"themePlugins.sitemap")])],-1),X=e("li",null,[e("p",null,[l("Type: "),e("code",null,"SitemapPluginOptions | boolean")])],-1),$=e("li",null,[e("p",null,[l("Default: "),e("code",null,"true")])],-1),ee=e("p",null,"Details:",-1),le=e("p",null,"Object value is supported as plugin options.",-1);function ne(se,te){const n=u("RouteLink");return a(),p("div",null,[d,e("ul",null,[h,r,e("li",null,[m,e("p",null,[l("Enable "),s(n,{to:"/plugins/active-header-links.html"},{default:t(()=>[l("@vuepress/plugin-active-header-links")]),_:1}),l(" or not.")])])]),g,e("ul",null,[_,b,e("li",null,[f,e("p",null,[l("Enable "),s(n,{to:"/plugins/back-to-top.html"},{default:t(()=>[l("@vuepress/plugin-back-to-top")]),_:1}),l(" or not.")]),v])]),D,e("ul",null,[y,e("li",null,[k,e("p",null,[l("Enable custom containers that powered by "),s(n,{to:"/plugins/container.html"},{default:t(()=>[l("@vuepress/plugin-container")]),_:1}),l(" or not.")]),C]),e("li",null,[P,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/markdown.html#custom-containers"},{default:t(()=>[l("Default Theme > Markdown > Custom Containers")]),_:1})])])])]),x,e("ul",null,[T,E,e("li",null,[w,e("p",null,[l("Enable "),s(n,{to:"/plugins/external-link-icon.html"},{default:t(()=>[l("@vuepress/plugin-external-link-icon")]),_:1}),l(" or not.")])])]),O,e("ul",null,[z,L,e("li",null,[N,e("p",null,[l("Enable "),s(n,{to:"/plugins/copy-code.html"},{default:t(()=>[l("@vuepress/plugin-copy-code")]),_:1}),l(" or not.")]),S])]),j,e("ul",null,[A,B,e("li",null,[R,e("p",null,[l("Enable "),s(n,{to:"/plugins/git.html"},{default:t(()=>[l("@vuepress/plugin-git")]),_:1}),l(" or not.")])])]),V,e("ul",null,[F,H,e("li",null,[I,e("p",null,[l("Enable "),s(n,{to:"/plugins/medium-zoom.html"},{default:t(()=>[l("@vuepress/plugin-medium-zoom")]),_:1}),l(" or not.")])])]),M,e("ul",null,[G,Y,e("li",null,[Z,e("p",null,[l("Enable "),s(n,{to:"/plugins/nprogress.html"},{default:t(()=>[l("@vuepress/plugin-nprogress")]),_:1}),l(" or not.")])])]),J,e("ul",null,[U,q,e("li",null,[K,e("p",null,[l("Enable "),s(n,{to:"/plugins/seo/"},{default:t(()=>[l("@vuepress/plugin-seo")]),_:1}),l(" or not.")]),Q])]),W,e("ul",null,[X,$,e("li",null,[ee,e("p",null,[l("Enable "),s(n,{to:"/plugins/sitemap/"},{default:t(()=>[l("@vuepress/plugin-sitemap")]),_:1}),l(" or not.")]),le])])])}const oe=o(c,[["render",ne],["__file","plugin.html.vue"]]),ue=JSON.parse('{"path":"/themes/default/plugin.html","title":"Plugins Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"themePlugins.activeHeaderLinks","slug":"themeplugins-activeheaderlinks","link":"#themeplugins-activeheaderlinks","children":[]},{"level":2,"title":"themePlugins.backToTop","slug":"themeplugins-backtotop","link":"#themeplugins-backtotop","children":[]},{"level":2,"title":"themePlugins.container","slug":"themeplugins-container","link":"#themeplugins-container","children":[]},{"level":2,"title":"themePlugins.externalLinkIcon","slug":"themeplugins-externallinkicon","link":"#themeplugins-externallinkicon","children":[]},{"level":2,"title":"themePlugins.copyCode","slug":"themeplugins-copycode","link":"#themeplugins-copycode","children":[]},{"level":2,"title":"themePlugins.git","slug":"themeplugins-git","link":"#themeplugins-git","children":[]},{"level":2,"title":"themePlugins.mediumZoom","slug":"themeplugins-mediumzoom","link":"#themeplugins-mediumzoom","children":[]},{"level":2,"title":"themePlugins.nprogress","slug":"themeplugins-nprogress","link":"#themeplugins-nprogress","children":[]},{"level":2,"title":"themePlugins.seo","slug":"themeplugins-seo","link":"#themeplugins-seo","children":[]},{"level":2,"title":"themePlugins.sitemap","slug":"themeplugins-sitemap","link":"#themeplugins-sitemap","children":[]}],"git":{"updatedTime":1706960012000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"themes/default/plugin.md"}');export{oe as comp,ue as data};
diff --git a/assets/prismjs.html-BV9oIJOT.js b/assets/prismjs.html-BV9oIJOT.js
new file mode 100644
index 0000000000..48167a1257
--- /dev/null
+++ b/assets/prismjs.html-BV9oIJOT.js
@@ -0,0 +1,11 @@
+import{_ as o,r as l,o as t,c as p,a as n,b as s,d as e,e as r}from"./app-BcH8wZQx.js";const c={},d=s("h1",{id:"prismjs",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#prismjs"},[s("span",null,"prismjs")])],-1),u={href:"https://prismjs.com/",target:"_blank",rel:"noopener noreferrer"},m=r(`This plugin has been integrated into the default theme.
Notice that this plugin would only tokenize the code fence without adding styles. When using it with a custom theme, you may need to choose and import Prism.js style theme yourself.
npm i -D @vuepress/plugin-prismjs@next
+
import { prismjsPlugin } from '@vuepress/plugin-prismjs'
+
+export default {
+ plugins: [
+ prismjsPlugin ({
+ // options
+ }),
+ ],
+}
+
`,7),h=s("li",null,[s("p",null,[e("Type: "),s("code",null,"string[]")])],-1),g=s("li",null,[s("p",null,[e("Default: "),s("code",null,"['markdown', 'jsdoc', 'yaml']")])],-1),v=s("p",null,"Details:",-1),D=s("p",null,"Languages to preload.",-1),_=s("p",null,"By default, languages will be loaded on demand when parsing markdown files.",-1),y={href:"https://github.com/PrismJS/prism/issues/2716",target:"_blank",rel:"noopener noreferrer"};function b(f,k){const i=l("NpmBadge"),a=l("ExternalLinkIcon");return t(),p("div",null,[d,n(i,{package:"@vuepress/plugin-prismjs"}),s("p",null,[e("This plugin will enable syntax highlighting for markdown code fence with "),s("a",u,[e("Prism.js"),n(a)]),e(".")]),m,s("ul",null,[h,g,s("li",null,[v,D,_,s("p",null,[e("However, Prism.js has "),s("a",y,[e("some potential issues"),n(a)]),e(" about loading languages dynamically. To avoid them, you can preload languages via this option.")])])])])}const j=o(c,[["render",b],["__file","prismjs.html.vue"]]),x=JSON.parse('{"path":"/plugins/prismjs.html","title":"prismjs","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"preloadLanguages","slug":"preloadlanguages","link":"#preloadlanguages","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/prismjs.md"}');export{j as comp,x as data};
diff --git a/assets/prismjs.html-C-Pt75oH.js b/assets/prismjs.html-C-Pt75oH.js
new file mode 100644
index 0000000000..83d6e89ab5
--- /dev/null
+++ b/assets/prismjs.html-C-Pt75oH.js
@@ -0,0 +1,11 @@
+import{_ as r,r as l,o as p,c as o,a as e,b as s,d as n,e as t}from"./app-BcH8wZQx.js";const c={},d=s("h1",{id:"prismjs",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#prismjs"},[s("span",null,"prismjs")])],-1),u={href:"https://prismjs.com/",target:"_blank",rel:"noopener noreferrer"},m=t(`该插件已经集成到默认主题中。
需要注意的是,该插件仅会给代码块添加 HTML 标记,而不会添加样式。当你在一个自定义主题中使用它时,可能需要自己选择并引入 Prism.js 样式主题。
npm i -D @vuepress/plugin-prismjs@next
+
import { prismjsPlugin } from '@vuepress/plugin-prismjs'
+
+export default {
+ plugins: [
+ prismjsPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
`,7),h=s("li",null,[s("p",null,[n("类型: "),s("code",null,"string[]")])],-1),g=s("li",null,[s("p",null,[n("默认值: "),s("code",null,"['markdown', 'jsdoc', 'yaml']")])],-1),v=s("p",null,"详情:",-1),D=s("p",null,"需要预加载的语言。",-1),_=s("p",null,"默认情况下,语言会在解析 Markdown 文件时按需加载。",-1),b={href:"https://github.com/PrismJS/prism/issues/2716",target:"_blank",rel:"noopener noreferrer"};function y(k,C){const i=l("NpmBadge"),a=l("ExternalLinkIcon");return p(),o("div",null,[d,e(i,{package:"@vuepress/plugin-prismjs"}),s("p",null,[n("该插件使用 "),s("a",u,[n("Prism.js"),e(a)]),n(" 来为 Markdown 代码块启用代码高亮。")]),m,s("ul",null,[h,g,s("li",null,[v,D,_,s("p",null,[n("然而, Prism.js 在动态加载语言时可能会遇到 "),s("a",b,[n("一些潜在的问题"),e(a)]),n(" 。为了避免这些问题,你可以使用该配置项来预加载一些语言。")])])])])}const j=r(c,[["render",y],["__file","prismjs.html.vue"]]),x=JSON.parse('{"path":"/zh/plugins/prismjs.html","title":"prismjs","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"preloadLanguages","slug":"preloadlanguages","link":"#preloadlanguages","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/prismjs.md"}');export{j as comp,x as data};
diff --git a/assets/reading-time.html-CKM9bz7P.js b/assets/reading-time.html-CKM9bz7P.js
new file mode 100644
index 0000000000..35571bb93a
--- /dev/null
+++ b/assets/reading-time.html-CKM9bz7P.js
@@ -0,0 +1,95 @@
+import{_ as a,r as l,o as e,c as p,a as i,b as s,e as o}from"./app-BcH8wZQx.js";const c={},r=s("h1",{id:"reading-time",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#reading-time"},[s("span",null,"reading-time")])],-1),t=o(`此插件会为每个页面生成字数统计与预计阅读时间。
npm i -D @vuepress/plugin-reading-time@next
+
import { readingTimePlugin } from '@vuepress/plugin-reading-time'
+
+export default {
+ plugins: [
+ readingTimePlugin ({
+ // 配置项
+ }),
+ ],
+}
+
插件会将相关信息注入到页面数据的 readingTime
,其中:
readingTime.minutes
:为预计阅读时间(分钟)number
readingTime.words
:字数统计,number
对于任何页面,你可以从 page.data.readingTime
获取预计阅读时间与字数统计:
page . data . readingTime // { minutes: 3.2, words: 934 }
+
你可以在 extendsPage
以及其他生命周期获取它做进一步处理:
export default {
+ // ...
+ extendsPage : ( page ) => {
+ page . data . readingTime // { minutes: 3.2, words: 934 }
+ },
+
+ onInitialized : ( app ) => {
+ app . pages . map (( page ) => {
+ page . data . readingTime // { minutes: 3.2, words: 934 }
+ })
+ },
+}
+
你可以从 @vuepress/plugin-reading-time/client
导入 useReadingTimeData
和 useReadingTimeLocale
来获取当前页面的阅读时间数据和语言环境数据:
< script setup lang = "ts" >
+import {
+ useReadingTimeData ,
+ useReadingTimeLocale ,
+} from '@vuepress/plugin-reading-time/client'
+
+const readingTimeData = useReadingTimeData () // { minutes: 1.1, words: 100 }
+const readingTimeLocale = useReadingTimeLocale () // { time: "1 分钟", words: "100 字" }
+</ script >
+
类型:number
默认值:300
详情: 每分钟阅读字数 内置支持语言 简体中文 (zh-CN)繁体中文 (zh-TW)英文(美国) (en-US)德语 (de-DE)德语(澳大利亚) (de-AT)俄语 (ru-RU)乌克兰语 (uk-UA)越南语 (vi-VN)葡萄牙语(巴西) (pt-BR)波兰语 (pl-PL)法语 (fr-FR)西班牙语 (es-ES)斯洛伐克 (sk-SK)日语 (ja-JP)土耳其语 (tr-TR)韩语 (ko-KR)芬兰语 (fi-FI)印尼语 (id-ID)荷兰语 (nl-NL)你可以从 @vuepress/plugin-reading-time/client
导入并使用这些 API:
interface ReadingTime {
+ /** 分钟为单位的预计阅读时长 */
+ minutes : number
+ /** 内容的字数 */
+ words : number
+}
+
+const useReadingTimeData : () => ComputedRef < ReadingTime | null >
+
当插件被禁用时会返回 null
。
interface ReadingTimeLocale {
+ /** 当前语言的预计阅读时间 */
+ time : string
+ /** 当前语言的字数文字 */
+ words : string
+}
+
+const useReadingTimeLocale : () => ComputedRef < ReadingTimeLocale >
+
由于此插件主要面向插件和主题开发者,所以提供了 "使用 API":
import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
+
+export default ( options ) => ( app ) => {
+ useReadingTimePlugin ( app , {
+ // 你的选项
+ })
+
+ return {
+ name: 'vuepress-plugin-xxx' , // or vuepress-theme-xxx
+ }
+}
+
为什么你应该使用 "使用 API"
当你多次注册一个插件时,vuepress 会给你一个警告,告诉你只有第一个插件会生效。useReadingTimePlugin
会自动检测插件是否已经注册,避免多次注册。
如果你在 extendsPage
生命周期访问阅读时间数据,那么 @vuepress/plugin-reading-time
必须在你的主题或插件之前被调用,否则你会得到未定义的 page.data.readingTime
。useReadingTimePlugin
确保了 @vuepress/plugin-reading-time
在你的主题或插件之前被调用。
我们也提供了一个 removeReadingTimePlugin
api 来移除插件。你可以使用它来确保你的调用生效或清除插件:
import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
+
+export default ( options ) => ( app ) => {
+ // 这会移除任何当前存在的阅读时间插件
+ removeReadingTimePlugin ( app )
+
+ // 所以这会生效,即使之前已经注册了一个阅读时间插件
+ useReadingTimePlugin ( app , {
+ // 你的选项
+ })
+
+ return {
+ name: 'vuepress-plugin-xxx' , // or vuepress-theme-xxx
+ }
+}
+
`,34);function d(D,u){const n=l("NpmBadge");return e(),p("div",null,[r,i(n,{package:"@vuepress/plugin-reading-time"}),t])}const y=a(c,[["render",d],["__file","reading-time.html.vue"]]),m=JSON.parse('{"path":"/zh/plugins/reading-time.html","title":"reading-time","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[{"level":3,"title":"在 Node 侧获取数据","slug":"在-node-侧获取数据","link":"#在-node-侧获取数据","children":[]},{"level":3,"title":"在客户端侧获取数据","slug":"在客户端侧获取数据","link":"#在客户端侧获取数据","children":[]}]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"wordPerMinute","slug":"wordperminute","link":"#wordperminute","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"客户端 API","slug":"客户端-api","link":"#客户端-api","children":[{"level":3,"title":"useReadingTimeData","slug":"usereadingtimedata","link":"#usereadingtimedata","children":[]},{"level":3,"title":"useReadingTimeLocale","slug":"usereadingtimelocale","link":"#usereadingtimelocale","children":[]}]},{"level":2,"title":"高级使用","slug":"高级使用","link":"#高级使用","children":[]}],"git":{"updatedTime":1708274662000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/reading-time.md"}');export{y as comp,m as data};
diff --git a/assets/reading-time.html-CnG6Oi7_.js b/assets/reading-time.html-CnG6Oi7_.js
new file mode 100644
index 0000000000..2f717432aa
--- /dev/null
+++ b/assets/reading-time.html-CnG6Oi7_.js
@@ -0,0 +1,95 @@
+import{_ as a,r as e,o as l,c as i,a as p,b as s,e as o}from"./app-BcH8wZQx.js";const t={},r=s("h1",{id:"reading-time",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#reading-time"},[s("span",null,"reading-time")])],-1),c=o(`This plugin will generate word count and estimated reading time for each page.
npm i -D @vuepress/plugin-reading-time@next
+
import { readingTimePlugin } from '@vuepress/plugin-reading-time'
+
+export default {
+ plugins: [
+ readingTimePlugin ({
+ // options
+ }),
+ ],
+}
+
The plugin will inject reading time information into the readingTime
of the page data, where:
readingTime.minutes
: estimated reading time (minutes) number
readingTime.words
: word count number
For any page, you can get estimated reading time and word count from page.data.readingTime
:
page . data . readingTime // { minutes: 3.2, words: 934 }
+
You can access it for further processing in the extendsPage
lifecycle and other lifecycle:
export default {
+ // ...
+ extendsPage : ( page ) => {
+ page . data . readingTime // { minutes: 3.2, words: 934 }
+ },
+
+ onInitialized : ( app ) => {
+ app . pages . map (( page ) => {
+ page . data . readingTime // { minutes: 3.2, words: 934 }
+ })
+ },
+}
+
You can import useReadingTimeData
and useReadingTimeLocale
from @vuepress/plugin-reading-time/client
to get the reading time data and locale data of the current page:
< script setup lang = "ts" >
+import {
+ useReadingTimeData ,
+ useReadingTimeLocale ,
+} from '@vuepress/plugin-reading-time/client'
+
+const readingTimeData = useReadingTimeData () // { minutes: 1.1, words: 100 }
+const readingTimeLocale = useReadingTimeLocale () // { time: "1 minute", words: "100 words" }
+</ script >
+
Type: number
Default: 300
Details: Reading speed (words per minute) Built-in Supported Languages Simplified Chinese (zh-CN)Traditional Chinese (zh-TW)English (United States) (en-US)German (de-DE)German (Australia) (de-AT)Russian (ru-RU)Ukrainian (uk-UA)Vietnamese (vi-VN)Portuguese (Brazil) (pt-BR)Polish (pl-PL)French (fr-FR)Spanish (es-ES)Slovak (sk-SK)Japanese (ja-JP)Turkish (tr-TR)Korean (ko-KR)Finnish (fi-FI)Indonesian (id-ID)Dutch (nl-NL)You can import and use these APIs from @vuepress/plugin-reading-time/client
:
These APIs won't throw even you disable the plugin.
interface ReadingTime {
+ /** Expect reading time in minute unit */
+ minutes : number
+ /** Words count of content */
+ words : number
+}
+
+const useReadingTimeData : () => ComputedRef < ReadingTime | null >
+
null
is returned when the plugin is disabled.
interface ReadingTimeLocale {
+ /** Expect reading time text in locale */
+ time : string
+ /** Word count text in locale */
+ words : string
+}
+
+const useReadingTimeLocale : () => ComputedRef < ReadingTimeLocale >
+
This plugin is targeting plugin and theme developers mostly, so we provide a "Use API":
import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
+
+export default ( options ) => ( app ) => {
+ useReadingTimePlugin ( app , {
+ // your options
+ })
+
+ return {
+ name: 'vuepress-plugin-xxx' , // or vuepress-theme-xxx
+ }
+}
+
Why you should use "Use API"
When you register a plugin multiple times, vuepress will gives you warning about that telling you only the first one takes effect. The useReadingTimePlugin
automatically detects if the plugin is registered and avoid registering multiple times. If you access reading time data in extendsPage
lifecycle, then @vuepress/plugin-reading-time
must be called before your theme or plugin, otherwise you will get undefined
for page.data.readingTime
. The useReadingTimePlugin
ensures that @vuepress/plugin-reading-time
is called before your theme or plugin. We also provides a removeReadingTimePlugin
api to remove the plugin.You can use this to ensure your call take effect or clear the plugin:
import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
+
+export default ( options ) => ( app ) => {
+ // this removes any existing reading time plugin at this time
+ removeReadingTimePlugin ( app )
+
+ // so this will take effect even if there is a reading time plugin registered before
+ useReadingTimePlugin ( app , {
+ // your options
+ })
+
+ return {
+ name: 'vuepress-plugin-xxx' , // or vuepress-theme-xxx
+ }
+}
+
`,34);function d(D,u){const n=e("NpmBadge");return l(),i("div",null,[r,p(n,{package:"@vuepress/plugin-reading-time"}),c])}const g=a(t,[["render",d],["__file","reading-time.html.vue"]]),y=JSON.parse('{"path":"/plugins/reading-time.html","title":"reading-time","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[{"level":3,"title":"Getting data on Node Side","slug":"getting-data-on-node-side","link":"#getting-data-on-node-side","children":[]},{"level":3,"title":"Getting data on Client Side","slug":"getting-data-on-client-side","link":"#getting-data-on-client-side","children":[]}]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"wordPerMinute","slug":"wordperminute","link":"#wordperminute","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Client API","slug":"client-api","link":"#client-api","children":[{"level":3,"title":"useReadingTimeData","slug":"usereadingtimedata","link":"#usereadingtimedata","children":[]},{"level":3,"title":"useReadingTimeLocale","slug":"usereadingtimelocale","link":"#usereadingtimelocale","children":[]}]},{"level":2,"title":"Advanced Usage","slug":"advanced-usage","link":"#advanced-usage","children":[]}],"git":{"updatedTime":1708274662000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/reading-time.md"}');export{g as comp,y as data};
diff --git a/assets/redirect.html-By2J3jdY.js b/assets/redirect.html-By2J3jdY.js
new file mode 100644
index 0000000000..8dd1f052f2
--- /dev/null
+++ b/assets/redirect.html-By2J3jdY.js
@@ -0,0 +1,81 @@
+import{_ as a,r as l,o as n,c as o,a as t,b as s,e as i}from"./app-BcH8wZQx.js";const c={},p=s("h1",{id:"redirect",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#redirect"},[s("span",null,"redirect")])],-1),r=i(`This plugin can automatically handle redirects for your site.
npm i -D @vuepress/plugin-redirect@next
+
import { redirectPlugin } from '@vuepress/plugin-redirect'
+
+export default {
+ plugins: [
+ redirectPlugin ({
+ // options
+ }),
+ ],
+}
+
If you change the address of an existing page, you can use the redirectFrom
option in Frontmatter to redirect to the address of this page, which ensures that users are redirected to the new address when they visit the old link.
If you need to redirect an existing page to a new page, you can use the redirectTo
option in Frontmatter to set the address to redirect to. This way the page will redirect to the new address when accessed.
You can also set config
with a redirect map in plugin options, see config for more details.
The plugin can automatically redirect non-multilingual links to the multilingual pages the user needs based on the user's language preference.
To achieve this, you need to leave the default language directory (/
) blank and set autoLocale: true
in plugin options. The plugin will automatically redirect to the correct page according to the user's language.
I.E.: you need to set the following directory structure:
.
+├── en
+│ ├── ...
+│ ├── page.md
+│ └── README.md
+├── zh
+│ ├── ...
+│ ├── page.md
+│ └── README.md
+└── other_languages
+ ├── ...
+ ├── page.md
+ └── README.md
+
And set locales
in theme options with:
export default {
+ locales: {
+ '/en/' : {
+ lang: 'en-US' ,
+ // ...
+ },
+ '/zh/' : {
+ lang: 'zh-CN' ,
+ // ...
+ },
+ // other languages
+ },
+ // ...
+}
+
So when a user accesses /
or /page.html
, they are automatically redirected to /en/
/en/page.html
and /en/
/en/page.html
based on current browser language.
Customizing fallback behavior
Sometimes, users may add more than one language to the system settings. By default, when a site supports a preferred language, but the page not exists for the preferred language, the plugin attempts to match the alternate language set by the user.
If you don't need to fall back to the user's alternate language, but directly match the user's preferred language, set localeFallback: false
in the plugin options.
Customizing missing behavior
Sometimes, when a user visits a page, the document does not yet contain the language version the user needs (a common case is that the current page has not been localized in the relevant language), so the plugin needs to perform a default action, which you can customize by defaultBehavior
in the plugin options:
"defaultLocale"
: Redirect to default language or first available language page (default behavior)"homepage"
: redirect to the home page in the current language (only available if the document contains the user's language)"404"
: Redirect to page 404 in current language (only available if the document contains the user's language)Customizing default locale path
You can customize the default locale path by setting defaultLocale
in the plugin options. By default, the plugin uses the first locale key in locales
as the default language.
The plugin supports automatically switching the link to the multilingual page that the user needs according to the user's language preference when opening a multilingual document. In order to achieve this, you need to set switchLocale
in the plugin options, which can be the following two values:
direct
: switch directly to the user language preference page without askingmodal
: When the user's language preference is different from the current page language, show a modal asking whether to switch languageBy default, the plugin generates a locale setting by reading locale path
and lang
from the site's locales
option. Sometimes, you may want multiple languages to hit the same path, in which case you should set localeConfig
in plugin options.
For example, you might want all English users to match to /en/
and Chinese Traditional users to /zh/
, then you can set:
redirect ({
+ localeConfig: {
+ '/en/' : [ 'en-US' , 'en-UK' , 'en' ],
+ '/zh/' : [ 'zh-CN' , 'zh-TW' , 'zh' ],
+ },
+})
+
Sometimes you may change base
or use new domain for your site, so you may want the original site automatically redirects to the new one.
To solve this, the plugin provide vp-redirect
cli.
Usage:
+ $ vp-redirect generate [sourceDir]
+
+Options:
+ --hostname < hostnam e> Hostname to redirect to ( E.g.: https://new.example.com/ ) (default: / )
+ -c, --config < confi g> Set path to config file
+ -o, --output < outpu t> Set the output directory ( default: .vuepress/redirect )
+ --cache < cach e> Set the directory of the cache files
+ -t, --temp < tem p> Set the directory of the temporary files
+ --clean-cache Clean the cache files before generation
+ --clean-temp Clean the temporary files before generation
+ -h, --help Display this message
+
You need to pass in VuePress project source dir and also set the hostname
option. The redirect helper cli will initialize your VuePress project to get pages, then generate and output the redirect html files to the output directory.
By default, the plugin will output to .vuepress/redirect
directory under source directory. And you should upload it to your original site to provide redirection.
Type: Record<string, string> | ((app: App) => Record<string, string>)
Details: Redirect map.
Example:
When base is set to /base/
:
redirect /base/foo.html
to /base/bar.html
/base/baz.html
to https://example.com/qux.html
.redirect ({
+ config: {
+ '/foo.html' : '/bar.html' ,
+ '/baz.html' : 'https://example.com/qux.html' ,
+ },
+})
+
Redirect post folder to posts folder:
redirect ({
+ hostname: 'https://example.com' ,
+ config : ( app ) =>
+ Object . fromEntries (
+ app . pages
+ . filter (({ path }) => path . startsWith ( '/posts/' ))
+ . map (({ path }) => [ path . replace ( / ^ \\/ posts \\/ / , '/post/' ), path ]),
+ ),
+})
+
Type: boolean
Default: false
Details: Whether enable locales redirection. Type: boolean
Default: true
Details: Whether fallback to other locales user defined Type: "defaultLocale" | "homepage" | "404"
Default: "defaultLocale"
Details: Behavior when a locale version is not available for current link. Type: string
Default: the first locale Details: Default locale path. Type: string | string[]
Details: The link which this page redirects from. Type: string
Details: The link which this page redirects to. You can customize the style of the redirect popup via CSS variables:
:root {
+ --redirect-z-index : 1499 ;
+ --redirect-bg-color : #fff ;
+ --redirect-bg-color-light : #f3f4f5 ;
+ --redirect-bg-color-lighter : #eeeeee ;
+ --redirect-text-color : #2c3e50 ;
+ --redirect-primary-bg-color : #3eaf7c ;
+ --redirect-primary-hover-bg-color : #4abf8a ;
+ --redirect-primary-text-color : #fff ;
+}
+
`,55);function d(u,D){const e=l("NpmBadge");return n(),o("div",null,[p,t(e,{package:"@vuepress/plugin-redirect"}),r])}const y=a(c,[["render",d],["__file","redirect.html.vue"]]),g=JSON.parse('{"path":"/plugins/redirect.html","title":"redirect","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[{"level":3,"title":"Control Page Redirection","slug":"control-page-redirection","link":"#control-page-redirection","children":[]},{"level":3,"title":"Auto Locales","slug":"auto-locales","link":"#auto-locales","children":[]},{"level":3,"title":"Automatically switch languages","slug":"automatically-switch-languages","link":"#automatically-switch-languages","children":[]},{"level":3,"title":"Customizing Locale Settings","slug":"customizing-locale-settings","link":"#customizing-locale-settings","children":[]},{"level":3,"title":"Redirecting Sites","slug":"redirecting-sites","link":"#redirecting-sites","children":[]}]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"config","slug":"config","link":"#config","children":[]},{"level":3,"title":"autoLocale","slug":"autolocale","link":"#autolocale","children":[]},{"level":3,"title":"switchLocale","slug":"switchlocale","link":"#switchlocale","children":[]},{"level":3,"title":"localeConfig","slug":"localeconfig","link":"#localeconfig","children":[]},{"level":3,"title":"localeFallback","slug":"localefallback","link":"#localefallback","children":[]},{"level":3,"title":"defaultBehavior","slug":"defaultbehavior","link":"#defaultbehavior","children":[]},{"level":3,"title":"defaultLocale","slug":"defaultlocale","link":"#defaultlocale","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"redirectFrom","slug":"redirectfrom","link":"#redirectfrom","children":[]},{"level":3,"title":"redirectTo","slug":"redirectto","link":"#redirectto","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]}],"git":{"updatedTime":1707223547000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"plugins/redirect.md"}');export{y as comp,g as data};
diff --git a/assets/redirect.html-Ca7jGAoB.js b/assets/redirect.html-Ca7jGAoB.js
new file mode 100644
index 0000000000..07a4ef1551
--- /dev/null
+++ b/assets/redirect.html-Ca7jGAoB.js
@@ -0,0 +1,81 @@
+import{_ as c,r as l,o as i,c as r,a as n,b as s,d as a,w as t,e}from"./app-BcH8wZQx.js";const d={},D=s("h1",{id:"redirect",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#redirect"},[s("span",null,"redirect")])],-1),u=e(`此插件提供页面与整站重定向功能。
npm i -D @vuepress/plugin-redirect@next
+
import { redirectPlugin } from '@vuepress/plugin-redirect'
+
+export default {
+ plugins: [
+ redirectPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
如果你改动了已有页面的地址,你可以在 Frontmatter 中使用 redirectFrom
选项设置重定向到此页面的地址,这样可以保证用户在访问旧链接时重定向到新的地址。
如果你需要将已有的页面重定向到新的页面,可以在 Frontmatter 中使用 redirectTo
选项设置需要重定向到的地址。这样该页面会在访问时重定向到新的地址。
你还可以通过插件选项中的 config
设置一个重定向映射,详见 config 。
插件可以根据用户的语言首选项,自动将无多语言链接重定向到用户需要的多语言页面。为了实现这一点,你需要留空默认的语言目录 (/
),并在插件选项中设置 autoLocale: true
。插件会自动根据用户语言跳转到对应的语言页面。
也就是你需要设置以下目录结构:
.
+├── en
+│ ├── ...
+│ ├── page.md
+│ └── README.md
+├── zh
+│ ├── ...
+│ ├── page.md
+│ └── README.md
+└── other_languages
+ ├── ...
+ ├── page.md
+ └── README.md
+
并将主题选项的 locales 设置为:
export default {
+ locales: {
+ '/en/' : {
+ lang: 'en-US' ,
+ // ...
+ },
+ '/zh/' : {
+ lang: 'zh-CN' ,
+ // ...
+ },
+ // other languages
+ },
+ // ...
+}
+
这样当用户访问 /
或 /page.html
时,他们会自动根据当前浏览器语言重定向到 /en/
/en/page.html
与 /zh/
/zh/page.html
。
自定义回退行为
有些时候,用户可能会在系统设置中添加多个语言。默认情况下,在站点支持首选语言,但首选语言不存在相应页面时,插件会尝试匹配用户设置的备用语言。
如果不需要回退到用户备用语言,而直接匹配用户首选语言,请在插件选项中设置 localeFallback: false
。
自定义缺失行为
有些时候,当用户访问一个页面时,文档尚未包含用户需要的语言版本 (一个普遍的情况是当前页面尚未完成相关语言的本地化),这样插件需要做出默认行为,你可以通过插件选项中的 defaultBehavior
定制它:
"defaultLocale"
: 重定向到默认语言或首个可用语言页面 (默认行为)"homepage"
: 重定向到当前语言的主页 (仅在文档包含用户语言时可用)"404"
: 重定向到当前语言的 404 页 (仅在文档包含用户语言时可用)自定义默认路径
你可以通过设置插件选项中的 defaultLocale
来自定义默认路径。默认情况下,插件会使用 locales
中的第一个键名作为默认路径。
插件支持在多语言文档中,自动根据用户语言首选项,将链接切换到用户需要的多语言页面。为了实现这一点,你需要在插件选项中设置 switchLocale
,它可以是以下两个值:
direct
: 直接切换到用户语言首选项页面,而不询问modal
: 在用户语言首选项与当前页面语言不同时,弹出一个对话框询问用户是否切换语言默认情况下,插件会从站点的多语言配置 locales
选项中,读取 语言路径
和 lang
生成多语言配置。有些时候,你可能希望多个语言命中同一个路径,这种情况下,你应该设置插件的 localeConfig
选项。
比如,你可能希望所有英文用户都匹配到 /en/
,并将繁体中文用户匹配到 /zh/
中,那么你可以设置:
redirect ({
+ localeConfig: {
+ '/en/' : [ 'en-US' , 'en-UK' , 'en' ],
+ '/zh/' : [ 'zh-CN' , 'zh-TW' , 'zh' ],
+ },
+})
+
有时你可能会更改 base
或为你的站点使用新域名,因此你可能希望原始站点自动重定向到新站点。
为了解决这个问题,插件提供了 vp-redirect
脚手架。
使用:
+ $ vp-redirect generate [源文件夹]
+
+Options:
+ --hostname < hostnam e> 重定向到的域名 ( 例如: https://new.example.com/ ) (默认: / )
+ -c, --config < confi g> 设置配置文件路径
+ -o, --output < outpu t> 设置输出目录 ( 默认: .vuepress/redirect )
+ --cache < cach e> 设置缓存文件的目录
+ -t, --temp < tem p> 设置临时文件的目录
+ --clean-cache 生成前清理缓存文件
+ --clean-temp 生成前清理临时文件
+ -h, --help 显示此消息
+
你需要传入 VuePress 项目源目录并设置 hostname
选项。重定向助手脚手架将初始化你的 VuePress 项目以获取页面,然后在输出目录生成重定向 html 文件。
默认情况下,插件将输出到源文件夹下的 .vuepress/redirect
目录。你应该将其上传到你的原始站点以提供重定向。
类型:Record<string, string> | ((app: App) => Record<string, string>)
详情
页面重定向映射。
可直接传入对象或传入参数为 App
的函数返回值一个对象。
每个键名必须是一个绝对路径,代表重定向的源页面地址。
每个键值是重定向的目标地址,可以是绝对路径或完整路径。
示例:
当 base 为 /base/
时:
将 /base/foo.html
重定向到 /base/bar.html
将 /base/baz.html
重定向到 https://example.com/qux.html
。 redirect ({
+ config: {
+ '/foo.html' : '/bar.html' ,
+ '/baz.html' : 'https://example.com/qux.html' ,
+ },
+})
+
将 post 文件夹的路径重定向到 posts 文件夹
redirect ({
+ hostname: 'https://example.com' ,
+ config : ( app ) =>
+ Object . fromEntries (
+ app . pages
+ . filter (({ path }) => path . startsWith ( '/posts/' ))
+ . map (({ path }) => [ path . replace ( / ^ \\/ posts \\/ / , '/post/' ), path ]),
+ ),
+})
+
`,35),v=s("li",null,[a("类型:"),s("code",null,"boolean")],-1),y=s("li",null,[a("默认值: "),s("code",null,"false")],-1),h=s("li",null,"详情: 是否启用语言重定向",-1),m=e(`类型:Record<string, string | string[]>
详情:多语言语言配置 类型:boolean
默认值: true
详情:是否回退到用户定义的其他语言 类型:"defaultLocale" | "homepage" | "404"
默认值: "defaultLocale"
详情:当前链接没有可用的语言版本时的行为 类型:string
默认值: 首个语言路径 详情:默认语言路径 类型:string | string[]
详情:重定向到该页面的地址。 你可以通过 CSS 变量来自定义重定向弹窗的样式:
:root {
+ --redirect-z-index : 1499 ;
+ --redirect-bg-color : #fff ;
+ --redirect-bg-color-light : #f3f4f5 ;
+ --redirect-bg-color-lighter : #eeeeee ;
+ --redirect-text-color : #2c3e50 ;
+ --redirect-primary-bg-color : #3eaf7c ;
+ --redirect-primary-hover-bg-color : #4abf8a ;
+ --redirect-primary-text-color : #fff ;
+}
+
`,18);function C(b,g){const o=l("NpmBadge"),p=l("RouteLink");return i(),r("div",null,[D,n(o,{package:"@vuepress/plugin-rtl"}),u,s("ul",null,[v,y,h,s("li",null,[a("参考: "),s("ul",null,[s("li",null,[n(p,{to:"/zh/plugins/guide.html#%E9%87%8D%E5%AE%9A%E5%90%91%E8%AF%AD%E8%A8%80"},{default:t(()=>[a("指南 → 重定向语言")]),_:1})])])])]),m])}const f=c(d,[["render",C],["__file","redirect.html.vue"]]),A=JSON.parse('{"path":"/zh/plugins/redirect.html","title":"redirect","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[{"level":3,"title":"设置重定向","slug":"设置重定向","link":"#设置重定向","children":[]},{"level":3,"title":"自动多语言","slug":"自动多语言","link":"#自动多语言","children":[]},{"level":3,"title":"自动切换语言","slug":"自动切换语言","link":"#自动切换语言","children":[]},{"level":3,"title":"自定义多语言配置","slug":"自定义多语言配置","link":"#自定义多语言配置","children":[]},{"level":3,"title":"重定向站点","slug":"重定向站点","link":"#重定向站点","children":[]}]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"config","slug":"config","link":"#config","children":[]},{"level":3,"title":"autoLocale","slug":"autolocale","link":"#autolocale","children":[]},{"level":3,"title":"switchLocale","slug":"switchlocale","link":"#switchlocale","children":[]},{"level":3,"title":"localeConfig","slug":"localeconfig","link":"#localeconfig","children":[]},{"level":3,"title":"localeFallback","slug":"localefallback","link":"#localefallback","children":[]},{"level":3,"title":"defaultBehavior","slug":"defaultbehavior","link":"#defaultbehavior","children":[]},{"level":3,"title":"defaultLocale","slug":"defaultlocale","link":"#defaultlocale","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"redirectFrom","slug":"redirectfrom","link":"#redirectfrom","children":[]},{"level":3,"title":"redirectTo","slug":"redirectto","link":"#redirectto","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]}],"git":{"updatedTime":1707152654000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/redirect.md"}');export{f as comp,A as data};
diff --git a/assets/register-components.html-DOgNY1hI.js b/assets/register-components.html-DOgNY1hI.js
new file mode 100644
index 0000000000..e8c77c2e10
--- /dev/null
+++ b/assets/register-components.html-DOgNY1hI.js
@@ -0,0 +1,49 @@
+import{_ as t,r as a,o as i,c,a as e,b as s,d as n,e as l}from"./app-BcH8wZQx.js";const r={},D=s("h1",{id:"register-components",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#register-components"},[s("span",null,"register-components")])],-1),d=l(`根据组件文件或目录自动注册 Vue 组件。
npm i -D @vuepress/plugin-register-components@next
+
import { registerComponentsPlugin } from '@vuepress/plugin-register-components'
+
+export default {
+ plugins: [
+ registerComponentsPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
import { getDirname , path } from 'vuepress/utils'
+
+const __dirname = getDirname ( import . meta . url )
+
+export default {
+ plugins: [
+ registerComponentsPlugin ({
+ components: {
+ FooBar: path . resolve ( __dirname , './components/FooBar.vue' ),
+ },
+ }),
+ ],
+}
+
import { getDirname , path } from 'vuepress/utils'
+
+const __dirname = getDirname ( import . meta . url )
+
+export default {
+ plugins: [
+ registerComponentsPlugin ({
+ componentsDir: path . resolve ( __dirname , './components' ),
+ }),
+ ],
+}
+
组件目录:
components
+├─ FooBar.vue
+└─ Baz.vue
+
组件会像这样被注册:
import { defineAsyncComponent } from 'vue'
+
+app . component (
+ 'FooBar' ,
+ defineAsyncComponent (() => import ( '/path/to/components/FooBar.vue' )),
+)
+
+app . component (
+ 'Baz' ,
+ defineAsyncComponent (() => import ( '/path/to/components/Baz.vue' )),
+)
+
`,16),m=s("li",null,[s("p",null,[n("类型: "),s("code",null,"string[]")])],-1),u=s("li",null,[s("p",null,[n("默认值: "),s("code",null,"['**/*.vue']")])],-1),y=s("p",null,"详情:",-1),v={href:"https://github.com/sindresorhus/globby",target:"_blank",rel:"noopener noreferrer"},C=s("p",null,[n("该 Patterns 是相对于 "),s("a",{href:"#componentsdir"},"componentsDir"),n(" 目录的。")],-1),h=l(' ',2);function b(g,E){const p=a("NpmBadge"),o=a("ExternalLinkIcon");return i(),c("div",null,[D,e(p,{package:"@vuepress/plugin-register-components"}),d,s("ul",null,[m,u,s("li",null,[y,s("p",null,[n("使用 "),s("a",v,[n("globby"),e(o)]),n(" 来匹配组件文件的 Patterns 。")]),C])]),h])}const f=t(r,[["render",b],["__file","register-components.html.vue"]]),A=JSON.parse('{"path":"/zh/plugins/register-components.html","title":"register-components","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"components","slug":"components","link":"#components","children":[]},{"level":3,"title":"componentsDir","slug":"componentsdir","link":"#componentsdir","children":[]},{"level":3,"title":"componentsPatterns","slug":"componentspatterns","link":"#componentspatterns","children":[]},{"level":3,"title":"getComponentName","slug":"getcomponentname","link":"#getcomponentname","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/register-components.md"}');export{f as comp,A as data};
diff --git a/assets/register-components.html-T4_-J2Vs.js b/assets/register-components.html-T4_-J2Vs.js
new file mode 100644
index 0000000000..60e47db70b
--- /dev/null
+++ b/assets/register-components.html-T4_-J2Vs.js
@@ -0,0 +1,49 @@
+import{_ as t,r as e,o as i,c,a,b as s,d as n,e as l}from"./app-BcH8wZQx.js";const r={},D=s("h1",{id:"register-components",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#register-components"},[s("span",null,"register-components")])],-1),d=l(`Register Vue components from component files or directory automatically.
npm i -D @vuepress/plugin-register-components@next
+
import { registerComponentsPlugin } from '@vuepress/plugin-register-components'
+
+export default {
+ plugins: [
+ registerComponentsPlugin ({
+ // options
+ }),
+ ],
+}
+
Type: Record<string, string>
Default: {}
Details:
An object that defines name of components and their corresponding file path.
The key will be used as the component name, and the value is an absolute path of the component file.
If the component name from this option conflicts with componentsDir option, this option will have a higher priority.
Example:
import { getDirname , path } from 'vuepress/utils'
+
+const __dirname = getDirname ( import . meta . url )
+
+export default {
+ plugins: [
+ registerComponentsPlugin ({
+ components: {
+ FooBar: path . resolve ( __dirname , './components/FooBar.vue' ),
+ },
+ }),
+ ],
+}
+
import { getDirname , path } from 'vuepress/utils'
+
+const __dirname = getDirname ( import . meta . url )
+
+export default {
+ plugins: [
+ registerComponentsPlugin ({
+ componentsDir: path . resolve ( __dirname , './components' ),
+ }),
+ ],
+}
+
Components directory:
components
+├─ FooBar.vue
+└─ Baz.vue
+
Components will be registered like this:
import { defineAsyncComponent } from 'vue'
+
+app . component (
+ 'FooBar' ,
+ defineAsyncComponent (() => import ( '/path/to/components/FooBar.vue' )),
+)
+
+app . component (
+ 'Baz' ,
+ defineAsyncComponent (() => import ( '/path/to/components/Baz.vue' )),
+)
+
`,16),m=s("li",null,[s("p",null,[n("Type: "),s("code",null,"string[]")])],-1),u=s("li",null,[s("p",null,[n("Default: "),s("code",null,"['**/*.vue']")])],-1),y=s("p",null,"Details:",-1),v={href:"https://github.com/sindresorhus/globby",target:"_blank",rel:"noopener noreferrer"},C=s("p",null,[n("The patterns are relative to "),s("a",{href:"#componentsdir"},"componentsDir"),n(".")],-1),h=l('Type: (filename: string) => string
Default: (filename) => path.trimExt(filename.replace(/\\/|\\\\/g, '-'))
Details:
A function to get component name from the filename.
It will only take effect on the files in the componentsDir which are matched with the componentsPatterns .
Notice that the filename
is a filepath relative to componentsDir .
',2);function b(g,f){const o=e("NpmBadge"),p=e("ExternalLinkIcon");return i(),c("div",null,[D,a(o,{package:"@vuepress/plugin-register-components"}),d,s("ul",null,[m,u,s("li",null,[y,s("p",null,[n("Patterns to match component files using "),s("a",v,[n("globby"),a(p)]),n(".")]),C])]),h])}const _=t(r,[["render",b],["__file","register-components.html.vue"]]),A=JSON.parse('{"path":"/plugins/register-components.html","title":"register-components","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"components","slug":"components","link":"#components","children":[]},{"level":3,"title":"componentsDir","slug":"componentsdir","link":"#componentsdir","children":[]},{"level":3,"title":"componentsPatterns","slug":"componentspatterns","link":"#componentspatterns","children":[]},{"level":3,"title":"getComponentName","slug":"getcomponentname","link":"#getcomponentname","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/register-components.md"}');export{_ as comp,A as data};
diff --git a/assets/remove-pwa.html-Csjzw7C2.js b/assets/remove-pwa.html-Csjzw7C2.js
new file mode 100644
index 0000000000..80dc443ff6
--- /dev/null
+++ b/assets/remove-pwa.html-Csjzw7C2.js
@@ -0,0 +1,11 @@
+import{_ as o,r as s,o as t,c as r,a,b as e,d as n,w as c,e as p}from"./app-BcH8wZQx.js";const d={},u=e("h1",{id:"remove-pwa",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#remove-pwa"},[e("span",null,"remove-pwa")])],-1),h=e("p",null,"This plugin removes any related service worker from your VuePress site, so that users can still get updates if you removed any PWA plugin after enabling it.",-1),v={class:"custom-container tip"},m=e("p",{class:"custom-container-title"},"Why this plugin is needed if you used PWA plugin once?",-1),g=e("code",null,"@vuepress/plugin-pwa",-1),w=e("p",null,"However, if you remove pwa plugin, the old service worker will still be there, but they can never get an update because they can never found a new service worker to update to. So users will stay with the old version of your site.",-1),D=e("p",null,"To solve this problem:",-1),y=e("ol",null,[e("li",null,"A new service worker with empty contents shall be generated in the original place."),e("li",null,"The new service worker shall attempt to remove contents that old service worker cached, then it should unregister itself.")],-1),f=p(`npm i -D @vuepress/plugin-remove-pwa@next
+
import { removePwaPlugin } from '@vuepress/plugin-remove-pwa'
+
+export default {
+ plugins: [
+ removePwaPlugin ({
+ // options
+ }),
+ ],
+}
+
Type: string
Default: 'workbox'
Details: The cache prefix for the service worker. Type: string
Default: 'service-worker.js'
Details: The location of the old service worker. `,8);function _(b,k){const l=s("NpmBadge"),i=s("RouteLink");return t(),r("div",null,[u,a(l,{package:"@vuepress/plugin-remove-pwa"}),h,e("div",v,[m,e("p",null,[n("PWA plugins like "),a(i,{to:"/plugins/pwa/"},{default:c(()=>[g]),_:1}),n(" register service worker to your site, which will cache your site and make it available offline.")]),w,D,y]),f])}const C=o(d,[["render",_],["__file","remove-pwa.html.vue"]]),P=JSON.parse('{"path":"/plugins/remove-pwa.html","title":"remove-pwa","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"cachePrefix","slug":"cacheprefix","link":"#cacheprefix","children":[]},{"level":3,"title":"swLocation","slug":"swlocation","link":"#swlocation","children":[]}]}],"git":{"updatedTime":1708397875000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"plugins/remove-pwa.md"}');export{C as comp,P as data};
diff --git a/assets/remove-pwa.html-Q3Bnn4Lw.js b/assets/remove-pwa.html-Q3Bnn4Lw.js
new file mode 100644
index 0000000000..982038723c
--- /dev/null
+++ b/assets/remove-pwa.html-Q3Bnn4Lw.js
@@ -0,0 +1,11 @@
+import{_ as o,r as s,o as r,c,a,b as e,d as n,w as t,e as p}from"./app-BcH8wZQx.js";const d={},u=e("h1",{id:"remove-pwa",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#remove-pwa"},[e("span",null,"remove-pwa")])],-1),v=e("p",null,"此插件从你的 VuePress 站点中删除任何相关的 Service Worker,因此如果你在启用后任何 PWA 插件后移除它们,用户仍然可以获得更新。",-1),h={class:"custom-container tip"},m=e("p",{class:"custom-container-title"},"如果你启用过 PWA,为什么需要这个插件?",-1),D=e("code",null,"@vuepress/plugin-pwa",-1),_=e("p",null,"但是,如果你删除 PWA 插件,先前的 Service Worker 仍将在那里,但它们永远无法获得更新,因为他们永远无法找到要更新的新 Service Worker。 因此,用户将继续使用你网站的旧版本。",-1),b=e("p",null,"要解决这个问题:",-1),g=e("ol",null,[e("li",null,[e("p",null,"一个新的内容为空的 Service Worker 需要生成在原位置。")]),e("li",null,[e("p",null,"新的 Service Worker 应该尝试删除旧 Service Worker 缓存的内容,然后它应该注销自己。")])],-1),k=p(`npm i -D @vuepress/plugin-remove-pwa@next
+
import { removePwaPlugin } from '@vuepress/plugin-remove-pwa'
+
+export default {
+ plugins: [
+ removePwaPlugin ({
+ // options
+ }),
+ ],
+}
+
类型:string
默认值:'workbox'
详情:Service worker 的缓存前缀。 类型: string
默认值:'service-worker.js'
详情:旧 Service Worker 的位置。 `,8);function w(x,f){const l=s("NpmBadge"),i=s("RouteLink");return r(),c("div",null,[u,a(l,{package:"@vuepress/plugin-remove-pwa"}),v,e("div",h,[m,e("p",null,[n("PWA 插件,如 "),a(i,{to:"/zh/plugins/pwa/"},{default:t(()=>[D]),_:1}),n(" 将 Service Worker 注册到你的站点,这将缓存你的站点并使其离线可用。")]),_,b,g]),k])}const C=o(d,[["render",w],["__file","remove-pwa.html.vue"]]),P=JSON.parse('{"path":"/zh/plugins/remove-pwa.html","title":"remove-pwa","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"cachePrefix","slug":"cacheprefix","link":"#cacheprefix","children":[]},{"level":3,"title":"swLocation","slug":"swlocation","link":"#swlocation","children":[]}]}],"git":{"updatedTime":1708397875000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/plugins/remove-pwa.md"}');export{C as comp,P as data};
diff --git a/assets/rtl.html-D6vZNIXg.js b/assets/rtl.html-D6vZNIXg.js
new file mode 100644
index 0000000000..1eb8057d18
--- /dev/null
+++ b/assets/rtl.html-D6vZNIXg.js
@@ -0,0 +1,17 @@
+import{_ as e,r as a,o as n,c as o,a as t,b as s,e as i}from"./app-BcH8wZQx.js";const p={},c=s("h1",{id:"rtl",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#rtl"},[s("span",null,"rtl")])],-1),r=i(`This plugin will set direction to rtl on configured locales.
npm i -D @vuepress/plugin-rtl@next
+
import { rtlPlugin } from '@vuepress/plugin-rtl'
+
+export default {
+ plugins: [
+ rtlPlugin ({
+ // options
+ locales: [ '/ar/' ],
+ }),
+ ],
+}
+
Type: string[]
Default: ['/']
Details: Locale path to enable rtl. `,9);function d(D,u){const l=a("NpmBadge");return n(),o("div",null,[c,t(l,{package:"@vuepress/plugin-rtl"}),r])}const v=e(p,[["render",d],["__file","rtl.html.vue"]]),m=JSON.parse('{"path":"/plugins/rtl.html","title":"rtl","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]},{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]}]}],"git":{"updatedTime":1706801200000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/rtl.md"}');export{v as comp,m as data};
diff --git a/assets/rtl.html-cZbNgJzy.js b/assets/rtl.html-cZbNgJzy.js
new file mode 100644
index 0000000000..c844342e0b
--- /dev/null
+++ b/assets/rtl.html-cZbNgJzy.js
@@ -0,0 +1,17 @@
+import{_ as e,r as a,o as n,c as p,a as o,b as s,e as i}from"./app-BcH8wZQx.js";const c={},r=s("h1",{id:"rtl",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#rtl"},[s("span",null,"rtl")])],-1),t=i(`此插件会在配置的语言上设置 rtl 方向。
npm i -D @vuepress/plugin-rtl@next
+
import { rtlPlugin } from '@vuepress/plugin-rtl'
+
+export default {
+ plugins: [
+ rtlPlugin ({
+ // 配置项
+ locales: [ '/ar/' ],
+ }),
+ ],
+}
+
类型:string[]
默认值:['/']
详情: 开启 RTL 布局的多语言路径。 `,9);function d(D,u){const l=a("NpmBadge");return n(),p("div",null,[r,o(l,{package:"@vuepress/plugin-rtl"}),t])}const h=e(c,[["render",d],["__file","rtl.html.vue"]]),m=JSON.parse('{"path":"/zh/plugins/rtl.html","title":"rtl","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]},{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]}]}],"git":{"updatedTime":1706801200000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/rtl.md"}');export{h as comp,m as data};
diff --git a/assets/search.html-DUJUfS5f.js b/assets/search.html-DUJUfS5f.js
new file mode 100644
index 0000000000..305eed82fb
--- /dev/null
+++ b/assets/search.html-DUJUfS5f.js
@@ -0,0 +1,75 @@
+import{_ as t,r as l,o as r,c as d,a as e,b as s,d as n,w as o,e as a}from"./app-BcH8wZQx.js";const D={},u=s("h1",{id:"search",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#search"},[s("span",null,"search")])],-1),h=a(`Provide local search to your documentation site.
TIP
Default theme will add search box to the navbar once you configure this plugin correctly.
This plugin may not be used directly in other themes, so you'd better refer to the documentation of your theme for more details.
npm i -D @vuepress/plugin-search@next
+
import { searchPlugin } from '@vuepress/plugin-search'
+
+export default {
+ plugins: [
+ searchPlugin ({
+ // options
+ }),
+ ],
+}
+
This plugin will generate search index from your pages locally, and load the search index file when users enter your site. In other words, this is a lightweight built-in search which does not require any external requests.
`,7),y=a(`Type: Record<string, { placeholder: string }>
Details:
The text of the search box in different locales.
If this option is not specified, it will fallback to default text.
Example:
export default {
+ plugins: [
+ searchPlugin ({
+ locales: {
+ '/' : {
+ placeholder: 'Search' ,
+ },
+ '/zh/' : {
+ placeholder: '搜索' ,
+ },
+ },
+ }),
+ ],
+}
+
`,4),v=a(`Type: (string | HotKeyOptions)[]
export interface HotKeyOptions {
+ /**
+ * Value of \`event.key\` to trigger the hot key
+ */
+ key : string ;
+ /**
+ * Whether to press \`event.altKey\` at the same time
+ *
+ * @default false
+ */
+ alt ?: boolean ;
+ /**
+ * Whether to press \`event.ctrlKey\` at the same time
+ *
+ * @default false
+ */
+ ctrl ?: boolean ;
+ /**
+ * Whether to press \`event.shiftKey\` at the same time
+ *
+ * @default false
+ */
+ shift ?: boolean ;
+}
+
`,3),m=s("li",null,[s("p",null,[n("Default: "),s("code",null,"['s', '/']")])],-1),b=s("p",null,"Details:",-1),C={href:"http://keycode.info/",target:"_blank",rel:"noopener noreferrer"},g=s("p",null,"When hotkeys are pressed, the search box input will be focused.",-1),f=s("p",null,"Set to an empty array to disable hotkeys.",-1),x=a(`export default {
+ plugins: [
+ searchPlugin ({
+ // exclude the homepage
+ isSearchable : ( page ) => page . path !== '/' ,
+ }),
+ ],
+}
+
Type: (page: Page) => string[]
Default: () => []
Details:
A function to add extra fields to the search index of a page.
By default, this plugin will use page title and headers as the search index. This option could help you to add more searchable fields.
Example:
export default {
+ plugins: [
+ searchPlugin ({
+ // allow searching the \`tags\` frontmatter
+ getExtraFields : ( page ) => page . frontmatter . tags ?? [],
+ }),
+ ],
+}
+
You can customize the style of the search box via CSS variables:
:root {
+ --search-bg-color : #ffffff ;
+ --search-accent-color : #3eaf7c ;
+ --search-text-color : #2c3e50 ;
+ --search-border-color : #eaecef ;
+ --search-item-text-color : #5d81a5 ;
+ --search-item-focus-bg-color : #f3f4f5 ;
+ --search-input-width : 8rem ;
+ --search-result-width : 20rem ;
+}
+
Details:
This plugin will register a <SearchBox />
component globally, and you can use it without any props.
Put this component to where you want to place the search box. For example, default theme puts this component to the end of the navbar.
TIP
This component is mainly used for theme development. You don't need to use it directly in most cases.
`,15);function E(k,A){const i=l("NpmBadge"),p=l("RouteLink"),c=l("ExternalLinkIcon");return r(),d("div",null,[u,e(i,{package:"@vuepress/plugin-search"}),h,s("p",null,[n("However, when your site has a large number of pages, the size of search index file would be very large, which could slow down the page loading speed. In this case, we recommend you to use a more professional solution - "),e(p,{to:"/plugins/docsearch.html"},{default:o(()=>[n("docsearch")]),_:1}),n(".")]),y,s("ul",null,[s("li",null,[n("Also see: "),s("ul",null,[s("li",null,[e(p,{to:"/guide/i18n.html"},{default:o(()=>[n("Guide > I18n")]),_:1})])])])]),v,s("ul",null,[m,s("li",null,[b,s("p",null,[n("Specify the "),s("a",C,[n("event.key"),e(c)]),n(" of the hotkeys.")]),g,f])]),x])}const F=t(D,[["render",E],["__file","search.html.vue"]]),w=JSON.parse('{"path":"/plugins/search.html","title":"search","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Local Search Index","slug":"local-search-index","link":"#local-search-index","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]},{"level":3,"title":"hotKeys","slug":"hotkeys","link":"#hotkeys","children":[]},{"level":3,"title":"maxSuggestions","slug":"maxsuggestions","link":"#maxsuggestions","children":[]},{"level":3,"title":"isSearchable","slug":"issearchable","link":"#issearchable","children":[]},{"level":3,"title":"getExtraFields","slug":"getextrafields","link":"#getextrafields","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]},{"level":2,"title":"Components","slug":"components","link":"#components","children":[{"level":3,"title":"SearchBox","slug":"searchbox","link":"#searchbox","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/search.md"}');export{F as comp,w as data};
diff --git a/assets/search.html-OjDPvSyi.js b/assets/search.html-OjDPvSyi.js
new file mode 100644
index 0000000000..cd6f30be66
--- /dev/null
+++ b/assets/search.html-OjDPvSyi.js
@@ -0,0 +1,75 @@
+import{_ as r,r as e,o as t,c as d,a,b as s,d as n,w as i,e as l}from"./app-BcH8wZQx.js";const D={},u=s("h1",{id:"search",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#search"},[s("span",null,"search")])],-1),v=l(`为你的文档网站提供本地搜索能力。
提示
当你正确配置该插件后,默认主题会把搜索框添加到导航栏。
该插件不一定能在其他主题中直接使用,因此你应参考主题本身的文档来获取更多信息。
npm i -D @vuepress/plugin-search@next
+
import { searchPlugin } from '@vuepress/plugin-search'
+
+export default {
+ plugins: [
+ searchPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
该插件会根据你的页面,在本地生成搜索索引,然后在用户访问站点时加载搜索索引文件。换句话说,这是一个轻量级的内置搜索能力,不会进行任何外部请求。
`,7),y=l(`export default {
+ plugins: [
+ searchPlugin ({
+ locales: {
+ '/' : {
+ placeholder: 'Search' ,
+ },
+ '/zh/' : {
+ placeholder: '搜索' ,
+ },
+ },
+ }),
+ ],
+}
+
`,4),h=l(`类型: (string | HotKeyOptions)[]
export interface HotKeyOptions {
+ /**
+ * Value of \`event.key\` to trigger the hot key
+ */
+ key : string ;
+ /**
+ * Whether to press \`event.altKey\` at the same time
+ *
+ * @default false
+ */
+ alt ?: boolean ;
+ /**
+ * Whether to press \`event.ctrlKey\` at the same time
+ *
+ * @default false
+ */
+ ctrl ?: boolean ;
+ /**
+ * Whether to press \`event.shiftKey\` at the same time
+ *
+ * @default false
+ */
+ shift ?: boolean ;
+}
+
`,3),m=s("li",null,[s("p",null,[n("默认值: "),s("code",null,"['s', '/']")])],-1),b=s("p",null,"详情:",-1),C={href:"http://keycode.info/",target:"_blank",rel:"noopener noreferrer"},g=s("p",null,"当按下热键时,搜索框会被聚焦。",-1),E=s("p",null,"将该配置项设为空数组可以禁用热键功能。",-1),x=l(`类型: number
默认值: 5
详情:
指定搜索结果的最大条数。
export default {
+ plugins: [
+ searchPlugin ({
+ // 排除首页
+ isSearchable : ( page ) => page . path !== '/' ,
+ }),
+ ],
+}
+
export default {
+ plugins: [
+ searchPlugin ({
+ // 允许搜索 Frontmatter 中的 \`tags\`
+ getExtraFields : ( page ) => page . frontmatter . tags ?? [],
+ }),
+ ],
+}
+
你可以通过 CSS 变量来自定义搜索框的样式:
:root {
+ --search-bg-color : #ffffff ;
+ --search-accent-color : #3eaf7c ;
+ --search-text-color : #2c3e50 ;
+ --search-border-color : #eaecef ;
+ --search-item-text-color : #5d81a5 ;
+ --search-item-focus-bg-color : #f3f4f5 ;
+ --search-input-width : 8rem ;
+ --search-result-width : 20rem ;
+}
+
提示
该组件主要用于主题开发。在大多数情况下你不需要直接使用该组件。
`,15);function f(k,_){const o=e("NpmBadge"),p=e("RouteLink"),c=e("ExternalLinkIcon");return t(),d("div",null,[u,a(o,{package:"@vuepress/plugin-search"}),v,s("p",null,[n("然而,当你的站点包含大量页面时,搜索索引文件也会变得非常大,它可能会拖慢你的页面加载速度。在这种情况下,我们建议你使用更成熟的解决方案 - "),a(p,{to:"/zh/plugins/docsearch.html"},{default:i(()=>[n("docsearch")]),_:1}),n(" 。")]),y,s("ul",null,[s("li",null,[n("参考: "),s("ul",null,[s("li",null,[a(p,{to:"/guide/i18n.html"},{default:i(()=>[n("指南 > 多语言支持")]),_:1})])])])]),h,s("ul",null,[m,s("li",null,[b,s("p",null,[n("指定热键的 "),s("a",C,[n("event.key"),a(c)]),n(" 。")]),g,E])]),x])}const F=r(D,[["render",f],["__file","search.html.vue"]]),B=JSON.parse('{"path":"/zh/plugins/search.html","title":"search","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"本地搜索索引","slug":"本地搜索索引","link":"#本地搜索索引","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]},{"level":3,"title":"hotKeys","slug":"hotkeys","link":"#hotkeys","children":[]},{"level":3,"title":"maxSuggestions","slug":"maxsuggestions","link":"#maxsuggestions","children":[]},{"level":3,"title":"isSearchable","slug":"issearchable","link":"#issearchable","children":[]},{"level":3,"title":"getExtraFields","slug":"getextrafields","link":"#getextrafields","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]},{"level":2,"title":"组件","slug":"组件","link":"#组件","children":[{"level":3,"title":"SearchBox","slug":"searchbox","link":"#searchbox","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/search.md"}');export{F as comp,B as data};
diff --git a/assets/shared.html-CEoo3Y0d.js b/assets/shared.html-CEoo3Y0d.js
new file mode 100644
index 0000000000..7ea9e91944
--- /dev/null
+++ b/assets/shared.html-CEoo3Y0d.js
@@ -0,0 +1,110 @@
+import{_ as s,o as n,c as a,e}from"./app-BcH8wZQx.js";const l={},o=e(`The following functions are available on both Node.js and Client.
Encode/decode and zip/unzip data.
This is useful in markdown plugins when you want to encode string content and pass it to the component through props.
You may simply achieve this with encodeURIComponent
and decodeURIComponent
, but it can be very large if the content contains lots of special characters.
So we provide encodeData
and decodeData
to zip and encode content.
export const encodeData : (
+ data : string ,
+ level : DeflateOptions [ 'level' ] = 6 ,
+) => string
+
+export const decodeData : ( compressed : string ) => string
+
const content = \`
+{
+ "type": "bar",
+ "data": {
+ "labels": ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
+ "datasets": [
+ {
+ "label": "# of Votes",
+ "data": [12, 19, 3, 5, 2, 3],
+ "backgroundColor": [
+ "rgba(255, 99, 132, 0.2)",
+ "rgba(54, 162, 235, 0.2)",
+ "rgba(255, 206, 86, 0.2)",
+ "rgba(75, 192, 192, 0.2)",
+ "rgba(153, 102, 255, 0.2)",
+ "rgba(255, 159, 64, 0.2)"
+ ],
+ "borderColor": [
+ "rgba(255, 99, 132, 1)",
+ "rgba(54, 162, 235, 1)",
+ "rgba(255, 206, 86, 1)",
+ "rgba(75, 192, 192, 1)",
+ "rgba(153, 102, 255, 1)",
+ "rgba(255, 159, 64, 1)"
+ ],
+ "borderWidth": 1
+ }
+ ]
+ },
+ "options": {
+ "scales": {
+ "y": {
+ "beginAtZero": true
+ }
+ }
+ }
+}
+\`
+
+const prop = encodeData ( content ) // "eJyNUsFOwzAMve8rrHABKZqWlg5WxAE4cARxAMHEIV1NmQhNlaaCCe3fcdKtW0sLWGpjxy/v+UV512mlcIyfhTa2hHP4GgHYVYExsEQaxqlMpZWxbwAomaAqY5izO0wZB3apKnTrIyqlP1x2bRBzl9xWplC+eWNkniF7dmw1X4nWsfgaNtwNP2kfgH6Be22x9CPUUQ8yFwEHMeMQcog4UBFuiF0kcvGWGV3l6ZVW2uw0XDCTJfIwiOjYjAhESIcn4+BoT2MLio6pP6V+EBJ6AOSZgsmUwyl9A6ATwoiZn3lYTkTkRkycnuP8TU9ENPqUxuuA9i9BmxTNPy9A/G2/F9I23wtpW++FdIwPKzW2W5Afph+WqX2NQWz313XicT7XhV3qnB5f/ejKhVTYVACrXUqUmC3zC/uERsdgTYUdVr/Qb302+gZxe7S/"
+
+decodeData ( prop ) // will be the original content
+
+// if you use \`encodeURIComponent\`, it will be much longer
+encodeURIComponent ( content ) // '%0A%7B%0A%20%20%22type%22%3A%20%22bar%22%2C%0A%20%20%22data%22%3A%20%7B%0A%20%20%20%20%22labels%22%3A%20%5B%22Red%22%2C%20%22Blue%22%2C%20%22Yellow%22%2C%20%22Green%22%2C%20%22Purple%22%2C%20%22Orange%22%5D%2C%0A%20%20%20%20%22datasets%22%3A%20%5B%0A%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22label%22%3A%20%22%23%20of%20Votes%22%2C%0A%20%20%20%20%20%20%20%20%22data%22%3A%20%5B12%2C%2019%2C%203%2C%205%2C%202%2C%203%5D%2C%0A%20%20%20%20%20%20%20%20%22backgroundColor%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%2099%2C%20132%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(54%2C%20162%2C%20235%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20206%2C%2086%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(75%2C%20192%2C%20192%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(153%2C%20102%2C%20255%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20159%2C%2064%2C%200.2)%22%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22borderColor%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%2099%2C%20132%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(54%2C%20162%2C%20235%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20206%2C%2086%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(75%2C%20192%2C%20192%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(153%2C%20102%2C%20255%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20159%2C%2064%2C%201)%22%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22borderWidth%22%3A%201%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%5D%0A%20%20%7D%2C%0A%20%20%22options%22%3A%20%7B%0A%20%20%20%20%22scales%22%3A%20%7B%0A%20%20%20%20%20%20%22y%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22beginAtZero%22%3A%20true%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A'
+
isDef(x)
: Check if x
is defined.isBoolean(x)
: Check if x
is a boolean.isString(x)
: Check if x
is a string.isNumber(x)
: Check if x
is a number.isPlainObject(x)
: Check if x
is a plain object.isArray(x)
: Check if x
is an array.isFunction(x)
: Check if x
is a function.isRegExp(x)
: Check if x
is a regular expression.startsWith(a, b)
: Check if string a
starts with string b
.endsWith(a, b)
: Check if string a
ends with string b
.Return false
if a
is not a string.
keys(x)
: Return an array of keys of object x
.
values(x)
: Return an array of values of object x
.
entries(x)
: Convert object x
to an array of key-value pairs.
fromEntries(x)
: Convert an array of key-value pairs x
to an object.
deepAssign(x, y, ...)
: A deep version of Object.assign
.
Example // or @vuepress/helper/client
+import { deepAssign } from '@vuepress/helper'
+
+const defaultOptions = {
+ optionA: {
+ optionA1: 'defaultOptionA1' ,
+ optionA2: 'defaultOptionA2' ,
+ optionA3: 'defaultOptionA3' ,
+ },
+ optionB: true ,
+ optionC: 'optionC' ,
+}
+
+const userOptions = {
+ optionA: {
+ optionA1: 'optionA1' ,
+ optionA2: 'optionA2' ,
+ },
+ optionB: false ,
+}
+
+deepAssign ( defaultOptions , userOptions )
+// {
+// optionA: {
+// optionA1: "optionA1",
+// optionA2: "optionA2",
+// optionA3: "defaultOptionA3",
+// },
+// optionB: false,
+// optionC: "optionC",
+// }
+
getDate(x)
: Convert input x
to a date. It can support Date
, timestamp, and date string. The support degree of date string depends on the Date.parse
support degree of the environment. Return null
when it cannot be converted to a date.
Example getDate ( '2021-01-01' ) // a Date object represents 2021-01-01
+getDate ( 1609459200000 ) // a Date object represents 2021-01-01
+getDate ( '2021-01-01T00:00:00.000Z' ) // a Date object represents 2021-01-01
+getDate ( '2021/01/01' ) // a Date object represents 2021-01-01 (might be null in some browsers)
+getDate ( 'invalid date' ) // null
+getDate ( undefined ) // null
+getDate (- 32 ) // null
+
dateSorter
: Sort the values that can be converted to dates from new to old, and the values that cannot be converted to dates will be at the end.
Example const arr = [
+ '2020-01-01' ,
+ 1609459200000 ,
+ '2022-01-01T00:00:00.000Z' ,
+ '2023/01/01' ,
+ 'invalid date' ,
+ undefined ,
+ - 32 ,
+]
+
+arr . sort ( dateSorter )
+// [
+// '2023/01/01',
+// '2022-01-01T00:00:00.000Z',
+// 1609459200000,
+// '2020-01-01',
+// 'invalid date',
+// undefined,
+// -32,
+// ]
+
isLinkHttp(x)
: Check if x
is a valid HTTP URL.isLinkWithProtocol(x)
: Check if x
is a valid URL with protocol.isLinkExternal(x)
: Check if x
is a valid external URL.isLinkAbsolute(x)
: Check if x
is a valid absolute URL.ensureEndingSlash(x)
: Ensure x
ends with a slash.ensureLeadingSlash(x)
: Ensure x
starts with a slash.removeEndingSlash(x)
: Ensure x
does not end with a slash.removeLeadingSlash(x)
: Ensure x
does not start with a slash. `,20),p=[o];function i(c,t){return n(),a("div",null,p)}const d=s(l,[["render",i],["__file","shared.html.vue"]]),u=JSON.parse('{"path":"/tools/helper/shared.html","title":"Shared Methods","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Data Related","slug":"data-related","link":"#data-related","children":[]},{"level":2,"title":"Type Helper","slug":"type-helper","link":"#type-helper","children":[]},{"level":2,"title":"String Related","slug":"string-related","link":"#string-related","children":[]},{"level":2,"title":"对象相关","slug":"对象相关","link":"#对象相关","children":[]},{"level":2,"title":"Date Related","slug":"date-related","link":"#date-related","children":[]},{"level":2,"title":"Link Related","slug":"link-related","link":"#link-related","children":[]}],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"tools/helper/shared.md"}');export{d as comp,u as data};
diff --git a/assets/shared.html-nZLDKTaP.js b/assets/shared.html-nZLDKTaP.js
new file mode 100644
index 0000000000..cc6b8083a6
--- /dev/null
+++ b/assets/shared.html-nZLDKTaP.js
@@ -0,0 +1,110 @@
+import{_ as s,o as n,c as a,e as l}from"./app-BcH8wZQx.js";const e={},o=l(`以下函数在 Node.js 和客户端上均可用。
此方法在 MarkdownIt 插件中很有用。有些时候你可能需要在 Markdown 插件中生成组件,并将复杂的数据写入到组件属性中,一个通常做法是使用 JSON.stringify
+ encodeURIComponent
,并在客户端 decodeURIComponent
+ JSON.parse
。但如果内容包含很多特殊字符,转换结果会很长。
所以我们提供 encodeData
和 decodeData
来压缩和编码内容。
export const encodeData : (
+ data : string ,
+ level : DeflateOptions [ 'level' ] = 6 ,
+) => string
+
+export const decodeData : ( compressed : string ) => string
+
const content = \`
+{
+ "type": "bar",
+ "data": {
+ "labels": ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
+ "datasets": [
+ {
+ "label": "# of Votes",
+ "data": [12, 19, 3, 5, 2, 3],
+ "backgroundColor": [
+ "rgba(255, 99, 132, 0.2)",
+ "rgba(54, 162, 235, 0.2)",
+ "rgba(255, 206, 86, 0.2)",
+ "rgba(75, 192, 192, 0.2)",
+ "rgba(153, 102, 255, 0.2)",
+ "rgba(255, 159, 64, 0.2)"
+ ],
+ "borderColor": [
+ "rgba(255, 99, 132, 1)",
+ "rgba(54, 162, 235, 1)",
+ "rgba(255, 206, 86, 1)",
+ "rgba(75, 192, 192, 1)",
+ "rgba(153, 102, 255, 1)",
+ "rgba(255, 159, 64, 1)"
+ ],
+ "borderWidth": 1
+ }
+ ]
+ },
+ "options": {
+ "scales": {
+ "y": {
+ "beginAtZero": true
+ }
+ }
+ }
+}
+\`
+
+const prop = encodeData ( content ) // "eJyNUsFOwzAMve8rrHABKZqWlg5WxAE4cARxAMHEIV1NmQhNlaaCCe3fcdKtW0sLWGpjxy/v+UV512mlcIyfhTa2hHP4GgHYVYExsEQaxqlMpZWxbwAomaAqY5izO0wZB3apKnTrIyqlP1x2bRBzl9xWplC+eWNkniF7dmw1X4nWsfgaNtwNP2kfgH6Be22x9CPUUQ8yFwEHMeMQcog4UBFuiF0kcvGWGV3l6ZVW2uw0XDCTJfIwiOjYjAhESIcn4+BoT2MLio6pP6V+EBJ6AOSZgsmUwyl9A6ATwoiZn3lYTkTkRkycnuP8TU9ENPqUxuuA9i9BmxTNPy9A/G2/F9I23wtpW++FdIwPKzW2W5Afph+WqX2NQWz313XicT7XhV3qnB5f/ejKhVTYVACrXUqUmC3zC/uERsdgTYUdVr/Qb302+gZxe7S/"
+
+decodeData ( prop ) // will be the original content
+
+// if you use \`encodeURIComponent\`, it will be much longer
+encodeURIComponent ( content ) // '%0A%7B%0A%20%20%22type%22%3A%20%22bar%22%2C%0A%20%20%22data%22%3A%20%7B%0A%20%20%20%20%22labels%22%3A%20%5B%22Red%22%2C%20%22Blue%22%2C%20%22Yellow%22%2C%20%22Green%22%2C%20%22Purple%22%2C%20%22Orange%22%5D%2C%0A%20%20%20%20%22datasets%22%3A%20%5B%0A%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22label%22%3A%20%22%23%20of%20Votes%22%2C%0A%20%20%20%20%20%20%20%20%22data%22%3A%20%5B12%2C%2019%2C%203%2C%205%2C%202%2C%203%5D%2C%0A%20%20%20%20%20%20%20%20%22backgroundColor%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%2099%2C%20132%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(54%2C%20162%2C%20235%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20206%2C%2086%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(75%2C%20192%2C%20192%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(153%2C%20102%2C%20255%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20159%2C%2064%2C%200.2)%22%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22borderColor%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%2099%2C%20132%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(54%2C%20162%2C%20235%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20206%2C%2086%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(75%2C%20192%2C%20192%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(153%2C%20102%2C%20255%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20159%2C%2064%2C%201)%22%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22borderWidth%22%3A%201%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%5D%0A%20%20%7D%2C%0A%20%20%22options%22%3A%20%7B%0A%20%20%20%20%22scales%22%3A%20%7B%0A%20%20%20%20%20%20%22y%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22beginAtZero%22%3A%20true%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A'
+
isDef(x)
: 判断 x 是否定义。isBoolean(x)
: 判断 x 是否为布尔值。isString(x)
: 判断 x 是否为字符串。isNumber(x)
: 判断 x 是否为数字。isPlainObject(x)
: 判断值是否为纯对象。isArray(x)
: 判断 x 是否为数组isFunction(x)
: 判断 x 是否为函数。isRegExp(x)
: 判断 x 是否为正则表达式startsWith(a, b)
: 判断字符串 a 是否以指定字符串 b 开头endsWith(a, b)
: 判断字符串 a 是否以指定字符串 b 结尾当 a 不是字符串时返回 false
keys(x)
: 以数组形式返回对象 x 的键
values(x)
: 以数组形式返回对象 x 的值
entries(x)
: 将对象 x 转换为键值对数组。
fromEntries(x)
: 将键值对数组 x 转换为对象。
deepAssign(x, y, ...)
: Object.assign
的深度版本。
示例 // or @vuepress/helper/client
+import { deepAssign } from '@vuepress/helper'
+
+const defaultOptions = {
+ optionA: {
+ optionA1: 'defaultOptionA1' ,
+ optionA2: 'defaultOptionA2' ,
+ optionA3: 'defaultOptionA3' ,
+ },
+ optionB: true ,
+ optionC: 'optionC' ,
+}
+
+const userOptions = {
+ optionA: {
+ optionA1: 'optionA1' ,
+ optionA2: 'optionA2' ,
+ },
+ optionB: false ,
+}
+
+deepAssign ( defaultOptions , userOptions )
+// {
+// optionA: {
+// optionA1: "optionA1",
+// optionA2: "optionA2",
+// optionA3: "defaultOptionA3",
+// },
+// optionB: false,
+// optionC: "optionC",
+// }
+
isLinkHttp(x)
: x 是否是有效的 HTTP URL。isLinkWithProtocol(x)
: x 是否是有效的带有协议的 URL。isLinkExternal(x)
: x 是否是有效的外部 URL。isLinkAbsolute(x)
: x 是否是有效的绝对 URL。ensureEndingSlash(x)
: 确保 x 以斜杠结尾。ensureLeadingSlash(x)
: 确保 x 以斜杠开头。removeEndingSlash(x)
: 确保 x 不以斜杠结尾。removeLeadingSlash(x)
: 确保 x 不以斜杠开头。 `,18),p=[o];function i(c,t){return n(),a("div",null,p)}const d=s(e,[["render",i],["__file","shared.html.vue"]]),D=JSON.parse('{"path":"/zh/tools/helper/shared.html","title":"共享方法","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"数据相关","slug":"数据相关","link":"#数据相关","children":[]},{"level":2,"title":"类型助手","slug":"类型助手","link":"#类型助手","children":[]},{"level":2,"title":"字符串相关","slug":"字符串相关","link":"#字符串相关","children":[]},{"level":2,"title":"对象相关","slug":"对象相关","link":"#对象相关","children":[]},{"level":2,"title":"日期相关","slug":"日期相关","link":"#日期相关","children":[]},{"level":2,"title":"链接相关","slug":"链接相关","link":"#链接相关","children":[]}],"git":{"updatedTime":1706960075000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/tools/helper/shared.md"}');export{d as comp,D as data};
diff --git a/assets/shiki.html-CIFr6R9Y.js b/assets/shiki.html-CIFr6R9Y.js
new file mode 100644
index 0000000000..43fec935fa
--- /dev/null
+++ b/assets/shiki.html-CIFr6R9Y.js
@@ -0,0 +1,12 @@
+import{_ as t,r as a,o as r,c as p,a as l,b as s,d as e,e as i}from"./app-BcH8wZQx.js";const c={},h=s("h1",{id:"shiki",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#shiki"},[s("span",null,"shiki")])],-1),d={href:"https://shiki.matsu.io/",target:"_blank",rel:"noopener noreferrer"},u={class:"custom-container tip"},m=s("p",{class:"custom-container-title"},"提示",-1),_={href:"https://shiki.matsu.io/",target:"_blank",rel:"noopener noreferrer"},D={href:"https://prismjs.com/",target:"_blank",rel:"noopener noreferrer"},k=s("p",null,[e("你可以考虑在 "),s("code",null,"dev"),e(" 模式下禁用该插件来获取更好的开发体验。")],-1),g=i(`npm i -D @vuepress/plugin-shiki@next
+
import { shikiPlugin } from '@vuepress/plugin-shiki'
+
+export default {
+ plugins: [
+ shikiPlugin ({
+ // 配置项
+ langs: [ 'ts' , 'json' , 'vue' , 'md' , 'bash' , 'diff' ],
+ }),
+ ],
+}
+
`,5),v=s("li",null,[s("p",null,[e("类型: "),s("code",null,"ShikiLang[]")])],-1),y=s("li",null,[s("p",null,"详情:"),s("p",null,"Shiki 要解析的代码块的语言。"),s("p",null,[e("该配置项会被传递到 Shiki 的 "),s("code",null,"getHighlighter()"),e(" 方法中。")]),s("p",null,"你最好明确传入所有你使用的语言列表,否则 Shiki 会加载所有语言,并可能影响性能。")],-1),b=s("p",null,"参考:",-1),f={href:"https://shiki.style/languages",target:"_blank",rel:"noopener noreferrer"},C=s("h3",{id:"theme",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#theme"},[s("span",null,"theme")])],-1),E=i("类型: ShikiTheme
默认值: 'nord'
详情:
Shiki 的主题。
该配置项会被传递到 Shiki 的 codeToHtml()
方法中。
",3),x=s("p",null,"参考:",-1),S={href:"https://shiki.style/themes",target:"_blank",rel:"noopener noreferrer"},N=s("h3",{id:"themes",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#themes"},[s("span",null,"themes")])],-1),T=s("li",null,[s("p",null,[e("类型:"),s("code",null,"Record<'dark' | 'light', ShikiTheme>")])],-1),A=s("li",null,[s("p",null,"详情:"),s("p",null,"Shiki 的暗黑和明亮模式双主题。"),s("p",null,[e("该配置项会被传递到 Shiki 的 "),s("code",null,"codeToHtml()"),e(" 方法中。")])],-1),B=s("p",null,"参考:",-1),V={href:"https://shiki.style/guide/dual-themes",target:"_blank",rel:"noopener noreferrer"};function H(L,P){const o=a("NpmBadge"),n=a("ExternalLinkIcon");return r(),p("div",null,[h,l(o,{package:"@vuepress/plugin-shiki"}),s("p",null,[e("该插件使用 "),s("a",d,[e("Shiki"),l(n)]),e(" 来为 Markdown 代码块启用代码高亮。")]),s("div",u,[m,s("p",null,[s("a",_,[e("Shiki"),l(n)]),e(" 是 VSCode 正在使用的代码高亮器。它具有更高的保真度,但可能会比 "),s("a",D,[e("Prism.js"),l(n)]),e(" 要慢一些,特别是在有大量代码块需要处理的时候。")]),k]),g,s("ul",null,[v,y,s("li",null,[b,s("ul",null,[s("li",null,[s("a",f,[e("shiki > Languages"),l(n)])])])])]),C,s("ul",null,[E,s("li",null,[x,s("ul",null,[s("li",null,[s("a",S,[e("Shiki > Themes"),l(n)])])])])]),N,s("ul",null,[T,A,s("li",null,[B,s("ul",null,[s("li",null,[s("a",V,[e("Shiki > Dual Themes"),l(n)])])])])])])}const z=t(c,[["render",H],["__file","shiki.html.vue"]]),F=JSON.parse('{"path":"/zh/plugins/shiki.html","title":"shiki","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"langs","slug":"langs","link":"#langs","children":[]},{"level":3,"title":"theme","slug":"theme","link":"#theme","children":[]},{"level":3,"title":"themes","slug":"themes","link":"#themes","children":[]}]}],"git":{"updatedTime":1707215812000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/plugins/shiki.md"}');export{z as comp,F as data};
diff --git a/assets/shiki.html-R2m4HGs-.js b/assets/shiki.html-R2m4HGs-.js
new file mode 100644
index 0000000000..b0612518b0
--- /dev/null
+++ b/assets/shiki.html-R2m4HGs-.js
@@ -0,0 +1,12 @@
+import{_ as t,r as a,o as p,c as r,a as l,b as e,d as s,e as i}from"./app-BcH8wZQx.js";const c={},h=e("h1",{id:"shiki",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#shiki"},[e("span",null,"shiki")])],-1),d={href:"https://shiki.style/",target:"_blank",rel:"noopener noreferrer"},u={class:"custom-container tip"},m=e("p",{class:"custom-container-title"},"TIP",-1),g={href:"https://shiki.matsu.io/",target:"_blank",rel:"noopener noreferrer"},D={href:"https://prismjs.com/",target:"_blank",rel:"noopener noreferrer"},_=e("p",null,[s("You could consider disabling this plugin in "),e("code",null,"dev"),s(" mode to get better development experience.")],-1),k=i(`npm i -D @vuepress/plugin-shiki@next
+
import { shikiPlugin } from '@vuepress/plugin-shiki'
+
+export default {
+ plugins: [
+ shikiPlugin ({
+ // options
+ langs: [ 'ts' , 'json' , 'vue' , 'md' , 'bash' , 'diff' ],
+ }),
+ ],
+}
+
`,5),y=e("li",null,[e("p",null,[s("Type: "),e("code",null,"ShikiLang[]")])],-1),b=e("li",null,[e("p",null,"Details:"),e("p",null,"Languages of code blocks to be parsed by Shiki."),e("p",null,[s("This option will be forwarded to "),e("code",null,"getHighlighter()"),s(" method of Shiki.")]),e("p",null,"You'd better provide the languages list you are using explicitly, otherwise Shiki will load all languages and can affect performance.")],-1),v=e("p",null,"Also see:",-1),f={href:"https://shiki.style/languages",target:"_blank",rel:"noopener noreferrer"},C=e("h3",{id:"theme",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#theme"},[e("span",null,"theme")])],-1),x=i("Type: ShikiTheme
Default: 'nord'
Details:
Theme of Shiki.
This option will be forwarded to codeToHtml()
method of Shiki.
",3),E=e("p",null,"Also see:",-1),S={href:"https://shiki.style/themes",target:"_blank",rel:"noopener noreferrer"},T=e("h3",{id:"themes",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#themes"},[e("span",null,"themes")])],-1),w=e("li",null,[e("p",null,[s("Type: "),e("code",null,"Record<'dark' | 'light', ShikiTheme>")])],-1),A=e("li",null,[e("p",null,"Details:"),e("p",null,"Dark / Light Dual themes of Shiki."),e("p",null,[s("This option will be forwarded to "),e("code",null,"codeToHtml()"),s(" method of Shiki.")])],-1),N=e("p",null,"Also see:",-1),L={href:"https://shiki.style/guide/dual-themes",target:"_blank",rel:"noopener noreferrer"};function B(P,V){const o=a("NpmBadge"),n=a("ExternalLinkIcon");return p(),r("div",null,[h,l(o,{package:"@vuepress/plugin-shiki"}),e("p",null,[s("This plugin will enable syntax highlighting for markdown code fence with "),e("a",d,[s("Shiki"),l(n)]),s(".")]),e("div",u,[m,e("p",null,[e("a",g,[s("Shiki"),l(n)]),s(" is the syntax highlighter being used by VSCode. It has higher fidelity, but it could be slower than "),e("a",D,[s("Prism.js"),l(n)]),s(", especially when you have a lot of code blocks.")]),_]),k,e("ul",null,[y,b,e("li",null,[v,e("ul",null,[e("li",null,[e("a",f,[s("Shiki > Languages"),l(n)])])])])]),C,e("ul",null,[x,e("li",null,[E,e("ul",null,[e("li",null,[e("a",S,[s("Shiki > Themes"),l(n)])])])])]),T,e("ul",null,[w,A,e("li",null,[N,e("ul",null,[e("li",null,[e("a",L,[s("Shiki > Dual Themes"),l(n)])])])])])])}const I=t(c,[["render",B],["__file","shiki.html.vue"]]),j=JSON.parse('{"path":"/plugins/shiki.html","title":"shiki","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"langs","slug":"langs","link":"#langs","children":[]},{"level":3,"title":"theme","slug":"theme","link":"#theme","children":[]},{"level":3,"title":"themes","slug":"themes","link":"#themes","children":[]}]}],"git":{"updatedTime":1707215812000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"plugins/shiki.md"}');export{I as comp,j as data};
diff --git a/assets/style-BPe9ZiPR.css b/assets/style-BPe9ZiPR.css
new file mode 100644
index 0000000000..fe1f594128
--- /dev/null
+++ b/assets/style-BPe9ZiPR.css
@@ -0,0 +1 @@
+@charset "UTF-8";.vp-back-to-top-button{position:fixed!important;bottom:4rem;inset-inline-end:1rem;z-index:100;width:3rem;height:3rem;padding:.5rem;border-width:0;border-radius:50%;background:var(--back-to-top-bg-color);color:var(--back-to-top-color);box-shadow:2px 2px 10px 4px var(--back-to-top-shadow);cursor:pointer}@media (max-width: 959px){.vp-back-to-top-button{width:2.5rem;height:2.5rem}}@media print{.vp-back-to-top-button{display:none}}.vp-back-to-top-button:hover{color:var(--back-to-top-color-hover)}.vp-back-to-top-button .back-to-top-icon{overflow:hidden;width:100%;height:100%;background:currentcolor;border-radius:50%;-webkit-mask-image:var(--back-to-top-icon);mask-image:var(--back-to-top-icon);-webkit-mask-position:50%;mask-position:50%;-webkit-mask-size:cover;mask-size:cover}.vp-scroll-progress{position:absolute;right:-2px;bottom:-2px;width:calc(100% + 4px);height:calc(100% + 4px)}.vp-scroll-progress svg{width:100%;height:100%}.vp-scroll-progress circle{opacity:.9;fill:none;stroke:currentColor;transform:rotate(-90deg);transform-origin:50% 50%;r:22;stroke-dasharray:0% 314.1593%;stroke-width:3px}@media (max-width: 959px){.vp-scroll-progress circle{r:18}}.back-to-top-enter-active,.back-to-top-leave-active{transition:opacity .3s}.back-to-top-enter-from,.back-to-top-leave-to{opacity:0}:root{--back-to-top-z-index: 5;--back-to-top-icon: url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201024%201024'%3e%3cpath%20d='M512%20843.2c-36.2%200-66.4-13.6-85.8-21.8-10.8-4.6-22.6%203.6-21.8%2015.2l7%20102c.4%206.2%207.6%209.4%2012.6%205.6l29-22c3.6-2.8%209-1.8%2011.4%202l41%2064.2c3%204.8%2010.2%204.8%2013.2%200l41-64.2c2.4-3.8%207.8-4.8%2011.4-2l29%2022c5%203.8%2012.2.6%2012.6-5.6l7-102c.8-11.6-11-20-21.8-15.2-19.6%208.2-49.6%2021.8-85.8%2021.8'/%3e%3cpath%20d='m795.4%20586.2-96-98.2C699.4%20172%20513%2032%20513%2032S324.8%20172%20324.8%20488l-96%2098.2c-3.6%203.6-5.2%209-4.4%2014.2L261.2%20824c1.8%2011.4%2014.2%2017%2023.6%2010.8L419%20744s41.4%2040%2094.2%2040%2092.2-40%2092.2-40l134.2%2090.8c9.2%206.2%2021.6.6%2023.6-10.8l37-223.8c.4-5.2-1.2-10.4-4.8-14M513%20384c-34%200-61.4-28.6-61.4-64s27.6-64%2061.4-64c34%200%2061.4%2028.6%2061.4%2064S547%20384%20513%20384'/%3e%3c/svg%3e");--back-to-top-bg-color: #fff;--back-to-top-color: #3eaf7c;--back-to-top-color-hover: #71cda3;--back-to-top-shadow: rgb(0 0 0 / 20%)}div[class*=language-]:hover:before{display:none}div[class*=language-]:hover .vp-copy-code-button{opacity:1}.vp-copy-code-button{position:absolute;top:.5em;right:.5em;z-index:5;width:2.5rem;height:2.5rem;padding:0;border-width:0;border-radius:.5rem;background:transparent;outline:none;opacity:0;cursor:pointer;transition:opacity .4s}@media print{.vp-copy-code-button{display:none}}.vp-copy-code-button:focus,.vp-copy-code-button.copied{opacity:1}.vp-copy-code-button:hover,.vp-copy-code-button.copied{background:var(--copy-code-hover)}.vp-copy-code-button.copied .vp-copy-icon{-webkit-mask-image:var(--code-copied-icon);mask-image:var(--code-copied-icon)}.vp-copy-code-button.copied:after{content:attr(data-copied);position:absolute;top:0;right:calc(100% + .25rem);display:block;height:1.25rem;padding:.625rem;border-radius:.5rem;background:var(--copy-code-hover);color:var(--copy-code-color);font-weight:500;line-height:1.25rem;white-space:nowrap}.vp-copy-icon{width:1.25rem;height:1.25rem;padding:.625rem;background:currentcolor;color:var(--copy-code-color);font-size:1.25rem;-webkit-mask-image:var(--code-copy-icon);mask-image:var(--code-copy-icon);-webkit-mask-position:50%;mask-position:50%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:1em;mask-size:1em}:root{--code-copy-icon: url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20height='20'%20width='20'%20stroke='rgba(128,128,128,1)'%20stroke-width='2'%3e%3cpath%20stroke-linecap='round'%20stroke-linejoin='round'%20d='M9%205H7a2%202%200%200%200-2%202v12a2%202%200%200%200%202%202h10a2%202%200%200%200%202-2V7a2%202%200%200%200-2-2h-2M9%205a2%202%200%200%200%202%202h2a2%202%200%200%200%202-2M9%205a2%202%200%200%201%202-2h2a2%202%200%200%201%202%202'%20/%3e%3c/svg%3e");--code-copied-icon: url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20height='20'%20width='20'%20stroke='rgba(128,128,128,1)'%20stroke-width='2'%3e%3cpath%20stroke-linecap='round'%20stroke-linejoin='round'%20d='M9%205H7a2%202%200%200%200-2%202v12a2%202%200%200%200%202%202h10a2%202%200%200%200%202-2V7a2%202%200%200%200-2-2h-2M9%205a2%202%200%200%200%202%202h2a2%202%200%200%200%202-2M9%205a2%202%200%200%201%202-2h2a2%202%200%200%201%202%202m-6%209%202%202%204-4'%20/%3e%3c/svg%3e");--copy-code-color: #9e9e9e;--copy-code-hover: rgb(0 0 0 / 50%)}:root{--external-link-icon-color: #aaa}.external-link-icon{position:relative;display:inline-block;color:var(--external-link-icon-color);vertical-align:middle;top:-1px}@media print{.external-link-icon{display:none}}.external-link-icon-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}:root{--medium-zoom-z-index: 100;--medium-zoom-bg-color: #ffffff;--medium-zoom-opacity: 1}.medium-zoom-overlay{background-color:var(--medium-zoom-bg-color)!important;z-index:var(--medium-zoom-z-index)}.medium-zoom-overlay~img{z-index:calc(var(--medium-zoom-z-index) + 1)}.medium-zoom--opened .medium-zoom-overlay{opacity:var(--medium-zoom-opacity)}:root{--nprogress-color: #29d;--nprogress-z-index: 1031}#nprogress{pointer-events:none}#nprogress .bar{background:var(--nprogress-color);position:fixed;z-index:var(--nprogress-z-index);top:0;left:0;width:100%;height:2px}:root{--c-brand: #3eaf7c;--c-brand-light: #4abf8a;--c-bg: #ffffff;--c-bg-light: #f3f4f5;--c-bg-lighter: #eeeeee;--c-bg-dark: #ebebec;--c-bg-darker: #e6e6e6;--c-bg-navbar: var(--c-bg);--c-bg-sidebar: var(--c-bg);--c-bg-arrow: #cccccc;--c-text: #2c3e50;--c-text-accent: var(--c-brand);--c-text-light: #3a5169;--c-text-lighter: #4e6e8e;--c-text-lightest: #6a8bad;--c-text-quote: #999999;--c-border: #eaecef;--c-border-dark: #dfe2e5;--c-tip: #42b983;--c-tip-bg: var(--c-bg-light);--c-tip-title: var(--c-text);--c-tip-text: var(--c-text);--c-tip-text-accent: var(--c-text-accent);--c-warning: #ffc310;--c-warning-bg: #fffae3;--c-warning-bg-light: #fff3ba;--c-warning-bg-lighter: #fff0b0;--c-warning-border-dark: #f7dc91;--c-warning-details-bg: #fff5ca;--c-warning-title: #f1b300;--c-warning-text: #746000;--c-warning-text-accent: #edb100;--c-warning-text-light: #c1971c;--c-warning-text-quote: #ccab49;--c-danger: #f11e37;--c-danger-bg: #ffe0e0;--c-danger-bg-light: #ffcfde;--c-danger-bg-lighter: #ffc9c9;--c-danger-border-dark: #f1abab;--c-danger-details-bg: #ffd4d4;--c-danger-title: #ed1e2c;--c-danger-text: #660000;--c-danger-text-accent: #bd1a1a;--c-danger-text-light: #b5474d;--c-danger-text-quote: #c15b5b;--c-details-bg: #eeeeee;--c-badge-tip: var(--c-tip);--c-badge-warning: #ecc808;--c-badge-warning-text: var(--c-bg);--c-badge-danger: #dc2626;--c-badge-danger-text: var(--c-bg);--c-code-group-tab-title: rgba(255, 255, 255, .9);--c-code-group-tab-bg: var(--code-bg-color);--c-code-group-tab-outline: var(var(--c-code-group-tab-title));--c-code-group-tab-active-border: var(--c-brand);--t-color: .3s ease;--t-transform: .3s ease;--code-bg-color: #282c34;--code-hl-bg-color: rgba(0, 0, 0, .66);--code-ln-color: #9e9e9e;--code-ln-wrapper-width: 3.5rem;--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;--font-family-code: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;--navbar-height: 3.6rem;--navbar-padding-v: .7rem;--navbar-padding-h: 1.5rem;--sidebar-width: 20rem;--sidebar-width-mobile: calc(var(--sidebar-width) * .82);--content-width: 740px;--homepage-width: 960px}.vp-back-to-top-button{--back-to-top-color: var(--c-brand);--back-to-top-color-hover: var(--c-brand-light);--back-to-top-bg-color: var(--c-bg)}.vp-catalog-wrapper{--catalog-bg-color: var(--c-bg);--catalog-bg-secondary-color: var(--c-bg-dark);--catalog-border-color: var(--c-border);--catalog-active-color: var(--c-brand);--catalog-hover-color: var(--c-brand-light)}.DocSearch{--docsearch-primary-color: var(--c-brand);--docsearch-text-color: var(--c-text);--docsearch-highlight-color: var(--c-brand);--docsearch-muted-color: var(--c-text-quote);--docsearch-container-background: rgba(9, 10, 17, .8);--docsearch-modal-background: var(--c-bg-light);--docsearch-searchbox-background: var(--c-bg-lighter);--docsearch-searchbox-focus-background: var(--c-bg);--docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);--docsearch-hit-color: var(--c-text-light);--docsearch-hit-active-color: var(--c-bg);--docsearch-hit-background: var(--c-bg);--docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);--docsearch-footer-background: var(--c-bg)}.external-link-icon{--external-link-icon-color: var(--c-text-quote)}.medium-zoom-overlay{--medium-zoom-bg-color: var(--c-bg)}#nprogress{--nprogress-color: var(--c-brand)}body{--photo-swipe-bullet: var(--c-bg);--photo-swipe-bullet-active: var(--c-brand)}html{--pwa-text-color: var(--c-text);--pwa-bg-color: var(--c-bg);--pwa-border-color: var(--c-brand);--pwa-btn-text-color: var(--c-bg);--pwa-btn-bg-color: var(--c-brand);--pwa-btn-hover-bg-color: var(--c-brand-light)}html.dark{--pwa-shadow-color: rgb(0 0 0 / 30%);--pwa-content-color: #ccc;--pwa-content-light-color: #999}.language-modal-mask{--redirect-bg-color: var(--c-bg);--redirect-bg-color-light: var(--c-bg-light);--redirect-bg-color-lighter: var(--c-bg-lighter);--redirect-text-color: var(--c-text);--redirect-primary-bg-color: var(--c-brand);--redirect-primary-hover-bg-color: var(--c-brand-light);--redirect-primary-text-color: var(--c-bg)}.search-box{--search-bg-color: var(--c-bg);--search-accent-color: var(--c-brand);--search-text-color: var(--c-text);--search-border-color: var(--c-border);--search-item-text-color: var(--c-text-lighter);--search-item-focus-bg-color: var(--c-bg-light)}html.dark{--c-brand: #3aa675;--c-brand-light: #349469;--c-bg: #22272e;--c-bg-light: #2b313a;--c-bg-lighter: #262c34;--c-bg-dark: #343b44;--c-bg-darker: #37404c;--c-text: #adbac7;--c-text-light: #96a7b7;--c-text-lighter: #8b9eb0;--c-text-lightest: #8094a8;--c-border: #3e4c5a;--c-border-dark: #34404c;--c-tip: #318a62;--c-warning: #e0ad15;--c-warning-bg: #2d2f2d;--c-warning-bg-light: #423e2a;--c-warning-bg-lighter: #44442f;--c-warning-border-dark: #957c35;--c-warning-details-bg: #39392d;--c-warning-title: #fdca31;--c-warning-text: #d8d96d;--c-warning-text-accent: #ffbf00;--c-warning-text-light: #ddb84b;--c-warning-text-quote: #ccab49;--c-danger: #fc1e38;--c-danger-bg: #39232c;--c-danger-bg-light: #4b2b35;--c-danger-bg-lighter: #553040;--c-danger-border-dark: #a25151;--c-danger-details-bg: #482936;--c-danger-title: #fc2d3b;--c-danger-text: #ea9ca0;--c-danger-text-accent: #fd3636;--c-danger-text-light: #d9777c;--c-danger-text-quote: #d56b6b;--c-details-bg: #323843;--c-badge-warning: var(--c-warning);--c-badge-warning-text: #3c2e05;--c-badge-danger: var(--c-danger);--c-badge-danger-text: #401416;--code-hl-bg-color: #363b46}html.dark .DocSearch{--docsearch-logo-color: var(--c-text);--docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;--docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d, 0 2px 2px 0 rgba(3, 4, 9, .3);--docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);--docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, .5), 0 -4px 8px 0 rgba(0, 0, 0, .2)}html,body{padding:0;margin:0;background-color:var(--c-bg);transition:background-color var(--t-color)}html.dark{color-scheme:dark}html{font-size:16px}body{font-family:var(--font-family);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:1rem;color:var(--c-text)}a{font-weight:500;color:var(--c-text-accent);text-decoration:none;overflow-wrap:break-word}p a code{font-weight:400;color:var(--c-text-accent)}kbd{font-family:var(--font-family-code);color:var(--c-text);background:var(--c-bg-lighter);border:solid .15rem var(--c-border-dark);border-bottom:solid .25rem var(--c-border-dark);border-radius:.15rem;padding:0 .15em}code{font-family:var(--font-family-code);color:var(--c-text-lighter);padding:.25rem .5rem;margin:0;font-size:.85em;background-color:var(--c-bg-light);border-radius:3px;overflow-wrap:break-word;transition:background-color var(--t-color)}blockquote{font-size:1rem;color:var(--c-text-quote);border-left:.2rem solid var(--c-border-dark);margin:1rem 0;padding:.25rem 0 .25rem 1rem;overflow-wrap:break-word}blockquote>p{margin:0}ul,ol{padding-left:1.2em}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25;overflow-wrap:break-word}h1:focus-visible,h2:focus-visible,h3:focus-visible,h4:focus-visible,h5:focus-visible,h6:focus-visible{outline:none}h1 .header-anchor,h2 .header-anchor,h3 .header-anchor,h4 .header-anchor,h5 .header-anchor,h6 .header-anchor{color:inherit;text-decoration:none;position:relative}h1 .header-anchor:hover:before,h2 .header-anchor:hover:before,h3 .header-anchor:hover:before,h4 .header-anchor:hover:before,h5 .header-anchor:hover:before,h6 .header-anchor:hover:before{font-size:.8em;content:"¶";position:absolute;left:-.75em;color:var(--c-brand)}h1 .header-anchor:focus-visible,h2 .header-anchor:focus-visible,h3 .header-anchor:focus-visible,h4 .header-anchor:focus-visible,h5 .header-anchor:focus-visible,h6 .header-anchor:focus-visible{outline:none}h1 .header-anchor:focus-visible:before,h2 .header-anchor:focus-visible:before,h3 .header-anchor:focus-visible:before,h4 .header-anchor:focus-visible:before,h5 .header-anchor:focus-visible:before,h6 .header-anchor:focus-visible:before{content:"¶";position:absolute;left:-.75em;color:var(--c-brand);outline:auto}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem;border-bottom:1px solid var(--c-border);transition:border-color var(--t-color)}h3{font-size:1.35rem}h4{font-size:1.15rem}h5{font-size:1.05rem}h6{font-size:1rem}@media print{a[href^="http://"]:after,a[href^="https://"]:after{content:" (" attr(href) ") "}}p,ul,ol{line-height:1.7;overflow-wrap:break-word}hr{border:0;border-top:1px solid var(--c-border)}table{border-collapse:collapse;margin:1rem 0;display:block;overflow-x:auto;transition:border-color var(--t-color)}tr{border-top:1px solid var(--c-border-dark);transition:border-color var(--t-color)}tr:nth-child(2n){background-color:var(--c-bg-light);transition:background-color var(--t-color)}tr:nth-child(2n) code{background-color:var(--c-bg-dark)}th,td{padding:.6em 1em;border:1px solid var(--c-border-dark);transition:border-color var(--t-color)}.arrow{display:inline-block;width:0;height:0}.arrow.up{border-left:4px solid transparent;border-right:4px solid transparent;border-bottom:6px solid var(--c-bg-arrow)}.arrow.down{border-left:4px solid transparent;border-right:4px solid transparent;border-top:6px solid var(--c-bg-arrow)}.arrow.right{border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:6px solid var(--c-bg-arrow)}.arrow.left{border-top:4px solid transparent;border-bottom:4px solid transparent;border-right:6px solid var(--c-bg-arrow)}.badge{display:inline-block;font-size:14px;font-weight:600;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:var(--c-bg);vertical-align:top;transition:color var(--t-color),background-color var(--t-color)}.badge.tip{background-color:var(--c-badge-tip)}.badge.warning{background-color:var(--c-badge-warning);color:var(--c-badge-warning-text)}.badge.danger{background-color:var(--c-badge-danger);color:var(--c-badge-danger-text)}.badge+.badge{margin-left:5px}code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:var(--font-family-code);font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.comment,.token.block-comment,.token.prolog,.token.doctype,.token.cdata{color:#999}.token.punctuation{color:#ccc}.token.tag,.token.attr-name,.token.namespace,.token.deleted{color:#ec5975}.token.function-name{color:#6196cc}.token.boolean,.token.number,.token.function{color:#f08d49}.token.property,.token.class-name,.token.constant,.token.symbol{color:#f8c555}.token.selector,.token.important,.token.atrule,.token.keyword,.token.builtin{color:#cc99cd}.token.string,.token.char,.token.attr-value,.token.regex,.token.variable{color:#7ec699}.token.operator,.token.entity,.token.url{color:#67cdcc}.token.important,.token.bold{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:#3eaf7c}.theme-default-content pre,.theme-default-content pre[class*=language-]{line-height:1.375;padding:1.3rem 1.5rem;margin:.85rem 0;border-radius:6px;overflow:auto}.theme-default-content pre code,.theme-default-content pre[class*=language-] code{color:#fff;padding:0;background-color:transparent!important;border-radius:0;overflow-wrap:unset;-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}.theme-default-content .line-number{font-family:var(--font-family-code)}div[class*=language-]{position:relative;background-color:var(--code-bg-color);border-radius:6px}div[class*=language-]:before{content:attr(data-title);position:absolute;z-index:3;top:.8em;right:1em;font-size:.75rem;color:var(--code-ln-color)}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{background:transparent!important;position:relative;z-index:1}div[class*=language-] .highlight-lines{-webkit-user-select:none;-moz-user-select:none;user-select:none;padding-top:1.3rem;position:absolute;top:0;left:0;width:100%;line-height:1.375}div[class*=language-] .highlight-lines .highlight-line{background-color:var(--code-hl-bg-color)}div[class*=language-]:not(.line-numbers-mode) .line-numbers{display:none}div[class*=language-].line-numbers-mode .highlight-lines .highlight-line{position:relative}div[class*=language-].line-numbers-mode .highlight-lines .highlight-line:before{content:" ";position:absolute;z-index:2;left:0;top:0;display:block;width:var(--code-ln-wrapper-width);height:100%}div[class*=language-].line-numbers-mode pre{margin-left:var(--code-ln-wrapper-width);padding-left:1rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers{position:absolute;top:0;width:var(--code-ln-wrapper-width);text-align:center;color:var(--code-ln-color);padding-top:1.25rem;line-height:1.375;counter-reset:line-number}div[class*=language-].line-numbers-mode .line-numbers .line-number{position:relative;z-index:3;-webkit-user-select:none;-moz-user-select:none;user-select:none;height:1.375em}div[class*=language-].line-numbers-mode .line-numbers .line-number:before{counter-increment:line-number;content:counter(line-number);font-size:.85em}div[class*=language-].line-numbers-mode:after{content:"";position:absolute;top:0;left:0;width:var(--code-ln-wrapper-width);height:100%;border-radius:6px 0 0 6px;border-right:1px solid var(--code-hl-bg-color)}@media (max-width: 419px){.theme-default-content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}.code-group__nav{margin-top:.85rem;margin-bottom:calc(-1.7rem - 6px);padding-bottom:calc(1.7rem - 6px);padding-left:10px;padding-top:10px;border-top-left-radius:6px;border-top-right-radius:6px;background-color:var(--c-code-group-tab-bg)}.code-group__nav-tab{border:0;padding:5px;cursor:pointer;background-color:transparent;font-size:.85em;line-height:1.4;color:var(--c-code-group-tab-title);font-weight:600}.code-group__nav-tab:focus{outline:none}.code-group__nav-tab:focus-visible{outline:1px solid var(--c-code-group-tab-outline)}.code-group__nav-tab-active{border-bottom:var(--c-code-group-tab-active-border) 1px solid}@media (max-width: 419px){.code-group__nav{margin-left:-1.5rem;margin-right:-1.5rem;border-radius:0}}.code-group-item{display:none}.code-group-item__active{display:block}.code-group-item>pre{background-color:orange}.custom-container{transition:color var(--t-color),border-color var(--t-color),background-color var(--t-color)}.custom-container .custom-container-title{font-weight:600}.custom-container .custom-container-title:not(:only-child){margin-bottom:-.4rem}.custom-container.tip,.custom-container.warning,.custom-container.danger{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-container.tip{border-color:var(--c-tip);background-color:var(--c-tip-bg);color:var(--c-tip-text)}.custom-container.tip .custom-container-title{color:var(--c-tip-title)}.custom-container.tip a{color:var(--c-tip-text-accent)}.custom-container.tip code{background-color:var(--c-bg-dark)}.custom-container.warning{border-color:var(--c-warning);background-color:var(--c-warning-bg);color:var(--c-warning-text)}.custom-container.warning .custom-container-title{color:var(--c-warning-title)}.custom-container.warning a{color:var(--c-warning-text-accent)}.custom-container.warning blockquote{border-left-color:var(--c-warning-border-dark);color:var(--c-warning-text-quote)}.custom-container.warning code{color:var(--c-warning-text-light);background-color:var(--c-warning-bg-light)}.custom-container.warning details{background-color:var(--c-warning-details-bg)}.custom-container.warning details code{background-color:var(--c-warning-bg-lighter)}.custom-container.warning .external-link-icon{--external-link-icon-color: var(--c-warning-text-quote)}.custom-container.danger{border-color:var(--c-danger);background-color:var(--c-danger-bg);color:var(--c-danger-text)}.custom-container.danger .custom-container-title{color:var(--c-danger-title)}.custom-container.danger a{color:var(--c-danger-text-accent)}.custom-container.danger blockquote{border-left-color:var(--c-danger-border-dark);color:var(--c-danger-text-quote)}.custom-container.danger code{color:var(--c-danger-text-light);background-color:var(--c-danger-bg-light)}.custom-container.danger details{background-color:var(--c-danger-details-bg)}.custom-container.danger details code{background-color:var(--c-danger-bg-lighter)}.custom-container.danger .external-link-icon{--external-link-icon-color: var(--c-danger-text-quote)}.custom-container.details{display:block;position:relative;border-radius:2px;margin:1.6em 0;padding:1.6em;background-color:var(--c-details-bg)}.custom-container.details code{background-color:var(--c-bg-darker)}.custom-container.details h4{margin-top:0}.custom-container.details figure:last-child,.custom-container.details p:last-child{margin-bottom:0;padding-bottom:0}.custom-container.details summary{outline:none;cursor:pointer}.home{padding:var(--navbar-height) 2rem 0;max-width:var(--homepage-width);margin:0 auto;display:block}.home .hero{text-align:center}.home .hero img{max-width:100%;max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero h1,.home .hero .description,.home .hero .actions{margin:1.8rem auto}.home .hero .actions{display:flex;flex-wrap:wrap;gap:1rem;justify-content:center}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:var(--c-text-lightest)}.home .hero .action-button{display:inline-block;font-size:1.2rem;padding:.8rem 1.6rem;border-width:2px;border-style:solid;border-radius:4px;transition:background-color var(--t-color);box-sizing:border-box}.home .hero .action-button.primary{color:var(--c-bg);background-color:var(--c-brand);border-color:var(--c-brand)}.home .hero .action-button.primary:hover{background-color:var(--c-brand-light)}.home .hero .action-button.secondary{color:var(--c-brand);background-color:var(--c-bg);border-color:var(--c-brand)}.home .hero .action-button.secondary:hover{color:var(--c-bg);background-color:var(--c-brand-light)}.home .features{border-top:1px solid var(--c-border);transition:border-color var(--t-color);padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:var(--c-text-light)}.home .feature p{color:var(--c-text-lighter)}.home .theme-default-content{padding:0;margin:0}.home .footer{padding:2.5rem;border-top:1px solid var(--c-border);text-align:center;color:var(--c-text-lighter);transition:border-color var(--t-color)}@media (max-width: 719px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width: 419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero h1,.home .hero .description,.home .hero .actions{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.page{padding-top:var(--navbar-height);padding-left:var(--sidebar-width)}.navbar{position:fixed;z-index:20;top:0;left:0;right:0;height:var(--navbar-height);box-sizing:border-box;border-bottom:1px solid var(--c-border);background-color:var(--c-bg-navbar);transition:background-color var(--t-color),border-color var(--t-color)}.sidebar{font-size:16px;width:var(--sidebar-width);position:fixed;z-index:10;margin:0;top:var(--navbar-height);left:0;bottom:0;box-sizing:border-box;border-right:1px solid var(--c-border);overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--c-brand) var(--c-border);background-color:var(--c-bg-sidebar);transition:transform var(--t-transform),background-color var(--t-color),border-color var(--t-color)}.sidebar::-webkit-scrollbar{width:7px}.sidebar::-webkit-scrollbar-track{background-color:var(--c-border)}.sidebar::-webkit-scrollbar-thumb{background-color:var(--c-brand)}.sidebar-mask{position:fixed;z-index:9;top:0;left:0;width:100vw;height:100vh;display:none}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(1){transform:rotate(45deg) translate3d(5.5px,5.5px,0)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(2){transform:scale3d(0,1,1)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(3){transform:rotate(-45deg) translate3d(6px,-6px,0)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(1),.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(3){transform-origin:center}.theme-container.no-navbar .theme-default-content h1,.theme-container.no-navbar .theme-default-content h2,.theme-container.no-navbar .theme-default-content h3,.theme-container.no-navbar .theme-default-content h4,.theme-container.no-navbar .theme-default-content h5,.theme-container.no-navbar .theme-default-content h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .page{padding-top:0}.theme-container.no-navbar .sidebar{top:0}.theme-container.no-sidebar .sidebar{display:none}@media (max-width: 719px){.theme-container.no-sidebar .sidebar{display:block}}.theme-container.no-sidebar .page{padding-left:0}.theme-default-content a:not(.header-anchor):hover{text-decoration:underline}.theme-default-content img{max-width:100%}.theme-default-content h1,.theme-default-content h2,.theme-default-content h3,.theme-default-content h4,.theme-default-content h5,.theme-default-content h6{margin-top:calc(.5rem - var(--navbar-height));padding-top:calc(1rem + var(--navbar-height));margin-bottom:0}.theme-default-content h1:first-child,.theme-default-content h2:first-child,.theme-default-content h3:first-child,.theme-default-content h4:first-child,.theme-default-content h5:first-child,.theme-default-content h6:first-child{margin-bottom:1rem}.theme-default-content h1:first-child+p,.theme-default-content h1:first-child+pre,.theme-default-content h1:first-child+.custom-container,.theme-default-content h2:first-child+p,.theme-default-content h2:first-child+pre,.theme-default-content h2:first-child+.custom-container,.theme-default-content h3:first-child+p,.theme-default-content h3:first-child+pre,.theme-default-content h3:first-child+.custom-container,.theme-default-content h4:first-child+p,.theme-default-content h4:first-child+pre,.theme-default-content h4:first-child+.custom-container,.theme-default-content h5:first-child+p,.theme-default-content h5:first-child+pre,.theme-default-content h5:first-child+.custom-container,.theme-default-content h6:first-child+p,.theme-default-content h6:first-child+pre,.theme-default-content h6:first-child+.custom-container{margin-top:2rem}@media (max-width: 959px){.sidebar{font-size:15px;width:var(--sidebar-width-mobile)}.page{padding-left:var(--sidebar-width-mobile)}}@media (max-width: 719px){.sidebar{top:0;padding-top:var(--navbar-height);transform:translate(-100%)}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translate(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width: 419px){h1{font-size:1.9rem}}.navbar{--navbar-line-height: calc( var(--navbar-height) - 2 * var(--navbar-padding-v) );padding:var(--navbar-padding-v) var(--navbar-padding-h);line-height:var(--navbar-line-height)}.navbar .logo{height:var(--navbar-line-height);margin-right:var(--navbar-padding-v);vertical-align:top}.navbar .site-name{font-size:1.3rem;font-weight:600;color:var(--c-text);position:relative}.navbar .navbar-items-wrapper{display:flex;position:absolute;box-sizing:border-box;top:var(--navbar-padding-v);right:var(--navbar-padding-h);height:var(--navbar-line-height);padding-left:var(--navbar-padding-h);white-space:nowrap;font-size:.9rem}.navbar .navbar-items-wrapper .search-box{flex:0 0 auto;vertical-align:top}@media screen and (max-width: 719px){.navbar{padding-left:4rem}.navbar .site-name{display:block;width:calc(100vw - 11rem);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.navbar .can-hide{display:none}}.navbar-items{display:inline-block}@media print{.navbar-items{display:none}}.navbar-items a{display:inline-block;line-height:1.4rem;color:inherit}.navbar-items a:hover,.navbar-items a.route-link-active{color:var(--c-text)}.navbar-items .navbar-item{position:relative;display:inline-block;margin-left:1.5rem;line-height:var(--navbar-line-height)}.navbar-items .navbar-item:first-child{margin-left:0}.navbar-items .navbar-item>a:hover,.navbar-items .navbar-item>a.route-link-active{margin-bottom:-2px;border-bottom:2px solid var(--c-text-accent)}@media (max-width: 719px){.navbar-items .navbar-item{margin-left:0}.navbar-items .navbar-item>a:hover,.navbar-items .navbar-item>a.route-link-active{margin-bottom:0;border-bottom:none}.navbar-items a:hover,.navbar-items a.route-link-active{color:var(--c-text-accent)}}.toggle-sidebar-button{position:absolute;top:.6rem;left:1rem;display:none;padding:.6rem;cursor:pointer}.toggle-sidebar-button .icon{display:flex;flex-direction:column;justify-content:center;align-items:center;width:1.25rem;height:1.25rem;cursor:inherit}.toggle-sidebar-button .icon span{display:inline-block;width:100%;height:2px;border-radius:2px;background-color:var(--c-text);transition:transform var(--t-transform)}.toggle-sidebar-button .icon span:nth-child(2){margin:6px 0}@media screen and (max-width: 719px){.toggle-sidebar-button{display:block}}.toggle-color-mode-button{display:flex;margin:auto;margin-left:1rem;border:0;background:none;color:var(--c-text);opacity:.8;cursor:pointer}@media print{.toggle-color-mode-button{display:none}}.toggle-color-mode-button:hover{opacity:1}.toggle-color-mode-button .icon{width:1.25rem;height:1.25rem}.DocSearch{transition:background-color var(--t-color)}.navbar-dropdown-wrapper{cursor:pointer}.navbar-dropdown-wrapper .navbar-dropdown-title,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile{display:block;font-size:.9rem;font-family:inherit;cursor:inherit;padding:inherit;line-height:1.4rem;background:transparent;border:none;font-weight:500;color:var(--c-text)}.navbar-dropdown-wrapper .navbar-dropdown-title:hover,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile:hover{border-color:transparent}.navbar-dropdown-wrapper .navbar-dropdown-title .arrow,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.navbar-dropdown-wrapper .navbar-dropdown-title-mobile{display:none;font-weight:600;font-size:inherit}.navbar-dropdown-wrapper .navbar-dropdown-title-mobile:hover{color:var(--c-text-accent)}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item{color:inherit;line-height:1.7rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle{margin:.45rem 0 0;border-top:1px solid var(--c-border);padding:1rem 0 .45rem;font-size:.9rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>span{padding:0 1.5rem 0 1.25rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>a{font-weight:inherit}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>a.route-link-active:after{display:none}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem-wrapper{padding:0;list-style:none}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem-wrapper .navbar-dropdown-subitem{font-size:.9em}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a:hover,.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.route-link-active{color:var(--c-text-accent)}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.route-link-active:after{content:"";width:0;height:0;border-left:5px solid var(--c-text-accent);border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(50% - 2px);left:9px}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item:first-child .navbar-dropdown-subtitle{margin-top:0;padding-top:0;border-top:0}.navbar-dropdown-wrapper.mobile.open .navbar-dropdown-title,.navbar-dropdown-wrapper.mobile.open .navbar-dropdown-title-mobile{margin-bottom:.5rem}.navbar-dropdown-wrapper.mobile .navbar-dropdown-title,.navbar-dropdown-wrapper.mobile .navbar-dropdown-title-mobile{display:none}.navbar-dropdown-wrapper.mobile .navbar-dropdown-title-mobile{display:block}.navbar-dropdown-wrapper.mobile .navbar-dropdown{transition:height .1s ease-out;overflow:hidden}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle{border-top:0;margin-top:0;padding-top:0;padding-bottom:0}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle,.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item>a{font-size:15px;line-height:2rem}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem{font-size:14px;padding-left:1rem}.navbar-dropdown-wrapper:not(.mobile){height:1.8rem}.navbar-dropdown-wrapper:not(.mobile):hover .navbar-dropdown,.navbar-dropdown-wrapper:not(.mobile).open .navbar-dropdown{display:block!important}.navbar-dropdown-wrapper:not(.mobile).open:blur{display:none}.navbar-dropdown-wrapper:not(.mobile) .navbar-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:var(--c-bg-navbar);padding:.6rem 0;border:1px solid var(--c-border);border-bottom-color:var(--c-border-dark);text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}.page{padding-bottom:2rem;display:block}.page .theme-default-content{max-width:var(--content-width);margin:0 auto;padding:2rem 2.5rem;padding-top:0}@media (max-width: 959px){.page .theme-default-content{padding:2rem}}@media (max-width: 419px){.page .theme-default-content{padding:1.5rem}}.page-meta{max-width:var(--content-width);margin:0 auto;padding:1rem 2.5rem;overflow:auto}@media (max-width: 959px){.page-meta{padding:2rem}}@media (max-width: 419px){.page-meta{padding:1.5rem}}.page-meta .meta-item{cursor:default;margin-top:.8rem}.page-meta .meta-item .meta-item-label{font-weight:500;color:var(--c-text-lighter)}.page-meta .meta-item .meta-item-info{font-weight:400;color:var(--c-text-quote)}.page-meta .edit-link{display:inline-block;margin-right:.25rem}@media print{.page-meta .edit-link{display:none}}.page-meta .last-updated{float:right}@media (max-width: 719px){.page-meta .last-updated{font-size:.8em;float:none}.page-meta .contributors{font-size:.8em}}.page-nav{max-width:var(--content-width);margin:0 auto;padding:1rem 2.5rem 2rem;padding-bottom:0}@media (max-width: 959px){.page-nav{padding:2rem}}@media (max-width: 419px){.page-nav{padding:1.5rem}}.page-nav .inner{min-height:2rem;margin-top:0;border-top:1px solid var(--c-border);transition:border-color var(--t-color);padding-top:1rem;overflow:auto}.page-nav .prev a:before{content:"←"}.page-nav .next{float:right}.page-nav .next a:after{content:"→"}.sidebar ul{padding:0;margin:0;list-style-type:none}.sidebar a{display:inline-block}.sidebar .navbar-items{display:none;border-bottom:1px solid var(--c-border);transition:border-color var(--t-color);padding:.5rem 0 .75rem}.sidebar .navbar-items a{font-weight:600}.sidebar .navbar-items .navbar-item{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar .sidebar-items{padding:1.5rem 0}@media (max-width: 719px){.sidebar .navbar-items{display:block}.sidebar .navbar-items .navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.route-link-active:after{top:calc(1rem - 2px)}.sidebar .sidebar-items{padding:1rem 0}}.sidebar-item{cursor:default;border-left:.25rem solid transparent;color:var(--c-text)}.sidebar-item:focus-visible{outline-width:1px;outline-offset:-1px}.sidebar-item.active:not(p.sidebar-heading){font-weight:600;color:var(--c-text-accent);border-left-color:var(--c-text-accent)}.sidebar-item.sidebar-heading{transition:color .15s ease;font-size:1.1em;font-weight:700;padding:.35rem 1.5rem .35rem 1.25rem;width:100%;box-sizing:border-box;margin:0}.sidebar-item.sidebar-heading+.sidebar-item-children{transition:height .1s ease-out;overflow:hidden;margin-bottom:.75rem}.sidebar-item.collapsible{cursor:pointer}.sidebar-item.collapsible .arrow{position:relative;top:-.12em;left:.5em}.sidebar-item:not(.sidebar-heading){font-size:1em;font-weight:400;display:inline-block;margin:0;padding:.35rem 1rem .35rem 2rem;line-height:1.4;width:100%;box-sizing:border-box}.sidebar-item:not(.sidebar-heading)+.sidebar-item-children{padding-left:1rem;font-size:.95em}.sidebar-item-children .sidebar-item-children .sidebar-item:not(.sidebar-heading){padding:.25rem 1rem .25rem 1.75rem}.sidebar-item-children .sidebar-item-children .sidebar-item:not(.sidebar-heading).active{font-weight:500;border-left-color:transparent}a.sidebar-heading+.sidebar-item-children .sidebar-item:not(.sidebar-heading).active{border-left-color:transparent}a.sidebar-item{cursor:pointer}a.sidebar-item:hover{color:var(--c-text-accent)}.table-of-contents .badge{vertical-align:middle}.dropdown-enter-from,.dropdown-leave-to{height:0!important}.fade-slide-y-enter-active{transition:all .2s ease}.fade-slide-y-leave-active{transition:all .2s cubic-bezier(1,.5,.8,1)}.fade-slide-y-enter-from,.fade-slide-y-leave-to{transform:translateY(10px);opacity:0}.footnote-item{margin-top:-3.6rem;padding-top:4.1rem}.footnote-item>p{margin-bottom:0}.footnote-ref{position:relative}.footnote-anchor{position:absolute;top:-4.1rem}.vp-catalog-wrapper{margin-top:8px;margin-bottom:8px}.vp-catalog-wrapper.index ol{padding-inline-start:0}.vp-catalog-wrapper.index li{list-style-type:none}.vp-catalog-wrapper.index .vp-catalogs{padding-inline-start:0}.vp-catalog-wrapper.index .vp-catalog{list-style-type:none}.vp-catalog-wrapper.index .vp-catalog-title:before{content:"§" counter(catalog-item,upper-roman) " "}.vp-catalog-wrapper.index .vp-child-catalogs{counter-reset:child-catalog}.vp-catalog-wrapper.index .vp-child-catalog{counter-increment:child-catalog}.vp-catalog-wrapper.index .vp-child-catalog .vp-catalog-title:before{content:counter(catalog-item) "." counter(child-catalog) " "}.vp-catalog-wrapper.index .vp-sub-catalogs{padding-inline-start:.5rem}.vp-catalogs{margin:0;counter-reset:catalog-item}.vp-catalogs.deep{padding-inline-start:0}.vp-catalogs.deep .vp-catalog{list-style-type:none}.vp-catalogs .font-icon{vertical-align:baseline;margin-inline-end:.25rem}.vp-catalog{counter-increment:catalog-item}.vp-catalog-main-title{margin-top:calc(.5rem - var(--navbar-height, 3.6rem));margin-bottom:.5rem;padding-top:var(--navbar-height, 3.6rem);font-weight:500;font-size:1.75rem}.vp-catalog-main-title:first-child{margin-bottom:.5rem!important}.vp-catalog-main-title:only-child{margin-bottom:0!important}.vp-catalog-main-title .vp-link{text-decoration:none!important}.vp-catalog-child-title{margin-bottom:.5rem!important}.vp-catalog-child-title.has-children{margin-top:calc(.5rem - var(--navbar-height, 3.6rem));padding-top:var(--navbar-height, 3.6rem);border-bottom:1px solid var(--catalog-border-color);font-weight:500;font-size:1.3rem;transition:border-color .3s}.vp-catalog-child-title.has-children:only-child{margin-bottom:0!important}.vp-catalog-child-title .vp-link{text-decoration:none!important}.vp-catalog-sub-title{font-weight:500;font-size:1.1rem}.vp-catalog-sub-title:only-child{margin-bottom:0!important}.vp-catalog-title{color:inherit;text-decoration:none}.vp-catalog-title:hover{color:var(--catalog-active-color)}.vp-child-catalogs{margin:0}.vp-child-catalog{list-style-type:disc}.vp-sub-catalogs{counter-reset:sub-catalog}.vp-sub-catalog{counter-increment:sub-catalog}.vp-sub-catalog .vp-link:before{content:counter(catalog-item) "." counter(child-catalog) "." counter(sub-catalog) " "}.vp-sub-catalogs-wrapper{display:flex;flex-wrap:wrap}.vp-sub-catalog-link{display:inline-block;margin:4px 8px;padding:4px 8px;border-radius:6px;background-color:var(--catalog-bg-secondary-color);line-height:1.5;overflow-wrap:break-word;transition:background-color .3s,color .3s}.vp-sub-catalog-link:hover{background-color:var(--catalog-hover-color);color:var(--catalog-bg-color);text-decoration:none!important}.vp-catalog-header-anchor{font-size:.85em;float:left;margin-left:-1em;padding-right:0;margin-top:.125em;opacity:0;-webkit-user-select:none;-moz-user-select:none;user-select:none;text-decoration:none;content:"¶"}@media print{.vp-catalog-header-anchor{display:none}}h2:hover .vp-catalog-header-anchor,h3:hover .vp-catalog-header-anchor{opacity:1;text-decoration:none}.vp-catalog-header-anchor:focus-visible{opacity:1}.vp-empty-catalog{font-size:1.25rem;text-align:center}:root{--catalog-bg-color: #fff;--catalog-bg-secondary-color: #f8f8f8;--catalog-border-color: #e5e5e5;--catalog-active-color: #3eaf7c;--catalog-hover-color: #71cda3}.lang-modal-fade-enter-active,.lang-modal-fade-leave-active{transition:opacity .5s}.lang-modal-fade-enter,.lang-modal-fade-leave-to{opacity:0}.lang-modal-mask{position:fixed;top:0;right:0;bottom:0;left:0;z-index:var(--redirect-z-index);display:flex;align-items:center;justify-content:center;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}@media print{.lang-modal-mask{display:none}}.lang-modal-wrapper{position:relative;z-index:1500;overflow:hidden;max-width:80vw;padding:1rem 2rem;border-radius:8px;background:var(--redirect-bg-color);box-shadow:0 2px 6px 0 var(--card-shadow)}.lang-modal-action{display:block;width:100%;margin:1rem 0;padding:.5rem .75rem;border:none;border-radius:8px;background-color:var(--redirect-bg-color-lighter);color:inherit;cursor:pointer}.lang-modal-action:hover{background-color:var(--redirect-bg-color-light)}.lang-modal-action.primary{background-color:var(--redirect-primary-color);color:var(--redirect-primary-text-color)}.lang-modal-action.primary:hover{background-color:var(--redirect-primary-hover-color)}:root{--redirect-z-index: 1499;--redirect-bg-color: #fff;--redirect-bg-color-light: #f3f4f5;--redirect-bg-color-lighter: #eeeeee;--redirect-text-color: #2c3e50;--redirect-primary-bg-color: #3eaf7c;--redirect-primary-hover-bg-color: #4abf8a;--redirect-primary-text-color: #fff}:root{--search-bg-color: #ffffff;--search-accent-color: #3eaf7c;--search-text-color: #2c3e50;--search-border-color: #eaecef;--search-item-text-color: #5d81a5;--search-item-focus-bg-color: #f3f4f5;--search-input-width: 8rem;--search-result-width: 20rem}.search-box{display:inline-block;position:relative;margin-left:1rem}@media print{.search-box{display:none}}.search-box input{-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:text;width:var(--search-input-width);height:2rem;color:var(--search-text-color);display:inline-block;border:1px solid var(--search-border-color);border-radius:2rem;font-size:.9rem;line-height:2rem;padding:0 .5rem 0 2rem;outline:none;transition:all ease .3s;background:var(--search-bg-color) url("data:image/svg+xml,%3c?xml%20version='1.0'%20encoding='UTF-8'?%3e%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='12'%20height='13'%3e%3cg%20stroke-width='2'%20stroke='%23aaa'%20fill='none'%3e%3cpath%20d='M11.29%2011.71l-4-4'/%3e%3ccircle%20cx='5'%20cy='5'%20r='4'/%3e%3c/g%3e%3c/svg%3e") .6rem .5rem no-repeat;background-size:1rem}@media (max-width: 719px){.search-box input{cursor:pointer;width:0;border-color:transparent;position:relative}}.search-box input:focus{cursor:auto;border-color:var(--search-accent-color)}@media (max-width: 719px){.search-box input:focus{cursor:text;left:0;width:10rem}}@media (max-width: 419px){.search-box input:focus{width:8rem}}.search-box .suggestions{background:var(--search-bg-color);width:var(--search-result-width);position:absolute;top:2rem;right:0;border:1px solid var(--search-border-color);border-radius:6px;padding:.4rem;list-style-type:none}@media (max-width: 419px){.search-box .suggestions{width:calc(100vw - 4rem);right:-.5rem}}.search-box .suggestion{line-height:1.4;padding:.4rem .6rem;border-radius:4px;cursor:pointer}.search-box .suggestion a{white-space:normal;color:var(--search-item-text-color)}.search-box .suggestion.focus{background-color:var(--search-item-focus-bg-color)}.search-box .suggestion.focus a{color:var(--search-accent-color)}.search-box .suggestion .page-title{font-weight:600}.search-box .suggestion .page-header{font-size:.9em;margin-left:.25em}.npm-badge[data-v-c758b2a0]{margin-right:.5rem}
diff --git a/assets/styles.html-DT7C6wK_.js b/assets/styles.html-DT7C6wK_.js
new file mode 100644
index 0000000000..05169bf6dd
--- /dev/null
+++ b/assets/styles.html-DT7C6wK_.js
@@ -0,0 +1,263 @@
+import{_ as o,r as a,o as c,c as D,a as l,b as s,d as n,e as r}from"./app-BcH8wZQx.js";const t={},i=s("h1",{id:"styles",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#styles"},[s("span",null,"Styles")])],-1),y={href:"https://sass-lang.com/",target:"_blank",rel:"noopener noreferrer"},d=r(`Users can customize style variables via a palette file , and add extra styles via a style file .
The path of the palette file is .vuepress/styles/palette.scss
.
You can make use of it to override predefined SASS variables of the default theme.
Click to expand SASS variables // responsive breakpoints
+$MQNarrow : 959px !default ;
+$MQMobile : 719px !default ;
+$MQMobileNarrow : 419px !default ;
+
The path of the style file is .vuepress/styles/index.scss
.
You can add extra styles here, or override the default styles:
:root {
+ scroll-behavior : smooth ;
+}
+
You can also make use of it to override predefined CSS variables of the default theme.
Click to expand CSS variables :root {
+ // brand colors
+ --c-brand : #3eaf7c ;
+ --c-brand-light : #4abf8a ;
+
+ // background colors
+ --c-bg : #ffffff ;
+ --c-bg-light : #f3f4f5 ;
+ --c-bg-lighter : #eeeeee ;
+ --c-bg-dark : #ebebec ;
+ --c-bg-darker : #e6e6e6 ;
+ --c-bg-navbar : var ( --c-bg );
+ --c-bg-sidebar : var ( --c-bg );
+ --c-bg-arrow : #cccccc ;
+
+ // text colors
+ --c-text : #2c3e50 ;
+ --c-text-accent : var ( --c-brand );
+ --c-text-light : #3a5169 ;
+ --c-text-lighter : #4e6e8e ;
+ --c-text-lightest : #6a8bad ;
+ --c-text-quote : #999999 ;
+
+ // border colors
+ --c-border : #eaecef ;
+ --c-border-dark : #dfe2e5 ;
+
+ // custom container colors
+ --c-tip : #42b983 ;
+ --c-tip-bg : var ( --c-bg-light );
+ --c-tip-title : var ( --c-text );
+ --c-tip-text : var ( --c-text );
+ --c-tip-text-accent : var ( --c-text-accent );
+ --c-warning : #ffc310 ;
+ --c-warning-bg : #fffae3 ;
+ --c-warning-bg-light : #fff3ba ;
+ --c-warning-bg-lighter : #fff0b0 ;
+ --c-warning-border-dark : #f7dc91 ;
+ --c-warning-details-bg : #fff5ca ;
+ --c-warning-title : #f1b300 ;
+ --c-warning-text : #746000 ;
+ --c-warning-text-accent : #edb100 ;
+ --c-warning-text-light : #c1971c ;
+ --c-warning-text-quote : #ccab49 ;
+ --c-danger : #f11e37 ;
+ --c-danger-bg : #ffe0e0 ;
+ --c-danger-bg-light : #ffcfde ;
+ --c-danger-bg-lighter : #ffc9c9 ;
+ --c-danger-border-dark : #f1abab ;
+ --c-danger-details-bg : #ffd4d4 ;
+ --c-danger-title : #ed1e2c ;
+ --c-danger-text : #660000 ;
+ --c-danger-text-accent : #bd1a1a ;
+ --c-danger-text-light : #b5474d ;
+ --c-danger-text-quote : #c15b5b ;
+ --c-details-bg : #eeeeee ;
+
+ // badge component colors
+ --c-badge-tip : var ( --c-tip );
+ --c-badge-warning : #ecc808 ;
+ --c-badge-warning-text : var ( --c-bg );
+ --c-badge-danger : #dc2626 ;
+ --c-badge-danger-text : var ( --c-bg );
+
+ // code group colors
+ --c-code-group-tab-title : rgba ( 255 , 255 , 255 , 0.9 );
+ --c-code-group-tab-bg : var ( --code-bg-color );
+ --c-code-group-tab-outline : var ( var ( --c-code-group-tab-title ));
+ --c-code-group-tab-active-border : var ( --c-brand );
+
+ // transition vars
+ --t-color : 0.3s ease ;
+ --t-transform : 0.3s ease ;
+
+ // code blocks vars
+ --code-bg-color : #282c34 ;
+ --code-hl-bg-color : rgba ( 0 , 0 , 0 , 0.66 );
+ --code-ln-color : #9e9e9e ;
+ --code-ln-wrapper-width : 3.5rem ;
+
+ // font vars
+ --font-family : -apple-system, BlinkMacSystemFont, 'Segoe UI' , Roboto, Oxygen,
+ Ubuntu, Cantarell, 'Fira Sans' , 'Droid Sans' , 'Helvetica Neue' , sans-serif ;
+ --font-family-code : Consolas, Monaco, 'Andale Mono' , 'Ubuntu Mono' , monospace ;
+
+ // layout vars
+ --navbar-height : 3.6rem ;
+ --navbar-padding-v : 0.7rem ;
+ --navbar-padding-h : 1.5rem ;
+ --sidebar-width : 20rem ;
+ --sidebar-width-mobile : calc ( var ( --sidebar-width ) * 0.82 );
+ --content-width : 740px ;
+ --homepage-width : 960px ;
+}
+
+// plugin-back-to-top
+.vp-back-to-top-button {
+ --back-to-top-color : var ( --c-brand );
+ --back-to-top-color-hover : var ( --c-brand-light );
+ --back-to-top-bg-color : var ( --c-bg );
+}
+
+// plugin-catalog
+.vp-catalog-wrapper {
+ --catalog-bg-color : var ( --c-bg );
+ --catalog-bg-secondary-color : var ( --c-bg-dark );
+ --catalog-border-color : var ( --c-border );
+ --catalog-active-color : var ( --c-brand );
+ --catalog-hover-color : var ( --c-brand-light );
+}
+
+// plugin-docsearch
+.DocSearch {
+ --docsearch-primary-color : var ( --c-brand );
+ --docsearch-text-color : var ( --c-text );
+ --docsearch-highlight-color : var ( --c-brand );
+ --docsearch-muted-color : var ( --c-text-quote );
+ --docsearch-container-background : rgba ( 9 , 10 , 17 , 0.8 );
+ --docsearch-modal-background : var ( --c-bg-light );
+ --docsearch-searchbox-background : var ( --c-bg-lighter );
+ --docsearch-searchbox-focus-background : var ( --c-bg );
+ --docsearch-searchbox-shadow : inset 0 0 0 2px var ( --c-brand );
+ --docsearch-hit-color : var ( --c-text-light );
+ --docsearch-hit-active-color : var ( --c-bg );
+ --docsearch-hit-background : var ( --c-bg );
+ --docsearch-hit-shadow : 0 1px 3px 0 var ( --c-border-dark );
+ --docsearch-footer-background : var ( --c-bg );
+}
+
+// plugin-external-link-icon
+.external-link-icon {
+ --external-link-icon-color : var ( --c-text-quote );
+}
+
+// plugin-medium-zoom
+.medium-zoom-overlay {
+ --medium-zoom-bg-color : var ( --c-bg );
+}
+
+// plugin-nprogress
+#nprogress {
+ --nprogress-color : var ( --c-brand );
+}
+
+// plugin-photo-swipe
+body {
+ --photo-swipe-bullet : var ( --c-bg );
+ --photo-swipe-bullet-active : var ( --c-brand );
+}
+
+// plugin-pwa-popup
+html {
+ --pwa-text-color : var ( --c-text );
+ --pwa-bg-color : var ( --c-bg );
+ --pwa-border-color : var ( --c-brand );
+ --pwa-btn-text-color : var ( --c-bg );
+ --pwa-btn-bg-color : var ( --c-brand );
+ --pwa-btn-hover-bg-color : var ( --c-brand-light );
+}
+
+html.dark {
+ --pwa-shadow-color : rgb ( 0 0 0 / 30% );
+ --pwa-content-color : #ccc ;
+ --pwa-content-light-color : #999 ;
+}
+
+// plugin-redirect
+.language-modal-mask {
+ --redirect-bg-color : var ( --c-bg );
+ --redirect-bg-color-light : var ( --c-bg-light );
+ --redirect-bg-color-lighter : var ( --c-bg-lighter );
+ --redirect-text-color : var ( --c-text );
+ --redirect-primary-bg-color : var ( --c-brand );
+ --redirect-primary-hover-bg-color : var ( --c-brand-light );
+ --redirect-primary-text-color : var ( --c-bg );
+}
+
+// plugin-search
+.search-box {
+ --search-bg-color : var ( --c-bg );
+ --search-accent-color : var ( --c-brand );
+ --search-text-color : var ( --c-text );
+ --search-border-color : var ( --c-border );
+
+ --search-item-text-color : var ( --c-text-lighter );
+ --search-item-focus-bg-color : var ( --c-bg-light );
+}
+
Click to expand dark mode CSS variables html.dark {
+ // brand colors
+ --c-brand : #3aa675 ;
+ --c-brand-light : #349469 ;
+
+ // background colors
+ --c-bg : #22272e ;
+ --c-bg-light : #2b313a ;
+ --c-bg-lighter : #262c34 ;
+ --c-bg-dark : #343b44 ;
+ --c-bg-darker : #37404c ;
+
+ // text colors
+ --c-text : #adbac7 ;
+ --c-text-light : #96a7b7 ;
+ --c-text-lighter : #8b9eb0 ;
+ --c-text-lightest : #8094a8 ;
+
+ // border colors
+ --c-border : #3e4c5a ;
+ --c-border-dark : #34404c ;
+
+ // custom container colors
+ --c-tip : #318a62 ;
+ --c-warning : #e0ad15 ;
+ --c-warning-bg : #2d2f2d ;
+ --c-warning-bg-light : #423e2a ;
+ --c-warning-bg-lighter : #44442f ;
+ --c-warning-border-dark : #957c35 ;
+ --c-warning-details-bg : #39392d ;
+ --c-warning-title : #fdca31 ;
+ --c-warning-text : #d8d96d ;
+ --c-warning-text-accent : #ffbf00 ;
+ --c-warning-text-light : #ddb84b ;
+ --c-warning-text-quote : #ccab49 ;
+ --c-danger : #fc1e38 ;
+ --c-danger-bg : #39232c ;
+ --c-danger-bg-light : #4b2b35 ;
+ --c-danger-bg-lighter : #553040 ;
+ --c-danger-border-dark : #a25151 ;
+ --c-danger-details-bg : #482936 ;
+ --c-danger-title : #fc2d3b ;
+ --c-danger-text : #ea9ca0 ;
+ --c-danger-text-accent : #fd3636 ;
+ --c-danger-text-light : #d9777c ;
+ --c-danger-text-quote : #d56b6b ;
+ --c-details-bg : #323843 ;
+
+ // badge component colors
+ --c-badge-warning : var ( --c-warning );
+ --c-badge-warning-text : #3c2e05 ;
+ --c-badge-danger : var ( --c-danger );
+ --c-badge-danger-text : #401416 ;
+
+ // code blocks vars
+ --code-hl-bg-color : #363b46 ;
+}
+
+// plugin-docsearch
+html.dark .DocSearch {
+ --docsearch-logo-color : var ( --c-text );
+ --docsearch-modal-shadow : inset 1px 1px 0 0 #2c2e40 , 0 3px 8px 0 #000309 ;
+ --docsearch-key-shadow : inset 0 -2px 0 0 #282d55 , inset 0 0 1px 1px #51577d ,
+ 0 2px 2px 0 rgba ( 3 , 4 , 9 , 0.3 );
+ --docsearch-key-gradient : linear-gradient ( -225deg , #444950 , #1c1e21 );
+ --docsearch-footer-shadow : inset 0 1px 0 0 rgba ( 73 , 76 , 106 , 0.5 ),
+ 0 -4px 8px 0 rgba ( 0 , 0 , 0 , 0.2 );
+}
+
`,12);function C(v,b){const p=a("NpmBadge"),e=a("ExternalLinkIcon");return c(),D("div",null,[i,l(p,{package:"@vuepress/theme-default"}),s("p",null,[n("The default theme uses "),s("a",y,[n("SASS"),l(e)]),n(" as the CSS pre-processor.")]),d])}const u=o(t,[["render",C],["__file","styles.html.vue"]]),m=JSON.parse('{"path":"/themes/default/styles.html","title":"Styles","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Palette File","slug":"palette-file","link":"#palette-file","children":[]},{"level":2,"title":"Style File","slug":"style-file","link":"#style-file","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"themes/default/styles.md"}');export{u as comp,m as data};
diff --git a/assets/styles.html-v6v-VKwH.js b/assets/styles.html-v6v-VKwH.js
new file mode 100644
index 0000000000..ce9124297e
--- /dev/null
+++ b/assets/styles.html-v6v-VKwH.js
@@ -0,0 +1,263 @@
+import{_ as e,r as a,o as c,c as D,a as l,b as s,d as n,e as r}from"./app-BcH8wZQx.js";const t={},i=s("h1",{id:"样式",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#样式"},[s("span",null,"样式")])],-1),y={href:"https://sass-lang.com/",target:"_blank",rel:"noopener noreferrer"},d=r(`用户可以通过 palette 文件 来自定义样式变量,还可以通过 style 文件 来添加额外的样式。
Palette 文件的路径是 .vuepress/styles/palette.scss
。
你可以利用它来覆盖默认主题的预定义 SASS 变量。
点击查看 SASS 变量 // responsive breakpoints
+$MQNarrow : 959px !default ;
+$MQMobile : 719px !default ;
+$MQMobileNarrow : 419px !default ;
+
Style 文件的路径是 .vuepress/styles/index.scss
。
你可以在这里添加额外的样式,或者覆盖默认样式:
:root {
+ scroll-behavior : smooth ;
+}
+
你也可以利用它来覆盖默认主题的预定义 CSS 变量。
点击查看 CSS 变量 :root {
+ // brand colors
+ --c-brand : #3eaf7c ;
+ --c-brand-light : #4abf8a ;
+
+ // background colors
+ --c-bg : #ffffff ;
+ --c-bg-light : #f3f4f5 ;
+ --c-bg-lighter : #eeeeee ;
+ --c-bg-dark : #ebebec ;
+ --c-bg-darker : #e6e6e6 ;
+ --c-bg-navbar : var ( --c-bg );
+ --c-bg-sidebar : var ( --c-bg );
+ --c-bg-arrow : #cccccc ;
+
+ // text colors
+ --c-text : #2c3e50 ;
+ --c-text-accent : var ( --c-brand );
+ --c-text-light : #3a5169 ;
+ --c-text-lighter : #4e6e8e ;
+ --c-text-lightest : #6a8bad ;
+ --c-text-quote : #999999 ;
+
+ // border colors
+ --c-border : #eaecef ;
+ --c-border-dark : #dfe2e5 ;
+
+ // custom container colors
+ --c-tip : #42b983 ;
+ --c-tip-bg : var ( --c-bg-light );
+ --c-tip-title : var ( --c-text );
+ --c-tip-text : var ( --c-text );
+ --c-tip-text-accent : var ( --c-text-accent );
+ --c-warning : #ffc310 ;
+ --c-warning-bg : #fffae3 ;
+ --c-warning-bg-light : #fff3ba ;
+ --c-warning-bg-lighter : #fff0b0 ;
+ --c-warning-border-dark : #f7dc91 ;
+ --c-warning-details-bg : #fff5ca ;
+ --c-warning-title : #f1b300 ;
+ --c-warning-text : #746000 ;
+ --c-warning-text-accent : #edb100 ;
+ --c-warning-text-light : #c1971c ;
+ --c-warning-text-quote : #ccab49 ;
+ --c-danger : #f11e37 ;
+ --c-danger-bg : #ffe0e0 ;
+ --c-danger-bg-light : #ffcfde ;
+ --c-danger-bg-lighter : #ffc9c9 ;
+ --c-danger-border-dark : #f1abab ;
+ --c-danger-details-bg : #ffd4d4 ;
+ --c-danger-title : #ed1e2c ;
+ --c-danger-text : #660000 ;
+ --c-danger-text-accent : #bd1a1a ;
+ --c-danger-text-light : #b5474d ;
+ --c-danger-text-quote : #c15b5b ;
+ --c-details-bg : #eeeeee ;
+
+ // badge component colors
+ --c-badge-tip : var ( --c-tip );
+ --c-badge-warning : #ecc808 ;
+ --c-badge-warning-text : var ( --c-bg );
+ --c-badge-danger : #dc2626 ;
+ --c-badge-danger-text : var ( --c-bg );
+
+ // code group colors
+ --c-code-group-tab-title : rgba ( 255 , 255 , 255 , 0.9 );
+ --c-code-group-tab-bg : var ( --code-bg-color );
+ --c-code-group-tab-outline : var ( var ( --c-code-group-tab-title ));
+ --c-code-group-tab-active-border : var ( --c-brand );
+
+ // transition vars
+ --t-color : 0.3s ease ;
+ --t-transform : 0.3s ease ;
+
+ // code blocks vars
+ --code-bg-color : #282c34 ;
+ --code-hl-bg-color : rgba ( 0 , 0 , 0 , 0.66 );
+ --code-ln-color : #9e9e9e ;
+ --code-ln-wrapper-width : 3.5rem ;
+
+ // font vars
+ --font-family : -apple-system, BlinkMacSystemFont, 'Segoe UI' , Roboto, Oxygen,
+ Ubuntu, Cantarell, 'Fira Sans' , 'Droid Sans' , 'Helvetica Neue' , sans-serif ;
+ --font-family-code : Consolas, Monaco, 'Andale Mono' , 'Ubuntu Mono' , monospace ;
+
+ // layout vars
+ --navbar-height : 3.6rem ;
+ --navbar-padding-v : 0.7rem ;
+ --navbar-padding-h : 1.5rem ;
+ --sidebar-width : 20rem ;
+ --sidebar-width-mobile : calc ( var ( --sidebar-width ) * 0.82 );
+ --content-width : 740px ;
+ --homepage-width : 960px ;
+}
+
+// plugin-back-to-top
+.vp-back-to-top-button {
+ --back-to-top-color : var ( --c-brand );
+ --back-to-top-color-hover : var ( --c-brand-light );
+ --back-to-top-bg-color : var ( --c-bg );
+}
+
+// plugin-catalog
+.vp-catalog-wrapper {
+ --catalog-bg-color : var ( --c-bg );
+ --catalog-bg-secondary-color : var ( --c-bg-dark );
+ --catalog-border-color : var ( --c-border );
+ --catalog-active-color : var ( --c-brand );
+ --catalog-hover-color : var ( --c-brand-light );
+}
+
+// plugin-docsearch
+.DocSearch {
+ --docsearch-primary-color : var ( --c-brand );
+ --docsearch-text-color : var ( --c-text );
+ --docsearch-highlight-color : var ( --c-brand );
+ --docsearch-muted-color : var ( --c-text-quote );
+ --docsearch-container-background : rgba ( 9 , 10 , 17 , 0.8 );
+ --docsearch-modal-background : var ( --c-bg-light );
+ --docsearch-searchbox-background : var ( --c-bg-lighter );
+ --docsearch-searchbox-focus-background : var ( --c-bg );
+ --docsearch-searchbox-shadow : inset 0 0 0 2px var ( --c-brand );
+ --docsearch-hit-color : var ( --c-text-light );
+ --docsearch-hit-active-color : var ( --c-bg );
+ --docsearch-hit-background : var ( --c-bg );
+ --docsearch-hit-shadow : 0 1px 3px 0 var ( --c-border-dark );
+ --docsearch-footer-background : var ( --c-bg );
+}
+
+// plugin-external-link-icon
+.external-link-icon {
+ --external-link-icon-color : var ( --c-text-quote );
+}
+
+// plugin-medium-zoom
+.medium-zoom-overlay {
+ --medium-zoom-bg-color : var ( --c-bg );
+}
+
+// plugin-nprogress
+#nprogress {
+ --nprogress-color : var ( --c-brand );
+}
+
+// plugin-photo-swipe
+body {
+ --photo-swipe-bullet : var ( --c-bg );
+ --photo-swipe-bullet-active : var ( --c-brand );
+}
+
+// plugin-pwa-popup
+html {
+ --pwa-text-color : var ( --c-text );
+ --pwa-bg-color : var ( --c-bg );
+ --pwa-border-color : var ( --c-brand );
+ --pwa-btn-text-color : var ( --c-bg );
+ --pwa-btn-bg-color : var ( --c-brand );
+ --pwa-btn-hover-bg-color : var ( --c-brand-light );
+}
+
+html.dark {
+ --pwa-shadow-color : rgb ( 0 0 0 / 30% );
+ --pwa-content-color : #ccc ;
+ --pwa-content-light-color : #999 ;
+}
+
+// plugin-redirect
+.language-modal-mask {
+ --redirect-bg-color : var ( --c-bg );
+ --redirect-bg-color-light : var ( --c-bg-light );
+ --redirect-bg-color-lighter : var ( --c-bg-lighter );
+ --redirect-text-color : var ( --c-text );
+ --redirect-primary-bg-color : var ( --c-brand );
+ --redirect-primary-hover-bg-color : var ( --c-brand-light );
+ --redirect-primary-text-color : var ( --c-bg );
+}
+
+// plugin-search
+.search-box {
+ --search-bg-color : var ( --c-bg );
+ --search-accent-color : var ( --c-brand );
+ --search-text-color : var ( --c-text );
+ --search-border-color : var ( --c-border );
+
+ --search-item-text-color : var ( --c-text-lighter );
+ --search-item-focus-bg-color : var ( --c-bg-light );
+}
+
点击查看暗黑模式 CSS 变量 html.dark {
+ // brand colors
+ --c-brand : #3aa675 ;
+ --c-brand-light : #349469 ;
+
+ // background colors
+ --c-bg : #22272e ;
+ --c-bg-light : #2b313a ;
+ --c-bg-lighter : #262c34 ;
+ --c-bg-dark : #343b44 ;
+ --c-bg-darker : #37404c ;
+
+ // text colors
+ --c-text : #adbac7 ;
+ --c-text-light : #96a7b7 ;
+ --c-text-lighter : #8b9eb0 ;
+ --c-text-lightest : #8094a8 ;
+
+ // border colors
+ --c-border : #3e4c5a ;
+ --c-border-dark : #34404c ;
+
+ // custom container colors
+ --c-tip : #318a62 ;
+ --c-warning : #e0ad15 ;
+ --c-warning-bg : #2d2f2d ;
+ --c-warning-bg-light : #423e2a ;
+ --c-warning-bg-lighter : #44442f ;
+ --c-warning-border-dark : #957c35 ;
+ --c-warning-details-bg : #39392d ;
+ --c-warning-title : #fdca31 ;
+ --c-warning-text : #d8d96d ;
+ --c-warning-text-accent : #ffbf00 ;
+ --c-warning-text-light : #ddb84b ;
+ --c-warning-text-quote : #ccab49 ;
+ --c-danger : #fc1e38 ;
+ --c-danger-bg : #39232c ;
+ --c-danger-bg-light : #4b2b35 ;
+ --c-danger-bg-lighter : #553040 ;
+ --c-danger-border-dark : #a25151 ;
+ --c-danger-details-bg : #482936 ;
+ --c-danger-title : #fc2d3b ;
+ --c-danger-text : #ea9ca0 ;
+ --c-danger-text-accent : #fd3636 ;
+ --c-danger-text-light : #d9777c ;
+ --c-danger-text-quote : #d56b6b ;
+ --c-details-bg : #323843 ;
+
+ // badge component colors
+ --c-badge-warning : var ( --c-warning );
+ --c-badge-warning-text : #3c2e05 ;
+ --c-badge-danger : var ( --c-danger );
+ --c-badge-danger-text : #401416 ;
+
+ // code blocks vars
+ --code-hl-bg-color : #363b46 ;
+}
+
+// plugin-docsearch
+html.dark .DocSearch {
+ --docsearch-logo-color : var ( --c-text );
+ --docsearch-modal-shadow : inset 1px 1px 0 0 #2c2e40 , 0 3px 8px 0 #000309 ;
+ --docsearch-key-shadow : inset 0 -2px 0 0 #282d55 , inset 0 0 1px 1px #51577d ,
+ 0 2px 2px 0 rgba ( 3 , 4 , 9 , 0.3 );
+ --docsearch-key-gradient : linear-gradient ( -225deg , #444950 , #1c1e21 );
+ --docsearch-footer-shadow : inset 0 1px 0 0 rgba ( 73 , 76 , 106 , 0.5 ),
+ 0 -4px 8px 0 rgba ( 0 , 0 , 0 , 0.2 );
+}
+
`,12);function C(v,b){const p=a("NpmBadge"),o=a("ExternalLinkIcon");return c(),D("div",null,[i,l(p,{package:"@vuepress/theme-default"}),s("p",null,[n("默认主题使用 "),s("a",y,[n("SASS"),l(o)]),n(" 作为 CSS 预处理器。")]),d])}const u=e(t,[["render",C],["__file","styles.html.vue"]]),m=JSON.parse('{"path":"/zh/themes/default/styles.html","title":"样式","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"Palette 文件","slug":"palette-文件","link":"#palette-文件","children":[]},{"level":2,"title":"Style 文件","slug":"style-文件","link":"#style-文件","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/themes/default/styles.md"}');export{u as comp,m as data};
diff --git a/assets/theme-data.html-C3p3zJjZ.js b/assets/theme-data.html-C3p3zJjZ.js
new file mode 100644
index 0000000000..1dc729e915
--- /dev/null
+++ b/assets/theme-data.html-C3p3zJjZ.js
@@ -0,0 +1,51 @@
+import{_ as o,r as n,o as t,c as i,a as e,b as s,d as a,w as c,e as r}from"./app-BcH8wZQx.js";const D={},d=s("h1",{id:"theme-data",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#theme-data"},[s("span",null,"theme-data")])],-1),m=r(`该插件主要用于开发主题,并且已经集成到默认主题中。大部分情况下你不需要直接使用它。
对于主题作者,该插件可以提供与 VuePress 及默认主题相同的多语言支持机制。但是如果你的主题不需要提供多语言支持,或者你想用你自己的方式来实现多语言支持,那么你不需要使用该插件。
npm i -D @vuepress/plugin-theme-data@next
+
import { themeDataPlugin } from '@vuepress/plugin-theme-data'
+
+export default {
+ plugins: [
+ themeDataPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
export default {
+ plugins: [
+ themeDataPlugin ({
+ themeData: {
+ foo: 'foo' ,
+ locales: {
+ '/zh/' : {
+ foo: 'zh-foo' ,
+ },
+ },
+ },
+ }),
+ ],
+}
+
注意
主题数据对象在传递到客户端之前,会使用 JSON.stringify()
进行处理,因此你需要保证你提供的是一个可以被 JSON 序列化的对象。
import { useThemeData } from '@vuepress/plugin-theme-data/client'
+import type { ThemeData } from '@vuepress/plugin-theme-data/client'
+
+type MyThemeData = ThemeData <{
+ foo : string
+}>
+
+export default {
+ setup () {
+ const themeData = useThemeData < MyThemeData >()
+ console . log ( themeData . value )
+ },
+}
+
import { useThemeLocaleData } from '@vuepress/plugin-theme-data/client'
+import type { ThemeData } from '@vuepress/plugin-theme-data/client'
+
+type MyThemeData = ThemeData <{
+ foo : string
+}>
+
+export default {
+ setup () {
+ const themeLocaleData = useThemeLocaleData < MyThemeData >()
+ console . log ( themeLocaleData . value )
+ },
+}
+
`,17);function u(y,v){const l=n("NpmBadge"),p=n("RouteLink");return t(),i("div",null,[d,e(l,{package:"@vuepress/plugin-theme-data"}),s("p",null,[a("为你的主题提供客户端数据,包含 VuePress 的 "),e(p,{to:"/guide/i18n.html"},{default:c(()=>[a("多语言支持")]),_:1}),a(" 。")]),m])}const C=o(D,[["render",u],["__file","theme-data.html.vue"]]),b=JSON.parse('{"path":"/zh/plugins/theme-data.html","title":"theme-data","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"themeData","slug":"themedata","link":"#themedata","children":[]}]},{"level":2,"title":"Composition API","slug":"composition-api","link":"#composition-api","children":[{"level":3,"title":"useThemeData","slug":"usethemedata","link":"#usethemedata","children":[]},{"level":3,"title":"useThemeLocaleData","slug":"usethemelocaledata","link":"#usethemelocaledata","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/theme-data.md"}');export{C as comp,b as data};
diff --git a/assets/theme-data.html-DXyfngG7.js b/assets/theme-data.html-DXyfngG7.js
new file mode 100644
index 0000000000..8d50f8fba9
--- /dev/null
+++ b/assets/theme-data.html-DXyfngG7.js
@@ -0,0 +1,51 @@
+import{_ as o,r as e,o as t,c as i,a as n,b as s,d as a,w as c,e as r}from"./app-BcH8wZQx.js";const d={},D=s("h1",{id:"theme-data",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#theme-data"},[s("span",null,"theme-data")])],-1),u=r(`This plugin is mainly used to develop themes, and has been integrated into the default theme. You won't need to use it directly in most cases.
For theme authors, this plugin will help you to use the same i18n mechanism as VuePress and the default theme. But if you don't want to provide i18n support, or you want to implement in your own way, you don't need this plugin.
npm i -D @vuepress/plugin-theme-data@next
+
import { themeDataPlugin } from '@vuepress/plugin-theme-data'
+
+export default {
+ plugins: [
+ themeDataPlugin ({
+ // options
+ }),
+ ],
+}
+
Type: ThemeData
Default: {}
Details:
The theme data object that you want to use in client side.
You can provide theme data in Node side via this option, and use it in client side via useThemeData and useThemeLocaleData .
Example:
export default {
+ plugins: [
+ themeDataPlugin ({
+ themeData: {
+ foo: 'foo' ,
+ locales: {
+ '/zh/' : {
+ foo: 'zh-foo' ,
+ },
+ },
+ },
+ }),
+ ],
+}
+
WARNING
The theme data object will be processed by JSON.stringify()
before forwarding to client side, so you should ensure that you are providing a JSON-friendly object.
import { useThemeData } from '@vuepress/plugin-theme-data/client'
+import type { ThemeData } from '@vuepress/plugin-theme-data/client'
+
+type MyThemeData = ThemeData <{
+ foo : string
+}>
+
+export default {
+ setup () {
+ const themeData = useThemeData < MyThemeData >()
+ console . log ( themeData . value )
+ },
+}
+
import { useThemeLocaleData } from '@vuepress/plugin-theme-data/client'
+import type { ThemeData } from '@vuepress/plugin-theme-data/client'
+
+type MyThemeData = ThemeData <{
+ foo : string
+}>
+
+export default {
+ setup () {
+ const themeLocaleData = useThemeLocaleData < MyThemeData >()
+ console . log ( themeLocaleData . value )
+ },
+}
+
`,17);function m(y,h){const l=e("NpmBadge"),p=e("RouteLink");return t(),i("div",null,[D,n(l,{package:"@vuepress/plugin-theme-data"}),s("p",null,[a("Provide client data for your theme, with VuePress "),n(p,{to:"/guide/i18n.html"},{default:c(()=>[a("i18n")]),_:1}),a(" support.")]),u])}const C=o(d,[["render",m],["__file","theme-data.html.vue"]]),b=JSON.parse('{"path":"/plugins/theme-data.html","title":"theme-data","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"themeData","slug":"themedata","link":"#themedata","children":[]}]},{"level":2,"title":"Composition API","slug":"composition-api","link":"#composition-api","children":[{"level":3,"title":"useThemeData","slug":"usethemedata","link":"#usethemedata","children":[]},{"level":3,"title":"useThemeLocaleData","slug":"usethemelocaledata","link":"#usethemelocaledata","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/theme-data.md"}');export{C as comp,b as data};
diff --git a/assets/toc.html-BS2GHIGv.js b/assets/toc.html-BS2GHIGv.js
new file mode 100644
index 0000000000..3a8e318a39
--- /dev/null
+++ b/assets/toc.html-BS2GHIGv.js
@@ -0,0 +1,74 @@
+import{_ as i,r as p,o as c,c as r,a,b as n,d as s,w as e,e as o}from"./app-BcH8wZQx.js";const d={},D=n("h1",{id:"toc",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#toc"},[n("span",null,"toc")])],-1),u=o(`This plugin will provide a table-of-contents (TOC) component.
npm i -D @vuepress/plugin-toc@next
+
import { tocPlugin } from '@vuepress/plugin-toc'
+
+export default {
+ plugins: [
+ tocPlugin ({
+ // options
+ }),
+ ],
+}
+
`,5),y=o(`<!-- markdown toc syntax -->
+
+[[ toc ]]
+
+<!-- vue toc component -->
+< Toc />
+
Both of them can be pre-rendered correctly in build mode. However, there are some differences between them.
The markdown syntax [[toc]]
could only be used in markdown files. It is parsed by markdown-it, and the generated TOC is static content.
The component <Toc/>
could be used in both markdown files and vue files. It is loaded by vue, and the generated TOC is a vue component.
`,4),v=n("code",null,"linkClass",-1),m=n("code",null,"linkActiveClass",-1),C=o(`Therefore, this plugin is more useful for theme developers.
The TOC component also accepts props for customization.
< template >
+ < Toc : headers = " headers " : options = " options " />
+</ template >
+
interface PageHeader {
+ level : number
+ title : string
+ slug : string
+ children : PageHeader []
+}
+
Details:
Specify the headers array to render.
If this prop is not specified, the headers of current page will be used.
Type: Partial<TocPropsOptions>
interface TocPropsOptions {
+ containerTag : string
+ containerClass : string
+ listClass : string
+ itemClass : string
+ linkTag : 'a' | 'RouterLink' | 'RouteLink'
+ linkClass : string
+ linkActiveClass : string
+ linkChildrenActiveClass : string
+}
+
const defaultOptions = {
+ containerTag: 'nav' ,
+ containerClass: 'vuepress-toc' ,
+ listClass: 'vuepress-toc-list' ,
+ itemClass: 'vuepress-toc-item' ,
+ linkTag: 'RouteLink' ,
+ linkClass: 'vuepress-toc-link' ,
+ linkActiveClass: 'active' ,
+ linkChildrenActiveClass: 'active' ,
+}
+
Details:
Customize the TOC component.
If the containerTag
is set to an empty string ''
, the <nav>
container will be removed totally.
Example:
The rendered TOC component with default options looks like:
< template >
+ <!-- container -->
+ < nav class = "vuepress-toc" >
+ <!-- list -->
+ < ul class = "vuepress-toc-list" >
+ <!-- item -->
+ < li class = "vuepress-toc-item" >
+ <!-- link -->
+ < RouteLink class = "vuepress-toc-link" to = "#foo" > Foo </ RouteLink >
+ </ li >
+ <!-- item with children -->
+ < li class = "vuepress-toc-item" >
+ <!-- link (children active) -->
+ < RouteLink class = "vuepress-toc-link active" to = "#bar" > Bar </ RouteLink >
+ <!-- list (children) -->
+ < ul class = "vuepress-toc-list" >
+ <!-- item -->
+ < li class = "vuepress-toc-item" >
+ <!-- link (active) -->
+ < RouteLink class = "vuepress-toc-link active" to = "#bar-child" >
+ Bar Child
+ </ RouteLink >
+ </ li >
+ </ ul >
+ </ li >
+ </ ul >
+ </ nav >
+</ template >
+
`,20);function h(b,g){const t=p("NpmBadge"),l=p("RouteLink");return c(),r("div",null,[D,a(t,{package:"@vuepress/plugin-toc"}),u,n("p",null,[s("Similar to the "),a(l,{to:"/guide/markdown.html#table-of-contents"},{default:e(()=>[s("Table of Contents Markdown Syntax")]),_:1}),s(", the TOC component that provided by this plugin could be used in your markdown content directly:")]),y,n("p",null,[s("This plugin could work together with "),a(l,{to:"/plugins/active-header-links.html"},{default:e(()=>[s("@vuepress/plugin-active-header-links")]),_:1}),s(" by setting the "),a(l,{to:"/plugins/active-header-links.html#headerlinkselector"},{default:e(()=>[s("headerLinkSelector")]),_:1}),s(" to match the "),v,s(" option. When the page scroll to a certain header anchor, this corresponding link will be added "),m,s(" class name.")]),C])}const k=i(d,[["render",h],["__file","toc.html.vue"]]),f=JSON.parse('{"path":"/plugins/toc.html","title":"toc","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Differences with Markdown TOC Syntax","slug":"differences-with-markdown-toc-syntax","link":"#differences-with-markdown-toc-syntax","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"componentName","slug":"componentname","link":"#componentname","children":[]},{"level":3,"title":"defaultPropsOptions","slug":"defaultpropsoptions","link":"#defaultpropsoptions","children":[]}]},{"level":2,"title":"Component Props","slug":"component-props","link":"#component-props","children":[{"level":3,"title":"headers","slug":"headers","link":"#headers","children":[]},{"level":3,"title":"options","slug":"options-1","link":"#options-1","children":[]}]}],"git":{"updatedTime":1707119730000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":4}]},"filePathRelative":"plugins/toc.md"}');export{k as comp,f as data};
diff --git a/assets/toc.html-WqXaJU_S.js b/assets/toc.html-WqXaJU_S.js
new file mode 100644
index 0000000000..cdf2988d57
--- /dev/null
+++ b/assets/toc.html-WqXaJU_S.js
@@ -0,0 +1,74 @@
+import{_ as i,r as o,o as c,c as r,a as l,b as n,d as s,w as e,e as p}from"./app-BcH8wZQx.js";const d={},D=n("h1",{id:"toc",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#toc"},[n("span",null,"toc")])],-1),u=p(`该插件会提供一个目录 (table-of-contents, TOC) 组件。
npm i -D @vuepress/plugin-toc@next
+
import { tocPlugin } from '@vuepress/plugin-toc'
+
+export default {
+ plugins: [
+ tocPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
`,5),y=p(`<!-- Markdown 目录语法 -->
+
+[[ toc ]]
+
+<!-- Vue 目录组件 -->
+< Toc />
+
在 Build 模式中,它们都可以被正确地预渲染。然而,它们之间存在一些区别。
Markdown 语法 [[toc]]
仅能在 Markdown 文件中使用。它是由 markdown-it 解析的,生成的目录是静态内容。
组件 <Toc/>
既可以用在 Markdown 文件中,也可以用在 Vue 文件中。它是由 Vue 加载的,生成的目录是一个 Vue 组件。
`,4),v=n("code",null,"linkClass",-1),C=n("code",null,"linkActiveClass",-1),m=p(`因此,该插件对于主题开发者来说更为有用。
类型: string
默认值: 'Toc'
详情:
指定目录组件的名称。
目录组件可以通过 Props 来进行自定义。
< template >
+ < Toc : headers = " headers " : options = " options " />
+</ template >
+
interface PageHeader {
+ level : number
+ title : string
+ slug : string
+ children : PageHeader []
+}
+
类型: Partial<TocPropsOptions>
interface TocPropsOptions {
+ containerTag : string
+ containerClass : string
+ listClass : string
+ itemClass : string
+ linkTag : 'a' | 'RouterLink' | 'RouteLink'
+ linkClass : string
+ linkActiveClass : string
+ linkChildrenActiveClass : string
+}
+
const defaultOptions = {
+ containerTag: 'nav' ,
+ containerClass: 'vuepress-toc' ,
+ listClass: 'vuepress-toc-list' ,
+ itemClass: 'vuepress-toc-item' ,
+ linkTag: 'RouteLink' ,
+ linkClass: 'vuepress-toc-link' ,
+ linkActiveClass: 'active' ,
+ linkChildrenActiveClass: 'active' ,
+}
+
< template >
+ <!-- container -->
+ < nav class = "vuepress-toc" >
+ <!-- list -->
+ < ul class = "vuepress-toc-list" >
+ <!-- item -->
+ < li class = "vuepress-toc-item" >
+ <!-- link -->
+ < RouteLink class = "vuepress-toc-link" to = "#foo" > Foo </ RouteLink >
+ </ li >
+ <!-- item with children -->
+ < li class = "vuepress-toc-item" >
+ <!-- link (children active) -->
+ < RouteLink class = "vuepress-toc-link active" to = "#bar" > Bar </ RouteLink >
+ <!-- list (children) -->
+ < ul class = "vuepress-toc-list" >
+ <!-- item -->
+ < li class = "vuepress-toc-item" >
+ <!-- link (active) -->
+ < RouteLink class = "vuepress-toc-link active" to = "#bar-child" >
+ Bar Child
+ </ RouteLink >
+ </ li >
+ </ ul >
+ </ li >
+ </ ul >
+ </ nav >
+</ template >
+
`,20);function b(h,g){const t=o("NpmBadge"),a=o("RouteLink");return c(),r("div",null,[D,l(t,{package:"@vuepress/plugin-toc"}),u,n("p",null,[s("与 "),l(a,{to:"/guide/markdown.html#%E7%9B%AE%E5%BD%95"},{default:e(()=>[s("Markdown 目录语法")]),_:1}),s(" 类似,该插件提供的目录组件可以直接在你的 Markdown 内容中使用:")]),y,n("p",null,[s("该插件可以和 "),l(a,{to:"/zh/plugins/active-header-links.html"},{default:e(()=>[s("@vuepress/plugin-active-header-links")]),_:1}),s(" 协同工作,你只需要将 "),l(a,{to:"/zh/plugins/active-header-links.html#headerlinkselector"},{default:e(()=>[s("headerLinkSelector")]),_:1}),s(" 与该插件的 "),v,s(" 匹配即可。当页面滚动至某个标题锚点后,对应的链接就会被加上 "),C,s(" 类名。")]),m])}const k=i(d,[["render",b],["__file","toc.html.vue"]]),F=JSON.parse('{"path":"/zh/plugins/toc.html","title":"toc","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"与 Markdown 目录语法的区别","slug":"与-markdown-目录语法的区别","link":"#与-markdown-目录语法的区别","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"componentName","slug":"componentname","link":"#componentname","children":[]},{"level":3,"title":"defaultPropsOptions","slug":"defaultpropsoptions","link":"#defaultpropsoptions","children":[]}]},{"level":2,"title":"组件 Props","slug":"组件-props","link":"#组件-props","children":[{"level":3,"title":"headers","slug":"headers","link":"#headers","children":[]},{"level":3,"title":"options","slug":"options","link":"#options","children":[]}]}],"git":{"updatedTime":1707119730000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":4}]},"filePathRelative":"zh/plugins/toc.md"}');export{k as comp,F as data};
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 0000000000..e481e5dda7
Binary files /dev/null and b/favicon.ico differ
diff --git a/images/cookbook/extending-a-theme-01.png b/images/cookbook/extending-a-theme-01.png
new file mode 100644
index 0000000000..9ba6d7e812
Binary files /dev/null and b/images/cookbook/extending-a-theme-01.png differ
diff --git a/images/hero.png b/images/hero.png
new file mode 100644
index 0000000000..ac6beaff06
Binary files /dev/null and b/images/hero.png differ
diff --git a/images/icons/android-chrome-192x192.png b/images/icons/android-chrome-192x192.png
new file mode 100644
index 0000000000..ddd043910e
Binary files /dev/null and b/images/icons/android-chrome-192x192.png differ
diff --git a/images/icons/android-chrome-384x384.png b/images/icons/android-chrome-384x384.png
new file mode 100644
index 0000000000..86e1fd58b3
Binary files /dev/null and b/images/icons/android-chrome-384x384.png differ
diff --git a/images/icons/apple-touch-icon.png b/images/icons/apple-touch-icon.png
new file mode 100644
index 0000000000..208915f1de
Binary files /dev/null and b/images/icons/apple-touch-icon.png differ
diff --git a/images/icons/favicon-16x16.png b/images/icons/favicon-16x16.png
new file mode 100644
index 0000000000..ca5047e7b8
Binary files /dev/null and b/images/icons/favicon-16x16.png differ
diff --git a/images/icons/favicon-32x32.png b/images/icons/favicon-32x32.png
new file mode 100644
index 0000000000..e275ce9ba1
Binary files /dev/null and b/images/icons/favicon-32x32.png differ
diff --git a/images/icons/mstile-150x150.png b/images/icons/mstile-150x150.png
new file mode 100644
index 0000000000..d0b1439483
Binary files /dev/null and b/images/icons/mstile-150x150.png differ
diff --git a/images/icons/safari-pinned-tab.svg b/images/icons/safari-pinned-tab.svg
new file mode 100644
index 0000000000..dc0b992c04
--- /dev/null
+++ b/images/icons/safari-pinned-tab.svg
@@ -0,0 +1,23 @@
+
+
+
+
+Created by potrace 1.11, written by Peter Selinger 2001-2013
+
+
+
+
+
diff --git a/images/logo.png b/images/logo.png
new file mode 100644
index 0000000000..60e17006ad
Binary files /dev/null and b/images/logo.png differ
diff --git a/index.html b/index.html
new file mode 100644
index 0000000000..c768b2f865
--- /dev/null
+++ b/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ Home | VuePress Ecosystem
+
+
+
+
+
+
+
+
+
diff --git a/manifest.webmanifest b/manifest.webmanifest
new file mode 100644
index 0000000000..d2e935f1ae
--- /dev/null
+++ b/manifest.webmanifest
@@ -0,0 +1,21 @@
+{
+ "name": "VuePress",
+ "short_name": "VuePress",
+ "description": "Vue-powered Static Site Generator",
+ "start_url": "/index.html",
+ "display": "standalone",
+ "background_color": "#fff",
+ "theme_color": "#3eaf7c",
+ "icons": [
+ {
+ "src": "/images/icons/android-chrome-192x192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "/images/icons/android-chrome-384x384.png",
+ "sizes": "384x384",
+ "type": "image/png"
+ }
+ ]
+}
diff --git a/plugins/active-header-links.html b/plugins/active-header-links.html
new file mode 100644
index 0000000000..ba2fef6ded
--- /dev/null
+++ b/plugins/active-header-links.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ active-header-links | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development This plugin will listen to page scroll event. When the page scrolls to a certain header anchor , this plugin will change the route hash to that header anchor if there is a corresponding header link .
This plugin is mainly used to develop themes, and has been integrated into the default theme. You won't need to use it directly in most cases.
npm i -D @vuepress/plugin-active-header-links@next
+
import { activeHeaderLinksPlugin } from '@vuepress/plugin-active-header-links'
+
+export default {
+ plugins: [
+ activeHeaderLinksPlugin ({
+ // options
+ }),
+ ],
+}
+
Type: string
Default: 'a.sidebar-item'
Details:
Selector of header link .
If a header anchor does not have a corresponding header link , this plugin won't change the route hash to that anchor when scrolling to it.
Last Updated:
Contributors: Mr.Hope
git
+
+
+
diff --git a/plugins/back-to-top.html b/plugins/back-to-top.html
new file mode 100644
index 0000000000..9f295c0f76
--- /dev/null
+++ b/plugins/back-to-top.html
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+ back-to-top | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development This plugin will add a back to top button to your site. The button will be displayed in the bottom right corner of the page when scrolling down. By clicking the button, the page will scroll to the top.
This plugin has been integrated into the default theme.
npm i -D @vuepress/plugin-back-to-top@next
+
import { backToTopPlugin } from '@vuepress/plugin-back-to-top'
+
+export default {
+ plugins: [ backToTopPlugin ()],
+}
+
Type: number
Default: 100
Details: Scroll threshold distance to display back to top button (in pixels) Type: boolean
Default: true
Details: Whether display progress bar around icon You can customize the style of the back to top button via CSS variables:
:root {
+ --back-to-top-z-index : 5 ;
+ --back-to-top-icon : url ( "back-to-top.svg" );
+ --back-to-top-bg-color : #fff ;
+ --back-to-top-color : #3eaf7c ;
+ --back-to-top-color-hover : #71cda3 ;
+ --back-to-top-shadow : rgb ( 0 0 0 / 20% );
+}
+
Last Updated:
Contributors: Mr.Hope
catalog
+
+
+
diff --git a/plugins/baidu-analytics.html b/plugins/baidu-analytics.html
new file mode 100644
index 0000000000..3d8b792fbe
--- /dev/null
+++ b/plugins/baidu-analytics.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ baidu-analytics | VuePress Ecosystem
+
+
+
+
+
+
+
+
+
diff --git a/plugins/blog/config.html b/plugins/blog/config.html
new file mode 100644
index 0000000000..7e87163d35
--- /dev/null
+++ b/plugins/blog/config.html
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
+
+ Config | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Type: (page: Page) => Record<string, unknown>
Required: No
Reference:
Details:
Function getting article info.
Article info will be injected in route meta so that they will be available later in client composables.
Type: (page: Page) => boolean
Default: (page) => Boolean(page.filePathRelative) && !page.frontmatter.home
Reference:
Details:
Page filter, determine whether a page should be included.
By default, all the pages generated from Markdown files but not homepage will be included as articles.
Type: BlogCategoryOptions[]
Required: No Reference: Details: Blog category config, see Blog Category Config Type: BlogTypeOptions[]
Required: No Reference: Details: Blog type config, see Blog Type Config Type: (name: string) => string
Default: (name) => name.replace(/ _/g, '-').replace(/[:?*|\\/<>]/g, "").toLowerCase()
Details: Slugify function, used to convert key name which they are register in routes. Type: boolean
Default: true
Reference: Details: Whether generate excerpt for page. Type: string
Default: <!-- more -->
Reference: Details: Separator used to split excerpt from page content. Type: (page: Page) => boolean
Default: filter
option
Reference:
Details:
Page filter, determine whether the plugin should generate excerpt for it.
TIP
You should use this to skip pages that you don't need to generate excerpt for. E.g.: If users set excerpt
or description
in frontmatter, you may want to use them directly.
Type: (tagName: string) => boolean
Default: () => false
Reference:
Details:
Tags which is considered as custom elements.
This is used to determine whether a tag is a custom element since all unknown tags are removed in excerpt.
Blog category config should be an array, while each item is controlling a "category" rule.
interface BlogCategoryOptions {
+ /**
+ * Unique category name
+ */
+ key : string
+
+ /**
+ * Function getting category from page
+ */
+ getter : ( page : Page ) => string []
+
+ /**
+ * A custom function to sort the pages
+ */
+ sorter ?: ( pageA : Page , pageB : Page ) => number
+
+ /**
+ * Path pattern of page to be registered
+ *
+ * @description `:key` will be replaced by the "slugify" result of the original key
+ *
+ * @default `/:key/`
+ */
+ path ?: string
+
+ /**
+ * Page layout name
+ *
+ * @default ' Layout '
+ */
+ layout ?: string
+
+ /**
+ * Frontmatter
+ */
+ frontmatter ?: ( localePath : string ) => Record < string , string >
+
+ /**
+ * Item page path pattern or custom function to be registered
+ *
+ * @description When filling in a string, `:key` and `:name` will be replaced by the "slugify" result of the original key and name
+ *
+ * @default `/:key/:name/`
+ */
+ itemPath ?: string | (( name : string ) => string )
+
+ /**
+ * Item page layout name
+ *
+ * @default ' Layout '
+ */
+ itemLayout ?: string
+
+ /**
+ * Items Frontmatter
+ */
+ itemFrontmatter ?: ( name : string , localePath : string ) => Record < string , string >
+}
+
Blog type config should be an array, while each item is controlling a "type" rule.
interface BlogTypeOptions {
+ /**
+ * Unique type name
+ */
+ key : string
+
+ /**
+ * A filter function to determine whether a page should be the type
+ */
+ filter : ( page : Page ) => boolean
+
+ /**
+ * A custom function to sort the pages
+ */
+ sorter ?: ( pageA : Page , pageB : Page ) => number
+
+ /**
+ * Page path to be registered
+ *
+ * @default ' /:key/ '
+ */
+ path ?: string
+
+ /**
+ * Layout name
+ *
+ * @default ' Layout '
+ */
+ layout ?: string
+
+ /**
+ * Frontmatter
+ */
+ frontmatter ?: ( localePath : string ) => Record < string , string >
+}
+
You can import the following API from @vuepress/plugin-blog/client
.
Blog category
const useBlogCategory : <
+ T extends Record < string , unknown > = Record < string , unknown >,
+>(
+ key ?: string ,
+) => ComputedRef < BlogCategoryData < T >>
+
Argument key
should be the category unique key.
If no key is passed, the plugin will try to use the key in current path.
Blog category
const useBlogType : <
+ T extends Record < string , unknown > = Record < string , unknown >,
+>(
+ key ?: string ,
+) => ComputedRef < BlogTypeData < T >>
+
Argument key
should be the type unique key.
If no key is passed, the plugin will try to use the key in current path.
Returning values are:
interface Article < T extends Record < string , unknown > = Record < string , unknown >> {
+ /** Article path */
+ path : string
+ /** Article info */
+ info : T
+}
+
+interface BlogCategoryData <
+ T extends Record < string , unknown > = Record < string , unknown >,
+> {
+ /** Category path */
+ path : string
+
+ /**
+ * Only available when current route matches an item path
+ */
+ currentItems ?: Article < T >[]
+
+ /** Category map */
+ map : {
+ /** Unique key under current category */
+ [ key : string ]: {
+ /** Category path of the key */
+ path : string
+ /** Category items of the key */
+ items : Article < T >[]
+ }
+ }
+}
+
+interface BlogTypeData <
+ T extends Record < string , unknown > = Record < string , unknown >,
+> {
+ /** Type path */
+ path : string
+
+ /** Items under current type */
+ items : Article < T >[]
+}
+
Last Updated:
Contributors: Mr.Hope
Guide
+
+
+
diff --git a/plugins/blog/guide.html b/plugins/blog/guide.html
new file mode 100644
index 0000000000..daa2317e5c
--- /dev/null
+++ b/plugins/blog/guide.html
@@ -0,0 +1,254 @@
+
+
+
+
+
+
+
+
+ Guide | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development With @vuepress/plugin-blog
, you can easily bring blog feature into your theme.
The plugin filters all pages using filter
option to drop pages you don't want.
By default, all pages generating from Markdown files but not homepage are considered as articles.
You can fully customize pages to collect through option filter
, which accepts a function (page: Page) => boolean
.
You should set getInfo
option with a function accepting Page
as argument and returning an object containing the info you want.
The plugin will collect all the info you want and write them to routeMeta
field of each page, so you will be able to get this information through Composition API later.
Demo import { blogPlugin } from '@vuepress/plugin-blog'
+
+export default {
+ name: 'vuepress-theme-xxx' ,
+ plugins: [
+ blogPlugin ({
+ filter : ({ filePathRelative , frontmatter }) => {
+ // drop those pages which is NOT generated from file
+ if (! filePathRelative ) return false
+
+ // drop those pages in `archives` directory
+ if ( filePathRelative . startsWith ( 'archives/' )) return false
+
+ // drop those pages which do not use default layout
+ if ( frontmatter . home || frontmatter . layout ) return false
+
+ return true
+ },
+
+ getInfo : ({ frontmatter , git = {}, data = {} }) => {
+ // getting page info
+ const info : Record < string , any > = {
+ author: frontmatter . author || '' ,
+ categories: frontmatter . categories || [],
+ date: frontmatter . date || git . createdTime || null ,
+ tags: frontmatter . tags || [],
+ excerpt: data . excerpt || '' ,
+ }
+
+ return info
+ },
+ }),
+ // other plugins ...
+ ],
+}
+
Basically, you would want 2 types of collection in your blog:
Category:
"Category" means grouping articles with their labels.
For example, each article may have their "categories" and "tags".
Type:
"Type" means identifying articles with conditions.
For example, you may want to describe some of your articles as diary.
After understanding description of these 2 types, you can set category
and type
options, each accepts an array, and each element represents a configuration.
Let's start with 2 examples here.
Imagine you are setting tags for each articles with tag
field in page frontmatter. You want a tag mapping page in /tag/
with TagMap
layout, and group each tag list with tagName in /tag/tagName
with TagList
layout, you probably need a configuration like this:
import { blogPlugin } from '@vuepress/plugin-blog'
+
+export default {
+ name: 'vuepress-theme-xxx' ,
+ plugins: [
+ blogPlugin ({
+ // other options ...
+ category: [
+ {
+ key: 'tag' ,
+ getter : ({ frontmatter }) => frontmatter . tag || [],
+ path: '/tag/' ,
+ layout: 'TagMap' ,
+ frontmatter : () => ({ title: 'Tag page' }),
+ itemPath: '/tag/:name/' ,
+ itemLayout: 'TagList' ,
+ itemFrontmatter : ( name ) => ({ title: `Tag ${ name } ` }),
+ },
+ ],
+ }),
+ // other plugins ...
+ ],
+}
+
Also, you may want to star some of your articles, and display them to visitors. When you are setting star: true
in frontmatter to mark them, you probably need a configuration like this to display them in /star/
path with StarList
layout:
import { blogPlugin } from '@vuepress/plugin-blog'
+
+export default {
+ name: 'vuepress-theme-xxx' ,
+ plugins: [
+ blogPlugin ({
+ // other options ...
+ type: [
+ {
+ key: 'star' ,
+ filter : ({ frontmatter }) => frontmatter . star ,
+ path: '/star/' ,
+ layout: 'StarList' ,
+ frontmatter : () => ({ title: 'Star page' }),
+ },
+ ],
+ }),
+ // other plugins ...
+ ],
+}
+
See, setting these 2 types is easy. For full options, please see Category Config and Type Config .
When generating each page, the plugin will set following information under frontmatter.blog
:
interface BlogFrontmatterOptions {
+ /** Current type of the page */
+ type : 'category' | 'type'
+ /** Unique key under current category or tag */
+ key : string
+ /**
+ * Current category name
+ *
+ * @description Only available in category item page
+ */
+ name ?: string
+}
+
So you can invoke useBlogCategory()
and useBlogType()
directly, and the result will be the category or type bind to current route.
Also, you can pass key
you want as argument, then you will get information bind to that key.
So with node side settings above, you can get information about "tag" and "star" in client side:
TagMap
layout:
< script setup lang = "ts" >
+import { useBlogCategory } from '@vuepress/plugin-blog'
+import { RouteLink } from 'vuepress/client'
+
+const categoryMap = useBlogCategory ( 'tag' )
+</ script >
+
+< template >
+ < div >
+ < h1 > Tag page </ h1 >
+ < ul >
+ < li v-for = " ({ items , path }, name ) in categoryMap . map " >
+ < RouteLink : key = " name " : to = " path " class = "category" >
+ {{ name }}
+ < span class = "category-num" >
+ {{ items . length }}
+ </ span >
+ </ RouteLink >
+ </ li >
+ </ ul >
+ </ div >
+</ template >
+
TagList
layout:
< script setup lang = "ts" >
+import { useBlogCategory } from '@vuepress/plugin-blog'
+
+const categoryMap = useBlogCategory ( 'tag' )
+</ script >
+
+< template >
+ < div >
+ < h1 > Tag page </ h1 >
+ < div class = "category-wrapper" >
+ < RouteLink
+ v-for = " ({ items , path }, name ) in categoryMap . map "
+ : key = " name "
+ : to = " path "
+ class = "category"
+ >
+ {{ name }}
+ < span class = "category-num" >
+ {{ items . length }}
+ </ span >
+ </ RouteLink >
+ </ div >
+ < div class = "article-wrapper" v-if = " categoryMap . currentItems " >
+ < div v-if = " ! categoryMap . currentItems . length " > Nothing in here. </ div >
+ < article
+ v-for = " { info , path } in categoryMap . currentItems "
+ class = "article"
+ @ click = " $router . push ( path ) "
+ >
+ < header class = "title" >
+ {{
+ ( isTimeline
+ ? ` ${ new Date ( info . date ). toLocaleDateString () } : `
+ : '' ) + info . title
+ }}
+ </ header >
+ < hr />
+ < div class = "article-info" >
+ < span v-if = " info . author " class = "author"
+ > Author: {{ info . author }} </ span
+ >
+ < span v-if = " info . date && ! isTimeline " class = "date"
+ > Date: {{ new Date ( info . date ). toLocaleDateString () }} </ span
+ >
+ < span v-if = " info . category " class = "category"
+ > Category: {{ info . category . join ( ', ' ) }} </ span
+ >
+ < span v-if = " info . tag " class = "tag"
+ > Tag: {{ info . tag . join ( ', ' ) }} </ span
+ >
+ </ div >
+ < div v-if = " info . excerpt " class = "excerpt" v-html = " info . excerpt " / >
+ </ article >
+ </ div >
+ </ div >
+</ template >
+
StarList
layout:
< script setup lang = "ts" >
+import { useBlogType } from '@vuepress/plugin-blog/client'
+import { RouteLink } from 'vuepress/client'
+
+import ArticleList from '../components/ArticleList.vue'
+import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
+
+const stars = useBlogType ( 'star' )
+</ script >
+
+< template >
+ < div class = "article-wrapper" v-if = " stars . items " >
+ < div v-if = " ! stars . items . length " > Nothing in here. </ div >
+ < article
+ v-for = " { info , path } in stars . items "
+ class = "article"
+ @ click = " $router . push ( path ) "
+ >
+ < header class = "title" >
+ {{
+ ( isTimeline ? ` ${ new Date ( info . date ). toLocaleDateString () } : ` : '' ) +
+ info . title
+ }}
+ </ header >
+ < hr />
+ < div class = "article-info" >
+ < span v-if = " info . author " class = "author" > Author: {{ info . author }} </ span >
+ < span v-if = " info . date && ! isTimeline " class = "date"
+ > Date: {{ new Date ( info . date ). toLocaleDateString () }} </ span
+ >
+ < span v-if = " info . category " class = "category"
+ > Category: {{ info . category . join ( ', ' ) }} </ span
+ >
+ < span v-if = " info . tag " class = "tag" > Tag: {{ info . tag . join ( ', ' ) }} </ span >
+ </ div >
+ < div v-if = " info . excerpt " class = "excerpt" v-html = " info . excerpt " / >
+ </ article >
+ </ div >
+</ template >
+
For return types, please see Composition API Return Types .
This plugin adds native i18n support, so your settings will be automatically applied to each language.
For example, if user has the following locales' config, and you are setting the "star" example above:
export default {
+ locales: {
+ '/' : {
+ lang: 'en-US' ,
+ },
+ '/zh/' : {
+ lang: 'zh-CN' ,
+ },
+ },
+}
+
Then /zh/star/
and /star/
will both be available, and only articles under the correct locale will appear.
This plugin provides a built-in excerpt generator, which can be enabled by setting excerpt
option to true
.
Excerpt introduction
An excerpt is an HTML fragment that is used to display a short description of an article in the blog list, so the excerpt has the following restrictions:
It doesn't support any unknown tags (including all Vue components) and Vue syntax, so these contents will be removed when generating. If you have custom components (non-Vue components), set isCustomElement
option. Since the snippet is an HTML fragment, you will not be able to import any images via relative paths or aliases, they will be removed directly. If you want to keep images, please use absolute path based on .vuepress/public
or full URL to ensure they can be accessed in other places. The excerpt generator will try to find a valid excerpt separator from markdown contents, if it finds one, it will use content before the separator. The separator is default <!-- more -->
, and you can customize it by setting excerptSeparator
option.
If it cannot find a valid separator, it will parse content from the beginning of markdown file, and stop till its length reaches a preset value. The value is default 300
, and you can customize it by setting excerptLength
option.
To choose which page should generate excerpt, you can use excerptFilter
option.
Example
Normally you may want to use frontmatter.description
if users set them, so you can let filter function return false
if frontmatter.description
is not empty.
Last Updated:
Contributors: Mr.Hope
Config
+
+
+
diff --git a/plugins/blog/index.html b/plugins/blog/index.html
new file mode 100644
index 0000000000..32dc084ecf
--- /dev/null
+++ b/plugins/blog/index.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ blog | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development npm i -D @vuepress/plugin-blog@next
+
import { blogPlugin } from '@vuepress/plugin-blog'
+
+export default {
+ plugins: [
+ blogPlugin ({
+ // options
+ }),
+ ],
+}
+
Last Updated:
Contributors: Mr.Hope
Feed
+
+
+
diff --git a/plugins/catalog.html b/plugins/catalog.html
new file mode 100644
index 0000000000..2bef58ca21
--- /dev/null
+++ b/plugins/catalog.html
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+ catalog | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development The plugin can automatically generate catalog pages and provide catalog components.
npm i -D @vuepress/plugin-catalog@next
+
import { catalogPlugin } from '@vuepress/plugin-catalog'
+
+export default {
+ plugins: [
+ catalogPlugin ({
+ // Your options
+ }),
+ ],
+}
+
First, you should set catalog info in routeMeta:
import { catalogPlugin } from '@vuepress/plugin-catalog'
+
+export default {
+ extendsPage : ( page ) => {
+ // set catalog info in routeMeta
+ page . routeMeta = {
+ // catalog title
+ title: page . title ,
+ // ... other information
+ }
+ },
+}
+
You can then import defineCatalogInfoGetter
from @vuepress/plugin-catalog/client
and use it in client config fileopen in new window to extract catalog info from meta.
import { defineCatalogInfoGetter } from '@vuepress/plugin-catalog/client'
+
+export default {
+ setup : () => {
+ defineCatalogInfoGetter (( meta ) =>
+ meta . title ? { title: meta . title } : null ,
+ )
+ },
+}
+
Catalog info should contains:
title
: catalog titleorder
: catalog order (optional)content
: catalog content component (optional)Sorting with order
The plugin will sort pages by order
in the following way:
// order positive numbers from small to large
+Project with order 1
+Project with order 2
+...
+Project with order 10
+...
+// Project without order
+Project without order
+Project without order
+...
+// order negative numbers from small to large
+Project with order -10
+// ...
+Project with order -2
+Project with order -1
+
Type: 1 | 2 | 3
Default: 3
Details: Max depth of catalog items. Type: boolean
Default: false
Details: Whether show index for catalog Type: string
Required: No Details: Component name to use as catalog. Built-in Supported Languages Simplified Chinese (zh-CN)Traditional Chinese (zh-TW)English (United States) (en-US)German (de-DE)German (Australia) (de-AT)Russian (ru-RU)Ukrainian (uk-UA)Vietnamese (vi-VN)Portuguese (Brazil) (pt-BR)Polish (pl-PL)French (fr-FR)Spanish (es-ES)Slovak (sk-SK)Japanese (ja-JP)Turkish (tr-TR)Korean (ko-KR)Finnish (fi-FI)Indonesian (id-ID)Dutch (nl-NL)interface CatalogInfo {
+ /** Catalog title */
+ title : string
+ /** Catalog order */
+ order ?: number
+ /** Catalog content */
+ content ?: Component
+}
+
+type CatalogInfoGetter = ( meta : Record < string , unknown >) => CatalogInfo | null
+
+const defineCatalogInfoGetter : ( options : CatalogInfoGetter ) => void
+
Customize how to extract catalog info from meta.
Details:
The plugin will globally register a <Catalog />
component by default (unless you set the component
option).
You can use <Catalog />
in the theme layout or directly in the Markdown file.
The component supports four props:
level
: Change the display depth (maximum support 3 levels), default is 3
.base
: Display catalog of the specified folder, default is the current folder directory.index
: Add an index number to the directory item, default is no number.hideHeading
: Hide the component title, default is to display the Catalog
title.You can customize the style of catalog via CSS variables:
:root {
+ --catalog-bg-color : #fff ;
+ --catalog-bg-secondary-color : #f8f8f8 ;
+ --catalog-border-color : #e5e5e5 ;
+ --catalog-active-color : #3eaf7c ;
+ --catalog-hover-color : #71cda3 ;
+}
+
Last Updated:
Contributors: Mr.Hope
back-to-top container
+
+
+
diff --git a/plugins/container.html b/plugins/container.html
new file mode 100644
index 0000000000..53f47d9df0
--- /dev/null
+++ b/plugins/container.html
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+ container | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Register markdown custom containers in your VuePress site.
This plugin simplifies the use of markdown-it-containeropen in new window , but also retains its original capabilities.
The Custom Containers of default theme is powered by this plugin.
npm i -D @vuepress/plugin-container@next
+
import { containerPlugin } from '@vuepress/plugin-container'
+
+export default {
+ plugins: [
+ containerPlugin ({
+ // options
+ }),
+ ],
+}
+
::: < type > [ info ]
+[ content ]
+:::
+
The type
is required and should be specified via type option. The info
is optional, and the default value can be specified via defaultInfo
in locales option. The content
can be any valid markdown content. TIP
This plugin can be used multiple times to support different types of containers.
Type: Record<string, { defaultInfo: string }>
Details:
The default info
of the container in different locales.
If this option is not specified, the default info
will fallback to the uppercase of the type option.
Example:
export default {
+ plugins: [
+ containerPlugin ({
+ type: 'tip' ,
+ locales: {
+ '/' : {
+ defaultInfo: 'TIP' ,
+ },
+ '/zh/' : {
+ defaultInfo: '提示' ,
+ },
+ },
+ }),
+ ],
+}
+
( info : string ): string =>
+ `<div class="custom-container ${ type } "> ${ info ? `<p class="custom-container-title"> ${ info } </p>` : '' } \n `
+
Details:
A function to render the starting tag of the container.
The first param is the info
part of container syntax .
This option will not take effect if you don't specify the after option.
(): string => '</div> \n '
+
Details:
A function to render the ending tag of the container.
The first param is the info
part of container syntax .
This option will not take effect if you don't specify the before option.
type MarkdownItContainerRenderFunction = (
+ tokens : Token [],
+ index : number ,
+ options : any ,
+ env : MarkdownEnv ,
+ self : Renderer ,
+) => string
+
Last Updated:
Contributors: Mr.Hope
catalog copy-code
+
+
+
diff --git a/plugins/copy-code.html b/plugins/copy-code.html
new file mode 100644
index 0000000000..28f0bce4ce
--- /dev/null
+++ b/plugins/copy-code.html
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+ copy-code | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development This plugin will automatically add a copy button to the top right corner of each code block on PC devices.
The default selector matches @vuepress/theme-default
, so you might need to change it when integrating your own theme.
npm i -D @vuepress/plugin-copy-code@next
+
import { copyCodePlugin } from '@vuepress/plugin-copy-code'
+
+export default {
+ plugins: [
+ copyCodePlugin ({
+ // options
+ }),
+ ],
+}
+
Type: number
Default: 800
Details:
The delay of registering copy code buttons, in ms.
If the theme you are using has a switching animation, it is recommended to configure this option to Switch animation duration + 200
.
Built-in Supported Languages Simplified Chinese (zh-CN)Traditional Chinese (zh-TW)English (United States) (en-US)German (de-DE)German (Australia) (de-AT)Russian (ru-RU)Ukrainian (uk-UA)Vietnamese (vi-VN)Portuguese (Brazil) (pt-BR)Polish (pl-PL)French (fr-FR)Spanish (es-ES)Slovak (sk-SK)Japanese (ja-JP)Turkish (tr-TR)Korean (ko-KR)Finnish (fi-FI)Indonesian (id-ID)Dutch (nl-NL)You can customize the icon of the copy button via CSS variables:
:root {
+ --code-copy-icon : url ( "copy-button.svg" );
+ --code-copied-icon : url ( "copied-button.svg" );
+ --copy-code-color : #9e9e9e ;
+ --copy-code-hover : rgb ( 0 0 0 / 50% );
+}
+
Last Updated:
Contributors: Mr.Hope
container copyright
+
+
+
diff --git a/plugins/copyright.html b/plugins/copyright.html
new file mode 100644
index 0000000000..29f9ddf3f4
--- /dev/null
+++ b/plugins/copyright.html
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+ copyright | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development This plugin can automatically append copyright information when visitors copy content from your site, and can also prohibit site copying or selection.
npm i -D @vuepress/plugin-copyright@next
+
import { copyrightPlugin } from '@vuepress/plugin-copyright'
+
+export default {
+ plugins: [
+ copyrightPlugin ({
+ // options
+ }),
+ ],
+}
+
This plugin is disabled globally by default , you can:
Manually enable it by setting copy: true
in page frontmatter Set global: true
in plugin options to enable it globally, and set copy: false
in page frontmatter to disable it. To avoid disturbing visitors, copyright information will be appended only when the copied content length is greater than 100. Set triggerLength
in plugin options if you want to change this threshold, or via copy.triggerLength
in page frontmatter.
You can set default author and license information via author
and license
in plugin options.
If your site have different authors and license in different pages, you can set authorGetter
and licenseGetter
with function (page: Page) => string
that takes the current page object as parameter and returns the corresponding information.
The plugin will generate copyright information from author, license, and page link via template by default, and append it when copying. If you think that this is not flexible enough, you can set copyrightGetter
option to return a completely customized information with Page object or return null to use the default template.
If you want to prevent users copying long content, you can set maxLength
in plugin options to customize this limit, or via copy.maxLength
in page frontmatter.
If you don't want users to copy your entire site or specific page text, you can set disableCopy
in plugin options or copy.disableCopy
in page frontmatter, the latter has higher priority. If you don't want users to select your entire site or specific page text, you can set disableSelection
in plugin options or copy.disableSelection
in page frontmatter. This option has higher priority. Type: string
Details: Default author Information Type: string
Details: Default license Information Type: (page: Page) => string | null
Details: Author getter Type: (page: Page) => string | null
Details: License getter Type: (page: Page) => string | null
Details: Copyright getter Type: number
Default: 100
Details: Min content length triggering copyright append Type: number
Default: 0
Details: Max content length which allows to copy, 0
means no limit. Type: boolean
Default: false
Details: Whether enable globally. Type: boolean
Default: false
Details: Disable copy Type: boolean
Default: false
Details: Disable selection Type: string
Details: Canonical deploy location. Example
If you are deploying same content under https://myblog.com
and https://blog.com/username/
, you may want to prefer one site as reference link.
If you prefer the first one, you should set canonical
to https://myblog.com
If you prefer the second one, you should set canonical
to https://blog.com/username/
So copyright message triggered on another site also points to your preferred site.
Built-in Supported Languages Simplified Chinese (zh-CN)Traditional Chinese (zh-TW)English (United States) (en-US)German (de-DE)German (Australia) (de-AT)Russian (ru-RU)Ukrainian (uk-UA)Vietnamese (vi-VN)Portuguese (Brazil) (pt-BR)Polish (pl-PL)French (fr-FR)Spanish (es-ES)Slovak (sk-SK)Japanese (ja-JP)Turkish (tr-TR)Korean (ko-KR)Finnish (fi-FI)Indonesian (id-ID)Dutch (nl-NL)Type: number
Default: 100
Details: Min content length triggering copyright append Type: number
Default: 0
Details: Max content length which allows to copy, 0
means no limit. Type: boolean
Default: false
Details: Disable copy Type: boolean
Default: false
Details: Disable selection Last Updated:
Contributors: Mr.Hope
copy-code external-link-icon
+
+
+
diff --git a/plugins/docsearch.html b/plugins/docsearch.html
new file mode 100644
index 0000000000..e35b125b04
--- /dev/null
+++ b/plugins/docsearch.html
@@ -0,0 +1,241 @@
+
+
+
+
+
+
+
+
+ docsearch | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Integrate Algolia DocSearchopen in new window into VuePress, which can provide search to your documentation site.
TIP
Default theme will add DocSearch to the navbar once you configure this plugin correctly.
This plugin may not be used directly in other themes, so you'd better refer to the documentation of your theme for more details.
npm i -D @vuepress/plugin-docsearch@next
+
import { docsearchPlugin } from '@vuepress/plugin-docsearch'
+
+export default {
+ plugins: [
+ docsearchPlugin ({
+ // options
+ }),
+ ],
+}
+
You need to submit the URL of your siteopen in new window to join the DocSearch program. The DocSearch team will send apiKey and indexName to your email once the index is generated. Then you can configure this plugin to enable DocSearch in VuePress.
Alternatively, you can run your own crawleropen in new window to generate the index, and then use your own appId , apiKey and indexName to configure this plugin.
Official crawler config new Crawler ({
+ appId: 'YOUR_APP_ID' ,
+ apiKey: 'YOUR_API_KEY' ,
+ rateLimit: 8 ,
+ startUrls: [
+ // These are urls which algolia start to craw
+ // If your site is divided in to mutiple parts,
+ // you may want to set mutiple entry links
+ 'https://YOUR_WEBSITE_URL/' ,
+ ],
+ sitemaps: [
+ // if you are using sitemap plugins (e.g.: @vuepress-plugin/sitemap), you may provide one
+ 'https://YOUR_WEBSITE_URL/sitemap.xml' ,
+ ],
+ ignoreCanonicalTo: false ,
+ exclusionPatterns: [
+ // You can use this to stop algolia crawing some paths
+ ],
+ discoveryPatterns: [
+ // These are urls which algolia looking for,
+ 'https://YOUR_WEBSITE_URL/**' ,
+ ],
+ // Crawler schedule, set it according to your docs update frequency
+ schedule: 'at 02:00 every 1 day' ,
+ actions: [
+ // you may have mutiple actions, especially when you are deploying mutiple docs under one domain
+ {
+ // name the index with name you like
+ indexName: 'YOUR_INDEX_NAME' ,
+ // paths where the index take effect
+ pathsToMatch: [ 'https://YOUR_WEBSITE_URL/**' ],
+ // controls how algolia extracts records from your site
+ recordExtractor : ({ $ , helpers }) => {
+ // options for @vuepress/theme-default
+ return helpers . docsearch ({
+ recordProps: {
+ lvl0: {
+ selectors: '.sidebar-heading.active' ,
+ defaultValue: 'Documentation' ,
+ },
+ lvl1: '.theme-default-content h1' ,
+ lvl2: '.theme-default-content h2' ,
+ lvl3: '.theme-default-content h3' ,
+ lvl4: '.theme-default-content h4' ,
+ lvl5: '.theme-default-content h5' ,
+ lvl6: '.theme-default-content h6' ,
+ content: '.theme-default-content p, .theme-default-content li' ,
+ },
+ indexHeadings: true ,
+ })
+ },
+ },
+ ],
+ initialIndexSettings: {
+ // controls how index are initialized
+ // only has effects before index are initialize
+ // you may need to delete your index and recraw after modification
+ YOUR_INDEX_NAME: {
+ attributesForFaceting: [ 'type' , 'lang' ],
+ attributesToRetrieve: [ 'hierarchy' , 'content' , 'anchor' , 'url' ],
+ attributesToHighlight: [ 'hierarchy' , 'hierarchy_camel' , 'content' ],
+ attributesToSnippet: [ 'content:10' ],
+ camelCaseAttributes: [ 'hierarchy' , 'hierarchy_radio' , 'content' ],
+ searchableAttributes: [
+ 'unordered(hierarchy_radio_camel.lvl0)' ,
+ 'unordered(hierarchy_radio.lvl0)' ,
+ 'unordered(hierarchy_radio_camel.lvl1)' ,
+ 'unordered(hierarchy_radio.lvl1)' ,
+ 'unordered(hierarchy_radio_camel.lvl2)' ,
+ 'unordered(hierarchy_radio.lvl2)' ,
+ 'unordered(hierarchy_radio_camel.lvl3)' ,
+ 'unordered(hierarchy_radio.lvl3)' ,
+ 'unordered(hierarchy_radio_camel.lvl4)' ,
+ 'unordered(hierarchy_radio.lvl4)' ,
+ 'unordered(hierarchy_radio_camel.lvl5)' ,
+ 'unordered(hierarchy_radio.lvl5)' ,
+ 'unordered(hierarchy_radio_camel.lvl6)' ,
+ 'unordered(hierarchy_radio.lvl6)' ,
+ 'unordered(hierarchy_camel.lvl0)' ,
+ 'unordered(hierarchy.lvl0)' ,
+ 'unordered(hierarchy_camel.lvl1)' ,
+ 'unordered(hierarchy.lvl1)' ,
+ 'unordered(hierarchy_camel.lvl2)' ,
+ 'unordered(hierarchy.lvl2)' ,
+ 'unordered(hierarchy_camel.lvl3)' ,
+ 'unordered(hierarchy.lvl3)' ,
+ 'unordered(hierarchy_camel.lvl4)' ,
+ 'unordered(hierarchy.lvl4)' ,
+ 'unordered(hierarchy_camel.lvl5)' ,
+ 'unordered(hierarchy.lvl5)' ,
+ 'unordered(hierarchy_camel.lvl6)' ,
+ 'unordered(hierarchy.lvl6)' ,
+ 'content' ,
+ ],
+ distinct: true ,
+ attributeForDistinct: 'url' ,
+ customRanking: [
+ 'desc(weight.pageRank)' ,
+ 'desc(weight.level)' ,
+ 'asc(weight.position)' ,
+ ],
+ ranking: [
+ 'words' ,
+ 'filters' ,
+ 'typo' ,
+ 'attribute' ,
+ 'proximity' ,
+ 'exact' ,
+ 'custom' ,
+ ],
+ highlightPreTag: '<span class="algolia-docsearch-suggestion--highlight">' ,
+ highlightPostTag: '</span>' ,
+ minWordSizefor1Typo: 3 ,
+ minWordSizefor2Typos: 7 ,
+ allowTyposOnNumericTokens: false ,
+ minProximity: 1 ,
+ ignorePlurals: true ,
+ advancedSyntax: true ,
+ attributeCriteriaComputedByMinProximity: true ,
+ removeWordsIfNoResults: 'allOptional' ,
+ },
+ },
+})
+
The above recordProps
is the configuration used for the default theme. You can modify them according to the theme you are using.
Notice that the initialIndexSettings.YOUR_INDEX_NAME.attributesForFaceting
fields must include 'lang'
to make this plugin work properly.
TIP
If you are not using default theme, or you meet any problems when using docsearch, you can also check the above example crawler config, and ahead to Algolia Crawleropen in new window , and edit your config with 'Editor' panel in project sidebar.
Type: Record<string, DocsearchPluginOptions>
Details:
Options of this plugin in different locales.
All other options of this plugin are acceptable in locale config.
Example:
export default {
+ plugins: [
+ docsearchPlugin ({
+ appId: '<APP_ID>' ,
+ apiKey: '<API_KEY>' ,
+ indexName: '<INDEX_NAME>' ,
+ locales: {
+ '/' : {
+ placeholder: 'Search Documentation' ,
+ translations: {
+ button: {
+ buttonText: 'Search Documentation' ,
+ },
+ },
+ },
+ '/zh/' : {
+ placeholder: '搜索文档' ,
+ translations: {
+ button: {
+ buttonText: '搜索文档' ,
+ },
+ },
+ },
+ },
+ }),
+ ],
+}
+
Type: string
Default: base
Details:
The base path of the search index.
If you are deploying your site to multiple domains, you don't need to submit all of them to DocSearch and generate search index separately. You could choose one of the domains as the index domain , and only submit the index domain to Docsearch for crawling search index. Then, you could reuse the search index across all deployments.
However, if the base of your deployments are different for different domains, you need to set the option to the base of your index domain , so that other deployments could reuse the search index correctly.
Type: boolean
Default: true
Details:
Whether to inject the default styles of DocSearch or not.
If you think the default styles of DocSearch is not compatible with your site, you can try to override the default styles, or set this option to false
to totally exclude the default styles.
When this option is disabled, you need to import your own styles for DocSearch. Also notice that all styles customization in Styles section would be unavailable.
You can customize styles via CSS variables that provided by @docsearch/cssopen in new window :
:root {
+ --docsearch-primary-color : rgb ( 84 , 104 , 255 );
+ --docsearch-text-color : rgb ( 28 , 30 , 33 );
+ --docsearch-spacing : 12px ;
+ --docsearch-icon-stroke-width : 1.4 ;
+ --docsearch-highlight-color : var ( --docsearch-primary-color );
+ --docsearch-muted-color : rgb ( 150 , 159 , 175 );
+ --docsearch-container-background : rgba ( 101 , 108 , 133 , 0.8 );
+ --docsearch-logo-color : rgba ( 84 , 104 , 255 );
+
+ /* modal */
+ --docsearch-modal-width : 560px ;
+ --docsearch-modal-height : 600px ;
+ --docsearch-modal-background : rgb ( 245 , 246 , 247 );
+ --docsearch-modal-shadow : inset 1px 1px 0 0 rgba ( 255 , 255 , 255 , 0.5 ), 0 3px
+ 8px 0 rgba ( 85 , 90 , 100 , 1 );
+
+ /* searchbox */
+ --docsearch-searchbox-height : 56px ;
+ --docsearch-searchbox-background : rgb ( 235 , 237 , 240 );
+ --docsearch-searchbox-focus-background : #fff ;
+ --docsearch-searchbox-shadow : inset 0 0 0 2px var ( --docsearch-primary-color );
+
+ /* hit */
+ --docsearch-hit-height : 56px ;
+ --docsearch-hit-color : rgb ( 68 , 73 , 80 );
+ --docsearch-hit-active-color : #fff ;
+ --docsearch-hit-background : #fff ;
+ --docsearch-hit-shadow : 0 1px 3px 0 rgb ( 212 , 217 , 225 );
+
+ /* key */
+ --docsearch-key-gradient : linear-gradient (
+ -225deg ,
+ rgb ( 213 , 219 , 228 ) 0% ,
+ rgb ( 248 , 248 , 248 ) 100%
+ );
+ --docsearch-key-shadow : inset 0 -2px 0 0 rgb ( 205 , 205 , 230 ), inset 0 0 1px 1px
+ #fff , 0 1px 2px 1px rgba ( 30 , 35 , 90 , 0.4 );
+
+ /* footer */
+ --docsearch-footer-height : 44px ;
+ --docsearch-footer-background : #fff ;
+ --docsearch-footer-shadow : 0 -1px 0 0 rgb ( 224 , 227 , 232 ), 0 -3px 6px 0 rgba ( 69 , 98 , 155 , 0.12 );
+}
+
Details:
This plugin will register a <Docsearch />
component globally, and you can use it without any props.
Put this component to where you want to place the docsearch button. For example, default theme puts this component to the end of the navbar.
TIP
This component is mainly used for theme development. You don't need to use it directly in most cases.
Last Updated:
Contributors: Mr.Hope
search
+
+
+
diff --git a/plugins/external-link-icon.html b/plugins/external-link-icon.html
new file mode 100644
index 0000000000..6c5124d54a
--- /dev/null
+++ b/plugins/external-link-icon.html
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+ external-link-icon | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development This plugin will add an icon to the external link in your markdown content, i.e. open in new window
This plugin has been integrated into the default theme.
npm i -D @vuepress/plugin-external-link-icon@next
+
import { externalLinkIconPlugin } from '@vuepress/plugin-external-link-icon'
+
+export default {
+ plugins: [
+ externalLinkIconPlugin ({
+ // options
+ }),
+ ],
+}
+
Type: Record<string, { openInNewWindow: string }>
Details:
The a11y text of the external link icon in different locales.
If this option is not specified, it will fallback to default text.
Example:
export default {
+ plugins: [
+ externalLinkIconPlugin ({
+ locales: {
+ '/' : {
+ openInNewWindow: 'open in new window' ,
+ },
+ '/zh/' : {
+ openInNewWindow: '在新窗口打开' ,
+ },
+ },
+ }),
+ ],
+}
+
You can customize the style of the external link icon via CSS variables:
:root {
+ --external-link-icon-color : #aaa ;
+}
+
TIP
This component is mainly used for theme development. You don't need to use it directly in most cases.
Last Updated:
Contributors: Mr.Hope
copyright medium-zoom
+
+
+
diff --git a/plugins/feed/channel.html b/plugins/feed/channel.html
new file mode 100644
index 0000000000..c3dac41741
--- /dev/null
+++ b/plugins/feed/channel.html
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+ Channel Config | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development The channel plugin option is used to config the feed channel.
Type: string
Default: SiteConfig.title
Channel title
Type: string
Default: Deployment link (generated by options.hostname
and context.base
) Channel address
Type: string
Default: SiteConfig.description
Channel description
Type: string
Default:
siteConfig.locales['/'].lang
If the above is not provided, fall back to "en-US"
The language of the channel
Channel copyright information
Type: string
(must be a valid Date ISOString) Default: time when the plugin is called each time Recommended to set manually: Yes Publish date of the Channel
Type: string
(must be a valid Date ISOString) Default: time when the plugin is called each time Last update time of channel content
Type: number
Recommended to set manually: Yes The effective time of the content. It's the time to keep the cache after request without making new requests.
Type: string
Recommended to set manually: Yes A picture presenting the channel. A square picture with a size not smaller than 512×512 is recommended.
Type: string
Recommended to set manually: Yes An icon representing a channel, a square picture, with not less than 128×128 in size, and transparent background color is recommended.
Type: FeedAuthor
Recommended to set manually: Yes The author of the channel.
FeedAuthor format interface FeedAuthor {
+ /** Author name */
+ name : string
+ /** Author's email */
+ email ?: string
+ /** Author's site */
+ url ?: string
+ /**
+ * Author's avatar address
+ *
+ * Square, preferably not less than 128×128 with transparent background
+ */
+ avatar ?: string
+}
+
Link to Websub. Websub requires a server backend, which is inconsistent with VuePress, so ignore it if there is no special need.
Last Updated:
Contributors: Mr.Hope
Frontmatter Config Feed Getter
+
+
+
diff --git a/plugins/feed/config.html b/plugins/feed/config.html
new file mode 100644
index 0000000000..0970ade7b9
--- /dev/null
+++ b/plugins/feed/config.html
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+ Plugin Config | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Type: string
Required: Yes The domain name of the deployment site.
Type: boolean
Default: false
Whether to output Atom syntax files.
Type: boolean
Default: false
Whether output JSON syntax files.
Type: boolean
Default: false
Whether to output RSS syntax files.
A large image/icon of the feed, probably used as banner.
A small icon of the feed, probably used as favicon.
Set the maximum number of items in the feed. After all pages are sorted, the first count
items will be intercepted.
If your site has a lot of articles, you may consider this option to reduce feed file size.
Type: (RegExp | string)[] | (tagName: string) => boolean
Custom element or component which should be preserved in feed.
By default, all unknown tags will be removed.
A custom filter function, used to filter feed items.
Custom sorter function for feed items.
The default sorting behavior is by file adding time coming from git (needs @vuepress/plugin-git
).
TIP
You should enable @vuepress/plugin-git
to get the newest created pages as feed items. Otherwise, the feed items will be sorted by the default order of pages in VuePress.
channel
option is used to config Feed Channels .
For available options, please see Config → Channel
Type: boolean
Default: false
Whether enabled in devServer.
TIP
For performance reasons, we do not provide hot reload. Reboot your devServer to sync your changes.
Type: string
Default: "http://localhost:${port}"
Hostname to use in devServer
Type: string
Default: "atom.xml"
Atom syntax output filename, relative to dest folder.
Type: string
Default: Content of @vuepress/plugin-feed/templates/atom.xsl
Atom xsl template file content.
Type: string
Default: "atom.xsl"
Atom xsl filename, relative to dest folder.
Type: string
Default: "feed.json"
JSON syntax output filename, relative to dest folder.
Type: string
Default: "rss.xml"
RSS syntax output filename, relative to dest folder.
Type: string
Default: Content of @vuepress/plugin-feed/templates/rss.xsl
RSS xsl template file content.
Type: string
Default: "rss.xsl"
RSS syntax xsl filename, relative to dest folder.
Feed generation controller, see Feed Getter .
The plugin has a built-in getter, only set this if you want full control of feed generation.
Type: Record<string, BaseFeedOptions>
You can use it to specific options for each locale.
Any options above are supported except hostname
.
Last Updated:
Contributors: Mr.Hope
Guide Frontmatter Config
+
+
+
diff --git a/plugins/feed/frontmatter.html b/plugins/feed/frontmatter.html
new file mode 100644
index 0000000000..07f3956c04
--- /dev/null
+++ b/plugins/feed/frontmatter.html
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+ Frontmatter Config | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development You can control each feed item generation by setting page frontmatter.
By default, all articles are added to the feed stream. Set feed: false
in frontmatter to remove a page from feed.
Automatically generated by VuePress, defaults to the h1 content of the page
Description of the page
Date when the page was published
Whether the page is an article
If this is set to false
, the page will not be included in the final feed.
Page copyright information
Image used as page cover , should be full link or absolute link.
The title of the feed item
Description of the feed item
The content of the feed item
Type: FeedAuthor[] | FeedAuthor
The author of the feed item
FeedAuthor format interface FeedAuthor {
+ /**
+ * Author name
+ */
+ name ?: string
+
+ /**
+ * Author email
+ */
+ email ?: string
+
+ /**
+ * Author site
+ *
+ * @description json format only
+ */
+ url ?: string
+
+ /**
+ * Author avatar
+ *
+ * @description json format only
+ */
+ avatar ?: string
+}
+
Type: FeedContributor[] | FeedContributor
Contributors to feed item
FeedContributor format interface FeedContributor {
+ /**
+ * Author name
+ */
+ name ?: string
+
+ /**
+ * Author email
+ */
+ email ?: string
+
+ /**
+ * Author site
+ *
+ * @description json format only
+ */
+ url ?: string
+
+ /**
+ * Author avatar
+ *
+ * @description json format only
+ */
+ avatar ?: string
+}
+
The identifier of feed item, used to identify the feed item.
You should ensure every feed has a unique guid.
Last Updated:
Contributors: Mr.Hope
Plugin Config Channel Config
+
+
+
diff --git a/plugins/feed/getter.html b/plugins/feed/getter.html
new file mode 100644
index 0000000000..8ecb7a8dce
--- /dev/null
+++ b/plugins/feed/getter.html
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+ Feed Getter | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development You can take full control of feed items generation by setting getter
in the plugin options.
Type: (page: Page) => string
Item title getter
Type: (page: Page) => string
Item link getter
Type: (page: Page) => string | undefined
Item description getter
TIP
Due to Atom support HTML in summary, so you can return HTML content here if possible, but the content must start with mark html:
.
Type: (page: Page) => string
Item content getter
Type: (page: Page) => FeedAuthor[]
Item author getter.
The getter should return an empty array when author information is missing.
FeedAuthor format interface FeedAuthor {
+ /**
+ * Author name
+ */
+ name ?: string
+
+ /**
+ * Author email
+ */
+ email ?: string
+
+ /**
+ * Author site
+ *
+ * @description json format only
+ */
+ url ?: string
+
+ /**
+ * Author avatar
+ *
+ * @description json format only
+ */
+ avatar ?: string
+}
+
Type: (page: Page) => FeedCategory[] | undefined
Item category getter.
FeedCategory format interface FeedCategory {
+ /**
+ * Category Name
+ */
+ name : string
+
+ /**
+ * A string that identifies a categorization taxonomy
+ *
+ * @description rss format only
+ */
+ domain ?: string
+
+ /**
+ * the categorization scheme via a URI
+ *
+ * @description atom format only
+ */
+ scheme ?: string
+}
+
Type: (page: Page) => FeedEnclosure | undefined
Item enclosure getter.
FeedEnclosure format interface FeedEnclosure {
+ /**
+ * Enclosure link
+ */
+ url : string
+
+ /**
+ * what its type is
+ *
+ * @description should be a standard MIME Type, rss format only
+ */
+ Type : string
+
+ /**
+ * Size in bytes
+ *
+ * @description rss format only
+ */
+ length ?: number
+}
+
Type: (page: Page) => Date | undefined
Item release date getter
Type: (page: Page) => Date
Item last update date getter
Type: (page: Page) => string
Item Image Getter
Ensure it's returning a full URL
Type: (page: Page) => FeedContributor[]
Item Contributor Getter
The getter should return an empty array when contributor information is missing.
FeedContributor format interface FeedContributor {
+ /**
+ * Author name
+ */
+ name ?: string
+
+ /**
+ * Author email
+ */
+ email ?: string
+
+ /**
+ * Author site
+ *
+ * @description json format only
+ */
+ url ?: string
+
+ /**
+ * Author avatar
+ *
+ * @description json format only
+ */
+ avatar ?: string
+}
+
Type: (page: Page) => string | undefined
Item copyright getter
Last Updated:
Contributors: Mr.Hope
Channel Config
+
+
+
diff --git a/plugins/feed/guide.html b/plugins/feed/guide.html
new file mode 100644
index 0000000000..e3c472c8e6
--- /dev/null
+++ b/plugins/feed/guide.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ Guide | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development The plugin can generate feed files in the following three formats for you:
Please set atom
, json
or rss
to true
in the plugin options according to the formats you want to generate.
To correctly generate feed links, you need to set hostname
in the plugin options,
When you open the feed file in browser, we magically convert atom and rss feed xml to human readable html via xsl template. Check atomopen in new window and rssopen in new window feed of this site as an example!
If you want to preview your feed in devServer, set devServer: true
in plugin options. You may also need to set devHostname
if you are not using the default http://localhost:{port}
.
You can customize the feed channel information by setting the channel
option.
We recommend the following settings:
Convert the date of creating the feed to ISOString and write it into channel.pubDate
The update period of the content set in channel.ttl
(unit: minutes) Set copyright information via channel.copyright
Set the channel author via channel.author
. For detailed options and their default values, see Channel Config
By default, all articles are added to the feed stream.
You can set feed
and other options in page frontmatter to control contents of feed item. See Frontmatter Config for how they are converted.
You can take full control of feed items generation by configuring the getter
in the plugin options. For detailed options and their default values, see Configuration → Feed Getter .
The plugin generates separate feeds for each language.
You can provide different settings for different languages via locales
in the plugin options.
Last Updated:
Contributors: Mr.Hope
Plugin Config
+
+
+
diff --git a/plugins/feed/index.html b/plugins/feed/index.html
new file mode 100644
index 0000000000..130fee4100
--- /dev/null
+++ b/plugins/feed/index.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ feed | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development npm i -D @vuepress/plugin-feed@next
+
import { feedPlugin } from '@vuepress/plugin-feed'
+
+export default {
+ plugins: [
+ feedPlugin ({
+ // options
+ }),
+ ],
+}
+
Last Updated:
Contributors: Mr.Hope
Blog
+
+
+
diff --git a/plugins/git.html b/plugins/git.html
new file mode 100644
index 0000000000..1957251271
--- /dev/null
+++ b/plugins/git.html
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+ git | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development This plugin will collect git information of your pages, including the created and updated time, the contributors, etc.
The lastUpdated and contributors of default theme is powered by this plugin.
This plugin is mainly used to develop themes. You won't need to use it directly in most cases.
npm i -D @vuepress/plugin-git@next
+
import { gitPlugin } from '@vuepress/plugin-git'
+
+export default {
+ plugins: [
+ gitPlugin ({
+ // options
+ }),
+ ],
+}
+
This plugin requires your project to be inside a Git Repositoryopen in new window , so that it can collect information from the commit history.
You should ensure all commits are available when building your site. For example, CI workflows usually clone your repository with --depth 1open in new window to avoid fetching all commits, so you should disable the behavior to make this plugin work properly in CI.
WARNING
This plugin will significantly slow down the speed of data preparation, especially when you have a lot of pages. You can consider disabling this plugin in dev
mode to get better development experience.
---
+gitInclude :
+ - relative/path/to/file1
+ - relative/path/to/file2
+---
+
This plugin will add a git
field to page data.
After using this plugin, you can get the collected git information in page data:
import type { GitPluginPageData } from '@vuepress/plugin-git'
+import { usePageData } from 'vuepress/client'
+
+export default {
+ setup () {
+ const page = usePageData < GitPluginPageData >()
+ console . log ( page . value . git )
+ },
+}
+
interface GitContributor {
+ name : string
+ email : string
+ commits : number
+}
+
Last Updated:
Contributors: Mr.Hope
active-header-links palette
+
+
+
diff --git a/plugins/google-analytics.html b/plugins/google-analytics.html
new file mode 100644
index 0000000000..47b092a69c
--- /dev/null
+++ b/plugins/google-analytics.html
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+ google-analytics | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Integrate Google Analyticsopen in new window into VuePress.
This plugin will import gtag.jsopen in new window for Google Analytics 4open in new window .
npm i -D @vuepress/plugin-google-analytics@next
+
import { googleAnalyticsPlugin } from '@vuepress/plugin-google-analytics'
+
+export default {
+ plugins: [
+ googleAnalyticsPlugin ({
+ // options
+ }),
+ ],
+}
+
Google Analytics will automatically collect some eventsopen in new window , such as page_view
, first_visit
, etc.
So if you only want to collect some basic data of your site, you don't need to do anything else except setting the Measurement ID correctly.
After using this plugin, the global gtag()
function is available on the window
object, and you can use it for custom events reportingopen in new window .
Type: string
Details:
The Measurement ID of Google Analytics 4, which should start with 'G-'
.
You can follow the instructions hereopen in new window to find your Measurement ID. Notice the difference between Google Analytics 4 Measurement ID (i.e. "G-" ID) and Universal Analytics Tracking ID (i.e. "UA-" ID).
Example:
export default {
+ plugins: [
+ googleAnalyticsPlugin ({
+ id: 'G-XXXXXXXXXX' ,
+ }),
+ ],
+}
+
export default {
+ plugins: [
+ googleAnalyticsPlugin ({
+ id: 'G-XXXXXXXXXX' ,
+ debug: true ,
+ }),
+ ],
+}
+
Last Updated:
Contributors: Mr.Hope
baidu-analytics
+
+
+
diff --git a/plugins/index.html b/plugins/index.html
new file mode 100644
index 0000000000..bcb1a40ac7
--- /dev/null
+++ b/plugins/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ Plugins | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Last Updated:
Contributors: Mr.Hope
+
+
+
diff --git a/plugins/medium-zoom.html b/plugins/medium-zoom.html
new file mode 100644
index 0000000000..fecf867706
--- /dev/null
+++ b/plugins/medium-zoom.html
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+ medium-zoom | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Integrate medium-zoomopen in new window into VuePress, which can provide the ability to zoom images.
This plugin has been integrated into the default theme.
npm i -D @vuepress/plugin-medium-zoom@next
+
import { mediumZoomPlugin } from '@vuepress/plugin-medium-zoom'
+
+export default {
+ plugins: [
+ mediumZoomPlugin ({
+ // options
+ }),
+ ],
+}
+
Type: Object
Details:
Options for medium-zoom.
Also see:
You can customize most of the zoom styles via zoomOptions , while this plugin also provides some CSS variables for additional customization:
:root {
+ --medium-zoom-z-index : 100 ;
+ --medium-zoom-bg-color : #ffffff ;
+ --medium-zoom-opacity : 1 ;
+}
+
Details:
Returns the Zoom
instance that used by this plugin, so that you can use the instance methodsopen in new window directly.
This plugin will make images zoomable after navigating to current page. But if you are going to add new images dynamically, you may need this method to make those new images zoomable, too.
This plugin adds an extra refresh
method on the Zoom
instance, which will call zoom.detach()
then zoom.attach()
with the selector as the default parameter. It will help you to refresh the zoomable images for current page.
Example:
import { nextTick } from 'vue'
+import { useMediumZoom } from '@vuepress/plugin-medium-zoom/client'
+
+export default {
+ setup () {
+ const zoom = useMediumZoom ()
+
+ // ... do something to add new images in current page
+
+ // then you may need to call `refresh` manually to make those new images zoomable
+ nextTick (() => {
+ zoom . refresh ()
+ })
+ },
+}
+
Last Updated:
Contributors: Mr.Hope
external-link-icon nprogress
+
+
+
diff --git a/plugins/nprogress.html b/plugins/nprogress.html
new file mode 100644
index 0000000000..6dcdda5a22
--- /dev/null
+++ b/plugins/nprogress.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ nprogress | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Integrate nprogressopen in new window into VuePress, which can provide a progress bar when navigating to another page.
This plugin has been integrated into the default theme.
npm i -D @vuepress/plugin-nprogress@next
+
import { nprogressPlugin } from '@vuepress/plugin-nprogress'
+
+export default {
+ plugins: [ nprogressPlugin ()],
+}
+
You can customize the style of the progress bar via CSS variables:
:root {
+ --nprogress-color : #29d ;
+ --nprogress-z-index : 1031 ;
+}
+
Last Updated:
Contributors: Mr.Hope
medium-zoom photo-swipe
+
+
+
diff --git a/plugins/palette.html b/plugins/palette.html
new file mode 100644
index 0000000000..8b12e5fb39
--- /dev/null
+++ b/plugins/palette.html
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+ palette | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Provide palette support for your theme.
This plugin is mainly used to develop themes, and has been integrated into the default theme. You won't need to use it directly in most cases.
For theme authors, this plugin will help you to provide styles customization for users.
npm i -D @vuepress/plugin-palette@next
+
import { palettePlugin } from '@vuepress/plugin-palette'
+
+export default {
+ plugins: [
+ palettePlugin ({
+ // options
+ }),
+ ],
+}
+
This plugin will provide a @vuepress/plugin-palette/palette
(palette file) and a @vuepress/plugin-palette/style
(style file) to be imported in your theme styles.
The palette file is used for defining style variables, so it's likely to be imported at the beginning of your theme styles. For example, users can define CSS variablesopen in new window , SASS variablesopen in new window , LESS variablesopen in new window or Stylus variablesopen in new window in the palette, and then you can use those variables in your theme styles.
The style file is used for overriding the default styles or adding extra styles, so it's likely to be imported at the end of your theme styles.
Use this plugin in your theme, assuming you are using SASS:
export default {
+ // ...
+ plugins: [ palettePlugin ({ preset: 'sass' })],
+}
+
Import the plugin's palette file where your theme needs to use the corresponding variables, such as in the Layout.vue
file:
< template >
+ < h1 class = "palette-title" > Hello, Palette! </ h1 >
+</ template >
+
+< style lang = "scss" >
+/* import variables from the plugin's palette file */
+@import '@vuepress/plugin-palette/palette' ;
+
+/* set default value for variables */
+$color : red !default ;
+
+/* use variables in your styles */
+.palette-title {
+ color : $color ;
+}
+</ style >
+
Then users can customize variables in .vuepress/styles/palette.scss
:
Import the plugin's style file after your theme's styles, for example, in the clientConfigFile
:
// import your theme's style file
+import 'path/to/your/theme/style'
+// import the plugin's style file
+import '@vuepress/plugin-palette/style'
+
Then users can add extra styles in .vuepress/styles/index.scss
and override the default styles of your theme:
h1 {
+ font-size : 2.5rem ;
+}
+
Type: 'css' | 'sass' | 'less' | 'stylus'
Default: 'css'
Details:
Set preset for other options.
If you don't need advanced customization of the plugin, it's recommended to only set this option and omit other options.
Type: string
Default:
css: '.vuepress/styles/palette.css'
sass: '.vuepress/styles/palette.scss'
less: '.vuepress/styles/palette.less'
stylus: '.vuepress/styles/palette.styl'
Details:
File path of the user palette file, relative to source directory.
The default value depends on the preset option.
The file is where users define style variables, and it's recommended to keep the default file path as a convention.
Type: string
Default:
css: 'styles/palette.css'
sass: 'styles/palette.scss'
less: 'styles/palette.less'
stylus: 'styles/palette.styl'
Details:
File path of the generated palette temp file, relative to temp directory.
The default value depends on the preset option.
You should import the palette file via '@vuepress/plugin-palette/palette'
alias, so you don't need to change this option in most cases.
Type: string
Default:
css: '.vuepress/styles/index.css'
sass: '.vuepress/styles/index.scss'
less: '.vuepress/styles/index.less'
stylus: '.vuepress/styles/index.styl'
Details:
File path of the user style file, relative to source directory.
The default value depends on the preset option.
The file is where users override default styles or add extra styles, and it's recommended to keep the default file path as a convention.
Type: string
Default:
css: 'styles/index.css'
sass: 'styles/index.scss'
less: 'styles/index.less'
stylus: 'styles/index.styl'
Details:
File path of the generated style temp file, relative to temp directory.
The default value depends on the preset option.
You should import the style file via '@vuepress/plugin-palette/style'
alias, so you don't need to change this option in most cases.
Type: (filePath: string) => string
Default:
css: (filePath) => `@import '${filePath}';\n`
sass: (filePath) => `@forward 'file:///${filePath}';\n`
less: (filePath) => `@import '${filePath}';\n`
stylus: (filePath) => `@require '${filePath}';\n`
Details:
Function to generate import code.
The default value depends on the preset option.
This option is used for generating tempPaletteFile and tempStyleFile , and you don't need to change this option in most cases.
Last Updated:
Contributors: Mr.Hope
git reading-time
+
+
+
diff --git a/plugins/photo-swipe.html b/plugins/photo-swipe.html
new file mode 100644
index 0000000000..702fe1551b
--- /dev/null
+++ b/plugins/photo-swipe.html
@@ -0,0 +1,181 @@
+
+
+
+
+
+
+
+
+ photo-swipe | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development This plugin will make the pictures in the body of the page enter the preview mode when clicked.
npm i -D @vuepress/plugin-photo-swipe@next
+
import { photoSwipePlugin } from '@vuepress/plugin-photo-swipe'
+
+export default {
+ plugins: [
+ photoSwipePlugin ({
+ // options
+ }),
+ ],
+}
+
In preview mode, you can:
Swipe left and right to preview other pictures on the page in order View the description of the picture Zoom in and zoom out the picture View pictures in full screen Download pictures Share pictures TIP
Besides clicking "×" in the upper right corner to exit the preview mode, scrolling up and down more than a certain distance will also exit preview mode. On mobile devices, or using the PC trackpad, you can use pan and zoom gestures to pan and zoom in the preview mode. Type: string | string[]
Default: ".theme-default-content :not(a) > img:not([no-view])"
Details: Image selector Type: boolean
Default: true
Details: Whether close the current image when scrolling. Built-in Supported Languages Simplified Chinese (zh-CN)Traditional Chinese (zh-TW)English (United States) (en-US)German (de-DE)German (Australia) (de-AT)Russian (ru-RU)Ukrainian (uk-UA)Vietnamese (vi-VN)Portuguese (Brazil) (pt-BR)Polish (pl-PL)French (fr-FR)Spanish (es-ES)Slovak (sk-SK)Japanese (ja-JP)Turkish (tr-TR)Korean (ko-KR)Finnish (fi-FI)Indonesian (id-ID)Dutch (nl-NL)Type: string | false
Details: Image selector for the current page, or false
to disable photo-swipe in current page.
Options passed to photo-swipe
open in new window
import { definePhotoSwipeConfig } from '@vuepress/plugin-photo-swipe/client'
+
+definePhotoSwipeConfig ({
+ // set photoswipe options here
+})
+
+export default {}
+
You can also call photoswipe with apis.
createPhotoSwipe
allows you to programmatically view images links with PhotoSwipe:
< script setup lang = "ts" >
+import { onMounted , onUnmounted } from 'vue' ;
+import { createPhotoSwipe } from "@vuepress/plugin-photo-swipe/client" ;
+
+let state : PhotoSwipeState | null = null ;
+
+const openPhotoSwipe = ( index : number ) => {
+ state ?. open ( index - 1 );
+};
+
+onMounted ( async () => {
+ // create a new photoswipe instance with image links
+ state = await createPhotoSwipe (
+ [
+ 'https://exmaple.com/image1.png'
+ 'https://exmaple.com/image2.png'
+ 'https://exmaple.com/image3.png'
+ ],
+ {
+ // photoswipe options
+ }
+ );
+});
+
+onUnmounted (() => {
+ state ?. destroy ()
+})
+</ script >
+
+< template >
+ < button v-for = " i in 3 " @ click = " openPhotoSwipe ( i ) " > open photo {{ i }} </ button >
+</ template >
+
registerPhotoSwipe
allows you to register photoswipe for the given image elements:
< script setup lang = "ts" >
+import { onMounted , onUnmounted } from 'vue'
+import { registerPhotoSwipe } from '@vuepress/plugin-photo-swipe/client'
+
+let destroy : () => void | null = null
+
+onMounted ( async () => {
+ await nextTick ()
+
+ const images = Array . from ( document . querySelectorAll ( 'img' ))
+
+ // create a new photoswipe instance on image elements
+ state = await registerPhotoSwipe ( images , {
+ // photoswipe options
+ })
+})
+
+onUnmounted (() => {
+ destroy ?.()
+})
+</ script >
+
You can customize the style via CSS variables:
:root {
+ --photo-swipe-bullet : #fff ;
+ --photo-swipe-bullet-active : #3eaf7c ;
+}
+
Last Updated:
Contributors: Mr.Hope
nprogress redirect
+
+
+
diff --git a/plugins/prismjs.html b/plugins/prismjs.html
new file mode 100644
index 0000000000..9abb7b7073
--- /dev/null
+++ b/plugins/prismjs.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ prismjs | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development This plugin will enable syntax highlighting for markdown code fence with Prism.jsopen in new window .
This plugin has been integrated into the default theme.
Notice that this plugin would only tokenize the code fence without adding styles. When using it with a custom theme, you may need to choose and import Prism.js style theme yourself.
npm i -D @vuepress/plugin-prismjs@next
+
import { prismjsPlugin } from '@vuepress/plugin-prismjs'
+
+export default {
+ plugins: [
+ prismjsPlugin ({
+ // options
+ }),
+ ],
+}
+
Type: string[]
Default: ['markdown', 'jsdoc', 'yaml']
Details:
Languages to preload.
By default, languages will be loaded on demand when parsing markdown files.
However, Prism.js has some potential issuesopen in new window about loading languages dynamically. To avoid them, you can preload languages via this option.
Last Updated:
Contributors: Mr.Hope
shiki
+
+
+
diff --git a/plugins/pwa/config.html b/plugins/pwa/config.html
new file mode 100644
index 0000000000..0050afa455
--- /dev/null
+++ b/plugins/pwa/config.html
@@ -0,0 +1,176 @@
+
+
+
+
+
+
+
+
+ Config | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Type: boolean
Default: false
Details: Whether display install button when Service Worker is first registered successfully. Type: string
Details:
Link of favicon.ico.
WARNING
We recommend you to set favicon for your site.
Type: string
Default: "#46bd87"
Details:
Theme Color of the pwa.
Type: boolean
Default: false
Details:
Whether cache pictures
Special settings for better supporting Safari, ignoring these options are safe.
Type: string
Details: Icon link used by Safari. Type: "black" | "white"
Default: "black"
Details: Status bar color for Safari Type: string
Details: Safari mask icon Special settings for Microsoft tiles, ignoring these options are safe.
Type: string
Details: Tile image Type: string
Default value: themeColor
Details: Tile color Type: string
Default: "PwaFoundPopup"
Details: Path of custom hint popup component. Type: string
Default: "PwaReadyPopup"
Details: Path of custom update popup component. Type: boolean
Default: false
Details: Whether append base to all absolute links in options. Built-in Supported Languages Simplified Chinese (zh-CN)Traditional Chinese (zh-TW)English (United States) (en-US)German (de-DE)German (Australia) (de-AT)Russian (ru-RU)Ukrainian (uk-UA)Vietnamese (vi-VN)Portuguese (Brazil) (pt-BR)Polish (pl-PL)French (fr-FR)Spanish (es-ES)Slovak (sk-SK)Japanese (ja-JP)Turkish (tr-TR)Korean (ko-KR)Finnish (fi-FI)Indonesian (id-ID)Dutch (nl-NL)import { usePwaEvent } from '@vuepress/plugin-pwa/client'
+
+export default {
+ setup () {
+ const event = usePwaEvent ()
+ event . on ( 'ready' , ( registration ) => {
+ console . log ( 'Service worker is active.' )
+ })
+ },
+}
+
import { forceUpdate } from '@vuepress/plugin-pwa/client'
+import { onMounted } from 'vue'
+
+export default {
+ setup () {
+ onMounted (() => {
+ forceUpdate ()
+ })
+ },
+}
+
Parameter Type Description serviceWorkerPath string
Path of the service worker hooks object
Hooks of service worker showStatus boolean
Log service worker status in console
interface Hooks {
+ registrationOptions ?: RegistrationOptions
+ ready ?: ( registration : ServiceWorkerRegistration ) => void
+ registered ?: ( registration : ServiceWorkerRegistration ) => void
+ cached ?: ( registration : ServiceWorkerRegistration ) => void
+ updated ?: ( registration : ServiceWorkerRegistration ) => void
+ updatefound ?: ( registration : ServiceWorkerRegistration ) => void
+ offline ?: () => void
+ error ?: ( error : Error ) => void
+}
+
import { registerSW } from '@vuepress/plugin-pwa/client'
+import { onMounted } from 'vue'
+
+export default {
+ setup () {
+ onMounted (() => {
+ registerSW ( '/service-worker.js' , {
+ ready ( registration ) {
+ console . log ( 'Service worker is active.' )
+ },
+ })
+ })
+ },
+}
+
Parameter Type Description registration ServiceWorkerRegistration
The registration of the service worker you want activate
import { usePwaEvent , skipWaiting } from '@vuepress/plugin-pwa/client'
+
+export default {
+ setup () {
+ const event = usePwaEvent ()
+
+ event . on ( 'updated' , ( registration ) => {
+ console . log ( 'The waiting service worker is available.' )
+ // activate the waiting service worker
+ skipWaiting ( registration )
+ })
+ },
+}
+
import { unregisterSW } from '@vuepress/plugin-pwa/client'
+import { onMounted } from 'vue'
+
+export default {
+ setup () {
+ onMounted (() => {
+ unregisterSW ()
+ })
+ },
+}
+
You can customize the style via CSS variables:
:root {
+ --pwa-z-index : 10 ;
+ --pwa-color : #2c3e50 ;
+ --pwa-bg-color : #ffffff ;
+ --pwa-border-color : #3eaf7c ;
+ --pwa-shadow-color : rgb ( 0 0 0 / 15% );
+ --pwa-btn-text-color : #ffffff ;
+ --pwa-btn-bg-color : #3eaf7c ;
+ --pwa-btn-hover-bg-color : #4abf8a ;
+ --pwa-content-color : #333 ;
+ --pwa-content-light-color : #666 ;
+}
+
Last Updated:
Contributors: Mr.Hope
Guide
+
+
+
diff --git a/plugins/pwa/guide.html b/plugins/pwa/guide.html
new file mode 100644
index 0000000000..fbc5bc5046
--- /dev/null
+++ b/plugins/pwa/guide.html
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+ Guide | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Make your VuePress site a Progressive Web Application (PWA).
This plugin uses workbox-buildopen in new window to generate service worker file, and uses register-service-workeropen in new window to register service worker.
WARNING
If you enabled this plugin once and you want to disable it, you might need `@vuepress/plugin-remove-pwa to remove the existing service worker.
A PWA uses a Service Worker (SW for short) to cache and proxy site content.
To make your website fully compliant with PWA, a Web app manifests file is needed, and your pwa should satisfy the installability specification.
You can set manifest
option to customize the manifest file, or provide a manifest.webmanifest
or manifest.json
in public folder. The former has higher priority.
The plugin will automatically generate manifest.webmanifest
for you and add manifest link declaration in each page, while you should still at least set a valid icon through manifest.icons
or other icon related options in the PWA plugin.
WARNING
The installability specification requires at least one valid icon to be declared in the manifest.
So if you do not configure manifest.icons
, visitors can only enjoy the offline accessibility brought by the Service Worker cache, while cannot install your site as a PWA.
Besides the plugin does not process anything in the manifest by default, but outputs them as-is. This means that if you plan to deploy to a subdirectory, you should append the URL prefix to manifest Urls yourself. If everything you need is all under base
directory, you can set appendBase: true
in plugin options to let the plugin append base
to any links in manifest.
To better control what the Service Worker can pre-cache, the plugin provides related options for cache control.
By default, the plugin will pre-cache all js
css
files, and only homepage and 404 html are cached. The plugin will also cache font files (woff, woff2, eot, ttf, otf) and SVG icons.
If your site has only a few important images, and want them displayed in offline mode, you can cache site images by setting cacheImage: true
.
We recognize images by file extension. Any files ending with .png
, .jpg
, .jpeg
, .gif
, .bmp
, .webp
will be regarded as images.
If you have small sites, and would like to make document fully offline available, you can set cacheHTML
to true
to cache all HTML files.
Why only home and 404 page been cached by default?
Though VuePress generates HTML files through SSG for all pages, these files are mainly used for SEO and allow you to directly configure the backend without SPA Visit any link.
VuePress is essentially an SPA. This means that you only need to cache the home page and enter from the home page to access all pages normally. Therefore, not caching other HTML by default can effectively reduce the cache size (40% smaller in size) and speed up the SW update speed.
But this also has the disadvantage. If the user enters the site directly from a non-home page, the HTML file for the first page still needs to be loaded from the internet. Also, in offline environment, users can only enter through the homepage and then navigate to the corresponding page by themselves. If they directly access a link, an inaccessible prompt will appear.
To prevent large files from being included in the pre-cache list, any files > 2 MB or images > 1 MB will be omitted. You can customize these limits with maxSize
and maxImageSize
options (in KB unit).
We provide the update
option to control how users receive updates.
The default value of the update
option is "available"
, which means that when new content available, the new SW will be installed and its resources will be fetched silently in the background. A pop-up window appears once the new SW is ready, and users can choose whether to refresh immediately to view new content. This means users are reading old content before a new SW is ready.
If your project is still in building stage, and you want to alert the user that he may be reading outdated content, you can set this to "hint"
. This allows users to be notified that new content has been published within seconds after visiting docs. But the negative effect of this is that if the user chooses to update before the new SW is ready, he will need to get all the resources of the page from the internet before the new SW installs and controls the page.
If your docs are stable, or you're hosting a blog and don't care much about users receiving the latest version right away, you can set this to "disabled"
, which means that the new SW will be installed completely silently in the background and start waiting, when all pages controlled by old SW are all closed, the new SW will start to take control and provide users with new content during next visit. This setting can prevent users from being disturbed during the visit.
To speed up user access under weak or no network conditions through SW, but also want users to always access new content, you can set this option to "force"
. This means any old SW will be removed as soon as a new SW is detected, and all pages are refreshed to ensure the user is browsing the latest content. The biggest disadvantage is that all users will experience unexpected sudden refresh within seconds after reentering an updated site.
When new content is detected (new SW detected), a update found popup appears; and when the new content is ready, an update ready popup appears.
If you are not satisfied with the default popup content, you can use your own component. Import PwaFoundPopup
or PwaReadyPopup
from @vuepress/plugin-pwa/client
and use its slot to customize the popup content, then pass the component path to foundComponent
or readyComponent
option:
< script setup lang = "ts" >
+import { PwaFoundPopup } from '@vuepress/plugin-pwa/client'
+</ script >
+< template >
+ < PwaFoundPopup v-slot = " { found , refresh } " >
+ < div v-if = " found " >
+ New content is found.
+ < button @ click = " refresh " > Refresh </ button >
+ </ div >
+ </ PwaFoundPopup >
+</ template >
+
< script setup lang = "ts" >
+import { PwaReadyPopup } from '@vuepress/plugin-pwa/client'
+</ script >
+< template >
+ < PwaReadyPopup v-slot = " { isReady , reload } " >
+ < div v-if = " isReady " >
+ New content is ready.
+ < button @ click = " reload " > Apply </ button >
+ </ div >
+ </ PwaReadyPopup >
+</ template >
+
The plugin also provides other PWA-related options, such as Microsoft tile icon and color settings, Apple icon and so on. If you are an advanced user, you can also set generateSwConfig
to configure workbox-build
. Check Plugin options for more details.
For more details, please see:
Last Updated:
Contributors: Mr.Hope
Config
+
+
+
diff --git a/plugins/pwa/index.html b/plugins/pwa/index.html
new file mode 100644
index 0000000000..41d75cd7e1
--- /dev/null
+++ b/plugins/pwa/index.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ pwa | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development npm i -D @vuepress/plugin-pwa@next
+
import { pwaPlugin } from '@vuepress/plugin-pwa'
+
+export default {
+ plugins: [
+ pwaPlugin ({
+ // options
+ }),
+ ],
+}
+
Last Updated:
Contributors: Mr.Hope
remove-pwa
+
+
+
diff --git a/plugins/reading-time.html b/plugins/reading-time.html
new file mode 100644
index 0000000000..eab67d1a5d
--- /dev/null
+++ b/plugins/reading-time.html
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+ reading-time | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development This plugin will generate word count and estimated reading time for each page.
npm i -D @vuepress/plugin-reading-time@next
+
import { readingTimePlugin } from '@vuepress/plugin-reading-time'
+
+export default {
+ plugins: [
+ readingTimePlugin ({
+ // options
+ }),
+ ],
+}
+
The plugin will inject reading time information into the readingTime
of the page data, where:
readingTime.minutes
: estimated reading time (minutes) number
readingTime.words
: word count number
For any page, you can get estimated reading time and word count from page.data.readingTime
:
page . data . readingTime // { minutes: 3.2, words: 934 }
+
You can access it for further processing in the extendsPage
lifecycle and other lifecycle:
export default {
+ // ...
+ extendsPage : ( page ) => {
+ page . data . readingTime // { minutes: 3.2, words: 934 }
+ },
+
+ onInitialized : ( app ) => {
+ app . pages . map (( page ) => {
+ page . data . readingTime // { minutes: 3.2, words: 934 }
+ })
+ },
+}
+
You can import useReadingTimeData
and useReadingTimeLocale
from @vuepress/plugin-reading-time/client
to get the reading time data and locale data of the current page:
< script setup lang = "ts" >
+import {
+ useReadingTimeData ,
+ useReadingTimeLocale ,
+} from '@vuepress/plugin-reading-time/client'
+
+const readingTimeData = useReadingTimeData () // { minutes: 1.1, words: 100 }
+const readingTimeLocale = useReadingTimeLocale () // { time: "1 minute", words: "100 words" }
+</ script >
+
Type: number
Default: 300
Details: Reading speed (words per minute) Built-in Supported Languages Simplified Chinese (zh-CN)Traditional Chinese (zh-TW)English (United States) (en-US)German (de-DE)German (Australia) (de-AT)Russian (ru-RU)Ukrainian (uk-UA)Vietnamese (vi-VN)Portuguese (Brazil) (pt-BR)Polish (pl-PL)French (fr-FR)Spanish (es-ES)Slovak (sk-SK)Japanese (ja-JP)Turkish (tr-TR)Korean (ko-KR)Finnish (fi-FI)Indonesian (id-ID)Dutch (nl-NL)You can import and use these APIs from @vuepress/plugin-reading-time/client
:
These APIs won't throw even you disable the plugin.
interface ReadingTime {
+ /** Expect reading time in minute unit */
+ minutes : number
+ /** Words count of content */
+ words : number
+}
+
+const useReadingTimeData : () => ComputedRef < ReadingTime | null >
+
null
is returned when the plugin is disabled.
interface ReadingTimeLocale {
+ /** Expect reading time text in locale */
+ time : string
+ /** Word count text in locale */
+ words : string
+}
+
+const useReadingTimeLocale : () => ComputedRef < ReadingTimeLocale >
+
This plugin is targeting plugin and theme developers mostly, so we provide a "Use API":
import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
+
+export default ( options ) => ( app ) => {
+ useReadingTimePlugin ( app , {
+ // your options
+ })
+
+ return {
+ name: 'vuepress-plugin-xxx' , // or vuepress-theme-xxx
+ }
+}
+
Why you should use "Use API"
When you register a plugin multiple times, vuepress will gives you warning about that telling you only the first one takes effect. The useReadingTimePlugin
automatically detects if the plugin is registered and avoid registering multiple times. If you access reading time data in extendsPage
lifecycle, then @vuepress/plugin-reading-time
must be called before your theme or plugin, otherwise you will get undefined
for page.data.readingTime
. The useReadingTimePlugin
ensures that @vuepress/plugin-reading-time
is called before your theme or plugin. We also provides a removeReadingTimePlugin
api to remove the plugin.You can use this to ensure your call take effect or clear the plugin:
import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
+
+export default ( options ) => ( app ) => {
+ // this removes any existing reading time plugin at this time
+ removeReadingTimePlugin ( app )
+
+ // so this will take effect even if there is a reading time plugin registered before
+ useReadingTimePlugin ( app , {
+ // your options
+ })
+
+ return {
+ name: 'vuepress-plugin-xxx' , // or vuepress-theme-xxx
+ }
+}
+
Last Updated:
Contributors: Mr.Hope
palette rtl
+
+
+
diff --git a/plugins/redirect.html b/plugins/redirect.html
new file mode 100644
index 0000000000..6aed3643bb
--- /dev/null
+++ b/plugins/redirect.html
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+ redirect | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development This plugin can automatically handle redirects for your site.
npm i -D @vuepress/plugin-redirect@next
+
import { redirectPlugin } from '@vuepress/plugin-redirect'
+
+export default {
+ plugins: [
+ redirectPlugin ({
+ // options
+ }),
+ ],
+}
+
If you change the address of an existing page, you can use the redirectFrom
option in Frontmatter to redirect to the address of this page, which ensures that users are redirected to the new address when they visit the old link.
If you need to redirect an existing page to a new page, you can use the redirectTo
option in Frontmatter to set the address to redirect to. This way the page will redirect to the new address when accessed.
You can also set config
with a redirect map in plugin options, see config for more details.
The plugin can automatically redirect non-multilingual links to the multilingual pages the user needs based on the user's language preference.
To achieve this, you need to leave the default language directory (/
) blank and set autoLocale: true
in plugin options. The plugin will automatically redirect to the correct page according to the user's language.
I.E.: you need to set the following directory structure:
.
+├── en
+│ ├── ...
+│ ├── page.md
+│ └── README.md
+├── zh
+│ ├── ...
+│ ├── page.md
+│ └── README.md
+└── other_languages
+ ├── ...
+ ├── page.md
+ └── README.md
+
And set locales
in theme options with:
export default {
+ locales: {
+ '/en/' : {
+ lang: 'en-US' ,
+ // ...
+ },
+ '/zh/' : {
+ lang: 'zh-CN' ,
+ // ...
+ },
+ // other languages
+ },
+ // ...
+}
+
So when a user accesses /
or /page.html
, they are automatically redirected to /en/
/en/page.html
and /en/
/en/page.html
based on current browser language.
Customizing fallback behavior
Sometimes, users may add more than one language to the system settings. By default, when a site supports a preferred language, but the page not exists for the preferred language, the plugin attempts to match the alternate language set by the user.
If you don't need to fall back to the user's alternate language, but directly match the user's preferred language, set localeFallback: false
in the plugin options.
Customizing missing behavior
Sometimes, when a user visits a page, the document does not yet contain the language version the user needs (a common case is that the current page has not been localized in the relevant language), so the plugin needs to perform a default action, which you can customize by defaultBehavior
in the plugin options:
"defaultLocale"
: Redirect to default language or first available language page (default behavior)"homepage"
: redirect to the home page in the current language (only available if the document contains the user's language)"404"
: Redirect to page 404 in current language (only available if the document contains the user's language)Customizing default locale path
You can customize the default locale path by setting defaultLocale
in the plugin options. By default, the plugin uses the first locale key in locales
as the default language.
The plugin supports automatically switching the link to the multilingual page that the user needs according to the user's language preference when opening a multilingual document. In order to achieve this, you need to set switchLocale
in the plugin options, which can be the following two values:
direct
: switch directly to the user language preference page without askingmodal
: When the user's language preference is different from the current page language, show a modal asking whether to switch languageBy default, the plugin generates a locale setting by reading locale path
and lang
from the site's locales
option. Sometimes, you may want multiple languages to hit the same path, in which case you should set localeConfig
in plugin options.
For example, you might want all English users to match to /en/
and Chinese Traditional users to /zh/
, then you can set:
redirect ({
+ localeConfig: {
+ '/en/' : [ 'en-US' , 'en-UK' , 'en' ],
+ '/zh/' : [ 'zh-CN' , 'zh-TW' , 'zh' ],
+ },
+})
+
Sometimes you may change base
or use new domain for your site, so you may want the original site automatically redirects to the new one.
To solve this, the plugin provide vp-redirect
cli.
Usage:
+ $ vp-redirect generate [sourceDir]
+
+Options:
+ --hostname < hostnam e> Hostname to redirect to ( E.g.: https://new.example.com/ ) (default: / )
+ -c, --config < confi g> Set path to config file
+ -o, --output < outpu t> Set the output directory ( default: .vuepress/redirect )
+ --cache < cach e> Set the directory of the cache files
+ -t, --temp < tem p> Set the directory of the temporary files
+ --clean-cache Clean the cache files before generation
+ --clean-temp Clean the temporary files before generation
+ -h, --help Display this message
+
You need to pass in VuePress project source dir and also set the hostname
option. The redirect helper cli will initialize your VuePress project to get pages, then generate and output the redirect html files to the output directory.
By default, the plugin will output to .vuepress/redirect
directory under source directory. And you should upload it to your original site to provide redirection.
Type: Record<string, string> | ((app: App) => Record<string, string>)
Details: Redirect map.
Example:
When base is set to /base/
:
redirect /base/foo.html
to /base/bar.html
/base/baz.html
to https://example.com/qux.html
.redirect ({
+ config: {
+ '/foo.html' : '/bar.html' ,
+ '/baz.html' : 'https://example.com/qux.html' ,
+ },
+})
+
Redirect post folder to posts folder:
redirect ({
+ hostname: 'https://example.com' ,
+ config : ( app ) =>
+ Object . fromEntries (
+ app . pages
+ . filter (({ path }) => path . startsWith ( '/posts/' ))
+ . map (({ path }) => [ path . replace ( / ^ \/ posts \/ / , '/post/' ), path ]),
+ ),
+})
+
Type: boolean
Default: false
Details: Whether enable locales redirection. Type: boolean
Default: true
Details: Whether fallback to other locales user defined Type: "defaultLocale" | "homepage" | "404"
Default: "defaultLocale"
Details: Behavior when a locale version is not available for current link. Type: string
Default: the first locale Details: Default locale path. Type: string | string[]
Details: The link which this page redirects from. Type: string
Details: The link which this page redirects to. You can customize the style of the redirect popup via CSS variables:
:root {
+ --redirect-z-index : 1499 ;
+ --redirect-bg-color : #fff ;
+ --redirect-bg-color-light : #f3f4f5 ;
+ --redirect-bg-color-lighter : #eeeeee ;
+ --redirect-text-color : #2c3e50 ;
+ --redirect-primary-bg-color : #3eaf7c ;
+ --redirect-primary-hover-bg-color : #4abf8a ;
+ --redirect-primary-text-color : #fff ;
+}
+
Last Updated:
Contributors: Mr.Hope
photo-swipe register-components
+
+
+
diff --git a/plugins/register-components.html b/plugins/register-components.html
new file mode 100644
index 0000000000..20f953dcfe
--- /dev/null
+++ b/plugins/register-components.html
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+ register-components | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Register Vue components from component files or directory automatically.
npm i -D @vuepress/plugin-register-components@next
+
import { registerComponentsPlugin } from '@vuepress/plugin-register-components'
+
+export default {
+ plugins: [
+ registerComponentsPlugin ({
+ // options
+ }),
+ ],
+}
+
Type: Record<string, string>
Default: {}
Details:
An object that defines name of components and their corresponding file path.
The key will be used as the component name, and the value is an absolute path of the component file.
If the component name from this option conflicts with componentsDir option, this option will have a higher priority.
Example:
import { getDirname , path } from 'vuepress/utils'
+
+const __dirname = getDirname ( import . meta . url )
+
+export default {
+ plugins: [
+ registerComponentsPlugin ({
+ components: {
+ FooBar: path . resolve ( __dirname , './components/FooBar.vue' ),
+ },
+ }),
+ ],
+}
+
import { getDirname , path } from 'vuepress/utils'
+
+const __dirname = getDirname ( import . meta . url )
+
+export default {
+ plugins: [
+ registerComponentsPlugin ({
+ componentsDir: path . resolve ( __dirname , './components' ),
+ }),
+ ],
+}
+
Components directory:
components
+├─ FooBar.vue
+└─ Baz.vue
+
Components will be registered like this:
import { defineAsyncComponent } from 'vue'
+
+app . component (
+ 'FooBar' ,
+ defineAsyncComponent (() => import ( '/path/to/components/FooBar.vue' )),
+)
+
+app . component (
+ 'Baz' ,
+ defineAsyncComponent (() => import ( '/path/to/components/Baz.vue' )),
+)
+
Type: (filename: string) => string
Default: (filename) => path.trimExt(filename.replace(/\/|\\/g, '-'))
Details:
A function to get component name from the filename.
It will only take effect on the files in the componentsDir which are matched with the componentsPatterns .
Notice that the filename
is a filepath relative to componentsDir .
Last Updated:
Contributors: Mr.Hope
redirect
+
+
+
diff --git a/plugins/remove-pwa.html b/plugins/remove-pwa.html
new file mode 100644
index 0000000000..7b7877d1c6
--- /dev/null
+++ b/plugins/remove-pwa.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ remove-pwa | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development This plugin removes any related service worker from your VuePress site, so that users can still get updates if you removed any PWA plugin after enabling it.
Why this plugin is needed if you used PWA plugin once?
PWA plugins like @vuepress/plugin-pwa
register service worker to your site, which will cache your site and make it available offline.
However, if you remove pwa plugin, the old service worker will still be there, but they can never get an update because they can never found a new service worker to update to. So users will stay with the old version of your site.
To solve this problem:
A new service worker with empty contents shall be generated in the original place. The new service worker shall attempt to remove contents that old service worker cached, then it should unregister itself. npm i -D @vuepress/plugin-remove-pwa@next
+
import { removePwaPlugin } from '@vuepress/plugin-remove-pwa'
+
+export default {
+ plugins: [
+ removePwaPlugin ({
+ // options
+ }),
+ ],
+}
+
Type: string
Default: 'workbox'
Details: The cache prefix for the service worker. Type: string
Default: 'service-worker.js'
Details: The location of the old service worker. Last Updated:
Contributors: Mr.Hope
PWA
+
+
+
diff --git a/plugins/rtl.html b/plugins/rtl.html
new file mode 100644
index 0000000000..d105a7c1d1
--- /dev/null
+++ b/plugins/rtl.html
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+ rtl | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development This plugin will set direction to rtl on configured locales.
npm i -D @vuepress/plugin-rtl@next
+
import { rtlPlugin } from '@vuepress/plugin-rtl'
+
+export default {
+ plugins: [
+ rtlPlugin ({
+ // options
+ locales: [ '/ar/' ],
+ }),
+ ],
+}
+
Type: string[]
Default: ['/']
Details: Locale path to enable rtl. Last Updated:
Contributors: Mr.Hope
reading-time theme-data
+
+
+
diff --git a/plugins/search.html b/plugins/search.html
new file mode 100644
index 0000000000..e65ee87eec
--- /dev/null
+++ b/plugins/search.html
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+ search | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Provide local search to your documentation site.
TIP
Default theme will add search box to the navbar once you configure this plugin correctly.
This plugin may not be used directly in other themes, so you'd better refer to the documentation of your theme for more details.
npm i -D @vuepress/plugin-search@next
+
import { searchPlugin } from '@vuepress/plugin-search'
+
+export default {
+ plugins: [
+ searchPlugin ({
+ // options
+ }),
+ ],
+}
+
This plugin will generate search index from your pages locally, and load the search index file when users enter your site. In other words, this is a lightweight built-in search which does not require any external requests.
However, when your site has a large number of pages, the size of search index file would be very large, which could slow down the page loading speed. In this case, we recommend you to use a more professional solution - docsearch .
Type: Record<string, { placeholder: string }>
Details:
The text of the search box in different locales.
If this option is not specified, it will fallback to default text.
Example:
export default {
+ plugins: [
+ searchPlugin ({
+ locales: {
+ '/' : {
+ placeholder: 'Search' ,
+ },
+ '/zh/' : {
+ placeholder: '搜索' ,
+ },
+ },
+ }),
+ ],
+}
+
Type: (string | HotKeyOptions)[]
export interface HotKeyOptions {
+ /**
+ * Value of `event.key` to trigger the hot key
+ */
+ key : string ;
+ /**
+ * Whether to press `event.altKey` at the same time
+ *
+ * @default false
+ */
+ alt ?: boolean ;
+ /**
+ * Whether to press `event.ctrlKey` at the same time
+ *
+ * @default false
+ */
+ ctrl ?: boolean ;
+ /**
+ * Whether to press `event.shiftKey` at the same time
+ *
+ * @default false
+ */
+ shift ?: boolean ;
+}
+
Default: ['s', '/']
Details:
Specify the event.keyopen in new window of the hotkeys.
When hotkeys are pressed, the search box input will be focused.
Set to an empty array to disable hotkeys.
export default {
+ plugins: [
+ searchPlugin ({
+ // exclude the homepage
+ isSearchable : ( page ) => page . path !== '/' ,
+ }),
+ ],
+}
+
Type: (page: Page) => string[]
Default: () => []
Details:
A function to add extra fields to the search index of a page.
By default, this plugin will use page title and headers as the search index. This option could help you to add more searchable fields.
Example:
export default {
+ plugins: [
+ searchPlugin ({
+ // allow searching the `tags` frontmatter
+ getExtraFields : ( page ) => page . frontmatter . tags ?? [],
+ }),
+ ],
+}
+
You can customize the style of the search box via CSS variables:
:root {
+ --search-bg-color : #ffffff ;
+ --search-accent-color : #3eaf7c ;
+ --search-text-color : #2c3e50 ;
+ --search-border-color : #eaecef ;
+ --search-item-text-color : #5d81a5 ;
+ --search-item-focus-bg-color : #f3f4f5 ;
+ --search-input-width : 8rem ;
+ --search-result-width : 20rem ;
+}
+
Details:
This plugin will register a <SearchBox />
component globally, and you can use it without any props.
Put this component to where you want to place the search box. For example, default theme puts this component to the end of the navbar.
TIP
This component is mainly used for theme development. You don't need to use it directly in most cases.
Last Updated:
Contributors: Mr.Hope
docsearch
+
+
+
diff --git a/plugins/seo/config.html b/plugins/seo/config.html
new file mode 100644
index 0000000000..1778f4ab97
--- /dev/null
+++ b/plugins/seo/config.html
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+ Config | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Type: string
Required: Yes
Details:
Deploy hostname.
Type: Author
type AuthorName = string
+
+interface AuthorInfo {
+ /**
+ * Author name
+ */
+ name : string
+
+ /**
+ * Author website
+ */
+ url ?: string
+
+ /**
+ * Author email
+ */
+ email ?: string
+}
+
+type Author = AuthorName | AuthorName [] | AuthorInfo | AuthorInfo []
+
Required: No
Details:
Default author.
Last Updated:
Contributors: Mr.Hope
Guide
+
+
+
diff --git a/plugins/seo/guide.html b/plugins/seo/guide.html
new file mode 100644
index 0000000000..378c7b823e
--- /dev/null
+++ b/plugins/seo/guide.html
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+ Guide | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development This plugin will make your site fully support Open Content Protocol OGPopen in new window and JSON-LD 1.1open in new window to enhance the SEO of the site.
The plugin works out of the box. Without any config, it will extract information from the page content as much as possible to complete the necessary tags required by OGP and JSON-LD.
By default, the plugin will read the site config and page frontmatter to automatically generate tags as much as possible. Such as site name, page title, page type, writing date, last update date, and article tags are all automatically generated.
The following are the <meta>
tags and their values that will be injected into <head>
by default:
The following are the <meta>
tags and their value injected into <head>
by default to satisfy OGP:
Meta Name Value og:url
options.hostname
+ path
og:site_name
siteConfig.title
og:title
page.title
og:description
page.frontmatter.description
|| auto generated (when autoDescription
is true
in plugin options)og:type
"article"
og:image
options.hostname
+ page.frontmatter.image
||first image in page || fallbackImage
in plugin optionsog:updated_time
page.git.updatedTime
og:locale
page.lang
og:locale:alternate
Other languages in siteData.locales
twitter:card
"summary_large_image"
(only available when image found)twitter:image:alt
page.title
(only available when image found)article:author
page.frontmatter.author
|| options.author
article:tag
page.frontmatter.tags
|| page.frontmatter.tag
article:published_time
page.frontmatter.date
|| page.git.createdTime
article:modified_time
page.git.updatedTime
Property Name Value @context
"https://schema.org"
@type
"NewsArticle"
headline
page.title
image
image in page || options.hostname
+ page.frontmatter.image
|| siteFavIcon
in plugin options datePublished
page.frontmatter.date
|| page.git.createdTime
dateModified
page.git.updatedTime
author
page.frontmatter.author
|| options.author
You can configure the head
option in the page's frontmatter to add specific tags to the page <head>
to enhance SEO. For example:
---
+head :
+ - - meta
+ - name : keywords
+ content : SEO plugin
+---
+
Will automatically inject <meta name="keywords" content="SEO plugin" />
.
The plugin also gives you full control over the build logic.
For most pages, there are basically only two types: articles and website, so the plugin provides the isArticle
option to allow you to provide logic for identifying articles.
The option accepts a function in the format (page: Page) => boolean
, by default all non-home pages generated from Markdown files are treated as articles.
TIP
If a page does fit into the "unpopular" genre like books, music, etc., you can handle them by setting the three options below.
You can use the plugin options ogp
to pass in a function to modify the default OGP object to your needs and return it.
function ogp (
+ /** OGP Object inferred by plugin */
+ ogp : SeoContent ,
+ /** Page Object */
+ page : Page ,
+ /** VuePress App */
+ app : App ,
+): SeoContent
+
For detailed parameter structure, see Config .
For example, if you are using a third-party theme and set a banner
in frontmatter for each article according to the theme requirements, then you can pass in the following ogp
:
seoPlugin ({
+ ogp : ( ogp , page ) => ({
+ ... ogp ,
+ 'og:image' : page . frontmatter . banner || ogp [ 'og:image' ],
+ }),
+})
+
Like OGP, you can use the plugin options jsonLd
to pass in a function to modify the default JSON-LD object to your needs and return it.
function jsonLd (
+ /** JSON-LD Object inferred by plugin */
+ jsonLD : ArticleSchema | BlogPostingSchema | WebPageSchema ,
+ /** Page Object */
+ page : Page ,
+ /** VuePress App */
+ app : App ,
+): ArticleSchema | BlogPostingSchema | WebPageSchema
+
If you are deploying your content to different sites, or same content under different URLs, you may need to set canonical
option to provide a "Canonical Link" for your page. You can either set a string which will be appended before page route link, or adding a custom function (page: Page) => string | null
to return a canonical link if necessary.
Example
If your sites are deployed under docs directory in example.com
, but available in:
http://example.com/docs/xxx
https://example.com/docs/xxx
http://www.example.com/docs/xxx
https://www.example.com/docs/xxx
(primary)To let search engine results always be the primary choice, you may need to set canonical
to https://www.example.com/docs/
, so that search engine will know that the fourth URL is preferred to be indexed.
Sometimes you may need to fit other protocols or provide the corresponding SEO tags in the format provided by other search engines. In this case, you can use the customHead
option, whose type is:
function customHead (
+ /** Head tag config */
+ head : HeadConfig [],
+ /** Page Object */
+ page : Page ,
+ /** VuePress App */
+ app : App ,
+): void
+
You should modify the head
array in this function directly.
S earch e ngine optimization (SEO) is the process of improving the quality and quantity of site traffic to a site or a web page from search engines. SEO targets unpaid traffic (known as "natural" or "organic" results) rather than direct traffic or paid traffic. Unpaid traffic may originate from different kinds of searches, including image search, video search, academic search, news search, and industry-specific vertical search engines.
As an internet marketing strategy, SEO considers how search engines work, the computer-programmed algorithms that dictate search engine behavior, what people search for, the actual search terms or keywords typed into search engines, and which search engines are preferred by their targeted audience. SEO is performed because a site will receive more visitors from a search engine when sites rank higher on the search engine results page (SERP). These visitors can then potentially be converted into customers.
You can use Google Rich Media Structure Test Toolopen in new window to test this site.
Last Updated:
Contributors: Mr.Hope
Config
+
+
+
diff --git a/plugins/seo/index.html b/plugins/seo/index.html
new file mode 100644
index 0000000000..1b53702da0
--- /dev/null
+++ b/plugins/seo/index.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ seo | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development npm i -D @vuepress/plugin-seo@next
+
import { seoPlugin } from '@vuepress/plugin-seo'
+
+export default {
+ plugins: [
+ seoPlugin ({
+ // options
+ }),
+ ],
+}
+
Last Updated:
Contributors: Mr.Hope
Sitemap
+
+
+
diff --git a/plugins/shiki.html b/plugins/shiki.html
new file mode 100644
index 0000000000..ff06661648
--- /dev/null
+++ b/plugins/shiki.html
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+ shiki | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development This plugin will enable syntax highlighting for markdown code fence with Shikiopen in new window .
TIP
Shikiopen in new window is the syntax highlighter being used by VSCode. It has higher fidelity, but it could be slower than Prism.jsopen in new window , especially when you have a lot of code blocks.
You could consider disabling this plugin in dev
mode to get better development experience.
npm i -D @vuepress/plugin-shiki@next
+
import { shikiPlugin } from '@vuepress/plugin-shiki'
+
+export default {
+ plugins: [
+ shikiPlugin ({
+ // options
+ langs: [ 'ts' , 'json' , 'vue' , 'md' , 'bash' , 'diff' ],
+ }),
+ ],
+}
+
Type: ShikiLang[]
Details:
Languages of code blocks to be parsed by Shiki.
This option will be forwarded to getHighlighter()
method of Shiki.
You'd better provide the languages list you are using explicitly, otherwise Shiki will load all languages and can affect performance.
Also see:
Type: Record<'dark' | 'light', ShikiTheme>
Details:
Dark / Light Dual themes of Shiki.
This option will be forwarded to codeToHtml()
method of Shiki.
Also see:
Last Updated:
Contributors: Mr.Hope
prismjs
+
+
+
diff --git a/plugins/sitemap/config.html b/plugins/sitemap/config.html
new file mode 100644
index 0000000000..10dce7f5d1
--- /dev/null
+++ b/plugins/sitemap/config.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ Config | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Type: string[]
Details:
Extra link to be included.
If you have some links not including in VuePress Router (normally in public directory or generated by other tools directly), you may need this option.
Example: ['/about.html', '/api/']
Type: string[]
Default: ['/404.html']
Details:
Urls excluding from sitemap, starting with absolute path.
By default, all the urls generated by VuePress (excluding 404 page) will be added into sitemap.
TIP
For performance reasons, we do not provide hot reload. Reboot your devServer to sync your changes.
Type: "always" | "hourly" | "daily" | "weekly" |"monthly" | "yearly" | "never"
Default value: "daily"
Details:
Page default update frequency, will be overridden by sitemap.changefreq in Frontmatter.
Type: (page: Page, app: App) => string
Details:
Last modify time getter. By default, the plugin will use the timestamp generated by git plugin.
Last Updated:
Contributors: Mr.Hope
Guide Frontmatter
+
+
+
diff --git a/plugins/sitemap/frontmatter.html b/plugins/sitemap/frontmatter.html
new file mode 100644
index 0000000000..6c44894f7a
--- /dev/null
+++ b/plugins/sitemap/frontmatter.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ Frontmatter | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Last Updated:
Contributors: Mr.Hope
Config
+
+
+
diff --git a/plugins/sitemap/guide.html b/plugins/sitemap/guide.html
new file mode 100644
index 0000000000..eda41b0df6
--- /dev/null
+++ b/plugins/sitemap/guide.html
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+ Guide | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development This plugin will automatically generate a Sitemap for your site. To let this plugin work, you need to pass the deployed domain name to the hostname
option of the plugin. If you want to preview in devServer, set devServer
options.
The plugin will automatically generate the last update time of the page based on the Git timestamp of the page, and will also declare the alternative links of the page in other languages according to the locales' config.
By default, all site links except 404 page will be added to the Sitemap.
To add other pages to the Sitemap outside the VuePress project page, please turn them into an array and pass to the extraUrls
plugin option.
If you don't want certain pages to appear in the sitemap, you can turn their paths into an array and pass to the excludePaths
plugin option, or set sitemap
to false
in the frontmatter of the corresponding page.
You can also control the output link through the sitemapFilename
option of the plugin, the link is relative to output directory. By default, the plugin will use sitemap.xml
.
The default update cycle of the page is daily
(every day). To modify the entire page cycle, please set changefreq
in the plugin options. You can also set sitemap.changefreq
in the frontmatter of the page. Note that page has a higher priority.
The legal frequencies are:
"always"
"hourly"
"daily"
"weekly"
"monthly"
"yearly"
"never"
You can set priority
in the plugin to provide a default value. At the same time you can set the priority for each page through sitemap.priority
in frontmatter. Acceptable values are floating point numbers from 0
to 1
.
You can use option modifyTimeGetter
to return a time in ISO string format, which is generated by the Git plugin by default.
The following is an example based on the last modification time of a file.
// Based on file last modified time
+({
+ modifyTimeGetter : ( page , app ) =>
+ fs . statSync ( app . dir . source ( page . filePathRelative )). mtime . toISOString ();
+})
+
Sitemaps provide SEO (Search Engine Optimization):
Provide search engine spiders with links of the entire site; Provide some links for search engine spiders to dynamic pages or pages that are difficult to reach by other methods; If a visitor attempts to access a URL that does not exist within the site's domain, the visitor will be directed to a "file not found" error page, and the sitemap can be used as a navigation page. A sitemap enhances SEO by making all pages findable.
Most search engines only follow a limited number of links within a page, so when the site is very large, a sitemap becomes essential to make everything on the site accessible to search engines and visitors.
Sitemaps is a protocol for site administrators to publish pages that can be crawled on a site to search engine spiders. The content of sitemap files must follow the definition in XML format. Each URL can contain the update period and last update time, the priority of the URL across the site. This allows search engines to crawl site content better and more efficiently.
Together with robots.txt
Sitemap is basically used by search engines, when using this plugin, you'd better ensure that you have a valid robots.txt
in the .vuepress/public
directory to allow search engines spiders to visit your site. The simplest robots.txt is as follows (allow all search engines to access all paths)
User-agent: *
+
+Allow: /
+
Last Updated:
Contributors: Mr.Hope
Config
+
+
+
diff --git a/plugins/sitemap/index.html b/plugins/sitemap/index.html
new file mode 100644
index 0000000000..be4e14f839
--- /dev/null
+++ b/plugins/sitemap/index.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ sitemap | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development npm i -D @vuepress/plugin-sitemap@next
+
import { sitemapPlugin } from '@vuepress/plugin-sitemap'
+
+export default {
+ plugins: [
+ sitemapPlugin ({
+ // options
+ }),
+ ],
+}
+
Last Updated:
Contributors: Mr.Hope
SEO baidu-analytics
+
+
+
diff --git a/plugins/theme-data.html b/plugins/theme-data.html
new file mode 100644
index 0000000000..0af0c994af
--- /dev/null
+++ b/plugins/theme-data.html
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+ theme-data | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Provide client data for your theme, with VuePress i18n support.
This plugin is mainly used to develop themes, and has been integrated into the default theme. You won't need to use it directly in most cases.
For theme authors, this plugin will help you to use the same i18n mechanism as VuePress and the default theme. But if you don't want to provide i18n support, or you want to implement in your own way, you don't need this plugin.
npm i -D @vuepress/plugin-theme-data@next
+
import { themeDataPlugin } from '@vuepress/plugin-theme-data'
+
+export default {
+ plugins: [
+ themeDataPlugin ({
+ // options
+ }),
+ ],
+}
+
Type: ThemeData
Default: {}
Details:
The theme data object that you want to use in client side.
You can provide theme data in Node side via this option, and use it in client side via useThemeData and useThemeLocaleData .
Example:
export default {
+ plugins: [
+ themeDataPlugin ({
+ themeData: {
+ foo: 'foo' ,
+ locales: {
+ '/zh/' : {
+ foo: 'zh-foo' ,
+ },
+ },
+ },
+ }),
+ ],
+}
+
WARNING
The theme data object will be processed by JSON.stringify()
before forwarding to client side, so you should ensure that you are providing a JSON-friendly object.
import { useThemeData } from '@vuepress/plugin-theme-data/client'
+import type { ThemeData } from '@vuepress/plugin-theme-data/client'
+
+type MyThemeData = ThemeData <{
+ foo : string
+}>
+
+export default {
+ setup () {
+ const themeData = useThemeData < MyThemeData >()
+ console . log ( themeData . value )
+ },
+}
+
import { useThemeLocaleData } from '@vuepress/plugin-theme-data/client'
+import type { ThemeData } from '@vuepress/plugin-theme-data/client'
+
+type MyThemeData = ThemeData <{
+ foo : string
+}>
+
+export default {
+ setup () {
+ const themeLocaleData = useThemeLocaleData < MyThemeData >()
+ console . log ( themeLocaleData . value )
+ },
+}
+
Last Updated:
Contributors: Mr.Hope
rtl toc
+
+
+
diff --git a/plugins/toc.html b/plugins/toc.html
new file mode 100644
index 0000000000..3513077a7c
--- /dev/null
+++ b/plugins/toc.html
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+ toc | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development This plugin will provide a table-of-contents (TOC) component.
npm i -D @vuepress/plugin-toc@next
+
import { tocPlugin } from '@vuepress/plugin-toc'
+
+export default {
+ plugins: [
+ tocPlugin ({
+ // options
+ }),
+ ],
+}
+
Similar to the Table of Contents Markdown Syntax , the TOC component that provided by this plugin could be used in your markdown content directly:
<!-- markdown toc syntax -->
+
+[[ toc ]]
+
+<!-- vue toc component -->
+< Toc />
+
Both of them can be pre-rendered correctly in build mode. However, there are some differences between them.
The markdown syntax [[toc]]
could only be used in markdown files. It is parsed by markdown-it, and the generated TOC is static content.
The component <Toc/>
could be used in both markdown files and vue files. It is loaded by vue, and the generated TOC is a vue component.
This plugin could work together with @vuepress/plugin-active-header-links by setting the headerLinkSelector to match the linkClass
option. When the page scroll to a certain header anchor, this corresponding link will be added linkActiveClass
class name.
Therefore, this plugin is more useful for theme developers.
The TOC component also accepts props for customization.
< template >
+ < Toc : headers = " headers " : options = " options " />
+</ template >
+
interface PageHeader {
+ level : number
+ title : string
+ slug : string
+ children : PageHeader []
+}
+
Details:
Specify the headers array to render.
If this prop is not specified, the headers of current page will be used.
Type: Partial<TocPropsOptions>
interface TocPropsOptions {
+ containerTag : string
+ containerClass : string
+ listClass : string
+ itemClass : string
+ linkTag : 'a' | 'RouterLink' | 'RouteLink'
+ linkClass : string
+ linkActiveClass : string
+ linkChildrenActiveClass : string
+}
+
const defaultOptions = {
+ containerTag: 'nav' ,
+ containerClass: 'vuepress-toc' ,
+ listClass: 'vuepress-toc-list' ,
+ itemClass: 'vuepress-toc-item' ,
+ linkTag: 'RouteLink' ,
+ linkClass: 'vuepress-toc-link' ,
+ linkActiveClass: 'active' ,
+ linkChildrenActiveClass: 'active' ,
+}
+
Details:
Customize the TOC component.
If the containerTag
is set to an empty string ''
, the <nav>
container will be removed totally.
Example:
The rendered TOC component with default options looks like:
< template >
+ <!-- container -->
+ < nav class = "vuepress-toc" >
+ <!-- list -->
+ < ul class = "vuepress-toc-list" >
+ <!-- item -->
+ < li class = "vuepress-toc-item" >
+ <!-- link -->
+ < RouteLink class = "vuepress-toc-link" to = "#foo" > Foo </ RouteLink >
+ </ li >
+ <!-- item with children -->
+ < li class = "vuepress-toc-item" >
+ <!-- link (children active) -->
+ < RouteLink class = "vuepress-toc-link active" to = "#bar" > Bar </ RouteLink >
+ <!-- list (children) -->
+ < ul class = "vuepress-toc-list" >
+ <!-- item -->
+ < li class = "vuepress-toc-item" >
+ <!-- link (active) -->
+ < RouteLink class = "vuepress-toc-link active" to = "#bar-child" >
+ Bar Child
+ </ RouteLink >
+ </ li >
+ </ ul >
+ </ li >
+ </ ul >
+ </ nav >
+</ template >
+
Last Updated:
Contributors: Mr.Hope
theme-data
+
+
+
diff --git a/themes/default/components.html b/themes/default/components.html
new file mode 100644
index 0000000000..fbfbc5ea37
--- /dev/null
+++ b/themes/default/components.html
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+ Built-in Components | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Props:
type Type: 'tip' | 'warning' | 'danger'
Default: 'tip'
text vertical Type: 'top' | 'middle' | 'bottom' | undefined
Default: undefined
Example:
Input
- VuePress - < Badge type = "tip" text = "v2" vertical = "top" />
+- VuePress - < Badge type = "warning" text = "v2" vertical = "middle" />
+- VuePress - < Badge type = "danger" text = "v2" vertical = "bottom" />
+
Output
VuePress - v2 VuePress - v2 VuePress - v2 Props:
title Type: string
Required: true
active Type: boolean
Default: false
Details:
This component must be placed inside a CodeGroup component.
Use the active
prop to set the initial active item, or the first item will be activated by default.
Example:
Input
< CodeGroup >
+ < CodeGroupItem title = "pnpm" >
+
+```bash:no-line-numbers
+pnpm install
+```
+
+ </ CodeGroupItem >
+
+ < CodeGroupItem title = "yarn" >
+
+```bash:no-line-numbers
+yarn install
+```
+
+ </ CodeGroupItem >
+
+ < CodeGroupItem title = "npm" active >
+
+```bash:no-line-numbers
+npm install
+```
+
+ </ CodeGroupItem >
+</ CodeGroup >
+
Output
WARNING
You must add an empty line between the starting tag of <CodeGroupItem>
and the code fence, otherwise the code fence will not be parsed correctly by Markdown.
All content must be valid Markdown first, and then a Vue SFC.
Learn more: Cookbook > Markdown and Vue SFCopen in new window
Alternatively, you can use the custom containers .
Last Updated:
Contributors: Mr.Hope
Frontmatter Markdown
+
+
+
diff --git a/themes/default/config.html b/themes/default/config.html
new file mode 100644
index 0000000000..87e083e43a
--- /dev/null
+++ b/themes/default/config.html
@@ -0,0 +1,177 @@
+
+
+
+
+
+
+
+
+ Config | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Type: { [path: string]: Partial<DefaultThemeLocaleData> }
Default: {}
Details:
Specify locales for i18n support.
All the options inside the Locale Config section can be used in locales.
This option will only take effect in default theme, so don't confuse with locales
in Site Config .
Also see:
Config of this section can be used as normal config, and can also be used in the locales option.
Type: 'auto' | 'light' | 'dark'
Default: 'auto'
Details:
Default color mode.
If set to 'auto'
, the initial color mode will be automatically set according to prefers-color-schemeopen in new window .
Also see:
Type: boolean
Default: true
Details:
Enable color mode switching or not.
If set to true
, a button to switch color mode will be displayed in the navbar.
Also see:
Type: false | (NavbarItem | NavbarGroup | string)[]
Default: []
Details:
Configuration of navbar.
Set to false
to disable navbar.
To configure the navbar items, you can set it to a navbar array , each item of which could be a NavbarItem
object, a NavbarGroup
object, or a string:
A NavbarItem
object should have a text
field and a link
field, could have an optional activeMatch
field. A NavbarGroup
object should have a text
field and a children
field. The children
field should be a navbar array , too. A string should be the path to the target page file. It will be converted to a NavbarItem
object, using the page title as text
, and the page route path as link
. Example 1:
export default {
+ theme: defaultTheme ({
+ navbar: [
+ // NavbarItem
+ {
+ text: 'Foo' ,
+ link: '/foo/' ,
+ },
+ // NavbarGroup
+ {
+ text: 'Group' ,
+ children: [ '/group/foo.md' , '/group/bar.md' ],
+ },
+ // string - page file path
+ '/bar/README.md' ,
+ ],
+ }),
+}
+
export default {
+ theme: defaultTheme ({
+ navbar: [
+ // nested group - max depth is 2
+ {
+ text: 'Group' ,
+ children: [
+ {
+ text: 'SubGroup' ,
+ children: [ '/group/sub/foo.md' , '/group/sub/bar.md' ],
+ },
+ ],
+ },
+ // control when should the item be active
+ {
+ text: 'Group 2' ,
+ children: [
+ {
+ text: 'Always active' ,
+ link: '/' ,
+ // this item will always be active
+ activeMatch: '/' ,
+ },
+ {
+ text: 'Active on /foo/' ,
+ link: '/not-foo/' ,
+ // this item will be active when current route path starts with /foo/
+ // regular expression is supported
+ activeMatch: '^/foo/' ,
+ },
+ ],
+ },
+ ],
+ }),
+}
+
Type: null | string
Details:
Specify the url of logo image.
The logo image will be displayed at the left end of the navbar.
Set to null
to disable logo.
Example:
export default {
+ theme: defaultTheme ({
+ // public file path
+ logo: '/hero.png' ,
+ // url
+ logo: 'https://vuejs.org/images/logo.png' ,
+ }),
+}
+
Type: null | string
Details:
Specify the url of logo image to be used in dark mode.
You can make use of this option if you want to use different logo config in dark mode.
Set to null
to disable logo in dark mode. Omit this option to use logo in dark mode.
Also see:
Type: string
Details:
Specify the repository url of your project.
This will be used as the link of the repository link , which will be displayed as the last item of the navbar.
export default {
+ theme: defaultTheme ({
+ // If you set it in the form of `organization/repository`
+ // we will take it as a GitHub repo
+ repo: 'vuejs/vuepress' ,
+ // You can also set it to a URL directly
+ repo: 'https://gitlab.com/foo/bar' ,
+ }),
+}
+
Type: false | 'auto' | SidebarConfigArray | SidebarConfigObject
Default: 'auto'
Details:
Configuration of sidebar.
You can override this global option via sidebar frontmatter in your pages.
Set to false
to disable sidebar.
If you set it to 'auto'
, the sidebar will be automatically generated from the page headers.
To configure the sidebar items manually, you can set this option to a sidebar array , each item of which could be a SidebarItem
object or a string:
A SidebarItem
object should have a text
field, could have an optional link
field, an optional children
field and an optional collapsible
field. The children
field should be a sidebar array . The collapsible
field controls whether the item is collapsible. A string should be the path to the target page file. It will be converted to a SidebarItem
object, whose text
is the page title, link
is the page route path, and children
is automatically generated from the page headers. If you want to set different sidebar for different sub paths, you can set this option to a sidebar object :
The key should be the path prefix. The value should be a sidebar array or set to 'heading'
to automatically generate the sidebar from the page headers for just the corresponding path. Example 1:
export default {
+ theme: defaultTheme ({
+ // sidebar array
+ // all pages will use the same sidebar
+ sidebar: [
+ // SidebarItem
+ {
+ text: 'Foo' ,
+ link: '/foo/' ,
+ children: [
+ // SidebarItem
+ {
+ text: 'github' ,
+ link: 'https://github.com' ,
+ children: [],
+ },
+ // string - page file path
+ '/foo/bar.md' ,
+ ],
+ },
+ // string - page file path
+ '/bar/README.md' ,
+ ],
+ }),
+}
+
export default {
+ theme: defaultTheme ({
+ // sidebar object
+ // pages under different sub paths will use different sidebar
+ sidebar: {
+ '/guide/' : [
+ {
+ text: 'Guide' ,
+ children: [ '/guide/introduction.md' , '/guide/getting-started.md' ],
+ },
+ ],
+ '/reference/' : 'heading' ,
+ },
+ }),
+}
+
export default {
+ theme: defaultTheme ({
+ // collapsible sidebar
+ sidebar: {
+ '/reference/' : [
+ {
+ text: 'VuePress Reference' ,
+ collapsible: true ,
+ children: [ '/reference/cli.md' , '/reference/config.md' ],
+ },
+ {
+ text: 'Bundlers Reference' ,
+ collapsible: true ,
+ children: [
+ '/reference/bundler/vite.md' ,
+ '/reference/bundler/webpack.md' ,
+ ],
+ },
+ ],
+ },
+ }),
+}
+
Type: number
Default: 2
Details:
Set the maximum depth of the sidebar children which are automatically generated from the page headers.
Set to 0
to disable all levels of headers. Set to 1
to include <h2>
headers. Set to 2
to include <h2>
and <h3>
headers. ... The max value depends on which levels of headers you have extracted via markdown.headers.levelopen in new window .
The default value of markdown.headers.level
is [2, 3]
, so the default max value of sidebarDepth
is 2
.
You can override this global option via sidebarDepth frontmatter in your pages.
Type: string
Details:
Specify the pattern of the edit this page link.
This will be used for generating the edit this page link.
If you don't set this option, the pattern will be inferred from the docsRepo option. But if your documentation repository is not hosted on a common platform, for example, GitHub, GitLab, Bitbucket, Gitee, etc., you have to set this option explicitly to make the edit this page link work.
Usage:
Pattern Description :repo
The docs repo url, i.e. docsRepo :branch
The docs repo branch, i.e. docsBranch :path
The path of the page source file, i.e. docsDir joins the relative path of the page file
Example:
export default {
+ theme: defaultTheme ({
+ docsRepo: 'https://gitlab.com/owner/name' ,
+ docsBranch: 'master' ,
+ docsDir: 'docs' ,
+ editLinkPattern: ':repo/-/edit/:branch/:path' ,
+ }),
+}
+
The generated link will look like 'https://gitlab.com/owner/name/-/edit/master/docs/path/to/file.md'
.
Type: string
Details:
Specify the repository url of your documentation source files.
This will be used for generating the edit this page link.
If you don't set this option, it will use the repo option by default. But if your documentation source files are in a different repository, you will need to set this option.
Type: boolean
Default: true
Details:
Enable the last updated timestamp or not.
You can override this global option via lastUpdated frontmatter in your pages. Notice that if you have already set this option to false
, this feature will be disabled totally and could not be enabled in locales nor page frontmatter.
Type: boolean
Default: true
Details:
Enable the contributors list or not.
You can override this global option via contributors frontmatter in your pages. Notice that if you have already set this option to false
, this feature will be disabled totally and could not be enabled in locales nor page frontmatter.
Last Updated:
Contributors: Mr.Hope
Plugins Config
+
+
+
diff --git a/themes/default/extending.html b/themes/default/extending.html
new file mode 100644
index 0000000000..2e661204dd
--- /dev/null
+++ b/themes/default/extending.html
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+ Extending | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development VuePress default theme is widely used by users, so it is designed to be extendable, allowing users to make their own customization with ease.
Default theme's Layout
provides some slots:
navbar
navbar-before
navbar-after
sidebar
sidebar-top
sidebar-bottom
page
page-top
page-bottom
page-content-top
page-content-bottom
With the help of them, you can add or replace content easily. Here comes an example to introduce how to extend default theme with layout slots.
Firstly, create a client config file .vuepress/client.ts
:
import { defineClientConfig } from 'vuepress/client'
+import Layout from './layouts/Layout.vue'
+
+export default defineClientConfig ({
+ layouts: {
+ Layout ,
+ },
+})
+
Next, create the .vuepress/layouts/Layout.vue
, and make use of the slots that provided by the Layout
of default theme:
< script setup >
+import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
+</ script >
+
+< template >
+ < ParentLayout >
+ < template # page-bottom >
+ < div class = "my-footer" > This is my custom page footer </ div >
+ </ template >
+ </ ParentLayout >
+</ template >
+
+< style lang = "css" >
+.my-footer {
+ text-align : center ;
+}
+</ style >
+
Then the default Layout
layout has been overridden by your own local layout, which will add a custom footer to every normal pages in default theme (excluding homepage):
The layout slots are useful, but sometimes you might find it's not flexible enough. Default theme also provides the ability to replace a single component.
Default theme has registered aliasopen in new window for every non-global componentsopen in new window with a @theme
prefix. For example, the alias of HomeFooter.vue
is @theme/HomeFooter.vue
.
Then, if you want to replace the HomeFooter.vue
component, just override the alias in your config file .vuepress/config.ts
:
import { defaultTheme } from '@vuepress/theme-default'
+import { getDirname , path } from 'vuepress/utils'
+import { defineUserConfig } from 'vuepress'
+
+const __dirname = getDirname ( import . meta . url )
+
+export default defineUserConfig ({
+ theme: defaultTheme (),
+ alias: {
+ '@theme/HomeFooter.vue' : path . resolve (
+ __dirname ,
+ './components/MyHomeFooter.vue' ,
+ ),
+ },
+})
+
Instead of extending the default theme directly in .vuepress/config.ts
and .vuepress/client.ts
, you can also develop your own theme extending the default theme:
import { defaultTheme , type DefaultThemeOptions } from '@vuepress/theme-default'
+import type { Theme } from 'vuepress/core'
+import { getDirname , path } from 'vuepress/utils'
+
+const __dirname = getDirname ( import . meta . url )
+
+export const childTheme = ( options : DefaultThemeOptions ): Theme => {
+ return {
+ name: 'vuepress-theme-child' ,
+ extends: defaultTheme ( options ),
+
+ // override layouts in child theme's client config file
+ // notice that you would build ts to js before publishing to npm,
+ // so this should be the path to the js file
+ clientConfigFile: path . resolve ( __dirname , './client.js' ),
+
+ // override component alias
+ alias: {
+ '@theme/HomeFooter.vue' : path . resolve (
+ __dirname ,
+ './components/MyHomeFooter.vue' ,
+ ),
+ },
+ }
+}
+
Last Updated:
Contributors: Mr.Hope
Styles
+
+
+
diff --git a/themes/default/frontmatter.html b/themes/default/frontmatter.html
new file mode 100644
index 0000000000..263e632918
--- /dev/null
+++ b/themes/default/frontmatter.html
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+ Frontmatter | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Frontmatter in this section will take effect in all types of pages.
Type: boolean
Details:
Show navbar on this page or not.
If you disable navbar in theme config, this frontmatter will not take effect.
Also see:
---
+pageClass : custom-page-class
+---
+
Then you can customize styles of this page in .vuepress/styles/index.scss
file:
.theme-container.custom-page-class {
+ /* page styles */
+}
+
Frontmatter in this section will only take effect in home pages.
Type: boolean
Details:
Specify whether the page is homepage or a normal page.
If you don't set this frontmatter or set it to false
, the page would be a normal page .
Example:
---
+# public file path
+heroImage : /images/hero.png
+# url
+heroImage : https://vuejs.org/images/logo.png
+---
+
Type: number
Default: 280
Details:
Specify the height
attribute of the hero <img>
tag.
You may need to reduce this value if the height of your hero image is less than the default value.
Notice that the height is also constrained by CSS. This attribute is to reduce Cumulative Layout Shift (CLS)open in new window that caused by the loading of the hero image.
Type: string | null
Details:
Specify the the hero text.
This will fallback to the site titleopen in new window .
Set to null
to disable hero text.
Array <{
+ text : string
+ link : string
+ type ?: 'primary' | 'secondary'
+}>
+
---
+actions :
+ - text : Get Started
+ link : /guide/getting-started.html
+ type : primary
+ - text : Introduction
+ link : /guide/introduction.html
+ type : secondary
+---
+
Array <{
+ title : string
+ details : string
+}>
+
---
+features :
+ - title : Simplicity First
+ details : Minimal setup with markdown-centered project structure helps you focus on writing.
+ - title : Vue-Powered
+ details : Enjoy the dev experience of Vue, use Vue components in markdown, and develop custom themes with Vue.
+ - title : Performant
+ details : VuePress generates pre-rendered static HTML for each page, and runs as an SPA once a page is loaded.
+---
+
Frontmatter in this section will only take effect in normal pages.
Type: NavLink | string
Details:
Specify the link of the previous page.
If you don't set this frontmatter, the link will be inferred from the sidebar config.
To configure the prev link manually, you can set this frontmatter to a NavLink
object or a string:
A NavLink
object should have a text
field and a link
field. A string should be the path to the target page file. It will be converted to a NavLink
object, whose text
is the page title, and link
is the page route path. Example:
---
+# NavLink
+prev :
+ text : Get Started
+ link : /guide/getting-started.html
+
+# NavLink - external url
+prev :
+ text : GitHub
+ link : https://github.com
+
+# string - page file path
+prev : /guide/getting-started.md
+
+# string - page file relative path
+prev : ../../guide/getting-started.md
+---
+
Type: NavLink | string
Details:
Specify the link of the next page.
If you don't set this frontmatter, the link will be inferred from the sidebar config.
The type is the same as prev frontmatter.
Last Updated:
Contributors: Mr.Hope
Locale Config Built-in Components
+
+
+
diff --git a/themes/default/index.html b/themes/default/index.html
new file mode 100644
index 0000000000..d32d8830aa
--- /dev/null
+++ b/themes/default/index.html
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+ theme-default | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Install @vuepress/theme-default
:
npm install @vuepress/theme-default@next
+
import { defaultTheme } from '@vuepress/theme-default'
+
+export default {
+ theme: defaultTheme ({
+ // set theme config here
+ }),
+}
+
Last Updated:
Contributors: Mr.Hope
+
+
+
diff --git a/themes/default/locale.html b/themes/default/locale.html
new file mode 100644
index 0000000000..b5ba606e1e
--- /dev/null
+++ b/themes/default/locale.html
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+ Locale Config | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development These options configure locale-related texts.
If your site is served in a different language besides English, you should set these options per locale to provide translations.
Type: string
Details:
Specify the repository label of your project.
This will be used as the text of the repository link , which will be displayed as the last item of the navbar.
If you don't set this option explicitly, it will be automatically inferred from the repo option.
Type: string
Details:
Specify the name of the language of a locale.
This option will only take effect inside the locales of your theme config. It will be used as the language name of the locale, which will be displayed in the select language menu .
Example:
export default {
+ locales: {
+ '/' : {
+ lang: 'en-US' ,
+ },
+ '/zh/' : {
+ lang: 'zh-CN' ,
+ },
+ },
+ theme: defaultTheme ({
+ locales: {
+ '/' : {
+ selectLanguageName: 'English' ,
+ },
+ '/zh/' : {
+ selectLanguageName: '简体中文' ,
+ },
+ },
+ }),
+}
+
Type: string
Default: 'open in new window'
Details:
Specify the sr-only
text of the ExternalLinkIcon .
This is mainly for a11y purpose.
Also see:
Type: string
Default: 'toggle color mode'
Details:
Title text for the color mode toggle button.
This is mainly for a11y purpose.
Also see:
Type: string
Default: 'toggle sidebar'
Details:
Title text for sidebar toggle button.
This is mainly for a11y purpose.
Last Updated:
Contributors: Mr.Hope
Plugins Config Frontmatter
+
+
+
diff --git a/themes/default/markdown.html b/themes/default/markdown.html
new file mode 100644
index 0000000000..c0ed2a9999
--- /dev/null
+++ b/themes/default/markdown.html
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+ Markdown | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Input
::: tip
+This is a tip
+:::
+
+::: warning
+This is a warning
+:::
+
+::: danger
+This is a dangerous warning
+:::
+
+::: details
+This is a details block
+:::
+
Output
DANGER
This is a dangerous warning
This is a details block
Example 2 (custom title): Input
::: danger STOP
+Danger zone, do not proceed
+:::
+
+::: details Click me to view the code
+
+```ts
+console . log ( 'Hello, VuePress!' )
+```
+
+:::
+
Output
STOP
Danger zone, do not proceed
Click me to view the code console . log ( 'Hello, VuePress!' )
+
Example 3 (code group alias): Input
:::: code-group
+::: code-group-item FOO
+
+```ts
+const foo = 'foo'
+```
+
+:::
+
+::: code-group-item BAR
+
+```ts
+const bar = 'bar'
+```
+
+:::
+::::
+
Output
Last Updated:
Contributors: Mr.Hope
Built-in Components Styles
+
+
+
diff --git a/themes/default/plugin.html b/themes/default/plugin.html
new file mode 100644
index 0000000000..8943bee51c
--- /dev/null
+++ b/themes/default/plugin.html
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+ Plugins Config | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development You can configure the plugins that used by default theme with themePlugins
.
Default theme is using some plugins by default. You can disable a plugin if you really do not want to use it. Make sure you understand what the plugin is for before disabling it.
import { defaultTheme } from '@vuepress/theme-default'
+
+export default {
+ theme: defaultTheme ({
+ themePlugins: {
+ // customize theme plugins here
+ },
+ }),
+}
+
Type: BackToTopPluginOptions | boolean
Default: true
Details:
Enable @vuepress/plugin-back-to-top or not.
Object value is supported as plugin options.
Type: Record<ContainerType, boolean>
Details:
Enable custom containers that powered by @vuepress/plugin-container or not.
ContainerType
type is:
tip
warning
danger
details
codeGroup
codeGroupItem
Also see:
Type: CopyCodePluginOptions | boolean
Default: true
Details:
Enable @vuepress/plugin-copy-code or not.
Object value is supported as plugin options.
Type: SeoPluginOptions | boolean
Default: true
Details:
Enable @vuepress/plugin-seo or not.
Object value is supported as plugin options.
Type: SitemapPluginOptions | boolean
Default: true
Details:
Enable @vuepress/plugin-sitemap or not.
Object value is supported as plugin options.
Last Updated:
Contributors: Mr.Hope
Config Locale Config
+
+
+
diff --git a/themes/default/styles.html b/themes/default/styles.html
new file mode 100644
index 0000000000..9508a6833f
--- /dev/null
+++ b/themes/default/styles.html
@@ -0,0 +1,299 @@
+
+
+
+
+
+
+
+
+ Styles | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development The default theme uses SASSopen in new window as the CSS pre-processor.
Users can customize style variables via a palette file , and add extra styles via a style file .
The path of the palette file is .vuepress/styles/palette.scss
.
You can make use of it to override predefined SASS variables of the default theme.
Click to expand SASS variables // responsive breakpoints
+$MQNarrow : 959px !default ;
+$MQMobile : 719px !default ;
+$MQMobileNarrow : 419px !default ;
+
The path of the style file is .vuepress/styles/index.scss
.
You can add extra styles here, or override the default styles:
:root {
+ scroll-behavior : smooth ;
+}
+
You can also make use of it to override predefined CSS variables of the default theme.
Click to expand CSS variables :root {
+ // brand colors
+ --c-brand : #3eaf7c ;
+ --c-brand-light : #4abf8a ;
+
+ // background colors
+ --c-bg : #ffffff ;
+ --c-bg-light : #f3f4f5 ;
+ --c-bg-lighter : #eeeeee ;
+ --c-bg-dark : #ebebec ;
+ --c-bg-darker : #e6e6e6 ;
+ --c-bg-navbar : var ( --c-bg );
+ --c-bg-sidebar : var ( --c-bg );
+ --c-bg-arrow : #cccccc ;
+
+ // text colors
+ --c-text : #2c3e50 ;
+ --c-text-accent : var ( --c-brand );
+ --c-text-light : #3a5169 ;
+ --c-text-lighter : #4e6e8e ;
+ --c-text-lightest : #6a8bad ;
+ --c-text-quote : #999999 ;
+
+ // border colors
+ --c-border : #eaecef ;
+ --c-border-dark : #dfe2e5 ;
+
+ // custom container colors
+ --c-tip : #42b983 ;
+ --c-tip-bg : var ( --c-bg-light );
+ --c-tip-title : var ( --c-text );
+ --c-tip-text : var ( --c-text );
+ --c-tip-text-accent : var ( --c-text-accent );
+ --c-warning : #ffc310 ;
+ --c-warning-bg : #fffae3 ;
+ --c-warning-bg-light : #fff3ba ;
+ --c-warning-bg-lighter : #fff0b0 ;
+ --c-warning-border-dark : #f7dc91 ;
+ --c-warning-details-bg : #fff5ca ;
+ --c-warning-title : #f1b300 ;
+ --c-warning-text : #746000 ;
+ --c-warning-text-accent : #edb100 ;
+ --c-warning-text-light : #c1971c ;
+ --c-warning-text-quote : #ccab49 ;
+ --c-danger : #f11e37 ;
+ --c-danger-bg : #ffe0e0 ;
+ --c-danger-bg-light : #ffcfde ;
+ --c-danger-bg-lighter : #ffc9c9 ;
+ --c-danger-border-dark : #f1abab ;
+ --c-danger-details-bg : #ffd4d4 ;
+ --c-danger-title : #ed1e2c ;
+ --c-danger-text : #660000 ;
+ --c-danger-text-accent : #bd1a1a ;
+ --c-danger-text-light : #b5474d ;
+ --c-danger-text-quote : #c15b5b ;
+ --c-details-bg : #eeeeee ;
+
+ // badge component colors
+ --c-badge-tip : var ( --c-tip );
+ --c-badge-warning : #ecc808 ;
+ --c-badge-warning-text : var ( --c-bg );
+ --c-badge-danger : #dc2626 ;
+ --c-badge-danger-text : var ( --c-bg );
+
+ // code group colors
+ --c-code-group-tab-title : rgba ( 255 , 255 , 255 , 0.9 );
+ --c-code-group-tab-bg : var ( --code-bg-color );
+ --c-code-group-tab-outline : var ( var ( --c-code-group-tab-title ));
+ --c-code-group-tab-active-border : var ( --c-brand );
+
+ // transition vars
+ --t-color : 0.3s ease ;
+ --t-transform : 0.3s ease ;
+
+ // code blocks vars
+ --code-bg-color : #282c34 ;
+ --code-hl-bg-color : rgba ( 0 , 0 , 0 , 0.66 );
+ --code-ln-color : #9e9e9e ;
+ --code-ln-wrapper-width : 3.5rem ;
+
+ // font vars
+ --font-family : -apple-system, BlinkMacSystemFont, 'Segoe UI' , Roboto, Oxygen,
+ Ubuntu, Cantarell, 'Fira Sans' , 'Droid Sans' , 'Helvetica Neue' , sans-serif ;
+ --font-family-code : Consolas, Monaco, 'Andale Mono' , 'Ubuntu Mono' , monospace ;
+
+ // layout vars
+ --navbar-height : 3.6rem ;
+ --navbar-padding-v : 0.7rem ;
+ --navbar-padding-h : 1.5rem ;
+ --sidebar-width : 20rem ;
+ --sidebar-width-mobile : calc ( var ( --sidebar-width ) * 0.82 );
+ --content-width : 740px ;
+ --homepage-width : 960px ;
+}
+
+// plugin-back-to-top
+.vp-back-to-top-button {
+ --back-to-top-color : var ( --c-brand );
+ --back-to-top-color-hover : var ( --c-brand-light );
+ --back-to-top-bg-color : var ( --c-bg );
+}
+
+// plugin-catalog
+.vp-catalog-wrapper {
+ --catalog-bg-color : var ( --c-bg );
+ --catalog-bg-secondary-color : var ( --c-bg-dark );
+ --catalog-border-color : var ( --c-border );
+ --catalog-active-color : var ( --c-brand );
+ --catalog-hover-color : var ( --c-brand-light );
+}
+
+// plugin-docsearch
+.DocSearch {
+ --docsearch-primary-color : var ( --c-brand );
+ --docsearch-text-color : var ( --c-text );
+ --docsearch-highlight-color : var ( --c-brand );
+ --docsearch-muted-color : var ( --c-text-quote );
+ --docsearch-container-background : rgba ( 9 , 10 , 17 , 0.8 );
+ --docsearch-modal-background : var ( --c-bg-light );
+ --docsearch-searchbox-background : var ( --c-bg-lighter );
+ --docsearch-searchbox-focus-background : var ( --c-bg );
+ --docsearch-searchbox-shadow : inset 0 0 0 2px var ( --c-brand );
+ --docsearch-hit-color : var ( --c-text-light );
+ --docsearch-hit-active-color : var ( --c-bg );
+ --docsearch-hit-background : var ( --c-bg );
+ --docsearch-hit-shadow : 0 1px 3px 0 var ( --c-border-dark );
+ --docsearch-footer-background : var ( --c-bg );
+}
+
+// plugin-external-link-icon
+.external-link-icon {
+ --external-link-icon-color : var ( --c-text-quote );
+}
+
+// plugin-medium-zoom
+.medium-zoom-overlay {
+ --medium-zoom-bg-color : var ( --c-bg );
+}
+
+// plugin-nprogress
+#nprogress {
+ --nprogress-color : var ( --c-brand );
+}
+
+// plugin-photo-swipe
+body {
+ --photo-swipe-bullet : var ( --c-bg );
+ --photo-swipe-bullet-active : var ( --c-brand );
+}
+
+// plugin-pwa-popup
+html {
+ --pwa-text-color : var ( --c-text );
+ --pwa-bg-color : var ( --c-bg );
+ --pwa-border-color : var ( --c-brand );
+ --pwa-btn-text-color : var ( --c-bg );
+ --pwa-btn-bg-color : var ( --c-brand );
+ --pwa-btn-hover-bg-color : var ( --c-brand-light );
+}
+
+html.dark {
+ --pwa-shadow-color : rgb ( 0 0 0 / 30% );
+ --pwa-content-color : #ccc ;
+ --pwa-content-light-color : #999 ;
+}
+
+// plugin-redirect
+.language-modal-mask {
+ --redirect-bg-color : var ( --c-bg );
+ --redirect-bg-color-light : var ( --c-bg-light );
+ --redirect-bg-color-lighter : var ( --c-bg-lighter );
+ --redirect-text-color : var ( --c-text );
+ --redirect-primary-bg-color : var ( --c-brand );
+ --redirect-primary-hover-bg-color : var ( --c-brand-light );
+ --redirect-primary-text-color : var ( --c-bg );
+}
+
+// plugin-search
+.search-box {
+ --search-bg-color : var ( --c-bg );
+ --search-accent-color : var ( --c-brand );
+ --search-text-color : var ( --c-text );
+ --search-border-color : var ( --c-border );
+
+ --search-item-text-color : var ( --c-text-lighter );
+ --search-item-focus-bg-color : var ( --c-bg-light );
+}
+
Click to expand dark mode CSS variables html.dark {
+ // brand colors
+ --c-brand : #3aa675 ;
+ --c-brand-light : #349469 ;
+
+ // background colors
+ --c-bg : #22272e ;
+ --c-bg-light : #2b313a ;
+ --c-bg-lighter : #262c34 ;
+ --c-bg-dark : #343b44 ;
+ --c-bg-darker : #37404c ;
+
+ // text colors
+ --c-text : #adbac7 ;
+ --c-text-light : #96a7b7 ;
+ --c-text-lighter : #8b9eb0 ;
+ --c-text-lightest : #8094a8 ;
+
+ // border colors
+ --c-border : #3e4c5a ;
+ --c-border-dark : #34404c ;
+
+ // custom container colors
+ --c-tip : #318a62 ;
+ --c-warning : #e0ad15 ;
+ --c-warning-bg : #2d2f2d ;
+ --c-warning-bg-light : #423e2a ;
+ --c-warning-bg-lighter : #44442f ;
+ --c-warning-border-dark : #957c35 ;
+ --c-warning-details-bg : #39392d ;
+ --c-warning-title : #fdca31 ;
+ --c-warning-text : #d8d96d ;
+ --c-warning-text-accent : #ffbf00 ;
+ --c-warning-text-light : #ddb84b ;
+ --c-warning-text-quote : #ccab49 ;
+ --c-danger : #fc1e38 ;
+ --c-danger-bg : #39232c ;
+ --c-danger-bg-light : #4b2b35 ;
+ --c-danger-bg-lighter : #553040 ;
+ --c-danger-border-dark : #a25151 ;
+ --c-danger-details-bg : #482936 ;
+ --c-danger-title : #fc2d3b ;
+ --c-danger-text : #ea9ca0 ;
+ --c-danger-text-accent : #fd3636 ;
+ --c-danger-text-light : #d9777c ;
+ --c-danger-text-quote : #d56b6b ;
+ --c-details-bg : #323843 ;
+
+ // badge component colors
+ --c-badge-warning : var ( --c-warning );
+ --c-badge-warning-text : #3c2e05 ;
+ --c-badge-danger : var ( --c-danger );
+ --c-badge-danger-text : #401416 ;
+
+ // code blocks vars
+ --code-hl-bg-color : #363b46 ;
+}
+
+// plugin-docsearch
+html.dark .DocSearch {
+ --docsearch-logo-color : var ( --c-text );
+ --docsearch-modal-shadow : inset 1px 1px 0 0 #2c2e40 , 0 3px 8px 0 #000309 ;
+ --docsearch-key-shadow : inset 0 -2px 0 0 #282d55 , inset 0 0 1px 1px #51577d ,
+ 0 2px 2px 0 rgba ( 3 , 4 , 9 , 0.3 );
+ --docsearch-key-gradient : linear-gradient ( -225deg , #444950 , #1c1e21 );
+ --docsearch-footer-shadow : inset 0 1px 0 0 rgba ( 73 , 76 , 106 , 0.5 ),
+ 0 -4px 8px 0 rgba ( 0 , 0 , 0 , 0.2 );
+}
+
Last Updated:
Contributors: Mr.Hope
Markdown Extending
+
+
+
diff --git a/themes/index.html b/themes/index.html
new file mode 100644
index 0000000000..642632e1aa
--- /dev/null
+++ b/themes/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ Themes | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Last Updated:
Contributors: Mr.Hope
+
+
+
diff --git a/tools/helper/client.html b/tools/helper/client.html
new file mode 100644
index 0000000000..a37e130f77
--- /dev/null
+++ b/tools/helper/client.html
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+ Client Related | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Check whether a component is registered globally.
TIP
Local import of the component does not affect the result. When calling outside setup
scope, you need to pass the app
instance as the second parameter. export const hasGlobalComponent : ( name : string , app ?: App ) => boolean
+
Example // if you globally register `<my-component>`
+hasGlobalComponent ( 'MyComponent' ) // true
+hasGlobalComponent ( 'my-component' ) // true
+
+hasGlobalComponent ( 'MyComponent2' ) // false
+
Get current locale config from locales settings.
export const useLocaleConfig : < T extends LocaleData >(
+ localesConfig : RequiredLocaleConfig < T >,
+) => ComputedRef < T >
+
Example const localesCOnfig = {
+ '/' : 'Title' ,
+ '/zh/' : '标题' ,
+}
+
+const locale = useLocaleConfig ( localesConfig )
+
+// under `/page`
+locale . value // 'Title'
+
+// under `/zh/page`
+locale . value // '标题'
+
Last Updated:
Contributors: Mr.Hope
Page Related Shared Methods
+
+
+
diff --git a/tools/helper/index.html b/tools/helper/index.html
new file mode 100644
index 0000000000..2c69440548
--- /dev/null
+++ b/tools/helper/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ @vuepress/helper | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development This package is a helper utility for VuePress developers.
Last Updated:
Contributors: Mr.Hope
+
+
+
diff --git a/tools/helper/node/bundler.html b/tools/helper/node/bundler.html
new file mode 100644
index 0000000000..add55141b7
--- /dev/null
+++ b/tools/helper/node/bundler.html
@@ -0,0 +1,212 @@
+
+
+
+
+
+
+
+
+ Bundler Related | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development Bundler function is for appending or modifying bundler options in theme and plugins.
All functions should be called in extendsBundlerOptions
lifecycle hook.
TIP
We are omitting that in examples. The actual code should be like this:
// import functions you need
+import { addCustomElement } from '@vuepress/helper'
+
+export const yourPlugin = {
+ // ...
+ extendsBundlerOptions : ( bundlerOptions , app ) => {
+ // add them here
+ addCustomElement ( bundlerOptions , app , 'my-custom-element' )
+ },
+}
+
Get current bundler name.
export const getBundlerName : ( app : App ) => string
+
Example // @vuepress/bundler-vite
+getBundleName ( app ) === 'vite' // true
+// @vuepress/bundler-webpack
+getBundleName ( app ) === 'webpack' // true
+
Add a custom element declaration to the current bundler.
/**
+ * Add tags as customElement
+ *
+ * @param bundlerOptions VuePress Bundler config
+ * @param app VuePress Node App
+ * @param customElements tags recognized as custom element
+ */
+export const addCustomElement = (
+ bundlerOptions : unknown ,
+ app : App ,
+ customElement : string [] | string | RegExp
+) => void ;
+
Example import { addCustomElement } from '@vuepress/helper'
+
+addCustomElement ( bundlerConfig , app , 'my-custom-element' )
+addCustomElement ( bundlerOptions , app , [
+ 'custom-element1' ,
+ 'custom-element2' ,
+ // all tags start with `math-`
+ / ^ math-/ ,
+])
+
Provides contents for specific path in dev server.
export interface DevServerOptions {
+ /**
+ * Path to be responded
+ */
+ path : string ;
+ /**
+ * Respond function
+ */
+ response : ( request ?: IncomingMessage ) => Promise < string | Buffer >;
+
+ /**
+ * error msg
+ */
+ errMsg ?: string ;
+}
+
+/**
+ * Handle specific path when running VuePress Dev Server
+ *
+ * @param bundlerOptions VuePress Bundler config
+ * @param app VuePress Node App
+ * @param path Path to be responded
+ * @param response respond function
+ * @param errMsg error msg
+ */
+export const customizeDevServer : (
+ bundlerOptions : unknown ,
+ app : App ,
+ {
+ errMsg : "The server encountered an error" ,
+ response : responseHandler ,
+ path ,
+ }: CustomServerOptions
+) => void ;
+
Example import { useCustomDevServer } from '@vuepress/helper'
+
+// handle `/api/` path
+useCustomDevServer ( bundlerOptions , app , {
+ path: '/api/' ,
+ response : async () => getData (),
+ errMsg: 'Unexpected api error' ,
+})
+
addViteOptimizeDepsInclude
Add modules to Vite optimizeDeps.include
list
TIP
If a package meets one of the following conditions, you should consider adding it here.
It's in CJS format It's dependencies include CJS package It's dynamically imported via import()
addViteOptimizeDepsExclude
Add modules to Vite optimizeDeps.exclude
list
If a package and its dependencies are all pure ESM packages, you should consider adding it here.
addViteSsrExternal
Add modules to Vite ssr.external
list
If a package is a pure ESM package and does not use aliases or define variables, you should consider adding it here.
addViteSsrNoExternal
Add modules to Vite ssr.noExternal
list
If an alias or define is used within a package, you must add it here.
/**
+ * Add modules to Vite `optimizeDeps.include` list
+ */
+export const addViteOptimizeDepsInclude : (
+ bundlerOptions : unknown ,
+ app : App ,
+ module : string | string [],
+) => void
+
+/**
+ * Add modules to Vite `optimizeDeps.exclude` list
+ */
+export const addViteOptimizeDepsExclude : (
+ bundlerOptions : unknown ,
+ app : App ,
+ module : string | string [],
+) => void
+
+/**
+ * Add modules to Vite `ssr.external` list
+ */
+export const addViteSsrExternal : (
+ bundlerOptions : unknown ,
+ app : App ,
+ module : string | string [],
+) => void
+
+/**
+ * Add modules to Vite `ssr.noExternal` list
+ */
+export const addViteSsrNoExternal : (
+ bundlerOptions : unknown ,
+ app : App ,
+ module : string | string [],
+) => void
+
Examples import {
+ addViteOptimizeDepsInclude ,
+ addViteOptimizeDepsExclude ,
+ addViteSsrExternal ,
+ addViteSsrNoExternal ,
+} from '@vuepress/helper'
+
+addViteOptimizeDepsInclude ( bundlerOptions , app , [ 'vue' , 'vue-router' ])
+addViteOptimizeDepsExclude ( bundlerOptions , app , 'packageA' )
+addViteSsrNoExternal ( bundlerOptions , app , [ 'vue' , 'vue-router' ])
+addViteSsrExternal ( bundlerOptions , app , 'packageA' )
+
addViteConfig
A function for you to add vite config
export const addViteConfig : (
+ bundlerOptions : unknown ,
+ app : App ,
+ config : Record < string , unknown >,
+) => void
+
Example import { addViteConfig } from '@vuepress/helper'
+
+addViteConfig ( bundlerOptions , app , {
+ build: {
+ charset: 'utf8' ,
+ },
+})
+
mergeViteConfig
A function for you to merge vite config.
WARNING
Your users may choose to use other bundler so it's pretty bad to declare vite as deps!
export const mergeViteConfig : (
+ defaults : Record < string , any >,
+ overrides : Record < string , any >,
+) => Record < string , any >
+
Example import { mergeViteConfig } from '@vuepress/helper'
+
+config . viteOptions = mergeViteConfig ( config . viteOptions , {
+ build: {
+ charset: 'utf8' ,
+ },
+})
+
chainWebpack
Chain webpack config.
export const chainWebpack : (
+ bundlerOptions : unknown ,
+ app : App ,
+ chainWebpack : (
+ config : WebpackChainConfig ,
+ isServer : boolean ,
+ isBuild : boolean ,
+ ) => void ,
+) => void
+
Example import { chainWebpack } from '@vuepress/helper'
+
+chainWebpack ( bundlerOptions , app , ( config , isServer , isBuild ) => {
+ // do some customize here
+})
+
configWebpack
Config Webpack
export const configWebpack : (
+ bundlerOptions : unknown ,
+ app : App ,
+ configureWebpack : (
+ config : WebpackConfiguration ,
+ isServer : boolean ,
+ isBuild : boolean ,
+ ) => void ,
+) => void
+
Example import { configWebpack } from '@vuepress/helper'
+
+configWebpack ( bundlerOptions , app , ( config , isServer , isBuild ) => {
+ // do some customize here
+})
+
Last Updated:
Contributors: Mr.Hope
Page Related
+
+
+
diff --git a/tools/helper/node/index.html b/tools/helper/node/index.html
new file mode 100644
index 0000000000..fac003c5db
--- /dev/null
+++ b/tools/helper/node/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ Node | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development
+
+
+
diff --git a/tools/helper/node/page.html b/tools/helper/node/page.html
new file mode 100644
index 0000000000..443927ea00
--- /dev/null
+++ b/tools/helper/node/page.html
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+ Page Related | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development These functions generate common information for your pages.
Get the excerpt of the page.
export interface PageExcerptOptions {
+ /**
+ * Excerpt separator
+ *
+ * @default " <!-- more --> "
+ */
+ separator ?: string
+
+ /**
+ * Length of excerpt
+ *
+ * @description Excerpt length will be the minimal possible length reaching this value
+ *
+ * @default 300
+ */
+ length ?: number
+
+ /**
+ * Tags which is considered as custom elements
+ *
+ * @description This is used to determine whether a tag is a custom element since all unknown tags are removed in excerpt.
+ */
+ isCustomElement ?: ( tagName : string ) => boolean
+
+ /**
+ * Whether keep page title (first h1) in excerpt
+ *
+ * @default false
+ */
+ keepPageTitle ?: boolean
+
+ /**
+ * Whether preserve tags like line numbers and highlight lines for code blocks
+ *
+ * @default false
+ */
+ keepFenceDom ?: boolean
+}
+
+export const getPageExcerpt : (
+ app : App ,
+ page : Page ,
+ options ?: PageExcerptOptions ,
+) => string
+
Get plain text of the page.
export interface PageTextOptions {
+ /**
+ * Whether convert text to single line content
+ *
+ * @default false
+ */
+ singleLine ?: boolean
+
+ /**
+ * Length of text
+ *
+ * @description Text length will be the minimal possible length reaching this value
+ *
+ * @default 300
+ */
+ length ?: number
+
+ /**
+ * Tags to be removed
+ *
+ * @description Table and code blocks are removed by default.
+ *
+ * @default ['table', 'pre']
+ */
+ removedTags ?: string []
+}
+
+export const getPageText : (
+ app : App ,
+ page : Page ,
+ options ?: PageTextOptions ,
+) => string
+
Last Updated:
Contributors: Mr.Hope
Bundler Related Client Related
+
+
+
diff --git a/tools/helper/shared.html b/tools/helper/shared.html
new file mode 100644
index 0000000000..2eaae650fa
--- /dev/null
+++ b/tools/helper/shared.html
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+
+ Shared Methods | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development The following functions are available on both Node.js and Client.
Encode/decode and zip/unzip data.
This is useful in markdown plugins when you want to encode string content and pass it to the component through props.
You may simply achieve this with encodeURIComponent
and decodeURIComponent
, but it can be very large if the content contains lots of special characters.
So we provide encodeData
and decodeData
to zip and encode content.
export const encodeData : (
+ data : string ,
+ level : DeflateOptions [ 'level' ] = 6 ,
+) => string
+
+export const decodeData : ( compressed : string ) => string
+
const content = `
+{
+ "type": "bar",
+ "data": {
+ "labels": ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
+ "datasets": [
+ {
+ "label": "# of Votes",
+ "data": [12, 19, 3, 5, 2, 3],
+ "backgroundColor": [
+ "rgba(255, 99, 132, 0.2)",
+ "rgba(54, 162, 235, 0.2)",
+ "rgba(255, 206, 86, 0.2)",
+ "rgba(75, 192, 192, 0.2)",
+ "rgba(153, 102, 255, 0.2)",
+ "rgba(255, 159, 64, 0.2)"
+ ],
+ "borderColor": [
+ "rgba(255, 99, 132, 1)",
+ "rgba(54, 162, 235, 1)",
+ "rgba(255, 206, 86, 1)",
+ "rgba(75, 192, 192, 1)",
+ "rgba(153, 102, 255, 1)",
+ "rgba(255, 159, 64, 1)"
+ ],
+ "borderWidth": 1
+ }
+ ]
+ },
+ "options": {
+ "scales": {
+ "y": {
+ "beginAtZero": true
+ }
+ }
+ }
+}
+`
+
+const prop = encodeData ( content ) // "eJyNUsFOwzAMve8rrHABKZqWlg5WxAE4cARxAMHEIV1NmQhNlaaCCe3fcdKtW0sLWGpjxy/v+UV512mlcIyfhTa2hHP4GgHYVYExsEQaxqlMpZWxbwAomaAqY5izO0wZB3apKnTrIyqlP1x2bRBzl9xWplC+eWNkniF7dmw1X4nWsfgaNtwNP2kfgH6Be22x9CPUUQ8yFwEHMeMQcog4UBFuiF0kcvGWGV3l6ZVW2uw0XDCTJfIwiOjYjAhESIcn4+BoT2MLio6pP6V+EBJ6AOSZgsmUwyl9A6ATwoiZn3lYTkTkRkycnuP8TU9ENPqUxuuA9i9BmxTNPy9A/G2/F9I23wtpW++FdIwPKzW2W5Afph+WqX2NQWz313XicT7XhV3qnB5f/ejKhVTYVACrXUqUmC3zC/uERsdgTYUdVr/Qb302+gZxe7S/"
+
+decodeData ( prop ) // will be the original content
+
+// if you use `encodeURIComponent`, it will be much longer
+encodeURIComponent ( content ) // '%0A%7B%0A%20%20%22type%22%3A%20%22bar%22%2C%0A%20%20%22data%22%3A%20%7B%0A%20%20%20%20%22labels%22%3A%20%5B%22Red%22%2C%20%22Blue%22%2C%20%22Yellow%22%2C%20%22Green%22%2C%20%22Purple%22%2C%20%22Orange%22%5D%2C%0A%20%20%20%20%22datasets%22%3A%20%5B%0A%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22label%22%3A%20%22%23%20of%20Votes%22%2C%0A%20%20%20%20%20%20%20%20%22data%22%3A%20%5B12%2C%2019%2C%203%2C%205%2C%202%2C%203%5D%2C%0A%20%20%20%20%20%20%20%20%22backgroundColor%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%2099%2C%20132%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(54%2C%20162%2C%20235%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20206%2C%2086%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(75%2C%20192%2C%20192%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(153%2C%20102%2C%20255%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20159%2C%2064%2C%200.2)%22%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22borderColor%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%2099%2C%20132%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(54%2C%20162%2C%20235%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20206%2C%2086%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(75%2C%20192%2C%20192%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(153%2C%20102%2C%20255%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20159%2C%2064%2C%201)%22%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22borderWidth%22%3A%201%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%5D%0A%20%20%7D%2C%0A%20%20%22options%22%3A%20%7B%0A%20%20%20%20%22scales%22%3A%20%7B%0A%20%20%20%20%20%20%22y%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22beginAtZero%22%3A%20true%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A'
+
isDef(x)
: Check if x
is defined.isBoolean(x)
: Check if x
is a boolean.isString(x)
: Check if x
is a string.isNumber(x)
: Check if x
is a number.isPlainObject(x)
: Check if x
is a plain object.isArray(x)
: Check if x
is an array.isFunction(x)
: Check if x
is a function.isRegExp(x)
: Check if x
is a regular expression.startsWith(a, b)
: Check if string a
starts with string b
.endsWith(a, b)
: Check if string a
ends with string b
.Return false
if a
is not a string.
keys(x)
: Return an array of keys of object x
.
values(x)
: Return an array of values of object x
.
entries(x)
: Convert object x
to an array of key-value pairs.
fromEntries(x)
: Convert an array of key-value pairs x
to an object.
deepAssign(x, y, ...)
: A deep version of Object.assign
.
Example // or @vuepress/helper/client
+import { deepAssign } from '@vuepress/helper'
+
+const defaultOptions = {
+ optionA: {
+ optionA1: 'defaultOptionA1' ,
+ optionA2: 'defaultOptionA2' ,
+ optionA3: 'defaultOptionA3' ,
+ },
+ optionB: true ,
+ optionC: 'optionC' ,
+}
+
+const userOptions = {
+ optionA: {
+ optionA1: 'optionA1' ,
+ optionA2: 'optionA2' ,
+ },
+ optionB: false ,
+}
+
+deepAssign ( defaultOptions , userOptions )
+// {
+// optionA: {
+// optionA1: "optionA1",
+// optionA2: "optionA2",
+// optionA3: "defaultOptionA3",
+// },
+// optionB: false,
+// optionC: "optionC",
+// }
+
getDate(x)
: Convert input x
to a date. It can support Date
, timestamp, and date string. The support degree of date string depends on the Date.parse
support degree of the environment. Return null
when it cannot be converted to a date.
Example getDate ( '2021-01-01' ) // a Date object represents 2021-01-01
+getDate ( 1609459200000 ) // a Date object represents 2021-01-01
+getDate ( '2021-01-01T00:00:00.000Z' ) // a Date object represents 2021-01-01
+getDate ( '2021/01/01' ) // a Date object represents 2021-01-01 (might be null in some browsers)
+getDate ( 'invalid date' ) // null
+getDate ( undefined ) // null
+getDate (- 32 ) // null
+
dateSorter
: Sort the values that can be converted to dates from new to old, and the values that cannot be converted to dates will be at the end.
Example const arr = [
+ '2020-01-01' ,
+ 1609459200000 ,
+ '2022-01-01T00:00:00.000Z' ,
+ '2023/01/01' ,
+ 'invalid date' ,
+ undefined ,
+ - 32 ,
+]
+
+arr . sort ( dateSorter )
+// [
+// '2023/01/01',
+// '2022-01-01T00:00:00.000Z',
+// 1609459200000,
+// '2020-01-01',
+// 'invalid date',
+// undefined,
+// -32,
+// ]
+
isLinkHttp(x)
: Check if x
is a valid HTTP URL.isLinkWithProtocol(x)
: Check if x
is a valid URL with protocol.isLinkExternal(x)
: Check if x
is a valid external URL.isLinkAbsolute(x)
: Check if x
is a valid absolute URL.ensureEndingSlash(x)
: Ensure x
ends with a slash.ensureLeadingSlash(x)
: Ensure x
starts with a slash.removeEndingSlash(x)
: Ensure x
does not end with a slash.removeLeadingSlash(x)
: Ensure x
does not start with a slash.Last Updated:
Contributors: Mr.Hope
Client Related
+
+
+
diff --git a/tools/index.html b/tools/index.html
new file mode 100644
index 0000000000..64d5e17ace
--- /dev/null
+++ b/tools/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ Tools | VuePress Ecosystem
+
+
+
+
+
+ VuePress Ecosystem Plugins Plugins Common Features Search Blogging PWA SEO Syntax Highlighting Theme Development
+
+
+
diff --git a/zh/index.html b/zh/index.html
new file mode 100644
index 0000000000..0919ff43e1
--- /dev/null
+++ b/zh/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ 首页 | VuePress 生态系统
+
+
+
+
+
+
+
+
+
diff --git a/zh/plugins/active-header-links.html b/zh/plugins/active-header-links.html
new file mode 100644
index 0000000000..9a5b73bf87
--- /dev/null
+++ b/zh/plugins/active-header-links.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ active-header-links | VuePress 生态系统
+
+
+
+
+
+ 该插件会监听页面滚动事件。当页面滚动至某个 标题锚点 后,如果存在对应的 标题链接 ,那么该插件会将路由 Hash 更改为该 标题锚点 。
该插件主要用于开发主题,并且已经集成到默认主题中。大部分情况下你不需要直接使用它。
npm i -D @vuepress/plugin-active-header-links@next
+
import { activeHeaderLinksPlugin } from '@vuepress/plugin-active-header-links'
+
+export default {
+ plugins: [
+ activeHeaderLinksPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
类型: number
默认值: 200
详情:
滚动事件监听器的 Debounce 延迟。
git
+
+
+
diff --git a/zh/plugins/back-to-top.html b/zh/plugins/back-to-top.html
new file mode 100644
index 0000000000..4852a6b6d8
--- /dev/null
+++ b/zh/plugins/back-to-top.html
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+ back-to-top | VuePress 生态系统
+
+
+
+
+
+ 该插件会给你的站点添加一个 返回顶部 按钮。当页面向下滚动时,该按钮会显示在页面的右下角,点击它就会滚动到页面顶部。
该插件已经集成到默认主题中。
npm i -D @vuepress/plugin-back-to-top@next
+
import { backToTopPlugin } from '@vuepress/plugin-back-to-top'
+
+export default {
+ plugins: [ backToTopPlugin ()],
+}
+
类型:数字
默认值:100
详情:显示返回顶部按钮的滚动阈值距离(以像素为单位) 类型:布尔值
默认值:true
详情:是否在图标周围显示进度条 你可以通过 CSS 变量来自定义 返回顶部 按钮的样式:
:root {
+ --back-to-top-z-index : 5 ;
+ --back-to-top-icon : url ( "back-to-top.svg" );
+ --back-to-top-bg-color : #fff ;
+ --back-to-top-color : #3eaf7c ;
+ --back-to-top-color-hover : #71cda3 ;
+ --back-to-top-shadow : rgb ( 0 0 0 / 20% );
+}
+
catalog
+
+
+
diff --git a/zh/plugins/baidu-analytics.html b/zh/plugins/baidu-analytics.html
new file mode 100644
index 0000000000..8d64fcf183
--- /dev/null
+++ b/zh/plugins/baidu-analytics.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ baidu-analytics | VuePress 生态系统
+
+
+
+
+
+ 将 百度统计在新窗口打开 集成到 VuePress 中。
npm i -D @vuepress/plugin-baidu-analytics@next
+
import { baiduAnalyticsPlugin } from '@vuepress/plugin-baidu-analytics'
+
+export default {
+ plugins: [
+ baiduAnalyticsPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
在使用该插件之后,一个全局的 hmt
数组会被挂载到 window
对象上,你可以使用它进行 自定义事件的上报在新窗口打开 。
类型: string
详情: 百度统计的 ID ,即 hm.js
URL 中的查询参数。 站点地图 google-analytics
+
+
+
diff --git a/zh/plugins/blog/config.html b/zh/plugins/blog/config.html
new file mode 100644
index 0000000000..2cee9b0d7d
--- /dev/null
+++ b/zh/plugins/blog/config.html
@@ -0,0 +1,179 @@
+
+
+
+
+
+
+
+
+ 配置 | VuePress 生态系统
+
+
+
+
+
+ 类型: (page: Page) => boolean
默认值: (page) => Boolean(page.filePathRelative) && !page.frontmatter.home
参考:
详情:
页面过滤器,此函数用于鉴别页面是否作为文章。
默认情况下,所有从 Markdown 源文件中生成的非主页页面,会被作为文章。
类型: BlogTypeOptions[]
必填: 否
参考:
详情:
博客分类配置,详见 博客类型配置 。
类型: (name: string) => string
默认值: (name) => name.replace(/ _/g, '-').replace(/[:?*|\\/<>]/g, "").toLowerCase()
详情:Slugify 函数,用于转换 key 在路由中注册的形式。 类型: boolean
默认值: true
详情:是否生成摘要。 类型: string
默认值: <!-- more -->
详情:摘要分隔符。 类型: number
默认值: 300
参考:
详情:
自动生成的摘要的长度。
提示
摘要的长度会尽可能的接近这个值。如果设置为 0
,意味着不自动生成摘要。
类型: string
默认值: "_blog"
详情: 注入文章信息至路由元数据时使用的键名。
提示
设置为空字符串会直接注入路由元数据 (而不是一个键下)。
类型: boolean
默认值: 是否使用 --debug
标记
详情: 是否在开发服务器中启用实时热重载。
致主题开发者
默认情况下它是禁用的,因为它确实会对具有很多分类和类别的站点产生性能影响,并且在编辑 Markdown 时会减慢热重载的速度。
如果用户正在添加或组织类别或标签,你可以告诉他们启用此功能,其余的时间最好禁用它。
此外,你可以尝试检测用户项目中的页面数并决定是否启用它。
博客分类配置应为一个数组,每一项控制一个分类规则。
interface BlogCategoryOptions {
+ /**
+ * 唯一的分类名称
+ */
+ key : string
+
+ /**
+ * 从页面中获取分类的函数
+ */
+ getter : ( page : Page ) => string []
+
+ /**
+ * 页面排序器
+ */
+ sorter ?: ( pageA : Page , pageB : Page ) => number
+
+ /**
+ * 待注册的页面路径图案
+ *
+ * @description `:key` 将会被替换为原 key 的 slugify 结果
+ *
+ * @default `/:key/`
+ */
+ path ?: string | false
+
+ /**
+ * 页面布局组件名称
+ *
+ * @default ' Layout '
+ */
+ layout ?: string
+
+ /**
+ * Front Matter 配置
+ */
+ frontmatter ?: ( localePath : string ) => Record < string , string >
+
+ /**
+ * 待注册的项目页面路径图案或自定义函数
+ *
+ * @description 当填入字符串的时候, `:key` 和 `:name` 会被自动替换为原始的 key、name 的 slugify 结果。
+ *
+ * @default `/:key/:name/`
+ */
+ itemPath ?: string | (( name : string ) => string ) | false
+
+ /**
+ * 项目页面布局组件名称
+ *
+ * @default ' Layout '
+ */
+ itemLayout ?: string
+
+ /**
+ * 项目 Front Matter 配置
+ */
+ itemFrontmatter ?: ( name : string , localePath : string ) => Record < string , string >
+}
+
博客类型配置应为一个数组,每一项控制一个类型规则。
interface BlogTypeOptions {
+ /**
+ * 唯一的类型名称
+ */
+ key : string
+
+ /**
+ * 一个过滤函数来决定页面是否满足此类型
+ */
+ filter : ( page : Page ) => boolean
+
+ /**
+ * 页面排序器
+ */
+ sorter ?: ( pageA : Page , pageB : Page ) => number
+
+ /**
+ * 待注册的页面路径
+ *
+ * @default ' /:key/ '
+ */
+ path ?: string | false
+
+ /**
+ * 页面布局组件名称
+ *
+ * @default ' Layout '
+ */
+ layout ?: string
+
+ /**
+ * Front Matter 配置
+ */
+ frontmatter ?: ( localePath : string ) => Record < string , string >
+}
+
你可以从 @vuepress/plugin-blog/client
导入下列 API:
详细的返回值如下:
interface Article < T extends Record < string , unknown > = Record < string , unknown >> {
+ /** 文章路径 */
+ path : string
+ /** 文章信息 */
+ info : T
+}
+
+interface BlogCategoryData <
+ T extends Record < string , unknown > = Record < string , unknown >,
+> {
+ /** 分类路径 */
+ path : string
+
+ /**
+ * 仅当当前路径和某个子项目匹配时可用
+ */
+ currentItems ?: Article < T >[]
+
+ /** 分类映射 */
+ map : {
+ /** 当前分类下全局唯一的 key */
+ [ key : string ]: {
+ /** 对应键值的分类路径 */
+ path : string
+ /** 对应键值的项目 */
+ items : Article < T >[]
+ }
+ }
+}
+
+interface BlogTypeData <
+ T extends Record < string , unknown > = Record < string , unknown >,
+> {
+ /** 类别路径 */
+ path : string
+
+ /** 当前类别下的项目 */
+ items : Article < T >[]
+}
+
指南
+
+
+
diff --git a/zh/plugins/blog/guide.html b/zh/plugins/blog/guide.html
new file mode 100644
index 0000000000..42bfaafae6
--- /dev/null
+++ b/zh/plugins/blog/guide.html
@@ -0,0 +1,251 @@
+
+
+
+
+
+
+
+
+ 指南 | VuePress 生态系统
+
+
+
+
+
+ 使用 @vuepress/plugin-blog
,你可以轻松地将博客功能引入主题。
起步时,插件会首选过滤并选择那些需要作为文章的页面。这将剔除你不想要的页面,并在后续处理中排除它们。
默认情况下,所有从 Markdown 文件生成但不是主页的页面,都将被视作文章。
你可能需要设置 filter
选项来完全自定义要收集的页面。 filter
接受一个形状为 (page: Page) => boolean
的函数。
接着,你应该设置 getInfo
选项为一个接受 Page
作为参数并返回包含所需信息的对象的函数。这样稍后,你可以从组合 API 中获取这些信息。
案例 import { blogPlugin } from '@vuepress/plugin-blog'
+
+export default {
+ name: 'vuepress-theme-xxx' ,
+ plugins: [
+ blogPlugin ({
+ filter : ({ filePathRelative , frontmatter }) => {
+ // 舍弃那些不是从 Markdown 文件生成的页面
+ if (! filePathRelative ) return false
+
+ // 舍弃 `archives` 文件夹的页面
+ if ( filePathRelative . startsWith ( 'archives/' )) return false
+
+ // 舍弃那些没有使用默认布局的页面
+ if ( frontmatter . home || frontmatter . layout ) return false
+
+ return true
+ },
+
+ getInfo : ({ frontmatter , git = {}, data = {} }) => {
+ // 获取页面信息
+ const info : Record < string , any > = {
+ author: frontmatter . author || '' ,
+ categories: frontmatter . categories || [],
+ date: frontmatter . date || git . createdTime || null ,
+ tags: frontmatter . tags || [],
+ excerpt: data . excerpt || '' ,
+ }
+
+ return info
+ },
+ }),
+ // 其他插件 ...
+ ],
+}
+
基本上,你的博客中需要两种“类型”:
了解这两种类型的描述后,你可以设置 category
和 type
选项,它们都接受一个数组,每个元素代表一个配置。
让我们从此处 2 个例子开始。
假设你想为每篇文章设置标签,并且你正在通过 frontmatter.tag
设置它们。同时,你想要在 /tag/
中使用 TagMap
布局的标签页面,并在/tag/标签名称
中使用 TagList
布局对标签按名称进行分组,你可能需要这样的配置:
import { blogPlugin } from '@vuepress/plugin-blog'
+
+export default {
+ name: 'vuepress-theme-xxx' ,
+ plugins: [
+ blogPlugin ({
+ // 其他配置 ...
+ category: [
+ {
+ key: 'tag' ,
+ getter : ({ frontmatter }) => frontmatter . tag || [],
+ path: '/tag/' ,
+ layout: 'TagMap' ,
+ frontmatter : () => ({ title: '标签页' }),
+ itemPath: '/tag/:name/' ,
+ itemLayout: 'TagList' ,
+ itemFrontmatter : ( name ) => ({ title: ` ${ name } 标签` }),
+ },
+ ],
+ }),
+ // 其他插件 ...
+ ],
+}
+
此外,你可能希望为你的一些文章加注星标,并将其展示给访问者。当你在 frontmatter 中设置 star: true
来标记它们时,你可能需要这样的配置来在 /star/
路径中以 StarList
布局显示它们:
import { blogPlugin } from '@vuepress/plugin-blog'
+
+export default {
+ name: 'vuepress-theme-xxx' ,
+ plugins: [
+ blogPlugin ({
+ // 其他配置 ...
+ type: [
+ {
+ key: 'star' ,
+ filter : ({ frontmatter }) => frontmatter . star ,
+ path: '/star/' ,
+ layout: 'StarList' ,
+ frontmatter : () => ({ title: '星标文章' }),
+ },
+ ],
+ }),
+ // 其他插件 ...
+ ],
+}
+
看,设置这两种类型很容易。有关完整选项,请参阅 博客分类配置 和 博客分类配置 。
当生成每个页面时,插件将在 frontmatter.blog
中设置如下信息
interface BlogFrontmatterOptions {
+ /** 当前页面的类型 */
+ type : 'category' | 'type'
+ /** 在当前分类或类别下全局唯一的 key */
+ key : string
+ /**
+ * 当前的分类名称
+ *
+ * @description 仅在分类子项目页面中可用
+ */
+ name ?: string
+}
+
所以你可以直接调用 useBlogCategory()
和 useBlogType()
,结果将是当前路由绑定的类别或类型。
此外,你可以通过传递所需的 key
作为参数,来将获得绑定到该 key
的信息。
对于上方的 Node 配置而言,你可以在客户端通过如下方式获取 tag 和 star 的信息:
TagMap
布局:
< template >
+ < div >
+ < h1 > Tag page </ h1 >
+ < ul >
+ < li v-for = " ({ items , path }, name ) in categoryMap . map " >
+ < RouteLink : key = " name " : to = " path " class = "category" >
+ {{ name }}
+ < span class = "category-num" >
+ {{ items . length }}
+ </ span >
+ </ RouteLink >
+ </ li >
+ </ ul >
+ </ div >
+</ template >
+< script setup lang = "ts" >
+import { useBlogCategory } from '@vuepress/plugin-blog'
+import { RouteLink } from 'vuepress/client'
+
+const categoryMap = useBlogCategory ( 'tag' )
+</ script >
+
TagList
布局:
< template >
+ < div >
+ < h1 > Tag page </ h1 >
+ < div class = "category-wrapper" >
+ < RouteLink
+ v-for = " ({ items , path }, name ) in categoryMap . map "
+ : key = " name "
+ : to = " path "
+ class = "category"
+ >
+ {{ name }}
+ < span class = "category-num" >
+ {{ items . length }}
+ </ span >
+ </ RouteLink >
+ </ div >
+ < div class = "article-wrapper" v-if = " categoryMap . currentItems " >
+ < div v-if = " ! categoryMap . currentItems . length " > Nothing in here. </ div >
+ < article
+ v-for = " { info , path } in categoryMap . currentItems "
+ class = "article"
+ @ click = " $router . push ( path ) "
+ >
+ < header class = "title" >
+ {{
+ ( isTimeline
+ ? ` ${ new Date ( info . date ). toLocaleDateString () } : `
+ : '' ) + info . title
+ }}
+ </ header >
+ < hr />
+ < div class = "article-info" >
+ < span v-if = " info . author " class = "author"
+ > Author: {{ info . author }} </ span
+ >
+ < span v-if = " info . date && ! isTimeline " class = "date"
+ > Date: {{ new Date ( info . date ). toLocaleDateString () }} </ span
+ >
+ < span v-if = " info . category " class = "category"
+ > Category: {{ info . category . join ( ', ' ) }} </ span
+ >
+ < span v-if = " info . tag " class = "tag"
+ > Tag: {{ info . tag . join ( ', ' ) }} </ span
+ >
+ </ div >
+ < div v-if = " info . excerpt " class = "excerpt" v-html = " info . excerpt " / >
+ </ article >
+ </ div >
+ </ div >
+</ template >
+< script setup lang = "ts" >
+import { useBlogCategory } from '@vuepress/plugin-blog'
+import { RouteLink } from 'vuepress/client'
+
+const categoryMap = useBlogCategory ( 'tag' )
+</ script >
+
StarList
布局:
< template >
+ < div class = "article-wrapper" v-if = " stars . items " >
+ < div v-if = " ! stars . items . length " > Nothing in here. </ div >
+ < article
+ v-for = " { info , path } in stars . items "
+ class = "article"
+ @ click = " $router . push ( path ) "
+ >
+ < header class = "title" >
+ {{
+ ( isTimeline ? ` ${ new Date ( info . date ). toLocaleDateString () } : ` : '' ) +
+ info . title
+ }}
+ </ header >
+ < hr />
+ < div class = "article-info" >
+ < span v-if = " info . author " class = "author" > Author: {{ info . author }} </ span >
+ < span v-if = " info . date && ! isTimeline " class = "date"
+ > Date: {{ new Date ( info . date ). toLocaleDateString () }} </ span
+ >
+ < span v-if = " info . category " class = "category"
+ > Category: {{ info . category . join ( ', ' ) }} </ span
+ >
+ < span v-if = " info . tag " class = "tag" > Tag: {{ info . tag . join ( ', ' ) }} </ span >
+ </ div >
+ < div v-if = " info . excerpt " class = "excerpt" v-html = " info . excerpt " / >
+ </ article >
+ </ div >
+</ template >
+< script setup lang = "ts" >
+import { useBlogType } from '@vuepress/plugin-blog/client'
+
+import ArticleList from '../components/ArticleList.vue'
+import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
+
+const stars = useBlogType ( 'star' )
+</ script >
+
有关返回类型,请参阅 Composition API 返回类型 。
该插件添加了原生多语言支持,因此你的设置将自动应用于每种语言。
例如,如果用户进行了以下 locales 配置,并且你正在设置上面的“star”示例:
export default {
+ locales: {
+ '/' : {
+ lang: 'en-US' ,
+ },
+ '/zh/' : {
+ lang: 'zh-CN' ,
+ },
+ },
+}
+
那么 /zh/star/
和 /star/
都将可用,并且只会显示对应语言下的文章。
这个插件提供了一个内置的摘要生成器,可以通过将 excerpt
选项设置为 true
来启用。
摘要介绍
摘要是一个 HTML 片段,被用于在博客列表中显示文章的简短描述,所以摘要有如下限制:
摘要不支持任何未知标签以及 Vue 语法,所以此类内容会在生成时被移除。如果你有自定义组件 (非 Vue 组件),请配置 isCustomElement
选项。 由于摘要是一个 HTML 片段,所以你将无法通过相对路径或别名引入任何图片,这些图片会被直接移除。如果你想要保留图片,请使用基于 .vuepress/public
的绝对路径或完整路径以确保它们可以在其他地址被访问。 摘要生成器将尝试从 Frontmatter 内容中找到有效的摘要分隔符,如果找到,它将使用分隔符之前的内容,分隔符默认为 <!-- more -->
,并且你可以通过 excerptSeparator
选项来自定义它。
如果找不到有效的分隔符,它将从 Markdown 文件的开头开始解析内容,直到长度达到预设值时停止。该值默认为 300
,你可以通过设置 excerptLength
选项来自定义它。
要选择哪个页面应该生成摘要,你可以使用 excerptFilter
选项。
示例
通常,如果用户设置了 frontmatter.description
,你可能希望使用它们,因此如果 frontmatter.description
不为空,你可以让过滤器函数返回 false
。
配置
+
+
+
diff --git a/zh/plugins/blog/index.html b/zh/plugins/blog/index.html
new file mode 100644
index 0000000000..d3483b80b3
--- /dev/null
+++ b/zh/plugins/blog/index.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ blog | VuePress 生态系统
+
+
+
+
+
+ npm i -D @vuepress/plugin-blog@next
+
import { blogPlugin } from '@vuepress/plugin-blog'
+
+export default {
+ plugins: [
+ blogPlugin ({
+ // 选项
+ }),
+ ],
+}
+
Feed
+
+
+
diff --git a/zh/plugins/catalog.html b/zh/plugins/catalog.html
new file mode 100644
index 0000000000..de91f34dcc
--- /dev/null
+++ b/zh/plugins/catalog.html
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+ catalog | VuePress 生态系统
+
+
+
+
+
+ 此插件可以自动生成目录页面,也提供目录组件。
npm i -D @vuepress/plugin-catalog@next
+
import { catalogPlugin } from '@vuepress/plugin-catalog'
+
+export default {
+ plugins: [
+ catalogPlugin ({
+ // 你的选项
+ }),
+ ],
+}
+
首先,你应该在路由元信息中设置目录信息:
import { catalogPlugin } from '@vuepress/plugin-catalog'
+
+export default {
+ extendsPage : ( page ) => {
+ // 在 routeMeta 中设置目录信息
+ page . routeMeta = {
+ // 目录标题
+ title: page . title ,
+ // ... 其他信息
+ }
+ },
+}
+
你可以之后导入 defineCatalogInfoGetter
并在 客户端配置文件在新窗口打开 中使用它来从元信息中提取目录信息。
import { defineCatalogInfoGetter } from '@vuepress/plugin-catalog/client'
+
+export default {
+ setup : () => {
+ defineCatalogInfoGetter (( meta ) =>
+ meta . title ? { title: meta . title } : null ,
+ )
+ },
+}
+
目录信息应包含:
title
: 目录标题order
: 目录顺序 (可选)content
: 目录内容组件 (可选)通过 order 排序
插件将按以下方式通过 order
对页面进行排序:
// 从小到大依次排列正数
+order 1 的项目
+order 2 的项目
+...
+order 10 的项目
+...
+// 无 order 的项目
+无 order 的项目
+无 order 的项目
+...
+// 从小到大依次排列负数
+order -10 的项目
+// ...
+order -2 的项目
+order -1 的项目
+
类型:1 | 2 | 3
默认值:3
详情:目录项级别的最大深度。 类型:boolean
默认值:false
详情:目录是否显示索引 类型:(RegExp | string)[]
默认值:[]
详情:
生成中需要排除的目录页路径。
"/foo/"
意味着仅排除 /foo/
文件夹的目录页生成。/^\/foo\//
意味着排除 /foo/
文件夹及其子文件夹的目录页生成。内置支持语言 简体中文 (zh-CN)繁体中文 (zh-TW)英文(美国) (en-US)德语 (de-DE)德语(澳大利亚) (de-AT)俄语 (ru-RU)乌克兰语 (uk-UA)越南语 (vi-VN)葡萄牙语(巴西) (pt-BR)波兰语 (pl-PL)法语 (fr-FR)西班牙语 (es-ES)斯洛伐克 (sk-SK)日语 (ja-JP)土耳其语 (tr-TR)韩语 (ko-KR)芬兰语 (fi-FI)印尼语 (id-ID)荷兰语 (nl-NL)印尼语 (id-ID)荷兰语 (nl-NL)interface CatalogInfo {
+ /** 目录标题 */
+ title : string
+ /** 目录顺序 */
+ order ?: number
+ /** 目录内容 */
+ content ?: Component
+}
+
+type CatalogInfoGetter = ( meta : Record < string , unknown >) => CatalogInfo | null
+
+const defineCatalogInfoGetter : ( options : CatalogInfoGetter ) => void
+
自定义如何从 meta 中提取目录信息。
你可以通过 CSS 变量来自定义目录样式:
:root {
+ --catalog-bg-color : #fff ;
+ --catalog-bg-secondary-color : #f8f8f8 ;
+ --catalog-border-color : #e5e5e5 ;
+ --catalog-active-color : #3eaf7c ;
+ --catalog-hover-color : #71cda3 ;
+}
+
back-to-top container
+
+
+
diff --git a/zh/plugins/container.html b/zh/plugins/container.html
new file mode 100644
index 0000000000..1f3af9e255
--- /dev/null
+++ b/zh/plugins/container.html
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+ container | VuePress 生态系统
+
+
+
+
+
+ 为你的 VuePress 站点注册自定义容器。
该插件简化了 markdown-it-container在新窗口打开 的使用方法,但同时也保留了其原本的能力。
默认主题的 自定义容器 就是由该插件支持的。
npm i -D @vuepress/plugin-container@next
+
import { containerPlugin } from '@vuepress/plugin-container'
+
+export default {
+ plugins: [
+ containerPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
::: < type > [ info ]
+[ content ]
+:::
+
type
是必需的,应通过 type 配置项来指定。info
是可选的,其默认值可以通过 locales 的 defaultInfo
配置项来指定。content
可是任何合法的 Markdown 内容。提示
该插件可以被多次使用,以便支持不同类型的容器。
类型: Record<string, { defaultInfo: string }>
详情:
容器在不同 locales 下的默认 info
。
如果没有指定该配置项,默认 info
会使用大写的 type 。
示例:
export default {
+ plugins: [
+ containerPlugin ({
+ type: 'tip' ,
+ locales: {
+ '/' : {
+ defaultInfo: 'TIP' ,
+ },
+ '/zh/' : {
+ defaultInfo: '提示' ,
+ },
+ },
+ }),
+ ],
+}
+
( info : string ): string =>
+ `<div class="custom-container ${ type } "> ${ info ? `<p class="custom-container-title"> ${ info } </p>` : '' } \n `
+
(): string => '</div> \n '
+
type MarkdownItContainerRenderFunction = (
+ tokens : Token [],
+ index : number ,
+ options : any ,
+ env : MarkdownEnv ,
+ self : Renderer ,
+) => string
+
catalog copy-code
+
+
+
diff --git a/zh/plugins/copy-code.html b/zh/plugins/copy-code.html
new file mode 100644
index 0000000000..4125898cf4
--- /dev/null
+++ b/zh/plugins/copy-code.html
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+ copy-code | VuePress 生态系统
+
+
+
+
+
+ 此插件会自动在 PC 设备上为每个代码块右上角添加复制按钮。
默认选择器匹配 @vuepress/theme-default
,所以在你自己的主题中集成时可能需要调整它。
npm i -D @vuepress/plugin-copy-code@next
+
import { copyCodePlugin } from '@vuepress/plugin-copy-code'
+
+export default {
+ plugins: [
+ copyCodePlugin ({
+ // options
+ }),
+ ],
+}
+
类型:boolean
默认值:false
详情:
是否展示在移动端
类型:number
默认值:2000
详情:
提示消息显示时间,设置为 0
会禁用提示。
内置支持语言 简体中文 (zh-CN)繁体中文 (zh-TW)英文(美国) (en-US)德语 (de-DE)德语(澳大利亚) (de-AT)俄语 (ru-RU)乌克兰语 (uk-UA)越南语 (vi-VN)葡萄牙语(巴西) (pt-BR)波兰语 (pl-PL)法语 (fr-FR)西班牙语 (es-ES)斯洛伐克 (sk-SK)日语 (ja-JP)土耳其语 (tr-TR)韩语 (ko-KR)芬兰语 (fi-FI)印尼语 (id-ID)荷兰语 (nl-NL)你可以通过 CSS 变量来自定义复制按钮 的样式:
:root {
+ --code-copy-icon : url ( "copy-button.svg" );
+ --code-copied-icon : url ( "copied-button.svg" );
+ --copy-code-color : #9e9e9e ;
+ --copy-code-hover : rgb ( 0 0 0 / 50% );
+}
+
container copyright
+
+
+
diff --git a/zh/plugins/copyright.html b/zh/plugins/copyright.html
new file mode 100644
index 0000000000..9aa872fe74
--- /dev/null
+++ b/zh/plugins/copyright.html
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+ copyright | VuePress 生态系统
+
+
+
+
+
+ 此插件可以在访问者从你的站点复制内容时,自动追加版权信息,也可以禁止站点的复制或者选择。
npm i -D @vuepress/plugin-copyright@next
+
import { copyrightPlugin } from '@vuepress/plugin-copyright'
+
+export default {
+ plugins: [
+ copyrightPlugin ({
+ // options
+ }),
+ ],
+}
+
此插件默认全局禁用 。你可以:
在特定页面的 frontmatter 中设置 copy: true
手动开启。 在插件选项中设置 global: true
让其全局生效,并在页面的 frontmatter 中设置 copy: false
禁用它。 处于不打扰用户的考虑,默认配置下仅当复制长度超过 100 时才会追加版权信息。如果你希望改变这个触发值,你可以插件选项中设置 triggerLength
,或在页面 frontmatter 单独设置 copy.triggerLength
。
你可以通过插件的 author
和 license
选项设置全局作者和协议信息。
如果文档的不同部分拥有不同的作者和协议,你可以通过 authorGetter
和 licenseGetter
传入一个使用当前页面对象作为参数的函数 (page: Page) => string
并通过它返回相应信息。
插件会默认通过模板从作者、协议和页面链接生成版权信息,并在复制时追加。如果你认为这不够灵活,你可以设置 copyrightGetter
返回一个完全由你自定义的版权信息,或返回 null 以使用默认模板。
如果你希望禁止用户复制较长内容,你可以在插件选项中设置 maxLength
控制这个临界值,或在页面 frontmatter 单独设置 copy.maxLength
。
如果你不希望用户复制你的整个站点或特定页面文字,你可以在插件选项中设置 disableCopy
或在页面 frontmatter 中设置 copy.disableCopy
来禁用复制,后者具有更高优先级。 如果你不希望用户选择你的整个站点或特定页面文字,你可以在插件选项中设置 disableSelection
或在页面 frontmatter 中设置 copy.disableSelection
来禁用文字选择。此选项具有更高优先级 类型:(page: Page) => string | null
详情:作者信息获取器 类型:(page: Page) => string | null
详情:协议信息获取器 类型:(page: Page) => string | null
详情:协议信息获取器 类型:number
默认值:100
详情:触发附加版权的最小内容长度 类型:number
默认值:0
详情:允许复制的最大内容长度,0
意味着无限制。 类型:boolean
默认值:false
详情:是否全局启用 类型:boolean
默认值:false
详情:禁用复制 类型:boolean
默认值:false
详情:禁用选择 例子
如果你在 https://myblog.com
和 https://blog.com/username/
下部署相同的内容,你可能希望选择一个站点作为首选链接。
如果你倾向于使用第一个,你应该将 canonical
设置为 https://myblog.com
如果你倾向于使用第二个,你应该将 canonical
设置为 https://blog.com/username/
这样,在另一个站点触发的版权信息也会指向你的首选站点。
内置支持语言 简体中文 (zh-CN)繁体中文 (zh-TW)英文(美国) (en-US)德语 (de-DE)德语(澳大利亚) (de-AT)俄语 (ru-RU)乌克兰语 (uk-UA)越南语 (vi-VN)葡萄牙语(巴西) (pt-BR)波兰语 (pl-PL)法语 (fr-FR)西班牙语 (es-ES)斯洛伐克 (sk-SK)日语 (ja-JP)土耳其语 (tr-TR)韩语 (ko-KR)芬兰语 (fi-FI)印尼语 (id-ID)荷兰语 (nl-NL)类型:number
默认值:100
详情: 触发附加版权的最小内容长度 类型:number
默认值:0
详情: 允许复制的最大内容长度,0
意味着无限制。 类型:boolean
默认值:false
详情:禁用复制 类型:boolean
默认值:false
详情:禁用选择 copy-code external-link-icon
+
+
+
diff --git a/zh/plugins/docsearch.html b/zh/plugins/docsearch.html
new file mode 100644
index 0000000000..0a61eaa5f1
--- /dev/null
+++ b/zh/plugins/docsearch.html
@@ -0,0 +1,239 @@
+
+
+
+
+
+
+
+
+ docsearch | VuePress 生态系统
+
+
+
+
+
+ 将 Algolia DocSearch在新窗口打开 集成到 VuePress 中,为你的文档网站提供搜索功能。
提示
当你正确配置该插件后,默认主题会把 DocSearch 按钮添加到导航栏。
该插件不一定能在其他主题中直接使用,因此你应参考主题本身的文档来获取更多信息。
npm i -D @vuepress/plugin-docsearch@next
+
import { docsearchPlugin } from '@vuepress/plugin-docsearch'
+
+export default {
+ plugins: [
+ docsearchPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
你需要 提交你的网站 URL在新窗口打开 来加入 DocSearch 项目。当你的索引成功创建后, DocSearch 团队会将 apiKey 和 indexName 发送到你的邮箱。接下来,你就可以配置该插件,在 VuePress 中启用 DocSearch 了。
或者,你也可以 运行你自己的爬虫在新窗口打开 来创建索引,然后使用你自己的 appId , apiKey 和 indexName 来配置该插件。
官方爬虫配置示例 new Crawler ({
+ appId: 'YOUR_APP_ID' ,
+ apiKey: 'YOUR_API_KEY' ,
+ rateLimit: 8 ,
+ startUrls: [
+ // 这是 Algolia 开始抓取网站的初始地址
+ // 如果你的网站被分为数个独立部分,你可能需要在此设置多个入口链接
+ 'https://YOUR_WEBSITE_URL/' ,
+ ],
+ sitemaps: [
+ // 如果你在使用 Sitemap 插件 (如: @vuepress-plugin/sitemap),你可以提供 Sitemap 链接
+ 'https://YOUR_WEBSITE_URL/sitemap.xml' ,
+ ],
+ ignoreCanonicalTo: false ,
+ exclusionPatterns: [
+ // 你可以通过它阻止 Algolia 抓取某些 URL
+ ],
+ discoveryPatterns: [
+ // 这是 Algolia 抓取 URL 的范围
+ 'https://YOUR_WEBSITE_URL/**' ,
+ ],
+ // 爬虫执行的计划时间,可根据文档更新频率设置
+ schedule: 'at 02:00 every 1 day' ,
+ actions: [
+ // 你可以拥有多个 action,特别是你在一个域名下部署多个文档时
+ {
+ // 使用适当的名称为索引命名
+ indexName: 'YOUR_INDEX_NAME' ,
+ // 索引生效的路径
+ pathsToMatch: [ 'https://YOUR_WEBSITE_URL/**' ],
+ // 控制 Algolia 如何抓取你的站点
+ recordExtractor : ({ $ , helpers }) => {
+ // @vuepress/theme-default 的选项
+ return helpers . docsearch ({
+ recordProps: {
+ lvl0: {
+ selectors: '.sidebar-heading.active' ,
+ defaultValue: 'Documentation' ,
+ },
+ lvl1: '.theme-default-content h1' ,
+ lvl2: '.theme-default-content h2' ,
+ lvl3: '.theme-default-content h3' ,
+ lvl4: '.theme-default-content h4' ,
+ lvl5: '.theme-default-content h5' ,
+ lvl6: '.theme-default-content h6' ,
+ content: '.theme-default-content p, .theme-default-content li' ,
+ },
+ indexHeadings: true ,
+ })
+ },
+ },
+ ],
+ initialIndexSettings: {
+ // 控制索引如何被初始化,这仅当索引尚未生成时有效
+ // 你可能需要在修改后手动删除并重新生成新的索引
+ YOUR_INDEX_NAME: {
+ attributesForFaceting: [ 'type' , 'lang' ],
+ attributesToRetrieve: [ 'hierarchy' , 'content' , 'anchor' , 'url' ],
+ attributesToHighlight: [ 'hierarchy' , 'hierarchy_camel' , 'content' ],
+ attributesToSnippet: [ 'content:10' ],
+ camelCaseAttributes: [ 'hierarchy' , 'hierarchy_radio' , 'content' ],
+ searchableAttributes: [
+ 'unordered(hierarchy_radio_camel.lvl0)' ,
+ 'unordered(hierarchy_radio.lvl0)' ,
+ 'unordered(hierarchy_radio_camel.lvl1)' ,
+ 'unordered(hierarchy_radio.lvl1)' ,
+ 'unordered(hierarchy_radio_camel.lvl2)' ,
+ 'unordered(hierarchy_radio.lvl2)' ,
+ 'unordered(hierarchy_radio_camel.lvl3)' ,
+ 'unordered(hierarchy_radio.lvl3)' ,
+ 'unordered(hierarchy_radio_camel.lvl4)' ,
+ 'unordered(hierarchy_radio.lvl4)' ,
+ 'unordered(hierarchy_radio_camel.lvl5)' ,
+ 'unordered(hierarchy_radio.lvl5)' ,
+ 'unordered(hierarchy_radio_camel.lvl6)' ,
+ 'unordered(hierarchy_radio.lvl6)' ,
+ 'unordered(hierarchy_camel.lvl0)' ,
+ 'unordered(hierarchy.lvl0)' ,
+ 'unordered(hierarchy_camel.lvl1)' ,
+ 'unordered(hierarchy.lvl1)' ,
+ 'unordered(hierarchy_camel.lvl2)' ,
+ 'unordered(hierarchy.lvl2)' ,
+ 'unordered(hierarchy_camel.lvl3)' ,
+ 'unordered(hierarchy.lvl3)' ,
+ 'unordered(hierarchy_camel.lvl4)' ,
+ 'unordered(hierarchy.lvl4)' ,
+ 'unordered(hierarchy_camel.lvl5)' ,
+ 'unordered(hierarchy.lvl5)' ,
+ 'unordered(hierarchy_camel.lvl6)' ,
+ 'unordered(hierarchy.lvl6)' ,
+ 'content' ,
+ ],
+ distinct: true ,
+ attributeForDistinct: 'url' ,
+ customRanking: [
+ 'desc(weight.pageRank)' ,
+ 'desc(weight.level)' ,
+ 'asc(weight.position)' ,
+ ],
+ ranking: [
+ 'words' ,
+ 'filters' ,
+ 'typo' ,
+ 'attribute' ,
+ 'proximity' ,
+ 'exact' ,
+ 'custom' ,
+ ],
+ highlightPreTag: '<span class="algolia-docsearch-suggestion--highlight">' ,
+ highlightPostTag: '</span>' ,
+ minWordSizefor1Typo: 3 ,
+ minWordSizefor2Typos: 7 ,
+ allowTyposOnNumericTokens: false ,
+ minProximity: 1 ,
+ ignorePlurals: true ,
+ advancedSyntax: true ,
+ attributeCriteriaComputedByMinProximity: true ,
+ removeWordsIfNoResults: 'allOptional' ,
+ },
+ },
+})
+
上述 recordProps
是用于默认主题的配置,你可以根据你使用的主题来修改它们。
注意 initialIndexSettings.YOUR_INDEX_NAME.attributesForFaceting
字段必须 包含 'lang'
,否则该插件将无法正常工作。
提示
如果你使用的不是默认主题,或者在使用 Docsearch 的时候遇到了任何问题,你也可以检查上述的爬虫配置示例,然后前往 Algolia Crawler在新窗口打开 仓库,在你项目侧边栏中的 Editor 页面中修改你的配置。
类型: string
是否必需: true
详情:
用于设置你的 Application ID。
参考:
类型: SearchParameters
详情:
Algolia 搜索 API 参数。
参考:
类型: string
默认值: 'Search docs'
详情:
搜索输入框的 placeholder 属性。
参考:
类型: string
详情:
打开弹窗时的初始请求。
参考:
类型: Record<string, DocsearchPluginOptions>
详情:
在不同 locales 下对该插件进行不同的配置。
该插件的所有其他选项都可以在 locale 中进行配置。
示例:
export default {
+ plugins: [
+ docsearchPlugin ({
+ appId: '<APP_ID>' ,
+ apiKey: '<API_KEY>' ,
+ indexName: '<INDEX_NAME>' ,
+ locales: {
+ '/' : {
+ placeholder: 'Search Documentation' ,
+ translations: {
+ button: {
+ buttonText: 'Search Documentation' ,
+ },
+ },
+ },
+ '/zh/' : {
+ placeholder: '搜索文档' ,
+ translations: {
+ button: {
+ buttonText: '搜索文档' ,
+ },
+ },
+ },
+ },
+ }),
+ ],
+}
+
你可以通过 @docsearch/css在新窗口打开 提供的 CSS 变量来自定义样式:
:root {
+ --docsearch-primary-color : rgb ( 84 , 104 , 255 );
+ --docsearch-text-color : rgb ( 28 , 30 , 33 );
+ --docsearch-spacing : 12px ;
+ --docsearch-icon-stroke-width : 1.4 ;
+ --docsearch-highlight-color : var ( --docsearch-primary-color );
+ --docsearch-muted-color : rgb ( 150 , 159 , 175 );
+ --docsearch-container-background : rgba ( 101 , 108 , 133 , 0.8 );
+ --docsearch-logo-color : rgba ( 84 , 104 , 255 );
+
+ /* modal */
+ --docsearch-modal-width : 560px ;
+ --docsearch-modal-height : 600px ;
+ --docsearch-modal-background : rgb ( 245 , 246 , 247 );
+ --docsearch-modal-shadow : inset 1px 1px 0 0 rgba ( 255 , 255 , 255 , 0.5 ), 0 3px
+ 8px 0 rgba ( 85 , 90 , 100 , 1 );
+
+ /* searchbox */
+ --docsearch-searchbox-height : 56px ;
+ --docsearch-searchbox-background : rgb ( 235 , 237 , 240 );
+ --docsearch-searchbox-focus-background : #fff ;
+ --docsearch-searchbox-shadow : inset 0 0 0 2px var ( --docsearch-primary-color );
+
+ /* hit */
+ --docsearch-hit-height : 56px ;
+ --docsearch-hit-color : rgb ( 68 , 73 , 80 );
+ --docsearch-hit-active-color : #fff ;
+ --docsearch-hit-background : #fff ;
+ --docsearch-hit-shadow : 0 1px 3px 0 rgb ( 212 , 217 , 225 );
+
+ /* key */
+ --docsearch-key-gradient : linear-gradient (
+ -225deg ,
+ rgb ( 213 , 219 , 228 ) 0% ,
+ rgb ( 248 , 248 , 248 ) 100%
+ );
+ --docsearch-key-shadow : inset 0 -2px 0 0 rgb ( 205 , 205 , 230 ), inset 0 0 1px 1px
+ #fff , 0 1px 2px 1px rgba ( 30 , 35 , 90 , 0.4 );
+
+ /* footer */
+ --docsearch-footer-height : 44px ;
+ --docsearch-footer-background : #fff ;
+ --docsearch-footer-shadow : 0 -1px 0 0 rgb ( 224 , 227 , 232 ), 0 -3px 6px 0 rgba ( 69 , 98 , 155 , 0.12 );
+}
+
提示
该组件主要用于主题开发。在大多数情况下你不需要直接使用该组件。
search
+
+
+
diff --git a/zh/plugins/external-link-icon.html b/zh/plugins/external-link-icon.html
new file mode 100644
index 0000000000..4808218932
--- /dev/null
+++ b/zh/plugins/external-link-icon.html
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+ external-link-icon | VuePress 生态系统
+
+
+
+
+
+ 该插件会为你 Markdown 内容中的外部链接添加一个图标,即 在新窗口打开
该插件已经集成到默认主题中。
npm i -D @vuepress/plugin-external-link-icon@next
+
import { externalLinkIconPlugin } from '@vuepress/plugin-external-link-icon'
+
+export default {
+ plugins: [
+ externalLinkIconPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
export default {
+ plugins: [
+ externalLinkIconPlugin ({
+ locales: {
+ '/' : {
+ openInNewWindow: 'open in new window' ,
+ },
+ '/zh/' : {
+ openInNewWindow: '在新窗口打开' ,
+ },
+ },
+ }),
+ ],
+}
+
类型: boolean
详情:
是否在当前页面的外部链接的后面添加外部链接图标。
你可以通过 CSS 变量来自定义外部链接图标的样式:
:root {
+ --external-link-icon-color : #aaa ;
+}
+
提示
该组件主要用于主题开发。在大多数情况下你不需要直接使用该组件。
copyright medium-zoom
+
+
+
diff --git a/zh/plugins/feed/channel.html b/zh/plugins/feed/channel.html
new file mode 100644
index 0000000000..c4ede1eb3d
--- /dev/null
+++ b/zh/plugins/feed/channel.html
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+ 频道设置 | VuePress 生态系统
+
+
+
+
+
+ channel
插件选项用于配置 feed 的频道。
类型:string
默认值:SiteConfig.title
频道的标题
类型:string
默认值:部署的网址 (通过 options.hostname
和 context.base
生成) 频道地址
类型:string
默认值:SiteConfig.description
频道描述信息
类型:string
默认值: siteConfig.locales['/'].locales
如果上述未提供,回退到 "en-US"
频道使用的语言
类型:string
默认值: 尝试读取 channel 选项中的 author.name
生成 Copyright by $author
建议自行设置: 是 频道版权信息
类型:string
(需是合法的 Date ISOString) 默认值:每次插件构建时刻 建议自行设置: 是 频道内容的发布时间
类型:string
(需是合法的 Date ISOString) 默认值:每次插件构建时刻 频道内容的上次更新时间
内容有效时间,即获取后保持缓存而不进行新获取的时间
这是一个会在频道中使用的图片,建议设置正方形图片、尺寸最好不小于 512×512。
一个代表频道的图标,建议设置正方形图片、尺寸最好不小于 128×128,背景色透明。
频道的作者。
FeedAuthor 格式 interface FeedAuthor {
+ /** 作者姓名 */
+ name : string
+ /** 作者电子邮箱 */
+ email ?: string
+ /** 作者网站 */
+ url ?: string
+ /**
+ * 作者头像地址
+ *
+ * 正方形,最好不小于 128×128,透明背景
+ */
+ avatar ?: string
+}
+
Websub 的链接。Websub 需要服务器后端,与 VuePress 主旨不符,如无特殊需要忽略即可。
Frontmatter 配置 Feed 获取器
+
+
+
diff --git a/zh/plugins/feed/config.html b/zh/plugins/feed/config.html
new file mode 100644
index 0000000000..12251da715
--- /dev/null
+++ b/zh/plugins/feed/config.html
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+ 插件配置 | VuePress 生态系统
+
+
+
+
+
+ 部署网站的域名。
是否启用 Atom 格式输出。
是否启用 JSON 格式输出。
是否启用 RSS 格式输出。
一个大的图片,用作 feed 展示。
一个小的图标,显示在订阅列表中。
设置 feed 的最大项目数量。在所有页面排序好后,插件会截取前 count 个项目。
如果你的站点文章很多,你应该考虑设置这个选项以减少 feed 文件大小。
类型:(RegExp | string)[] | (tagName:string) => boolean
应在 Feed 中保留的自定义元素或组件。
自定义的过滤函数,用于过滤哪些项目在 feed 中显示。
Feed 项目的排序器。
默认的排序行为是通过 Git 的文件添加日期 (需要 @vuepress/plugin-git
)。
提示
你应该启用 @vuepress/plugin-git
来获取最新创建的页面作为 feed 项目。否则,feed 项目将按照 VuePress 中页面的默认顺序排序。
channel
选项用于配置 Feed 频道。
可用选项详见 配置 → 频道设置
是否在开发服务器中启用
提示
由于性能原因,我们不提供热更新。重启开发服务器以同步你的变更。
类型:string
默认值:"http://localhost:${port}"
开发服务器使用的主机名
Atom 格式输出路径,相对于输出路径。
类型:string
默认值:@vuepress/plugin-feed/templates/atom.xsl
的内容 Atom xsl 模板文件没人陪美国
Atom xsl 输出路径,相对于输出路径。
JSON 格式输出路径,相对于输出路径。
RSS 格式输出路径,相对于输出路径。
类型:string
默认值:@vuepress/plugin-feed/templates/rss.xsl
的内容 RSS xsl 模板文件内容。
RSS xsl 输出路径,相对于输出路径。
Feed 生成控制器,详见 Feed 生成器 。
此插件内置了生成器,只有当你想完全控制 feed 生成时才需要设置此选项。
类型:Record<string, BaseFeedOptions>
你可以将它用于每个语言环境的特定选项。
除 hostname
外,上述任何选项均受支持。
指南 插件配置
+
+
+
diff --git a/zh/plugins/feed/frontmatter.html b/zh/plugins/feed/frontmatter.html
new file mode 100644
index 0000000000..e16d7932a8
--- /dev/null
+++ b/zh/plugins/feed/frontmatter.html
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+ Frontmatter 配置 | VuePress 生态系统
+
+
+
+
+
+ 你可以通过配置每个页面的 Frontmatter,来对每个 Feed 项目生成进行单独的控制。
默认情况下,所有文章均会被添加至 feed 流。如果你想在 feed 中移除特定页面,你可以在 frontmatter 中设置 feed: false
。
由 VuePress 自动生成,默认为页面的 h1 内容
页面描述
页面的发布日期
该页面是否是文章
如果此项设置为 false
,则该页不会包含在最终的 feed 中。
页面版权信息
页面的封面/分享图,需为完整链接或绝对链接。
Feed 项目的标题
Feed 项目的描述
Feed 项目的内容
类型:FeedAuthor[] | FeedAuthor
Feed 项目的作者
FeedAuthor 格式 interface FeedAuthor {
+ /**
+ * 作者名字
+ */
+ name ?: string
+
+ /**
+ * 作者邮件
+ */
+ email ?: string
+
+ /**
+ * 作者网站
+ *
+ * @description json format only
+ */
+ url ?: string
+
+ /**
+ * 作者头像
+ *
+ * @description json format only
+ */
+ avatar ?: string
+}
+
类型:FeedContributor[] | FeedContributor
Feed 项目的贡献者
FeedContributor 格式 interface FeedContributor {
+ /**
+ * 作者名字
+ */
+ name ?: string
+
+ /**
+ * 作者邮件
+ */
+ email ?: string
+
+ /**
+ * 作者网站
+ *
+ * @description json format only
+ */
+ url ?: string
+
+ /**
+ * 作者头像
+ *
+ * @description json format only
+ */
+ avatar ?: string
+}
+
Feed 项目的标识符,用于标识 Feed 项目。
你应该确保每个 Feed 项目有全局唯一的 guid。
插件配置 频道设置
+
+
+
diff --git a/zh/plugins/feed/getter.html b/zh/plugins/feed/getter.html
new file mode 100644
index 0000000000..3b253291c3
--- /dev/null
+++ b/zh/plugins/feed/getter.html
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+ Feed 获取器 | VuePress 生态系统
+
+
+
+
+
+ 你可以通过控制插件选项中的 getter
来完全控制 Feed 项目的生成。
类型:(page: Page) => string
项目标题获取器
类型:(page: Page) => string
项目链接获取器
类型:(page: Page) => string | undefined
项目描述获取器
提示
因为 Atom 在摘要中支持 HTML,所以如果可能的话,你可以在这里返回 HTML 内容,但内容必须以标记 html:
开头。
类型:(page: Page) => string
项目内容获取器
类型:(page: Page) => FeedAuthor[]
项目作者获取器。
FeedAuthor 格式 interface FeedAuthor {
+ /**
+ * 作者名字
+ */
+ name ?: string
+
+ /**
+ * 作者邮件
+ */
+ email ?: string
+
+ /**
+ * 作者网站
+ *
+ * @description json format only
+ */
+ url ?: string
+
+ /**
+ * 作者头像
+ *
+ * @description json format only
+ */
+ avatar ?: string
+}
+
类型:(page: Page) => FeedCategory[] | undefined
项目分类获取器。
FeedCategory 格式 interface FeedCategory {
+ /**
+ * 分类名称
+ */
+ name : string
+
+ /**
+ * 标识分类法的字符串
+ *
+ * @description rss format only
+ */
+ domain ?: string
+
+ /**
+ * URI 标识的分类 scheme
+ *
+ * @description atom format only
+ */
+ scheme ?: string
+}
+
类型:(page: Page) => FeedEnclosure | undefined
项目附件获取器。
FeedEnclosure 格式 interface FeedEnclosure {
+ /**
+ * Enclosure 地址
+ */
+ url : string
+
+ /**
+ * 类型
+ *
+ * @description 应为一个标准的 MIME 类型,rss format only
+ */
+ type : string
+
+ /**
+ * 按照字节数计算的大小
+ *
+ * @description rss format only
+ */
+ length ?: number
+}
+
类型:(page: Page) => Date | undefined
项目发布日期获取器
项目最后更新日期获取器
类型:(page: Page) => string
项目图片获取器
类型:(page: Page) => FeedContributor[]
项目贡献者获取器
FeedContributor 格式 interface FeedContributor {
+ /**
+ * 作者名字
+ */
+ name ?: string
+
+ /**
+ * 作者邮件
+ */
+ email ?: string
+
+ /**
+ * 作者网站
+ *
+ * @description json format only
+ */
+ url ?: string
+
+ /**
+ * 作者头像
+ *
+ * @description json format only
+ */
+ avatar ?: string
+}
+
类型:(page: Page) => string | undefined
项目版权获取器
频道设置
+
+
+
diff --git a/zh/plugins/feed/guide.html b/zh/plugins/feed/guide.html
new file mode 100644
index 0000000000..7354d4188e
--- /dev/null
+++ b/zh/plugins/feed/guide.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ 指南 | VuePress 生态系统
+
+
+
+
+
+ 插件可为你生成以下三种格式的 feed 文件:
请按照需要生成的格式,在插件选项中设置 atom
, json
或 rss
为 true
。
为了正确生成 Feed 链接,你需要在插件选项中设置 hostname
。
当你在浏览器中打开 Feed 文件时,我们会通过 xsl 模板将 atom 和 rss feed xml 魔法般地转换为可读的 html。你可以查看本站的 atom在新窗口打开 和 rss在新窗口打开 feed 作为案例!
如果你想在开发服务器中预览 Feed,你需要在插件选项中设置 devServer: true
。如果你没有使用默认的 http://localhost:{port}
,你还需要设置 devHostname
。
你可以通过设置 channel
选项来自自定义 Feed 频道的各项信息。
我们推荐进行如下设置:
将建立 Feed 的日期转换为 ISOString 写入到 channel.pubDate
中 通过 channel.ttl
中设置内容的更新周期(单位: 分钟) 通过 channel.copyright
设置版权信息 通过 channel.author
设置频道作者。 详细的选项及其默认值详见 配置 → 频道设置
默认情况下,所有文章均会被添加至 feed 流。
你可以在 frontmatter 中配置 feed
和其他选项控制每个页面的 Feed 项目内容,详见 Frontmatter 选项 了解它们如何被转换。
你可以通过配置插件选项中的 getter
完全控制 Feed 项目的生成逻辑。 详细的选项及其默认值详见 配置 → Feed 获取器
插件会针对每个语言生成单独的 Feed。
你可以通过插件选项中的 locales
分别对不同语言提供不同的默认设置。
插件配置
+
+
+
diff --git a/zh/plugins/feed/index.html b/zh/plugins/feed/index.html
new file mode 100644
index 0000000000..78c6bc6afa
--- /dev/null
+++ b/zh/plugins/feed/index.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ feed | VuePress 生态系统
+
+
+
+
+
+ npm i -D @vuepress/plugin-feed@next
+
import { feedPlugin } from '@vuepress/plugin-feed'
+
+export default {
+ plugins: [
+ feedPlugin ({
+ // 选项
+ }),
+ ],
+}
+
博客
+
+
+
diff --git a/zh/plugins/git.html b/zh/plugins/git.html
new file mode 100644
index 0000000000..f33d1256cb
--- /dev/null
+++ b/zh/plugins/git.html
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+ git | VuePress 生态系统
+
+
+
+
+
+ 该插件会收集你的页面的 Git 信息,包括创建和更新时间、贡献者等。
默认主题的 lastUpdated 和 contributors 就是由该插件支持的。
该插件主要用于开发主题,大部分情况下你不需要直接使用它。
npm i -D @vuepress/plugin-git@next
+
import { gitPlugin } from '@vuepress/plugin-git'
+
+export default {
+ plugins: [
+ gitPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
该插件要求你的项目在 Git 仓库在新窗口打开 下,这样它才能从提交历史记录中收集信息。
在构建站点时,你应该确保所有的提交记录是可以获取到的。举例来说, CI 工作流通常会在克隆你的仓库时添加 --depth 1在新窗口打开 参数来避免拉取全部的提交记录,因此你需要禁用这个功能,以便该插件在 CI 可以中正常使用。
注意
该插件会显著降低准备数据的速度,特别是在你的页面数量很多的时候。你可以考虑在 dev
模式下禁用该插件来获取更好的开发体验。
类型: boolean
默认值: true
详情:
是否收集页面的创建时间。
类型: boolean
默认值: true
详情:
是否收集页面的更新时间。
类型: boolean
默认值: true
详情:
是否收集页面的贡献者。
---
+gitInclude :
+ - relative/path/to/file1
+ - relative/path/to/file2
+---
+
该插件会向页面数据中添加一个 git
字段。
在使用该插件后,可以在页面数据中获取该插件收集到的 Git 信息:
import type { GitPluginPageData } from '@vuepress/plugin-git'
+import { usePageData } from 'vuepress/client'
+
+export default {
+ setup () {
+ const page = usePageData < GitPluginPageData >()
+ console . log ( page . value . git )
+ },
+}
+
interface GitContributor {
+ name : string
+ email : string
+ commits : number
+}
+
active-header-links palette
+
+
+
diff --git a/zh/plugins/google-analytics.html b/zh/plugins/google-analytics.html
new file mode 100644
index 0000000000..2767a8871a
--- /dev/null
+++ b/zh/plugins/google-analytics.html
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+ google-analytics | VuePress 生态系统
+
+
+
+
+
+ 将 Google Analytics在新窗口打开 集成到 VuePress 中。
该插件会通过引入 gtag.js在新窗口打开 来启用 Google Analytics 4在新窗口打开 。
npm i -D @vuepress/plugin-google-analytics@next
+
import { googleAnalyticsPlugin } from '@vuepress/plugin-google-analytics'
+
+export default {
+ plugins: [
+ googleAnalyticsPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
Google Analytics 会 自动收集部分事件在新窗口打开 ,比如 page_view
, first_visit
等。
因此,如果你只是想收集站点的一些基础数据,你只需要正确设置 Measurement ID ,不需要再额外做其他事情。
在引入该插件之后,一个全局的 gtag()
函数会被挂载到 window
对象上,你可以使用它进行 自定义事件的上报在新窗口打开 。
export default {
+ plugins: [
+ googleAnalyticsPlugin ({
+ id: 'G-XXXXXXXXXX' ,
+ }),
+ ],
+}
+
export default {
+ plugins: [
+ googleAnalyticsPlugin ({
+ id: 'G-XXXXXXXXXX' ,
+ debug: true ,
+ }),
+ ],
+}
+
baidu-analytics
+
+
+
diff --git a/zh/plugins/index.html b/zh/plugins/index.html
new file mode 100644
index 0000000000..5d7c9dbe1e
--- /dev/null
+++ b/zh/plugins/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ 插件 | VuePress 生态系统
+
+
+
+
+
+
+
+
+
diff --git a/zh/plugins/medium-zoom.html b/zh/plugins/medium-zoom.html
new file mode 100644
index 0000000000..f5408a50e0
--- /dev/null
+++ b/zh/plugins/medium-zoom.html
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+ medium-zoom | VuePress 生态系统
+
+
+
+
+
+ 将 medium-zoom在新窗口打开 集成到 VuePress 中,为图片提供可缩放的功能。
该插件已经集成到默认主题中。
npm i -D @vuepress/plugin-medium-zoom@next
+
import { mediumZoomPlugin } from '@vuepress/plugin-medium-zoom'
+
+export default {
+ plugins: [
+ mediumZoomPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
类型: Object
详情:
medium-zoom 的配置项。
参考:
你可以通过 zoomOptions 对大部分的缩放样式进行自定义,不过作为补充,该插件同样提供了一些 CSS 变量:
:root {
+ --medium-zoom-z-index : 100 ;
+ --medium-zoom-bg-color : #ffffff ;
+ --medium-zoom-opacity : 1 ;
+}
+
详情:
返回该插件使用的 Zoom
实例,便于你直接使用实例上的 methods在新窗口打开 。
该插件会在切换路由进入当前页面时使图片支持缩放。但如果你要动态添加新图片,那么你可能就需要这个方法来让这些新图片也支持缩放。
该插件在 Zoom
实例上额外添加了一个 refresh
方法,它将使用 selector 作为默认参数,先调用 zoom.detach()
再调用 zoom.attach()
,便于你快速刷新当前页面图片的缩放状态。
示例:
import { nextTick } from 'vue'
+import { useMediumZoom } from '@vuepress/plugin-medium-zoom/client'
+
+export default {
+ setup () {
+ const zoom = useMediumZoom ()
+
+ // ... 进行了一些操作,在当前页面添加了新的图片
+
+ // 此时你可能需要手动调用 `refresh` 来让这些新图片支持缩放
+ nextTick (() => {
+ zoom . refresh ()
+ })
+ },
+}
+
external-link-icon nprogress
+
+
+
diff --git a/zh/plugins/nprogress.html b/zh/plugins/nprogress.html
new file mode 100644
index 0000000000..f3effac49a
--- /dev/null
+++ b/zh/plugins/nprogress.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ nprogress | VuePress 生态系统
+
+
+
+
+
+ 将 nprogress在新窗口打开 集成到 VuePress 中,在切换到另一个页面时会展示进度条。
该插件已经集成到默认主题中。
npm i -D @vuepress/plugin-nprogress@next
+
import { nprogressPlugin } from '@vuepress/plugin-nprogress'
+
+export default {
+ plugins: [ nprogressPlugin ()],
+}
+
你可以通过 CSS 变量来自定义进度条的样式:
:root {
+ --nprogress-color : #29d ;
+ --nprogress-z-index : 1031 ;
+}
+
medium-zoom photo-swipe
+
+
+
diff --git a/zh/plugins/palette.html b/zh/plugins/palette.html
new file mode 100644
index 0000000000..0e1fb94b77
--- /dev/null
+++ b/zh/plugins/palette.html
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+ palette | VuePress 生态系统
+
+
+
+
+
+ 为你的主题提供调色板功能。
该插件主要用于开发主题,并且已经集成到默认主题中。大部分情况下你不需要直接使用它。
对于主题作者,该插件可以帮助你提供用户自定义样式的能力。
npm i -D @vuepress/plugin-palette@next
+
import { palettePlugin } from '@vuepress/plugin-palette'
+
+export default {
+ plugins: [
+ palettePlugin ({
+ // 配置项
+ }),
+ ],
+}
+
该插件会提供一个 @vuepress/plugin-palette/palette
(调色板文件)和一个 @vuepress/plugin-palette/style
(样式文件),用于在你的主题样式中引入。
调色板文件用于定义样式变量,因此它一般会在你主题样式的开头引入。举例来说,用户可以在调色板中定义 CSS 变量在新窗口打开 、 SASS 变量在新窗口打开 、 LESS 变量在新窗口打开 或 Stylus 变量在新窗口打开 ,然后你可以在你的主题样式中使用这些变量。
样式文件用于覆盖默认样式或添加额外样式,因此它一般会在你主题样式的末尾引入。
在你的主题中使用该插件,假设你使用 SASS 作为 CSS 预处理器:
export default {
+ // ...
+ plugins: [ palettePlugin ({ preset: 'sass' })],
+}
+
在你主题需要使用对应变量的地方引入该插件的调色板文件,比如在 Layout.vue
中:
< template >
+ < h1 class = "palette-title" > 你好,调色板! </ h1 >
+</ template >
+
+< style lang = "scss" >
+/* 从该插件的调色板中引入变量 */
+@import '@vuepress/plugin-palette/palette' ;
+
+/* 设置变量的默认值 */
+$color : red !default ;
+
+/* 在你的样式中使用变量 */
+.palette-title {
+ color : $color ;
+}
+</ style >
+
然后,用户就可以在 .vuepress/styles/palette.scss
中自定义变量:
在你主题的样式之后引入该插件的样式文件,比如在 clientConfigFile
中:
// 引入你主题本身的样式文件
+import 'path/to/your/theme/style'
+// 引入该插件的样式文件
+import '@vuepress/plugin-palette/style'
+
然后,用户就可以在 .vuepress/styles/index.scss
中添加额外样式,并可以覆盖你主题本身的样式:
h1 {
+ font-size : 2.5rem ;
+}
+
git reading-time
+
+
+
diff --git a/zh/plugins/photo-swipe.html b/zh/plugins/photo-swipe.html
new file mode 100644
index 0000000000..932bc75102
--- /dev/null
+++ b/zh/plugins/photo-swipe.html
@@ -0,0 +1,181 @@
+
+
+
+
+
+
+
+
+ photo-swipe | VuePress 生态系统
+
+
+
+
+
+ 此插件会使页面正文内的图片在点击时进入浏览模式浏览。
npm i -D @vuepress/plugin-photo-swipe@next
+
import { photoSwipePlugin } from '@vuepress/plugin-photo-swipe'
+
+export default {
+ plugins: [
+ photoSwipePlugin ({
+ // 选项
+ }),
+ ],
+}
+
在图片预览模式中,你可以:
左右滑动按顺序浏览页面内其他的图片 查看图片的描述 对图片进行缩放 全屏浏览图片 下载图片 分享图片 提示
除了点击右上角的 "×" 退出浏览模式外,在上下滚动超过一定距离后,会自动退出图片浏览模式。 在移动端,或使用 PC 触控板,你可以使用平移、缩放手势在浏览模式中平移、缩放图片。 类型:string | string[]
默认值:".theme-default-content :not(a) > img:not([no-view])"
详情:图片选择器 类型:boolean
默认值:true
详情:是否在滚动时关闭当前图片。 类型:number
默认值:800
详情:
操作页面 DOM 的延时,单位 ms。
提示
如果你使用的主题有切换动画,建议配置此选项为 切换动画时长 + 200
。
内置支持语言 简体中文 (zh-CN)繁体中文 (zh-TW)英文(美国) (en-US)德语 (de-DE)德语(澳大利亚) (de-AT)俄语 (ru-RU)乌克兰语 (uk-UA)越南语 (vi-VN)葡萄牙语(巴西) (pt-BR)波兰语 (pl-PL)法语 (fr-FR)西班牙语 (es-ES)斯洛伐克 (sk-SK)日语 (ja-JP)土耳其语 (tr-TR)韩语 (ko-KR)芬兰语 (fi-FI)印尼语 (id-ID)荷兰语 (nl-NL)传递给 photo-swipe
在新窗口打开 的额外选项。
import { definePhotoSwipeConfig } from '@vuepress/plugin-photo-swipe/client'
+
+definePhotoSwipeConfig ({
+ // 在此设置 photoswipe 选项
+})
+
+export default {}
+
你可以通过 API 来调用 photoswipe。
createPhotoSwipe
允许你以编程的方式查看图片链接:
< script setup lang = "ts" >
+import { onMounted , onUnmounted } from 'vue' ;
+import { createPhotoSwipe } from "@vuepress/plugin-photo-swipe/client" ;
+
+let state : PhotoSwipeState | null = null ;
+
+const openPhotoSwipe = ( index : number ) => {
+ state ?. open ( index - 1 );
+};
+
+onMounted ( async () => {
+ // 通过图片链接创建一个新的 photoswipe 实例
+ state = await createPhotoSwipe (
+ [
+ 'https://exmaple.com/image1.png'
+ 'https://exmaple.com/image2.png'
+ 'https://exmaple.com/image3.png'
+ ],
+ {
+ // photoswipe 选项
+ }
+ );
+});
+
+onUnmounted (() => {
+ state ?. destroy ()
+})
+</ script >
+
+< template >
+ < button v-for = " i in 3 " @ click = " openPhotoSwipe ( i ) " > open photo {{ i }} </ button >
+</ template >
+
registerPhotoSwipe
允许你为给定的图片元素注册 photoswipe:
< script setup lang = "ts" >
+import { onMounted , onUnmounted } from 'vue'
+import { registerPhotoSwipe } from '@vuepress/plugin-photo-swipe/client'
+
+let destroy : () => void | null = null
+
+onMounted ( async () => {
+ await nextTick ()
+
+ const images = Array . from ( document . querySelectorAll ( 'img' ))
+
+ // 通过图片元素创建一个新的 photoswipe 实例
+ state = await registerPhotoSwipe ( images , {
+ // photoswipe 选项
+ })
+})
+
+onUnmounted (() => {
+ destroy ?.()
+})
+</ script >
+
你可以通过 CSS 变量来自定义部分样式:
:root {
+ --photo-swipe-bullet : #fff ;
+ --photo-swipe-bullet-active : #3eaf7c ;
+}
+
nprogress redirect
+
+
+
diff --git a/zh/plugins/prismjs.html b/zh/plugins/prismjs.html
new file mode 100644
index 0000000000..8d4ab6ee1f
--- /dev/null
+++ b/zh/plugins/prismjs.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ prismjs | VuePress 生态系统
+
+
+
+
+
+ 该插件使用 Prism.js在新窗口打开 来为 Markdown 代码块启用代码高亮。
该插件已经集成到默认主题中。
需要注意的是,该插件仅会给代码块添加 HTML 标记,而不会添加样式。当你在一个自定义主题中使用它时,可能需要自己选择并引入 Prism.js 样式主题。
npm i -D @vuepress/plugin-prismjs@next
+
import { prismjsPlugin } from '@vuepress/plugin-prismjs'
+
+export default {
+ plugins: [
+ prismjsPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
类型: string[]
默认值: ['markdown', 'jsdoc', 'yaml']
详情:
需要预加载的语言。
默认情况下,语言会在解析 Markdown 文件时按需加载。
然而, Prism.js 在动态加载语言时可能会遇到 一些潜在的问题在新窗口打开 。为了避免这些问题,你可以使用该配置项来预加载一些语言。
shiki
+
+
+
diff --git a/zh/plugins/pwa/config.html b/zh/plugins/pwa/config.html
new file mode 100644
index 0000000000..a1edb22e4e
--- /dev/null
+++ b/zh/plugins/pwa/config.html
@@ -0,0 +1,176 @@
+
+
+
+
+
+
+
+
+ 配置 | VuePress 生态系统
+
+
+
+
+
+ 类型:string
详情:
favicon.ico
地址,填入绝对路径。
类型:string
默认值:"#46bd87"
详情:PWA 的主题色。 类型:boolean
默认值:false
详情:是否缓存主页和 404 错误页之外的 HTML 文件 类型:boolean
默认值:false
详情:是否缓存图片。 类型:number
默认值:2048
详情:
允许缓存的最大大小 (以 KB 为单位)
注意
此选项具有最高优先级,任何超过此值的文件都会被排除。
所以你如果生成了很大的 HTML 或 JS 文件,请考虑调高此值,否则你的 PWA 可能无法在离线模式下正常运行。
类型:number
默认值:1024
详情:
图片允许缓存的最大大小 (以 KB 为单位)
更高支持苹果的特殊设置,忽略它们是安全的。
类型:string
详情:填入苹果使用的图标地址,推荐 152×152 大小 类型:"black" | "white"
默认值:"black"
详情:Safari 状态栏颜色 针对微软磁贴的特殊设置,忽略它们是安全的。
类型:string
默认值:themeColor
详情:磁贴颜色。 类型:string
默认值:"PwaFoundPopup"
详情:自定义的提示弹窗组件路径。 类型:string
默认值:"PwaReadyPopup"
详情:自定义的更新弹窗组件路径。 类型:boolean
默认值:false
详情:是否为选项中所有绝对链接添加 base。 类型:PwaPluginLocaleConfig
interface PwaPluginLocaleData {
+ /**
+ * 安装按钮文字
+ */
+ install : string
+
+ /**
+ * iOS 安装文字
+ */
+ iOSInstall : string
+
+ /**
+ * 取消按钮文字
+ */
+ cancel : string
+
+ /**
+ * 关闭按钮文字
+ */
+ close : string
+
+ /**
+ * 上一张图片文字
+ */
+ prevImage : string
+
+ /**
+ * 下一张图片文字
+ */
+ nextImage : string
+
+ /**
+ * 安装解释
+ */
+ explain : string
+
+ /**
+ * 描述标签文字
+ */
+ desc : string
+
+ /**
+ * 特性标签文字
+ */
+ feature : string
+
+ /**
+ * 更新内容提示文字
+ */
+ hint : string
+
+ /**
+ * 更新内容可用文字
+ */
+ update : string
+}
+
+interface PwaPluginLocaleConfig {
+ [ localePath : string ]: PwaPluginLocaleData
+}
+
详情:
PWA 插件的国际化配置。
内置支持语言 简体中文 (zh-CN)繁体中文 (zh-TW)英文(美国) (en-US)德语 (de-DE)德语(澳大利亚) (de-AT)俄语 (ru-RU)乌克兰语 (uk-UA)越南语 (vi-VN)葡萄牙语(巴西) (pt-BR)波兰语 (pl-PL)法语 (fr-FR)西班牙语 (es-ES)斯洛伐克 (sk-SK)日语 (ja-JP)土耳其语 (tr-TR)韩语 (ko-KR)芬兰语 (fi-FI)印尼语 (id-ID)荷兰语 (nl-NL)import { usePwaEvent } from '@vuepress/plugin-pwa/client'
+
+export default {
+ setup () {
+ const event = usePwaEvent ()
+ event . on ( 'ready' , ( registration ) => {
+ console . log ( 'Service worker is active.' )
+ })
+ },
+}
+
import { forceUpdate } from '@vuepress/plugin-pwa/client'
+import { onMounted } from 'vue'
+
+export default {
+ setup () {
+ onMounted (() => {
+ forceUpdate ()
+ })
+ },
+}
+
详情:
手动注册 Service Worker。
参数:
参数 类型 描述 serviceWorkerPath string
Service worker 的路径 hooks object
Service worker 的钩子 showStatus boolean
在控制台输出状态日志
interface Hooks {
+ registrationOptions ?: RegistrationOptions
+ ready ?: ( registration : ServiceWorkerRegistration ) => void
+ registered ?: ( registration : ServiceWorkerRegistration ) => void
+ cached ?: ( registration : ServiceWorkerRegistration ) => void
+ updated ?: ( registration : ServiceWorkerRegistration ) => void
+ updatefound ?: ( registration : ServiceWorkerRegistration ) => void
+ offline ?: () => void
+ error ?: ( error : Error ) => void
+}
+
import { registerSW } from '@vuepress/plugin-pwa/client'
+import { onMounted } from 'vue'
+
+export default {
+ setup () {
+ onMounted (() => {
+ registerSW ( '/service-worker.js' , {
+ ready ( registration ) {
+ console . log ( 'Service worker is active.' )
+ },
+ })
+ })
+ },
+}
+
详情:
激活等待中的 Service Worker。
参数:
参数 类型 描述 registration ServiceWorkerRegistration
想要激活的 Service Worker 的注册
import { usePwaEvent , skipWaiting } from '@vuepress/plugin-pwa/client'
+
+export default {
+ setup () {
+ const event = usePwaEvent ()
+
+ event . on ( 'updated' , ( registration ) => {
+ console . log ( 'The waiting service worker is available.' )
+ // activate the waiting service worker
+ skipWaiting ( registration )
+ })
+ },
+}
+
详情:
手动注销 Service Worker。
示例:
import { unregisterSW } from '@vuepress/plugin-pwa/client'
+import { onMounted } from 'vue'
+
+export default {
+ setup () {
+ onMounted (() => {
+ unregisterSW ()
+ })
+ },
+}
+
你可以通过 CSS 变量来自定义样式:
:root {
+ --pwa-z-index : 10 ;
+ --pwa-color : #2c3e50 ;
+ --pwa-bg-color : #ffffff ;
+ --pwa-border-color : #3eaf7c ;
+ --pwa-shadow-color : rgb ( 0 0 0 / 15% );
+ --pwa-btn-text-color : #ffffff ;
+ --pwa-btn-bg-color : #3eaf7c ;
+ --pwa-btn-hover-bg-color : #4abf8a ;
+ --pwa-content-color : #333 ;
+ --pwa-content-light-color : #666 ;
+}
+
指南
+
+
+
diff --git a/zh/plugins/pwa/guide.html b/zh/plugins/pwa/guide.html
new file mode 100644
index 0000000000..df11320bb6
--- /dev/null
+++ b/zh/plugins/pwa/guide.html
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+ 指南 | VuePress 生态系统
+
+
+
+
+
+ 将你的 VuePress 站点变成渐进式网络应用程序 (PWA)。
此插件使用 workbox-build在新窗口打开 生成 Service Worker 文件,并使用 register-service-worker在新窗口打开 注册 Service Worker。
A PWA uses a Service Worker (SW for short) to cache and proxy site content.
一个 PWA 使用 Service Worker (简称 SW) 来获取并托管网站内容。
为了使你的网站符合 PWA 的要求,一个网络 App 清单文件是必要的,并且你的 PWA 应满足可安装性要求。
你可以通过设置 manifest
选项来自定义 manifest 文件,或者在 public 文件夹中提供 manifest.webmanifest
或 manifest.json
。前者优先级更高。
插件会自动为你生成 manifest.webmanifest
,并在每个页面的 <head>
中添加清单链接声明,但是 你至少应该通过 manifest.icons
或 PWA 插件中的其他选项设置一个有效的图标。
注意
可安装性规范要求 manifest 中至少声明一个有效的图标。
所以如果你不配置 manifest.icons
,访问者只能享受到 Service Worker 缓存带来的离线可访问性,而并不能作为 PWA 进行安装。
此外,该插件默认不处理清单中的任何内容,而是按原样输出。 这意味着,如果你计划部署到子目录,则应自行将 URL 前缀附加到自己的清单 Urls 中。如果你需要的所有东西都在 base 文件夹下,你可以在插件选项中设置 appendBase: true
让插件将 base
自动附加到任何地址。
为了更好的控制 Service Worker 可以预缓存的内容,插件提供了相关的缓存控制选项。
默认情况下插件会预缓存所有的 JS 和 CSS 文件,但仅缓存主页和 404 页面的 HTML。插件同时还会缓存字体文件 (woff, woff2, eot, ttf, otf) 和 SVG 图标。
如果你的站点只有少量重要图片,并希望它们在离线模式下显示,你可以通过设置 cacheImage
选项为 true
来缓存站点图片。
我们通过文件后缀名识别图片,任何以 .png
, .jpg
, .jpeg
, .gif
, .bmp
, .webp
结尾的文件都会视为图片。
当你网站体积不大,并且希望文档完全离线可用时,你可以通过设置 cacheHTML
为 true
来缓存所有 HTML 页面。
为什么默认不缓存非主页和 404 页面
虽然说 VuePress 为所有的页面通过 SSG 生成了 HTML 文件,但是这些文件主要用于 SEO,并能够让你在后端不做 SPA 配置的情况下能够直接访问任何链接。
VuePress 本质上是一个 SPA。这意味着你只需要缓存主页并从主页进入即可正常访问所有页面。所以默认不缓存其他 HTML 能够有效减小缓存大小 (可以缩减大约 40% 的体积),加快 SW 更新速度。
但是这样做也有缺点,如果用户直接从非主页进入网站,首个页面的 HTML 文件仍需要从互联网加载。同时离线环境下,用户只能通过主页进入再自行导航到对应页面,直接访问某个链接会出现无法访问的提示。
为了防止在预缓存列表中包含大文件,任何 > 2 MB 的文件或 > 1 MB 的图片都将被忽略。 你可以通过 maxSize
和 maxImageSize
来自定义大小限制 (单位为 KB)。
我们提供 update
选项控制用户如何接收更新。
update
选项的默认值是 "available"
,这意味着当网站内容更新后,新的 SW 会在后台静默安装,并在安装结束后弹窗提示用户新内容就绪。用户可以自主选择是否立即刷新查看新内容。这意味在新 SW 就绪前用户会访问旧版本网站。
如果你的文档仍在建设期,希望尽早提示用户他可能在阅读已过时的内容,你可以将其设置为 "hint"
。这样用户在进入文档后数秒内就可以收到新内容已发布的通知。但这样做的负面效果是如果用户在新 SW 就绪前选择更新,那么他将在新 SW 安装并接管页面前,需要从互联网获取页面的全部资源。
如果你的文档很稳定,或者你在托管博客,不太关心用户立即接收到最新版本,你可以将其设置为 "disabled"
,这意味着新的 SW 将在后台完全静默安装并在安装后等待,当旧版本 SW 控制的页面全部关闭后,新 SW 将再下次访问接管并提供用户新内容。此设置可以避免用户在访中被弹窗打扰。
如果你希望通过 SW 来加速用户在弱网或无网条件下的访问,但同时希望用户时刻访问新内容,你可以将此选项设置为 "force"
。这意味着检测到新 SW 后旧 SW 将会被立刻销毁并且页面会被刷新以确保用户浏览最新内容。最大的缺点就是致新 SW 发布后,用户在重新进入网站后的几秒内会遇到预期之外的突然刷新,并且他们将必须通过互联网访问文档并完全重新安装最新的 SW。
当检测到新内容 (检测到新的 SW) 时,更新提示弹窗将会出现;当新内容就绪时,更新就绪弹窗将会出现。
如果你对默认的弹窗不满意,你可以自行编写组件更换。从 @vuepress/plugin-pwa/client
中导入 PwaFoundPopup
或 PwaReadyPopup
并使用其 slot 来自定义弹窗内容,然后将组件路径传递给 foundComponent
或 readyComponent
选项。
< script setup lang = "ts" >
+import { PwaFoundPopup } from '@vuepress/plugin-pwa/client'
+</ script >
+< template >
+ < PwaFoundPopup v-slot = " { found , refresh } " >
+ < div v-if = " found " >
+ 已找到新内容
+ < button @ click = " refresh " > 刷新 </ button >
+ </ div >
+ </ PwaFoundPopup >
+</ template >
+
< script setup lang = "ts" >
+import { PwaReadyPopup } from '@vuepress/plugin-pwa/client'
+</ script >
+< template >
+ < PwaReadyPopup v-slot = " { isReady , reload } " >
+ < div v-if = " isReady " >
+ 新内容已就绪
+ < button @ click = " reload " > 应用 </ button >
+ </ div >
+ </ PwaReadyPopup >
+</ template >
+
插件还提供了其他 PWA 相关选项,比如微软磁贴图标与颜色设置,苹果图标等。 如果你是一个高级用户,你也可以设置 generateSwConfig
来配置 workbox-build
。查看 插件选项 了解更多细节。
更多内容,请详见:
配置
+
+
+
diff --git a/zh/plugins/pwa/index.html b/zh/plugins/pwa/index.html
new file mode 100644
index 0000000000..42f4906bdd
--- /dev/null
+++ b/zh/plugins/pwa/index.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ pwa | VuePress 生态系统
+
+
+
+
+
+ npm i -D @vuepress/plugin-pwa@next
+
import { pwaPlugin } from '@vuepress/plugin-pwa'
+
+export default {
+ plugins: [
+ pwaPlugin ({
+ // 选项
+ }),
+ ],
+}
+
remove-pwa
+
+
+
diff --git a/zh/plugins/reading-time.html b/zh/plugins/reading-time.html
new file mode 100644
index 0000000000..8d5b907f16
--- /dev/null
+++ b/zh/plugins/reading-time.html
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+ reading-time | VuePress 生态系统
+
+
+
+
+
+ 此插件会为每个页面生成字数统计与预计阅读时间。
npm i -D @vuepress/plugin-reading-time@next
+
import { readingTimePlugin } from '@vuepress/plugin-reading-time'
+
+export default {
+ plugins: [
+ readingTimePlugin ({
+ // 配置项
+ }),
+ ],
+}
+
插件会将相关信息注入到页面数据的 readingTime
,其中:
readingTime.minutes
:为预计阅读时间(分钟)number
readingTime.words
:字数统计,number
对于任何页面,你可以从 page.data.readingTime
获取预计阅读时间与字数统计:
page . data . readingTime // { minutes: 3.2, words: 934 }
+
你可以在 extendsPage
以及其他生命周期获取它做进一步处理:
export default {
+ // ...
+ extendsPage : ( page ) => {
+ page . data . readingTime // { minutes: 3.2, words: 934 }
+ },
+
+ onInitialized : ( app ) => {
+ app . pages . map (( page ) => {
+ page . data . readingTime // { minutes: 3.2, words: 934 }
+ })
+ },
+}
+
你可以从 @vuepress/plugin-reading-time/client
导入 useReadingTimeData
和 useReadingTimeLocale
来获取当前页面的阅读时间数据和语言环境数据:
< script setup lang = "ts" >
+import {
+ useReadingTimeData ,
+ useReadingTimeLocale ,
+} from '@vuepress/plugin-reading-time/client'
+
+const readingTimeData = useReadingTimeData () // { minutes: 1.1, words: 100 }
+const readingTimeLocale = useReadingTimeLocale () // { time: "1 分钟", words: "100 字" }
+</ script >
+
类型:number
默认值:300
详情: 每分钟阅读字数 内置支持语言 简体中文 (zh-CN)繁体中文 (zh-TW)英文(美国) (en-US)德语 (de-DE)德语(澳大利亚) (de-AT)俄语 (ru-RU)乌克兰语 (uk-UA)越南语 (vi-VN)葡萄牙语(巴西) (pt-BR)波兰语 (pl-PL)法语 (fr-FR)西班牙语 (es-ES)斯洛伐克 (sk-SK)日语 (ja-JP)土耳其语 (tr-TR)韩语 (ko-KR)芬兰语 (fi-FI)印尼语 (id-ID)荷兰语 (nl-NL)你可以从 @vuepress/plugin-reading-time/client
导入并使用这些 API:
interface ReadingTime {
+ /** 分钟为单位的预计阅读时长 */
+ minutes : number
+ /** 内容的字数 */
+ words : number
+}
+
+const useReadingTimeData : () => ComputedRef < ReadingTime | null >
+
当插件被禁用时会返回 null
。
interface ReadingTimeLocale {
+ /** 当前语言的预计阅读时间 */
+ time : string
+ /** 当前语言的字数文字 */
+ words : string
+}
+
+const useReadingTimeLocale : () => ComputedRef < ReadingTimeLocale >
+
由于此插件主要面向插件和主题开发者,所以提供了 "使用 API":
import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
+
+export default ( options ) => ( app ) => {
+ useReadingTimePlugin ( app , {
+ // 你的选项
+ })
+
+ return {
+ name: 'vuepress-plugin-xxx' , // or vuepress-theme-xxx
+ }
+}
+
为什么你应该使用 "使用 API"
当你多次注册一个插件时,vuepress 会给你一个警告,告诉你只有第一个插件会生效。useReadingTimePlugin
会自动检测插件是否已经注册,避免多次注册。
如果你在 extendsPage
生命周期访问阅读时间数据,那么 @vuepress/plugin-reading-time
必须在你的主题或插件之前被调用,否则你会得到未定义的 page.data.readingTime
。useReadingTimePlugin
确保了 @vuepress/plugin-reading-time
在你的主题或插件之前被调用。
我们也提供了一个 removeReadingTimePlugin
api 来移除插件。你可以使用它来确保你的调用生效或清除插件:
import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
+
+export default ( options ) => ( app ) => {
+ // 这会移除任何当前存在的阅读时间插件
+ removeReadingTimePlugin ( app )
+
+ // 所以这会生效,即使之前已经注册了一个阅读时间插件
+ useReadingTimePlugin ( app , {
+ // 你的选项
+ })
+
+ return {
+ name: 'vuepress-plugin-xxx' , // or vuepress-theme-xxx
+ }
+}
+
palette rtl
+
+
+
diff --git a/zh/plugins/redirect.html b/zh/plugins/redirect.html
new file mode 100644
index 0000000000..0aa3258cd6
--- /dev/null
+++ b/zh/plugins/redirect.html
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+ redirect | VuePress 生态系统
+
+
+
+
+
+ 此插件提供页面与整站重定向功能。
npm i -D @vuepress/plugin-redirect@next
+
import { redirectPlugin } from '@vuepress/plugin-redirect'
+
+export default {
+ plugins: [
+ redirectPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
如果你改动了已有页面的地址,你可以在 Frontmatter 中使用 redirectFrom
选项设置重定向到此页面的地址,这样可以保证用户在访问旧链接时重定向到新的地址。
如果你需要将已有的页面重定向到新的页面,可以在 Frontmatter 中使用 redirectTo
选项设置需要重定向到的地址。这样该页面会在访问时重定向到新的地址。
你还可以通过插件选项中的 config
设置一个重定向映射,详见 config 。
插件可以根据用户的语言首选项,自动将无多语言链接重定向到用户需要的多语言页面。为了实现这一点,你需要留空默认的语言目录 (/
),并在插件选项中设置 autoLocale: true
。插件会自动根据用户语言跳转到对应的语言页面。
也就是你需要设置以下目录结构:
.
+├── en
+│ ├── ...
+│ ├── page.md
+│ └── README.md
+├── zh
+│ ├── ...
+│ ├── page.md
+│ └── README.md
+└── other_languages
+ ├── ...
+ ├── page.md
+ └── README.md
+
并将主题选项的 locales 设置为:
export default {
+ locales: {
+ '/en/' : {
+ lang: 'en-US' ,
+ // ...
+ },
+ '/zh/' : {
+ lang: 'zh-CN' ,
+ // ...
+ },
+ // other languages
+ },
+ // ...
+}
+
这样当用户访问 /
或 /page.html
时,他们会自动根据当前浏览器语言重定向到 /en/
/en/page.html
与 /zh/
/zh/page.html
。
自定义回退行为
有些时候,用户可能会在系统设置中添加多个语言。默认情况下,在站点支持首选语言,但首选语言不存在相应页面时,插件会尝试匹配用户设置的备用语言。
如果不需要回退到用户备用语言,而直接匹配用户首选语言,请在插件选项中设置 localeFallback: false
。
自定义缺失行为
有些时候,当用户访问一个页面时,文档尚未包含用户需要的语言版本 (一个普遍的情况是当前页面尚未完成相关语言的本地化),这样插件需要做出默认行为,你可以通过插件选项中的 defaultBehavior
定制它:
"defaultLocale"
: 重定向到默认语言或首个可用语言页面 (默认行为)"homepage"
: 重定向到当前语言的主页 (仅在文档包含用户语言时可用)"404"
: 重定向到当前语言的 404 页 (仅在文档包含用户语言时可用)自定义默认路径
你可以通过设置插件选项中的 defaultLocale
来自定义默认路径。默认情况下,插件会使用 locales
中的第一个键名作为默认路径。
插件支持在多语言文档中,自动根据用户语言首选项,将链接切换到用户需要的多语言页面。为了实现这一点,你需要在插件选项中设置 switchLocale
,它可以是以下两个值:
direct
: 直接切换到用户语言首选项页面,而不询问modal
: 在用户语言首选项与当前页面语言不同时,弹出一个对话框询问用户是否切换语言默认情况下,插件会从站点的多语言配置 locales
选项中,读取 语言路径
和 lang
生成多语言配置。有些时候,你可能希望多个语言命中同一个路径,这种情况下,你应该设置插件的 localeConfig
选项。
比如,你可能希望所有英文用户都匹配到 /en/
,并将繁体中文用户匹配到 /zh/
中,那么你可以设置:
redirect ({
+ localeConfig: {
+ '/en/' : [ 'en-US' , 'en-UK' , 'en' ],
+ '/zh/' : [ 'zh-CN' , 'zh-TW' , 'zh' ],
+ },
+})
+
有时你可能会更改 base
或为你的站点使用新域名,因此你可能希望原始站点自动重定向到新站点。
为了解决这个问题,插件提供了 vp-redirect
脚手架。
使用:
+ $ vp-redirect generate [源文件夹]
+
+Options:
+ --hostname < hostnam e> 重定向到的域名 ( 例如: https://new.example.com/ ) (默认: / )
+ -c, --config < confi g> 设置配置文件路径
+ -o, --output < outpu t> 设置输出目录 ( 默认: .vuepress/redirect )
+ --cache < cach e> 设置缓存文件的目录
+ -t, --temp < tem p> 设置临时文件的目录
+ --clean-cache 生成前清理缓存文件
+ --clean-temp 生成前清理临时文件
+ -h, --help 显示此消息
+
你需要传入 VuePress 项目源目录并设置 hostname
选项。重定向助手脚手架将初始化你的 VuePress 项目以获取页面,然后在输出目录生成重定向 html 文件。
默认情况下,插件将输出到源文件夹下的 .vuepress/redirect
目录。你应该将其上传到你的原始站点以提供重定向。
类型:Record<string, string> | ((app: App) => Record<string, string>)
详情
页面重定向映射。
可直接传入对象或传入参数为 App
的函数返回值一个对象。
每个键名必须是一个绝对路径,代表重定向的源页面地址。
每个键值是重定向的目标地址,可以是绝对路径或完整路径。
示例:
当 base 为 /base/
时:
将 /base/foo.html
重定向到 /base/bar.html
将 /base/baz.html
重定向到 https://example.com/qux.html
。 redirect ({
+ config: {
+ '/foo.html' : '/bar.html' ,
+ '/baz.html' : 'https://example.com/qux.html' ,
+ },
+})
+
将 post 文件夹的路径重定向到 posts 文件夹
redirect ({
+ hostname: 'https://example.com' ,
+ config : ( app ) =>
+ Object . fromEntries (
+ app . pages
+ . filter (({ path }) => path . startsWith ( '/posts/' ))
+ . map (({ path }) => [ path . replace ( / ^ \/ posts \/ / , '/post/' ), path ]),
+ ),
+})
+
类型:boolean
默认值: false
详情: 是否启用语言重定向 参考: 类型:Record<string, string | string[]>
详情:多语言语言配置 类型:boolean
默认值: true
详情:是否回退到用户定义的其他语言 类型:"defaultLocale" | "homepage" | "404"
默认值: "defaultLocale"
详情:当前链接没有可用的语言版本时的行为 类型:string
默认值: 首个语言路径 详情:默认语言路径 类型:string | string[]
详情:重定向到该页面的地址。 你可以通过 CSS 变量来自定义重定向弹窗的样式:
:root {
+ --redirect-z-index : 1499 ;
+ --redirect-bg-color : #fff ;
+ --redirect-bg-color-light : #f3f4f5 ;
+ --redirect-bg-color-lighter : #eeeeee ;
+ --redirect-text-color : #2c3e50 ;
+ --redirect-primary-bg-color : #3eaf7c ;
+ --redirect-primary-hover-bg-color : #4abf8a ;
+ --redirect-primary-text-color : #fff ;
+}
+
photo-swipe register-components
+
+
+
diff --git a/zh/plugins/register-components.html b/zh/plugins/register-components.html
new file mode 100644
index 0000000000..2d78ac5d7a
--- /dev/null
+++ b/zh/plugins/register-components.html
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+ register-components | VuePress 生态系统
+
+
+
+
+
+ 根据组件文件或目录自动注册 Vue 组件。
npm i -D @vuepress/plugin-register-components@next
+
import { registerComponentsPlugin } from '@vuepress/plugin-register-components'
+
+export default {
+ plugins: [
+ registerComponentsPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
import { getDirname , path } from 'vuepress/utils'
+
+const __dirname = getDirname ( import . meta . url )
+
+export default {
+ plugins: [
+ registerComponentsPlugin ({
+ components: {
+ FooBar: path . resolve ( __dirname , './components/FooBar.vue' ),
+ },
+ }),
+ ],
+}
+
import { getDirname , path } from 'vuepress/utils'
+
+const __dirname = getDirname ( import . meta . url )
+
+export default {
+ plugins: [
+ registerComponentsPlugin ({
+ componentsDir: path . resolve ( __dirname , './components' ),
+ }),
+ ],
+}
+
组件目录:
components
+├─ FooBar.vue
+└─ Baz.vue
+
组件会像这样被注册:
import { defineAsyncComponent } from 'vue'
+
+app . component (
+ 'FooBar' ,
+ defineAsyncComponent (() => import ( '/path/to/components/FooBar.vue' )),
+)
+
+app . component (
+ 'Baz' ,
+ defineAsyncComponent (() => import ( '/path/to/components/Baz.vue' )),
+)
+
redirect
+
+
+
diff --git a/zh/plugins/remove-pwa.html b/zh/plugins/remove-pwa.html
new file mode 100644
index 0000000000..792e2ad912
--- /dev/null
+++ b/zh/plugins/remove-pwa.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ remove-pwa | VuePress 生态系统
+
+
+
+
+
+ 此插件从你的 VuePress 站点中删除任何相关的 Service Worker,因此如果你在启用后任何 PWA 插件后移除它们,用户仍然可以获得更新。
如果你启用过 PWA,为什么需要这个插件?
PWA 插件,如 @vuepress/plugin-pwa
将 Service Worker 注册到你的站点,这将缓存你的站点并使其离线可用。
但是,如果你删除 PWA 插件,先前的 Service Worker 仍将在那里,但它们永远无法获得更新,因为他们永远无法找到要更新的新 Service Worker。 因此,用户将继续使用你网站的旧版本。
要解决这个问题:
一个新的内容为空的 Service Worker 需要生成在原位置。
新的 Service Worker 应该尝试删除旧 Service Worker 缓存的内容,然后它应该注销自己。
npm i -D @vuepress/plugin-remove-pwa@next
+
import { removePwaPlugin } from '@vuepress/plugin-remove-pwa'
+
+export default {
+ plugins: [
+ removePwaPlugin ({
+ // options
+ }),
+ ],
+}
+
类型:string
默认值:'workbox'
详情:Service worker 的缓存前缀。 类型: string
默认值:'service-worker.js'
详情:旧 Service Worker 的位置。 PWA
+
+
+
diff --git a/zh/plugins/rtl.html b/zh/plugins/rtl.html
new file mode 100644
index 0000000000..93298605bd
--- /dev/null
+++ b/zh/plugins/rtl.html
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+ rtl | VuePress 生态系统
+
+
+
+
+
+ 此插件会在配置的语言上设置 rtl 方向。
npm i -D @vuepress/plugin-rtl@next
+
import { rtlPlugin } from '@vuepress/plugin-rtl'
+
+export default {
+ plugins: [
+ rtlPlugin ({
+ // 配置项
+ locales: [ '/ar/' ],
+ }),
+ ],
+}
+
类型:string[]
默认值:['/']
详情: 开启 RTL 布局的多语言路径。 reading-time theme-data
+
+
+
diff --git a/zh/plugins/search.html b/zh/plugins/search.html
new file mode 100644
index 0000000000..8fd04a12a1
--- /dev/null
+++ b/zh/plugins/search.html
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+ search | VuePress 生态系统
+
+
+
+
+
+ 为你的文档网站提供本地搜索能力。
提示
当你正确配置该插件后,默认主题会把搜索框添加到导航栏。
该插件不一定能在其他主题中直接使用,因此你应参考主题本身的文档来获取更多信息。
npm i -D @vuepress/plugin-search@next
+
import { searchPlugin } from '@vuepress/plugin-search'
+
+export default {
+ plugins: [
+ searchPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
该插件会根据你的页面,在本地生成搜索索引,然后在用户访问站点时加载搜索索引文件。换句话说,这是一个轻量级的内置搜索能力,不会进行任何外部请求。
然而,当你的站点包含大量页面时,搜索索引文件也会变得非常大,它可能会拖慢你的页面加载速度。在这种情况下,我们建议你使用更成熟的解决方案 - docsearch 。
export default {
+ plugins: [
+ searchPlugin ({
+ locales: {
+ '/' : {
+ placeholder: 'Search' ,
+ },
+ '/zh/' : {
+ placeholder: '搜索' ,
+ },
+ },
+ }),
+ ],
+}
+
类型: (string | HotKeyOptions)[]
export interface HotKeyOptions {
+ /**
+ * Value of `event.key` to trigger the hot key
+ */
+ key : string ;
+ /**
+ * Whether to press `event.altKey` at the same time
+ *
+ * @default false
+ */
+ alt ?: boolean ;
+ /**
+ * Whether to press `event.ctrlKey` at the same time
+ *
+ * @default false
+ */
+ ctrl ?: boolean ;
+ /**
+ * Whether to press `event.shiftKey` at the same time
+ *
+ * @default false
+ */
+ shift ?: boolean ;
+}
+
默认值: ['s', '/']
详情:
指定热键的 event.key在新窗口打开 。
当按下热键时,搜索框会被聚焦。
将该配置项设为空数组可以禁用热键功能。
类型: number
默认值: 5
详情:
指定搜索结果的最大条数。
export default {
+ plugins: [
+ searchPlugin ({
+ // 排除首页
+ isSearchable : ( page ) => page . path !== '/' ,
+ }),
+ ],
+}
+
export default {
+ plugins: [
+ searchPlugin ({
+ // 允许搜索 Frontmatter 中的 `tags`
+ getExtraFields : ( page ) => page . frontmatter . tags ?? [],
+ }),
+ ],
+}
+
你可以通过 CSS 变量来自定义搜索框的样式:
:root {
+ --search-bg-color : #ffffff ;
+ --search-accent-color : #3eaf7c ;
+ --search-text-color : #2c3e50 ;
+ --search-border-color : #eaecef ;
+ --search-item-text-color : #5d81a5 ;
+ --search-item-focus-bg-color : #f3f4f5 ;
+ --search-input-width : 8rem ;
+ --search-result-width : 20rem ;
+}
+
提示
该组件主要用于主题开发。在大多数情况下你不需要直接使用该组件。
docsearch
+
+
+
diff --git a/zh/plugins/seo/config.html b/zh/plugins/seo/config.html
new file mode 100644
index 0000000000..bd4da25e0f
--- /dev/null
+++ b/zh/plugins/seo/config.html
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+ 选项 | VuePress 生态系统
+
+
+
+
+
+ 类型:Author
type AuthorName = string
+
+interface AuthorInfo {
+ /**
+ * 作者姓名
+ */
+ name : string
+
+ /**
+ * 作者网站
+ */
+ url ?: string
+
+ /**
+ * 作者 Email
+ */
+ email ?: string
+}
+
+type Author = AuthorName | AuthorName [] | AuthorInfo | AuthorInfo []
+
详情:
默认作者
类型:boolean
默认值:true
详情:
是否自动生成描述
类型:string
详情:
当找不到图片时的回退图片链接
类型:string
详情:
你的 twitter 用户名
指南
+
+
+
diff --git a/zh/plugins/seo/guide.html b/zh/plugins/seo/guide.html
new file mode 100644
index 0000000000..fc9c32e953
--- /dev/null
+++ b/zh/plugins/seo/guide.html
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+ 指南 | VuePress 生态系统
+
+
+
+
+
+ 本插件会通过向网站 <head>
注入标签,让你的网站完全支持 开放内容协议 OGP在新窗口打开 和 JSON-LD 1.1在新窗口打开 ,以全面增强站点的搜索引擎优化性。
插件开箱即用,在不做任何配置的情况下,会尽可能通过页面内容,提取对应的信息补全 OGP 与 JSON-LD 所需的必要标签。
默认情况下,插件会读取站点配置、主题配置与页面的 frontmatter 来尽可能自动生成。诸如站点名称,页面标题,页面类型,写作日期,最后更新日期,文章标签均会自动生成。
属性名称 值 og:url
options.hostname
+ path
og:site_name
siteConfig.title
og:title
page.title
og:description
page.frontmatter.description
|| 自动生成 (当插件选项中的 autoDescription
为 true
时)og:type
"article"
og:image
options.hostname
+ page.frontmatter.image
|| 页面的第一张图片|| 插件选项的 fallbackImage
og:updated_time
page.git.updatedTime
og:locale
page.lang
og:locale:alternate
siteData.locales
包含的其他语言twitter:card
"summary_large_image"
(仅在找到图片时)twitter:image:alt
page.title
(仅在找到图片时)article:author
page.frontmatter.author
|| options.author
article:tag
page.frontmatter.tags
|| page.frontmatter.tag
article:published_time
page.frontmatter.date
|| page.git.createdTime
article:modified_time
page.git.updatedTime
属性名 值 @context
"https://schema.org"
@type
"NewsArticle"
headline
page.title
image
页面中的图片|| options.hostname
+ page.frontmatter.image
datePublished
page.frontmatter.date
|| page.git.createdTime
dateModified
page.git.updatedTime
author
page.frontmatter.author
|| options.author
你可以在页面的 frontmatter 中配置 head
选项,自主添加特定标签到页面 <head>
以增强 SEO。
如:
---
+head :
+ - - meta
+ - name : keywords
+ content : SEO plugin
+---
+
会自动注入 <meta name="keywords" content="SEO plugin" />
。
本插件也支持你完全控制生成逻辑。
对于大多数页面,基本只有文章和网页两种类型,所以插件提供了 isArticle
选项让你提供辨别文章的逻辑。
选项接受一个 (page: Page) => boolean
格式的函数,默认情况下从 Markdown 文件生成的非主页页面都会被视为文章。
提示
如果某个网页的确符合图书、音乐之类的“冷门”类型,你可以通过设置下方三个选项处理它们。
你可以使用插件选项的 ogp
传入一个函数来按照你的需要修改默认 OGP 对象并返回。
function ogp (
+ /** 插件推断的 OGP 信息 */
+ ogp : SeoContent ,
+ /** 页面对象 */
+ page : Page ,
+ /** VuePress App */
+ app : App ,
+): SeoContent
+
详细的参数结构详见 配置 。
比如你在使用某个第三方主题,并按照主题要求为每篇文章在 Front Matter 中设置了 banner
,那你可以传入这样的 ogp
:
seoPlugin ({
+ ogp : ( ogp , page ) => ({
+ ... ogp ,
+ 'og:image' : page . frontmatter . banner || ogp [ 'og:image' ],
+ }),
+})
+
同 OGP,你可以使用插件选项的 jsonLd
传入一个函数来按照你的需要修改默认 JSON-LD 对象并返回。
function jsonLd (
+ /** 由插件推断出的 JSON-LD 对象 */
+ jsonLD : ArticleSchema | BlogPostingSchema | WebPageSchema ,
+ /** 页面对象 */
+ page : Page ,
+ /** VuePress App */
+ app : App ,
+): ArticleSchema | BlogPostingSchema | WebPageSchema
+
如果你将内容部署到不同的站点,或不同 URL 下的相同内容,你可能需要设置 canonical
选项为你的页面提供 “规范链接”。 你可以设置一个字符串,这样它会附加在页面路由链接之前,或者添加一个自定义函数 (page: Page) => string | null
返回规范链接。
例子
如果你的站点部署在 example.com
的 docs 文件夹下,但同时在下列网址中可用:
http://example.com/docs/xxx
https://example.com/docs/xxx
http://www.example.com/docs/xxx
https://www.example.com/docs/xxx
(首选)要让搜索引擎结果始终是首选,你可能需要将 canonical
设置为 https://www.example.com/docs/
,以便搜索引擎知道首选第四个 URL 作为索引结果。
有些时候你可能需要符合其他协议或按照其他搜索引擎提供的格式提供对应的 SEO 标签,此时你可以使用 customHead
选项,其类型为:
function customHead (
+ /** head 标签配置 */
+ head : HeadConfig [],
+ /** 页面对象 */
+ page : Page ,
+ /** VuePress App */
+ app : App ,
+): void
+
你应该直接修改传入的 head
参数。
搜索引擎优化 (S earch E ngine O ptimization),是一种透过了解搜索引擎的运作规则来调整网站,以及提高目的网站在有关搜索引擎内排名的方式。由于不少研究发现,搜索引擎的用户往往只会留意搜索结果最前面的几个条目,所以不少网站都希望透过各种形式来影响搜索引擎的排序,让自己的网站可以有优秀的搜索排名。 所谓“针对搜索引擎作最优化的处理”,是指为了要让网站更容易被搜索引擎接受。搜索引擎会将网站彼此间的内容做一些相关性的资料比对,然后再由浏览器将这些内容以最快速且接近最完整的方式,呈现给搜索者。搜索引擎优化就是通过搜索引擎的规则进行优化,为用户打造更好的用户体验,最终的目的就是做好用户体验。
你可以使用 Google 富媒体结构测试工具在新窗口打开 测试本站点。
选项
+
+
+
diff --git a/zh/plugins/seo/index.html b/zh/plugins/seo/index.html
new file mode 100644
index 0000000000..c7771c54ba
--- /dev/null
+++ b/zh/plugins/seo/index.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ seo | VuePress 生态系统
+
+
+
+
+
+ npm i -D @vuepress/plugin-seo@next
+
import { seoPlugin } from '@vuepress/plugin-seo'
+
+export default {
+ plugins: [
+ seoPlugin ({
+ // 选项
+ }),
+ ],
+}
+
站点地图
+
+
+
diff --git a/zh/plugins/shiki.html b/zh/plugins/shiki.html
new file mode 100644
index 0000000000..7f14bf9a47
--- /dev/null
+++ b/zh/plugins/shiki.html
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+ shiki | VuePress 生态系统
+
+
+
+
+
+ 该插件使用 Shiki在新窗口打开 来为 Markdown 代码块启用代码高亮。
npm i -D @vuepress/plugin-shiki@next
+
import { shikiPlugin } from '@vuepress/plugin-shiki'
+
+export default {
+ plugins: [
+ shikiPlugin ({
+ // 配置项
+ langs: [ 'ts' , 'json' , 'vue' , 'md' , 'bash' , 'diff' ],
+ }),
+ ],
+}
+
prismjs
+
+
+
diff --git a/zh/plugins/sitemap/config.html b/zh/plugins/sitemap/config.html
new file mode 100644
index 0000000000..4080ec7a74
--- /dev/null
+++ b/zh/plugins/sitemap/config.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ 配置 | VuePress 生态系统
+
+
+
+
+
+ 类型:string
必填:是
详情:
当前网站部署到的域名,插件需要此选项才能工作。
类型:string[]
详情:
需要额外包含的网址。
如果你有一些不包含在 VuePress 路由中的链接 (如: 存放在 public 文件夹下的页面或其他插件或工具直接生成的页面),你可能需要设置此项。
示例:['/about.html', '/api/']
类型:boolean
默认值:false
详情:
是否在开发服务器中启用
提示
由于性能原因,我们不提供热更新。重启开发服务器以同步你的变更。
类型:string
默认值:"sitemap.xml"
详情:
输出的文件名,相对于输出目录。
类型:string
默认值:"sitemap.xsl"
详情:
输出的 xsl 文件名,相对于输出目录。
类型:number
默认值:0.5
详情:
页面优先级,范围 0
至 1
。
指南 Frontmatter
+
+
+
diff --git a/zh/plugins/sitemap/frontmatter.html b/zh/plugins/sitemap/frontmatter.html
new file mode 100644
index 0000000000..dba9057a0f
--- /dev/null
+++ b/zh/plugins/sitemap/frontmatter.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ Frontmatter | VuePress 生态系统
+
+
+
+
+
+ 类型:number
默认值:0.5
详情:
页面优先级,范围 0
至 1
。
配置
+
+
+
diff --git a/zh/plugins/sitemap/guide.html b/zh/plugins/sitemap/guide.html
new file mode 100644
index 0000000000..446b8ee008
--- /dev/null
+++ b/zh/plugins/sitemap/guide.html
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+ 指南 | VuePress 生态系统
+
+
+
+
+
+ 本插件会为你的网站自动生成 Sitemap。为了使插件正常工作,你需要将部署的域名传递给插件的 hostname
选项。如果你想在开发服务器中预览,请配置 devServer
选项。
插件会自动根据页面的 Git 的时间戳生成页面的最后更新时间,同时会根据站点的多语言配置声明页面的其他语言版本替代地址。
默认情况下,所有除 404 页面以外的网站链接均会被添加进 Sitemap。
如果你希望在 VuePress 项目页面之外,添加其他页面链接到 Sitemap,请将它们变成数组传入插件的 extraUrls
选项。
如果你需要排除一些页面路径,你可以将它们变成数组传入到插件的 excludePaths
选项。你也可以在对应页面的 frontmatter 中,设置 sitemap
为 false
。
你还可以通过插件的 sitemapFilename
选项控制输出的地址,此地址相对于输出目录,默认为 sitemap.xml
。
页面默认的更新周期是 daily
(每天),如果你希望修改全部的页面周期,请在插件选项中设置 changefreq
。你也可以在页面的 frontmatter 中设置 sitemap.changefreq
,页面具有更高的优先级。
合法的频率有:
"always"
"hourly"
"daily"
"weekly"
"monthly"
"yearly"
"never"
你可以在插件中设置 priority
以提供一个默认值。同时你可以通过 frontmatter 中的 sitemap.priority
来为每个页面设置优先级。可接受的值为 0
到 1
的浮点数。
你可以通过插件的 modifyTimeGetter
来返回一个 ISO 字符串格式的时间,默认会通过 Git 插件生成。
以下是一个基于文件最后修改时间的例子。
// 基于文件最后修改时间
+({
+ modifyTimeGetter : ( page , app ) =>
+ fs . statSync ( app . dir . source ( page . filePathRelative )). mtime . toISOString ();
+})
+
网站地图 (Sitemap) 提供搜索引擎优化 (SEO):
为搜索引擎爬虫提供可以浏览整个网站的链接; 为搜索引擎爬虫提供一些链接,指向动态页面或者采用其他方法比较难以到达的页面; 如果访问者试图访问网站所在域内并不存在的 URL,那么这个访问者就会被转到“无法找到文件”的错误页面,而网站地图可以作为导航页。 网站地图通过使所有页面可被找到来增强搜索引擎优化的效果。
大部分搜索引擎只跟踪页面内有限数量的链接,因此当网站非常大的时候,网站地图对于使搜索引擎和访问者可以访问网站中的所有内容就变得必不可少了。
Sitemaps 是站点管理员向搜索引擎爬虫公布站点可被抓取页面的协议,sitemap 文件内容必须遵循 XML 格式的定义。每个 URL 可以包含更新的周期和时间、URL 在整个站点中的优先级。这样可以让搜索引擎更佳有效的抓取网站内容。
同步配置 robots.txt
由于 Sitemap 面向搜索引擎,配合此插件使用时,你最好保证你在 .vuepress/public
文件夹下放置了有效的 robots.txt
,以允许搜索引擎收录。一个最简单的 robots.txt 如下 (允许所有搜索引擎访问所有路径)
User-agent: *
+
+Allow: /
+
配置
+
+
+
diff --git a/zh/plugins/sitemap/index.html b/zh/plugins/sitemap/index.html
new file mode 100644
index 0000000000..b670155493
--- /dev/null
+++ b/zh/plugins/sitemap/index.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ sitemap | VuePress 生态系统
+
+
+
+
+
+ npm i -D @vuepress/plugin-sitemap@next
+
import { sitemapPlugin } from '@vuepress/plugin-sitemap'
+
+export default {
+ plugins: [
+ sitemapPlugin ({
+ // 选项
+ }),
+ ],
+}
+
搜索引擎增强 baidu-analytics
+
+
+
diff --git a/zh/plugins/theme-data.html b/zh/plugins/theme-data.html
new file mode 100644
index 0000000000..eea680b0a6
--- /dev/null
+++ b/zh/plugins/theme-data.html
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+ theme-data | VuePress 生态系统
+
+
+
+
+
+ 为你的主题提供客户端数据,包含 VuePress 的 多语言支持 。
该插件主要用于开发主题,并且已经集成到默认主题中。大部分情况下你不需要直接使用它。
对于主题作者,该插件可以提供与 VuePress 及默认主题相同的多语言支持机制。但是如果你的主题不需要提供多语言支持,或者你想用你自己的方式来实现多语言支持,那么你不需要使用该插件。
npm i -D @vuepress/plugin-theme-data@next
+
import { themeDataPlugin } from '@vuepress/plugin-theme-data'
+
+export default {
+ plugins: [
+ themeDataPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
export default {
+ plugins: [
+ themeDataPlugin ({
+ themeData: {
+ foo: 'foo' ,
+ locales: {
+ '/zh/' : {
+ foo: 'zh-foo' ,
+ },
+ },
+ },
+ }),
+ ],
+}
+
注意
主题数据对象在传递到客户端之前,会使用 JSON.stringify()
进行处理,因此你需要保证你提供的是一个可以被 JSON 序列化的对象。
import { useThemeData } from '@vuepress/plugin-theme-data/client'
+import type { ThemeData } from '@vuepress/plugin-theme-data/client'
+
+type MyThemeData = ThemeData <{
+ foo : string
+}>
+
+export default {
+ setup () {
+ const themeData = useThemeData < MyThemeData >()
+ console . log ( themeData . value )
+ },
+}
+
import { useThemeLocaleData } from '@vuepress/plugin-theme-data/client'
+import type { ThemeData } from '@vuepress/plugin-theme-data/client'
+
+type MyThemeData = ThemeData <{
+ foo : string
+}>
+
+export default {
+ setup () {
+ const themeLocaleData = useThemeLocaleData < MyThemeData >()
+ console . log ( themeLocaleData . value )
+ },
+}
+
rtl toc
+
+
+
diff --git a/zh/plugins/toc.html b/zh/plugins/toc.html
new file mode 100644
index 0000000000..02da24fec2
--- /dev/null
+++ b/zh/plugins/toc.html
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+ toc | VuePress 生态系统
+
+
+
+
+
+ 该插件会提供一个目录 (table-of-contents, TOC) 组件。
npm i -D @vuepress/plugin-toc@next
+
import { tocPlugin } from '@vuepress/plugin-toc'
+
+export default {
+ plugins: [
+ tocPlugin ({
+ // 配置项
+ }),
+ ],
+}
+
与 Markdown 目录语法 类似,该插件提供的目录组件可以直接在你的 Markdown 内容中使用:
<!-- Markdown 目录语法 -->
+
+[[ toc ]]
+
+<!-- Vue 目录组件 -->
+< Toc />
+
在 Build 模式中,它们都可以被正确地预渲染。然而,它们之间存在一些区别。
Markdown 语法 [[toc]]
仅能在 Markdown 文件中使用。它是由 markdown-it 解析的,生成的目录是静态内容。
组件 <Toc/>
既可以用在 Markdown 文件中,也可以用在 Vue 文件中。它是由 Vue 加载的,生成的目录是一个 Vue 组件。
该插件可以和 @vuepress/plugin-active-header-links 协同工作,你只需要将 headerLinkSelector 与该插件的 linkClass
匹配即可。当页面滚动至某个标题锚点后,对应的链接就会被加上 linkActiveClass
类名。
因此,该插件对于主题开发者来说更为有用。
类型: string
默认值: 'Toc'
详情:
指定目录组件的名称。
目录组件可以通过 Props 来进行自定义。
< template >
+ < Toc : headers = " headers " : options = " options " />
+</ template >
+
interface PageHeader {
+ level : number
+ title : string
+ slug : string
+ children : PageHeader []
+}
+
类型: Partial<TocPropsOptions>
interface TocPropsOptions {
+ containerTag : string
+ containerClass : string
+ listClass : string
+ itemClass : string
+ linkTag : 'a' | 'RouterLink' | 'RouteLink'
+ linkClass : string
+ linkActiveClass : string
+ linkChildrenActiveClass : string
+}
+
const defaultOptions = {
+ containerTag: 'nav' ,
+ containerClass: 'vuepress-toc' ,
+ listClass: 'vuepress-toc-list' ,
+ itemClass: 'vuepress-toc-item' ,
+ linkTag: 'RouteLink' ,
+ linkClass: 'vuepress-toc-link' ,
+ linkActiveClass: 'active' ,
+ linkChildrenActiveClass: 'active' ,
+}
+
< template >
+ <!-- container -->
+ < nav class = "vuepress-toc" >
+ <!-- list -->
+ < ul class = "vuepress-toc-list" >
+ <!-- item -->
+ < li class = "vuepress-toc-item" >
+ <!-- link -->
+ < RouteLink class = "vuepress-toc-link" to = "#foo" > Foo </ RouteLink >
+ </ li >
+ <!-- item with children -->
+ < li class = "vuepress-toc-item" >
+ <!-- link (children active) -->
+ < RouteLink class = "vuepress-toc-link active" to = "#bar" > Bar </ RouteLink >
+ <!-- list (children) -->
+ < ul class = "vuepress-toc-list" >
+ <!-- item -->
+ < li class = "vuepress-toc-item" >
+ <!-- link (active) -->
+ < RouteLink class = "vuepress-toc-link active" to = "#bar-child" >
+ Bar Child
+ </ RouteLink >
+ </ li >
+ </ ul >
+ </ li >
+ </ ul >
+ </ nav >
+</ template >
+
theme-data
+
+
+
diff --git a/zh/themes/default/components.html b/zh/themes/default/components.html
new file mode 100644
index 0000000000..7479f56e98
--- /dev/null
+++ b/zh/themes/default/components.html
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+ 内置组件 | VuePress 生态系统
+
+
+
+
+
+ Props:
type 类型: 'tip' | 'warning' | 'danger'
默认值: 'tip'
text vertical 类型: 'top' | 'middle' | 'bottom' | undefined
默认值: undefined
示例:
输入
- VuePress - < Badge type = "tip" text = "v2" vertical = "top" />
+- VuePress - < Badge type = "warning" text = "v2" vertical = "middle" />
+- VuePress - < Badge type = "danger" text = "v2" vertical = "bottom" />
+
输出
VuePress - v2 VuePress - v2 VuePress - v2 输入
< CodeGroup >
+ < CodeGroupItem title = "pnpm" >
+
+```bash:no-line-numbers
+pnpm install
+```
+
+ </ CodeGroupItem >
+
+ < CodeGroupItem title = "yarn" >
+
+```bash:no-line-numbers
+yarn install
+```
+
+ </ CodeGroupItem >
+
+ < CodeGroupItem title = "npm" active >
+
+```bash:no-line-numbers
+npm install
+```
+
+ </ CodeGroupItem >
+</ CodeGroup >
+
输出
Frontmatter Markdown
+
+
+
diff --git a/zh/themes/default/config.html b/zh/themes/default/config.html
new file mode 100644
index 0000000000..cb0eda8f65
--- /dev/null
+++ b/zh/themes/default/config.html
@@ -0,0 +1,177 @@
+
+
+
+
+
+
+
+
+ 配置 | VuePress 生态系统
+
+
+
+
+
+ 类型: { [path: string]: Partial<DefaultThemeLocaleData> }
默认值: {}
详情:
多语言支持的各个语言 locales 。
所有在 Locale 配置 章节内的配置项都可以在 locales 中使用。
该配置项仅能在默认主题内生效,注意不要和 站点配置在新窗口打开 中的 locales
混淆。
参考:
该章节内的配置项可以作为一般配置使用,也可以使用在 locales 内。
类型: string
默认值: /
详情:
首页的路径。
它将被用于:
导航栏中 Logo 的链接 404 页面的 返回首页 链接 export default {
+ theme: defaultTheme ({
+ navbar: [
+ // NavbarItem
+ {
+ text: 'Foo' ,
+ link: '/foo/' ,
+ },
+ // NavbarGroup
+ {
+ text: 'Group' ,
+ children: [ '/group/foo.md' , '/group/bar.md' ],
+ },
+ // 字符串 - 页面文件路径
+ '/bar/README.md' ,
+ ],
+ }),
+}
+
export default {
+ theme: defaultTheme ({
+ navbar: [
+ // 嵌套 Group - 最大深度为 2
+ {
+ text: 'Group' ,
+ children: [
+ {
+ text: 'SubGroup' ,
+ children: [ '/group/sub/foo.md' , '/group/sub/bar.md' ],
+ },
+ ],
+ },
+ // 控制元素何时被激活
+ {
+ text: 'Group 2' ,
+ children: [
+ {
+ text: 'Always active' ,
+ link: '/' ,
+ // 该元素将一直处于激活状态
+ activeMatch: '/' ,
+ },
+ {
+ text: 'Active on /foo/' ,
+ link: '/not-foo/' ,
+ // 该元素在当前路由路径是 /foo/ 开头时激活
+ // 支持正则表达式
+ activeMatch: '^/foo/' ,
+ },
+ ],
+ },
+ ],
+ }),
+}
+
类型: null | string
详情:
Logo 图片的 URL。
Logo 图片将会显示在导航栏的左端。
设置为 null
可以禁用 Logo 。
示例:
export default {
+ theme: defaultTheme ({
+ // Public 文件路径
+ logo: '/images/hero.png' ,
+ // URL
+ logo: 'https://vuejs.org/images/logo.png' ,
+ }),
+}
+
类型:null | string
详情:
指定 Logo 图片的替代文字。
当未指定时,将默认与站点标题相同。
export default {
+ theme: defaultTheme ({
+ // 如果你按照 `organization/repository` 的格式设置它
+ // 我们会将它作为一个 GitHub 仓库
+ repo: 'vuejs/vuepress' ,
+ // 你也可以直接将它设置为一个 URL
+ repo: 'https://gitlab.com/foo/bar' ,
+ }),
+}
+
类型: false | 'auto' | SidebarConfigArray | SidebarConfigObject
默认值: 'auto'
详情:
侧边栏配置。
你可以通过页面的 sidebar frontmatter 来覆盖这个全局配置。
设置为 false
可以禁用侧边栏。
如果你设置为 'auto'
,侧边栏会根据页面标题自动生成。
为了手动配置侧边栏元素,你可以将其设置为 侧边栏数组 ,其中的每个元素是一个 SidebarItem
对象或者一个字符串:
SidebarItem
对象应该有一个 text
字段,有一个可选的 link
字段、一个可选的 children
字段和一个可选的 collapsible
字段。 children
字段同样是一个 侧边栏数组 。 collapsible
字段来控制它是否可折叠。字符串应为目标页面文件的路径。它将会被转换为 SidebarItem
对象,将页面标题作为 text
,将页面路由路径作为 link
,并根据页面小标题自动生成 children
。 如果你想在不同子路径中使用不同的侧边栏,你可以将该配置项设置为 侧边栏对象 :
Key 为路径前缀。 Value 为 侧边栏数组 或 "heading"
以自动为相应路径生成基于标题的侧边栏。 示例 1:
export default {
+ theme: defaultTheme ({
+ // 侧边栏数组
+ // 所有页面会使用相同的侧边栏
+ sidebar: [
+ // SidebarItem
+ {
+ text: 'Foo' ,
+ link: '/foo/' ,
+ children: [
+ // SidebarItem
+ {
+ text: 'github' ,
+ link: 'https://github.com' ,
+ children: [],
+ },
+ // 字符串 - 页面文件路径
+ '/foo/bar.md' ,
+ ],
+ },
+ // 字符串 - 页面文件路径
+ '/bar/README.md' ,
+ ],
+ }),
+}
+
export default {
+ theme: defaultTheme ({
+ // 侧边栏对象
+ // 不同子路径下的页面会使用不同的侧边栏
+ sidebar: {
+ '/guide/' : [
+ {
+ text: 'Guide' ,
+ children: [ '/guide/introduction.md' , '/guide/getting-started.md' ],
+ },
+ ],
+ '/reference/' : 'heading' ,
+ },
+ }),
+}
+
export default {
+ theme: defaultTheme ({
+ // 可折叠的侧边栏
+ sidebar: {
+ '/reference/' : [
+ {
+ text: 'VuePress Reference' ,
+ collapsible: true ,
+ children: [ '/reference/cli.md' , '/reference/config.md' ],
+ },
+ {
+ text: 'Bundlers Reference' ,
+ collapsible: true ,
+ children: [
+ '/reference/bundler/vite.md' ,
+ '/reference/bundler/webpack.md' ,
+ ],
+ },
+ ],
+ },
+ }),
+}
+
类型: number
默认值: 2
详情:
设置根据页面标题自动生成的侧边栏的最大深度。
设为 0
来禁用所有级别的页面标题。 设为 1
来包含 <h2>
标题。 设为 2
来包含 <h2>
和 <h3>
标题。 ... 最大值取决于你通过 markdown.headers.level在新窗口打开 提取了哪些级别的标题。
由于 markdown.headers.level
的默认值是 [2, 3]
,因此 sidebarDepth
的默认最大值是 2
。
你可以通过页面的 sidebarDepth frontmatter 来覆盖这个全局配置。
export default {
+ theme: defaultTheme ({
+ docsRepo: 'https://gitlab.com/owner/name' ,
+ docsBranch: 'master' ,
+ docsDir: 'docs' ,
+ editLinkPattern: ':repo/-/edit/:branch/:path' ,
+ }),
+}
+
则会生成类似于 'https://gitlab.com/owner/name/-/edit/master/docs/path/to/file.md'
的链接。
类型: string
默认值: 'main'
详情:
文档源文件的仓库分支。
它将会用于生成 编辑此页 的链接。
类型: string
默认值: ''
详情:
文档源文件存放在仓库中的目录名。
它将会用于生成 编辑此页 的链接。
插件配置
+
+
+
diff --git a/zh/themes/default/extending.html b/zh/themes/default/extending.html
new file mode 100644
index 0000000000..2e42e0cd17
--- /dev/null
+++ b/zh/themes/default/extending.html
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+ 继承 | VuePress 生态系统
+
+
+
+
+
+ VuePress 默认主题有着大量的用户,因此我们对它进行了一些便于继承的设计,以便用户轻松进行定制化。
默认主题的 Layout
布局提供了一些插槽:
navbar
navbar-before
navbar-after
sidebar
sidebar-top
sidebar-bottom
page
page-top
page-bottom
page-content-top
page-content-bottom
在它们的帮助下,你可以很容易地添加或替换内容。下面通过一个示例来介绍一下如何使用布局插槽来继承默认主题。
首先,创建一个客户端配置文件 .vuepress/client.ts
:
import { defineClientConfig } from 'vuepress/client'
+import Layout from './layouts/Layout.vue'
+
+export default defineClientConfig ({
+ layouts: {
+ Layout ,
+ },
+})
+
接下来,创建 .vuepress/layouts/Layout.vue
,并使用由默认主题的 Layout
布局提供的插槽:
< script setup >
+import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
+</ script >
+
+< template >
+ < ParentLayout >
+ < template # page-bottom >
+ < div class = "my-footer" > This is my custom page footer </ div >
+ </ template >
+ </ ParentLayout >
+</ template >
+
+< style lang = "css" >
+.my-footer {
+ text-align : center ;
+}
+</ style >
+
此时默认的 Layout
布局已经被你的本地布局覆盖,将会在除了首页外的所有页面添加一个自定义的页脚:
布局插槽十分实用,但有时候你可能会觉得它不够灵活。默认主题同样提供了替换单个组件的能力。
默认主题将所有 非全局的组件在新窗口打开 都注册了一个带 @theme
前缀的 alias在新窗口打开 。例如,HomeFooter.vue
的别名是 @theme/HomeFooter.vue
。
接下来,如果你想要替换 HomeFooter.vue
组件,只需要在配置文件 .vuepress/config.ts
中覆盖这个别名即可:
import { defaultTheme } from '@vuepress/theme-default'
+import { getDirname , path } from 'vuepress/utils'
+import { defineUserConfig } from 'vuepress'
+
+const __dirname = getDirname ( import . meta . url )
+
+export default defineUserConfig ({
+ theme: defaultTheme (),
+ alias: {
+ '@theme/HomeFooter.vue' : path . resolve (
+ __dirname ,
+ './components/MyHomeFooter.vue' ,
+ ),
+ },
+})
+
除了在 .vuepress/config.ts
和 .vuepress/client.ts
中直接扩展默认主题以外,你可以通过继承默认主题来开发一个你自己的主题:
import { defaultTheme , type DefaultThemeOptions } from '@vuepress/theme-default'
+import type { Theme } from 'vuepress/core'
+import { getDirname , path } from 'vuepress/utils'
+
+const __dirname = getDirname ( import . meta . url )
+
+export const childTheme = ( options : DefaultThemeOptions ): Theme => {
+ return {
+ name: 'vuepress-theme-child' ,
+ extends: defaultTheme ( options ),
+
+ // 在子主题的客户端配置文件中覆盖布局
+ // 注意,你在发布到 NPM 之前会将 TS 构建为 JS ,因此这里需要设置为 JS 文件的路径
+ clientConfigFile: path . resolve ( __dirname , './client.js' ),
+
+ // 覆盖组件别名
+ alias: {
+ '@theme/HomeFooter.vue' : path . resolve (
+ __dirname ,
+ './components/MyHomeFooter.vue' ,
+ ),
+ },
+ }
+}
+
样式
+
+
+
diff --git a/zh/themes/default/frontmatter.html b/zh/themes/default/frontmatter.html
new file mode 100644
index 0000000000..d438917eb2
--- /dev/null
+++ b/zh/themes/default/frontmatter.html
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+ Frontmatter | VuePress 生态系统
+
+
+
+
+
+ 本章节中的 Frontmatter 会在所有类型的页面中生效。
类型: string
详情:
为当前页面添加额外的类名。
示例:
---
+pageClass : custom-page-class
+---
+
然后你可以在 .vuepress/styles/index.scss
文件中为这个页面添加自定义样式:
.theme-container.custom-page-class {
+ /* 页面样式 */
+}
+
本章节中的 Frontmatter 只会在首页中生效。
类型: string
详情:
首页图片的 URL 。
示例:
---
+# Public 文件路径
+heroImage : /images/hero.png
+# URL
+heroImage : https://vuejs.org/images/logo.png
+---
+
参考:
类型: string
详情:
首页图片的 alt
属性。
如果不设置,则默认使用 heroText 。
类型: number
默认值: 280
详情:
首页图片 <img>
标签的 height
属性。
当你的首页图片高度小于默认值时,你可能需要减小该属性。
需要注意的是,首页图片的高度同样受到了 CSS 的约束。设置这个属性主要是为了减少由加载首页图片引起的 累积布局偏移 (CLS)在新窗口打开 。
Array <{
+ text : string
+ link : string
+ type ?: 'primary' | 'secondary'
+}>
+
---
+actions :
+ - text : 快速上手
+ link : /zh/guide/getting-started.html
+ type : primary
+ - text : 项目简介
+ link : /zh/guide/introduction.html
+ type : secondary
+---
+
Array <{
+ title : string
+ details : string
+}>
+
---
+features :
+ - title : 简洁至上
+ details : 以 Markdown 为中心的项目结构,以最少的配置帮助你专注于写作。
+ - title : Vue 驱动
+ details : 享受 Vue 的开发体验,可以在 Markdown 中使用 Vue 组件,又可以使用 Vue 来开发自定义主题。
+ - title : 高性能
+ details : VuePress 会为每个页面预渲染生成静态的 HTML,同时,每个页面被加载的时候,将作为 SPA 运行。
+---
+
本章节中的 Frontmatter 只会在普通页面中生效。
类型: boolean
详情:
是否在本页面中启用 编辑此页 链接。
参考:
类型: string
详情:
本页面中 编辑此页 链接的 Pattern 。
参考:
类型: boolean
详情:
是否在本页面中启用 最近更新时间戳 。
参考:
类型: boolean
详情:
是否在本页面中启用 贡献者列表 。
参考:
类型: number
详情:
配置本页面的侧边栏深度。
参考:
---
+# NavLink
+prev :
+ text : Get Started
+ link : /guide/getting-started.html
+
+# NavLink - 外部 URL
+prev :
+ text : GitHub
+ link : https://github.com
+
+# 字符串 - 页面文件路径
+prev : /guide/getting-started.md
+
+# 字符串 - 页面文件相对路径
+prev : ../../guide/getting-started.md
+---
+
语言配置 内置组件
+
+
+
diff --git a/zh/themes/default/index.html b/zh/themes/default/index.html
new file mode 100644
index 0000000000..78d06fdf12
--- /dev/null
+++ b/zh/themes/default/index.html
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+ 默认主题 | VuePress 生态系统
+
+
+
+
+
+ 安装默认主题:
npm i -D @vuepress/theme-default@next
+
在配置文件中指定主题:
import { defaultTheme } from '@vuepress/theme-default'
+
+export default {
+ theme: defaultTheme ({
+ // 在这里添加主题配置
+ }),
+}
+
+
+
+
diff --git a/zh/themes/default/locale.html b/zh/themes/default/locale.html
new file mode 100644
index 0000000000..5fcdd78d97
--- /dev/null
+++ b/zh/themes/default/locale.html
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+ 语言配置 | VuePress 生态系统
+
+
+
+
+
+ 这些选项用于配置与语言相关的文本。
如果你的站点是以英语以外的其他语言提供服务的,你应该为每个语言设置这些选项来提供翻译。
类型: string
详情:
选择语言菜单 的 aria-label
属性。
它主要是为了站点的可访问性 (a11y) 。
export default {
+ locales: {
+ '/' : {
+ lang: 'en-US' ,
+ },
+ '/zh/' : {
+ lang: 'zh-CN' ,
+ },
+ },
+ theme: defaultTheme ({
+ locales: {
+ '/' : {
+ selectLanguageName: 'English' ,
+ },
+ '/zh/' : {
+ selectLanguageName: '简体中文' ,
+ },
+ },
+ }),
+}
+
类型:null | string
详情:
导航栏中主导航 aria-label
属性的值。
类型: string
默认值: 'Edit this page'
详情:
编辑此页 链接的文字。
类型: string
默认值: 'Last Updated'
详情:
最近更新时间戳 标签的文字。
类型: string
默认值: 'Contributors'
详情:
贡献者列表 标签的文字。
类型: string
默认值: 'TIP'
详情:
Tip 自定义容器 的默认标题。
类型: string
默认值: 'WARNING'
详情:
Warning 自定义容器 的默认标题。
类型: string
默认值: 'DANGER'
详情:
Danger 自定义容器 的默认标题。
类型: string
默认值: 'Back to home'
详情:
404 页面中 返回首页 链接的文字。
类型: string
默认值: 'toggle color mode'
详情:
切换颜色模式按钮的标题文字。
它主要是为了站点的可访问性 (a11y) 。
参考:
类型: string
默认值: 'toggle sidebar'
详情:
切换侧边栏按钮的标题文字。
它主要是为了站点的可访问性 (a11y) 。
插件配置 Frontmatter
+
+
+
diff --git a/zh/themes/default/markdown.html b/zh/themes/default/markdown.html
new file mode 100644
index 0000000000..5047c90c17
--- /dev/null
+++ b/zh/themes/default/markdown.html
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+ Markdown | VuePress 生态系统
+
+
+
+
+
+ 输入
::: tip
+这是一个提示
+:::
+
+::: warning
+这是一个警告
+:::
+
+::: danger
+这是一个危险警告
+:::
+
+::: details
+这是一个 details 标签
+:::
+
输出
这是一个 details 标签
输入
::: danger STOP
+危险区域,禁止通行
+:::
+
+::: details 点击查看代码
+
+```ts
+console . log ( '你好,VuePress!' )
+```
+
+:::
+
输出
点击查看代码 console . log ( '你好,VuePress!' )
+
输入
:::: code-group
+::: code-group-item FOO
+
+```ts
+const foo = 'foo'
+```
+
+:::
+
+::: code-group-item BAR
+
+```ts
+const bar = 'bar'
+```
+
+:::
+::::
+
输出
内置组件 样式
+
+
+
diff --git a/zh/themes/default/plugin.html b/zh/themes/default/plugin.html
new file mode 100644
index 0000000000..ea16e7d26e
--- /dev/null
+++ b/zh/themes/default/plugin.html
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+ 插件配置 | VuePress 生态系统
+
+
+
+
+
+ 你可以通过 themePlugins
设置默认主题使用的插件。
默认主题使用了一些插件,如果你确实不需要该插件,你可以选择禁用它。在禁用插件之前,请确保你已了解它的用途。
import { defaultTheme } from '@vuepress/theme-default'
+
+export default {
+ theme: defaultTheme ({
+ themePlugins: {
+ // 在这里自定义主题插件
+ },
+ }),
+}
+
配置 语言配置
+
+
+
diff --git a/zh/themes/default/styles.html b/zh/themes/default/styles.html
new file mode 100644
index 0000000000..4782e0b903
--- /dev/null
+++ b/zh/themes/default/styles.html
@@ -0,0 +1,299 @@
+
+
+
+
+
+
+
+
+ 样式 | VuePress 生态系统
+
+
+
+
+
+ 默认主题使用 SASS在新窗口打开 作为 CSS 预处理器。
用户可以通过 palette 文件 来自定义样式变量,还可以通过 style 文件 来添加额外的样式。
Palette 文件的路径是 .vuepress/styles/palette.scss
。
你可以利用它来覆盖默认主题的预定义 SASS 变量。
点击查看 SASS 变量 // responsive breakpoints
+$MQNarrow : 959px !default ;
+$MQMobile : 719px !default ;
+$MQMobileNarrow : 419px !default ;
+
Style 文件的路径是 .vuepress/styles/index.scss
。
你可以在这里添加额外的样式,或者覆盖默认样式:
:root {
+ scroll-behavior : smooth ;
+}
+
你也可以利用它来覆盖默认主题的预定义 CSS 变量。
点击查看 CSS 变量 :root {
+ // brand colors
+ --c-brand : #3eaf7c ;
+ --c-brand-light : #4abf8a ;
+
+ // background colors
+ --c-bg : #ffffff ;
+ --c-bg-light : #f3f4f5 ;
+ --c-bg-lighter : #eeeeee ;
+ --c-bg-dark : #ebebec ;
+ --c-bg-darker : #e6e6e6 ;
+ --c-bg-navbar : var ( --c-bg );
+ --c-bg-sidebar : var ( --c-bg );
+ --c-bg-arrow : #cccccc ;
+
+ // text colors
+ --c-text : #2c3e50 ;
+ --c-text-accent : var ( --c-brand );
+ --c-text-light : #3a5169 ;
+ --c-text-lighter : #4e6e8e ;
+ --c-text-lightest : #6a8bad ;
+ --c-text-quote : #999999 ;
+
+ // border colors
+ --c-border : #eaecef ;
+ --c-border-dark : #dfe2e5 ;
+
+ // custom container colors
+ --c-tip : #42b983 ;
+ --c-tip-bg : var ( --c-bg-light );
+ --c-tip-title : var ( --c-text );
+ --c-tip-text : var ( --c-text );
+ --c-tip-text-accent : var ( --c-text-accent );
+ --c-warning : #ffc310 ;
+ --c-warning-bg : #fffae3 ;
+ --c-warning-bg-light : #fff3ba ;
+ --c-warning-bg-lighter : #fff0b0 ;
+ --c-warning-border-dark : #f7dc91 ;
+ --c-warning-details-bg : #fff5ca ;
+ --c-warning-title : #f1b300 ;
+ --c-warning-text : #746000 ;
+ --c-warning-text-accent : #edb100 ;
+ --c-warning-text-light : #c1971c ;
+ --c-warning-text-quote : #ccab49 ;
+ --c-danger : #f11e37 ;
+ --c-danger-bg : #ffe0e0 ;
+ --c-danger-bg-light : #ffcfde ;
+ --c-danger-bg-lighter : #ffc9c9 ;
+ --c-danger-border-dark : #f1abab ;
+ --c-danger-details-bg : #ffd4d4 ;
+ --c-danger-title : #ed1e2c ;
+ --c-danger-text : #660000 ;
+ --c-danger-text-accent : #bd1a1a ;
+ --c-danger-text-light : #b5474d ;
+ --c-danger-text-quote : #c15b5b ;
+ --c-details-bg : #eeeeee ;
+
+ // badge component colors
+ --c-badge-tip : var ( --c-tip );
+ --c-badge-warning : #ecc808 ;
+ --c-badge-warning-text : var ( --c-bg );
+ --c-badge-danger : #dc2626 ;
+ --c-badge-danger-text : var ( --c-bg );
+
+ // code group colors
+ --c-code-group-tab-title : rgba ( 255 , 255 , 255 , 0.9 );
+ --c-code-group-tab-bg : var ( --code-bg-color );
+ --c-code-group-tab-outline : var ( var ( --c-code-group-tab-title ));
+ --c-code-group-tab-active-border : var ( --c-brand );
+
+ // transition vars
+ --t-color : 0.3s ease ;
+ --t-transform : 0.3s ease ;
+
+ // code blocks vars
+ --code-bg-color : #282c34 ;
+ --code-hl-bg-color : rgba ( 0 , 0 , 0 , 0.66 );
+ --code-ln-color : #9e9e9e ;
+ --code-ln-wrapper-width : 3.5rem ;
+
+ // font vars
+ --font-family : -apple-system, BlinkMacSystemFont, 'Segoe UI' , Roboto, Oxygen,
+ Ubuntu, Cantarell, 'Fira Sans' , 'Droid Sans' , 'Helvetica Neue' , sans-serif ;
+ --font-family-code : Consolas, Monaco, 'Andale Mono' , 'Ubuntu Mono' , monospace ;
+
+ // layout vars
+ --navbar-height : 3.6rem ;
+ --navbar-padding-v : 0.7rem ;
+ --navbar-padding-h : 1.5rem ;
+ --sidebar-width : 20rem ;
+ --sidebar-width-mobile : calc ( var ( --sidebar-width ) * 0.82 );
+ --content-width : 740px ;
+ --homepage-width : 960px ;
+}
+
+// plugin-back-to-top
+.vp-back-to-top-button {
+ --back-to-top-color : var ( --c-brand );
+ --back-to-top-color-hover : var ( --c-brand-light );
+ --back-to-top-bg-color : var ( --c-bg );
+}
+
+// plugin-catalog
+.vp-catalog-wrapper {
+ --catalog-bg-color : var ( --c-bg );
+ --catalog-bg-secondary-color : var ( --c-bg-dark );
+ --catalog-border-color : var ( --c-border );
+ --catalog-active-color : var ( --c-brand );
+ --catalog-hover-color : var ( --c-brand-light );
+}
+
+// plugin-docsearch
+.DocSearch {
+ --docsearch-primary-color : var ( --c-brand );
+ --docsearch-text-color : var ( --c-text );
+ --docsearch-highlight-color : var ( --c-brand );
+ --docsearch-muted-color : var ( --c-text-quote );
+ --docsearch-container-background : rgba ( 9 , 10 , 17 , 0.8 );
+ --docsearch-modal-background : var ( --c-bg-light );
+ --docsearch-searchbox-background : var ( --c-bg-lighter );
+ --docsearch-searchbox-focus-background : var ( --c-bg );
+ --docsearch-searchbox-shadow : inset 0 0 0 2px var ( --c-brand );
+ --docsearch-hit-color : var ( --c-text-light );
+ --docsearch-hit-active-color : var ( --c-bg );
+ --docsearch-hit-background : var ( --c-bg );
+ --docsearch-hit-shadow : 0 1px 3px 0 var ( --c-border-dark );
+ --docsearch-footer-background : var ( --c-bg );
+}
+
+// plugin-external-link-icon
+.external-link-icon {
+ --external-link-icon-color : var ( --c-text-quote );
+}
+
+// plugin-medium-zoom
+.medium-zoom-overlay {
+ --medium-zoom-bg-color : var ( --c-bg );
+}
+
+// plugin-nprogress
+#nprogress {
+ --nprogress-color : var ( --c-brand );
+}
+
+// plugin-photo-swipe
+body {
+ --photo-swipe-bullet : var ( --c-bg );
+ --photo-swipe-bullet-active : var ( --c-brand );
+}
+
+// plugin-pwa-popup
+html {
+ --pwa-text-color : var ( --c-text );
+ --pwa-bg-color : var ( --c-bg );
+ --pwa-border-color : var ( --c-brand );
+ --pwa-btn-text-color : var ( --c-bg );
+ --pwa-btn-bg-color : var ( --c-brand );
+ --pwa-btn-hover-bg-color : var ( --c-brand-light );
+}
+
+html.dark {
+ --pwa-shadow-color : rgb ( 0 0 0 / 30% );
+ --pwa-content-color : #ccc ;
+ --pwa-content-light-color : #999 ;
+}
+
+// plugin-redirect
+.language-modal-mask {
+ --redirect-bg-color : var ( --c-bg );
+ --redirect-bg-color-light : var ( --c-bg-light );
+ --redirect-bg-color-lighter : var ( --c-bg-lighter );
+ --redirect-text-color : var ( --c-text );
+ --redirect-primary-bg-color : var ( --c-brand );
+ --redirect-primary-hover-bg-color : var ( --c-brand-light );
+ --redirect-primary-text-color : var ( --c-bg );
+}
+
+// plugin-search
+.search-box {
+ --search-bg-color : var ( --c-bg );
+ --search-accent-color : var ( --c-brand );
+ --search-text-color : var ( --c-text );
+ --search-border-color : var ( --c-border );
+
+ --search-item-text-color : var ( --c-text-lighter );
+ --search-item-focus-bg-color : var ( --c-bg-light );
+}
+
点击查看暗黑模式 CSS 变量 html.dark {
+ // brand colors
+ --c-brand : #3aa675 ;
+ --c-brand-light : #349469 ;
+
+ // background colors
+ --c-bg : #22272e ;
+ --c-bg-light : #2b313a ;
+ --c-bg-lighter : #262c34 ;
+ --c-bg-dark : #343b44 ;
+ --c-bg-darker : #37404c ;
+
+ // text colors
+ --c-text : #adbac7 ;
+ --c-text-light : #96a7b7 ;
+ --c-text-lighter : #8b9eb0 ;
+ --c-text-lightest : #8094a8 ;
+
+ // border colors
+ --c-border : #3e4c5a ;
+ --c-border-dark : #34404c ;
+
+ // custom container colors
+ --c-tip : #318a62 ;
+ --c-warning : #e0ad15 ;
+ --c-warning-bg : #2d2f2d ;
+ --c-warning-bg-light : #423e2a ;
+ --c-warning-bg-lighter : #44442f ;
+ --c-warning-border-dark : #957c35 ;
+ --c-warning-details-bg : #39392d ;
+ --c-warning-title : #fdca31 ;
+ --c-warning-text : #d8d96d ;
+ --c-warning-text-accent : #ffbf00 ;
+ --c-warning-text-light : #ddb84b ;
+ --c-warning-text-quote : #ccab49 ;
+ --c-danger : #fc1e38 ;
+ --c-danger-bg : #39232c ;
+ --c-danger-bg-light : #4b2b35 ;
+ --c-danger-bg-lighter : #553040 ;
+ --c-danger-border-dark : #a25151 ;
+ --c-danger-details-bg : #482936 ;
+ --c-danger-title : #fc2d3b ;
+ --c-danger-text : #ea9ca0 ;
+ --c-danger-text-accent : #fd3636 ;
+ --c-danger-text-light : #d9777c ;
+ --c-danger-text-quote : #d56b6b ;
+ --c-details-bg : #323843 ;
+
+ // badge component colors
+ --c-badge-warning : var ( --c-warning );
+ --c-badge-warning-text : #3c2e05 ;
+ --c-badge-danger : var ( --c-danger );
+ --c-badge-danger-text : #401416 ;
+
+ // code blocks vars
+ --code-hl-bg-color : #363b46 ;
+}
+
+// plugin-docsearch
+html.dark .DocSearch {
+ --docsearch-logo-color : var ( --c-text );
+ --docsearch-modal-shadow : inset 1px 1px 0 0 #2c2e40 , 0 3px 8px 0 #000309 ;
+ --docsearch-key-shadow : inset 0 -2px 0 0 #282d55 , inset 0 0 1px 1px #51577d ,
+ 0 2px 2px 0 rgba ( 3 , 4 , 9 , 0.3 );
+ --docsearch-key-gradient : linear-gradient ( -225deg , #444950 , #1c1e21 );
+ --docsearch-footer-shadow : inset 0 1px 0 0 rgba ( 73 , 76 , 106 , 0.5 ),
+ 0 -4px 8px 0 rgba ( 0 , 0 , 0 , 0.2 );
+}
+
Markdown 继承
+
+
+
diff --git a/zh/themes/index.html b/zh/themes/index.html
new file mode 100644
index 0000000000..89d0eaf34f
--- /dev/null
+++ b/zh/themes/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ 主题 | VuePress 生态系统
+
+
+
+
+
+
+
+
+
diff --git a/zh/tools/helper/client.html b/zh/tools/helper/client.html
new file mode 100644
index 0000000000..26809c7c88
--- /dev/null
+++ b/zh/tools/helper/client.html
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+ 客户端相关 | VuePress 生态系统
+
+
+
+
+
+ 检查组件是否已全局注册。
提示
组件的局部导入不影响结果。 当在 setup
之外调用时,你需要将 app
实例作为第二个参数传递。 export const hasGlobalComponent : ( name : string , app ?: App ) => boolean
+
示例 // 如果你全局注册了 `<my-component>`
+hasGlobalComponent ( 'MyComponent' ) // true
+hasGlobalComponent ( 'my-component' ) // true
+
+hasGlobalComponent ( 'MyComponent2' ) // false
+
从语言环境设置中获取当前语言环境配置。
export const useLocaleConfig : < T extends LocaleData >(
+ localesConfig : RequiredLocaleConfig < T >,
+) => ComputedRef < T >
+
示例 const localesCOnfig = {
+ '/' : 'Title' ,
+ '/zh/' : '标题' ,
+}
+
+const locale = useLocaleConfig ( localesConfig )
+
+// under `/page`
+locale . value // 'Title'
+
+// under `/zh/page`
+locale . value // '标题'
+
页面相关 共享方法
+
+
+
diff --git a/zh/tools/helper/index.html b/zh/tools/helper/index.html
new file mode 100644
index 0000000000..b946a58274
--- /dev/null
+++ b/zh/tools/helper/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ @vuepress/helper | VuePress 生态系统
+
+
+
+
+
+ 此包为 VuePress 开发者提供辅助函数。
+
+
+
diff --git a/zh/tools/helper/node/bundler.html b/zh/tools/helper/node/bundler.html
new file mode 100644
index 0000000000..5361191f02
--- /dev/null
+++ b/zh/tools/helper/node/bundler.html
@@ -0,0 +1,215 @@
+
+
+
+
+
+
+
+
+ 打包器相关 | VuePress 生态系统
+
+
+
+
+
+ 打包器函数用于在主题和插件中追加或修改打包器选项。
所有函数都应在 extendsBundlerOptions
生命周期挂钩中调用。
提示
我们在示例中省略了它。 实际代码应该是这样的:
// 导入你需要的函数
+import { addCustomElement } from '@vuepress/helper'
+
+export const yourPlugin = {
+ // ...
+ extendsBundlerOptions : ( bundlerOptions , app ) => {
+ // 在此添加它们
+ addCustomElement ( bundlerOptions , app , 'my-custom-element' )
+ },
+}
+
获取当前打包器的名称。
export const getBundlerName : ( app : App ) => string
+
示例 // @vuepress/bundler-vite
+getBundleName ( app ) === 'vite' // true
+// @vuepress/bundler-webpack
+getBundleName ( app ) === 'webpack' // true
+
将自定义元素声明添加到当前的打包器。
interface CustomElementCommonOptions {
+ app : App ;
+ config : unknown ;
+}
+/**
+ * Add tags as customElement
+ *
+ * @param bundlerOptions VuePress Bundler config
+ * @param app VuePress Node App
+ * @param customElements tags recognized as custom element
+ */
+export const addCustomElement = (
+ bundlerOptions : unknown ,
+ app : App ,
+ customElement : string [] | string | RegExp
+) => void ;
+
示例 import { addCustomElement } from '@vuepress/helper'
+
+addCustomElement ( bundlerConfig , app , 'my-custom-element' )
+addCustomElement ( bundlerOptions , app , [
+ 'custom-element1' ,
+ 'custom-element2' ,
+ // all tags start with `math-`
+ / ^ math-/ ,
+])
+
为开发服务器中的特定路径提供内容。
export interface DevServerOptions {
+ /**
+ * Path to be responded
+ */
+ path : string ;
+ /**
+ * Respond function
+ */
+ response : ( request ?: IncomingMessage ) => Promise < string | Buffer >;
+
+ /**
+ * error msg
+ */
+ errMsg ?: string ;
+}
+
+/**
+ * Handle specific path when running VuePress Dev Server
+ *
+ * @param bundlerOptions VuePress Bundler config
+ * @param app VuePress Node App
+ * @param path Path to be responded
+ * @param response respond function
+ * @param errMsg error msg
+ */
+export const customizeDevServer : (
+ bundlerOptions : unknown ,
+ app : App ,
+ {
+ errMsg : "The server encountered an error" ,
+ response : responseHandler ,
+ path ,
+ }: CustomServerOptions
+) => void ;
+
示例 import { useCustomDevServer } from '@vuepress/helper'
+
+// handle `/api/` path
+useCustomDevServer ( bundlerOptions , app , {
+ path: '/api/' ,
+ response : async () => getData (),
+ errMsg: 'Unexpected api error' ,
+})
+
addViteOptimizeDepsInclude
向 Vite optimizeDeps.include
列表中添加模块
提示
如果一个包满足下列条件之一,你应该考虑将它添加至此。
为 CJS 格式 包的依赖包含 CJS 包 包通过 import()
动态导入 addViteOptimizeDepsExclude
向 Vite optimizeDeps.exclude
列表中添加模块
如果一个包和它的依赖都是纯 ESM 包,你应该考虑将它添加至此。
addViteSsrExternal
向 Vite ssr.external
列表中添加模块
如果一个包是纯 ESM 包,且未使用别名 (alias) 或定义变量 (define),你应该考虑将它添加至此。
addViteSsrNoExternal
向 Vite ssr.noExternal
列表中添加模块
如果一个包内使用了别名 (alias) 或定义变量 (define),你必须将它添加至此。
/**
+ * Add modules to Vite `optimizeDeps.include` list
+ */
+export const addViteOptimizeDepsInclude : (
+ bundlerOptions : unknown ,
+ app : App ,
+ module : string | string [],
+) => void
+
+/**
+ * Add modules to Vite `optimizeDeps.exclude` list
+ */
+export const addViteOptimizeDepsExclude : (
+ bundlerOptions : unknown ,
+ app : App ,
+ module : string | string [],
+) => void
+
+/**
+ * Add modules to Vite `ssr.external` list
+ */
+export const addViteSsrExternal : (
+ bundlerOptions : unknown ,
+ app : App ,
+ module : string | string [],
+) => void
+
+/**
+ * Add modules to Vite `ssr.noExternal` list
+ */
+export const addViteSsrNoExternal : (
+ bundlerOptions : unknown ,
+ app : App ,
+ module : string | string [],
+) => void
+
示例 import {
+ addViteOptimizeDepsInclude ,
+ addViteOptimizeDepsExclude ,
+ addViteSsrExternal ,
+ addViteSsrNoExternal ,
+} from '@vuepress/helper'
+
+addViteOptimizeDepsInclude ( bundlerOptions , app , [ 'vue' , 'vue-router' ])
+addViteOptimizeDepsExclude ( bundlerOptions , app , 'packageA' )
+addViteSsrNoExternal ( bundlerOptions , app , [ 'vue' , 'vue-router' ])
+addViteSsrExternal ( bundlerOptions , app , 'packageA' )
+
addViteConfig
A function for you to add vite config
export const addViteConfig : (
+ bundlerOptions : unknown ,
+ app : App ,
+ config : Record < string , unknown >,
+) => void
+
Example import { addViteConfig } from '@vuepress/helper'
+
+addViteConfig ( bundlerOptions , app , {
+ build: {
+ charset: 'utf8' ,
+ },
+})
+
mergeViteConfig
无需导入 vite 即可合并 vite 配置的功能
export const mergeViteConfig : (
+ defaults : Record < string , any >,
+ overrides : Record < string , any >,
+) => Record < string , any >
+
注意
你不应将 vite 作为依赖,因为你的的用户可能选择其他打包器!
示例 import { mergeViteConfig } from "@vuepress/helper" ;
+
+config . viteOptions mergeViteConfig ( config . viteOptions , {
+ build: {
+ charset: "utf8" ,
+ },
+});
+
chainWebpack
链式修改 webpack 配置.
export const chainWebpack : (
+ { app , config }: WebpackCommonOptions ,
+ chainWebpack : (
+ config : WebpackChainConfig ,
+ isServer : boolean ,
+ isBuild : boolean ,
+ ) => void ,
+) => void
+
示例 import { chainWebpack } from '@vuepress/helper'
+
+chainWebpack ( bundlerOptions , app , ( config , isServer , isBuild ) => {
+ // do some customize here
+})
+
configWebpack
配置 Webpack
export const configWebpack : (
+ bundlerOptions : unknown ,
+ app : App ,
+ configureWebpack : (
+ config : WebpackConfiguration ,
+ isServer : boolean ,
+ isBuild : boolean ,
+ ) => void ,
+) => void
+
实例 import { configWebpack } from '@vuepress/helper'
+
+configWebpack ( bundlerOptions , app , ( config , isServer , isBuild ) => {
+ // do some customize here
+})
+
页面相关
+
+
+
diff --git a/zh/tools/helper/node/index.html b/zh/tools/helper/node/index.html
new file mode 100644
index 0000000000..4f2abd785a
--- /dev/null
+++ b/zh/tools/helper/node/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ Node | VuePress 生态系统
+
+
+
+
+
+
+
+
+
diff --git a/zh/tools/helper/node/page.html b/zh/tools/helper/node/page.html
new file mode 100644
index 0000000000..2051541b61
--- /dev/null
+++ b/zh/tools/helper/node/page.html
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+ 页面相关 | VuePress 生态系统
+
+
+
+
+
+ 这些函数为你的页面生成常见信息。
获取页面摘要。
export interface PageExcerptOptions {
+ /**
+ * 摘要分隔符
+ *
+ * @default " <!-- more --> "
+ */
+ separator ?: string
+
+ /**
+ * 摘要的长度
+ *
+ * @description 摘要的长度会尽可能的接近这个值
+ *
+ * @default 300
+ */
+ length ?: number
+
+ /**
+ * 被认为是自定义元素的标签
+ *
+ * @description 用于判断一个标签是否是自定义元素,因为在摘要中,所有的未知标签都会被移除。
+ */
+ isCustomElement ?: ( tagName : string ) => boolean
+
+ /**
+ * 是否保留页面标题 (第一个 h1)
+ *
+ * @default false
+ */
+ keepPageTitle ?: boolean
+
+ /**
+ * 是否保留代码块的标签,诸如行号和高亮行
+ *
+ * @default false
+ */
+ keepFenceDom ?: boolean
+}
+
+export const getPageExcerpt : (
+ app : App ,
+ page : Page ,
+ options ?: PageExcerptOptions ,
+) => string
+
获取页面纯文本。
export interface PageTextOptions {
+ /**
+ * 是否将文字转换成单行内容
+ *
+ * @default false
+ */
+ singleLine ?: boolean
+
+ /**
+ * 文字的长度
+ *
+ * @description 文字的长度会尽可能的接近这个值
+ *
+ * @default 300
+ */
+ length ?: number
+
+ /**
+ * 需要移除的标签
+ *
+ * @description 默认情况下表格和代码块会被移除
+ *
+ * @default ['table', 'pre']
+ */
+ removedTags ?: string []
+}
+
+export const getPageText : (
+ app : App ,
+ page : Page ,
+ options ?: PageTextOptions ,
+) => string
+
打包器相关 客户端相关
+
+
+
diff --git a/zh/tools/helper/shared.html b/zh/tools/helper/shared.html
new file mode 100644
index 0000000000..ee36f392b6
--- /dev/null
+++ b/zh/tools/helper/shared.html
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+
+ 共享方法 | VuePress 生态系统
+
+
+
+
+
+ 以下函数在 Node.js 和客户端上均可用。
此方法在 MarkdownIt 插件中很有用。有些时候你可能需要在 Markdown 插件中生成组件,并将复杂的数据写入到组件属性中,一个通常做法是使用 JSON.stringify
+ encodeURIComponent
,并在客户端 decodeURIComponent
+ JSON.parse
。但如果内容包含很多特殊字符,转换结果会很长。
所以我们提供 encodeData
和 decodeData
来压缩和编码内容。
export const encodeData : (
+ data : string ,
+ level : DeflateOptions [ 'level' ] = 6 ,
+) => string
+
+export const decodeData : ( compressed : string ) => string
+
const content = `
+{
+ "type": "bar",
+ "data": {
+ "labels": ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
+ "datasets": [
+ {
+ "label": "# of Votes",
+ "data": [12, 19, 3, 5, 2, 3],
+ "backgroundColor": [
+ "rgba(255, 99, 132, 0.2)",
+ "rgba(54, 162, 235, 0.2)",
+ "rgba(255, 206, 86, 0.2)",
+ "rgba(75, 192, 192, 0.2)",
+ "rgba(153, 102, 255, 0.2)",
+ "rgba(255, 159, 64, 0.2)"
+ ],
+ "borderColor": [
+ "rgba(255, 99, 132, 1)",
+ "rgba(54, 162, 235, 1)",
+ "rgba(255, 206, 86, 1)",
+ "rgba(75, 192, 192, 1)",
+ "rgba(153, 102, 255, 1)",
+ "rgba(255, 159, 64, 1)"
+ ],
+ "borderWidth": 1
+ }
+ ]
+ },
+ "options": {
+ "scales": {
+ "y": {
+ "beginAtZero": true
+ }
+ }
+ }
+}
+`
+
+const prop = encodeData ( content ) // "eJyNUsFOwzAMve8rrHABKZqWlg5WxAE4cARxAMHEIV1NmQhNlaaCCe3fcdKtW0sLWGpjxy/v+UV512mlcIyfhTa2hHP4GgHYVYExsEQaxqlMpZWxbwAomaAqY5izO0wZB3apKnTrIyqlP1x2bRBzl9xWplC+eWNkniF7dmw1X4nWsfgaNtwNP2kfgH6Be22x9CPUUQ8yFwEHMeMQcog4UBFuiF0kcvGWGV3l6ZVW2uw0XDCTJfIwiOjYjAhESIcn4+BoT2MLio6pP6V+EBJ6AOSZgsmUwyl9A6ATwoiZn3lYTkTkRkycnuP8TU9ENPqUxuuA9i9BmxTNPy9A/G2/F9I23wtpW++FdIwPKzW2W5Afph+WqX2NQWz313XicT7XhV3qnB5f/ejKhVTYVACrXUqUmC3zC/uERsdgTYUdVr/Qb302+gZxe7S/"
+
+decodeData ( prop ) // will be the original content
+
+// if you use `encodeURIComponent`, it will be much longer
+encodeURIComponent ( content ) // '%0A%7B%0A%20%20%22type%22%3A%20%22bar%22%2C%0A%20%20%22data%22%3A%20%7B%0A%20%20%20%20%22labels%22%3A%20%5B%22Red%22%2C%20%22Blue%22%2C%20%22Yellow%22%2C%20%22Green%22%2C%20%22Purple%22%2C%20%22Orange%22%5D%2C%0A%20%20%20%20%22datasets%22%3A%20%5B%0A%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22label%22%3A%20%22%23%20of%20Votes%22%2C%0A%20%20%20%20%20%20%20%20%22data%22%3A%20%5B12%2C%2019%2C%203%2C%205%2C%202%2C%203%5D%2C%0A%20%20%20%20%20%20%20%20%22backgroundColor%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%2099%2C%20132%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(54%2C%20162%2C%20235%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20206%2C%2086%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(75%2C%20192%2C%20192%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(153%2C%20102%2C%20255%2C%200.2)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20159%2C%2064%2C%200.2)%22%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22borderColor%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%2099%2C%20132%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(54%2C%20162%2C%20235%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20206%2C%2086%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(75%2C%20192%2C%20192%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(153%2C%20102%2C%20255%2C%201)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22rgba(255%2C%20159%2C%2064%2C%201)%22%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22borderWidth%22%3A%201%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%5D%0A%20%20%7D%2C%0A%20%20%22options%22%3A%20%7B%0A%20%20%20%20%22scales%22%3A%20%7B%0A%20%20%20%20%20%20%22y%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22beginAtZero%22%3A%20true%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A'
+
isDef(x)
: 判断 x 是否定义。isBoolean(x)
: 判断 x 是否为布尔值。isString(x)
: 判断 x 是否为字符串。isNumber(x)
: 判断 x 是否为数字。isPlainObject(x)
: 判断值是否为纯对象。isArray(x)
: 判断 x 是否为数组isFunction(x)
: 判断 x 是否为函数。isRegExp(x)
: 判断 x 是否为正则表达式startsWith(a, b)
: 判断字符串 a 是否以指定字符串 b 开头endsWith(a, b)
: 判断字符串 a 是否以指定字符串 b 结尾当 a 不是字符串时返回 false
keys(x)
: 以数组形式返回对象 x 的键
values(x)
: 以数组形式返回对象 x 的值
entries(x)
: 将对象 x 转换为键值对数组。
fromEntries(x)
: 将键值对数组 x 转换为对象。
deepAssign(x, y, ...)
: Object.assign
的深度版本。
示例 // or @vuepress/helper/client
+import { deepAssign } from '@vuepress/helper'
+
+const defaultOptions = {
+ optionA: {
+ optionA1: 'defaultOptionA1' ,
+ optionA2: 'defaultOptionA2' ,
+ optionA3: 'defaultOptionA3' ,
+ },
+ optionB: true ,
+ optionC: 'optionC' ,
+}
+
+const userOptions = {
+ optionA: {
+ optionA1: 'optionA1' ,
+ optionA2: 'optionA2' ,
+ },
+ optionB: false ,
+}
+
+deepAssign ( defaultOptions , userOptions )
+// {
+// optionA: {
+// optionA1: "optionA1",
+// optionA2: "optionA2",
+// optionA3: "defaultOptionA3",
+// },
+// optionB: false,
+// optionC: "optionC",
+// }
+
isLinkHttp(x)
: x 是否是有效的 HTTP URL。isLinkWithProtocol(x)
: x 是否是有效的带有协议的 URL。isLinkExternal(x)
: x 是否是有效的外部 URL。isLinkAbsolute(x)
: x 是否是有效的绝对 URL。ensureEndingSlash(x)
: 确保 x 以斜杠结尾。ensureLeadingSlash(x)
: 确保 x 以斜杠开头。removeEndingSlash(x)
: 确保 x 不以斜杠结尾。removeLeadingSlash(x)
: 确保 x 不以斜杠开头。 客户端相关
+
+
+
diff --git a/zh/tools/index.html b/zh/tools/index.html
new file mode 100644
index 0000000000..b37839f34d
--- /dev/null
+++ b/zh/tools/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ Tools | VuePress 生态系统
+
+
+
+
+
+
+
+
+