编辑富文本
了解在 React Native 中预览和编辑富文本的当前方法。
许多应用需要允许用户输入文本。例如,如果你想构建一个聊天或社交媒体应用,你可能会在很大程度上依赖文本输入。React Native 内置了一个 <TextInput> 组件,可以在许多简单情况下轻松实现这一功能。
🌐 A lot of applications need to allow users to type in text. For example, if you want to build a messaging or social media app, you will probably rely heavily on text inputs. React Native has a built-in <TextInput> component to implement this without much effort for many simple cases.
然而,有时你需要更加灵活。想想长篇社交媒体帖文、注意应用或文档编辑器。理想情况下,你需要允许使用不同的文本样式、列表、标题、嵌入图片等功能。这被称为富文本编辑器,而且在各处实现都是一个难题,包括在 React Native 中。
🌐 However, sometimes you need to be more flexible. Think of long social media posts, note apps, or document editors. Ideally, you need to allow different text styles, lists, headings, embedded images, and more. This is called a rich text editor, and it's a difficult problem to solve everywhere, including in React Native.
在 React Native 生态系统中,目前没有默认的解决方案。不过,本指南探讨了一些选项和有前景的方法,每种方法都有其利弊。
🌐 There is currently no default solution for that in the React Native ecosystem. However, this guide explores some options and promising approaches, each with its own tradeoffs.

