MeteorJS 开发的最佳实践与模式
1. 数据传输与字段过滤
在数据传输方面,使用 Web 套接字时,在发布数据时过滤必要字段非常重要。原因主要有两点:
-
服务器内存问题
:每个订阅都会在服务器主内存中创建一个缓存。如果文档尺寸巨大,会占用大量空间,可能导致严重的性能瓶颈。
-
客户端内存问题
:浏览器内存只能容纳一定量的数据。一旦达到上限,浏览器就会崩溃。因此,必须明智地处理 MiniMongo 要保存的数据量。
在创建游标时,应尽可能添加字段列表来过滤查询,示例代码如下:
Meteor.publish('projects', function(type) {
return MyCollection.find(type, {fields: {
type: 1,
content: 1
}});
});
2. 应用目录结构
MeteorJS 应用的目录结构没有严格的定义规则,开发者可根据自身需求创建。不过,了解 MeteorJS 加载文件的方式很有必要:
-
服务器目录
:该目录下的文件仅在服务器环境中加载。
-
客户端目录
:此目录下的文件仅在客户端环境中加载。
-
公共目录
:用于提供静态内容。
-
私有目录
:仅在服务器环境中可用,用于向服务器提供资产,如配置 JSON 文件或国家代码列表 JSON 文件等,可通过 Assets API 访问。
在客户端环境中,示例应用的页面源显示,加载顺序为:先加载包,接着是模板,然后是 lib 目录中的文件(如果有),最后是其他所有文件。如果加载顺序至关重要,建议将应用编写为一组包,因为只有在包中才能定义文件的加载顺序。
一般来说,目录分离可提高开发者理解代码的能力。将所有集合实例化代码写在集合目录中,便于查看。在 MeteorJS 应用中,客户端和服务器之间的代码共享很容易。若想让某些集合仅适用于客户端、某些仅适用于服务器,还有一些适用于两个环境,可在客户端、服务器和 lib 目录下创建多个集合目录并编写代码,路由也可如此处理。
要详细了解文件结构,可访问相关文档。
3. 静态资产服务
许多开发者使用公共目录来提供静态内容,这也是 MeteorJS 应用中公共目录的主要用途。但需注意,MeteorJS 应用本质上是 Node.js 应用,而 Node.js 在提供静态内容方面表现不佳。为解决此问题,可使用代理或 CDN 来提供静态内容。
以 Nginx 为例,可添加以下配置,让 Nginx 而非 MeteorJS 提供静态文件:
## serve static files by nginx instead of Meteor (the public/ folder)
location ~ \.(jpg|jpeg|png|gif|mp3|ico|pdf) {
root /opt/meteor/app/programs/web.browser/app; # this should point at the content from the public folder
access_log off;
expires 30d;
add_header Pragma public;
add_header Cache-Control "public";
}
除了 Nginx,还有很多其他代理可供选择。
4. 应用命名空间
全局变量一直被认为不利于代码的健康性。在 MeteorJS 中,经常会创建全局变量,这给很多开发者带来了问题。解决办法是使用命名空间,即创建一个全局变量,然后将每个逻辑代码模块作为该全局变量的子项,从而避免创建大量全局变量。命名空间已被证明是许多大型应用的最佳解决方案。
不过,在 MeteorJS 应用中使用命名空间的局限性较大,因为无法确定文件的加载顺序,难以预测所需的命名空间模块是否会在依赖它的其他模块之前加载。
虽然应用的某些部分可以使用命名空间,如集合和路由定义可在应用命名空间下进行命名,但命名空间下的逻辑块不一定能按正确顺序加载。对于想尽量避免使用全局变量的开发者,将应用开发为一组包是个不错的解决方案。在包中,可控制变量导出到环境中,且不存在加载顺序问题。
5. 转换类
若要在获取集合文档时对其进行操作,可在创建集合实例时使用 transform 属性定义转换,示例代码如下:
new Meteor.Collection("products", {
transform: function(doc) {
return doc.has_discount = (doc.actual_prize - doc.selling_prize) > 0;
}
});
这样可确保不在模板助手函数中编写任何转换逻辑,还可利用转换为每个文档创建带有辅助方法的模型。
6. 延迟补偿
延迟补偿是 MeteorJS 的重要原则之一。前面提到,MeteorJS 保存数据和更新 UI 的方式是模拟保存操作,并在操作触发端更新 UI。但数据到达服务器并保存到数据库后,会向操作触发端发送确认信息。如果保存失败,UI 会恢复到旧状态。
很多人认为这会导致闪烁问题。若认为这种延迟补偿是严重问题,可采用不同的方法来持久化数据。可定义服务器方法来持久化数据,而不是直接从客户端调用插入或更新操作。只需使用适当的数据调用服务器方法,即可避免闪烁问题。不过,有些开发者可能认为这违背了 MeteorJS 的原则,但最终用户体验才是最重要的,决策需开发者自行做出。
7. 性能和可扩展性问题识别
大多数情况下,需要使用分析工具来分析和了解生产代码的性能。实际用户使用的应用与测试环境中的应用存在不同的问题。为监控服务器的性能和可扩展性问题,显然需要一个工具,而 MeteorJS 默认没有提供。
不过,MeteorJS 社区的一位成员创建了一个很棒的工具 Kadira,它能帮助我们实时了解生产应用的详细情况,如订阅所需时间、方法调用响应时间、CPU 使用率等,为生产环境中的活动提供深入洞察。许多开发者使用 Kadira 识别出了生产中的瓶颈。
在 MeteorJS 应用中使用 Kadira 非常简单,步骤如下:
1. 注册账号,创建一个新应用。
2. 向应用添加 meteorhacks:kadira 包。
3. 在服务器目录的文件中写入连接代码
Kadira.connect('<appId>', '<appSecret>')
。
访问 Kadira 中的应用,即可查看应用的指标。可从在 Kadira 中创建的应用的设置面板获取 appId 和 appSecret 标识符。要了解更多关于 Kadira 的信息,可访问相关链接。
遵循上述最佳实践,有助于编写更稳定的 MeteorJS 应用。
8. 应用模式 - 包模式
由于 MeteorJS 相对较新,目前没有明确的应用模式可供遵循。不过,人们正在根据自身需求进行测试,并提出了各种可遵循的模式,这里介绍社区中讨论较多的包模式。
传统 MeteorJS 代码编写方式的问题
:
-
全局空间污染
:传统编写方式鼓励在全局空间中编写变量,这会带来诸多问题。
-
代码组织问题
:若不擅长按照 DRY(不要重复自己)和 KISS(保持简单愚蠢)原则逻辑分离代码,很快会陷入难以维护的意大利面条式代码。
-
测试问题
:应用的逻辑块组织不当,测试这种混乱的代码会很困难,也无法全面测量代码覆盖率。
包的优势
:
- 可在包内声明所有依赖项。
- 包具有可重用性,便于遵循 DRY 原则。
- 可控制依赖项的加载顺序。
- 可对每个独立的包进行测试,且包具有很强的独立性。
- 可控制只将需要的内容导出到客户端。
为演示包模式,将创建一个名为 DigiNotes 的小应用,目标是使用自定义包创建该应用,而不是像以前那样创建应用。
创建 DigiNotes 应用
:
1. 创建一个名为 DigiNotes 的 MeteorJS 应用。
2. 从 js 和 html 文件中删除所有与 hello 模板相关的代码,并删除 autopublish 和 insecure 包。
3. 创建包来构建应用。
创建命名空间包
:
运行
meteor create --package diginotes
命令,在 packages 目录下创建一个 diginotes 目录。在 diginotes.js 中添加以下代码:
DigiNotes = {};
DigiNotes.version = "0.1";
在 package.js 文件的 Package.onUse 方法中追加以下代码:
api.export("DigiNotes");
将 Package.describe 方法的 documentation 属性改为 null。运行
meteor add diginotes
命令将包添加到应用中,运行
meteor list
可查看自定义包。启动应用,在浏览器控制台输入
DigiNotes.version
,会输出 “0.1”。
创建笔记模块包
:
运行
meteor create --package diginotes:notes
命令创建包。需完成以下内容:
-
命名空间
:在 notes.js 中添加
DigiNotes.notes = {};
。
-
集合声明
:在 collection.js 中添加以下代码:
NotesCollection = new Mongo.Collection("notes");
NotesCollection.allow({
insert: function() {
return true;
}
});
- 发布代码 :在 publish.js 中添加:
Meteor.publish("notes", function() {
return NotesCollection.find({});
});
- 模板定义 :在 template.html 中添加以下代码:
<template name="notes">
<form id="note-form">
<textarea id="note" cols="30" rows="10" placeholder="Note"></textarea>
<input type="submit" value="Add" />
</form>
<div class="notes">
{{#if isLoading}}
Loading...
{{else}}
{{#if hasNotes}}
{{#each notes}}
{{>note}}
{{/each}}
{{else}}
No notes created.
{{/if}}
{{/if}}
</div>
</template>
<template name="note">
<div class="note-container">
<textarea class="note" readonly="true">{{description}}</textarea>
<div class="controls">
<button class="edit">✎</button>
<button class="delete">⊗</button>
</div>
</div>
</template>
- 模板助手 :在 helpers.js 中添加以下代码:
Template.notes.onCreated(function() {
Template.instance().subscribe("notes");
this.isLoading = false;
if(Template.subscriptionsReady) {
this.isLoading = false;
}
});
Template.notes.helpers({
notes: function() {
return NotesCollection.find({});
},
hasNotes: function() {
return NotesCollection.find({}).count();
}
});
Template.notes.events({
"submit #note-form": function(event) {
event.preventDefault();
var elem = document.getElementById("note"),
description = elem.value;
if(description = description.trim()) {
NotesCollection.insert({
description: description,
createdAt: Date.now(),
updatedAt: null
}, function(e, result) {
if(!e) {
elem.value = "";
}
});
} else {
noty({text: "Please enter some text!", type: "error", timeout: 2000});
}
}
});
- 样式 :在 styles.css 中添加样式代码,确保 UI 美观。
在 package.js 的 Package.onUse 方法中添加以下代码:
api.use(['ecmascript', 'templating', 'mongo', "hedcet:noty", "diginotes@0.0.1"]);
api.addFiles(['collection.js'], ["client", "server"]);
api.addFiles(['templates.html', 'helpers.js', 'styles.css'], ["client"]);
api.addFiles(['publish.js'], ["server"]);
运行
meteor add diginotes:notes
命令将包添加到应用中,并在 DigiNotes.html 文件的 body 标签中包含
{{> notes}}
模板,应用即可准备就绪。
以下是创建 DigiNotes 应用的流程图:
graph LR
A[创建 DigiNotes 应用] --> B[删除相关代码和包]
B --> C[创建命名空间包 diginotes]
C --> D[配置 diginotes 包]
D --> E[创建笔记模块包 diginotes:notes]
E --> F[完成笔记模块包内容]
F --> G[配置 package.js]
G --> H[添加包到应用并包含模板]
通过以上步骤和方法,能更好地开发和管理 MeteorJS 应用,提高代码的可维护性和性能。
MeteorJS 开发的最佳实践与模式(续)
9. 包模式的优势总结与对比
为了更清晰地展示包模式相对于传统 MeteorJS 代码编写方式的优势,我们可以通过以下表格进行对比:
| 对比项 | 传统编写方式 | 包模式 |
| — | — | — |
| 全局空间污染 | 鼓励在全局空间编写变量,易造成全局空间混乱 | 可创建一个全局变量作为命名空间,将逻辑模块作为子项,减少全局变量 |
| 代码组织 | 难以按照 DRY 和 KISS 原则组织代码,易形成难以维护的代码 | 可在包内声明依赖,遵循 DRY 原则,便于代码组织和管理 |
| 代码复用性 | 复用性较差,难以重复使用相同逻辑 | 包具有可重用性,方便在不同项目中使用 |
| 依赖加载顺序 | 无法控制依赖的加载顺序,可能导致问题 | 可控制依赖项的加载顺序,避免因加载顺序问题导致的错误 |
| 测试难度 | 逻辑块组织不当,测试困难,代码覆盖率难以测量 | 可对每个独立的包进行测试,包的独立性强,测试更方便 |
| 导出内容控制 | 难以控制哪些内容导出到客户端 | 可控制只将需要的内容导出到客户端 |
从表格中可以明显看出,包模式在多个方面都优于传统的编写方式,能够有效解决传统方式中存在的问题。
10. 代码优化与改进建议
在使用 MeteorJS 进行开发时,除了遵循上述的最佳实践和应用模式,还可以从以下几个方面对代码进行优化和改进:
数据查询优化
:
在进行数据查询时,尽量使用索引来提高查询效率。例如,在集合中创建索引可以加快查询速度。示例代码如下:
NotesCollection._ensureIndex({ description: 1 });
这样,在查询
description
字段时,数据库可以更快地定位到相关记录。
模板性能优化
:
避免在模板中进行复杂的计算和逻辑处理。可以将这些逻辑移到模板助手或服务器方法中。例如,在模板助手中进行数据的过滤和排序,而不是在模板中直接处理。
内存管理
:
及时清理不再使用的变量和对象,避免内存泄漏。在 MeteorJS 中,当订阅不再需要时,及时停止订阅,释放相关资源。示例代码如下:
var subscription = Template.instance().subscribe("notes");
// 当不需要订阅时
subscription.stop();
11. 常见问题及解决方案
在 MeteorJS 开发过程中,可能会遇到一些常见问题,以下是一些问题及对应的解决方案:
订阅延迟问题
:
当订阅数据时,可能会出现延迟,导致 UI 显示不及时。可以通过在模板中添加加载提示来解决这个问题。例如,在模板中添加一个加载状态变量:
Template.notes.onCreated(function() {
Template.instance().subscribe("notes");
this.isLoading = new ReactiveVar(true);
this.autorun(() => {
if (Template.subscriptionsReady()) {
this.isLoading.set(false);
}
});
});
在模板中根据加载状态显示不同的内容:
<template name="notes">
{{#if isLoading.get()}}
Loading...
{{else}}
<!-- 显示数据 -->
{{/if}}
</template>
全局变量冲突问题
:
如前面所述,全局变量容易导致冲突。使用命名空间或包模式可以有效解决这个问题。如果必须使用全局变量,尽量减少其使用范围,并使用有意义的名称。
性能瓶颈问题
:
当应用出现性能瓶颈时,可以使用 Kadira 等工具进行性能分析,找出问题所在。可能的原因包括查询效率低下、内存泄漏等,根据分析结果进行相应的优化。
12. 未来发展趋势与展望
随着 Web 技术的不断发展,MeteorJS 也在不断演进。未来,MeteorJS 可能会在以下几个方面得到进一步发展:
更好的性能优化
:
社区可能会开发更多的性能优化工具和技术,进一步提高 MeteorJS 应用的性能。例如,更智能的缓存机制、更高效的数据传输协议等。
与其他技术的集成
:
MeteorJS 可能会更好地与其他流行的前端框架和后端技术集成,如 React、Vue.js 等。这将使开发者能够更灵活地选择适合自己项目的技术栈。
更强大的开发工具
:
可能会出现更多功能强大的开发工具,帮助开发者更轻松地创建、调试和部署 MeteorJS 应用。例如,可视化的开发工具、自动化的测试框架等。
13. 总结
MeteorJS 作为一个强大的 Web 开发框架,提供了丰富的功能和便捷的开发体验。通过遵循数据传输字段过滤、合理的目录结构、使用包模式等最佳实践,可以有效解决传统开发方式中存在的问题,提高代码的可维护性、可扩展性和性能。
同时,使用 Kadira 等工具进行性能监控和分析,及时发现和解决应用中的问题。在开发过程中,不断优化代码,避免常见问题的出现。随着技术的不断发展,MeteorJS 也将不断进步,为开发者带来更多的可能性。
希望本文介绍的这些最佳实践和模式能够帮助开发者更好地开发 MeteorJS 应用,创造出更优秀的 Web 应用程序。以下是一个总结性的流程图,展示了 MeteorJS 开发的主要步骤和要点:
graph LR
A[开始开发] --> B[遵循最佳实践]
B --> C{选择开发模式}
C -->|包模式| D[创建和配置包]
C -->|传统模式| E[注意代码组织]
D --> F[优化代码]
E --> F
F --> G[性能监控与分析]
G --> H[解决问题与改进]
H --> I[持续开发与迭代]
通过以上的流程和方法,开发者可以构建出高质量、高性能的 MeteorJS 应用。
超级会员免费看
38

被折叠的 条评论
为什么被折叠?



