AICon上海|与字节、阿里、腾讯等企业共同探索Agent 时代的落地应用 了解详情
写点什么

Durandal 快速入门

  • 2014-03-05
  • 本文字数:6201 字

    阅读完需:约 20 分钟

Durandal 是一个轻量级的 JavaScript 框架,其目标是单页面应用(SPAs)的开发变得简单而优雅。它支持 MVC、MVP 和 MVVM 等模式,因此不论你采用哪种类型的前端架构,Durandal 都能胜任。

Durandal 以 RequireJS 为基础,加上一个轻量级的惯例层,带来了令人惊叹的生产效率,并且帮助你维持稳健的编码实践。配上开箱即用的富界面组件、模态对话框、事件 / 消息、组件、过渡效果、导航等等,使你可以轻松开发出任何你能想象的应用。

尽管 Durandal 才发布大约一年时间,但其社区正以飞快的速度成长。

因此,我们推出了一个 Kickstarter 来帮助我们在 2014 年完成一些神奇的事情,希望你能关注。但现在,我将向你展示如何开发一个简单的 Durandal 程序。

要开始使用 Durandal,可以有多种方式,这取决于你的平台。因为 Durandal 是一个纯 JavaScript 库,独立于任何服务端平台,我们尝试用多种方式来打包,以满足各类 Web 开发人员。在本教程中,我们将直接使用 HTML Starter Kit。你可以在官方网站上直接下载

下载完HTML Starter Kit 后,解压缩,你就可以直接在Firefox 各版本中打开_index.html_ 页面,运行其示例程序了。或者你也可以将其部署到Web 服务器中,浏览其index 页面。

Starter Kit 演示了一个基本的导航架构,包括导航、页面历史、数据绑定、模态对话框等等。当然,我们不只是看看而已,我们要从头开始写一个小程序。首先打开 app 文件夹,删除里面的所有内容,然后删除 _index.html_。这样我们就有了一个空项目,并且预配置了所有必须的 scripts 和 css。

_*__ 注:_IE, Chrome__ 和 __Safari__ 可能无法从文件系统中直接打开这类文件。如果你仍希望使用这些浏览器,可以将其部署到你喜欢的 _Web_ 服务器中。

Index.html
我们开始编写 _index.html_ 文件,内容如下:

复制代码
<html>
<head>
<link rel="stylesheet" href="lib/bootstrap/css/bootstrap.css" />
<link rel="stylesheet" href="lib/font-awesome/css/font-awesome.css" />
<link rel="stylesheet" href="lib/durandal/css/durandal.css" />
<link rel="stylesheet" href="css/starterkit.css" />
</head>
<body>
<div id="applicationHost"></div>
<script src="lib/require/require.js" data-main="app/main"></script>
</body>
</html>

我们看到,文件中只有一些 css 样式文件,一个 id 为 applicationHost 的简单的 div, ,一个 script 标签。我们加上了 Bootstrap FontAwesome ,使界面看起来美观一些,但它们并不是 Durandal 所必须的。我们要关注的关键代码是 script 标签。

Durandal 采用 RequireJS 作为其核心构件之一,鼓励模块化的编程方式。在 Durandal 应用中,所有的 JS 代码都写在模块中。上文 _index.html_ 中的 script 标签就是用于加载 RequireJS 来完成框架的模块策略。当模块加载器完成初始化后,它通过 _data-main_ 属性的值来启动应用。就像 C 语言中的 _main_ 函数一样,_data-main_ 属性指向的是主模块,是整个应用的入口。让我们进行下一步,创建这个模块。首先创建一个名为 _main.js_ 的文件,把它放到 _app_ 文件夹下。其代码如下:

复制代码
main.js
requirejs.config({
paths: {
'text': '../lib/require/text',
'durandal':'../lib/durandal/js',
'plugins' : '../lib/durandal/js/plugins',
'transitions' : '../lib/durandal/js/transitions',
'knockout': '../lib/knockout/knockout-2.3.0',
'jquery': '../lib/jquery/jquery-1.9.1'
}
});
define(function (require) {
var system = require('durandal/system'),
app = require('durandal/app');
system.debug(true);
app.title = 'Durandal Starter Kit';
app.configurePlugins({
router:true,
dialog: true
});
app.start().then(function() {
app.setRoot('shell');
});
});

这是一个非常标准的 Durandal 应用的样板配置。我们详细地看一下代码。

文件的最上面是 _requirejs.config_ 代码。这部分代码用于配置模块系统,使其能够找到核心库。配置的都是一些相对路径,因此当我们引用“jQuery”时,模块系统就知道到哪里去加载它。我们可以看到,路径的配置包括 requirejs 的 text 插件(用于加载视图)、Durandal 的核心模块、Knockout 和 jQuery。 Durandal 采用 RequireJS 来构建模块, Knockout 用于数据绑定,而 jQuery 用于浏览器层的抽象。

