最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Rust+React創(chuàng)建富文本編輯器

 更新時間:2022年07月04日 17:12:22   作者:智影Yodonicc  
這篇文章主要為大家介紹了Rust+React創(chuàng)建富文本編輯器示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

簡介

在Fiberplane,我們最近遇到了一個有趣的挑戰(zhàn):我們正在使用的富文本編輯器庫已經(jīng)過時了。我們曾經(jīng)使用Slate.js——一個很好的編輯器——但是當(dāng)我們?yōu)閰f(xié)作編輯實現(xiàn)我們自己的富文本基元時,我們發(fā)現(xiàn)我們自己的基元和Slate的數(shù)據(jù)模型之間的脫節(jié)是一個阻礙因素。所以我們開始思考——如果我們建立自己的富文本編輯器(RTE, Rich Text Editor)會怎樣?

從一個非常高層次的角度來看,一個富文本編輯器是由兩個部分組成的。

  • 一個數(shù)據(jù)模型和對其進(jìn)行操作的核心邏輯。
  • 一個渲染上述數(shù)據(jù)模型的狀態(tài)并處理用戶互動的視圖。

我們在視圖中使用了Slate,但結(jié)果是它也拉入了自己的數(shù)據(jù)模型。如果我們可以直接在React中實現(xiàn)視圖,我們可以大大簡化我們的堆棧,并完全控制它的每個方面。缺點是什么?RTEs因為需要支持復(fù)雜的用戶交互而臭名昭著,而現(xiàn)在我們需要自己處理每一個交互。

在這篇文章中,我們將討論我們所面臨的挑戰(zhàn)以及我們?nèi)绾谓鉀Q這些問題。

數(shù)據(jù)模型

我們的產(chǎn)品是一個協(xié)作式的筆記本編輯器。筆記本是一個基于塊的編輯器,由不同類型的單元組成,從文本單元到圖片和圖表。因此,我們確定了一個數(shù)據(jù)模型,它既有利于我們的協(xié)作功能,也有利于為我們在單元格內(nèi)使用的任何富文本字段提供動力的RTE。在這篇文章中,我們將重點討論TextCell。

struct TextCell {
    pub id: String,
    pub content: String,
    pub formatting: Option<Formatting>,
}

這里的content只是純文本內(nèi)容,而formatting是將純文本變成富文本的東西。"多汁"的部分都在格式化類型里面。

type Formatting = Vec<AnnotationWithOffset>;
?
struct AnnotationWithOffset {
    annotation: Annotation,
    offset: u32,
}
?
enum Annotation {
    StartBold,
    EndBold,
    StartItalics,
    EndItalics,
    StartLink { url: String },
    EndLink,
    /* more like these... */
}

正如你所看到的,這只不過是一個注釋列表,它定義了要應(yīng)用的格式化類型和它開始的偏移量。我們有意不選擇類似于HTML的樹狀結(jié)構(gòu),因為格式化范圍可以重疊,這將導(dǎo)致復(fù)雜的樹狀操作。此外,每個注釋只有一個偏移量的簡單性使我們很容易實現(xiàn)我們用于協(xié)作的操作轉(zhuǎn)換(OT)算法。

核心邏輯

隨著數(shù)據(jù)模型的出現(xiàn),也帶來了與之互動的代碼。當(dāng)你在一個單元格中打字時,我們在哪里插入新打的字符?這如何影響content和相關(guān)的formatting?如果你在一個選擇上切換格式,應(yīng)該發(fā)生什么?如果你將一個單元格從中間分割開來,又該怎么辦?所有這些以及更多都在Rust的核心邏輯中實現(xiàn)。

你要知道,無論如何我們都需要這些邏輯,因為我們的OT算法也需要它。但現(xiàn)在我們也能用同樣的原語來驅(qū)動我們的編輯器。

