写在前面
因为 zepto、jQuery2.x.x 和 Nuclear 都是为现代浏览器而出现,不兼容 IE8,适合现代浏览器的 web 开发或者移动 web/hybrid 开发。每个框架类库被大量用户大规模使用都说明其戳中了开发者的刚需。本文将对比 zepto/jQuery 到 Nuclear 的设计和演化的过程。
无框架时代
互联网的春风刚刮来的时候,人们当时利用三剑客制作网页。
1 2 3 4 5 6 7 |
<div onclick="showMsg()"></div> <script> function showMsg(){ alert("恭喜你实现第一个人机交互程序"); } </script> |
这里会发现 showMsg 必须是全局的,onclick 触发才能访问,这样就会导致每绑一个事件就要污染一个全局变量。这点问题难不倒前端工程师,加个超级 namespace,所有的事件挂在它下面:
1 2 3 4 5 6 7 8 9 |
<div onclick="SuperNamespce.showMsg1()"></div> <div onclick="SuperNamespce.showMsg2()"></div> <script> var SuperNamespce={}; SuperNamespce.showMsg1=function(){ } SuperNamespce.showMsg2=function(){ } </script> |
但是也有问题,比如这样的场景:
1 2 3 4 5 6 7 |
var SuperNamespce = {}; setTimeout(function () { SuperNamespce.showMsg1 = function () { } SuperNamespce.showMsg2 = function () { } }, 4000) |
或者更真实一点:
1 2 3 4 5 6 7 8 9 10 |
var SuperNamespce = {}; ajax({ url: "xxx", success: function () { SuperNamespce.showMsg1 = function () { } SuperNamespce.showMsg2 = function () { } } }) |
在定时器没执行完成或者 AJAX 没有 success 之前,用户的所有交互都会报:
1 2 |
Uncaught TypeError: SuperNamespce.showMsg1 is not a function Uncaught TypeError: SuperNamespce.showMsg2 is not a function |
然后,善于记录分析总结思考提炼的工程师们拿出本子记录下最佳实践:
- 不建议在 dom 元素上直接声明事件绑定调用
- 声明式事件绑定所调用的方法必定要污染全局某个变量
- 声明式事件绑定的相关 js 未执行完的情况下发生人机交互会报脚本错误,且严重影响用户体验
- 建议在 js 中先查找 dom、再给 dom 绑定事件
想象一下:一个按钮 5 秒后才绑的事件,用户前 4 秒内一直点都没反应,然后 5 秒到了,但是用户已经放弃该网页了。
util 库时代
开发者们按照上面总结的最佳实践,重构了上面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<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 好累,封个类库:
1 2 3 |
function query(selector){ //此处省略一万行代码 } |
绑定事件好累,封个类库(edwards 的 events.js):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
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); } }; |
再然后,开发者们觉得引用这么多工具库好累...
zepto/jQuery 时代
开发者们觉得引用这么多工具库,而且他们其实都隶属于同一类东西(查找 dom、dom 绑定事件都是操作 dom)可以糅合一起。就有了后来风靡全球的 jQuery 和 zepto 在 web 里实现人机交互:
1 2 3 |
$("#myID").click(function(){ alert("恭喜你使用三行代码实现了人机交互程序"); }) |
开发者的刚需就是:找到 dom、绑定事件、写逻辑。而且,上面的程序还不会丢失语义,一看就知道想干什么。但是:
- js 未执行完的情况下发生人机交互【虽然不会报脚本错误】,但是严重影响用户体验
开发者们被各种爽到之后,这个问题已经被抛到了九霄云外。那我们就继续往下看,看到哪个阶段把上面这个问题解决了?!
AngularJS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<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 绑定事件,所以没有解决。
React
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
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)。
Nuclear
理念:some HTML + scoped CSS + JS === Reusable Component
Nuclear 的网站在这里: http://alloyteam.github.io/Nuclear/ 里面有大量的介绍。
通过下面的使用方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
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 里生成如下的结构:
1 2 3 4 5 6 7 8 9 10 |
<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
感谢阅读~~
小易分享网 2016 年 8 月 18 日
这个文章写的好,转走了! http://www.xevip.cn
风爻 2016 年 7 月 19 日
对类 React 思路的前端框架都很有好感,然而痛点其实也有的,第一个问题是 SEO,还要中间引入一个渲染层,架构上麻烦;第二个问题是简洁程度,时间久了,糟糕的 React 代码,其实也就是呵呵,开发人员写 html 的时代已经过去很久了,jade 基本算是标准,之前也看到有尝试把 React 和 Jade 整合在一起的项目,不稳定、性能问题,都是事,整体乐观,但还有很长的路要走。嗯,尝试后保持观望,总结:其实也不用那么着急着否定这个世界 。
HD 2016 年 7 月 19 日
SEO 可以通过 server rendering 解决的。React 本身对 JS 的扩展已经会遇到困难,而和 Jade 共用就更困难了
slowly 2016 年 5 月 10 日
鼓励创新和思考解决业务问题的意识,但不要陷入轮子的围城中……
淡墨青衫 2016 年 5 月 9 日
Nuclear 是为了解决什么问题呢,相对于 React、Vue 有哪些优势?
张宇 2016 年 5 月 5 日
react 同样重视的是开发者的体验,每一行都加反斜线也是醉了
TAT.dnt 2016 年 5 月 5 日
http://alloyteam.github.io/Nuclear/example.html
meepo 2016 年 5 月 3 日
在 JS 中编写 HTML 代码的体验感觉很差,每一行需要加反斜杠或者引号,没有语法高亮,也用不了 zencoding。
TAT.dnt 2016 年 5 月 3 日
可以 es6 或者 拆分文件 或者 tpl in html 。 3 种解药任选: http://alloyteam.github.io/Nuclear/example.html
軟體植物 2016 年 5 月 18 日
你用过 WebStorm 的话,就不会这么说了
杨立ming__KK 2016 年 4 月 29 日
vuejs 感觉是目前组件化编程体验最好的了,
亚里士朱徳 2016 年 4 月 29 日
这样都通过 js 来加载组件的话,这样增加浏览器的重排和重绘会不会带来性能问题?
TAT.dnt 2016 年 4 月 29 日
按需加载组件渲染样式对现代浏览器来说不是问题。待后续的 nuclear 性能评测数据。还有就是完全可以通过构建按 css、js 打包提前加载或者按组件打包按需加载,这些都不是问题。
亚里士朱徳 2016 年 4 月 29 日
PC 端可能说得通,考虑过移动端尤其是低端安卓机么?
亚里士朱徳 2016 年 4 月 29 日
webpack 为什么不写进来呢?
TAT.dnt 2016 年 4 月 30 日
webpack 算是构建工具,可以和 nuclear 一起使用。对了,忘了说了。切断 observejs 双向绑定性能可以翻倍。比如最新的测试:http://alloyteam.github.io/Nuclear/pt/
TAT.dnt 2016 年 4 月 30 日
但是基于 html 的组件化应用编程和游戏编程不一样,帧率不用 60fps。低帧率按需更新用户根本不会觉得卡顿。对了,忘了说了。切断 observejs 双向绑定性能可以翻倍, 超大的 list 可以考虑不用双向绑定。比如最新的测试:http://alloyteam.github.io/Nuclear/pt/
戴文奇 2016 年 4 月 28 日
如果 父组件和子组件 都是异步拉数据渲染出来的。父组件需要在子组件渲染 ok 后立马做些处理 。用这个框架怎么处理?。。。
TAT.dnt 2016 年 4 月 29 日
最终目的都是要构造一个或两个 option,拉取完两条数据再构造就行。子组件的 option 在父组件 install 时候传递或者重新构造。
刘sir 2016 年 4 月 27 日
适合自己就是好的!
当当 2016 年 4 月 27 日
这是 KPI 压力下的产物吗
王道中强流 2016 年 4 月 27 日
感觉不怎么人性化呢~this.option.items.push(this.textBox.value) 看到这一句我的第一观感是:这哪来的 option,又是哪来的 items,然后又是哪来的 textBox……看完各种箭头后,才明白。不像我看到 Vue.js 的时候,第一眼就望穿秋水,清澈透亮。也许这项目有它的优点,但是按照目前这种使用方式,恐怕流行不起来。
TAT.dnt 2016 年 4 月 28 日
api 文档: http://alloyteam.github.io/Nuclear/api.html