完成 RequireJS 的配置后,定义应用的主模块。Durandal 应用的所有代码都写在模块中,并且按照以下格式:

复制代码
define(function (require) {
var someModule = require('some/module');
...other modules required here...
return someValue;
});

我们用 _define_ 函数来创建一个模块。它的参数是另一个函数,这个函数是模块的工厂。也就是说,这个函数的返回值将是模块的一个实例。当函数返回一个对象时,你可以创建单例对象模块。或者返回一个函数,你可以创建一个类构造器模块。另外,你也可以声明该模块依赖于其他模块,此时你需要使用 _require_ 函数来引入其他模块。

在上面的主模块代码中,你可以看到我们引入或者说 _require_ 了两个 Durandal 模块:system_ 和 _app。然后通过 _system_ 模块开启框架的调试功能,通过 _app_ 模块设置应用的标题,加载两个可选的插件。最后,我们调用 _start_ 方法启动应用。该方法是一个异步的动作,当它结束时,调用一个特殊的方法:setRoot

当 _setRoot_ 方法被调用时,才会真正去展示页面。你可以将 _root_ 视为应用的主窗口、主布局或者应用的 shell。该方法的参数指向一个包含 shell 的行为的模块。现在唯一的问题是,嗯,它还不存在。好吧,我们开始创建它。

Shell

Durandal 的大部组件都同时包含行为(behavior)和展现(presentation)。你的应用的 shell 毫无疑问也遵循这个规则。我们将通过两个文件来展示这两个不同的概念:一个是用于行为的 JavaScript 文件,另一个是用于展现的 HTML 文件。在 _app_ 文件夹下创建:shell.js_ 和 _shell.html_。_ 两个文件的代码如下所示,我们来看看它们是如何工作的。

复制代码
shell.js
define(function (require) {
var app = require('durandal/app'),
ko = require('knockout');
return {
name: ko.observable(),
sayHello: function() {
app.showMessage('Hello ' + this.name() + '! Nice to meet you.',
'Greetings');
}
};
});
shell.html
<section>
<h2>Hello! What is your name?</h2>
<form class="form-inline">
<fieldset>
<label>Name</label>
<input type="text" data-bind="value: name, valueUpdate:
'afterkeydown'"/>
<button type="submit" class="btn" data-bind="click: sayHello, enable:
name">Click Me</button>
</fieldset>
</form>
</section>

首先看看 _shell.js_,正如我们之前讲到的,它定义了一个模块。同时它还依赖两个别的模块:app_ 和 _knockout。从代码可以看出,模块返回了一个对象。这个对象有一个 _name_ 属性和一个 _sayHello_ 方法。属性 _name_ 看起来有点特别,它是我们使用 Knockout 创建的一个可被观察的 (observable) 属性。可被观察的(observable)属性支持数据到 html 的双向绑定,变更通知和一些其它特性。注意看 _sayHello_ 中调用了 _app_ 模块的方法来显示一个包含 _name_ 属性值的消息对话框。

要完全理解这些代码,你应该将它与 HTML 代码放到一起来看。注意观察 HTML 中的 _data-bind_ 属性。该属性将 HTML 与模块中的属性和方法联系起来。例如 input 标签通过 _data-bind_ 属性将它的值与 _name_ 属性连接起来的。它同时还指定,当 key down 事件发生时,属性 _name_ 的值将被更新(如果不指定事件,则默认光标离开时会进行更新)。同样地,按钮的 _click_ 事件绑定了 _sayHello_ 方法,而且仅当 _name_ 属性的值为真时,按钮才是有效状态。看起来是不是很酷?现在就把应用运行起来,你可以用 Firefox 打开 _index.html_,或者如果你使用其他浏览器的话,可以部署到 Web 服务器下然后浏览 _index.html_。当页面打开时,会经历以下这些步骤:

  1. 加载 RequireJS。
  2. RequireJS 将加载 _main.js_,然后配置框架。
  3. _main.js_ 调用 _setRoot_ 展示整个应用。
  4. 加载 _shell.js_ 和 _shell.html_,绑定数据,然后注入到页面的 _applicationHost_ div 中。

当页面加载完成后,在输入框中试着输入一些内容,观察按钮的有效性是如何变化的,然后点击按钮看看发生了什么。

导航

上面的例子看起来不错,但大多数应用都不只有一页。所以,我们开始下一步,将它扩展成具有页面导航的应用。首先,把 _shell.js_ 改名为 _home.js_,shell.html_ 改名为 _home.html。然后创建两个新文件 _shell.js_ 和 _home.js_ 用于导航。以下是两个新文件的代码:

