Upgrade to Pro — share decks privately, control downloads, hide ads and more …

物極必反 - riot.js 與 angular.js 雜談

物極必反 - riot.js 與 angular.js 雜談

第七次聚會

Johnson Liang

November 29, 2013
Tweet

More Decks by Johnson Liang

Other Decks in Programming

Transcript

  1. ≡ Johnson (MrOrz) ≡ Mobile HCI lab ≡ Dev @

    VUSE ≡ ࢿ㘤ܥ౷܇࿅൝ )5.-ߨࢣ 13年11⽉月30⽇日星期六
  2. MVC is more complex. The many arrows form a circle.

    The role of the controller is not clear, and the pattern can be interpreted in many different ways. In fact, this is the root cause for the explosion of client-side frameworks. ❌ https://moot.it/blog/technology/riotjs-the-1kb-mvp-framework.html 13年11⽉月30⽇日星期六
  3. Riot uses Model-View-Presenter (MVP) design pattern to organize your code

    so that it's modular, testable and easy to understand. https://moot.it/blog/technology/riotjs-the-1kb-mvp-framework.html 13年11⽉月30⽇日星期六
  4. $(function() { / 1. Initialize / var  todo  =  new

     Todo(); / 2. Listen to user events /      $("#new-­‐todo").keyup(function(e)  {            var  val  =  $.trim(this.value);            if  (e.which  ==  13  &&  val)  {            todo.add(val);  this.value  =  "";  }      });      $("#clear-­‐completed").click(function()  {          todo.remove("completed");      }) // ... / 3. Listen to model events / // an entry was edited      todo.on("add",  ...)              .on("remove",  function(items)  {                      $.each(items,  function()  {                            $("#"  +  this.id).remove()                      });              }); // ... }) 13年11⽉月30⽇日星期六
  5. $(function() { / 1. Initialize / var  todo  =  new

     Todo(); / 2. Listen to user events /      $("#new-­‐todo").keyup(function(e)  {            var  val  =  $.trim(this.value);            if  (e.which  ==  13  &&  val)  {            todo.add(val);  this.value  =  "";  }      });      $("#clear-­‐completed").click(function()  {          todo.remove("completed");      }) // ... / 3. Listen to model events / // an entry was edited      todo.on("add",  ...)              .on("remove",  function(items)  {                      $.each(items,  function()  {                            $("#"  +  this.id).remove()                      });              }); // ... }) 13年11⽉月30⽇日星期六
  6. $(function() { / 1. Initialize / var  todo  =  new

     Todo(); / 2. Listen to user events /      $("#new-­‐todo").keyup(function(e)  {            var  val  =  $.trim(this.value);            if  (e.which  ==  13  &&  val)  {            todo.add(val);  this.value  =  "";  }      });      $("#clear-­‐completed").click(function()  {          todo.remove("completed");      }) // ... / 3. Listen to model events / // an entry was edited      todo.on("add",  ...)              .on("remove",  function(items)  {                      $.each(items,  function()  {                            $("#"  +  this.id).remove()                      });              }); // ... }) 13年11⽉月30⽇日星期六
  7. $(function() { / 1. Initialize / var  todo  =  new

     Todo(); / 2. Listen to user events /      $("#new-­‐todo").keyup(function(e)  {            var  val  =  $.trim(this.value);            if  (e.which  ==  13  &&  val)  {            todo.add(val);  this.value  =  "";  }      });      $("#clear-­‐completed").click(function()  {          todo.remove("completed");      }) // ... / 3. Listen to model events / // an entry was edited      todo.on("add",  ...)              .on("remove",  function(items)  {                      $.each(items,  function()  {                            $("#"  +  this.id).remove()                      });              }); // ... }) 13年11⽉月30⽇日星期六
  8. $(function() { / 1. Initialize / var  todo  =  new

     Todo(); / 2. Listen to user events /      $("#new-­‐todo").keyup(function(e)  {            var  val  =  $.trim(this.value);            if  (e.which  ==  13  &&  val)  {            todo.add(val);  this.value  =  "";  }      });      $("#clear-­‐completed").click(function()  {          todo.remove("completed");      }) // ... / 3. Listen to model events / // an entry was edited      todo.on("add",  add).on("remove",  function(items)  {            $.each(items,  function()  {                  $("#"  +  this.id).remove()            });      }); // ... }) 13年11⽉月30⽇日星期六
  9. function  Todo(db)  {    var  items  =  [];    this.add

     =  function(name)  {        var  item  =  {  ...  }        items[item.id]  =  item;        this.trigger("add",  item);    }    this.remove  =  function(filter)  {        var  els  =  this.items(filter);        $.each(els,  function()  {            delete  items[this.id]        })        this.trigger("remove",  els);    }    $.observable(this); } $(function() { / 1. Initialize / var  todo  =  new  Todo(); / 2. Listen to user events /      $("#new-­‐todo").keyup(function(e)  {            var  val  =  $.trim(this.value);            if  (e.which  ==  13  &&  val)  {            todo.add(val);  this.value  =  "";  }      });      $("#clear-­‐completed").click(function()  {          todo.remove("completed");      }) // ... / 3. Listen to model events / // an entry was edited      todo.on("add",  add).on("remove",  function(items)  {            $.each(items,  function()  {                  $("#"  +  this.id).remove()            });      }); // ... }) 13年11⽉月30⽇日星期六
  10. function  Todo(db)  {    var  items  =  [];    this.add

     =  function(name)  {        var  item  =  {  ...  }        items[item.id]  =  item;        this.trigger("add",  item);    }    this.remove  =  function(filter)  {        var  els  =  this.items(filter);        $.each(els,  function()  {            delete  items[this.id]        })        this.trigger("remove",  els);    }    $.observable(this); } $(function() { / 1. Initialize / var  todo  =  new  Todo(); / 2. Listen to user events /      $("#new-­‐todo").keyup(function(e)  {            var  val  =  $.trim(this.value);            if  (e.which  ==  13  &&  val)  {            todo.add(val);  this.value  =  "";  }      });      $("#clear-­‐completed").click(function()  {          todo.remove("completed");      }) // ... / 3. Listen to model events / // an entry was edited      todo.on("add",  add).on("remove",  function(items)  {            $.each(items,  function()  {                  $("#"  +  this.id).remove()            });      }); // ... }) 13年11⽉月30⽇日星期六
  11. function  Todo(db)  {    var  items  =  [];    this.add

     =  function(name)  {        var  item  =  {  ...  }        items[item.id]  =  item;        this.trigger("add",  item);    }    this.remove  =  function(filter)  {        var  els  =  this.items(filter);        $.each(els,  function()  {            delete  items[this.id]        })        this.trigger("remove",  els);    }    $.observable(this); } $(function() { / 1. Initialize / var  todo  =  new  Todo(); / 2. Listen to user events /      $("#new-­‐todo").keyup(function(e)  {            var  val  =  $.trim(this.value);            if  (e.which  ==  13  &&  val)  {            todo.add(val);  this.value  =  "";  }      });      $("#clear-­‐completed").click(function()  {          todo.remove("completed");      }) // ... / 3. Listen to model events / // an entry was edited      todo.on("add",  add).on("remove",  function(items)  {            $.each(items,  function()  {                  $("#"  +  this.id).remove()            });      }); // ... }) 13年11⽉月30⽇日星期六
  12. function  Todo(db)  {    var  items  =  [];    this.add

     =  function(name)  {        var  item  =  {  ...  }        items[item.id]  =  item;        this.trigger("add",  item);    }    this.remove  =  function(filter)  {        var  els  =  this.items(filter);        $.each(els,  function()  {            delete  items[this.id]        })        this.trigger("remove",  els);    }    $.observable(this); } $(function() { / 1. Initialize / var  todo  =  new  Todo(); / 2. Listen to user events /      $("#new-­‐todo").keyup(function(e)  {            var  val  =  $.trim(this.value);            if  (e.which  ==  13  &&  val)  {            todo.add(val);  this.value  =  "";  }      });      $("#clear-­‐completed").click(function()  {          todo.remove("completed");      }) // ... / 3. Listen to model events / // an entry was edited      todo.on("add",  add).on("remove",  function(items)  {            $.each(items,  function()  {                  $("#"  +  this.id).remove()            });      }); // ... }) ࣄ݅ܥ౷ / Observer pattern trigger(eventName, args...) on(eventName, fn) off(eventName) 13年11⽉月30⽇日星期六
  13. (function() { / 1. Initialize / var  todo  =  new

     Todo(); / 2. Listen to user events /    $("#new-­‐todo").keyup(function(e)  {          var  val  =  $.trim(this.value);          if  (e.which  ==  13  &&  val)  {            todo.add(val);  this.value  =  "";  }    });    $("#clear-­‐completed").click(function()  {        todo.remove("completed");    }) // ... / 3. Listen to model events / // an entry was edited    todo.on("add",  add).on("remove",  function(items)  {          $.each(items,  function()  {                $("#"  +  this.id).remove()          });    }); // ... ) 13年11⽉月30⽇日星期六
  14. (function() { / 1. Initialize / var  todo  =  new

     Todo(); / 2. Listen to user events /    $("#new-­‐todo").keyup(function(e)  {          var  val  =  $.trim(this.value);          if  (e.which  ==  13  &&  val)  {            todo.add(val);  this.value  =  "";  }    });    $("#clear-­‐completed").click(function()  {        todo.remove("completed");    }) // ... / 3. Listen to model events / // an entry was edited    todo.on("add",  add).on("remove",  function(items)  {          $.each(items,  function()  {                $("#"  +  this.id).remove()          });    }); // ... ) 13年11⽉月30⽇日星期六
  15. (function() { / 1. Initialize / var  todo  =  new

     Todo(); / 2. Listen to user events /    $("#new-­‐todo").keyup(function(e)  {          var  val  =  $.trim(this.value);          if  (e.which  ==  13  &&  val)  {            todo.add(val);  this.value  =  "";  }    });    $("#clear-­‐completed").click(function()  {        todo.remove("completed");    }) // ... / 3. Listen to model events / // an entry was edited    todo.on("add",  add).on("remove",  function(items)  {          $.each(items,  function()  {                $("#"  +  this.id).remove()          });    }); // ... ) <script  type="html/todo">      <li  id="{id}">      <div  class="view">            <input  class="toggle"  type="checkbox">            <label>{name}</label>            <button  class="destroy"/>      </div>      <input  class="edit"  value="{name}">      </li> </script> 13年11⽉月30⽇日星期六
  16. (function() { / 1. Initialize / var  todo  =  new

     Todo(); / 2. Listen to user events /    $("#new-­‐todo").keyup(function(e)  {          var  val  =  $.trim(this.value);          if  (e.which  ==  13  &&  val)  {            todo.add(val);  this.value  =  "";  }    });    $("#clear-­‐completed").click(function()  {        todo.remove("completed");    }) // ... / 3. Listen to model events / // an entry was edited    todo.on("add",  add).on("remove",  function(items)  {          $.each(items,  function()  {                $("#"  +  this.id).remove()          });    }); // ... ) <script  type="html/todo">      <li  id="{id}">      <div  class="view">            <input  class="toggle"  type="checkbox">            <label>{name}</label>            <button  class="destroy"/>      </div>      <input  class="edit"  value="{name}">      </li> </script> 13年11⽉月30⽇日星期六
  17. Observables are the key to splitting your app into maintainable

    components. It's a classic design pattern to separate the Model from the View. A good event library is the single most important feature in a client-side framework. And this is where Riot places the biggest focus. https://moot.it/blog/technology/riotjs-the-1kb-mvp-framework.html 13年11⽉月30⽇日星期六
  18. No "Backbone way", "Angular way" or "Ember way". Frameworks come

    and go but classic programming skills are forever. https://moot.it/blog/technology/riotjs-the-1kb-mvp-framework.html 13年11⽉月30⽇日星期六
  19. Current data-binding frameworks promote the use of spaghetti on the

    HTML layer. Suddenly the onclick attribute is back! (I'm looking at you, Angular). Riot takes a puristic approach and does not allow you to do mix any logic inside HTML views. This is also the reason why Riot templating is fast – the templating logic is so simple. 13年11⽉月30⽇日星期六
  20. Unobtrusive Javascript ≡ Usability - ሣ࢖༻ऀိ㘸௚᧷ ≡ Graceful degration -

    ҆ᯩग़ࡨ ≡ Accessibility - ग़ࡨ࣌နೳఏڙ֩৺ޭೳ ≡ Separation of concern - ෼։+4࿨)5.- http://en.wikipedia.org/wiki/Unobtrusive_JavaScript 13年11⽉月30⽇日星期六
  21. You need a way of saying "Add the behavior here",

    just as CSS requires a way of saying "Use these styles here". In the mouseover example the id="mouseover" fulfills this function, but you could also use other attributes, or add behavior to, say, all <span>s that don’t have a class. -- ppk, quirksmode.org, 2004 13年11⽉月30⽇日星期六
  22. ࣗఆݩૉሱੑ http://angular-ui.github.io/bootstrap/    <carousel  interval="myInterval">        <slide  ng-­‐repeat="slide

     in  slides"  active="slide.active">            <img  ng-­‐src="{{slide.image}}"  style="margin:auto;">            <div  class="carousel-­‐caption">                <h4>Slide  {{$index}}</h4>                <p>{{slide.text}}</p>            </div>        </slide>    </carousel> 13年11⽉月30⽇日星期六
  23. ࣗఆݩૉሱੑ http://angular-ui.github.io/bootstrap/    <carousel  interval="myInterval">        <slide  ng-­‐repeat="slide

     in  slides"  active="slide.active">            <img  ng-­‐src="{{slide.image}}"  style="margin:auto;">            <div  class="carousel-­‐caption">                <h4>Slide  {{$index}}</h4>                <p>{{slide.text}}</p>            </div>        </slide>    </carousel> ݩૉ 13年11⽉月30⽇日星期六
  24. ࣗఆݩૉሱੑ http://angular-ui.github.io/bootstrap/    <carousel  interval="myInterval">        <slide  ng-­‐repeat="slide

     in  slides"  active="slide.active">            <img  ng-­‐src="{{slide.image}}"  style="margin:auto;">            <div  class="carousel-­‐caption">                <h4>Slide  {{$index}}</h4>                <p>{{slide.text}}</p>            </div>        </slide>    </carousel> ݩૉ ၏᫮ᅲత ሱੑ 13年11⽉月30⽇日星期六
  25. ఆٛ<slide> ≡ ໵ੋ declarative! app.directive('slide',  ['$parse',  function($parse)  {    return

     {        require:  '^carousel',        restrict:  'EA',        transclude:  true,        replace:  true,        templateUrl:  'template/carousel/slide.html',        scope:  {        },        link:  function  (scope,  element,  attrs,  carouselCtrl)  {            //Set  up  optional  'active'  =  binding            scope.$watch(function  parentActiveWatch()  {                var  parentActive  =  getActive(scope.$parent);                if  (parentActive  !==  scope.active)  {                    //  we  are  out  of  sync  and  need  to  copy                    if  (parentActive  !==  lastValue)  { 13年11⽉月30⽇日星期六
  26. ఆٛ<slide> ≡ ໵ੋ declarative! app.directive('slide',  ['$parse',  function($parse)  {    return

     {        require:  '^carousel',        restrict:  'EA',        transclude:  true,        replace:  true,        templateUrl:  'template/carousel/slide.html',        scope:  {        },        link:  function  (scope,  element,  attrs,  carouselCtrl)  {            //Set  up  optional  'active'  =  binding            scope.$watch(function  parentActiveWatch()  {                var  parentActive  =  getActive(scope.$parent);                if  (parentActive  !==  scope.active)  {                    //  we  are  out  of  sync  and  need  to  copy                    if  (parentActive  !==  lastValue)  { ਎ҝҰݸ ݩૉతੑ࣭ 13年11⽉月30⽇日星期六
  27. ఆٛ<slide> ≡ ໵ੋ declarative! app.directive('slide',  ['$parse',  function($parse)  {    return

     {        require:  '^carousel',        restrict:  'EA',        transclude:  true,        replace:  true,        templateUrl:  'template/carousel/slide.html',        scope:  {        },        link:  function  (scope,  element,  attrs,  carouselCtrl)  {            //Set  up  optional  'active'  =  binding            scope.$watch(function  parentActiveWatch()  {                var  parentActive  =  getActive(scope.$parent);                if  (parentActive  !==  scope.active)  {                    //  we  are  out  of  sync  and  need  to  copy                    if  (parentActive  !==  lastValue)  { ਎ҝҰݸ ݩૉతੑ࣭ ֎᧺ަڅ)5.- ʢߦҝ༻OHDMJDL౳ ัଊʣ 13年11⽉月30⽇日星期六
  28. ఆٛ<slide> ≡ ໵ੋ declarative! app.directive('slide',  ['$parse',  function($parse)  {    return

     {        require:  '^carousel',        restrict:  'EA',        transclude:  true,        replace:  true,        templateUrl:  'template/carousel/slide.html',        scope:  {        },        link:  function  (scope,  element,  attrs,  carouselCtrl)  {            //Set  up  optional  'active'  =  binding            scope.$watch(function  parentActiveWatch()  {                var  parentActive  =  getActive(scope.$parent);                if  (parentActive  !==  scope.active)  {                    //  we  are  out  of  sync  and  need  to  copy                    if  (parentActive  !==  lastValue)  { ਎ҝҰݸ ݩૉతੑ࣭ ֎᧺ަڅ)5.- ʢߦҝ༻OHDMJDL౳ ัଊʣ መࡍૢ࡞%0. 13年11⽉月30⽇日星期六
  29. More generally, JavaScript best practices often parallel those in other

    programming languages, such as encapsulation and abstraction layers, avoidance of global variables, meaningful naming conventions, use of appropriate design patterns, and systematic testing. Such principles are essential to large-scale software development, but have not been widely observed in JavaScript programming in the past; their adoption is seen as an essential component of JavaScript's transition from a "toy" language to a tool for serious development. “Unubstrusive Javascript”, Wikipedia 13年11⽉月30⽇日星期六