转载

基于Nuclear的Web组件-Todo的十一种写法

刀耕火种

刀耕火种是新石器时代残留的农业经营方式。又称迁移农业,为原始生荒耕作制。 
var TodoApp = Nuclear.create({     add: function (evt) {         evt.preventDefault();         var textBox = this.node.querySelector('input');         this.option.items.push(textBox.value);     },     installed: function () {         var form = this.node.querySelector('form');         form.addEventListener('submit', this.add.bind(this), false);     },     render: function () {         return '<div>/                  <h3>TODO</h3>/                  <ul> {{#items}} <li>{{.}}</li> {{/items}}</ul>/                   <form >/                    <input type="text"  />/                    <button>Add #{{items.length}}</button>/                  </form>/                 </div>';     } });   new TodoApp( { items: [] },"#container"); 

这种书写方式依赖延续了jQuery时代的思维方式:

  • js里查找dom
  • js里绑定事件

在以前的文章里写过,如果不使用组件化编程,js里查找dom以及在js里绑定事件可能会带来如下问题:

  • 浪费带宽
  • 用户反馈无响应
  • 脚本错误
  • 页面短暂错乱

上面的书写方式粗暴、原始、落后,即:刀耕火种。

石器锄耕

“石器锄耕”是奴隶社会时期的主要耕作方式,这一时期农业已经有了很大的发展。 
var TodoApp = Nuclear.create({ onRefresh: function () {       this.form.addEventListener("submit", function (evt) {         evt.preventDefault();                         this.option.items.push(this.textBox.value);     }.bind(this), false); }, render: function () {   return '<div>/              <h3>TODO</h3>/              <ul> {{#items}} <li>{{.}}</li> {{/items}}</ul>/              <form nc-id="form" >/                <input nc-id="textBox" type="text"  />/                <button>Add #{{items.length}}</button>/              </form>/            </div>'; } },{     diff:false });   new TodoApp( { items: [] },"#container"); 

会发现,查找dom的代码已销声匿迹。直接标记nc-id,就自动挂载在this下。

值得注意的是,传入了第二参数关闭了DOM diff。关掉diff的结果就是,每次组件HTML会全部重新替换渲染,绑定的事件全部丢失,所以需要将绑定事件的代码写入onRefresh里,这样每次重新渲染都会再绑定一次。

比刀耕火种先进一点:石器锄耕。

直捣黄龙

黄龙:即黄龙府,辖地在今吉林一带,应该是指长春市农安县,为金人腹地。一直打到黄龙府。指捣毁敌人的巢穴。指杀敌取胜。 
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: [] },"#container"); 

会发现,查找dom和绑定的代码同时销声匿迹!!

  • 需要使用input,直接标记nc-id为textBox,就可以this.textBox访问
  • 需要绑定事件,直接在HTML内声明事件和回调onsubmit="add(event)"

也可以通过add(event,this)拿到event和触发该事件的dom元素。

代码通俗简洁干净直接,目的直观明确。故:直捣黄龙。

子承父业

宋·释道原《景德传灯录·利山和尚》:“僧问:不历僧只获法身,请师直指。师云:子承父业。” 
var TodoList = Nuclear.create({     render: function () {         return '<ul> {{#items}} <li>{{.}}</li> {{/items}}</ul>';     } });   var TodoApp = TodoList.create({     add: function (evt) {         evt.preventDefault();         this.option.items.push(this.textBox.value);     },     render: function () {         return '<div>/                  <h3>TODO</h3>'                 + this._super.render() +                 '<form  onsubmit="add(event)" >/                    <input nc-id="textBox" type="text"  />/                    <button>Add #{{items.length}}</button>/                  </form>/                </div>';     } });   new TodoApp({ items: [] },"#container"); 

TodoApp不过是TodoList的炎黄子孙,故TodoApp可以通过this._super访问父辈。也可访问父辈任何方法。有人会说:“组合”优于“继承”的。一定要明白:OOP既能组合也能继承,是不冲突的。且看下面例子。

万夫一力

明·刘基 《郁离子·省敌》:“万夫一力,天下无敌。” 
var TodoList = Nuclear.create({     render: function () {         return '<ul> {{#items}} <li>{{.}}</li> {{/items}}</ul>';     } });   var TodoApp = Nuclear.create({     install: function () {         this.todoList = new TodoList({ items: [] })     },     add: function (evt) {             evt.preventDefault();             this.todoList.option.items.push(this.textBox.value);             this.refresh();     },     render: function () {       return '<div>/                  <h3>TODO</h3>'                 + this.todoList.render()+                 '<form onsubmit="add(event)" >/                    <input nc-id="textBox" type="text"  />/                    <button>Add #'+this.todoList.option.items.length+'</button>/                  </form>/                </div>';     } });   new TodoApp( {},"#todo2Container"); 

前一个例子的继承,这个例子是组合。不能说你的框架是Class base就不能使用组合,这是天大的误解。而我依稀记得极限编程关于面向对象设计的推论是: 优先使用对象组合,而不是类继承 。作为框架的设计者,虽然会有一些约束,但是如果连组合和继承都不能共存,那就是设计的最大败笔。

使用Nuclear既能继承也能组合,关键看程序逻辑该怎么抽象和实现复杂度。

藕断丝连

