【AICon】AI 基础设施、LLM运维、大模型训练与推理,一场会议,全方位涵盖! >>> 了解详情
写点什么

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

评论

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

平台工程指南:TheNewStack 发布的免费电子书

杨振涛

HashiCorp 平台工程 平台工程社区 PECommunity Platform Engineering

合约量化交易所系统开发——策略机器人

西安链酷科技

量化机器人 智能ai量化 量化交易软件

Stepn跑鞋系统开发NFT跑步链游玩法

西安链酷科技

DeFi质押挖矿 DeFi流动性挖矿 运动挖矿

达芬奇调色视频剪辑软件:DaVinci Resolve Studio 18 for Mac中文正式版

iMac小白

Timemator for Mac(优秀的时间追踪记录工具)v3.0.4激活版下载

iMac小白

Red Giant Trapcode Suite for Mac(红巨星粒子插件)v2024.0.2激活版

iMac小白

开源大模型驱动的智能编程与自然语言处理

百度开发者中心

nlp 大模型 LLM

壹维克斯ONE WIEX质押系统开发合约技术

西安链酷科技

智能合约 DAPP系统开发

上海站报名启动! 2023年开源产业生态大会OpenHarmony生态分论坛

OpenHarmony开发者

OpenHarmony

NFT交易所系统开发跨链技术

西安链酷科技

交易所开发软件开发 虚拟币开发

HarmonyOS设备管理开发:USB服务开发指导

HarmonyOS开发者

HarmonyOS

全面预算管理,帮助企业财务团队冲破市场挑战

智达方通

全面预算管理 财务团队

数字先锋| 雪域高原一朵“云”,天翼云助力青海打造省级融媒云平台

天翼云开发者社区

云计算 大数据 5G

短剧CPS分销系统程序开发搭建

西安链酷科技

直播系统 聚合cps系统

质押挖矿理财DAPP系统开发技术

西安链酷科技

web3 链游 钱包开发 多链钱包

Jogger慢跑者/Stepn跑鞋NFT系统开发案例

西安链酷科技

dapp开发 运动挖矿

卡牌类链游系统开发NFT游戏技术

西安链酷科技

链游开发 运动挖矿开发

Linux中tar命令的几个高级用法

这我可不懂

Linux

首个“全4K”运动会,上云!

天翼云开发者社区

云计算 大数据 云服务

免费获取GPT-4的五种工具

互联网工科生

人工智能 GPT-4

云游世界卷轴系统开发NFT技术

西安链酷科技

DeFi质押挖矿 defi开发

教你如何防止数据被异常篡改,并复原数据

秃头小帅oi

程序员 前端 低代码 数据异常

Web3钱包开发:解锁未来投资利润丰厚的机会

区块链软件开发推广运营

dapp开发 区块链开发 链游开发 NFT开发 公链开发

列举数据库缓存使用场景实例和命令速查表

华为云开发者联盟

数据库 后端 华为云 华为云开发者联盟

Vue + Element UI 实现复制当前行数据功能(复制到新增页面组件值不能更新等问题解决)

EquatorCoco

Vue 前端开发 UI

【论文解读】在上下文中学习创建任务向量

合合技术团队

人工智能 LLM ICL

浪潮云洲基于QID技术的"师旷"前装固件成功首发

财见

详解CCE服务:一站式告警配置和云原生日志视图

华为云开发者联盟

云原生 后端 华为云 华为云开发者联盟 华为云CCE容器服

2023年微软开源八个人工智能项目

树上有只程序猿

人工智能 微软 开源

Stepn跑鞋系统开发NFT链游技术

西安链酷科技

web3 链游开发

数字先锋| “翼”键上云,开启智慧医疗新时代!

天翼云开发者社区

人工智能 云计算 大数据

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