11

豪横!他徒手撸了个markdown笔记平台,只是不想委屈自己

 3 years ago
source link: https://zhuanlan.zhihu.com/p/368342990
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

豪横!他徒手撸了个markdown笔记平台,只是不想委屈自己

关注微信公众号:大转转FE。 一个有趣的前端团队~
v2-c4ef56b2202c5a8d5f13b79e69a6d1ef_720w.jpg

一、前言

作为开发者,我觉的用markdown写文档是一件很酷的事情。在之前,想找一款合适自己的markdown平台做笔记,尝试用了有道云、印象笔记、作业部落等平台,挺多功能需要收费或者满足不了,另外在产品设计上也不是自己想要的style,于是就想着打造一个属于自己风格的markdown笔记平台。

二、各平台markdown笔记差异

1.印象笔记

v2-ca794617409c0cfbc2869fd56ea18411_720w.jpg

印象笔记主要专注于word文本编辑,目前web端是不支持markdown语法编写的。在2018年,印象笔记客户端开始支持markdown语法。

使用markdown过程中的一些缺陷:

  • 编辑器主题太少
  • 文档区域代码不支持主题设置
  • 外链访问有限,仅支持移动端打开,并且下载app才能预览。
  • 不支持导出md文件、批量导出

2.有道云笔记

v2-e84c1f53a3483db145b793106d7d35fd_720w.jpg

有道云也是专注于word文本编辑,支持markdown语法开发。界面相比印象笔记会美观一点。

使用markdown过程中的一些缺陷:

  • 编辑、预览区域滚动相对体验不太友好,有点生硬。
  • 预览区域代码不支持主题设置
  • 不支持批量导出

3.其它平台

比如作业部落,主要专注markdown笔记编写,由个人开发的平台,另外一些功能需要收费才能够使用,功能也比较局限。

总是来说,每个平台的比较各有各的优势,同时也存在一些缺陷,由此也萌生了Hbook笔记这个平台。

三、Hbook笔记

1.介绍

Hbook笔记是一款专注于Markdown笔记的平台。

  • 支持编辑器、文档区域主题任意切换
  • 支持编辑、文档区域调整窗口大小
  • 支持单个、批量导出md文件
  • 支持文档截图
  • 文章支持分类
  • 支持控制外链访问
  • 拥有自己的个人主页

2.如何实现?

这是整个项目的一个架构图:

完成这么个项目,你需要具备的一些基本能力和条件:

  • [x] 一个域名
  • [x] 一台服务器(阿里云、腾讯云)
  • [x] web开发
  • [x] nodejs开发
  • [x] 安装node
  • [x] 安装pm2
  • [x] 安装nginx
  • [x] 安装mysql
  • [x] 安装redis

看着好像有点多?我已经为你准备好了教程,一步步教你如何安装部署。

安装部署教程[1]

前台实现

前台页面的核心实现其实就是编辑器,这里选用的是第三方 brace[2] 插件 ,brace支持各种语言的编辑器,这里选用 markdown 语法。

核心基础配置:

import ace from 'brace'
import 'brace/mode/markdown'
editor = ace.edit('editor');
editor.focus();

