前端轻量级MVC框架CanJS详解


Posted in Javascript onSeptember 26, 2014

选择正确的库

创建一个JS APP没有好的工具是很有难度的,jQuery只是操作DOM的库,没有提供任何创建APP的基础,这就是为什么我们要一个类似CanJS的专门的库。

CanJS 是一个轻量级的MVC库,提供你创建一个JS APP所需的工具。

CanJS 是一个轻量级的MVC库,提供你创建一个JS APP所需的工具。 它提供有MVC (Model-View-Control) 模式的基本框架,模板动态绑定, route的支持且 内存安全。同时支持 jQuery, Zepto, Mootools, YUI, Dojo,有丰富的扩展和插件。

第一部分你将学到:
创建Control控制层 和 View 视图层(UI模板) 来显示联系人
用Model模型层来表示数据
使用 fixtures 插件模拟ajax返回数据
你肯定激动了!我们开始码代码吧。
建立好你的文件夹和HTML
你先给你的APP创建一个文件夹,目录下再建立4个子文件夹:css, js,views 和 img。如下:
contacts_manager
css
js
views
img

保存以下的代码为 index.html:

<!doctype html>

<html lang="en">

  <head>

    <meta charset="utf-8">

    <title>CanJS Contacts Manager</title>

    <link rel="stylesheet" href="css/bootstrap.min.css">

    <link rel="stylesheet" href="css/contacts.css">

  </head>

  <body>

    <div class="container">

      <div class="row">

        <div class="span12">

          <h1>Contacts Manager</h1>

        </div>

      </div>

      <div class="row">

        <div class="span3">

          <div class="well">

            <nav id="filter"></nav>

          </div>

        </div>

        <div class="span9">

          <div id="create"></div>

          <div id="contacts"></div>

        </div>

      </div>

    </div>

    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.js"></script>

    <script src="js/can.jquery.min.js"></script>

    <script src="js/can.fixture.js"></script>

    <script src="js/contacts.js"></script>

  </body>

</html>

在页面的底部你加载所需的JS(包括你的APP:contacts.js)。
教程中用到的CSS和图片文件可以下载。

用View来打造你的UI

View是用来渲染你APP的UI模板。CanJS 支持多种模板引擎,本文用EJS ,CanJS包含有而且支持动态绑定。
EJS 模板的标签与HTML很像,支持包含JS代码,三种常用标签如下:
<% CODE %> 执行JS
<%= CODE %> 执行JS,并将非转义的结果写入当前位置的HTML
<%== CODE %>  执行JS,并将转义的结果写入当前位置的HTML(用于子模板).
模板可以从文件或者script标签中加载得到,本教程从 EJS 文件加载。

显示联系人

要创建联系人,你得先建立一个EJS 模板,保存以下代码为contactsList.ejs 进你的views 文件夹:

<ul class="clearfix">

  <% list(contacts, function(contact){ %>

    <li class="contact span8" <%= (el)-> el.data('contact', contact) %>>

      <%== can.view.render('views/contactView.ejs', {

        contact: contact, categories: categories

      }) %>

    </li>

  <% }) %>

</ul>

contactLists.ejs 会渲染一个联系人列表,我们分析一下此模板:

