標籤:

San - 一個傳統的MVVM組件框架

原文地址:San - 一個傳統的MVVM組件框架

前置聲明:這個框架是 @董睿 大神開發的,我只是轉發一下文章而已。

------ 分割線 ------

這一年多來,其實受到過不少質疑,比如「咦,你們又在發明輪子了?」。每當此時我只能嘿嘿嘿一笑,畢竟你做的東西看起來還只是個垃圾而已,而看起來我們有很多成熟的東西可以選了:Vue、React、Angular、Polymer等等。在今天,我們覺得 San 經過了一些項目的驗證(踩坑)和進化(填坑),能夠出來見人時,我們打算出來說說為啥要造輪子,造的是個啥樣的輪子。

為什麼要做 San

MVVM 並不是什麼新鮮事物,在 Web 上的應用我們也遠不是先驅。從幾年前,我們有些團隊在 Angular1 開始一些實踐,也有些團隊接觸了 React,但是讓我印象最深刻的還是 Vue,並不是因為多高深的技術,而是因為真的「好用」。我們在一些要求不那麼高(兼容性、性能等)的應用中實踐一些流行技術,並享受一些便利。將近2年前,我們對實踐過的東西進行了一些總結,有些東西已經比較常識了:

  • 組件化
  • 聲明式視圖
  • view=f(data)
  • 數據到視圖的渲染引擎
  • 非同步渲染
  • ……

但是由於 IE8- 佔有率仍然可觀,在 to C 的應用中,我們只能老老實實的 JQuery、挨個 DOM 操作。兼容性是橫在我們面前最大的問題。隨著時間流逝,總有一天兼容性將不再是問題,但你真的要等到幾年後所有落後都淘汰嗎?任何時候我們都會發現有一些東西將要被淘汰,有一些東西將要來,但如果你站著不動,還不如去當一塊叉燒咯。

說白了,不折騰會死的精神讓我們開了新坑,初衷僅僅是因為 兼容性 ,這種看起來不大又可笑的理由。但是它確實繞不開,它也可能會帶來其他問題,比如移動端和PC端無法使用相同的組件架構。

為什麼叫 San

在 2010 年左右,為了應對 SPA 類型的各種業務系統,我們寫了個 MVC 的框架叫 ER(Enterprise RIA),看起來是個 2。主席Justineo說既然新坑要比老坑更先進,那就叫 3 吧。一幫起名困難症患者覺得貌似很有道理,於是就這麼定了。

所以 San 不是什麼的縮寫,就是 3 而已……雖然名字很隨意,但是造輪子的過程我們是認真的

把 San 做成什麼樣

既然要自己做了,那我們希望完整的表達我們的想法和原則,不是東拼西湊的追隨。

怎樣都能用

你想怎樣引用一個 Library?

  • 直接下下來
  • npm install
  • CDN

產品開發是什麼環境?

  • 啥都沒,裸的,怎樣開發怎樣上線
  • 有些簡單的 bundle 和 compress
  • 模塊化管理,不過是古老的 AMD
  • 主流代表,WebPack + Babel

我們不關心你從哪裡來,要到哪裡去,我們只想給你提供一個舒適的港灣。這個廣告詞是不是噁心到大家了…… 怎樣都能用 確實是我們的目標,提供 CDN、支持 AMD 和 Global Object、npm publish 也都是很簡單的事情,更難抉擇的是 「你們怎麼解決兼容性問題」。

通過方法操作組件數據,解決兼容性

在 San 組件中,對數據的變更需要通過 set 或 splice 等方法。數據操作文檔詳細描述了這一點。這意味著:

  • 用最簡單的形式,解決兼容性問題
  • San 的開發體驗不可能做的比 Vue 更好
  • 數據操作的過程可控。實際上,從 3.1.0 開始,數據變更在內部是 Immutable 的
  • change tracking好做了。我們並不認為 v-dom 是萬金油,並且 San 是面向 Web 設計的,我們並沒期望它跨平台。所以少掉 v-dom 這一層是一件好事

我們也考慮過讓使用者自己通過 Immutable 的方式操作數據,然後再懟回來,但這樣對使用者的成本會變高,而且使用者未必會理解為啥要這樣干,所以還是封起來了。

this.data.set(user, userName);// vssetData(Object.assign({}, data, {user: userName}));

但是,把數據封起來意味著獲取數據成本也變高了,特別是想一次獲取多個數據的時候。所以我們把獲取數據的 get 方法實現為,無參的時候返回整個數據對象,如果你用 ESNext 開發可以方便的使用解構。但是,操作數據還是要通過 set 或 splice 等方法的。

let {name, email} = this.data.get();

組件形態

雖然我們很欣賞 Vue,但是我們並不認同 component = data。在 Vue 中,數據直接置於組件下,methods被規約。

