幕思城>電商行情>工具>淘寶軟件>打造Flutter高性能富文本編輯器——協(xié)議篇

    打造Flutter高性能富文本編輯器——協(xié)議篇

    2022-11-17|10:07|發(fā)布在分類 / 淘寶軟件| 閱讀:123

    閑魚作為一個(gè)二手閑置交易平臺(tái),賣家發(fā)布商品產(chǎn)出優(yōu)質(zhì)的供給尤為重要;商品發(fā)布器希望擁有富文本編輯能力,讓用戶簡(jiǎn)單便捷的方式產(chǎn)出更加優(yōu)質(zhì)的內(nèi)容;Flutter本身沒(méi)有富文本編輯器的能力的,只有最基礎(chǔ)的文本編輯器TextField;對(duì)于更加復(fù)雜的場(chǎng)景,比如支持自定義表情、主題、有序段落等能力,目前flutter組件是無(wú)法滿足我們的業(yè)務(wù)訴求,另外在交互體驗(yàn)上與Native仍然存在一定的差距;為了解決業(yè)務(wù)中面臨的以上問(wèn)題,我們決定設(shè)計(jì)并實(shí)現(xiàn)一個(gè)Flutter場(chǎng)景下高性能、可擴(kuò)展的富文本編輯器。

    富文本編輯器整體架構(gòu)設(shè)計(jì)

    首先我們來(lái)看一看整體的架構(gòu)設(shè)計(jì)分層:

    自下而上主要分四層:

    1. 1. 協(xié)議層:主要負(fù)責(zé)Model的定義、Selection描述、Commond事件邏輯處理,以及協(xié)議Normalizing校驗(yàn);
    2. 2. 能力擴(kuò)展層:能力擴(kuò)展層提供豐富的plugin能力,既有內(nèi)置的plugin,如:純文本轉(zhuǎn)換,undo/redo等能力,同時(shí)也非常方便的支持業(yè)務(wù)層自定義的擴(kuò)展,例如支持站外H5頁(yè)面展示的model to HTML的序列化plugin;
    3. 3. 渲染層:渲染層主要實(shí)現(xiàn)將富文本Model轉(zhuǎn)換成Flutter Widget渲染,以及光標(biāo)、選區(qū)、ToolBar等計(jì)算和渲染,以及用戶手勢(shì)交互事件等;
    4. 4. 業(yè)務(wù)擴(kuò)展層:在Mural的設(shè)計(jì)之初,可擴(kuò)展就是設(shè)計(jì)過(guò)程中非常重要的一部分,我們?yōu)闃I(yè)務(wù)方提供了非常靈活、功能強(qiáng)大的擴(kuò)展能力,通過(guò)自定義Node、Plugin、Normalizing,實(shí)現(xiàn)如自定義表情、主題、段落、語(yǔ)法高亮等能力;
    協(xié)議層設(shè)計(jì)

    富文本編輯器對(duì)大家來(lái)說(shuō)并不陌生,發(fā)展至今,已經(jīng)涌現(xiàn)出非常多有優(yōu)秀的開(kāi)源富文本編輯器;當(dāng)我們想要做Flutter富文本協(xié)議的時(shí)候,第一個(gè)想法就是先了解優(yōu)秀的開(kāi)源富文本編輯器方案,避免閉門造車;

    目前比較優(yōu)秀的開(kāi)源富文本編輯器,如CKEditor、Quill、Prosemirror、Draft、Slate等等;在了解和對(duì)比過(guò)后,我們決定使用Slate作為我們的富文本編輯器的協(xié)議;

    why Slatejs

    我們?yōu)槭裁催x擇Slate?

    插件是一等公民,能夠很好的滿足我們對(duì)于擴(kuò)展性的要求;

    Slatjs在設(shè)計(jì)上支持嵌套結(jié)構(gòu),可以滿足復(fù)雜的業(yè)務(wù)場(chǎng)景;

    與Dom相同的Data model,對(duì)于后面flutter渲染層的實(shí)現(xiàn),也變得更加方便;

    直觀的指令設(shè)計(jì),能夠非常好的支持plugin的自定義擴(kuò)展;

    Slate在設(shè)計(jì)上,協(xié)議層與渲染層是有明確的核心劃分,這讓我們可以復(fù)用Slate協(xié)議層的設(shè)計(jì),渲染層交給flutter來(lái)處理;

    除了上面的原因,我們選擇Slate另外一個(gè)很重要的原因,就是它的單元測(cè)試覆蓋率和完整度,讓我們對(duì)它的穩(wěn)定性更有信心;

    Slate協(xié)議層設(shè)計(jì)

    協(xié)議層的整體架構(gòu)設(shè)計(jì)如下圖:

    下面我們就以Slate為例,來(lái)看一看富文本編輯器的協(xié)議層設(shè)計(jì),需要定義的核心概念和模塊:

    1. 1. 嵌套Model定義;
    2. 2. 原子能力Operation設(shè)計(jì);
    3. 3. 秩序維護(hù)者Normalizing的設(shè)計(jì);

    協(xié)議層設(shè)計(jì)——嵌套Model設(shè)計(jì)

    Slate定義了三種類型的Node節(jié)點(diǎn):

    • • Editor:包含整個(gè)文檔內(nèi)容的根節(jié)點(diǎn);
    • • Element:在自定義域中擁有語(yǔ)義的容器節(jié)點(diǎn);
    • • Text:包含文檔文本的葉子節(jié)點(diǎn);

    Editor

    Editor抽象接口定義如下:

    Element

    Element節(jié)點(diǎn)比較特殊,既是Ancentor節(jié)點(diǎn),作為容器節(jié)點(diǎn)包含子節(jié)點(diǎn);同時(shí)又是Descendant節(jié)點(diǎn),可以作為其他容器節(jié)點(diǎn)的子節(jié)點(diǎn)存在。

    • • 塊(Blocks):Element默認(rèn)為Block類型的節(jié)點(diǎn),也就是獨(dú)立的一個(gè)段落;在Slate協(xié)議設(shè)計(jì)中,一個(gè)段落是不允許存在換行符的,當(dāng)輸入換行符的時(shí)候,就會(huì)生成一個(gè)新的Block類型的Element;
    • • 行內(nèi)(Inlines):同時(shí)Element也可以是Inline類型的節(jié)點(diǎn),作為另外一個(gè)Element的嵌套子節(jié)點(diǎn)存在,作為行內(nèi)元素渲染在一行;
    • • 空元素(Void):Element也可以是Void類型,這里Void與HTML中Void的是同一個(gè)概念:如果某個(gè)Node為Void,則表示這個(gè)Node節(jié)點(diǎn)是不可編輯狀態(tài),光標(biāo)無(wú)法定位到節(jié)點(diǎn)內(nèi)部,會(huì)被整體輸入和刪除;比如:@某個(gè)人、主題、富文本中的圖片或者視頻等等;

    Text

    Text節(jié)點(diǎn)是樹(shù)中的最低級(jí)葉子節(jié)點(diǎn),描述了文本內(nèi)容以及其他自定義的渲染元素;所有的自定義屬性都包含在properties屬性中:

    我們以下面這這段富文本為例:

    最終這樣一段富文本對(duì)應(yīng)的Mode定義如下:

    可以看到,Model的樹(shù)形結(jié)構(gòu)還是比較簡(jiǎn)單的,所有的屬性都存放在properties字段中,這也非常方便實(shí)現(xiàn)自定義擴(kuò)展;Flutter渲染層根據(jù)Node節(jié)點(diǎn)的Type以及properties屬性,將富文本內(nèi)容渲染到屏幕上;

    協(xié)議層設(shè)計(jì)——原子能力Operation

    接下來(lái)需要富文本Commond協(xié)議的設(shè)計(jì),用戶的每一次的文字輸入、刪除、文字加粗、換行等操作都是一次Command指令;Slate抽象定義了九個(gè)最基本的Operations,協(xié)議層所有的Commond指令,最終在協(xié)議層,都會(huì)轉(zhuǎn)換成一個(gè)或者多個(gè)operation操作:

    • • insert_node:插入Node節(jié)點(diǎn);
    • • insert_text:插入文本;
    • • merge_node:合并相同屬性的Node節(jié)點(diǎn);
    • • move_node:移動(dòng)Node;
    • • remove_node:刪除Node;
    • • remove_text:刪除文本;
    • • set_node:設(shè)置Node屬性;
    • • set_selection:設(shè)置Selection;
    • • split_node:拆分Node;

    下面我們通過(guò)對(duì)選中文本加粗操作為例,來(lái)了解Slate協(xié)議層Commond的處理過(guò)程:

    對(duì)選中文本加粗這樣一個(gè)Commond,協(xié)議層會(huì)將這個(gè)Commond拆解成三個(gè)Opeartion:

    • • split_node:將一個(gè)Text Node拆分成三個(gè)Text Node;
    • • set_selection:更新光標(biāo)選擇區(qū)域Selection;
    • • set_node:設(shè)置需要加粗Text Node節(jié)點(diǎn)properties的加粗屬性;

    當(dāng)一個(gè)Commond被協(xié)議層拆分成一個(gè)或者多個(gè)Opeartion執(zhí)行之后,會(huì)執(zhí)行一個(gè)非常重要的操作——Normalizing;

    秩序維護(hù)者——Normalizing

    每一次Command操作,絕大部分情況會(huì)對(duì)Model進(jìn)行相應(yīng)修改;我們需要一個(gè)秩序維護(hù)者——Normalizing,時(shí)刻保證對(duì)協(xié)議Model修改過(guò)之后,保持?jǐn)?shù)據(jù)結(jié)構(gòu)的正確性;

    Slate定義了幾個(gè)基本的內(nèi)置Normalizing規(guī)則:

    每一次Commond之后,Editor都會(huì)調(diào)用normalizeNode方法,在Normalizing的過(guò)程中,發(fā)現(xiàn)存在協(xié)議結(jié)構(gòu)錯(cuò)誤,需要進(jìn)行錯(cuò)誤修復(fù);

    Normalizing的另一個(gè)強(qiáng)大之處在于,我們可以通過(guò)自定義Normalizing,添加自定義的校驗(yàn)規(guī)則,實(shí)現(xiàn)自定義的需求;在后面的業(yè)務(wù)擴(kuò)展章節(jié)會(huì),我們會(huì)具體講解如何通過(guò)自定義Normalizing快速實(shí)現(xiàn)一個(gè)自定義主題的能力;

    總結(jié)

    目前Mural已經(jīng)在閑魚商品發(fā)布、商品詳情、消息等場(chǎng)景落地,支持了自定義表情、主題等業(yè)務(wù)能力,用戶體驗(yàn)方面也有了非常大的提升。

    本次主要介紹了富文本編輯器Mural整體的架構(gòu)設(shè)計(jì)以及協(xié)議層的設(shè)計(jì);后續(xù)我們會(huì)系列文章的方式介紹Mural在渲染層的設(shè)計(jì)、自定義擴(kuò)展設(shè)計(jì),以及交互體驗(yàn)、性能方面的優(yōu)化實(shí)踐,敬請(qǐng)期待!

    參考鏈接:

    [1] Slate:https://github.com/ianstormtaylor/slate

    這個(gè)問(wèn)題還有疑問(wèn)的話,可以加幕.思.城火星老師免費(fèi)咨詢,微.信號(hào)是為: msc496。

    難題沒(méi)解決?加我微信給你講!【僅限淘寶賣家交流運(yùn)營(yíng)知識(shí),非賣家不要加我哈】
    >

    推薦閱讀:

    淘寶直播在哪里看?怎么看淘寶直播?

    手機(jī)上如何修改/刪除評(píng)價(jià)?

    著作權(quán)-阿里巴巴旗下網(wǎng)站和其他網(wǎng)站和天貓代銷申訴要求

    更多資訊請(qǐng)關(guān)注幕 思 城。

    發(fā)表評(píng)論

    別默默看了 登錄\ 注冊(cè) 一起參與討論!

      微信掃碼回復(fù)「666