如何評價Knot.js?
它的應用場景有哪些?
---------------------------------------------------knot.js官網: Knot.js中文簡介: (博客園首發)開源框架knot.js正式發布了,為前端開發帶來全新的開發方式--CBS
我是來潑冷水的……
由於數據綁定實現方式本身其實不外乎那些方法,這就不說了。這裡主要談一下和其他框架完全不同的核心設計:CBS。
CBS的方式把數據綁定從HTML結構中單獨抽離,我們拋開語法不說,從選擇器指定綁定內容這方式來說,其實不難看出和jQuery是很像的。但是jQuery沒有數據綁定。從這點上說,也許把CBS直接變為一個支持綁定的jQuery更為直接和容易被人接受和使用。
如果拋開數據綁定所提供的數據變更自動刷新的巨大進步,CBS跟一個用jQuery構建頁面的方式是差不多的。
那麼jQuery的問題在哪裡?如果只是用jQuery來附加一些通用行為,基本沒什麼問題,因為耦合點只是少數的tag/class。但是如果複雜的視圖是用jQuery從數據構建,我相信很多人會發現,jQuery的DOM操作實際上是和HTML結構耦合的,因為你的耦合點不再是少數的tag/class了,而是包括大量的嵌套結構。同理,CBS表面上解耦,實質上並不能真的達到解耦的目的。反而拆解後帶來了成本,比如兩部分代碼中的耦合點必須同步,以及如何理解最終完整的html結構的心智負擔。
其實用selector來解耦不同層次在有些地方我是非常推薦的,比如我用css預處理器提供一堆mixin作為樣式抽象,然後用選擇器將html結構與具體的樣式抽象實現結合(即一份只有選擇器調用mixin和少量附加樣式的樣式表)。
為什麼我認為用在樣式上是恰當的,用於數據綁定就不恰當?
因為樣式是從屬於html的(至少在我的工作流程里是這樣),這樣變更方向是單向的,也就是html變更可以引起樣式表變化,樣式表不會反向引起html變更,而樣式抽象(那些mixin)變化是會影響樣式表,但因為樣式表的隔離作用而不會反向影響html。
然而,數據綁定並不是單向的,html結構變更會引起CBS的變化,數據結構變更也會引起CBS的變化,並引起html的變化。
實際上在React這樣的框架里,特彆強調是數據決定了html,和樣式依賴的方向正好相反。既然本來就是單向計算的函數,直接計算出結果就好了,用selector將html結構分離出來就並沒有什麼卵用。
從React、Angular或類似框架來看,複雜web應用的應對之道基本認識一致,就是數據綁定+組件。其實在刨除了CBS的html里我們仍然可依稀看到組件的形式(即模板定義),但是我們看不到組件和數據的關係,也看不到組件之間的調用關係,這些信息被抽到了CBS里。在CBS里我們可以看到數據如何被使用,可以看到調用關係,但是看不到完整的html結構。這使得我們無法容易的形成一致的組件觀念。從目前Knot的demo代碼來看,也看不到有其他能更好替代組件的機制。
以上僅根據目前看Knot文檔的印象寫成,不確切之處請見諒。
(注意,我不是說Knot不好,只是不夠好而已。話說用selector機製做額外一次分解的思路,其實pure這個模板也用了,似乎也沒多少人用。而我自己會在最近的項目更新里去掉jQuery,但暫時還無法上數據綁定的組件框架,所以會用類似的指定模板,以選擇器注入數據的方式使用。這一方式其實差不多等價於沒有數據綁定的Knot。也許等項目完成後可以更好的評價這種方式。)瀉藥。
呃,沒想到知乎上這麼快就出現了關於knot.js的問題,我還籌劃著回頭提一個問題自問自答來做廣告呢。 謝謝題主關注knot.js,也歡迎各位探討Knot.js的方方面面。歡迎潑涼水,你的涼水可能會變成Knot.js改進的新方向。:)
Knot.js解決的問題和AngularJS,KnockoutJS等一樣,都是為前端的MVVM結構提供數據綁定支持。大幅簡化複雜UI開發工作。
說Knot.js 首先就繞不開CBS。
CBS的設計目的是為了將夾雜在HTML中的綁定邏輯提出來,變成一種更易讀易寫易維護的形式。實用之後效果非常不錯。尤其在學習了LESS的做法加入嵌套之後,CBS會自然形成和HTML一致的結構,可維護性非常好。其實knot.js也支持和其他框架一樣,將綁定邏輯寫在HTML標籤里,但經過我自己近一年時間的使用之後,我認為獨立的CBS塊和CBS文件的可維護性要好得多。因此在Knot.js的官網的介紹和DEMO中,我把重點完全放在CBS身上,並且把CBS視為Knot.js對其他框架最大的優勢。Knot.js另一個亮點就是它的數據感知系統
AngularJS的前輩在開發的時候必須考慮早期版本的IE兼容問題,因而採取了很多措施才實現了數據的雙向綁定。這也帶來一大堆其它的問題,導致甚至setTimeout這樣的東西都得有一個angularJS自己的實現,讓用戶的學習曲線頗為陡峭。Knot.js開發的時候就不考慮兼容ie8以下版本。藉助Object.defineProperty, Knot.js得以實現了一個自動數據感知系統,這個系統讓Knot.js對與之綁定的數據幾乎沒有任何要求,你完全可以按自己的喜好用自己最習慣的方式編寫model/viewmodel。knot.js會自動偵測和它們的變化,按照你的要求刷新UI。 因此,Knot.js上手非常容易,你舊有的Javascript編程知識全都有效。Knot.js其餘的亮點我就不細說了,Debugger,小文件,性能好等等都可以算是,大家可以看官網或者我的博客。我是把knot.js按照我理想中的綁定框架設計的,到目前為止我當初的多數目標都已經達到。我相信Knot.js能夠給前端開發者提供很不一樣的開發體驗,請花點時間看一看,看完之後我相信你不會覺得是在浪費時間。
Knot.js才剛剛發布,請各位支持,幫助推廣。有精力有意願的朋友歡迎加入這個項目,Knot.js在Github上的地址在這裡:https://github.com/alexzhaosheng/knot.js, 官網在Github上的地址在這裡:alexzhaosheng/knot_website · GitHub 歡迎fork,歡迎push
----------------------
這是和 @賀師俊 的討論,知乎評論里放不下,就放到這裡了:謝謝冷水, 你的觀點很有啟發性。 不過我想針對其中的幾個要點再討論一下:
有一個小的誤會,CBS的設計目的並不是解耦。實際上CBS(包括很多綁定框架)的設計的出發點之一就是意識到必然有一部分貼近UI的邏輯無法和UI解除耦合(本來UI就是帶邏輯的),因而走另一條路解決UI邏輯的維護問題,即提供一種更直接,更簡單易讀,更貼近UI的方式寫這些邏輯。CBS所做的努力,無非就是把以往嵌入在HTML標記中的邏輯提取出來而已。這些邏輯和HTML本身依舊是強耦合的。 當然因此會帶來一個新問題即你提到的CBS和HTML同步的問題,但一旦你意識到CBS帶來的好處,我相信你會同意在這個問題上花費一點額外成本是值得的:CBS所提供的UI邏輯編寫方式用起來會很舒服,所以實用中你會把大量的UI邏輯直接用javascript寫在CBS中,這會讓CBS和HTML嵌入標記在使用上產生本質的不同。你不止在HTML端會有一個清秀的結果,連你的Javascript都會因此清秀很多。要知道UI相關的代碼往往就是設計上壞味道的元兇。而在CBS這一端,因為CBS在邏輯上會保持和HTML一致嵌套的結構,而且CBS內只包含動態的元素(往往也就是最重要的元素),所以最後CBS常常會變成一份HTML的提綱,可維護性其實相當不錯。CBS中的JS邏輯都直接被貼在和他強關聯的UI元素上,這種代碼組織方式實際用下來還是很舒服的, 最少比直接用jquery寫要直觀易懂易修改多了。
另外你的一個論點我認為有待商榷。的確數據結構變化會導致CBS和HTML的變化,但這裡的根本原因是因為數據結構變化,無論你用不用CBS,HTML都必然因此發生變化。CBS實際上是只是隨著HTML變化而已,和CSS並無不同,並不存在CBS導致HTML變化的情況。
關於組件方面,CBS實際上有完整的組件機制,我在knot.js首頁並沒有著重提,只是在tutorial中專門講了一章組件。歡迎去了解,也很期待你在這方面的意見。 CBS的復用不止可以通過組件。CBS組件是通過一個包含HTML資源的CBS私有包實現的,我自己在做項目的時候,甚至會直接使用私有包來把一些在項目中有相當的通用性而又不值得包裝為組件的東西完成復用。這玩意兒很有意思,就像一個可綁定的動態模板,我現在就正在籌劃怎麼發揮CBS私有包的威力做出更酷的功能。看到這個問題好幾天了,剛看到的時候,跟knot的作者alex的感覺是一樣的,深感吾道不孤,對於分離綁定的模板機制,看來還是有同路人的。我不知道alex是基於什麼緣由開始考慮並著手開發knot來實現這種分離綁定的機制,也不知道他的knot有沒有實際的項目實踐來支撐他的理想,但就我而言,我手上現時維護著兩個支持這種綁定分離模型的框架,一個後端基於java的web框架Asta4D,一個前端基於js的MVVM框架asta4js,都是服務於我們現時正在運行的線上服務的。我在其他回答中強調過很多次,雖然asta4js這個前端框架是今年才開始開發的,但我們在後端的Asta4D框架中實踐這種分離綁定模型已經超過3年了,因此,對於這種分離綁定的模板機制,究竟有沒有意義,我認為,我是可以給出非常深刻的體會的。@賀師俊和 @尤雨溪兩位大神都對這種模式提出了自己的疑慮(或者說批評),我來分別闡述一下我的體會和看法。
尤雨溪
我先引用一句原文: 「乾淨的 HTML」 的唯一好處就是方便對 JS 一點也不懂的設計師改 markup 或者寫樣式。我對這句話的看法是, 這句話基本正確,但需要去掉「唯一」二字。 這樣的好處是必然存在的,但實際上更大的好處在於,頁面的表達形式,與頁面的數據邏輯分離之後,表達形式(視覺和交互設計)的調整就變成了可以獨立進行的工作,尤雨溪認為將項目架構應該按照組件來劃分,但是即使按照組件劃分之後,一個網站在對頁面進行跟數據邏輯無關的重構的時候,即使對於一個負責獨立組件的全棧工程師而言,大規模的HTML結構變更後,要保證原有的數據綁定不出現問題也是一項及其繁瑣的工作,大量的循環需要重寫,大量的if else需要調整到正確的位置,等等等等不一而足,而且,顯而易見的,在設計和開發分離的團隊模型中,這些繁瑣的工作會變成更大的麻煩。但是,在分離綁定的模板模型下,這些繁瑣的工作都不復存在,工程師只需要在「乾淨的」HTML上自由的進行重構工作,所有的數據邏輯,都會由分離的綁定聲明保證正確性。我們三年多的實踐表明,超過90%的頁面重構,不需要修改頁面的綁定代碼,剩下的10%當中,超過9.9%的部分,可以在5分鐘內修復broken的綁定關係,因為大多數被broken的代碼,在數據本身的邏輯沒有變更的情況下,一般只需要重寫綁定目標的selector即可,少數特殊情況可能會變更綁定方式,比如原來是寫innertext的要變成寫attr或者其他什麼鬼,但即使如此,因為所有的數據生成,修飾和判定邏輯是不變的,因此,修改綁定聲明對我們來說,是一件幾乎不需要成本的事情。就不專門開個回答了,順道說兩句。綁定分離類的設計經常拿 「乾淨的 HTML」 作為賣點,但乾淨的 HTML 到底有多少價值,其實是個見仁見智的問題。
在我看來 「乾淨的 HTML」 的唯一好處就是方便對 JS 一點也不懂的設計師改 markup 或者寫樣式。在有這樣的訴求的團隊環境中,綁定分離類方案還是有其價值的。但是,並不是每個團隊都是這樣分工的。實際上,我認為這樣的分工本來就不是一個合理的分工。現在的項目架構已經很少按照文件類型來安排文件結構了,都是按照模塊或者組件分,而樣式一部分是全局的,另一部分是跟著組件走的。基於這樣的組織方式,更自然也更有效率的分工方式就是一個組件歸一個人管。在這樣的前提下,分離 markup 和綁定邏輯就失去了其意義,反而增加了閱讀和維護成本。
我承認,看到這裡,很多人會對我那個90%的數據表示懷疑,因為既然前提是大規模的頁面重構,伴隨著大規模的HTML結構變更,綁定錨點怎麼可能保持穩定?我記得尤雨溪曾經在我的某個回答下面問過我:設計師亂改class怎麼破?賀師俊同樣表達了如下疑問:
對於這個問題,我們在最初實踐這種模型的時候也遇到了同樣的困惑。我相信任何對這個模型進行深入思考的人都會意識到這個問題,同樣,這恐怕也是很多人不看好這個模型的原因。我不得不承認,如果我一開始就是單純的思考這個問題的話,我也會作出同樣的判斷並放棄這個模型,其實,我們公司的新員工剛接觸我們的框架的時候,也是同樣的驚奇:我的天,HTML結構變了我不就傻B了。。。很有趣的事情是,我們為什麼沒有放棄,原因很簡單,我們當時沒有選擇,最早這個模型是由Lift提出來的,我們當時用Lift進行開發,遇到這個問題,我們只有兩個選擇,要麼,在scala代碼裡面混寫HTML,要麼,忍受這種變更的風險。我們決定忍受風險,但其實一旦進入開發實踐,事情就變得非常簡單了,我們很快就發現了迴避風險的辦法,而且事實上我們事後來總結的話,這恐怕是這種綁定分離模型唯一正確的使用方法。我們來看看我們在user guide裡面總結的最佳實踐:那麼jQuery的問題在哪裡?如果只是用jQuery來附加一些通用行為,基本沒什麼問題,因為耦合點只是少數的tag/class。但是如果複雜的視圖是用jQuery從數據構建,我相信很多人會發現,jQuery的DOM操作實際上是和HTML結構耦合的,因為你的耦合點不再是少數的tag/class了,而是包括大量的嵌套結構。同理,CBS表面上解耦,實質上並不能真的達到解耦的目的。反而拆解後帶來了成本,比如兩部分代碼中的耦合點必須同步,以及如何理解最終完整的html結構的心智負擔。
其實用selector來解耦不同層次在有些地方我是非常推薦的,比如我用css預處理器提供一堆mixin作為樣式抽象,然後用選擇器將html結構與具體的樣式抽象實現結合(即一份只有選擇器調用mixin和少量附加樣式的樣式表)。為什麼我認為用在樣式上是恰當的,用於數據綁定就不恰當?因為樣式是從屬於html的(至少在我的工作流程里是這樣),這樣變更方向是單向的,也就是html變更可以引起樣式表變化,樣式表不會反向引起html變更,而樣式抽象(那些mixin)變化是會影響樣式表,但因為樣式表的隔離作用而不會反向影響html。然而,數據綁定並不是單向的,html結構變更會引起CBS的變化,數據結構變更也會引起CBS的變化,並引起html的變化。
"x-" convention
Since we are always assuming that the designers will change the page radically, the binding target selector may become invalid after page structure refactoring. To combat this issue, we introduced so called "x-" convention in our practice. We always add a css class starting with "x-" to the binding target DOM, which tells the designers try their best to keep the compatibility against the "x-" marked elements when they are refactoing the html source.
Although we always use "x-" convention to render our data, there are still some acceptable exceptions in our practice:
- use "a" directly when rendering the link to the "href" attribute
- use "img" directly when rendering the link to the "src" attribute
- use "li" directly when rendering a list(This one is controversial, some of our members argue that we should use "x-" instead of direct "li" selector)
簡單的說,我們總是會在綁定錨點上追加一個」x-「開頭的class,這樣一來,無論頁面的HTML怎麼變更,只要x約定沒有被破壞,所有的綁定邏輯就都可以保持穩定。如我前文所述,超過90%的情況下,對HTML頁面的重構無須破壞x約定,而對於剩下的10%,嗯,很容易搞定,對不對?
to @Alex Zhao,我這幾天一直在思考為什麼當初我們選擇了不同的綁定方向,一個原因是,我當時沒有時間去寫parser,而我們的項目在等著我的框架要用,另外一個原因,我事後意識到,大概就是因為我們的開發模式已經有根深蒂固的x約定文化,在這種情況下,我恐怕還是潛意識的覺得,從selector出發的綁定,其實意義不大,尤其是你創造的這種cbs的風格。一個更有趣的事實是,我們的後端框架Asta4D,當然,它只能做單向綁定,它的方向是從selector到model。我更仔細的思考了一下這個差異,為什麼我們會在服務端和客戶端選擇不同的方向。一個可能的理由是,在服務端,我們更關注數據,因此,我們將selector作為固定的出發點,同時,留下靈活度給另一端的數據。與此同時,在客戶端,我們恐怕更關注的是DOM上的數據表現,MVVM模型中的view mode本身並不需要靈活性,它的目的就是用來承載要表達的數據而已,因此,我們將綁定的出發點固定到mode上,從而留下靈活度給令一端的DOM。我並不知道我的思考究竟有沒有道理,比如我剛開始如果有足夠的時間來寫parser,可能我會很自然的選擇跟我們的服務端一致的綁定方向來開發這個框架(事實上我也的確寫了參考用的用戶代碼給團隊分享,大家看到可以用跟服務端框架一致的語法和模型來寫代碼還是很激動的,只是,我的確沒有時間去寫parser只好作罷),這樣就變成跟你的knot差不多的東西了。所以,以上這些算是見仁見智的一些思考,權作分享和參考。最後,再回應一下尤雨溪的另外一個評價astajs 的問題則在於綁定的過程變成了 imperative 而不是 declarative 的,雖然模板的可讀性加強了,但是犧牲了 JS 的可讀性...
我對declarative最大的怨念在於,框架需要保證我能夠100%的declare出我想要的東西,但實際上100%的框架都做不到,於是就有了angular飽受批評的directive機制。反過來,imperative的好處就在於,框架其實只是提供了一個回調imperative的機制,然後框架本身在這個回調機制上實現各種內置功能,對於開發者來說,最大的好處就是,定製化的成本很低。一個頁面中經常有一些頁面級別上通用的綁定邏輯,在declarative的模式下,要為它專門寫一個directive是一件成本效益比很低的事情,而在asta4js的imperative機制下,這只是定義一個函數的事情而已。
最後,我對這個「犧牲了 JS 的可讀性」的意思不是太明白,如果 @尤雨溪能夠稍微解釋一下的話,我再進一步討論這個話題。===============7.24 追加==============在回復賀師俊的評論的時候,我意識到我漏掉了一個這種分離綁定模式的另外一個巨大的優點,所以決定追加一點內容。這個優點其實跟我上面最後回復尤雨溪關於imperative的評論有關,算是對這種imperative機制的一個補充說明。對於傳統的嵌入式模板來說,經常遇到兩個頭痛的問題:1. 代碼如何復用2. 模板的聲明語法無法支持複雜的渲染/綁定邏輯對於第一點,模板的聲明語法幾乎沒有辦法提供任何幫助,頂多也就是在模板文件的級別上提供復用,但顯然這是不夠的,對於更細粒度的復用,要麼使用複雜的框架內置機制編寫擴展,要麼就只有copy/paste了。但對於我們這種分離綁定的模板機制而言,所有的綁定都是基於源語言的命令形式,因此,在代碼復用的問題上,可以利用到源語言的所有機制來組織代碼,對我們來說,綁定邏輯的復用,是自然而言的事情,而不是不得已的時候才勉為其難的做一下的事情。對於第二點,複雜的渲染/綁定邏輯,無論多牛x的嵌入式模板,它都不可能提供一個完整的語言所能提供的語法能力來幫助我們編寫代碼,綁定分離之後,所有這些都無須再依賴於功能貧乏的模板語言,而利用源語言來編寫所謂複雜的邏輯,對一個程序員來講,又能有多複雜呢?事實上,上面這兩點問題,在我十多年的web開發生涯中,一直纏繞著我,每每讓我痛不欲生,直到我發現並意識到綁定分離是可行的為止。Mark一下,43k大小對於移動端是個不小誘惑
推薦閱讀:
※為什麼沒有人出JS版的數據結構與演算法?
※JS模塊載入器載入原理是怎麼樣的?
※HTML 標籤屬性的全稱?
※如何評價性能大幅提升的Chrome 53?
※安卓工程師轉做前端,有什麼好的框架推薦?
TAG:前端開發 | JavaScript | MVVM | 前端框架 | AngularJS |