渲染富文本
🌐 Render rich text
有很多很好的选项来显示富文本:
🌐 There are a lot of good options to display rich text:
-
对于 Markdown 内容,你可以使用诸如
react-native-markdown-display的 Markdown 渲染器或其他渲染器。 -
对于 HTML 内容,你可以使用
@expo/html-elements或 Web 视图 (react-native-webview)。 -
为了拥有自定义格式和更多控制,你可以利用嵌套
<Text>组件来呈现样式和布局。<TextInput> <Text> <Text style={{ fontWeight: 900 }}>Some bold text</Text>Some regular text </Text> </TextInput> -
你也可以使用 Expo Modules API 利用原生平台的基本组件,通过第三方库(如 Android 上的 Markwon 和 iOS 上的
AttributedString)来编写自定义渲染组件。
编辑富文本的方法
🌐 Approaches to edit rich text
有几种方法可以让富文本渲染工作。然而,每种方法都有不同的限制。
🌐 There are a few approaches to get rich text rendering to work. However, all have different limitations.
基于 Webview 的编辑器
🌐 Webview-based editors
虽然大多数 React Native UI 组件封装了原生平台的基础组件,因此速度快、性能高,并且具有原生感,但基于 WebView 的富文本编辑器采用了不同的方法。
🌐 While most React Native UI components wrap native platform primitives and are fast, performant, and native feeling as a result, the webview-based rich text editors use a different approach.
他们将一个为网页构建的现有富文本编辑器通过 JavaScript 封装在 react-native-webview 中。它可以在所有平台(Android、iOS、Web)上运行,并且能够利用网页平台上流行的富文本编辑器,但这会带来性能和用户体验上的损失。
🌐 They wrap an existing rich text editor built for web with JavaScript inside a react-native-webview. It works on all platforms (Android, iOS, Web) and can take advantage of popular rich text editors available for the Web platform, but it has a performance and UX penalty.
你将无法在编辑器中使用原生 UI 组件。任何像提及或图片嵌入这样的功能实现都会重复功能,并且需要大量努力来实现。
🌐 You will not be able to use native UI components inside the editor. Any implementation of features like mentions or image embedding will duplicate features and require significant effort to implement.
现有的基于 webview 的 React Native 库
🌐 Existing webview-based React Native libraries
现有有几个 React Native 库可以实现富文本编辑。如果你需要一个基本的富文本编辑器,配置要求不高,并且对性能或用户体验没有严格要求,这些是最容易入门的选择:
🌐 There are a couple of existing React Native libraries to allow rich-text editing. These are the easiest options to get started if you need a basic rich text editor with limited configuration and don't have strict performance or UX requirements:
基于 webview 的自定义编辑器
🌐 Custom webview-based editor
如果你需要更多的可配置性,你可以使用现有的仅网页编辑器来构建类似的库。不过,你必须自己处理消息传递和网页实现。这会让你拥有底层编辑器提供的所有选项,并让你能够实现更多功能。
🌐 If you need more configurability, you can build a similar library with an existing web-only editor. However, you have to handle the message passing and web implementation yourself. This gives you all the options that the underlying editor offers and lets you implement more features.
你需要使用消息传递来在 Webview 与外部之间传递文本和 onChange 事件。由于富文本内容通常很长,最好将其建模为非受控组件,以防止每次键入时出现延迟。此外,如果能避免在每次键入时序列化并发送整个状态,也能提升性能。
🌐 You will need to use message passing to pass text and onChange events to and from the webview. Since rich texts often end up long, it's better to model it as an uncontrolled component to prevent lag on each keystroke. Also, if you can avoid serializing and sending the entire state on each keystroke that also improves performance.
构建在 React Native TextInput 之上
🌐 Building on top of React Native TextInput
在本节中,我们将讨论是否可以拥有用于通用目的的完整富文本输入功能。
🌐 In this section, let's discuss if it is possible to have a feature complete rich text input for general purposes.
React Native 允许嵌套 <Text> 组件,并允许它们作为 <TextInput> 的子组件使用,以呈现和编辑样式化文本。它与新的 React Native 架构是同步的(onChange 事件会在文本输入字段输入新字符后立即触发)。
🌐 React Native allows nested <Text> components and allows them to be used as children of <TextInput> to render and edit styled text. It is synchronous with the new React Native architecture (the onChange event fires immediately after a new character is typed into the text input field).
不幸的是,<TextInput> 组件是为处理普通文本而构建的,它在 onTextChange 回调中只返回一个字符串。这是一个很大的限制。
🌐 Unfortunately, the <TextInput> component is built toward working with regular text, and it only returns a string in it's onTextChange callback. This is a significant limitation.
这里有一个快速示例。让我们从渲染一个带有以下加粗文本的文本输入开始:
🌐 Here is a quick example. Let's start by rendering a text input with the following bold text:
<TextInput> <Text> {/* The following will render a bold text in this format: **aa**aa */} <Text style={{ fontWeight: 900 }}>aa</Text>aa </Text> </TextInput>
然后,让我们在文本输入中添加第五个字母 a。光标的位置应该决定新字母是否属于加粗字符串。不幸的是,回调只返回 aaaaa。
🌐 Then, let's append a fifth letter a to the text input. The position of your cursor should determine if the new letter is a part of the bold string or not. Unfortunately, the callback only returns aaaaa.
还有一个额外的 onSelectionChange 属性可以用来获取该信息。然而,这会显著增加任务的难度。插入额外的字符(例如用于列表或项目符号的新行)也会导致选择不同步。
🌐 There is an additional onSelectionChange prop that can be used to get that information. However, it makes the task significantly harder. Inserting additional characters (such as newlines for lists or bullet points) also desynchronizes the selection.
有人尝试过构建这样的编辑器,比如 markdown-editor(不再积极维护)和 rn-text-editor(处于测试阶段),但目前没有广泛使用的包存在。
🌐 There are some attempts to build such an editor, such as markdown-editor (not actively maintained) and rn-text-editor (in beta), but no widely used packages exist.
带有可见样式标记的 Markdown 编辑器
🌐 Markdown editors with visible styling markers
如果使用 Markdown 来为文本添加样式能满足你的需求,你可以在编辑时将 Markdown 渲染在一个独立的、不可编辑的视图中。使用任何 Markdown 渲染器自己构建并不复杂。你也可以使用第三方库,例如 react-native-markdown-editor。
🌐 If using a markdown to style text fulfills your requirement, you can render the markdown in a separate, non-editable view while editing. It is not complex to build on your own using any markdown renderer. You can also use a third-party library such as react-native-markdown-editor.
这种编辑体验适合高级用户或编程/技术应用。你也可以探索在仅在选定文本块中显示 Markdown 的同时呈现富文本,或其他混合方式的表现方式。
🌐 This editing experience suits power users or programming/tech applications. You can also explore rendering rich text while showing markdown only in the selected chunk of text or other hybrid approaches.
原生编辑器
🌐 Native editors
你可以使用封装在 React Native 模块中的原生 Android 或 iOS 富文本编辑器。有几种选择:
🌐 You can use a native Android or iOS rich text editor wrapped into a React Native module. There are a few options:
你也可以使用 Expo Modules API 封装任何原生富文本编辑器,但如果在每个平台上使用不同的编辑器,你需要统一它们的 API 和输入格式。
🌐 You can also wrap any native rich text editor using Expo Modules API, but if you use different ones on each platform, you need to unify their APIs and input formats.
富文本通常使用抽象语法树表示。例如,一个项目符号列表可以是类型为
bulleted-list的节点,包含多个类型为list-item的子节点。你可以将 HTML 和 Markdown 都转换为合适的 AST 格式。
还有一个将词汇编辑器移植到 Android 和 iOS,并提供 React Native 封装的努力,在这里跟踪。
🌐 There is also an effort to port the lexical editor to Android and iOS and provide a React Native wrapper, track it here.
概括
🌐 Summary
虽然有许多用于显示富文本的优秀选项,但在 React Native 中没有一种适用于所有情况的富文本编辑解决方案。目前没有一种流行的方案在大多数使用场景中足够满足需求。需要社区进一步贡献,以改进现有解决方案或增加新的解决方案。
🌐 While there are many great options for showing rich text, there is no one-size-fits-all solution for rich text editing in React Native. There's no popular solution that is sufficient in most use cases. Further contribution from the community is needed to improve existing solutions or add new ones.
目前,你需要在一个更复杂、功能更丰富但可能更难维护的原生编辑器和一个基于 react-native 基本组件构建的编辑器之间谨慎选择。这取决于该功能对你的应用来说有多核心,你的编辑器需要多少功能,以及你愿意投入多少精力去开发它。
🌐 For now, you need to carefully choose between a more complex, native editor that offers more features but may be harder to maintain, or an editor built on top of react-native primitives. This depends on how core the feature is to your app, how many features you need in your editor, and how much effort you are willing to spend on building it out.