【ArchSummit架构师峰会】探讨数据与人工智能相互驱动的关系>>> 了解详情
写点什么

使用 decj 简化 Web 前端开发(一):声明式 Javascript 动态加载和浏览器事件绑定

  • 2013-10-17
  • 本文字数:5161 字

    阅读完需:约 17 分钟

引言

Web 前端开发中,开发人员经常需要处理一些常规问题,如:

  • 在页面中引用多个相互存在依赖关系的 Javascript 文件
  • 在页面中引用 CSS 文件
  • 浏览器事件绑定
  • 表单的数据填充、数据打包提交、数据校验和格式化
  • 页面初始化逻辑

采用传统的命令式编程范式来处理这些问题时,开发人员不得不反复地通过编写代码调用相关 API 来完成这些常规任务。事实上,开发人员的主要精力应该集中在业务逻辑实现上,而非在这些常规任务上过多消耗时间。声明式编程范式可以帮助开发人员以最小的工作量去快速搞定这些常规任务,从而能够将更多的精力放在业务逻辑的实现上。

decj 是一款以声明式编程范式为基础的 Javascript 开源框架。本文将介绍如何使用 decj 框架以近乎零编码的高效率方式去搞定这些常规任务。本期将介绍 decj 的声明式 Javascript 文件动态按需加载和声明式跨浏览器事件绑定。

decj 框架简介

decj 的优势及主要特性

decj 使得开发人员能够进行模块化的声明式编程,其目标在于简化 Web 应用开发中常规问题的处理,使得开发人员能够将更多的精力放在业务逻辑处理上。简单来说,decj 的优势在于:

  • 声明式编程:使得开发人员能够集中精力在写必须由其编写的代码,提升开发效率。
  • decj 所解决的问题是几乎每个 Web 应用中都要面对的普遍的问题:如事件绑定、表单数据校验、格式化、表单提交等。
  • 代码即文档(Code is document):采用 decj 开发的应用,其代码某种程度上就是文档。

decj 的优势也就是声明式编程的优势。通过声明式编程,decj 使得开发者面对日常工作中经常要处理的问题时能够集中精力在“真正”需要其处理的问题上。

比如,但某个页面上的一个按钮被单击时,一段业务处理逻辑需要被执行。显然,这段被调用的业务逻辑才是开发者真正要集中精力处理的问题,因为业务逻辑是怎么样的只有人才能知道,而任何框架/库是无法得知并为开发人员代劳的。相反,采用命令式编程的框架 / 库,在处理此类问题时,开发人员往往得首先分心去处理一些非业务逻辑的问题,比如,如何让这个按钮响应单击事件。比如,若使用 jQuery 来实现,开发人员需要在代码中的恰当位置/时机(如

window 的 onload 事件被触发后)调用 jQuery 的 bind 方法,才能使按钮被单击后执行一段业务逻辑。如清单 1 代码所示:

清单 1. 命令式编程:使用 jQuery 处理事件绑定

复制代码
$(document).ready(function(){// 加载页面完毕后执行该函数
$('#aButton').bind('click',function(){
// 在此处编写或者调用业务逻辑实现代码
});
});

而采用 decj,开发人员无须关心事件处理 API 以及在何处、何时机调用这些 API。可以更加关注业务逻辑。如清单 2 代码所示:

清单 2. 声明式编程:使用 decj 处理事件绑定

复制代码
events:{
"click@aButton":function(){// 按钮 aButton 被单击时执行该函数
// 直接在此处编写或者调用业务逻辑实现代码
}
}

可以看出,清单 2 的代码中并没有关于事件 API 的调用,开发人员因此可以不必关注这些 API 以及何时在何地方调用它们。开发人员可以重点关注如何响应相关事件,以实现业务逻辑。另外,decj 的应用代码可以充当文档。例如,如清单 2 的代码所示,对于页面上的某个元素的某个事件,是采用那个事件处理器响应的这个信息一目了然,这有助于问题定位,因为定位问题的人可以快速确认他需要的信息。

decj 支持以下几个主要特性:

  • 声明式 Javascript 文件按需动态加载
  • 声明式跨浏览器的事件绑定
  • 声明式 CSS 文件按需动态加载
  • 声明式 HTML 表单增强:表单内容自动填充、表单数据自动提交、表单重置增强、表单数据校验、数据格式化
  • 声明式页面 / 模块初始化
  • 声明式国际化(I18N)支持:支持多语言和按需加载资源文件
  • HTML 代码 /CSS 文件 /Javascript 文件 / 资源文件并行加载