為了使這個邏輯易于測試,它被實現(xiàn)為純函數(shù),我們在TypeScript的Redux reducer中調(diào)用。我們創(chuàng)建了fp-bindgen來生成Rust代碼和調(diào)用它的TypeScript代碼之間的綁定關(guān)系。

為了適應(yīng)RTE(當(dāng)我們還在使用Slate時還不需要),我們不得不自己引入一段邏輯,就是光標(biāo)管理。例如,當(dāng)用戶按下左方向鍵時,我們分派一個MoveCursor動作,其有效載荷如下。

struct MoveCursorPayload {
    pub delta: i32,
    pub extend_selection: bool,
    pub unit: CursorUnit,
}

delta指定光標(biāo)是向前還是向后移動,通過指定一個1-1的值。extend_selection屬性是在用戶按住Shift鍵時使用的,用來擴展當(dāng)前的選擇,或者在還沒有選擇的情況下創(chuàng)建一個。這個unit決定了我們是按Unicode字母群(用戶通常稱之為 "字符")還是按單詞移動光標(biāo),用于用戶按住Ctrl/?鍵時。然后,我們的Rust還原器會處理這些動作,并處理所有的邊緣情況,包括確保光標(biāo)不會出現(xiàn)在@的中間。

視圖

在我們RTE的大部分開發(fā)過程中,我們的編輯器甚至不是一個編輯器。至少從瀏覽器的角度來看不是。這是因為瀏覽器通常只識別兩種類型的編輯器:純文本編輯器,如<input><textarea>元素,以及使用一種叫做contenteditable的屬性創(chuàng)建的自由格式編輯器。我們的編輯器兩者都不是。

我們在最終版本中仍然使用contenteditable屬性,因為我們很快會討論一些實際的影響,但我們有意識地決定盡可能少地依賴它。這對我們最初構(gòu)建RTE的方式產(chǎn)生了深遠(yuǎn)的影響,你將在本節(jié)中看到。

如果我們最初的版本根本沒有使用contenteditable,那么我們怎么能夠創(chuàng)建一個富文本編輯器?從用戶的角度來看,RTE只不過是一個看起來像文本字段的東西,有一個光標(biāo),允許他們輸入任何他們喜歡的內(nèi)容。

所以我們創(chuàng)建了一個普通的React組件,并根據(jù)單元格的contentformatting生成了富文本內(nèi)容,然后使用React.createElement()插入實際的元素,這些元素只是一個應(yīng)用了樣式的<span>元素的平面列表(偶爾會有<a>元素灑在鏈接上)。然后,我們添加了必要的事件處理程序來捕捉用戶的互動,這又將再次調(diào)用數(shù)據(jù)模型上的適當(dāng)邏輯。

那么用戶的光標(biāo)呢?只是另一個我們自己插入的小React組件。我們會在useLayoutEffect()鉤子中測量它需要的位置,然后根據(jù)這個來定位它。

所以......很簡單,很容易,對嗎?好吧,我們現(xiàn)在需要處理的大量的交互使這成為一個重大的挑戰(zhàn)。例如,讓我們再看一下光標(biāo)導(dǎo)航。上一節(jié)中的例子顯示了如何向左和向右移動光標(biāo)。但是如果用戶按了向下的箭頭,他們的光標(biāo)最終會在哪兩個字符之間呢?這不是一個簡單的問題,因為保持光標(biāo)的垂直位置需要測量上面那一行的字符的位置。但你如何定義什么是 "上面那一行"?無論是content還是formatting都不包含這些信息。然后記住我們還必須支持選擇。還有鼠標(biāo)互動...

這當(dāng)然會讓人感到不知所措,在開發(fā)過程中,可能很難保持對哪些工作和哪些不工作的概述。而這正是我們覺得最初沒有contenteditable的工作很好的原因。我們自己做所有的事情,使我們非常清楚自己的位置。任何不工作的交互都是我們?nèi)匀恍枰獙崿F(xiàn)的。沒有什么會意外地工作,因為瀏覽器為我們解決了這個問題--瀏覽器在這里處于次要地位。