new Vue({ el: #example-3, // methods被規約 methods: { reverseMessage: function () { this.message = this.message.split().reverse().join() } }})

我們更習慣 method 直接置於組件下,數據被規約(其實已經被封裝)。

san.defineComponent({ template: <div>...<button type="button" on-click="submit">submit</button></div>, // method 直接置於組件下 submit: function () { var title = this.data.get(title); if (!title) { return; } sendData({title: title}); }});

不過,這是一個理念問題,並沒有誰好誰壞。

組件聲明

我們認為組件應該是一個 class(不要較真,就是 function)。在 ESNext 中,我們可以利用 extends 構造組件之間的繼承關係。這樣看起來更自然。

import {Component} from san;class HelloComponent extends Component { static template = <p>Hello {{name}}!</p>; initData() { return {name: San} }}

ESNext 是無法聲明 prototype property 的。所以,對於 template / filters / components 等屬性,San 提供了 static property 的支持。

對於不願意使用 ESNext 的產品,我們提供 defineComponent 方法,能夠方便快捷的聲明組件。

var HelloComponent = san.defineComponent({ template: <p>Hello {{name}}!</p>, initData: function () { return {name: San} }});

組件反解

我們希望 San 能夠從帶有特定標記的 HTML 中,解析出組件結構來,通過組件來響應和管理後續的用戶交互等操作,我們管這事叫做 組件反解。 (什麼,你說叫反序列化?也行啊,開心就好)

  • 後端直出 HTML 在首屏時間是有優勢的,特別是內容為主的應用
  • 使用 NodeJS 提供在線 Web 服務不一定在任何地方都行得通,至少在我廠很多地方是行不通的。NodeJS 也不是萬金油

所以我們先制定了 特定標記 的協議,基於此實現了組件反解的功能。後來實現的 NodeJS 服務端渲染功能也是基於 組件反解 的,輸出符合協議的 HTML。

另外,對於服務端渲染,恐怕大家最關心的是性能。San的服務端渲染經過測試,比號稱最快的 JS 模板引擎 art-template 慢30%-40%,慢的部分主要是因為要額外生成前端可被辨識和反解的標記。已經是 string-based 模板引擎的性能級別了。

10k

在各種 Library 不在乎體積的今天,大體積的副作用其實並不少,除了網路傳輸以外,移動端 JS Parse 的時間其實並不可忽視。所以我設定了個目標,不包含開發調試支持的版本,GZip 後體積不能超過 10k。為什麼是 10k 呢,拍腦袋而已,可能是 mission impossible,不去試試誰知道呢?

最開始的簡陋版本確實不太大,但是由於增加兼容性的處理、增加新 feature、代碼拆分,讓我們不止一次體積超過 10k。每次回頭去找代碼有什麼可以優化的地方,到後來可優化的地方越來越少,也差點被當成強迫症患者送去醫院。不過到最後竟然真的做到了。

其實這也不是什麼很有技術含量的事情,為此我們直接手寫 ES5 代碼而不是 ESNext + Babel,在很多人看來還是挺 low B 的。具體是不是 10k 也沒什麼意義,只是態度而已。我們希望 San 的使用者不會受到體積的困擾,我們也希望體積強迫症患者能有更多的選擇。

性能

在我們剛開始做 San 的時候,很多流行的方案還是有一些性能問題的(比如Angular的更新、Vue的初始渲染等等)。但是世界變化那麼快,1年多過去了,現在大家的性能其實都還不錯,誰比誰笨呢?San 的性能也還不錯,但也沒有一騎絕塵,大家都差不多,不同場景也各有優勢。感興趣的同學自己測吧。

還有些什麼

應用狀態管理

這年頭,一個方案里沒有應用狀態管理,別人看你都像殘廢。所以我們提供了 san-store。它還是有自己的特點的:

  • 名字有特點。在大家都叫 nnnx 的時候,我們希望傳達 store 做為全局唯一的應用狀態源的觀念,就叫 store了
  • 抽象有不同。我們還是希望盡量好用好理解。redux 的模式我們嫌煩瑣,為了假裝有節操又不能抄 vuex,所以我們提供了更簡單的抽象,只有Action。
  • 應用狀態數據的操作,我們通過 san-update 完成

router

這也是一個沒有就殘廢的東西,但想想也沒啥好說的,有需要的自己看吧。san-router

組件庫

組件庫是減少實際業務開發工作量、解放生產力的根本。

  • Material Design 是認知度比較高的一套視覺體系,我們基於它開發了一個MUI組件庫。
  • WeUI 是微信輸出的、Mobile 友好的一套視覺體系,但是目前沒有 San 的實現,歡迎感興趣的同學來一套,質量好的話我們會在官網推薦喔。
  • AntDesign 是螞蟻輸出的、適合各種管理系統的一套視覺體系,但是目前沒有 San 的實現,也歡迎感興趣的同學來一套,質量好的話我們會在官網推薦喔。
  • 如果你的應用擁有自己的視覺體系,自己開發組件庫是免不了的

曾經有人和我說,你們應該推自己的組件庫啊,其實大家做應用的時候並不 care 是什麼,只要好看好用就行。可是我廠是沒有自己的視覺交互體系的,我能怎麼辦,我也很絕望啊。

DevTool

DevTool 在寫這篇廣告的時候還沒有 ready,快了,一周以內吧。請關注 San WebSite

最後

到這裡,應該不難看出,San 有一些 傳統 的地方:

  • 還在兼容 Old IE
  • 還在考慮不使用 babel transform 的業務開發場景
  • 還在為體積糾結
  • 還想保持後端無關,而不是推 NodeJS

如果拿車來比喻,我們想造的是一台陸巡。相比轎車甚至多數SUV,它沒有那麼好開,看不到很多 2.0T 的車尾燈;相比牧馬人和 benz G,他越野能力和通過性也沒那麼強。但是它很可靠,能穩穩噹噹、舒適地帶你到任何想去的地方。

既然你都能有耐心看到這,不介意關注下? ^_^

ecomfe/san


推薦閱讀:

組件化必殺技:styled-components 簡明教程【附視頻下載】
有關Bootstrap你想要知道的都在這裡
React 16 帶來了什麼以及對 Fiber 的解釋:特性概覽 — 萬眾期待的 React 16
【React/Redux】深入理解React服務端渲染

TAG:前端框架 |