一、前情提要:
我们在之前,通过引入 express,实现了一个简单(lou)的发布功能。
思源笔记折腾记录 - 实现简单的笔记发布 - 链滴 (ld246.com)
现在我们来实现一个复杂一点的。
二、为什么要用思源鼓捣网站
如果你随便搜索一下 cms 的话,就会看到大概这样的描述:
cms 指的是内容管理系统,可以用来管理网站的各种文件、数据、乃至其他信息。
额,为什么说到这个呢。
其实是之前我不是自建了一个网站吗,就知道 cms 这个词然后到处搜搜搜,然后最开始用的是 wordpress,之后鼓捣起来实在是有点麻烦。
就又换成了某个现成的建站系统。
然后发现哎呀好像还是不行,我写很多的东西的,他的编辑器虽然功能丰富记界面美观,但是写东西实在是太慢了。
之后又把内容迁移到了我来,开始是用 obsidian 编辑之后,复制到我来发布,后来是用思源编辑之后, 用我来发布。
再后来就发现,哎呀还是不行,我来的收费政策又改了,本来就赚不到钱,也不可能花太多钱在这个上面的,而且主要是发现自己手上没有数据,万一那边政策一个变化,文件不就等于丢了么。。。。。。
之后我在使用服务器上部署的思源的时候,突然想通了一件事情:
你看,我需要能够方便舒适地在各种地方编辑网站内容,思源可以做的对吧。
我需要能够上传下载和管理附件,思源也可以做的对吧。
我需要有一个基本的静态文件服务,思源也可以做的对吧。
我还需要它能够方便地“原样”在我自己的设备上和服务器上预览,欸,巧了,思源刚好开源了,界面可以直接摸,基础的样式框架也不用自己写了。
好耶~~~
额,其实最关键的是,现有的好多建站工具的文本编辑体验实在是有点不习惯,还是用熟悉的编辑器鼓捣起来舒服.....
好了,闲话说完了,我们看一下之前的发布效果:
额,不是说这个。
是这个:
还记得有个人曾经说过,想要做到所见即所得的发布么......
这个完全不像好吧。
更好看一点的页面模板
还记得思源本身有一个能够按照原样发布 html 的功能吗?
嗯,就是这个。
它导出的 html,是这样的:
看着还可以哦,我们来看一下是怎么做到的:
上面是导出文件的目录,嗯,所以我们只需要挨个导出文件,然后把他们发送给访问者就行了对吧。
额,你如果这样做其实也不是不行,不过我这里要用另外一种办法,毕竟我是想要用它来做一个简单的博客,那文档树、大纲、反向链接、 关系图、导航栏我还是都想要的。
所以我们还是要自己写一写。
获取块 dom
还记得我们之前用的是哪个接口来弄发布内容的吗?
let 文档内容 = await noobApi.核心api.exportPreview(
{
"id": 文档数据.id
}
)
这个接口获取的 DOM 实际上对应的是这个界面:
额,其实也还挺好,我们复制到知乎什么的也就基本上是这个样子了。
但是说好的原样的吗,我们必须给整一个差不多原样的出来。
这里需要的接口就是这个:
/api/getDoc
这个接口实际上就是获取文档 DOM 的接口。
这里我们先不纠结太多,直接按照这样传一下:
{
id:<块id>
size:102400
mode:0
}
也就是把之前的渲染函数里获取文档内容的部分从这样:
let 文档内容 = await noobApi.核心api.exportPreview(
{
id: 文档数据.id,
}
)
改成这样:
let 文档内容 = await noobApi.核心api.getDoc(
{
id: 文档数据.id,
mode:0,
size:102400
}
)
好,现在刷新一下在浏览器,就可以看到渲染结果了,它长这样:
这是为什么呢?
我们可以打开思源的开发者工具,找到这个元素
然后删掉它看看(右键里面就有删除),后果美如画:
这个时候我们再看看之前导出的 html 里的内容:
等于我们其实只拿到了一个没头没尾的 dom,光靠它,没有那些 js 和 css 代码,显然弄不出一个好看的导出页面。
所以又悟了:只要把这些加到渲染结果里面就可以了。
创建一个渲染主题
这个时候就该停下来想想了,难道还像我们之前写菜单的时候那样,在 js 里面硬写这些来搞吗?
这样怕是有点难受。
所以我们另外想一个办法。
来看一下一些其他建站系统的主题:
这个是 wordpress:
WordPress 主题制作教程:主题文件解析-菜鸟笔记 (coonote.com)
index.php //主模板文件,所有主题都需要它
style.css //样式表文件,不可缺少,并包含主题的信息标题
rtl.css //如果网站语言的文本方向是从右到左,则会自动包含从右到左的样式表
comments.php //评论模板。
page.php //访问者请求单个页面时使用页面模板,这些页面是内置模板。
home.php //默认情况下,主页模板是首页。如果您没有将WordPress设置为使用静态首页,则此模板用于显示最新帖子。
header.php //头部模板文件通常包含您网站的文档类型,元信息,样式表和脚本的链接以及其他数据。
footer.php //公共底部模板
sidebar.php //侧边栏模板
single.php中 //日志模板。
archive.php //归档模板,如果分类标签页没有模板的话,会使用这个模板
category.php //分类页模板
tag.php //标签页模板
search.php中 //搜索结果模板用于显示访问者的搜索结果。
404.php //当WordPress无法找到与访问者请求匹配的帖子,页面或其他内容时,将使用404模板
functions.php //增加WordPress功能
这是 hugo
如何创建自己的 hugo 主题 - 简书 (jianshu.com)
所以我们也来搞一个发布主题系统,嘿嘿嘿。
其他的我们先不管,先来看看内容页面,要实现一个内容页面模板,无非就是弄一个模板,然后填内容进去,那么怎么实现内容模板呢?(小编写不出东西来了都这样)
这里我使用了一个非常杀马特的办法,我们要知道,electron 的渲染进程虽然有 node 环境,但是它也有浏览器环境啊,所以我们就不去找各种奇怪的模板引擎了,我们直接这样:
let 渲染结果 = new DOMParser().parseFromString(docTemplate, "text/html");
渲染结果.getElementById('content').innerHTML = 文档数据.content
这个是什么意思呢,就是生成了一个 document
对象,所以在这个的基础上,我们可以使用 js 对这个对象进行各种操作,比如注入文档内容,这样也比较适合我们这种不大会写程序的嘛,不用学更多新的东西了,由于文档都是我们自己写的,就算解析成 html,应该也不会搞出什么奇怪的问题。
然后我们使用它,来实现一个渲染函数, 这里模仿一下 express 对请求的处理,每一步对渲染结果做一点操作,最后就搞出来一个完整的页面了,因为是像管道一样流过这一堆函数,所以我们叫它管线渲染器吧:
//这个就随便导出一个文档就行了
let 默认模板路径 = 代码片段路径 + 'publishTemplate/default/doc.html'
async function 渲染页面内容(req,res,渲染结果){
let 块id = req.params.blockID
let 页面数据 = await 获取数据(块id)
渲染结果.getElementById('publish-content').innerHTML = 页面数据.content
console.log(渲染结果)
return 渲染结果
}
let 默认渲染管线 = 生成管线渲染器([渲染页面内容],默认模板路径)
而这个是生成管线渲染器的实现:
export function 生成管线渲染器(渲染管线, 模板路径) {
return async (req, res) => {
//这里是告诉浏览器,我返回的是一个html页面
res.writeHead(200, { "Content-Type": "text/html;charset=utf-8" });
let 渲染结果 = new DOMParser().parseFromString(fs.readFileSync(模板路径), "text/html");
//这里是一个循环,不断地把渲染结果和请求喂给它们,所以渲染管线中的每一步也都可以跳出去,直接响应请求
for await (let 渲染函数 of 渲染管线) {
try {
//如果渲染结果没有这个函数说明它不是数据了
if (!渲染结果.querySelector) {
let tempdoc = new DOMParser().parseFromString(
渲染结果,
"text/html"
);
渲染结果 = tempdoc;
}
if (渲染结果.完成) {
return 渲染结果;
}
if (渲染函数 instanceof Function) {
渲染结果 = (await 渲染函数(req, res, 渲染结果)) || "";
}
let 文字渲染结果 = "";
try {
文字渲染结果 = 渲染结果.querySelector("body").innerHTML;
} catch (e) {
文字渲染结果 = 渲染结果;
let tempdoc = new DOMParser().parseFromString(
文字渲染结果,
"text/html"
);
渲染结果 = tempdoc;
console.error(e);
}
} catch (e) {
console.error(e);
continue;
}
}
//如果有结果就返回结果
try {
if (渲染结果) {
res.end(渲染结果.documentElement.innerHTML)
}
else {
res.end('渲染出错,没有有效的结果')
}
} catch (e) {
渲染结果 = null
console.error(e)
}
//万一我们这里对它还有其他操作呢
return 渲染结果
}
}
获取文档内容就不用说了:
async function 获取文档内容(块id) {
let stmt = `select * from blocks where id in (select root_id from blocks where id = "${块id}" )`
let 文档数据 = (await noobApi.核心api.sql({ stmt: stmt }))[0]
let 文档内容 = await noobApi.核心api.getDoc(
{
id: 文档数据.id,
mode: 0,
size: 102400
}
)
return 文档内容
}
这样弄完之后,我们再访问一下,发现效果还是这样:
那问题出在哪里呢?
<link rel="stylesheet" type="text/css" id="themeDefaultStyle" href="stage/build/export/base.css?2.5.2" />
<link rel="stylesheet" type="text/css" id="themeStyle" href="appearance/themes/daylight/theme.css?2.5.2" />
这是导出的文件里的样式表,我们的服务器从根目录就直接跳转到渲染页面了,所以没有给他们返回正确的文件。
所以我们改一下路由:
发布应用.use('/', async (req, res, next) => {
console.log(req)
req.url == '/' ? res.redirect('/block/20200812220555-lj3enxa') : null
next()
})
//把实际的返回弄到block路径下了
发布应用.use('/block/:blockID', 默认渲染管线)
然后想办法提供一下那两个文件。
之前导出的页面文件夹里面其实就有这些文件了,所以我们把它们也放到 publishTemplate/default
里面,然后给它们做下静态文件伺服就可以了:
发布应用.use('/appearance',express.static(代码片段路径 + 'publishTemplate/default/appearance'))
发布应用.use('/stage',express.static(代码片段路径 + 'publishTemplate/default/stage'))
现在再访问一下试试看:
bingo,完成。
这里使用的 express.static
方法的作用就是把某个文件夹当成静态文件夹挂在路径下面,所以用了它之后就可以访问到对应的文件了.
虽然还有很多问题,但是基本的内容渲染出来了不是吗?
这次就先这样吧。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于