在线预览地址:戳我
GitHub仓库: 戳我,欢迎star
介绍
技术栈为纯React。接下来介绍一些稍微重要的技术点:
contentEditable
核心的编辑功能用到了HTML5的 contentEditable
属性,但是在 react 中,你不能直接写下面的这种组件
<div contentEditable>
hello world
</div>
这会被 react 提示警告:
试想,平时的 react 组件的 children 都是我们写好内容并控制更新,而不是交给用户去随意更改。就算是可以随意更改的 <input>
组件,它的初始值也是一个 html 的 attribute 而不是 dom 的属性。
回到 contentEditable
,一个带 contentEditable
的组件被更改后,其 children 的内容实际上并没有随着 dom 的更改而更新,这有点类似非受控组件,但是非受控组件 <input>
并不带 children,刚才说到的,用一个 html attribute 来初始值,并通过 onChange
来获取 value 的更新。
然后我在 npm 上随便翻了下,果然有可以直接用的 contentEditable
组件,看了下源码,核心思想就是:
写一个普通的元素,比如 span,赋予 contentEditable
属性,不带 children ,然后用 dangerouslySetInnerHTML
来初始化,通过 onInput
来监听每次修改,通过 ref 来获取 innerHTML 就可以将编辑后的内容传递给 props 中的 onChange,没有了 children 的冗余,组件只是在维护一份 innerHTML
。这就解决了普通的 contentEditable
的两大痛点:1. 非受控 2. 状态冗余。
这里我没有直接使用 npm 上现成的组件库,因为需要比较多的钩子函数和自定义的功能,所以按照这个思路定制了一个具有 contentEditable
属性的组件。
List
还有一个问题是 List,需求是这样:右键单击 List 中任意一个 item 会弹出菜单,可以调整 List 中 items 的顺序,如图:
通过维护一个类似 key 的 index prop 来标注所有 item 的顺序,item 根据初始化的顺序有一个从 1 开始的 index
prop,在 List 的 state 中维护一个数组来记录所有 item 的顺序,插入 / 移动 / 删除对应的 item 时更新数组,render 函数再根据数组 中标记的顺序来渲染,每次只渲染移动的 item。
总结
最后介绍一下整个页面的功能:
- 可以对每个字段进行编辑
- 可以对列表字段随意添加移动删除
- 可以直接使用 markdown 的
[]()
语法来插入链接 - 可以上传本地头图
- 可以保存为 PNG, PDF, HTML 三种格式
在线预览地址:戳我
GitHub仓库: 戳我,欢迎star
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。