同步操作将从 deepinwiki/wiki 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
vuepress 是一个 markdown 转网站工具。
vue 是一个前端开发的框架,vuepress 是基于 vue 的将 markdown 文档转换为网站的框架。它的特点就是几乎不需要过多的配置,即可以将指定目录下的 md 文件,转换为对应的网站。
这对于使用 markdown,又想可以进行网页浏览的创作者来说是一个不错的工具。
# 2.0 测试版支持 vite
# vite 是一个开发时的网站服务器,让用户可以方便预览输出结果
# 你可以边写 md 文档,实时查看输出的网页
yarn add -D vuepress@next
package.json 样例:
{
"devDependencies": {
"vuepress": "^2.0.0-beta.64"
},
"scripts": {
"dev": "vuepress dev blogs",
"build": "vuepress build blogs"
}
}
# 上述配置定义了两个命令,其中 blogs 是存放 md 文档的目录
# 开启网站预览服务器
yarn dev
# 构建输出最终网站
yarn build
只要这样配置,既可以得到对应的网站。其中,.md 变为 .html 文件,readme.md 或者 index.md 会成为 index.html 即默认的首页。
但是并没有导航菜单。可以手动输入网址来查看最终效果。
vuepress node app -> { 临时模板文件 + vue app client } --(bundler node app)--> dev server 或者 ssr pre-render(网页预渲染)。
首先是 vuepress 通过 node 运行,输出临时文件(模板、数据、vue app),然后通过打包器,也就是 vite 启动预览服务器或输出最终网站。
每一个 md 文档都会转化为 vue sfc(单文件组件),可以在 md 文档中使用相关的标签。(当然这种侵入式的做法不太让我满意,我希望的是 md 是文档,至于怎么变成 html,这是另一种需求,应该独立配置)
名词解释:ssr 网页预渲染技术,是将很多 js 的结果作为输出,从而让网页尽可能的静态化,而无法静态化的部分,就留着作为网页中的 vue app js 代码执行。
这两个都是可以配置的。
参考:https://v2.vuepress.vuejs.org/zh/advanced/architecture.html
目录:
// config.js
// 用户配置
import {defineUserConfig } from 'vuepress'
export default defineUserConfig({
lang: 'zh-CN', // 设置页面的默认语言
title: '网站标题', // 作为页面标题的后缀: 网页标题|网站标题
description: '默认网页简介', // <meta name="description" />
base: '/', // 根目录,如网站位于 xxx 子目录下:/xxx/
// 在 head 标签内插入默认的元素 <link rel="icon" href="/logo.png" />
head: [['link', { rel:'icon', href:'/logo.png' }]],
// 本地化,可以设置 lang、title、description、head
locales: { '/en-US/':{lang: 'en-US'}},
})
// 主题配置
import {defaultTheme}from 'vuepress'
export default {
// 使用官方默认的主题
theme: defaultTheme({
// 导航栏
navbar: [{
text: '首页',
link: '/',
}],
}),
}
// 杂项
export default {
dest:`${sourceDir}/.vuepress/dist`, // 网站的输出目录
temp:`${sourceDir}/.vuepress/.temp`, // 临时文件目录
cache:`${sourceDir}/.vuepress/.cache`, // 缓存目录
public:`${sourceDir}/.vuepress/public`, // 资源文件目录(公共的图片等)
debug:false, // 是否启动调试模式
permalinkPattern: ':year/:slug.html'// 永久链接模板,例:2023/xxx.html
}
// 插件配置
import myPlugin from './my-plugin.js'
export default {
plugins: [ myPlugin() ],
}
// 配置打包工具
import {viteBundler} from 'vuepress'
export default {
bundler:viteBundler({}),
}
注意: js 要求每个文件只能有一个 export default,所以实际要配置的内容是要合并在一起的。
// client.js
// 客户端配置
import {defineClientConfig} from '@vuepress/client'
import MyComponent from '第三方组件.vue'
export default defineClientConfig({
// app: vue app
// router: vue router,一个虚拟网页链接的工具(所谓路由)
// siteData: base,lang,title,description,head,locales 配置对象
enhance({app, router, siteData}){
app.component('第三方组件’, MyComponent)
},
// vue 客户端 app 会调用 steup 组合式 api
setup(){},
// 全局组件列表
rootComponents:[],
// 布局
layouts:{},
})
这个工具默认就能产生一个真实的网站,这点让我很欣慰,因为很多同类产品,都要学习一大堆配置内容,才能最终出网站。
但是默认的网站有个最大的缺陷,那就是没有导航,不知道为啥没有提供这个。当然有导航栏插件,但是我们希望可以动态的生成一个目录,可以导航到所有文章的页面(这种功能也叫网站地图)。
而现实是只有侧边栏生成了当前页面的目录,而不是我们希望的整个网站的目录。
另外一个就是缺少搜索功能。搜索还是挺有用的,当然一个比较取巧的方案是调用百度或者谷歌,使用关键字 "site:我们的网站" 来搜索网站内容。但是有自己独立的搜索会显得网站比较精致和专业。
目标:
# 本地搜索要另外安装插件
# 安装插件要注意版本号,找到适合的版本号
# 有问题就删掉 yarn remove @vuepress/plugin-search
yarn add -D @vuepress/plugin-search@next
yarn 是靠 package.json 定义组件之间的依赖关系的,安装后我的内容是:
{
"devDependencies": {
"@vuepress/plugin-search": "^2.0.0-beta.66",
"vuepress": "^2.0.0-beta.66"
},
"scripts": {
"dev": "vuepress dev blogs",
"build": "vuepress build blogs"
},
"name": "deepinwiki",
"version": "1.0.0",
"description": "deepin wiki,爱好者的共享社区内容",
"main": "index.js",
"author": "htqx",
"license": "MIT"
}
vite 项目中的资源(也就是图片等),只要在 md 文档中引用的,会自动被打包到最终网站构建目录中去。但是,如果你想复制一些独立的资源,那么你得将它放在 blogs/.vuepress/public 目录中。其中 blogs 是我 md 文档的存放目录。.vuepress 是配置的主目录。存放在 public 的任何文件,都会原封不动拷贝到最终输出。
资源的路径以 public 为根目录,如 public/dddu32.ico,在 js 代码中就是 dddu32.ico 即可。
这里我提前准备好三个不同大小的 ico 文件,分别是 dddu32.ico、dddu48.ico、dddu96.ico 存放在 public 目录中。
目录结构:
// blogs/.vuepress/config.js
import { createPage, defineUserConfig, viteBundler } from 'vuepress'
import { defaultTheme } from 'vuepress'
import { getDirname, path } from '@vuepress/utils'
import { searchPlugin } from '@vuepress/plugin-search'
const dir_name = getDirname(import.meta.url)
const logo_url = path.resolve(dir_name, '../pics/dddu.jpg')
export default defineUserConfig({
lang: 'zh-Hans', // 设置页面的默认语言
title: 'deepin天天用', // 作为页面标题的后缀: 网页标题|网站标题
// <meta name="description" />
description: 'deepin天天用QQ群:7343311',
base: '/', // 根目录,如网站位于 xxx 子目录下:/xxx/
// 插入 head 标签下,一般是网站的元信息
// 这里指定网站的小图标
// <link rel="icon" href="dddu32.ico" sizes="32x32" />
head:[['link',{rel:'icon',href:"dddu32.ico", sizes:"32x32"}],
['link',{rel:'icon',href:"dddu48.ico", sizes:"48x48"}],
['link',{rel:'icon',href:"dddu96.ico", sizes:"96x96"}],],
// 本地化,可以设置 lang、title、description、head
locales: {
'/': { lang: 'zh-Hans' },
'/en-US/': { lang: 'en' },
},
// 主题
theme: defaultTheme({
// 本地化按钮
locales: {
'/': { selectLanguageName: '中文' },
'/en-US/': { selectLanguageName: 'English',
navbar:["../vuepress.html"] },
},
// 导航栏
// 将 gitee.com 替换为真实的网站即可使用搜索引擎来搜索内容
navbar: ['./vuepress.html',
{text:"全站搜索",
children:[{text: '百度',link: 'https://www.baidu.com/s?wd=site:gitee.com deepinwiki'},
{text: 'google', link: 'https://www.google.com/search?q=site:gitee.com deepinwiki'}]
}],
// git 仓库
repo: 'https://gitee.com/deepinwiki/wiki.git',
// 网站标志,左上角的图标
logo: '@fs/' + logo_url,
// 侧边栏目录深度
sidebarDepth: 4,
}),
plugins:[
// 本地搜索插件
searchPlugin({
// 搜索结果最大个数
maxSuggestions:10,
}),
],
// 初始化钩子,这里编程创建网站地图
async onInitialized(app){
// 找首页
let root = app.pages.find(elem=> elem.path === '/')
// 准备将目录内容插入到首页内容后面
let append = '\n\n## 目录'
app.pages.sort((a,b)=>b.data.git.updatedTime - a.data.git.updatedTime)
app.pages.forEach((element,index) => {
let slug = element.slug.trim()
if (!slug) slug = "404"
// [slug](link) 是 md 添加链接的语法
// 为了节省空间,一行展示 4 个文件
if ((index) % 4 == 0) append += '\n\n1. '
append += `[${slug}](${element.path})   `
})
// 把原来的首页移除
app.pages.splice(app.pages.indexOf(root), 1);
// 把新的首页替换进去
let new_root = await createPage(app, {
path: '/',
content: root.content + append,
})
app.pages.push(new_root)
// 设置英语首页
let en_root = app.pages.find(elem=>elem.path === '/README.en.html')
en_root.lang='en'
en_root.path='/en-US/'
},
})
以上就是全部配置了。这里并不需要使用到 client.js 配置客户端,客户端主要是改变主题的布局,我使用了官方的方案。
这里添加了导航栏:本文的链接,语言选择按钮,gitee 仓库链接,还有搜索链接(只会搜索标题,但也不错了)。
然后是动态生成一个首页,它是根据默认首页的内容,然后替换添加目录。目录是通过枚举所有页面,找到他们的链接。很简陋的方案,没有任何美化。
为了演示,提供了英语的本地化目录,但只有一个首页。
这里有一个技术要点,如果资源不存在 public,而是相对 blogs 目录存放的一些资源,要访问它需要使用 '@fs/绝对路径' 定位。
但这样只能在调试的时候临时访问,而不会复制到最终构建的网站中,所以最好不要这么做。
独立的资源当然最好还是放在 public 目录。
// 在 config.js 编程实现的方法
import fs from 'fs'
// 直接拷贝到项目的 public 资源目录(或其他可用的目录)
// 然后在 html 直接用相对目录 ./dddu.jpg 使用即可
fs.copyFileSync('blogs/pics/dddu.jpg', 'blogs/.vuepress/public/dddu.jpg')
组合式 api 中的 steup() 中不能使用浏览器的 api,因为 node.js 没有这些 api,只有当 onBeforeMount() 或 onMounted() 生命周期后,才能使用 DOM api。也就是它无法 SSR 服务器预渲染,因此使用 ClientOnly 组件包裹这些代码留到客户端 vue app 运行的代码。
Content 组件用于开发阶段,预览 md 文档内容。
前面说过,这个 node app 是负责构建内容输出的,它有一些实用的 api 这里摘录一下。这套 api 由 @vuepress/core 包提供。
import {createPage} from '@vuepress/core'
export default {
// Hook:初始化后
// 初始化后可以使用 app.pages,也就是页面对象可用
async onInitialized(app) {
app.pages.push(
await createPage(app, {
path: '/foo.html',
frontmatter:{
layout: 'Layout',
},
content: `这是动态生成的 md 文档:${{ new Date() }}`,
})
)
}
}
可以在 md 文档开始处,记录一些元信息用于转换。它的格式是 --- 开始和结尾的,内部是 yaml 格式。
yaml 格式参考:https://www.runoob.com/w3cnote/yaml-intro.html
它的很多项其实都是在替换默认的站点信息。
---
# 这是 yaml 的注释
# 定义都是按照如下格式
# : 值
# ? key
# key: 值
# 一般 key 不用定义,直接用字符串
# : 后注意要带空格
date: 2023-07-12 # 日期 yyyy-mm-dd
description: '网页描述'
# 或者
description: 网页描述第一行
网页描述第二行
# yaml 使用相同空格的缩进来表示同一层级的内容
# 或对象压缩为 {date:xxx,description:xxx}
# 数组压缩为 [1,2,3,4]
# 非压缩使用 - 前导的同缩进为兄弟元素
# 用空行区分不同的对象
# 在 head 标签中插入标签
# <meta name="foo" content="yaml 的缩进有点眼花" />
head:
- - meta
- name: foo
content: yaml 的缩进有点眼花
lang: 'zh-CN'
loyout: '布局由主题提供'
permalinkPattern: ':year/:month/:day/:slug.html'
routeMeta: {'key', 'haha'} # 路由使用的元信息
title: '网页标题'
---
由 @vuepress/client 包提供客户端可用的 api。
组合式 api(composition api):
__VUEPRESS_SSR__
: 判断是否在 ssr 中的环境变量由 @vuepress/core 包提供。插件的生命周期是很重要的。
调用时立即处理:
初始化 app 时处理:
准备文件时处理:
在 dev / build 时处理:
核心流程与 Hooks(钩子、生命周期的回调函数)
主题也是插件。
默认的 html 模板:@vuepress/client/templates/build.html
配置主题:
[{text, link} | {text, array} | link]
[{text,link,children} | path:{} ]
主题提供了插槽,使用布局来变更插槽原本的内容。
import {defineClientConfig} from '@vuepress/client'
import Layout from './layouts/Layout.vue'
export default defineClientConfig({
layouts:{
Layout,
}
})
<!-- ./layouts/Layout.vue -->
<script setup>
import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
</script>
<template>
<ParentLayout #sidebar-top>
<div>自定义侧边栏</div>
</ParentLayout>
</template>
官方主题在用户配置中,使用了别名 @theme/xxx.vue 定义了相关组件。只要替换掉别名的组件,即可完成组件的替换。
import { getDirname, path } from '@vuepress/utils'
import { defaultTheme, defineUserConfig } from 'vuepress'
const __dirname = getDirname(import.meta.url)
export default defineUserConfig({
theme: defaultTheme(),
alias: {
'@theme/HomeFooter.vue': path.resolve(__dirname, './components/MyHomeFooter.vue'),
},
})
官方组件的命名习惯,如 back-to-top 实际对应 @vuepress/plugin-back-to-top 插件。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。