这是一个无比诡异的 Quill 富文本编辑器组件的 bug,浪费了我周四整整一个下午。
唯一的收获是,把早已忘光的 React 组件封装,及组件通信的机制复习了一遍。
bug 现象
在一个 Ant Design Pro 写的后台操作界面中,在弹出的 Modal 组件中,内嵌一个 React Quill 的富文本编辑器组件。 进行内容编辑,输入 hello 换行,再换行,输入 world。保存。
再次打开 Modal,里面 Quill 展示的内容,会看到中间的换行不见了。。。 再次重复上面操作,hello world 直接变为了一行。 继续测试,会看到不单是换行,连列表样式也会消失。
排除服务器端的问题
debug 一下, 保存时,会看到向服务器 POST 了数据:
<p>hello</p><p><br></p><p>world</p>
再次从服务器 GET 拉取
<p>hello</p><p><br></p><p>world</p>
说明服务器的处理及存储是没有问题的。
Quill 内容渲染
同时打印了前端逻辑传递给 Modal 内 Quill 的内容,也是没问题的。
只有渲染时,才有问题:
<div class="ql-editor" data-gramm="false" contenteditable="true">
<p>hello</p>
<p>world</p>
</div>
会看到换行丢失。
再次保存,连 p 标签都会消失,最后只剩下
<p>helloworld</p>
对比其他项目
之前的项目中,测试了一下,没有这个问题。
但是版本有差异:
旧项目
"react-quill": "^2.0.0-beta.2",
新项目
"react-quill": "^2.0.0-beta.4",
其他依赖,Quill 及 Ant Design 版本都是一致的。
- 然后回退了版本跟旧项目一致,不行;
- 升级到最新的 react quill 2.0 正式版,也不行。
搜索关键词
我尝试了不少关键词,都没有找到类似的讨论,好不容易在 stack overflow 上找到一个 angular 中使用 Quill 的讨论。 现象非常像。
I recently encountered this problem, which was mostly apparent when the quill editor is rendered within tabs or steppers. The solution I've used is to use *ngIf to display the quill editor only when it is in view (An alternative is to trigger it on afterViewInit if what I've done doesn't exactly fit your project). Possibly, it strips all the tags and simply wraps the content with p tags.
看来正确的搜索关键词应该是:
quill strip all tags
终于找到了这个问题的讨论:
https://github.com/zenoamaro/react-quill/issues/814
这里有解决方案,太专业了。虽然,都没有测试成功,但是有了一个大概的方向。
就是某些条件下,不应该更新这个值。
- 关闭 modal 动画
- 不使用 destroyOnClose
- 判断是否是第一次渲染
- 判断是否是用户输入 (这个是不合理的,因为我还有图片插入,也不是用户输入,而是 api)
解决方案:封装组件
封装成组件,延迟加载。即便不是这个原因,封装一个组件也很有必要。重复代码太多了。
没想到,居然通过 ReactQuill 再次封装解决了。同时,在代码中加上了 github 那个讨论中的是否是第一次渲染的判断。
然后通过 react ant design Form.Item 的 props.value / props.onChange 的数据传递机制将结果返回。
实现代码
增加样式内置:
yarn add styled-components --save
组件代码:
import React from 'react';
import ReactQuill from 'react-quill';
import 'react-quill/dist/quill.snow.css';
import { uploadImage } from '@/services/ant-design-pro/api';
import styled from 'styled-components';
const Wrapper = styled.div`
.ql-editor {
min-height: ${(props) => props.minHeight || 200}px;
max-height: ${(props) => props.maxHeight || 500}px;
}
`;
function QuillFucker(props: any) {
const quill = React.useRef(null);
const [content, setContent] = React.useState(props.value || '');
const [isInitialRender, setIsInitialRender] = React.useState(true);
React.useEffect(() => {
setIsInitialRender(false);
}, []);
const toolbarContainer = [
// ...
];
const imageHandler = () => {
// ...
};
const modules = React.useMemo(
() => ({
toolbar: {
container: toolbarContainer,
handlers: {
image: imageHandler,
},
},
}),
[],
);
return (
<Wrapper minHeight={props.minHeight} maxHeight={props.maxHeight}>
{!isInitialRender && (
<ReactQuill
ref={quill}
theme="snow"
value={content}
onChange={(value, _delta, source) => {
setContent(value);
props.onChange(value);
}}
modules={modules}
/>
)}
</Wrapper>
);
}
export default QuillFucker;
最后
写前端代码,就如同在粪坑里游泳一般,首先你需要勇敢地跳进去。
然后,冷静下来,遇到💩💩💩不要慌,要坚信,会遇到更多的💩。
同时,在粪坑里继续制造你的💩,这样才能不至于窒息。
即便是 AI 也解决不了这些前端的历史粪坑问题。
微信关注我哦 👍
我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式