當(dāng)然,對于最終的版本,很難繞過使用contenteditable。這是因為如果沒有它,瀏覽器擴展將無法識別你的編輯器。而移動瀏覽器甚至?xí)B固地拒絕調(diào)出屏幕鍵盤......

手動差異化

所以我們確實需要contenteditable,但是還有一個問題。React不支持對已啟用contenteditable的元素的內(nèi)容進(jìn)行修補。這是有原因的:contenteditable基本上是告訴瀏覽器去玩吧。這就像一個沒有規(guī)則的操場。

React并不喜歡這樣。它依靠虛擬DOM來決定它需要如何更新實際的DOM,但當(dāng)瀏覽器可以在它不知情的情況下把地毯從它下面拉出來并更新實際的DOM時,這種方法就陷入了困境。這也是我們一開始就避免的原因。為了在更新我們的數(shù)據(jù)模型時能夠保留用戶的意圖(OT算法的一個重要方面),最好是了解導(dǎo)致任何變化的互動。但是,如果你試圖理解瀏覽器對DOM在內(nèi)容可編輯元素中的變化,你最多只能是猜測。

所以我們借鑒了React的玩法,實現(xiàn)我們自己的差異算法。但我們不是針對虛擬DOM進(jìn)行差分,而是在useLayoutEffect()鉤子函數(shù)中針對真實DOM進(jìn)行差分和修補。這相對簡單,因為我們的用例非常專業(yè),而且它還有一個好處,如果真實DOM中發(fā)生任何意外(可能是由于瀏覽器擴展),我們的算法將簡單地將視圖恢復(fù)到我們基于數(shù)據(jù)模型的預(yù)期。

雜項

上述所有內(nèi)容可能會讓你對編輯器的工作原理有一個較高的認(rèn)識,但魔鬼是在細(xì)節(jié)中的。下面是我們需要解決的一些小問題。

  • 支持Unicode。每個人都喜歡的標(biāo)準(zhǔn),但在工作中卻很麻煩。幸運的是,Rust有優(yōu)秀的unicode_segmentation板塊,對我們幫助很大。這幫助我們解決了一些問題,比如按字進(jìn)行光標(biāo)導(dǎo)航,以及確保光標(biāo)能正確地跳過字母群。
  • 光標(biāo)定位是很棘手的,但我們發(fā)現(xiàn)最好的方法是使用瀏覽器的Selection對象,并通過這種方式設(shè)置一個(透明的)本地光標(biāo)。然后我們使用getBoundingClientRect()來測量瀏覽器渲染光標(biāo)的位置,然后我們就可以在那里定位我們自己的光標(biāo)。
  • 組合事件被瀏覽器用來組成帶有重音的字符和處理拼音等輸入。不要忘記處理這些。

總結(jié)

創(chuàng)建你自己的富文本編輯器是一項艱巨的任務(wù),但只要有正確的架構(gòu)和良好的規(guī)劃,它肯定是可以做到的。如果你發(fā)現(xiàn)自己處于必須選擇或開發(fā)一個富文本編輯器的位置,我們希望你能發(fā)現(xiàn)這篇文章的有用信息。

注:特別感謝技術(shù)指導(dǎo)dazhao(趙達(dá))對本文翻譯的審閱指正。

作者:Arend van Beelen

原文鏈接:Creating a Rich Text Editor using Rust and React

