前端轻量级MVC框架CanJS详解

 更新时间:2014年09月26日 09:14:31   投稿:hebedich  
你可能听说了这个新MVC框架: CanJS。还有什么比用它来创建一个联系人Web App更能深入了解它的办法呢?当看完这篇教程,你将全面了解用它的什么工具来创建你自己的Web App。

选择正确的库

创建一个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,你将看到如下的联系人列表:

总结

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

相关文章

  • TypeScript前端上传文件到MinIO示例详解

    TypeScript前端上传文件到MinIO示例详解

    这篇文章主要为大家介绍了TypeScript前端上传文件到MinIO示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • typescript封装消息提示框插件ew-message使用实战

    typescript封装消息提示框插件ew-message使用实战

    这篇文章主要为大家介绍了typescript封装消息提示框插件ew-message使用实战,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • requireJS使用指南

    requireJS使用指南

    如今最常用的JavaScript库之一是RequireJS。最近我参与的每个项目,都用到了RequireJS,或者是我向它们推荐了增加RequireJS。在这篇文章中,我将描述RequireJS是什么,以及它的一些基础场景。&nbsp;
    2016-04-04
  • less简单入门(CSS 预处理语言)

    less简单入门(CSS 预处理语言)

    Less 是一门 CSS 预处理语言,它扩充了 CSS 语言,增加了诸如变量、混合(mixin)、函数等功能,让 CSS 更易维护、方便制作主题、扩充
    2017-03-03
  • TS中Array.reduce提示没有与此调用匹配的重载解析

    TS中Array.reduce提示没有与此调用匹配的重载解析

    这篇文章主要为大家介绍了TS中Array.reduce提示没有与此调用匹配的重载解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • 联合类型Union Types与交叉类型Intersection Types区别解析

    联合类型Union Types与交叉类型Intersection Types区别解析

    这篇文章主要为大家介绍了联合类型Union Types与交叉类型Intersection Types区别详解
    2023-06-06
  • 基于Javascript实现页面商品个数增减功能

    基于Javascript实现页面商品个数增减功能

    本文给大家介绍基于Javascript实现页面商品个数增减功能,通过点击数量增减个数,代码分为前端页面,后台返回代码,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2019-07-07
  • 前端构建 Less入门(CSS预处理器)

    前端构建 Less入门(CSS预处理器)

    众多CSS预处理器中Less的语法最接近原生CSS,因此相对来说更容易上手,假如有JS、C#等编程经验的话,其实上述的几种预处理器的学习成本也不会特别高。下面是我们这阵子的学习笔记,以便日后查阅
    2017-03-03
  • TypeScript 5.0 正式发布及使用指南详解

    TypeScript 5.0 正式发布及使用指南详解

    这篇文章主要为大家介绍了TypeScript 5.0 正式发布及使用指南,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • TypeScript 基本数据类型实例详解

    TypeScript 基本数据类型实例详解

    这篇文章主要为大家介绍了TypeScript 基本数据类型实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01

最新评论