下面我们详细介绍 decj 框架的两大基础---声明式编程和 Javascript 模块化编程。若读者已经熟悉这两基础可以跳过这两部分内容。然后,我们将从 Web 前端开发中的日常任务入手,探讨这些日常任务的常规实现方法的弊端以及如何利用 decj 的声明式编程去克服或绕过这些弊端。

声明式编程

多数通用编程语言 (如 Java 语言) 都采用命令式编程范式(Imperative)。这种方式下的编程,我们不仅要告诉机器它要做什么,还要告诉它如何去完成。而声明式(Declarative)编程是一种只需要告诉机器要完成什么,而无需说明如何去完成的一种编程范式。声明式编程往往能够减少开发人员的工作量,使代码更加简洁和富有表现力。

声明式编程的一个常见例子是数据库查询语言 SQL(Structured Query Language)。如下一个 SQL 查询语句,它仅仅说明了要查询什么样的数据,而无需说明如何去查询这些数据:

清单 3. 声明式编程的例子—SQL 语句

复制代码
SELECT license FROM frameworks WHERE name=’decj’
; -- 查询名为 decj 的框架的许可证信息

Javascript 语言所支持的 JSON(JavaScript Object Notation) 语法非常适合于作为 Javascript 声明式编程的语法基础。比如,假设某个 Javascript UI(User Interface)库提供了一个名为 createDialog 的函数用于创建基于 HTML 的网页对话框。该函数接受一个参数,用于配置所要创建的对话框的一些属性和行为。那么,在调用 createDialog 函数就可以使用 JSON 语法来声明所要创建的对话框的属性。如清单 4 所示:

清单 4. JSON 作为 Javascript 声明式编程的基础

复制代码
createDialog({// 创建一个标题为“成功”,宽 100 像素,高 200 像素的模态对话框
width:"100px",
height:"200px",
modal:true,
title:" 成功 "
});

Javascript 模块化编程

声明式编程是 decj 框架的核心,而模块化编程是其基础。decj 框架并不“再造车轮(Re-invent the wheel)”,它默认采用遵循 AMD(Asynchronous Module Definition)规范的 Javascript 库 RequireJS 来实现模块化编程。

AMD 规范定义了一个名为 define 的函数,通过该函数开发人员可以定义一个 Javascript 模块。在 AMD 规范中,一个 Javascript 模块可以是任何的 Javascript 对象,如函数、数组和普通 Javascript 对象。define 函数的签名如下:

复制代码
define(id?,dependencies?,factory);

该函数的各个参数含义如下:

id:可选字符串,表示所要定义的模块的唯一标识(ID)。

dependencies:可选数组,表示当前模块所依赖的其它各个模块的唯一标识。

factory:模块工厂。通常是一个匿名函数,负责返回所要定义的 Javascript 模块。该函数接受若干个参数,每个参数都与 dependencies 参数中指定的 ID 对应的 Javascript 模块对象一一对应。

如下代码定义了一个 Javascript 模块,该模块是一个普通的 Javascript 对象,如清单 5 所示:

清单 5. 基于 AMD 规范的 Javascript 模块化编程

复制代码
define(['jquery'],function(jquery){
// 该模块工厂依赖于 jQuery 库,故声明 ID 为 jquery 的模块为其依赖模块
// 返回一个模块对象。该对象包含一个名为 fieldValue 的方法
return {
fieldValue:function(fieldName){
// 返回当前页面中名为 fieldName 的表单控件的值
return jquery("[name='+fieldName+']").val();
}
//...
};
});

AMD 规范定义了另一名为 require 的函数用于加载指定的 Javascript 模块。不过,由于 decj 框架的声明式编程的特性,应用开发者一般无需使用该函数了。因此,本文不详细介绍该函数了。

开始使用 decj

首先,从 github 下载 decj 框架,并将其部署到你的 Web 服务器上。

然后,编写 Web 应用的主页。并在主页中添加一个 script 标签,使该标签的 src 属性和 data-main 属性分别引用 RequireJS 和 decj 框架的 Javascript 文件。如清单 6 所示:

