2

在线预览地址:戳我

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



fi3ework
114 声望3 粉丝