了解在 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's currently no default solution for that in the React Native ecosystem. In this guide, we explore 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 内容,你可以使用 Markdown 渲染器,例如 react-native-markdown-display
或其他。
¥For markdown content, you can use a markdown renderer such as react-native-markdown-display
or another.
对于 HTML 内容,你可以使用 @expo/html-elements
或 webview (react-native-webview
)。
¥For HTML content, you can use @expo/html-elements
or a webview (react-native-webview
).
要拥有自定义格式和更多控制,你可以利用嵌套 <Text>
组件来渲染样式和布局。
¥To have a custom format and more control, you can take advantage of nesting <Text>
components to render styles and layouts.
<TextInput>
<Text>
<Text style={{ fontWeight: 900 }}>Some bold text</Text>Some regular text
</Text>
</TextInput>
你还可以使用 Expo 模块 使用第三方库(例如 Android 上的 马克旺 和 iOS 上的 AttributedString
)使用原生平台原语编写自定义渲染器组件。
¥You can also use Expo Modules to write a custom renderer component with native platform primitives using third-party libraries such as Markwon on Android and AttributedString
on iOS.
¥Edit rich text
有几种方法可以实现富文本渲染。然而,它们都有不同的限制。
¥There are a few approaches to get rich text rendering to work. However, all have different limitations.
¥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),并且可以利用可用于 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.
¥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:
¥Custom webview-based editor
如果你需要更多的可配置性,你可以使用现有的纯 Web 编辑器构建类似的库。但是,你必须自己处理消息传递和 Web 实现。这为你提供了底层编辑器提供的所有选项,并允许你实现更多功能。
¥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.
你将需要使用消息传递将文本和 onChange
事件传递到 Web 视图或从 Web 视图传递文本和 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.
¥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 towards 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 editors with always 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 模块 封装任何原生富文本编辑器,但如果你在每个平台上使用不同的富文本编辑器,则需要统一它们的 API 和输入格式。
¥You can also wrap any native rich text editor using Expo Modules, 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 格式。¥Rich text is usually represented using abstract syntax trees. For example, a bullet list can be a node of type
bulleted-list
with several children of typelist-item
. You can convert both HTML and markdown to a suitable AST format.
还有一项努力是将词汇编辑器移植到 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.