技术演进中的开发沉思-207 JavaScript:部件开发
本文深入探讨了Dojo框架的模块化开发与性能优化。主要内容包括:1) 基础部件开发,介绍了TextBox和TabContainer等核心组件的使用方法;2) 自定义构建流程,详细说明如何通过profile配置裁剪模块、打包层文件以减小体积;3) 实际案例演示,展示如何开发模块化表单页面并优化部署。通过Dojo的部件体系和构建工具,开发者可以创建高效、可维护的Web应用,显著提升加载速度和运行性能。
在现代 Web 开发中,模块化和性能优化是两个核心诉求。Dojo 作为一个成熟的前端框架,通过其强大的部件(Widget)体系和自定义构建工具,为我们提供了优雅的解决方案。本文将带你深入了解 Dojo 的部件开发,并学习如何通过框架部署优化来提升应用性能。
1. 基础部件
Dojo 的部件(Widget)是构建用户界面的基础单元,它们封装了 HTML、CSS 和 JavaScript 逻辑,提供了可复用、可扩展的 UI 组件。
1.1 dijit/form/TextBox
dijit/form/TextBox 是 Dojo 中最常用的表单控件之一,它提供了比原生 <input> 更丰富的功能,如输入验证、占位符、禁用状态等。
示例代码:
require([
"dijit/form/TextBox",
"dojo/dom",
"dojo/domReady!"
], function(TextBox, dom) {
// 创建一个 TextBox 实例
var usernameTextBox = new TextBox({
placeHolder: "请输入用户名", // 占位符文本
required: true, // 是否为必填项
trim: true, // 自动去除首尾空格
style: {
width: "300px",
margin: "10px"
},
onChange: function(value) {
// 值改变时触发的事件
console.log("用户名已改为:", value);
}
}, "username"); // 将部件挂载到 id 为 "username" 的 DOM 节点上
// 手动设置值
usernameTextBox.set("value", "admin");
// 获取当前值
console.log("当前用户名:", usernameTextBox.get("value"));
});
HTML 结构:
<input type="text" id="username">
核心特性:
- 输入验证:通过
required、pattern等属性进行基础验证。 - 事件监听:
onChange、onFocus、onBlur等事件,方便处理用户交互。 - 样式定制:支持通过
style属性或 CSS 类进行样式调整。
1.2 dijit/layout/TabContainer
dijit/layout/TabContainer 是一个布局部件,用于将内容分成多个标签页,用户可以通过点击标签切换显示内容。它常用于复杂表单、数据展示等场景。
示例代码:
require([
"dijit/layout/TabContainer",
"dijit/layout/ContentPane",
"dojo/dom",
"dojo/domReady!"
], function(TabContainer, ContentPane, dom) {
// 创建 TabContainer 实例
var tabContainer = new TabContainer({
style: {
width: "500px",
height: "300px",
margin: "10px"
},
tabPosition: "top", // 标签位置:top、bottom、left、right
nested: false // 是否允许嵌套标签
}, "tabContainer");
// 创建第一个标签页
var tab1 = new ContentPane({
title: "基本信息", // 标签标题
content: `
<div style="padding: 10px;">
<label>姓名:</label>
<input type="text" id="name" style="width: 200px;"><br><br>
<label>年龄:</label>
<input type="number" id="age" style="width: 200px;">
</div>
`
});
// 创建第二个标签页
var tab2 = new ContentPane({
title: "兴趣爱好",
content: `
<div style="padding: 10px;">
<label>爱好:</label><br>
<input type="checkbox" id="hobby1"> 篮球<br>
<input type="checkbox" id="hobby2"> 读书<br>
<input type="checkbox" id="hobby3"> 旅游
</div>
`
});
// 将标签页添加到 TabContainer 中
tabContainer.addChild(tab1);
tabContainer.addChild(tab2);
// 启动 TabContainer
tabContainer.startup();
});
HTML 结构:
<div id="tabContainer"></div>
核心特性:
- 标签切换:支持通过点击标签或编程方式切换标签页。
- 布局灵活:标签位置可自定义,支持嵌套标签。
- 内容动态加载:可以通过
href属性加载远程内容。
2. 自定义构建
Dojo 框架默认包含了大量的模块和部件,但在实际项目中,我们可能只用到其中的一部分。通过自定义构建,我们可以裁剪掉不必要的模块,减小框架体积,提升应用加载性能。
2.1 自定义构建的核心概念
- 模块依赖:Dojo 采用 AMD 规范,模块之间通过
require和define进行依赖管理。 - 构建配置文件:用于定义构建规则,如要包含的模块、输出路径、压缩方式等。
- 层(Layer):将多个模块打包成一个文件,减少 HTTP 请求次数。
2.2 自定义构建的步骤
步骤 1:安装 Dojo 构建工具
首先,确保你已经安装了 Node.js 和 npm。然后,通过 npm 安装 Dojo 构建工具:
npm install -g dojo-cli
步骤 2:创建构建配置文件
在项目根目录下创建一个 profile.js 文件,用于定义构建规则。
示例 profile.js:
var profile = {
// 基础路径
basePath: "./src",
// 要构建的包
packages: [
{
name: "dojo",
location: "dojo" // dojo 核心库路径
},
{
name: "dijit",
location: "dijit" // dijit 部件库路径
},
{
name: "app",
location: "app" // 自定义应用模块路径
}
],
// 构建输出目录
outDir: "./build",
// 层定义
layers: {
// 核心层:包含应用所需的 Dojo 核心模块
"dojo/dojo": {
include: [
"dojo/dojo",
"dojo/dom",
"dojo/on",
"dojo/fx",
"dijit/form/TextBox",
"dijit/layout/TabContainer"
],
exclude: [
"dojo/request/script", // 排除不需要的模块
"dojo/request/xhr"
]
},
// 应用层:包含自定义应用模块
"app/main": {
include: [
"app/main",
"app/widgets/MyWidget"
],
exclude: [
"dojo/dojo" // 排除已包含在核心层中的模块
]
}
},
// 压缩选项
optimize: "uglify", // 压缩方式:uglify、closure、none
// 是否生成 source map
sourceMaps: true,
// 是否保留版权信息
copyright: "© 2024 My App"
};
module.exports = profile;
步骤 3:执行构建命令
在项目根目录下运行以下命令开始构建:
dojo build --profile profile.js
步骤 4:使用构建后的文件
构建完成后,会在 ./build 目录下生成压缩后的模块文件。在应用中,通过以下方式引入构建后的文件:
<script src="build/dojo/dojo.js"></script>
<script>
require(["app/main"], function(main) {
main.init();
});
</script>
2.3 自定义构建的优势
- 减小文件体积:裁剪掉不必要的模块,核心库体积可减小 50% 以上。
- 提升加载速度:减少 HTTP 请求次数,压缩后的文件加载更快。
- 优化性能:构建过程中会进行代码优化,如死代码消除、变量名混淆等。
3. 开发模块化表单页面并优化部署
3.1 项目需求
开发一个用户注册表单页面,包含以下功能:
- 分步骤填写表单(基本信息、账号信息、兴趣爱好)。
- 表单验证(必填项、格式验证)。
- 提交表单时显示加载动画。
- 通过自定义构建优化框架部署。
3.2 项目结构
project/
├── src/
│ ├── dojo/ # Dojo 核心库
│ ├── dijit/ # Dijit 部件库
│ ├── app/ # 自定义应用模块
│ │ ├── main.js # 应用入口
│ │ └── widgets/ # 自定义部件
│ │ └── FormWidget.js # 表单部件
│ └── index.html # 页面入口
├── profile.js # 构建配置文件
└── build/ # 构建输出目录(自动生成)
3.3 代码实现
3.3.1 自定义表单部件(app/widgets/FormWidget.js)
define([
"dojo/_base/declare",
"dijit/_WidgetBase",
"dijit/_TemplatedMixin",
"dijit/layout/TabContainer",
"dijit/layout/ContentPane",
"dijit/form/TextBox",
"dijit/form/PasswordTextBox",
"dijit/form/Button",
"dojo/text!app/widgets/FormWidget.html",
"dojo/on",
"dojo/fx"
], function(
declare,
_WidgetBase,
_TemplatedMixin,
TabContainer,
ContentPane,
TextBox,
PasswordTextBox,
Button,
template,
on,
fx
) {
return declare([_WidgetBase, _TemplatedMixin], {
templateString: template,
postCreate: function() {
this.inherited(arguments);
// 创建 TabContainer
this.tabContainer = new TabContainer({
style: "width: 100%; height: 400px;",
tabPosition: "top"
}, this.tabContainerNode);
// 基本信息标签页
var basicTab = new ContentPane({
title: "基本信息"
});
this.nameTextBox = new TextBox({
label: "姓名:",
required: true,
placeHolder: "请输入姓名"
});
basicTab.addChild(this.nameTextBox);
// 账号信息标签页
var accountTab = new ContentPane({
title: "账号信息"
});
this.usernameTextBox = new TextBox({
label: "用户名:",
required: true,
placeHolder: "请输入用户名"
});
this.passwordTextBox = new PasswordTextBox({
label: "密码:",
required: true,
placeHolder: "请输入密码"
});
accountTab.addChild(this.usernameTextBox);
accountTab.addChild(this.passwordTextBox);
// 兴趣爱好标签页
var hobbyTab = new ContentPane({
title: "兴趣爱好"
});
this.hobbyTextBox = new TextBox({
label: "兴趣爱好:",
placeHolder: "请输入兴趣爱好,多个用逗号分隔"
});
hobbyTab.addChild(this.hobbyTextBox);
// 添加标签页到 TabContainer
this.tabContainer.addChild(basicTab);
this.tabContainer.addChild(accountTab);
this.tabContainer.addChild(hobbyTab);
// 创建提交按钮
this.submitButton = new Button({
label: "提交",
onClick: dojo.hitch(this, this.onSubmit)
});
this.submitButton.placeAt(this.submitButtonNode);
// 启动部件
this.tabContainer.startup();
},
onSubmit: function() {
// 表单验证
if (!this.nameTextBox.isValid() ||
!this.usernameTextBox.isValid() ||
!this.passwordTextBox.isValid()) {
alert("请填写必填项!");
return;
}
// 显示加载动画
fx.fadeIn({
node: this.loadingNode,
duration: 300
}).play();
// 模拟表单提交
setTimeout(dojo.hitch(this, function() {
fx.fadeOut({
node: this.loadingNode,
duration: 300,
onEnd: dojo.hitch(this, function() {
alert("表单提交成功!");
})
}).play();
}), 2000);
}
});
});
3.3.2 表单部件模板(app/widgets/FormWidget.html)
<div class="form-widget">
<div id="tabContainerNode"></div>
<div style="text-align: center; margin-top: 10px;">
<div id="submitButtonNode"></div>
</div>
<div id="loadingNode" style="display: none; text-align: center; margin-top: 10px;">
<img src="loading.gif" alt="加载中...">
</div>
</div>
3.3.3 应用入口(app/main.js)
define([
"app/widgets/FormWidget",
"dojo/dom",
"dojo/domReady!"
], function(FormWidget, dom) {
var formWidget = new FormWidget({}, dom.byId("formContainer"));
formWidget.startup();
});
3.3.4 页面入口(src/index.html)
<!DOCTYPE html>
<html>
<head>
<title>用户注册表单</title>
<link rel="stylesheet" href="dijit/themes/claro/claro.css">
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
.form-widget {
width: 600px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 5px;
}
</style>
</head>
<body class="claro">
<h1 style="text-align: center;">用户注册</h1>
<div id="formContainer"></div>
<script src="dojo/dojo.js" data-dojo-config="async: true"></script>
<script>
require(["app/main"]);
</script>
</body>
</html>
3.4 自定义构建与部署优化
3.4.1 构建配置文件(profile.js)
var profile = {
basePath: "./src",
packages: [
{ name: "dojo", location: "dojo" },
{ name: "dijit", location: "dijit" },
{ name: "app", location: "app" }
],
outDir: "./build",
layers: {
"dojo/dojo": {
include: [
"dojo/dojo",
"dojo/dom",
"dojo/on",
"dojo/fx",
"dijit/layout/TabContainer",
"dijit/layout/ContentPane",
"dijit/form/TextBox",
"dijit/form/PasswordTextBox",
"dijit/form/Button"
]
},
"app/main": {
include: ["app/main", "app/widgets/FormWidget"],
exclude: ["dojo/dojo"]
}
},
optimize: "uglify",
sourceMaps: true
};
module.exports = profile;
3.4.2 执行构建
dojo build --profile profile.js
3.4.3 部署构建后的应用
将 build 目录下的文件部署到 Web 服务器,修改 index.html 中的脚本引入路径:
<script src="build/dojo/dojo.js" data-dojo-config="async: true"></script>
<script>
require(["app/main"]);
</script>
3.5 优化效果
- 文件体积减小:构建后的
dojo.js和app/main.js体积大幅减小。 - 加载速度提升:减少了 HTTP 请求次数,页面加载时间缩短。
- 性能优化:代码压缩和优化后,运行效率更高。
4. 最后小结
4.1 核心要点回顾
- 部件开发:Dojo 部件是 UI 构建的核心,通过封装 HTML、CSS 和 JavaScript,实现了组件的可复用性和可维护性。
- 自定义构建:通过裁剪不必要的模块和打包成层,可以显著减小框架体积,提升应用性能。
- 模块化部署:构建后的应用更适合生产环境部署,能够提供更快的加载速度和更好的用户体验。
4.2 进阶方向
- 自定义部件开发:学习如何创建更复杂的自定义部件,如数据表格、图表等。
- 构建优化技巧:深入了解构建配置选项,如代码分割、懒加载等。
- 性能监控与调优:使用浏览器开发者工具和性能监控工具,持续优化应用性能。
更多推荐




所有评论(0)