NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

使用 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:103819

评论

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

Java获取本机IP

代码的路

参与赢大奖!阿里云机器学习平台PAI助力开发者激发AIGC潜能

阿里云大数据AI技术

阿里云 AIGC

低代码赛道拥挤 生态聚合成为破局关键

力软低代码开发平台

微服务高并发:授权与系统自适应功能的实现原理

互联网架构师小马

授权 系统自适应

集结开发者力量,6月17日华为开发者联创日·深圳站即将启航!

说山水

程序员晋升指南!13年顶级架构设计经验的锦囊妙计与实践分享

互联网架构师小马

Java 架构

QCon高分演讲:火山引擎容器技术在边缘计算场景下的应用实践与探索

火山引擎边缘云

边缘计算 容器技术 实践 火山引擎边缘云

如何理解 REST 和 RPC 之间的差异?

Apifox

程序员 RPC Rest 协议 RPC调用

执行器-Query 执行详解

KaiwuDB

KaiwuDB Query执行

源生创新 云享未来|GOTC全球开源技术峰会华为云云原生精彩时刻

华为云开发者联盟

云原生 后端 华为云 华为云开发者联盟 企业号 6 月 PK 榜

正式启动|2023中国高校计算机大赛—大数据挑战赛火热报名中!

云智慧AIOps社区

大数据 AI 算法 运维 智能运维

KW 新闻 | KaiwuDB 亮相数字中国并发布离散制造场景解决方案

KaiwuDB

数字中国 KaiwuDB 离散制造业解决方案

在币圈不想被割韭菜?学习怎么当一个成功的项目方

加密先生

面试官:如何实现开关降级

互联网架构师小马

Java sentinel aop 开关降级

低代码崛起:让程序员饭碗不保?人工智能或成“帮凶”

加入高科技仿生人

人工智能 低代码 数智化

Sentinel熔断降级的规则及实现原理

互联网架构师小马

Java sentinel 熔断降级

TCMalloc 技术细节详解

KaiwuDB

KaiwuDB TCMalloc

什么是低代码(Low-Code)?

优秀

低代码 低代码Low-Code 低代码是什么

CloudOps自动化运维套件助力企业更好上云、用云、管云

阿里云弹性计算

云计算 大数据 阿里云 物联网

AIGC时代,设计软件应该做什么?丨AIGC X 企业服务

ToB行业头条

在百度生态用达人营销赋能品牌生意增长 | 度星选白皮书

说山水

一文走进 SQL 编译-语义解析

KaiwuDB

KaiwuDB SQL编译

开发一次、运行多端:Weex与小程序容器的卓越优势解析

FinFish

Weex 跨端开发 小程序容器 跨端框架 跨端技术

十年磨一剑,超级人工智能如果出现,人类将如何应对挑战?

这我可不懂

人工智能 AI AGI

如何修复 Mac 上的“未找到匹配的密钥交换方法”

背包客

macos SSH Mac 软件 mac电脑 macOS 13 Ventura

当GaussDB遇上了毕昇编译器

华为云开发者联盟

数据库 后端 华为云 华为云开发者联盟 企业号 6 月 PK 榜

破防了!阿里用17个真实企业级项目阐述Java系统分析与架构设计

互联网架构师小马

Java 架构设计 系统分析

【有奖调研】互联网新型社交,华为在找“元服务搭子”,快来集合!

HMS Core

HMS Core

人民日报:天翼云持续拓展云网基础设施覆盖广度和深度

天翼云开发者社区

云计算

政务云建设提速,天翼云夯实智慧政务数字底座

天翼云开发者社区

云计算 大数据

瓴羊Quick BI:轻松实现数据可视化大屏

对不起该用户已成仙‖

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