前端轻量级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 相关文章推荐
浅析JavaScript中的常用算法与函数
Nov 21 Javascript
$.each遍历对象、数组的属性值并进行处理
Jul 18 Javascript
javascript 动态创建表格的2种方法总结
Mar 04 Javascript
简单实现异步编程promise模式
Jul 31 Javascript
javascript实现无缝上下滚动特效
Dec 16 Javascript
AngularJS表单验证中级篇(3)
Sep 28 Javascript
angularjs实现下拉列表的选中事件示例
Mar 03 Javascript
JavaScript ES6中const、let与var的对比详解
Jun 18 Javascript
JS图片轮播与索引变色功能实例详解
Jul 06 Javascript
vue实现word,pdf文件的导出功能
Jul 31 Javascript
JavaScript正则表达式验证登录实例
Mar 18 Javascript
原生js+css实现tab切换功能
Sep 17 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数据库连接mysql与mysqli对比分析
2016/01/04 PHP
PHP实现的注册,登录及查询用户资料功能API接口示例
2017/06/06 PHP
PHP商品秒杀问题解决方案实例详解【mysql与redis】
2019/07/22 PHP
php array_map()函数实例用法
2021/03/03 PHP
一个很简单的办法实现TD的加亮效果.
2006/06/29 Javascript
用jQuery技术实现Tab页界面之二
2009/09/21 Javascript
JavaScript 函数式编程的原理
2009/10/16 Javascript
由Javascript实现的页面日历
2011/11/04 Javascript
javascript检测浏览器flash版本的实现代码
2011/12/06 Javascript
JQuery的自定义事件代码,触发,绑定简单实例
2013/08/01 Javascript
js实现跨域的方法实例详解
2015/06/24 Javascript
JS随机洗牌算法之数组随机排序
2016/03/23 Javascript
JS 调用微信扫一扫功能
2016/12/22 Javascript
Angular4学习教程之DOM属性绑定详解
2018/01/04 Javascript
读懂CommonJS的模块加载
2019/04/19 Javascript
vue项目打包之开发环境和部署环境的实现
2020/04/23 Javascript
js+css实现扇形导航效果
2020/08/18 Javascript
小程序点餐界面添加购物车左右摆动动画
2020/09/23 Javascript
Python下Fabric的简单部署方法
2015/07/14 Python
django创建自定义模板处理器的实例详解
2017/08/14 Python
pandas 条件搜索返回列表的方法
2018/10/30 Python
python 实现数字字符串左侧补零的方法
2018/12/04 Python
Python Cookie 读取和保存方法
2018/12/28 Python
Python常用模块之requests模块用法分析
2019/05/15 Python
Python中顺序表原理与实现方法详解
2019/12/03 Python
使用python创建生成动态链接库dll的方法
2020/05/09 Python
python实现数字炸弹游戏
2020/07/17 Python
详解Scrapy Redis入门实战
2020/11/18 Python
纽约通行卡:The New York Pass(免费游览纽约90多个景点)
2017/07/29 全球购物
先进个人事迹材料
2014/01/25 职场文书
北京大学自荐信范文
2014/01/28 职场文书
吃空饷专项治理工作实施方案
2014/03/04 职场文书
计算机专业自荐信范文
2014/05/28 职场文书
历史学专业求职信
2014/06/19 职场文书
2016年五一促销广告语
2016/01/28 职场文书
Python语言规范之Pylint的详细用法
2021/06/24 Python