清单 6. 开始使用 decj 框架

复制代码
<html>
<head>
<title>decj startup</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
function decjApp(){
return {
initialModule:'../module/initModule' // 定义初始模块的 ID
};
}
</script>
<script data-main="../js/lib/decj.js" src="../js/lib/requirejs.js"></script>
</head>
<body>
开始使用 decj!
</body>
</html>

模块化编程是 decj 的基础。一个 Web 应用可以包含多个模块,因此基于 decj 的 Web 应用需要一段应用启动代码。这段代码会定义 Web 应用的初始模块(即第一个会被加载的模块)的 ID。

应用启动代码是一个名为 decjApp 的函数,该函数返回一个普通的 Javascript 对象。该对象的 initialModule 属性用于指定当前应用第一个要加载的模块(初始模块),如清单 6 所示。decjApp 函数返回值的其它属性还可以用来定义 decj 的其它属性和行为。

最后,编写 decjApp 函数指定的初始模块对应的代码,这样一个基本的 decj Web 应用就可以使用了。清单 7 显示了一个示例模块代码。

清单 7. 一个 decj 示例模块

复制代码
define(['jquery','decj'],function(jQuery,decj){
alert('decj comes into play!');
return {// 返回模块对象
};
});

声明式 Javascript 文件动态按需加载

前文讲到,模块化编程是 decj 的基础。采用基于 AMD 规范的 Javascript 模块化编程的好处不仅仅是降低各个模块的耦合度,一定程度上也可以提高应用的性能。因为各个 Javascript 模块及其所依赖的其它模块只有当其代码确实要被调用的时候才会由模块加载器去动态加载。

decj 默认采用 RequireJS 作为其模块加载器,因此基于 decj 框架开发的应用中的各个 Javascript 文件在运行的时候是被动态按需加载的,而不是页面一加载时就把所有可能用的 Javascript 都一起加载。事实上,开发人员也可以选择使用其它符合 AMD 规范的模块加载器。

声明式跨浏览器事件绑定

客户端编程中,事件绑定是一个几乎每天都要处理的一个问题。比如,要使页面上一个 ID 为 chkShowLog 的 checkbox 响应单击事件。当该 checkbox 被单击时,ID 为 log 的元素会在被显示和隐藏之间来回切换。采用传统的编程范式来实现这样简单的一个功能,即便在采用 jQuery 这样能够使我们编写简练代码的 Javascript 库的情况下,开发人员也不得不编写代码来调用浏览器事件处理的相关 API,如清单 8 所示:

清单 8. 使用 jQuery 实现事件绑定

复制代码
$(document).ready(function(){// 在页面加载完毕后执行调用该函数
$('#chkShowLog').bind('click',function(ele){//ID 为 chkShowLog 的被单击后执行该函数
$('#log').toggle();// 显示或隐藏 ID 为 log 的元素
});
});

如果不采用任何 Javascript 库或框架,直接使用 Javascript 来实现清单 8 代码的功能,并且还要兼容不同浏览器的话,那么需要编写的代码就更加多和繁琐了。

decj 支持声明式的事件绑定。应用代码只需要在模块定义中声明哪个元素(事件目标元素)的哪个事件使用哪个监听器来处理,而无需调用任何与事件绑定有关的浏览器或者框架的 API。例如,清单 8 中的代码使用 decj 可以改写为清单 9 所示的代码:

清单 9. 使用 decj 的声明式事件绑定

复制代码
define(["decj"],function(decj){
return {
//…
events:{
"click@#chkShowLog":function(){
// 使该函数响应 ID 为 chkShowLog 的元素的 onclick 事件
$("#log").toggle();
}
}
//…
};
});

上面的的代码乍一看似乎比清单 8 中的代码要长。但事实上,清单 9 中的代码,除了下面清单 10 中的代码片段,其余的都是定义一个模块所必须的代码,而不属于事件绑定本身。并且,清单 9 中的代码没有任何 API 调用,开发人员只需要通过代码告诉框架本身无法知道的信息(即我们要采用哪个函数响应哪个元素的哪个事件)。

清单 10. 使用 decj 的声明式事件绑定

复制代码
events:{
"click@#chkShowLog":function(){// 使该函数响应 ID 为 chkShowLog 的元素的 onclick 事件
$("#log").toggle();
}
}

