我是小斑,一個富文本編輯器,今天聊聊咱這編輯器的基本組成:組件!在個人世界裏:Everything is a Component.
大到一篇文章,小到一個字符,都是一個組件。javascript
3 個月前,阿飛也就是個人創造者,寫下第一行代碼前,思考過這樣一個問題:如何描述一篇文章?或者說:是什麼組成了一篇文章?對於這個問題,你們都有本身的答案:文字、段落、圖片、標題、表格、列表等等。但軟件開發須要嚴謹的邏輯,繼續往下思考,就會引出一些問題:html
在軟件開發上,有一準則:前端
任何一個複雜的問題,均可以拆解爲一個個小而簡單的組成。
所以,在寫下小斑的第一行代碼前,阿飛帶着這些問題,經歷整理歸類,類似內容抽象提取後,最終得出如下類型關係圖,並由此得出結論:文章也是一個組件,由內容塊(Block
)組成。java
雖然說圖片中有如此多的組件,按是否抽象,可分爲兩個陣營,抽象組件與具象組件。git
那就先聊聊抽象組件。github
這麼多的組件中,近一半是抽象組件,因爲具象組件是抽象組件的具象化呈現,充分了解抽象組件後,具象組件的含義就能夠輕鬆理解了。markdown
Component
組件是全部組件的基石,就像變形金剛裏的火種,是全部組件最基本的構成,Component
賦予了組件如下能力:編輯器
同時,做爲全部組件的基類,Component
還規定了組件必須實現的方法,或是必須明確的屬性:函數
type
屬性;render
方法,規定組件該如何渲染本身;具體實現:Component
。ui
Inline
表明一個行內的塊,是光標能夠操做的最小部分:字符、表情圖片、公式(實現中)都派生於 Inline
類。
Inline
類管理了行內塊與內容塊(Block
)之間的聯繫。
具體實現:Inline
。
Block
表明內容塊,是組成集合的最小單位。一篇文章就是一個 Block
的集合,列表、表格也是,同時列表、表格又是 Block
的派生類,那麼列表嵌套列表這種結構就能輕鬆的表示了。
那內容塊(Block
)須要實現哪些基礎操做呢?
Block
)之間的互相轉換;依據分工的不一樣,Block
組件又能夠派生出 3 類組件:PlainText
、Collection
、Media
。
具體實現:Block
。
PlainText
爲純文本組件,其內容爲純字符,表現與代碼編輯器一致,派生出 Code
組件。
具體實現:PlainText
。
Media
爲多媒體組件,具象組件,可生成圖片、視頻、音頻的內容塊,實現了 Block
規定的全部方法。
具體實現:Media
。
Collection
表明集合,爲一系列組件的容器,控制其子組件的呈現效果。
容器組件的主要工做就是對子組件的增刪改查。
依據子組件的類型,集合組件能夠拆分爲 Inline
的集合(ContentCollection
),與 Block
的集合(StructureCollection
)。
具體實現:Collection
。
ContentCollection
爲 Inline
組件的集合,包含一連串文字、表情圖片、行內公式。
根據使用場景的不一樣,派生出標題、段落、表格項組件。
具體實現:ContentCollection
。
StructureCollection
爲 Block
組件的集合,經過 StructureCollection
與 Block
的配合、嵌套,就能夠完整的表現出一篇文章、列表等。
根據使用的場景,派生出列表、表格、文章等組件。
具體實現:StructureCollection
。
通過一步步的抽象,抽象組件派生出的具象組件已不需編寫太多的代碼去實現相應的功能,但卻有一個最重要的方法,必須得本身實現:render
。
render
函數在Component
組件下定義,是組件對外呈現的途徑。
那如何進行 render
呢?簡單的生成 Html
?小斑的目標但是生成任意環境下的文章,包括但不限於 Markdown
、Html
, 但 render
方法只有一個,如何生成多變的內容呢?
內容生成器,就該登場啦!
道家哲學有一句話說的好:以不變應萬變!
對應到小斑的世界裏,不變的是文章的結構,變的是生成的內容,那如何以不變的內容,去生成不一樣的內容呢?
既然組件對渲染不一樣的結果無能爲力,那何不把渲染這個任務外包給專業的團隊呢?
查看如下代碼:
class XXX extends Component { render() { return getContentBuilder().buildArticle( this.id, this.conent, // 表明組件的內容 this.decorate.getStyle(), // 組件的樣式信息 this.decorate.getData() // 組件所攜帶的信息 ); } }
經過 getContentBuilder
獲取生成器,告訴生成器來個 Article
,而後把本身所持有的屬性,內容一通扔給生成器,生成什麼我無論,大手一揮,躺下喝茶!
ps:爲何不把組件給扔給生成器呢?你們都知道 JS
是一門高度動態的語言,只要獲取了原對象,就能夠對這個對象胡做非爲,爲了保證組件不被修改,爲了維護愛和正義,把生成組件所必須的內容交給生成器就能夠啦!天下太平 ~
這裏你們可能會問:爲何要經過 getContentBuilder
方法獲取生成器,生成器做爲參數直接傳入不能夠嗎?
能夠固然是能夠的,但試想,若是我須要渲染一篇文章,實際調用 render
的是 Article
組件,其餘組件經過層層遞歸的方式調用 render
函數,固然將生成器依次傳入每一個組件中能夠解決這個問題,可是耦合性過高,不利於開發與維護,所以爲了將組件的內部邏輯與渲染行爲完全的封開,把獲取生成器這個動做再次外包出去,實在是香的不行!
所以這裏其實有兩個外包(代理)動做:
buildArticle
的部分;getContentBuilder
方法;既然這裏用了雙重外包,這麼複雜的概念,好處在哪呢?且聽我細細道來:
getContentBuilder
是一個方法,它能夠返回不一樣的生成器;幾個已經實現的生成器:
ContentBuilder
:用於生成可編輯的 Html
結構,該類爲編輯器的核心類,是 Html
可編輯的核心;HtmlBuilder
:用於生成靜態 Html
文本,生成的文本不可編輯,純文本的 Html
;MarkdownBuilder
:用於生成靜態 Markdown
文本;BaseBuilder
:生成器抽象類,任一輩子成器必須繼承並實現該類下全部的方法;關於組件的部分,大體就是這樣,太細反而會照成理解上的困難,今天就到這兒啦。組件究竟是如何組成,如何渲染,其實並非最重要,由於阿飛已經都弄好啦,最重要的是在文中提到關於軟件開發的兩點,:
小斑的強大也正是因巧妙的使用這兩點哦!相信你們若是能完全 get
到這兩點的精髓,處理平常問題也會更駕輕就熟哦 ~
好啦,小斑課堂到這結束,但願你們多多使用斑碼編輯器哦!愛大家哦~
我是小斑,我爲本身帶鹽!