// 设置字体 请勿用乱用字体,否则会影响光标位置问题,
editor.setOption('fontFamily', 'Menlo, 'Ubuntu Mono', Consolas, 'Courier New', 'Microsoft Yahei', 'Hiragino Sans GB', 'WenQuanYi Micro Hei', 'sans-serif');
// 设置字体大小
editor.setOption('fontSize', '18px');
// 设置内容
editor.setValue(editContent);
// 清除选中
editor.clearSelection();
// 设置编辑器语言模式
editor.getSession().setMode('ace/mode/markdown');
// 设置皮肤
vm.changeEditorSkin(vm.themeData.editor)
//自动换行,设置为off关闭
editor.setOption('wrap', 'free')
// 设置是否只读
editor.setReadOnly(true || false); 
// 是否展示行号
editor.setOption('showGutter', false);
editor.setOption('autoScrollEditorIntoView', true);
// 使用软标签
editor.getSession().setUseSoftTabs(false);
// 线条
editor.renderer.setShowPrintMargin(false);
// 设置编辑器左右边距
editor.renderer.setPadding(35);

在用的过程中值得注意的事:

  • 请勿用乱用字体,建议用默认就行,否则会影响光标位置问题。
  • 编辑器内容保存后出现xss问题,需要对文章内容相应的标签进行处理。

另外拿到文本之后,你需要把markdown语法转化成html展示,这里选用第三方 showdown[3] 插件,详情文档可点击查看。

使用示例:

import showdown from 'showdown'
const converter = new showdown.Converter({
    tables:true,
    tasklists:true,
    ghCodeBlocks:true,
    simpleLineBreaks:true,
    openLinksInNewWindow:true,
    backslashEscapesHTMLTags:true
});
const content = converter.makeHtml(val);

预览展示已经实现了,自然目录也少不了,目录只要把相应的标题标签全部找出来就行了,然后给每个不同的标题做一些不同层级的样式间距即可,大概实现方式:

const vm = this;
vm.anchorDom = [];

const anchorList  = rc.querySelectorAll('h1,h2,h3,h4,h5,h6');

anchorList.forEach(function(elem,index){
    const tag = elem.localName;
    vm.anchorDom.push({
        index:index,
        tag:tag,
        text:elem.innerText
    })
    elem.setAttribute('id','anchor-'+index);
})

另外编辑器滚动的时候预览区域也要相应的滚动到相应到位置,这边采用比较简单的方式,算出2个区域的高度比。

// 计算2边高度的占比
getScale() {
 const rightDiff = rc.offsetHeight - right.offsetHeight + 50; //预览内容高度减去盒子固定的高度
 const leftDiff = editor.renderer.layerConfig.maxHeight - left.offsetHeight; //编辑内容高度减去该盒子固定的高度
 return rightDiff / leftDiff;
}

然后监听2边的滚动事件做相应的处理:

// 编辑区滚动
editScroll() {
    const scale = this.getScale();
    right.scrollTop = left.scrollTop * scale
}
// 展示区滚动
showScroll() {
    const scale = this.getScale();
    editor.getSession().setScrollTop(right.scrollTop / scale);
}

另外考虑到编辑器、预览区域大小灵活问题,在中间滚动条也实现了可缩小或者扩大区域,根据自己喜好来调整,具体实现方式就是监听 mousemove 事件然后改变左右2边的宽度即可。

左边菜单获取到文章列表时需要生成树结构,实现方式:

/**
 * 无限分类格式化成树
 */
function toTree(data){
    // 删除 所有 children,以防止多次调用
    data.forEach(function(item) {
        delete item.children;
    });
    // 将数据存储为 以 id 为 KEY 的 map 索引数据列
    var map = {};
    data.forEach(function(item, index) {
        map[item.id] = item;
    });
    var val = [];
    data.forEach(function(item) {
        // 以当前遍历项,的pid,去map对象中找到索引的id
        var parent = map[item.parent_id];
        // 如果找到索引,那么说明此项不在顶级当中,那么需要把此项添加到,他对应的父级中
        if (parent) {
            (parent.children || (parent.children = [])).push(item);
        } else {
            //如果没有在map中找到对应的索引ID,那么直接把 当前的item添加到 val结果集中,作为顶级
            val.push(item);
        }
    });
    return val;
}

后台实现

后台技术这里主要采用nodejs,核心主要在表结构设计上,其它的相对还好。

这个项目的一个表结构设计,仅够大家参考了解:

字段类型是否必填备注
  • 文章分类表
字段类型是否必填备注字段类型是否必填备注

另外可对文章内容设置的标签进行更细一步进行分类,通过标签筛选出相对应的文章,这个实现逻辑比较复杂就不讲了,有兴趣的可以交流下。

基本上满足以上3个表就可以搭建自己的一个文章库了。

3.后续优化

  1. 实现文章密码访问权限
  2. 新增导出pdf文件
  3. 编辑器文本过滤非法标签
  4. 两边滚动区域更加精准

4.愿景

愿景:让笔记成为一种习惯,让分享成为一种快乐。

5.最后

原本的初心弄个笔记出来玩玩,但没想到自己做出来后却养成了一个写笔记的习惯,记录了工作上的点点滴滴,让自己也收获匪浅。

最后欢迎体验该产品,提出您宝贵的建议。Hbook[4]

四、总结

通过上面的讲解,相信你对整个流程也基本熟悉,自己也可以尝试动起来,有啥问题欢迎一起交流。

最后

最后,感谢您的阅读,有任何问题,欢迎评论区留言讨论,互相学习~ 本月我们将推出更多实践相关文章及有奖留言活动,也欢迎扫描下方二维码关注我们的微信公众号及时获取

参考资料

[1] 安装部署教程: http://bookcss.com/note/12/14

[2] brace: https://github.com/thlorenz/brace

[3] showdown: https://github.com/showdownjs/showdown

[4] Hbook: http://bookcss.com


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK