由兴趣驱动开源 cherry markdown 成长之路
如果无法正常显示,请先停止浏览器的去广告插件。
1. 由兴趣驱动开源
Cherry Markdown 成长之路
胡杰雄 腾讯TAPD 前端开发工程师
2. 1. 组件介绍与背景
目录
2. 内核引擎实现
3. 性能
4. 应用与拓展
3. 01
组件介绍与背景
4. Cherry Markdown 简介
工具栏
语言编辑区
Markdown解析引擎
富媒体预览区
5. 组件背景
公司内部使用不同开源组件 公司的开源协同战略
公司内部支持md的产品都是使用的外部开源组件,这一方面导 1. 2019 年公司开启开源协同战略
致不同产品md编辑体验、语法能力、语法规则不统一,用户在 2. 腾讯前端技术委员会的成立
产品间切换时学习成本高;另一方面引入外部组件缺乏技术审
核,有协议、安全风险
业界 Markdown 开源组件支持有限 个人原因、项目经验
业界 Markdown 开源组件支持的功能与场景有限,有的仅做语 个人兴趣驱动,想做一款业内好用的 Markdown 编辑器,加上
法转译的部分,有的是chrome插件或者现成无法拓展功能的应 部门内有志同道合的同事一起作战;TAPD Markdown 功能 6
用软件。 年的积累,场景多,用户反馈体验升级诉求。
组件预期: 语法转译 + 可视化 + 按需加载语法 + 自定义语法
6. 团队成立
2019·11
团队成立
4个来自3个产品团队的开发小伙伴聚到了一起
没有产品、没有KPI、没有经费、没有用户
一腔热情配张饼
7. 02
内核引擎实现
8. 内核引擎实现效果
Markdown解析引擎
9. 内核引擎业界实现 1 —— AST 语法树
# title
```javascript
var a = 1;
```
Markdown Flow
(结尾添加换行符 \n)
预处理
词法分析
文本切割
# title
```javascript
var a = 1;
```
语法分析
标题
render
DOM 树
Document
Heading
CodeBlock
代码块
优点 缺点
性能相对快 拓展性差
实现难度高
一次词法分析
语法分析需预编译 或 依赖第三方语法规则
10. 内核引擎业界实现 2 —— 正则表达式
# title
```javascript
var a = 1;
```
Markdown Flow
正则
语法 1 let temp = text.match(/^#\s/);
语法 1
If (temp[0] === ‘#’) {
// render
return ‘<h1>’+ rows.substring(2) + ‘</h1>’
}
render
语法 2
正则
render
DOM 树
// 正则
语法 2
…. 代码块 正则
… 代码块 render
<h1>title</h1>
<pre>
<code>
<span>var a = 1;</span>
</code>
</pre>
优点 缺点
易实现 性能相对慢
易拓展
多次正则匹配(词法分析)
11. Cherry Markdown 内核引擎实现目标
Markdown
语法可插拔
Markdown
语法学习成本低
易拓展
内核引擎调度
Markdown
语法链路简单
提高可维护性
12. Markdown Hook 机制
每个 Markdown 语法设为 Hook
优点:易拓展、调度 Markdown 语法链路灵活、学习成本低
13. Markdown Hook 链路配置 —— before、after
通过 before、after 插入数组链路的单元中
配置声明
Markdown Flow
编译链路
tapd-table
tapd-html
custom-hook
14. Markdown Hook 代码实现
代码
Hook 编译流程图
Markdown text input
Hook.rule ()
Hook.makeHtml ()
方法含义 makeHtml: 转义 markdown text 文本
rule: 语法匹配
优点 2种周期方法,概念少
result output
15. Markdown Hook 机制难点
部分 Markdown 语法 makeHtml 方法输出影响其他语法
代码块语法内容不允许被转义
16. Markdown Hook 分类
加粗
基础语法
Syntax 类
斜体
…
Hook
代码块
排它语法
Paragraph 类
基础语法:默认语法
排他:预先转译并缓存转译结果,等编译结束返回缓存结果
公式
…
17. Markdown Hook 难点解决 —— 排他
Markdown Flow
List
CodeBlock
List
基础类
排它类
CodeBlock
基础类
排它类
18. Markdown Hook 难点解决 —— 排他实例
19. Markdown Hook 心智思路开发
心智思路
真实链路
20. 内核引擎架构图
21. 第一版发布 · 内部开源
2019·11
团队成立
4个来自3个产品团队的开发小伙伴聚到了一起
没有产品、没有KPI、没有经费、没有用户
一腔热情配张饼
2020·4
第一版发布 · 内部开源
实现所有通用语法功能
内部产品率先使用(TAPD、乐享 iwiki)
第一次内部推广(3k+浏览,63收藏,63评论,团队扩张)
制定开发规范、代码提交规范、完成API文档
用户有了啥都有了
22. 03
性能
23. 正则表达式 —— 缺点
# title
```javascript
var a = 1;
```
Markdown Flow
正则
语法 1 let temp = text.match(/^#\s/);
语法 1
If (temp[0] === ‘#’) {
// render
return ‘<h1>’+ rows.substring(2) + ‘</h1>’
}
render
语法 2
正则
render
DOM 树
// 正则
语法 2
…. 代码块 正则
… 代码块 render
<h1>title</h1>
<pre>
<code>
<span>var a = 1;</span>
</code>
</pre>
优点 缺点
易实现 性能相对慢
易拓展
多次正则匹配(词法分析)
24. 场景
1. 首次阶段,全文编译
减少正则匹配次数
2. 后续编辑阶段
减少正则匹配原文行数
排它类
25. 编辑时优化
局部编译
1. 首次阶段 (以段落为节点存储原文)
首次编译
<h2></h2>
<p></p>
<ul></ul>
Render 信息缓存
md5StringCache[text1] = hash(text1)
md5StringCache[text2] = hash(text2)
md5StringCache[text3] = hash(text3)
(text1、text2、text3 为 markdown 原文)
2. 后续编辑阶段
编译 & 缓存
undefiend
查找
md5StringCache
md5StringCache
Hash
无操作
26. 优化效果
2000 行数的编译效果
首次阶段
编辑阶段
优化前
307
217.35
217.35
20
全文编译(ms)
局部编译(ms)
后化后
27. 大文档下,编辑实时回显带来性能开销
2000 行 text
渲染612.73ms
28. 性能低效原因分析
render
setData
Hooks
预览对象
innerHTML
Previewer DOM
耗时612.73ms
新 DOM Tree
原因:
1. 大批量 DOM 操作,全局更新预览区域
2. 绝大多数 DOM 无需更新
优化:局部更新
render
Hooks
新 DOM Tree
Virtual DOM
Diff
Old DOM Tree
Patch
end
耗时 ? ms
29. 解决思路—— Virtual Dom Diff 处理
负优化
30. Virtual Dom Diff 不是万能
Vue/React 框架
Cherry Markdown
data 响应 / setData
某组件
数据更新较快的原因: 慢原因:
1. 组件化:简化 Dom Tree 复杂度,各组件“自治” Dom 更新 1. Virtual Dom Diff 不适合复杂且数据量极大的场景
2. setData/ data 响应式: 触发某小块 Dom 区域的数据更新 2. 缺乏数据变化的监听事件
3. 预览区包含代码块、公式、画图等复杂 DOM 树,加重 Virtual DOM Diff 计算负担
31. 解决思路优化—— 局部编译 + 局部渲染
缩小 DOM Diff 成本
32. 局部编译 + 局部渲染
局部编译
编译
<h2></h2>
<p></p>
<ul></ul>
收集 data-sign
Hash Text
Hash List
原文 Hash 处理
Hash Text 放入 Render DOM Attr
方便预览对象收集
局部更新 Myers Diff + Virtual dom diff patch
update
收集 data-sign
Update Hash Virtual Dom patch
Insert Hash Append dom
Remove Hash Remove dom
Hash List
Myers Diff 算法
预览对象
Old Hash List
(最短编辑距离)
(找出最短增删改操作)
33. 渲染优化效果
优化前
612.73
47
渲染耗时(ms)
后化后
34. 局部编译+局部渲染效果
35. 0.4版发布
2020·8
2019·11
0.4版发布
团队成立
安全
4个来自3个产品团队的开发小伙伴聚到了一起
性能
扩展能力
没有产品、没有KPI、没有经费、没有用户
接入便捷性
对方不仅使用我们,还给我们提了一堆issue
一腔热情配张饼
2020·4
第一版发布 · 内部开源
实现所有通用语法功能
内部产品率先使用(TAPD、乐享)
第一次内部推广(3k+浏览,63收藏,63评论,团队扩张)
制定开发规范、代码提交规范、完成API文档
用户有了啥都有了
36. 04
应用与拓展
37. 应用层实现
开箱即用
· 实例化对象
实例化对象
New Cherry 对象。
· 框架提供容器作用
• Angular 、React、Vue等框架提供容器作用即可。
Vue 为例
框架提供容器环境
38. 拓展插件
· 自定义插件,拓展语法
实现自定义 codeBlock 插件
自定义 codeBlock 插件
39. 拓展能力例举——嵌入第三方应用
40. 成长时间轴·正式成为Oteam
2019·11 2020·8
团队成立 0.4版发布
4个来自3个产品团队的开发小伙伴聚到了一起 安全
没有产品、没有KPI、没有经费、没有用户
性能
扩展能力
接入便捷性
一腔热情配张饼
对方不仅使用我们,还给我们提了一堆issue
2020·4
第一版发布 · 内部开源
2021·5
正式成为腾讯 Oteam
实现所有通用语法功能 申请专利9篇
内部产品率先使用(TAPD、乐享) 参与人数达到12个团队35人
第一次内部推广(3k+浏览,63收藏,63评论,团队扩张) 与11款toC、toB、组件类产品合作(腾讯文档、TAPD、腾讯灯塔)
制定开发规范、代码提交规范、完成API文档 更多用户更多创意
用户有了啥都有了
41. 图片缩放、对齐、引用
42. 根据表格内容生成图表
自定义图形库
43. 字体颜色、字体大小
可能是唯一支持的 markdown 组件
44. 表格单元格合并
45. 复制HTML 粘贴自动转译成 Markdown
46. 图片大小
47. 导出
48. 成长时间轴·开源
2019·11 2020·8 2021·10
团队成立 0.4版发布 正式对外开源
4个来自3个产品团队的开发小伙伴聚到了一起 安全
没有产品、没有KPI、没有经费、没有用户
性能
扩展能力
接入便捷性
一腔热情配张饼
对方不仅使用我们,还给我们提了一堆issue
2020·4
第一版发布 · 内部开源
2021·5
正式成为腾讯 Oteam
实现所有通用语法功能 申请专利9篇
内部产品率先使用(TAPD、乐享) 参与人数达到12个团队35人
第一次内部推广(3k+浏览,63收藏,63评论,团队扩张) 与11款toC、toB、组件类产品合作(腾讯文档、TAPD、腾讯灯塔)
制定开发规范、代码提交规范、完成API文档 更多用户更多创意
用户有了啥都有了
49. 共建
50. 感谢倾听
大会官网