以上就是Rust+React創(chuàng)建富文本編輯器的詳細(xì)內(nèi)容,更多關(guān)于Rust React富文本編輯器的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Rust-使用dotenvy加載和使用環(huán)境變量的過程詳解

    Rust-使用dotenvy加載和使用環(huán)境變量的過程詳解

    系統(tǒng)的開發(fā),測試和部署離不開環(huán)境變量,今天分享在Rust的系統(tǒng)開發(fā)中,使用dotenvy來讀取和使用環(huán)境變量,感興趣的朋友跟隨小編一起看看吧
    2023-11-11
  • Rust中字符串類型String的46種常用方法分享

    Rust中字符串類型String的46種常用方法分享

    Rust主要有兩種類型的字符串:&str和String,本文主要為大家介紹的是String類型的字符串以及它常用的46種方法,感興趣的小伙伴可以了解一下
    2023-06-06
  • Rust?Atomics?and?Locks?源碼解讀

    Rust?Atomics?and?Locks?源碼解讀

    這篇文章主要為大家介紹了Rust?Atomics?and?Locks?源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • Rust生命周期之驗證引用有效性與防止懸垂引用方式

    Rust生命周期之驗證引用有效性與防止懸垂引用方式

    本文介紹了Rust中生命周期注解的應(yīng)用,包括防止懸垂引用、在函數(shù)中使用泛型生命周期、生命周期省略規(guī)則、在結(jié)構(gòu)體中使用生命周期、靜態(tài)生命周期以及如何將生命周期與泛型和特質(zhì)約束結(jié)合,通過這些機制,Rust在編譯時就能捕獲內(nèi)存安全問題
    2025-02-02
  • 深入講解下Rust模塊使用方式

    深入講解下Rust模塊使用方式

    很多時候,我們寫的代碼需要按模塊組織,因為我們無法將大量的代碼都寫在一個文件上,那樣不容易維護(hù),下面這篇文章主要給大家介紹了關(guān)于Rust模塊使用方式的相關(guān)資料,需要的朋友可以參考下
    2022-03-03
  • Rust?中?Deref?Coercion講解

    Rust?中?Deref?Coercion講解

    Rust 的設(shè)計理念一向是顯式比隱式好,也就是說所有的行為盡量在代碼中表現(xiàn)出來,這篇文章主要介紹了Rust?中?Deref?Coercion?介紹,需要的朋友可以參考下
    2022-10-10
  • rust多個mod文件引用和文件夾mod使用注意事項小結(jié)

    rust多個mod文件引用和文件夾mod使用注意事項小結(jié)

    在 Rust 項目中,可以使用 mod 關(guān)鍵字將一個文件夾或一個 rs 文件作為一個模塊引入到當(dāng)前文件中,本文給大家介紹rust多個mod文件引用和文件夾mod使用注意事項小結(jié),感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • rust?中生成與使用protobuf的方法

    rust?中生成與使用protobuf的方法

    這篇文章主要介紹了rust中protobuf生成與使用,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-05-05
  • C和Java沒那么香了,Serverless時代Rust即將稱王?

    C和Java沒那么香了,Serverless時代Rust即將稱王?

    Serverless Computing,即”無服務(wù)器計算”,其實這一概念在剛剛提出的時候并沒有獲得太多的關(guān)注,直到2014年AWS Lambda這一里程碑式的產(chǎn)品出現(xiàn)。Serverless算是正式走進(jìn)了云計算的舞臺
    2021-06-06
  • Rust 能夠取代 C 語言嗎

    Rust 能夠取代 C 語言嗎

    Rust 是 Mozilla 基金會的一個雄心勃勃的項目,號稱是 C 語言和 C++ 的繼任者,這篇文章主要介紹了Rust 能夠取代 C 語言嗎的相關(guān)知識,需要的朋友可以參考下
    2020-06-06

最新評論

唐山市| 黑龙江省| 泗洪县| 青龙| 电白县| 唐河县| 丰都县| 永福县| 咸阳市| 台安县| 榆林市| 漾濞| 巩义市| 萨嘎县| 汶川县| 黄冈市| 托克托县| 松桃| 连云港市| 吴堡县| 德令哈市| 德令哈市| 凌海市| 兴业县| 吉安市| 日照市| 洞头县| 汶川县| 宜兴市| 德阳市| 克拉玛依市| 富川| 临潭县| 通州市| 兰溪市| 泾源县| 黄陵县| 合阳县| 扎赉特旗| 根河市| 灵丘县|