首先這些年關(guān)于前端技術(shù)層出不窮,從最早的只用js做簡(jiǎn)單驗(yàn)證,到現(xiàn)在發(fā)現(xiàn)好像大前端已經(jīng)無(wú)所不能了的感覺(jué)。特別是為了降低前端開(kāi)發(fā)復(fù)雜度,涌現(xiàn)了一大批的MVC/MVVM模式的前端框架,不停了刷新我們的代碼組織結(jié)構(gòu)及開(kāi)發(fā)模式,比如:Backbone.js、EmberJS、KnockoutJS、AvalonJS(國(guó)產(chǎn))、AngularJS、Reactjs等等。
其實(shí)上面都算是代碼結(jié)構(gòu)組織類(lèi)的框架,而前端其實(shí)遠(yuǎn)不止這些,我大致分了下類(lèi),可能還有很多沒(méi)考慮到歡迎補(bǔ)充:
1、動(dòng)態(tài)加載,如requirejs seajs
2、代碼結(jié)構(gòu)組織類(lèi),即上面提到那些前端框架,Backbone.js、EmberJS、KnockoutJS、AvalonJS(國(guó)產(chǎn))、AngularJS、Reactjs、Polymer等
3、代碼類(lèi)庫(kù),如jquery zepto json2 momentjs
4、UI類(lèi)庫(kù),如yui jqueryui extjs easyui kendoui dhtmlx bootstrap,還有raphael.js之類(lèi)
5、插件,jqgrid datatable.js ckeditor等
6、IE兼容性,es5-shim.js html5shiv.js respond.js selectivizr.js等
7、工具類(lèi),如nodejs npm bower grunt gulp webpack等 還有l(wèi)ess sass對(duì)css預(yù)處理工具,特別是nodejs帶來(lái)全棧時(shí)代
我們的開(kāi)發(fā)模式第一次革命應(yīng)該算是從google提出ajax一直到nodejs變化是非常的大,我們也在嘗試著找到更合理更簡(jiǎn)單更適合的開(kāi)發(fā)模式。我自己也經(jīng)歷過(guò)從原生js的開(kāi)發(fā)到使用jquery操作dom,再到應(yīng)用knockoutjs、avalonjs、angularjs及reactjs到我們的項(xiàng)目中(backbonejs、emberjs、polymer我沒(méi)實(shí)際使用過(guò)),發(fā)現(xiàn)我們的程序越寫(xiě)越簡(jiǎn)單,而且越來(lái)越有意思。但每個(gè)框架專(zhuān)注點(diǎn)是不一樣的,而且也有一些缺點(diǎn):
1、angularjs,體積龐大過(guò)于復(fù)雜不適合輕量級(jí)應(yīng)用,而且我對(duì)1.3放棄IE8及2.0的跳躍式發(fā)展耿耿于懷,最終開(kāi)始把目光投向reactjs。
2、reactjs,研究了一段時(shí)間發(fā)現(xiàn)還真不錯(cuò),首先是比較輕量極,性能也是很贊,最主要是它提出的Virtual DOM的概念,我覺(jué)得也算是一個(gè)革新了,但是它其實(shí)算不上是一個(gè)框架,它的專(zhuān)注點(diǎn)在于生成UI,就跟ng中的directive一個(gè)等級(jí)的,所以很多東西還要自己實(shí)現(xiàn),它只能算我們的一個(gè)利器要架構(gòu)一個(gè)復(fù)雜的應(yīng)用還需要很多的工作。還有一點(diǎn)就是我比較不喜歡它的jsx這個(gè)玩意,雖然它可以在服務(wù)端處理。應(yīng)該算是我的個(gè)人偏見(jiàn)吧。
3、polymer理念很超前,基于web component實(shí)現(xiàn),我個(gè)人也覺(jué)得這是未來(lái)的趨勢(shì)。
但是目前來(lái)說(shuō)對(duì)于Shadow DOM的瀏覽器的兼容性:http://caniuse.mojijs.com/Home/Html/item/key/shadowdom/index.html,我估計(jì)除了研究沒(méi)人會(huì)想用它了,而且它還利用了html5的imports:http://www.html5rocks.com/en/tutorials/webcomponents/imports/,它這種模式產(chǎn)生的問(wèn)題非常明顯,就是需要import太多的html,而且還有嵌套的概念,可想而知。
這些框架都很強(qiáng)悍,但是我覺(jué)得實(shí)際開(kāi)發(fā),一個(gè)好的框架應(yīng)該是要把已有的類(lèi)庫(kù)、插件等等的這些完美的結(jié)合起,并且提供簡(jiǎn)單易用的接口及合理的代碼組織結(jié)構(gòu),現(xiàn)有的jquery插件非常的多(其實(shí)jquery比這些東西流行的多的多),現(xiàn)有的開(kāi)源很多的類(lèi)庫(kù)我們都可以使用。我的思路是現(xiàn)有的這些資源應(yīng)該要充分利用起來(lái)。所以我想自己整一個(gè)前面框架出來(lái)。我構(gòu)想設(shè)計(jì)了下:
1、web component思路,結(jié)合Shadow DOM 及 Virtual DOM,如果支持Shadom DOM就使用Shadom DOM否則使用Virtual DOM實(shí)現(xiàn)。
2、控件定義盡量簡(jiǎn)單易懂,可快速包裝現(xiàn)有的插件,控件存在繼承的概念,控件存在少量修改時(shí)可以考慮繼承覆寫(xiě)的方式實(shí)現(xiàn)。
3、實(shí)現(xiàn)MVVM的雙向綁定,實(shí)際是三向(實(shí)際dom,控件實(shí)例(無(wú)論是shadw還是virtual都對(duì)應(yīng)到一個(gè)virtal dom)、數(shù)據(jù))
我暫時(shí)把這個(gè)項(xiàng)目叫做 Chitu.js 即赤兔.js,接下來(lái)會(huì)放到github上。
1、標(biāo)簽定義
比如我們需要一個(gè)hello的控件
<hello id="id1" binding="hello1"></hello>
2、控件定義
我們定義hello的控件
chitu.component('hello', function (self) { //屬性定義 self.name = { get: function () { return self.root.getAttribute("name"); }, set: function (value) { self.root.setAttribute("name", value); } }; self.value = { get: function () { return self.root.innerText; }, set: function (value) { self.root.innerText = value; } }; //... //事件定義 self.onclick = function(){ }; //... //方法定義 self.setColor = function(color){ self.root.style.color = color; }; //... //控件生成 self.render = function () { //... var text = document.createTextNode(self.value); return text; }; //控件消亡 self.dispose = function(){ };});
這里就是整個(gè)hello的控件的定義,就是把hello控件實(shí)例看作一個(gè)virtual節(jié)點(diǎn),這是reactjs的思路,但是實(shí)現(xiàn)上面如果是支持shadow dom可以使用把render中返回的節(jié)點(diǎn)放到shadow dom中。
這里的控件屬性與實(shí)際dom的雙向連動(dòng)是通過(guò)引入get set方法來(lái)實(shí)現(xiàn)的,實(shí)際上這個(gè)代碼還需要經(jīng)過(guò)一次complie處理,把get set方法通過(guò)ES5的Object.defineProperty方法轉(zhuǎn)換成屬性(先不考慮兼容性)。
這里如果要集成jquery控件就非常容易了,比如combobox控件:
chitu.component('combobox', function(self){ self.value = { get:function(){return $(root).combobox('getValue');}, set:function(value){$(root).combobox('setValue',value);} }; self.render = function(){ $(root).combobox(options); return root; }; self.dispose = function(){ $(root).combobox('dispose'); };});
如果控件需要繼承可以這樣,定義一個(gè)mygridEx繼承mygrid控件
chitu.component("mygridEx", function(self){ //覆寫(xiě)屬性 self.attr1 = { get:function(){}, set:function(){} }; //覆寫(xiě)方法 self.getRowCount = function(){ }},"mygrid");
3、控制器
chitu.controller('ctrl1',function(scope){ scope.data ={ tenant_id:'001', tenant_name:'tenantname', height:100 }; scope.hello={ name:'helloname', value:'world' } scope.grid1 ={ width:500, height:'{data.height}', rowClick:function(row){ scope.vdom.id1.value = row.name; } }; scope.search1={ text:'{data.teannt_name}', searchClick:function(){ } };});
這里需要解釋下<hello id="id1" binding="hello1"></hello>
這里的binding實(shí)際上對(duì)應(yīng)的是controller當(dāng)中的屬性,這個(gè)屬性其實(shí)就是控件的選項(xiàng)參數(shù),我們以前調(diào)用jquery控件時(shí)
$('#id').jqgrid({ //一堆參數(shù)});
這樣的話,這一堆參數(shù)就可以通過(guò)一個(gè)binding跟頁(yè)面聯(lián)系起來(lái),方便控件的使用,當(dāng)然還可以在控件定義時(shí)就定義好很多默認(rèn)的參數(shù),只需要傳入需要改變的參數(shù)。
當(dāng)然我們的參數(shù)也不一定都要寫(xiě)在binding當(dāng)中,也可以單獨(dú)綁定控件屬性
<hello id="id1" [value]="world" ></hello><hello id="id1" [value]="{data.tenant_name}" ></hello><hello id="id1" binding="hello1" [value]="world" [onclick]="search1.searchClick"></hello>
第一行:沒(méi)有對(duì)應(yīng)的binding屬性,只有一個(gè)value屬性,value的值為world
第二行:value的值雙向綁定到controller中的data.tenant_name數(shù)據(jù)上
第三行:先從bindig的hello1中取值,如果有[value]則覆蓋原值
這里為什么給屬性加上[中括號(hào)],是因?yàn)閷傩越惺裁疵侄伎赡?盡量避免和原dom屬性沖突。
控件器中還有一點(diǎn)設(shè)計(jì)值得說(shuō)的是,我們可以把數(shù)據(jù)集中在scope.data中
1、其它地方綁定只需要{data.row1.field1}這樣指定綁定就好
2、我們可以不需要先data中的結(jié)構(gòu)是怎么樣子,根據(jù)綁定關(guān)系生成data的結(jié)構(gòu)
3、大多數(shù)情況下我們?nèi)』貋?lái)的數(shù)據(jù)就是一個(gè)返回result,我們直接賦值給scope.data即可,不需要hello1.value = result.name , grid1.data = result.list;這樣的處理。
4、我們需要提交數(shù)據(jù)時(shí),也只需要從data中取即可,非常方便
當(dāng)然對(duì)于普通的標(biāo)簽,我們也是可以綁定的
<div [title]="{data.title}" [text]="{data.text}" [onclick]="search1.searchClick" ></div>
這樣一來(lái),我們的代碼可能就會(huì)集中在controller的一些方法當(dāng)前中了,我們?cè)赾ontroller中可以直接獲取到每個(gè)控件實(shí)例,可以直接訪問(wèn)控件的各個(gè)屬性即可實(shí)現(xiàn)交互。
當(dāng)然如果有綁定到data數(shù)據(jù)中,直接操作數(shù)據(jù)也可以實(shí)現(xiàn)ui交互了。
上面我給大家描述了我的思路及想法,我們?cè)俜治隹赡苊鎸?duì)的問(wèn)題。
1、兼容性
目前只是在嘗試階段,很多問(wèn)題還沒(méi)碰到,我的目標(biāo)是盡量做到IE8+
2、性能
關(guān)于性能的消耗,可能主要是在于節(jié)點(diǎn)掃描、控件render、屬性綁定及屬性頻繁的刷新。節(jié)點(diǎn)掃描當(dāng)然會(huì)有些消耗,但是它帶來(lái)的方便性可以忽略它的消耗,除非你想一個(gè)一個(gè)控件指定去手動(dòng)調(diào)用,控件render就取決了你寫(xiě)的控件本身了,不算是這個(gè)框架的問(wèn)題,屬性綁定在數(shù)據(jù)量沒(méi)有大到一定程度是基本是不會(huì)有問(wèn)題的。
3、開(kāi)發(fā)效率
開(kāi)發(fā)效率主要就取決于是否做到了關(guān)注點(diǎn)分離,代碼組織是否清晰合理簡(jiǎn)單易懂,代碼只需要寫(xiě)有變化的東西,把重復(fù)的細(xì)節(jié)都封裝起來(lái),這個(gè)只能是盡量做到了。
關(guān)于我的其它的一些想法
1、需不需要引入類(lèi)似css的東西用于分離頁(yè)面與controller之間的綁定,這樣還可以去除節(jié)點(diǎn)掃描,提高性能,如下
var binding={ '#id':'todo1', '#grid1':'grid', '#hello1':'hello'};
2、關(guān)于render處理,這里我認(rèn)為有這些方式
a、用代碼實(shí)現(xiàn)(原生、jquery)
b、模板實(shí)現(xiàn)或拼接字符串
c、通過(guò)預(yù)生成的方式(類(lèi)似jsx,也可通過(guò)nodejs c# java等語(yǔ)言)
3、關(guān)于屬性刷新
因?yàn)榭丶傩允侵苯咏壎ǖ皆膁om上的,如果出現(xiàn)了大量的屬性刷新恐帶來(lái)性能問(wèn)題,是否考慮一個(gè)參數(shù)用來(lái)開(kāi)啟關(guān)閉(立即刷新、一次性刷新)
4、跟amd cmd結(jié)合
這個(gè)是沒(méi)有任何問(wèn)題的,而且如果控件都是包裝第三方控件的話,實(shí)現(xiàn)動(dòng)態(tài)加載還可以更加的方便。
5、是否還需要引入其它特性,如ng注入特性、過(guò)濾器等
6、哪些環(huán)節(jié)還可以在服務(wù)端預(yù)處理
目前我自己也才剛剛一個(gè)想法,有很多問(wèn)題可能還沒(méi)具體想清楚,現(xiàn)在自己先實(shí)現(xiàn)了一個(gè)簡(jiǎn)易的版本,等我先折騰出一個(gè)0.1的版本,我就會(huì)放到github上,如果有興趣的朋友,可以加入一起設(shè)計(jì)構(gòu)思及開(kāi)發(fā)。大家有好的想法,意見(jiàn)建議歡迎給我留言。
聯(lián)系客服