复制代码
shell.js
define(function (require) {
var router = require('plugins/router');
return {
router: router,
activate: function () {
router.map([
{ route: '', title:'Home', moduleId: 'home', nav: true }
]).buildNavigationModel();
return router.activate();
}
};
});
shell.html
<div>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<ul class="nav" data-bind="foreach: router.navigationModel">
<li data-bind="css: { active: isActive }">
<a data-bind="attr: { href: hash }, html: title"></a>
</li>
</ul>
<div class="loader pull-right" data-bind="css: { active:
router.isNavigating }">
<i class="icon-spinner icon-2x icon-spin"></i>
</div>
</div>
</div>
<div class="container-fluid page-host" data-bind="router: {
transition:'entrance' }"></div>
</div>

可以看到,shell.js_ 仍然是模块的标准定义方式。首先 require 了一个 router 插件,利用它创建对象。我们配置了一个 route 指向 _home_ 模块。router 配置中指定了每个 route 匹配的模式、显示的标题、对应加载的 moduleId,以及 _route_ 匹配时,是否应该将此 route 显示到导航栏中(nav:true)。配置完成后,调用 _buildNavigationModel。该方法利用 route 信息,构建一个特殊的集合,这个集合通过数据绑定,可以用来显示页面顶部的导航栏。最后,我们激活这个 router。所有这些都发生在 _activate_ 方法中。每次 Durandal 向屏幕展现一个组件时,它都会查找可选的 _activate_ 回调方法。如果找到,就会在数据绑定前调用此方法。因此这种方式允许你执行自定义的激活代码,例如上面的例子在 _activate_ 方法中配置应用程序的 router。

如果把模块和视图的代码放到一起,这次你应该很容易理解它们是如何工作的了。首先绑定 _navigationModel_ 属性,生成一个导航栏 navbar。每一个可导航的 route 被转换成一个 li 标签,其中包含一个链接。链接的标题对应 _title_,地址对应 _hash_。同时,在 navbar 中还包含了一个 spinner 动画图标,并将其与 _router.isNavigating_ 属性进行绑定。当应用从一个页面导航到另一页面时,我们就能看到一个 spinner 动画图标。

在 html 文件的最下方有一个特殊的 _router_ 绑定,也就是连接模块中的 router。它扮演占位符的角色,用于显示当前页面。属性 _transition_ 表示当页面发生变化时,Durandal 要使用“entrance”过渡动画。

现在将它运行起来。它看起来跟之前差不多,除了在顶端多了个导航栏。因为现在仍然只有一个页面,因此没有什么令人印象深刻的东西。我们开始创建第二个页面,以便能够在页面间前进或后退。

第二页:雷尼尔峰

我们创建的第二个页面将调用 Flickr,获取一组雷尼尔峰的照片并进行展示。首先修改 shell 的 router 配置,增加另一个 route,这样它就有了两个 route:

复制代码
{ route:'', title:'Home', moduleId:'home', nav:true },
{ route:'rainier', title:'Mount Rainier', moduleId:'rainier', nav:true }

第一个是我们之前的 home route,第二个是新的 route 用于即将创建的新页面。这时候,希望你已经知道应该怎么做了。我们为新页面创建一个模块(module)和一个视图(view)。

复制代码
rainier.js
define(function (require) {
var http = require('plugins/http'),
ko = require('knockout');
var url = 'http://api.flickr.com/services/feeds/photos_public.gne';
var qs = {
tags: 'mount ranier',
tagmode: 'any',
format: 'json'
};
return {
images: ko.observableArray([]),
activate: function () {
var that = this;
if (this.images().length > 0) {
return;
}
return http.jsonp(url, qs, 'jsoncallback').then(function(response) {
that.images(response.items);
});
}
};
});
rainier.html
<section>
<h2>Mount Rainier</h2>
<div class="row-fluid">
<ul class="thumbnails" data-bind="foreach: images">
<li>
<img style="width: 260px; height: 180px;" data-bind="attr:
{ src: media.m }"/>
</li>
</ul>
</div>
</section>

我想你已经能够看出来它是怎么工作的了。新模块在 activate 回调方法中使用 http 插件调用 Flickr 的 api 去获取图片。然后将数据保存到 _images_ 属性中。_Images_ 是一个可被观察的(observable)数组,页面绑定这个数组并展示图片。

将应用运行起来,可以看到在导航栏中出现了两项,你能在两者间进行前进和后退操作。你也可以使用浏览器的返回按钮。Durandal 能做的事情远不止这些。也许你该下载 Starter Kit 并仔细研究,就会发现一些更有趣的东西。又或者你应该将官方的示例程序下载下来,看看各种功能是如何工作的,例如发布 / 订阅消息、自定义模态对话框、组件、高级的视图组合等等。如果你想看一些更酷的东西,可以看看 Kickstarter 资助的系列培训中我们将构建的应用。

