由於zepto、jQuery2.x.x和Nuclear都是爲現代瀏覽器而出現,不兼容IE8,適合現代瀏覽器的web開發或者移動web/hybrid開發。每一個框架類庫被大量用戶大規模使用都說明其戳中了開發者的剛需。本文將對比zepto/jQuery到Nuclear的設計和演化的過程。css
互聯網的春風剛刮來的時候,人們當時利用三劍客製做網頁。html
<div onclick="showMsg()"></div> <script> function showMsg(){ alert("恭喜你實現第一我的機交互程序"); } </script>
這裏會發現showMsg必須是全局的,onclick觸發才能訪問,這樣就會致使每綁一個事件就要污染一個全局變量。這點問題難不倒前端工程師,加個超級namespace,全部的事件掛在它下面:前端
<div onclick="SuperNamespce.showMsg1()"></div> <div onclick="SuperNamespce.showMsg2()"></div> <script> var SuperNamespce={}; SuperNamespce.showMsg1=function(){ } SuperNamespce.showMsg2=function(){ } </script>
可是也有問題,好比這樣的場景:git
var SuperNamespce = {}; setTimeout(function () { SuperNamespce.showMsg1 = function () { } SuperNamespce.showMsg2 = function () { } }, 4000)
或者更真實一點:github
var SuperNamespce = {}; ajax({ url: "xxx", success: function () { SuperNamespce.showMsg1 = function () { } SuperNamespce.showMsg2 = function () { } } })
在定時器沒執行完成或者AJAX沒有success以前,用戶的全部交互都會報:web
Uncaught TypeError: SuperNamespce.showMsg1 is not a function Uncaught TypeError: SuperNamespce.showMsg2 is not a function
而後,善於記錄分析總結思考提煉的工程師們拿出本子記錄下最佳實踐:ajax
不建議在dom元素上直接聲明事件綁定調用編程
聲明式事件綁定所調用的方法一定要污染全局某個變量瀏覽器
聲明式事件綁定的相關js未執行完的狀況下發生人機交互會報腳本錯誤,且嚴重影響用戶體驗前端工程師
建議在js中先查找dom、再給dom綁定事件
想象一下:一個按鈕5秒後才綁的事件,用戶前4秒內一直點都沒反應,而後5秒到了,可是用戶已經放棄該網頁了。
開發者們按照上面總結的最佳實踐,重構了上面的代碼:
<div id="myID1"></div> <div id="myID2"></div> <script> var myID1 = document.getElementById("myID1"); var myID2 = document.getElementById("myID2") myID1.onclick = function () { alert(1); } myID2.onclick = function () { alert(2); } </script>
這給開發者們帶來了另一個麻煩的問題,之前聲明式直接在div上綁定事件不須要查找dom,因此不須要標記id,如今每一個須要綁定事件的dom都須要標記id用於js查找。並且,這種寫法依舊沒有改變聲明式事件綁定的一個問題:
js未執行完的狀況下發生人機交互【雖然不會報腳本錯誤】,可是嚴重影響用戶體驗
好比你div是個按鈕形態,看上去用戶就想點,一直點一直點。可是js還沒執行完,事件還沒綁定上去。用戶將收不到任何反饋。
可是開發者並不關係這‘毫秒’、甚至‘秒’級別的用戶體驗,也有的開發者利用UI邏輯去規避,好比先來個loading?好比綁定完事件再顯示該dom。
就這樣,這個問題就這麼不了了之~~~~。隨之而來的是:
查找dom好累,封個類庫:
function query(selector){ //此處省略一萬行代碼 }
綁定事件好累,封個類庫(edwards的events.js):
function addEvent(element, type, handler) { // assign each event handler a unique ID if (!handler.$$guid) handler.$$guid = addEvent.guid++; // create a hash table of event types for the element if (!element.events) element.events = {}; // create a hash table of event handlers for each element/event pair var handlers = element.events[type]; if (!handlers) { handlers = element.events[type] = {}; // store the existing event handler (if there is one) if (element["on" + type]) { handlers[0] = element["on" + type]; } } // store the event handler in the hash table handlers[handler.$$guid] = handler; // assign a global event handler to do all the work element["on" + type] = handleEvent; }; // a counter used to create unique IDs addEvent.guid = 1; function removeEvent(element, type, handler) { // delete the event handler from the hash table if (element.events && element.events[type]) { delete element.events[type][handler.$$guid]; } }; function handleEvent(event) { // grab the event object (IE uses a global event object) event = event || window.event; // get a reference to the hash table of event handlers var handlers = this.events[event.type]; // execute each event handler for (var i in handlers) { this.$$handleEvent = handlers[i]; this.$$handleEvent(event); } };
再而後,開發者們以爲引用這麼多工具庫好累...
開發者們以爲引用這麼多工具庫,並且他們其實都隸屬於同一類東西(查找dom、dom綁定事件都是操做dom)能夠糅合一塊兒。就有了後來風靡全球的jQuery和zepto在web裏實現人機交互:
$("#myID").click(function(){ alert("恭喜你使用三行代碼實現了人機交互程序"); })
開發者的剛需就是:找到dom、綁定事件、寫邏輯。並且,上面的程序還不會丟失語義,一看就知道想幹什麼。可是:
js未執行完的狀況下發生人機交互【雖然不會報腳本錯誤】,可是嚴重影響用戶體驗
開發者們被各類爽到以後,這個問題已經被拋到了九霄雲外。那咱們就繼續往下看,看到哪一個階段把上面這個問題解決了?!
<div ng-app="myApp" ng-controller="personCtrl"> <button ng-click="toggle()">隱藏/顯示</button> <p ng-show="myVar"> AngularJS </p> </div> <script> var app = angular.module('myApp', []); app.controller('personCtrl', function($scope) { $scope.myVar = true; $scope.toggle = function() { $scope.myVar = !$scope.myVar; }; }); </script>
由於AngularJS經過ng-click綁定事件,因此沒有解決。
var Photo = React.createClass({ toggleLiked: function() { this.setState({ liked: !this.state.liked }); }, getInitialState: function() { return { liked: false } }, render: function() { var buttonClass = this.state.liked ? 'active' : ''; return ( <div className='photo'> <button onClick={this.toggleLiked} className={buttonClass}>點我</button> </div> ) } });
由於React的佈局和邏輯放在一塊兒,解決了跨越了十多年之久的前端問題:
js未執行完的狀況下發生人機交互【雖然不會報腳本錯誤】,可是嚴重影響用戶體驗
經過把相關的佈局和邏輯放在同一個組件中,整個系統變得整潔清晰了。 咱們爲這個重要的洞見向 React 致敬。(引用於riot)
React的核心根本不是什麼UI=fn(state);不用React也能夠UI=fn(state)。
理念:some HTML + scoped CSS + JS === Reusable Component
Nuclear的網站在這裏: http://alloyteam.github.io/Nuclear/ 裏面有大量的介紹。
經過下面的使用方式:
var TodoApp = Nuclear.create({ add: function (evt) { evt.preventDefault(); this.option.items.push(this.textBox.value); }, render: function () { return '<div>\ <h3>TODO</h3>\ <ul> {{#items}} <li>{{.}}</li> {{/items}}</ul>\ <form onsubmit="add(event)" >\ <input nc-id="textBox" type="text" />\ <button>Add #{{items.length}}</button>\ </form>\ </div>'; } }); new TodoApp({ items: [] }, "#todoListContainer");
會在html裏生成以下的結構:
<div data-nuclearid="0"> <div> <h3>TODO</h3> <ul></ul> <form onsubmit="Nuclear.instances[0].add(event)"> <input nc-id="textBox" type="text"> <button>Add #0</button> </form> </div> </div>
更爲具體的對應能夠看這張圖片:
組件化編程
組件html結構、css和js必須在一塊兒,要麼都加載,要麼都不加載。
只加載其中一部分都是浪費如css加載了,組件沒用到js加載了,浪費帶寬
帶來很差的體驗,如組件js加載完了,css卻沒加載完成,致使用戶看到錯亂的頁面
腳本錯誤和糟糕體驗,如組件HTML和css加載完了,js卻沒加載完成,致使用戶交互無響應
Nuclear編程
組件化編程
超小的體積,5k
支持任意模板引擎
雙向綁定改善編程體驗
面向對象編程
支持局部CSS
回到最初的問題:
不建議在dom元素上直接聲明事件綁定調用(Nuclear建議事件直接綁在dom上)
聲明式事件綁定所調用的方法一定要污染全局某個變量(只污染了Nuclear)
聲明式事件綁定的相關js未執行完的狀況下發生人機交互會報腳本錯誤,且嚴重影響用戶體驗(組件化編程,組件的html、css和js是一個總體)
建議在js中先查找dom、再給dom綁定事件(Nuclear建議事件直接綁在dom上,查找dom的須要能夠標記nc-id或者nc-class)
總之:使用Nuclear組件化編程,使組件的HTML、CSS和JS同時一塊兒生效能夠規避許多問題。
Github: https://github.com/AlloyTeam/Nuclear
感謝閱讀~~