本文概述
在当今迅速发展的JavaScript框架和库中, 选择要作为开发基础的库可能是一个挑战。毕竟, 一旦走上使用特定框架的道路, 将代码迁移到另一个框架上就不是一件容易的事, 你可能永远没有时间或预算来执行。
那么, 为什么要使用Ractive.js?
与其他生成惰性HTML的工具不同, Ractive将模板转换为默认情况下可交互的应用程序的蓝图。尽管可以肯定地说, Ractive的贡献具有革命性而不是革命性, 但其价值仍然是重要的。
使Ractive如此有用的原因在于它提供了强大的功能, 但是这样做的方式对于开发人员而言非常简单。此外, 它相当优雅, 快速, 醒目且体积小。
在本文中, 我们将逐步介绍构建简单的Ractive搜索应用的过程, 并演示Ractive的一些关键功能及其简化网络应用和开发的方式。
什么是Ractive.js?
Ractive最初是为了以一种更优雅的方式解决数据绑定问题而创建的。为此, 它需要模板并将其转换为DOM的轻量级虚拟表示, 以便在数据更改时, 可以智能, 高效地更新实际DOM。
但是很明显, Ractive所采用的方法和基础架构也可以用于更有效地完成其他事情。例如, 它可以自动处理诸如重用事件处理程序之类的事情, 并在不再需要它们时自动解除绑定。事件委托变得不必要。与数据绑定一样, 这种方法可以防止代码随着应用程序的增长而变得笨拙。
现成提供了双向绑定, 动画和SVG支持等关键功能, 并且可以通过插件轻松添加自定义功能。
一些工具和框架迫使你学习新词汇并以特定方式构建应用程序, 而Ractive为你工作, 而不是相反。它还与其他库很好地集成。
我们的示例应用
我们的示例应用程序将用于根据技能搜索开发人员的srcmini数据库。我们的应用程序将有两个视图:
- 搜索:带有内联搜索框的技能列表
- 结果:技能视图包括开发人员列表
对于每个开发人员, 我们将显示他们的姓名, 照片, 简短描述和技能列表(每个技能将链接到相应的技能视图)。
(注意:本文末尾都提供了到该应用程序的在线工作实例和源代码存储库的链接。)
为了保持对Ractive框架的主要关注, 我们将采用通常在生产中不应该进行的许多简化:
- 默认主题。我们将使用具有默认主题的Bootstrap进行样式设置, 而不是自定义主题以适合你的应用程序样式。
- 依赖关系。我们将把依赖项添加为定义全局变量的单独脚本(而不是使用ES6模块, CommonJS或带有适当装载程序进行开发的AMD, 以及用于生产的构建步骤)。
- 静态数据。我们将使用通过刮擦srcmini网站上的公共页面而准备的静态数据。
- 没有客户端路由。这意味着该URL将与我们在视图之间切换时保持不变。你绝对不应该对SPA进行此操作, 尽管对于某些小型交互式组件来说也可以。 Ractive没有内置的路由器实现, 但可以与第3方路由器一起使用, 如本示例所示。
- 在HTML中的脚本标签内定义的模板。这不一定是个坏主意, 尤其是对于小型应用程序, 它有一些优点(它很简单, 你可以将这些客户端模板与服务器端模板一起处理, 例如进行国际化处理)。但是对于较大的应用程序, 你可以受益于预编译(也就是将模板预解析为内部JS表示形式)。
让我们开始使用Web Apps
好的, 就这么说, 让我们开始构建应用程序。我们将以迭代方式进行操作, 一个接一个地添加较小的功能, 并在遇到这些概念时对其进行探索。
首先, 创建一个包含两个文件的文件夹:index.html和script.js。我们的应用程序将非常简单, 并且可以从file://协议运行, 从而避免了启动开发服务器的需要(尽管你可以根据需要)。
搜索页面
我们将从实施搜索页面开始, 用户可以在其中选择一种技能, 以便在srcmini数据库中找到匹配的开发人员。
HTML骨架
让我们从这个简单的HTML页面开始:
<!doctype html>
热门搜寻
如你所见, 这是一个简单的HTML5文档。它从CDN, Lodash(一个很棒的数据处理库)和Ractive.js加载Bootstrap。
Ractive不需要将整个页面专用于SPA, 因此我们可以拥有一些静态内容。在我们的例子中, 它由一个容器元素和页面标题组成。
最后, 我们加载为演示准备的数据以及包含程序的脚本。
好的, 既然我们的HTML框架已经准备就绪, 让我们开始添加一些实际功能。
技能清单
我特别喜欢Ractive的一件事是, 它如何教会你思考要实现的最终表示(HTML), 然后使你能够专注于编写必要的代码来实现这一目标。
因此, 首先, 让我们建立一个技能列表作为初始视图。这样做仅需要:
- 添加将显示技能列表的HTML元素。
- 在我们的HTML中添加一小段模板代码。
- 编写一些简短的JavaScript将数据提供给模板, 以将其呈现在我们添加的HTML元素中。
HTML的模块包括以下内容:
Ractive对于指定HTML元素以接收要显示的数据没有特殊约定, 但是最简单的方法是在该元素中添加ID。为此, 我通常使用” root” ID。我们将很快看到Ractive初始化后的用法。对于那些好奇的人, 还有其他方法可以指定根元素。
type =” text / html”稍显笨拙的script元素是一个巧妙的技巧, 它可以使浏览器加载大量HTML而不进行解析或渲染, 因为浏览器会忽略未知类型的脚本。该脚本的内容是类似Moustache / Handlebars的模板(尽管Ractive确实具有一些扩展)。
我们首先假设可以访问技能数组来编写模板代码。我们使用{{#each}} mustache指令定义迭代。在指令内部, 可以以此方式访问当前元素。再一次, 我们假设Skills变量包含一个字符串数组, 因此我们只用插值小胡子{{this}}来渲染它。
好的, 那就是HTML。但是JavaScript呢?这就是将数据提供给模板的”魔术”的地方:
(function(){var skill = DB.skills, 开发人员= DB.developers;
var app = new Ractive({
el: '#root', template: '#tpl-app', data: {
skills: _.keys(DB.skills)
}
}); }());
印象深刻, 不是吗?仅用这10行代码, 我们就能:
- 从”数据库”中”检索”数据。
- 实例化一个新的Ractive应用。
- 告诉它使用id =” root”在元素内部进行渲染。
- 告诉它使用带有id =” tpl-app”的脚本元素来获取模板(还有其他方法)。
- 传递初始数据(我们将了解如何在运行时进行更改), 如果你习惯了Angular术语, 则传递”作用域”。
- 使用lodash键方法可以获取我们用作” DB”中对象键的技能名称。
因此, 基本上, 使用此脚本, 我们告诉框架该怎么做, 而不是如何去做。该模板声明了如何操作, 我个人觉得这很自然。
希望你开始有了这个主意, 所以现在让我们在现有功能的基础上实施一些更有用的功能。
技能搜寻
搜索页面当然需要一个搜索字段。并且我们希望它提供一种交互式功能, 当用户在搜索框中输入内容时, 技能列表将被过滤掉, 只包括那些包含输入的子字符串的技能(过滤不区分大小写)。
与Ractive一样, 我们首先定义模板(在考虑需要哪些新的上下文变量以及数据管理方面会发生什么变化的时候):
<!-- HERE’S OUR SEARCH BOX -->
<div class="row">
<form class="form-horizontal col-xs-6 col-xs-offset-6">
<input type="search" class="form-control"
value="{{ skillFilter }}"
placeholder="Type part of the skill name here">
</form>
</div>
<hr>
<!-- NOW INSTEAD OF DISPLAYING ALL SKILLS, WE INVOKE A
TO-BE-CREATED JAVASCRIPT skills() FUNCTION THAT WILL
FILTER THE SKILL LIST DOWN TO THOSE THAT MATCH THE
TEXT ENTERED BY THE USER -->
<div class="row">
{{#each skills()}}
<span class="col-xs-3">
<a href="#" class="label label-primary">{{this}}</a>
</span>
{{/each}}
</div> </script>
变化不多, 但仍有很多要学习的地方。
首先, 我们添加了一个包含搜索框的新<div>。很明显, 我们希望将输入连接到某个变量(除非你怀念旧的jQuery汤日时代)。 Ractive支持所谓的双向绑定, 这意味着你的JS代码可以检索值, 而无需从DOM手动读取它。在我们的例子中, 这是通过使用插值式胡子值=” {{skillFilter}}”来完成的。 Ractive了解我们希望将此变量绑定到输入的value属性。因此, 它会监视我们的输入并自动更新变量。只需一行HTML即可非常简洁。
其次, 如上面代码片段中的注释所述, 现在, 我们将不显示所有技能, 而是创建一个Skills()JS函数, 该函数将过滤技能列表, 并仅返回与用户输入的文本匹配的那些技能:
//将技能列表存储在Ractive范围之外的变量中var skillNames = _.keys(DB.skills);
var app = new Ractive({el:’#root’, template:’#tpl-app’, data:{//初始化上下文变量并不是严格要求//, 但通常被认为是一种很好的做法skillFilter:null,
// Define the skills() function right in our data object.
// Function is available to our template where we call it.
skills: function() {
// Get the skillFilter variable from the Ractive instance
// (available as ‘this’).
// NOTE WELL: Our use of a getter here tells Ractive that
// our function has a *dependency* on the skillFilter
// value, so this is significant.
var skillFilter = this.get('skillFilter');
if (!skillFilter) {
return skillNames;
}
skillFilter = new RegExp(_.escapeRegExp(skillFilter), 'i')
return _.filter(skillNames, function(skill) {
return skill.match(skillFilter);
});
}
} });
尽管这是干净且易于实现的, 但你可能想知道它如何影响性能。我的意思是, 我们每次都要调用一个函数吗?好吧, 每次? Ractive非常聪明, 仅在它们的依赖项(变量)发生变化时(Ractive知道何时发生这种情况, 这要归功于使用setter), 仅重新渲染模板的一部分(并从模板中调用任何函数)。
顺便说一句, 对于那些有兴趣进一步执行此操作的人, 还有一种更优雅的方法可以使用计算属性来进行此操作, 但是如果你愿意, 我会把它留给你自己玩。
结果页面
现在我们有了一个非常有用的可搜索技能列表, 让我们进入结果视图, 在其中显示匹配的开发人员列表。
在技能视图之间切换
显然有多种方法可以实现。我根据是否选择了技能选择了两种不同的观点。如果是这样, 我们将显示匹配的开发人员列表;如果没有, 我们将显示技能列表和搜索框。
因此, 对于初学者来说, 当用户选择(即单击)技能名称时, 需要隐藏技能列表, 而应将技能名称显示为页面标题。相反, 在选定的技能视图上, 需要有一种方法可以关闭该视图并返回技能列表。
这是我们走这条路的第一步:
好, 这里发生了很多事。
首先, 为了适应将其实现为两个不同的视图, 我将到目前为止的所有内容(即列表视图)都移到了称为局部视图的地方。实质上, 部分代码是我们将要包含在另一个位置(很快)的模板代码块。
然后, 我们要使我们的技能可单击, 当单击它时, 我们要导航到相应的技能视图。为此, 我们使用一种称为代理事件的方法, 即我们对物理事件做出反应(单击时, 名称是Ractive可以理解的名称), 并将其代理给逻辑事件(选择技能, 名称就是我们称之为的名称) )传递参数(你可能还记得这里代表技能名称)。
(仅供参考, 存在方法调用的另一种语法来完成相同的操作。)
接下来, 我们再次假设我们将使用一个名为currentSkill的变量, 该变量将具有所选技能的名称(如果有), 或者如果未选择任何技能, 则为空。因此, 我们定义了另一个部分, 该部分显示了当前的技能名称, 并且还有一个”关闭”链接, 应取消选择技能。
对于JavaScript, 主要添加的内容是用于订阅select-skill和deselect-skill事件的代码, 从而相应地更新currentSkill(和skillFilter):
var app = new Ractive({el:’#root’, template:’#tpl-app’, data:{skillFilter:null, currentSkill:null, //将currentSkill初始化为null
// skills function remains unchanged
skills: function() {
var skillFilter = this.get('skillFilter');
if (!skillFilter) {
return skillNames;
}
skillFilter = new RegExp(_.escapeRegExp(skillFilter), 'i')
return _.filter(skillNames, function(skill) {
return skill.match(skillFilter);
});
}
} });
//订阅逻辑事件select-skill app.on(‘select-skill’, function(event, skill){this.set({//将currentSkill设置为用户选择的技能currentSkill:技能, //重置SEARCH FILTER skillFilter:null});});
//订阅逻辑事件deselect-skill app.on(‘deselect-skill’, function(event){this.set(‘currentSkill’, null); //清除currentSkill});
列出每种技能的开发人员
为技能准备好新视图后, 我们现在可以添加一些内容了-该列表中包含的开发人员的实际列表。为此, 我们如下扩展该视图的局部视图:
{{#partial skillView}} <h2> {{currentSkill}}×关闭</ h2>
{{#each skillDevelopers(currentSkill)}}
<div class="panel panel-default">
<div class="panel-body">
{{ this.name }}
</div>
</div>
{{/each}} {{/partial}}
希望到此为止, 你会对这里发生的事情有感觉:我们在SkillView部分中添加了一个新的迭代部分, 该部分对新的SkillDevelopers函数的结果进行迭代, 接下来将要编写该函数。对于数组中的每个开发人员(由该skillDevelopers函数返回), 我们呈现一个面板并显示开发人员的姓名。请注意, 我可以在这里使用隐式形式{{name}}, Ractive将通过从当前上下文(在我们的情况下是受{{#each}}绑定的开发人员对象)开始的上下文链中搜索来找到合适的属性, 但是我更喜欢露骨。 Ractive文档中提供了有关上下文和引用的更多信息。
这是skillDevelopers()函数的实现:
skillDevelopers:function(skill){//从” DB”获取技能对象skill = skill [skill]; //安全检查, 如果(!skill){return; } //将开发人员的ID(SLUGS)映射到//实际开发人员的详细对象return _.map(skill.developers, function(slug){return developers [slug];}); }
扩展每个开发人员的条目
现在我们有了开发人员的工作清单, 是时候添加更多细节了, 当然还有张漂亮的照片:
{{#partial skillView}} <h2> {{currentSkill}}×关闭</ h2>
{{#each skillDevelopers(currentSkill)}}
<div class="panel panel-default">
<div class="panel-body media">
<div class="media-left">
<!-- ADD THE PHOTO -->
<img class="media-object img-circle"
width="64" height="64"
src="{{ this.photo }}" alt="{{ this.name }}">
</div>
<div class="media-body">
<!-- MAKE THE DEVELOPER’S NAME A HYPERLINK TO
THEIR PROFILE -->
<a class="h4 media-heading" href="{{ this.url }}"
target="_blank">
{{ this.name }}</a>
<!-- ADD MORE DETAILS (FROM THEIR PROFILE) -->
<p>{{ this.desc }}</p>
</div>
</div>
</div>
{{/each}} {{/partial}}
从Ractive的角度来看, 这里没有什么新鲜的东西, 但是Bootstrap功能的使用有所增加。
显示可点击的开发人员技能列表
到目前为止, 我们已经取得了良好的进展, 但是技能开发人员关系的另一面仍然是缺少的功能;也就是说, 我们希望显示每个开发人员的技能, 并且希望每个技能都是一个可点击的链接, 可将我们带到该技能的结果视图。
但是, 等等…我确定我们在技能列表中已经完全一样了。是的, 事实上, 我们做到了。它位于可单击的技能列表中, 但与每个开发人员的技能集不同。但是这些非常相似, 对我来说显然是我们应该重用那部分HTML的信号。为此, 偏爱是我们的朋友。
(注意:Ractive对于视图的可重用块(也称为组件)也有一种更高级的方法。它们确实非常有用, 但是为了简单起见, 我们现在不再讨论它们。)
因此, 这是我们使用部分代码来完成此操作的方式(请注意, 顺便说一句, 我们能够添加此功能而无需添加一行JavaScript代码!):
{{#partial skill}} {{this}} {{/ partial}}
{{#partial skillList}} <div class =” row”> <form class =” form-horizontal col-xs-6 col-xs-offset-6″> </ form> </ div> <hr> <div class =” row”> {{#each skill()}} {{> skill}} {{/ each}} </ div> {{/ partial}}
{{#partial skillView}} <h2> {{currentSkill}}×关闭</ h2>
{{#each skillDevelopers(currentSkill)}}
<div class="panel panel-default">
<div class="panel-body media">
<div class="media-left">
<img class="media-object img-circle"
width="64" height="64"
src="{{ this.photo }}" alt="{{ this.name }}">
</div>
<div class="media-body">
<a class="h4 media-heading" href="{{ this.url }}"
target="_blank">{{ this.name }}</a>
<p>{{ this.desc }}</p>
<p>
<!-- ITERATE OVER THE DEVELOPER’S SKILLS -->
{{#each this.skills}}
<!-- REUSE THE NEW "skill" PARTIAL TO DISPLAY EACH
DEVELOPER SKILL AS A CLICKABLE LINK -->
{{> skill}}
{{/each}}
</p>
</div>
</div>
</div>
{{/each}} {{/partial}}
我已经将技能列表附加到我们从”数据库”中检索到的开发人员对象上, 因此我做了一些更改:我将呈现技能标签的行移到了一部分, 并在原来的那部分使用了该部分。
然后, 当我遍历开发人员的技能时, 我可以重复使用同一部分, 以将这些技能中的每一个都显示为可单击的链接。而且, 如果你还记得的话, 这还会代理选择技能事件, 并传递相同的技能名称。这意味着我们可以将其包括在任何位置(具有适当的上下文), 并且我们将获得一个可点击的标签, 可通往技能视图!
最终触摸-预加载器
好的, 我们现在有一个基本的功能应用程序。它很干净而且可以快速运行, 但是加载仍然需要一些时间。 (在我们的情况下, 部分原因是由于我们使用了未连接的, 未缩小的资源, 但在现实世界中的应用中, 可能还有其他重要原因, 例如加载初始数据)。
因此, 作为最后一步, 我将向你展示一个巧妙的技巧, 以添加预加载动画, 在Ractive渲染我们的应用后, 该动画将被删除:
载入中…
那这里的魔力是什么?实际上很简单。我直接在我们的根元素上添加了一些内容(它是Bootstrap动画进度条, 但它可以是动画GIF或其他内容)。我认为它非常聪明-在加载脚本时, 用户会看到加载指示器(由于它不依赖JavaScript, 因此可以立即显示)。但是, 一旦Ractive应用程序初始化, Ractive将使用渲染的模板覆盖根元素的内容(从而删除预加载动画)。这样, 我们仅需一段静态HTML和0行逻辑即可实现此效果。我认为这很酷。
总结
考虑一下我们在这里完成的工作以及完成这项工作的难易程度。我们有一个非常全面的应用程序:它显示技能列表, 允许快速搜索技能(甚至在用户在搜索框中键入内容时支持技能列表的交互式更新), 允许导航至特定技能并返回, 并列出开发人员选择的每种技能。此外, 我们可以单击任何开发人员列出的任何技能, 将我们带到具有该技能的开发人员列表。以及少于80行的HTML和少于40行的JavaScript。在我看来, 这非常令人印象深刻, 并且充分说明了Ractive的功能, 优雅和简单。
该应用程序的有效版本可在此处在线获得, 完整的源代码已公开并在此处可用。
当然, 在本文中, 我们几乎没有涉及Ractive框架所能提供的一切。如果你喜欢到目前为止所看到的内容, 强烈建议你开始使用Ractive的60秒设置, 并开始自己探索Ractive提供的所有功能。
评论前必须登录!
注册