唐·孟郊《去妇》诗:“妾心藕中丝;虽断犹牵连。” 
var TodoList = Nuclear.create({     render: function () {         return '<ul> {{#items}} <li>{{.}}</li> {{/items}}</ul>';     } });   var TodoApp = Nuclear.create({     install: function () {         this.todoList = new TodoList(this.option)     },     add: function (evt) {             evt.preventDefault();                             this.option.items.push(this.textBox.value);     },     render: function () {       return '<div>/                  <h3>TODO</h3>'                 + this.todoList.render()+                 '<form  onsubmit="add(event)"  >/                    <input nc-id="textBox" type="text"  />/                    <button>Add #{{items.length}}</button>/                  </form>/                </div>';     } });   new TodoApp( { items: [] },"#container"); 

这个例子和上个例子的区别是:共用option。option的变更会自动更新依赖option的组件。

四海为家

 《汉书·高帝记》:“且夫天子以四海为家。” 
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>'; } }); var todo= new TodoApp( { items: [] });   todo.setNuclearContainer('#container'); 

且看上面的new TopApp没传第二个参数指定容器。后面使用setNuclearContainer指定容器。这个场景还是比较常见,创建游离态组件,后续根据需要指定容器。AlloyLever就是这么干的: https://github.com/AlloyTeam/AlloyLever/blob/master/src/js/main.js

如虎添翼

三国·蜀·诸葛亮《心书·兵机》:“将能执兵之权,操兵之势,而临群下,臂如猛虎加之羽翼,而翱翔四海。” 
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>';     },     style: function () {         return 'h3 { color:red; }/                 button{ color:green;}';     } }); var todoApp = new TodoApp({ items: [] }, '#container');   todoApp.option.items.push('Nuclear'); todoApp.option.items.push('Hello World!'); 

style方法内的样式自会对自身生效,不会污染其他组件。可以操作对象实例option,option的变更会自动更新组件,属性设置>方法调用。双向绑定的好处以前写过一篇文章专门介绍,从上面的例子也能可出:

  • 组件内部的代码更简洁
  • 组件外部的代码更简洁

壁垒森严

壁垒:古代军营四周的围墙;森严:整齐,严肃。原指军事戒备严密。现也用来比喻彼此界限划得很分明。 
<templateid="myTemplate">     <stylescoped>         h3{ color:red; }         button{ color:green;}     </style>       <div>         <div>             <h3>TODO</h3>             <ul>{{#items}}<li>{{.}}</li>{{/items}}</ul>             <formonsubmit="add(event)">                 <inputnc-id="textBox" type="text">                 <button>Add #{{items.length}}</button>             </form>         </div>     </div> </template>   <script>     var TodoApp = Nuclear.create({         install:function() {             this.todoTpl = document.querySelector("#myTemplate").innerHTML;         },         add: function (evt) {             evt.preventDefault();             this.option.items.push(this.textBox.value);         },         render: function () {             return this.todoTpl;         }     });       new TodoApp({ items: [] }, "#todoListContainer"); </script> 

不用担心template标签的兼容性问题,Nuclear帮你处理好了。支持所有现代浏览器(包括IE9+)。

Nuclear也在js里进行了动态插入了template { display: none !important; }。但是js还没执行到且浏览器不兼容template的话,用户会看到一闪而过的模板原始代码。

所以为了避免IE9一闪而过的模板原始代码直接显示,建议在head中加入。

<style>     template{          display: none !important;      } </style> 

如果你像手Q hybrid应用那样只需要兼容webkit的话,天生支持template,就不用加入上面的兼容样式。

鬼斧神工

《庄子·达生》:“梓庆削木为鐻,鐻成,见者惊忧鬼神。” 
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>                   <formonsubmit="add(event)" >                   <inputnc-id="textBox" type="text"  />                   <button>Add #{{items.length}}</button>                 </form>               </div>`;     },     style: function () {         return `h3 { color:red; }                 button{ color:green;}`;     } }); 

使用ES6 Template literals解决多行文本问题。

人剑合一

剑修者最高境界,人既是剑,剑既是人,剑随心发,人随剑杀 
var TodoApp = Nuclear.create({     add: function (evt) {         evt.preventDefault();         this.option.items.push(this.textBox.value);     },     render: function () {         return `<stylescoped>                   h3{ color:red; }                   button{ color:green;}                 </style>                 <div>                   <h3>TODO</h3>                   <ul> {{#items}} <li>{{.}}</li> {{/items}}</ul>                   <formonsubmit="add(event)" >                   <inputnc-id="textBox" type="text"  />                   <button>Add #{{items.length}}</button>                 </form>                 </div>`;     } }); 

使用ES6 Template literals解决多行文本问题,style和html当然可以合在一起。

Nuclear从出生,API简单稳定,几乎没怎么变动,内部是实现在不断的完善,API驱动非常重要,不能因为实现某些API困难而做任何妥协,比如让使用框架的着多传个参数、多调用一次方法,这都是设计的缺陷。

Nuclear就是不妥协的结果。因为简单的设计,所以可以有很多强大的玩法,待续…

广而告之

Nuclear Github: https://github.com/AlloyTeam/Nuclear

加入Nuclear,一起让组件化开发体验更加惬意、舒适..

原文  http://www.alloyteam.com/2016/05/web-components-based-on-nuclear-todo-ten-one-way/
正文到此结束
Loading...