decj 的声明式事件绑定是跨浏览器兼容的。开发人员只需在模块定义中声明 events 属性。该属性是一个普通的 Javascript 对象。在该对象中可以声明多个事件绑定。每个事件绑定遵从如下格式:

“事件名 @目标元素对应的 CSS 选择器”: 事件监听器函数

例如,要使名为 switchLang 的函数响应 name 属性为 lang 的元素的 onchange 事件,只需在模块定义的 events 属性中声明:

复制代码
change@[name=lang]”:switchLang

事件绑定声明中“@“后面的 CSS 选择器遵从 jQuery 所支持的各个 CSS 选择器(见: http://api.jquery.com/category/selectors )。

小结

本期介绍了 decj 的优势及主要特性,并详细介绍了 decj 的“声明式 Javascript 文件动态加载”和”声明式跨浏览器事件绑定“这两个特性如何解决 Web 前端开发中以下常规问题:

  • 在页面中引用多个相互存在依赖关系的 Javascript 文件
  • 浏览器事件绑定

下一期将介绍 decj 的以下特性:

  • 声明式表单功能增强(表单自动填充、打包、数据校验和格式化)
  • 声明式页面 / 模块初始化

感谢崔康对本文的审校。

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

2013-10-17 00:103816

评论

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

架构实战营 模块四作业

felix

架构实战营

移动千牛开放体验治理实践与防治方案

阿里巴巴终端技术

ios android 用户体验 舆情分析 移动端

东汉末年,他们却把「服务雪崩」玩到了极致(干货

Java 程序员 后端

为什么建议大家使用 Linux 开发?爽(外加七个感叹号)

Java 程序员 后端

不错,这么好的东西不白嫖也太可惜了!阿里内部首发“Springboot成长笔记”

Java 程序员 后端

个性化推荐系统设计(3

Java 程序员 后端

为了加快速度,Redis都做了哪些“变态”设计

Java 程序员 后端

三分钟:极速体验JAVA版目标检测(YOLO4)

Java 程序员 后端

阿里 P8大牛的 Maven学习笔记,在 GitHub上仅一天就获赞 上万

Java maven 编程 程序员

两年CRUD,没料到我这渣二本,备战两个月面试阿里,居然侥幸拿下P6的offer

Java高级开发

两年JAVA程序员的面试总结

Java 程序员 后端

主流的消息队列MQ比较,详解MQ的4类应用场景

Java 程序员 后端

不就是Redis吗?竟让我一个月拿了8个offer,其中两家都是一线大厂

Java 程序员 后端

《Linux一学就会》第三章:文件系统的管理方法和xfs文件系统备份恢复

侠盗安全

Linux linux运维 云计算架构师

不要再本地启动项目调试了,试SpringBoot远程调试你会发现新大陆!

Java 程序员 后端

全面解读!构建边云一体的智能应用技术实践

百度开发者中心

最佳实践 方法论 边缘计算 前沿科技

二维码扫码登录是什么原理?

Java 程序员 后端

为了加快速度,Redis都做了哪些“变态”设计(1)

Java 程序员 后端

三面微软,四面雅虎,外企面经复盘总结,那些你不知道的面试技巧

Java 程序员 后端

小学妹与我畅聊黑客渗透技术

喀拉峻

黑客 网络安全 信息安全 渗透测试

三种常见的限流算法

Java 程序员 后端

上线GitHub七天后就标星87

Java 程序员 后端

WeTest六周年|质领未来,向新而行

WeTest

两道面试题,带你解析Java类加载机制

Java 程序员 后端

中高级开发面试必问的Redis面试题,看这篇就够了!

Java 程序员 后端

事大发了!小助理告诉我:小伙伴21天斩获字节offer的关键竟是这份面试题!

Java 程序员 后端

万字长文!从底层开始带你了解并发编程,彻底帮你搞懂Java锁!

Java 程序员 后端

三年开发,头铁面试阿里4面技术+1面HR,终获offer

Java 程序员 后端

不是吧!你还不会在微服务中如何设计一个权限授权服务?

Java 程序员 后端

为什么你不应该恨Java!

Java 程序员 后端

为什么你的insert就死锁了

Java 程序员 后端

使用decj简化Web前端开发(一):声明式Javascript动态加载和浏览器事件绑定_语言 & 开发_黄文海_InfoQ精选文章