Durandal 通过模块化的方法,使得构建富客户端,动态的 JavaScript 应用更加简单。因此理所当然地,我们的社区发展得非常快。我们的计划才刚刚开始,希望你也能加入我们的奇妙旅程。请浏览一下我们的 Kickstarter 。2014 年我们已经有了一些很酷的目标,同时,也有一些非常棒的奖励给你。Web 正在发生改变,光明的未来指日可待,希望我们在那里相逢。

关于作者

Rob Eisenberg是一个 JavaScript 专家和.NET 架构师,工作在佛罗里达州的塔拉哈西,同时他还是 Blue Spire 咨询公司的总裁。Rob 在 9 岁时开始计算机编程,那时他彻底地爱上了家里的新 Commodore 64。他痴迷于编程,从 Commodore Basic 语言到 Q Basic 和 QuickBasic 开始,很快转到 C, C++, C#和 JavaScript。Rob 定期在 devlicio.us 上发表技术文章,并经常在区域性的专业活动中和相关公司做演讲和报告,主要涉及 Web 和.NET 技术,敏捷软件开发实践和 UI 工程。他是 Sam 出版的《Teach Yourself WPF in 24 Hours》一书的联合作者,也是 Durandal 和 Caliburn.Micro 框架的架构师和首席开发人员。

原文英文链接: Durandal: Quick Start


感谢马国耀对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

2014-03-05 10:3415052

评论

发布
暂无评论
发现更多内容

软件测试/测试开发 | 白盒测试方法论

测试人

软件测试 测试开发 白盒测试

认证培训 |【AIRIOT物联网平台应用与实战训练营】第二期火热报名中!

AIRIOT

物联网 培训 训练营 认证

观测云产品更新|新增用户访问监测自动化追踪;新增 CDN 质量分析;新增自定义查看器导航菜单等

观测云

运维 云服务 可观测 观测云 可观测性用观测云

CleanMyMacX4.20完美试用版功能介绍

茶色酒

CleanMyMacX4.20

Tuxera Ntfs2023永久电脑版下载

茶色酒

Tuxera NTFS2023

软件测试/测试开发 | 什么是软件缺陷

测试人

软件测试 自动化测试 测试开发

掌握统计学,做选择时的纠结少一半!

图灵教育

R语言 统计学

未来智安入选CCIA“网络安全服务阳光行动”成员单位

未来智安XDR SEC

网络安全

LeetCode题解:1238. 循环码排列,归纳法,详细注释

Lee Chen

JavaScript 算法 LeetCode

如何将BI 工具与业务系统进行单点登录对接,实现用户权限通用

葡萄城技术团队

Studio One6绿色版音乐创作编辑软件

茶色酒

Studio One6

hive etl 通过 ETL 导出 Hive 中的数据

weigeonlyyou

hadoop hive Hive SQL hadoo Kafka ETL

将企业文件共享解决方案与数据丢失防护配对

镭速

为什么负责任的技术始于数据治理

葡萄城技术团队

SREWorks前端低代码组件生态演进:monorepo架构重构和远程组件加载实践

阿里云大数据AI技术

大数据 运维 前端 低代码 企业号 2 月 PK 榜

Portraiture4.0简体中文版本磨皮插件

茶色酒

Portraiture4.0

【iOS逆向与安全】使用ollvm混淆你的源码

小陈

ios开发 逆向 iOS逆向 ios安全 逆向技术

掌握统计学,做选择时的纠结少一半!

图灵社区

R语言 统计学

BPMN2.0是什么?它能解决企业流程管理中哪些问题?

优秀

BPMN

加快发展先进制造业势在必行!

加入高科技仿生人

低代码 数字化转型 制造业 先进制造 数字转型

软件测试 | 代码覆盖率集成

测吧(北京)科技有限公司

测试

ChatGPT为什么在一夜之间爆火?| 社区征文

三掌柜

人工智能 openai ChatGPT

社区1月月报|OceanBase 4.1 即将发版,哪些功能将会更新?

OceanBase 数据库

数据库 oceanbase

架构实战营-模块1作业

sadhu

OpenHarmony应用集成AGC云存储

白晓明

云存储 DevEco Studio 端云协同 OpenHarmony应用开发 AppGallery Connect

CleanMyMac4.20免费版下载

茶色酒

CleanMyMac4.20

软件测试/测试开发 | Bug定位方法

测试人

软件测试 自动化测试 测试开发 bug定位

不到3天,省下近10万,低代码是真给老板省钱啊!

引迈信息

前端 敏捷开发 低代码

软件测试/测试开发 | 常用测试策略与测试手段

测试人

软件测试 自动化测试 测试方法

KCL v0.4.5 发布 - 更好的编写便利性改进,稳定性,体验提升与多平台支持

Peefy

开源 DevOps 云原生 编程语言 ​Rust

Durandal快速入门_JavaScript_Rob Eisenberg_InfoQ精选文章