<% list(contacts, function(contact){ %>

list()方法里的回调方法如果配合配置有观察者的list使用时,一旦list的数据发生改变就运用动态绑定重复调用。

<li class="contact span8" <%= (el)-> el.data('contact', contact) %>>

以上代码通过元素的回调方法生成 一个有联系人数据的<li>。 箭头后的方法执行后将el对象的数据设置给对应的元素。

<%== can.view.render('views/contactView.ejs', {

  contact: contact, categories: categories

}) %>

以上代码将子模板contactView.ejs 渲染成一个联系人。 can.view.render() 以模板和数据为参数返回HTML。

渲染单个联系人

子模板是一个将view组织成可管理块的好办法。 同时也使你的模板简单和易于重用。教程后面将会用到此模板来创建联系人,将下面的代码保存为contactView.ejs 进 views 文件夹:

<a href="javascript://" class="remove"><i class="icon-remove"></i></a>

<form>

<div class="row">

  <div class="span2">

    <img src="img/contact.png" width="100" height="100">

  </div>

  <div class="span3">

    <input type="text" name="name" placeholder="Add Name"

      <%= contact.attr('name') ? "value='" + contact.name + "'" : "class='empty'" %>>

    <select name="category">

      <% $.each(categories, function(i, category){ %>

        <option value="<%= category.data %>" <%= contact.category === category.data ? "selected" : "" %>>

          <%= category.name %>

        </option>

      <% }) %>

    </select>

  </div>

  <div class="span3">

    <label>Address</label>

    <input type="text" name="address"

      <%= contact.attr('address') ? "value='" + contact.address + "'" : "class='empty'" %>>

    <label>Phone</label>

    <input type="text" name="phone"

      <%= contact.attr('phone') ? "value='" + contact.phone + "'" : "class='empty'" %>>

    <label>Email</label>

    <input type="text" name="email"

      <%= contact.attr('email') ? "value='" + contact.email + "'" : "class='empty'" %>>

  </div>

</div>

</form>

联系人的属性都放入了 <input> 标签里,这就可以编辑更新用户的资料。

活化你的View(好文艺。。)

EJS 处理模板过程中如果有用到attr() ,它周围的代码将会交由事件处理器管理,监听对应属性的变化,当属性发生变化,APP中关联的UI将会被更新。这功能利益于模板动态绑定机制,EJS的动态绑定是有选择性的,只有使用了attr()时才会为对应的属性开启。
我们通过 contactView.ejs 中一个<input>标签来了解它的用法:

<input type="text" name="name" placeholder="Add Name"

  <%= contact.attr('name') ? "value='" + contact.name + "'" : "class='empty'" %>>

特殊标记里的代码将转变成事件绑定到此联系人的name属性上。当name属性发生变化,事件将被触发同时HTML结构会被更新。

使用can.Control来处理业务逻辑

can.Control 创建了一个可组织,内在无泄漏,全权控制器,能用来创建widget或者处理业务逻辑。你通过所需要数据为一个DOM元素创建一个Control实例,可以在你的Control中定义方法绑定事件。
当 Control 所关联的元素从DOM被删除时,Contol会自去销毁自己,同时清除所绑定的方法。
要创建一个 Control,通过传入你定义的包含有函数的对象给 can.Control() 来实现继承。接下来事件也给传进去了。
每个Contol实例都有几个重要的值和方法规范:
this ?  Control 实例的引用
this.element ? 实例中你所创建的DOM 元素
this.options ? 创建实例所需要的参数对象
init() ? 当实例创建成功时被调用

管理联系人

将以下代码片段添加到contacts.js 文件来创建管理联系人的Control:

Contacts = can.Control({

  init: function(){

    this.element.html(can.view('views/contactsList.ejs', {

      contacts: this.options.contacts,

      categories: this.options.categories

    }));

  }

})

当 Contacts 的实例被创建时, init() 会做两件事:
使用can.view() 来渲染联系人。 can.view() 接收两个参数:包含有模板和数据的文件或者stript标签;将返回一个documentFragment (一个管理DOM元素的轻量容器)。
使用jQuery.html()将can.view() 的documentFragment 插入Control的元素

使用Model来表现数据

Model 是APP数据的抽象层。本APP用到两个Model:一个对应联系人,一个对应类别。将以下代码添加到contacts.js:

Contact = can.Model({

  findAll: 'GET /contacts',

  create  : "POST /contacts",

  update  : "PUT /contacts/{id}",

  destroy : "DELETE /contacts/{id}"

},{});

 

Category = can.Model({

  findAll: 'GET /categories'

},{});

一个model 有5个方法可能定义来CRUD数据, 分别是findAll, findOne, create, update 和 destroy。你可重写这几个方法,不过最好的办法是使用 REST 服务(Representational State Transfer表述性状态转移)。正如上面的代码,你放心的忽略APP中不会用到的静态方法了。

这里要重点指出的是,model实例其实是源自 CanJS 的‘observables'。can.Observe 提供对象的观察者模式can.Observe.List 提供数组的观察模式。这意味着你可以通过attr()来get和set数据,同时监听数据的变动。
findAll() 方法返回一个 Model.list,就是当元素被添加或者移除时 can.Observe.List 所触发的事件。

使用Fixture来模仿Rest

Fixture拦截 AJAX 请求并通过文件或者方法来模拟应答。这对测试,或者后端还没有就绪时是非常有用的。Fixture就是APP的model模拟REST所需要的。
首先,你要准备一些数据给fixture,添加以下代码到:

var CONTACTS = [

  {

    id: 1,

    name: 'William',

    address: '1 CanJS Way',

    email: 'william@husker.com',

    phone: '0123456789',

    category: 'co-workers'

  },

  {

    id: 2,

    name: 'Laura',

    address: '1 CanJS Way',

    email: 'laura@starbuck.com',

    phone: '0123456789',

    category: 'friends'

  },

  {

    id: 3,

    name: 'Lee',

    address: '1 CanJS Way',

    email: 'lee@apollo.com',

    phone: '0123456789',

    category: 'family'

  }

];

 

var CATEGORIES = [

  {

    id: 1,

    name: 'Family',

    data: 'family'

  },

  {

    id: 2,

    name: 'Friends',

    data: 'friends'

  },

  {

    id: 3,

    name: 'Co-workers',

    data: 'co-workers'

  }

];

有了数据,要将其连接到fixture来模拟REST 。can.fixture()接收两个参数。 我们要拦截的URL和我们应答用的文件和方法。通常你要拦截的URL都是动态且遵循一个模式的。在需要在URL里添加以{}括起的通配符即可。

添加以下代码到contacts.js:

can.fixture('GET /contacts', function(){

return [CONTACTS];

});

 

var id= 4;

can.fixture("POST /contacts", function(){

return {id: (id++)}

});

 

can.fixture("PUT /contacts/{id}", function(){

return {};

});

 

can.fixture("DELETE /contacts/{id}", function(){

return {};

});

 

can.fixture('GET /categories', function(){

return [CATEGORIES];

});

前4个 fixture模拟 Contact model的GET, POST, PUT 和 DELETE 应答,第5个模拟 Category model的GET应答。

启动APP

你的APP有管理数据的Model,渲染联系人的 View,将这一切组织起来的的Control。现在要做的就是启动APP了。 Now you need to kickstart the application!
将以下代码添加到contacts.js :

$(document).ready(function(){

  $.when(Category.findAll(), Contact.findAll()).then(

    function(categoryResponse, contactResponse){

      var categories = categoryResponse[0],

        contacts = contactResponse[0];

 

      new Contacts('#contacts', {

        contacts: contacts,

        categories: categories

      });

  });

});

我们来分析一下这段代码:

$(document).ready(function(){

使用 jQuery.ready 方法监听DOM的ready。
$.when(Category.findAll(), Contact.findAll()).then(

  function(categoryResponse, contactResponse){

调用两个Model的 findAll() 方法来获取全部联系人的类型,由于findAll() 有延时, $.when()则确保两个请求同时完成后才执行回调方法。
var categories = categoryResponse[0],

  contacts = contactResponse[0];

从两个 findAll() 方法中获取对应Model实例的数据集。 是应答所返回的数组的第一个元素。

new Contacts('#contacts', {

  contacts: contacts,

  categories: categories

});

为 #contacts 元素创建Contact 的Control 。联系人和类型数据集传进Control。
用浏览器打开你的APP,你将看到如下的联系人列表:

前端轻量级MVC框架CanJS详解

总结

这是第教程系列的第一篇,你已经了解了CanJS的核心:
Models 你的APP数据的抽象层
Views 将数据转换成HTML的模板
Controls 组织关联一切

Javascript 相关文章推荐
js split 的用法和定义 js split分割字符串成数组的实例代码
May 13 Javascript
13 个JavaScript 性能提升技巧分享
Jul 26 Javascript
jQuery.holdReady()使用方法
May 20 Javascript
javascript删除元素节点removeChild()用法实例
May 26 Javascript
JS+CSS实现六级网站导航主菜单效果
Sep 28 Javascript
JS实现带有3D立体感的银灰色竖排折叠菜单代码
Oct 20 Javascript
jquery实现表格隔行换色效果
Nov 19 Javascript
基于JavaScript实现本地图片预览
Feb 08 Javascript
vue2.0 和 animate.css的结合使用
Dec 12 Javascript
使用vue实现grid-layout功能实例代码
Jan 05 Javascript
vue使用Font Awesome的方法步骤
Feb 26 Javascript
vue draggable resizable gorkys与v-chart使用与总结
Sep 05 Javascript
alert出数组中的随即值代码
Sep 25 #Javascript
jquery得到iframe src属性值的方法
Sep 25 #Javascript
jquery获得同源iframe内body下标签的值的方法
Sep 25 #Javascript
jquery 实现两Select 标签项互调示例代码
Sep 25 #Javascript
$(&quot;&quot;).click与onclick的区别示例介绍
Sep 25 #Javascript
Jquery设置attr的disabled属性控制某行显示或者隐藏
Sep 25 #Javascript
javascritp添加url参数将参数加入到url中
Sep 25 #Javascript
You might like
php session和cookie使用说明
2010/04/07 PHP
php实现不通过扩展名准确判断文件类型的方法【finfo_file方法与二进制流】
2017/04/18 PHP
基本jquery的控制tabs打开的数量的代码
2010/10/17 Javascript
jQuery EasyUI API 中文文档 - NumberSpinner数值微调器使用介绍
2011/10/21 Javascript
js获取html参数及向swf传递参数应用介绍
2013/02/18 Javascript
利用NodeJS和PhantomJS抓取网站页面信息以及网站截图
2013/11/18 NodeJs
jquery实现select选择框内容左右移动代码分享
2015/11/21 Javascript
jQuery+PHP+MySQL实现无限级联下拉框效果
2016/02/19 Javascript
ajax跨域调用webservice的实现代码
2016/05/09 Javascript
详解VueJs异步动态加载块
2017/03/09 Javascript
jQuery事件对象的属性和方法详解
2017/09/09 jQuery
禁止弹窗中蒙层底部页面跟随滚动的几种方法
2017/12/07 Javascript
浅析前端路由简介以及vue-router实现原理
2018/06/01 Javascript
vue左右侧联动滚动的实现代码
2018/06/06 Javascript
jQuery实现简易聊天框
2020/02/08 jQuery
[01:20]DOTA2 齐天大圣至宝动态展示
2016/12/13 DOTA
[45:15]Optic vs VP 2018国际邀请赛淘汰赛BO3 第一场 8.24
2018/08/25 DOTA
[01:02:04]EG vs Liquid 2019国际邀请赛淘汰赛 败者组 BO3 第一场 8.23
2019/09/05 DOTA
django如何连接已存在数据的数据库
2018/08/14 Python
Python qqbot 实现qq机器人的示例代码
2019/07/11 Python
在python Numpy中求向量和矩阵的范数实例
2019/08/26 Python
Python (Win)readline和tab补全的安装方法
2019/08/27 Python
Python Switch Case三种实现方法代码实例
2020/06/18 Python
Python 开发工具通过 agent 代理使用的方法
2020/09/27 Python
TensorFlow的环境配置与安装方法
2021/02/20 Python
瑞士灯具购物网站:Lampenwelt.ch
2018/07/08 全球购物
班组长安全职责
2014/01/05 职场文书
中学生自我鉴定
2014/02/04 职场文书
求职信怎么写范文
2014/05/26 职场文书
民生工作实施方案
2014/05/31 职场文书
市场策划求职信
2014/08/07 职场文书
群众路线自查自纠工作情况报告
2014/10/28 职场文书
2014年物资管理工作总结
2014/12/02 职场文书
中学生综合素质自我评价
2015/03/06 职场文书
2015年幼儿教师个人工作总结
2015/05/20 职场文书
MySQL一些常用高级SQL语句
2021/07/03 MySQL