主要用于记录一些短文或吐槽,这些内容不适合作为博客文章出现,该功能类似我很久之前实现过的时间轴,但上次更新时间轴已经是6年前的事情了,而且时间轴所使用的后端服务还是我十年前写的一个基于
Python+Flask的博客。近几年我一直在使用 usememos 作为个人朋友圈的替代品,此次也是准备将memos利用起来,在博客内嵌入一个新的闲言碎语的页面
实现
我使用的博客系统是我自己写的 Snow静态博客生成器,所以实现也很方便。
首先添加一个新的页面 content/pages/memos.org
1#+TITLE: 闲言碎语
2#+PROPERTY: SLUG memos
3#+PROPERTY: COMMENT True
4
5#+begin_export html
6<div id="memos" data-src="https://memos.libforest.com">
7 <div class="entry-loading">
8 <i class="fa fa-spinner fa-spin"></i>
9 <span>加载中...</span>
10 </div>
11</div>
12<script type="text/javascript" src="https://s.libforest.com/static/js/marked.min.js"></script>
13<script type="text/javascript" src="/static/js/memos.js"></script>
14#+end_export
使用 data-src 设置后端 memos 的服务接口,而 memos 的开放接口是 https://{api}/api/v1/memo, 返回数据如下
1{
2 "memos": [
3 {
4 "name": "memos/GorXqRpEUPCE3jVjY9pp8W",
5 "state": "NORMAL",
6 "createTime": "2025-11-11T08:57:38Z",
7 "updateTime": "2025-11-11T08:57:38Z",
8 "content": "测试图片",
9 "pinned": false,
10 "resources": [
11 {
12 "name": "resources/VPqiT6yFj2nBgyGsKtUWjj",
13 "filename": "02.png",
14 "type": "image/png",
15 "size": "1107605",
16 "memo": "memos/GorXqRpEUPCE3jVjY9pp8W"
17 }
18 ]
19 }
20 ],
21 "nextPageToken": "CAoQCg=="
22}
使用JS请求就是
1document.addEventListener("DOMContentLoaded", async () => {
2 let dom = document.querySelector("#memos");
3 let api = dom.getAttribute("data-src");
4 if (api && api != "") {
5 let resp = await fetch(`${api}/api/v1/memos`);
6 let data = await resp.json();
7 console.log(data.memos);
8 }
9});
注意:
我正在使用的memos版本在查询接口上的参数需要进行一层转换,必须使用生成的protobuf文件才行,这在单独的JS文件中不好实现,所以暂不支持添加参数查询,目前也只是显示第一页总共10条的数据,这对于我来说已经足够了,如果有需要下一页 ,可以利用返回数据里的 nextPageToken,将其作为查询参数添加到请求URL中
1`${api}/api/v1/memos?page_token=${nextPageToken}`
获取到数据后就能转换成 HTML
-
内容处理: memos 的内容基于
Markdown,所以我引入了marked.js作为解析库,同时还需要对标签进行解析和渲染1const memoTag = { 2 extensions: [{ 3 name: 'memoTag', 4 level: 'inline', 5 start(src) { return src.indexOf('#'); }, 6 tokenizer(src) { 7 const rule = /^#([^\s#]+)(\s*)/; 8 const match = rule.exec(src); 9 if (match) { 10 let html = `<span class="memo-tag">#${match[1]}</span>`; 11 if (match[2].endsWith('\n')) { 12 html = `${html}<br />` 13 } 14 return { 15 type: 'memoTag', 16 raw: match[0], 17 html: html 18 }; 19 } 20 }, 21 renderer(token) { 22 return token.html; 23 } 24 }], 25}; 26marked.setOptions({ 27 breaks: true, 28 smartypants: false, 29}); 30marked.use(memoTag); 31console.log(marked.parse(memo.content)); -
资源处理: 资源应该是包括图片、音频、视频等多种类型的,目前我只处理了图片类型,这也是基于我的需求
1function renderContent(memo) { 2 let resources = memo.resources.map((r) => { 3 const src = `${api}/file/${r.name}/${r.filename}?thumbnail=true`; 4 const style = "max-height: 16rem;border-radius: 0.5rem;object-fit: contain;"; 5 return `<a href="${src}" data-fancybox="image"><img data-src="${src}" class="lazyload" style="${style}" /></a>`; 6 }); 7 if (resources.length > 0) { 8 return `<div>${marked.parse(memo.content)} 9 <div style="display: flex;gap: 0.5rem; align-items: baseline;">${resources.join("")}</div> 10 </div>`; 11 } 12 return `<div>${marked.parse(memo.content)}</div>`; 13}
或许可以把该功能作为 shortcodes 使用, 完整代码可以在 memos.js 查看
其它
如果需要一个 memos 客户端,可以试试我写的 红枫笔记,除了支持本地笔记外,还支持 memos、blinko等远程服务的管理
知识共享署名-非商业性使用-相同方式共享4.0国际许可协议