前端轻量级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 相关文章推荐
通过event对象的fromElement属性解决热区设置主实体的一个bug
Dec 22 Javascript
jquery 插件学习(四)
Aug 06 Javascript
js判断设备是否为PC并调整图片大小
Feb 12 Javascript
windows8.1+iis8.5下安装node.js开发环境
Dec 12 Javascript
AngularJS入门教程中SQL实例详解
Jul 27 Javascript
jQuery实现的兼容性浮动层示例
Aug 02 Javascript
js canvas实现适用于移动端的百分比仪表盘dashboard
Jul 18 Javascript
JS与CSS3实现图片响应鼠标移动放大效果示例
May 04 Javascript
vue项目中使用fetch的实现方法
Apr 25 Javascript
vue 地图可视化 maptalks 篇实例代码详解
May 21 Javascript
vue组件 keep-alive 和 transition 使用详解
Oct 11 Javascript
node.js文件操作系统实例详解
Nov 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
php5.2.0内存管理改进
2007/01/22 PHP
PHP生成HTML静态页面实例代码
2008/08/31 PHP
php生成二维码的几种方式整理及使用实例
2013/06/03 PHP
php之XML转数组函数的详解
2013/06/07 PHP
PHP类继承 extends使用介绍
2014/01/14 PHP
php自动给网址加上链接的方法
2015/06/02 PHP
PHP时间处理类操作示例
2018/09/05 PHP
php设计模式之组合模式实例详解【星际争霸游戏案例】
2020/03/27 PHP
改变文件域的样式实现思路同时兼容ie、firefox
2013/10/23 Javascript
js判断为空Null与字符串为空简写方法
2014/02/24 Javascript
jquery动态添加元素事件失效问题解决方法
2014/05/23 Javascript
jquery动态加载js/css文件方法(自写小函数)
2014/10/11 Javascript
jquery解决客户端跨域访问问题
2015/01/06 Javascript
酷! 不同风格页面布局幻灯片特效js实现
2021/02/19 Javascript
纯js实现手风琴效果代码
2020/04/17 Javascript
浅谈AngularJS中ng-class的使用方法
2016/11/11 Javascript
详解nodejs模板引擎制作
2017/06/14 NodeJs
React教程之封装一个Portal可复用组件的方法
2018/01/02 Javascript
Webpack的Loader和Plugin的区别
2020/11/09 Javascript
python实现简单socket程序在两台电脑之间传输消息的方法
2015/03/13 Python
解决python3 urllib中urlopen报错的问题
2017/03/25 Python
Python爬虫之Selenium实现关闭浏览器
2020/12/04 Python
scrapy-splash简单使用详解
2021/02/21 Python
一款css实现的鼠标经过按钮的特效
2014/09/11 HTML / CSS
浅析HTML5中的download属性使用
2019/03/13 HTML / CSS
MAC Cosmetics官方网站:魅可专业艺术彩妆
2019/04/10 全球购物
咖啡厅创业计划书范本
2014/01/22 职场文书
超市采购员岗位职责
2014/02/01 职场文书
股东协议书
2014/04/14 职场文书
《画家乡》教学反思
2014/04/22 职场文书
硕士研究生就业推荐信
2014/05/18 职场文书
单位委托书怎么写
2014/08/02 职场文书
2015年公共机构节能宣传周活动总结
2015/03/26 职场文书
小米11和iphone12哪个值得买?小米11对比iphone12评测
2021/04/21 数码科技
Redis主从配置和底层实现原理解析(实战记录)
2021/06/30 Redis
HTML页面中使两个div并排显示的实现
2022/05/15 HTML / CSS