
哈勃! 我是Badoo的Miloš,这是我在Habr的第一篇文章,最初发布在我们的
技术博客中 。 希望您喜欢它,如果有任何疑问,请分享并发表评论
所以...反应,无磷?
它出现在这个十年的中期(被无休止的JavaScript框架之战困扰),拥抱
DOM ,通过将HTML与JavaScript混合在一起震惊了所有人,并改变了Web开发的格局,使人们无法识别。
所有这些成就,
甚至都不是框架 。
喜欢它还是讨厌它,React确实做得很好,那就是HTML模板。 与一个很棒的社区和一个健康的生态系统一起,不难看出它为什么成为最受欢迎和最有影响力的JavaScript库之一,即使不是最受欢迎的JavaScript库之一。
在移动网络团队中,我们没有遵循任何严格的JS框架-至少没有遵循任何流行的JS框架-我们使用了传统技术和现代技术的结合。 尽管这对我们来说很有效,但是操作DOM通常很困难,我们希望通过减少“手动”更新的次数,增加我们的代码重用性以及减少对内存泄漏的担心来减轻这种情况。
经过一番调查,React被认为是最佳选择,我们决定选择它。
在此过程的中间,我加入了Badoo。 之前曾经引导过并参与过React项目,但我在实践中意识到了它的优缺点,但是要迁移具有
数亿用户的成熟应用程序则是完全不同的挑战。
x
React将HTML和JavaScript混合为
JSX格式。 尽管看起来像模板语言,但JSX实际上只是一种语法,或者说是
语法糖 ,对于React调用,看起来与HTML非常相似。
我们自己的HTML文件组织得井井有条,我们的大多数渲染都像
template.render()
一样简单。 在转向React时,我们如何保持这种顺序和简单性? 对我来说,除了技术上的困难外,一个想法很明显:用JSX代码替换我们现有的调用。
经过一些初步的计划,我开始尝试并包装了一个命令行工具,该工具执行两个简单的操作:
- 读取UI(JavaScript)文件中引用的模板
- 用HTML内容替换
template.render()
调用
当然,这只会使我们前进一半,因为我们仍然必须手动修改HTML代码。 考虑到模板的数量和数量,我知道最好的方法是自动化。 最初的想法听起来很简单-如果可以解释,则可以实现。
将最初的工具演示给队友之后,我得到的最佳反馈是,有一个
解析器可用于我们使用的模板语言。 例如,这意味着我们可以比使用
正则表达式更容易地解析和翻译代码。 那时候我真的知道这行得通!
瞧,几天后,诞生了一种工具,用于将
Dust.js类似 HTML的模板转换为JSX React代码。 我们使用了Dust,但是由于解析器的广泛使用,翻译其他流行模板语言的过程应该相似。
有关更多技术细节,请跳到下面的“开源”部分。 我们使用了诸如
Esprima之类的工具来解析JS代码,并使用了
PEG.js解析器生成器来解析Dust模板。 用最简单的术语来说,就是翻译这种类型的模板代码:
<div class="encounters {?isExpanded}is-expanded{/isExpanded}"> {?showTooltip} <div class="tooltip"> <span>{#_t}{encounters_tooltip}{/_t}</span> <div class="icon"> {@Icon name="icon-encounters" size="stretch" /} </div> </div> {/showTooltip} <div class="images"> {#images} <img src="{src}"> <input type="radio" id="{id}" {?selected}checked{/selected} /> <label for="showme-{id}"> {name} </label> {/images} </div> <div class="footer"> {! encounters-footer template will be injected here !} </div> </div>
与其等效的JSX代码:
<div className="encounters {props.isExpanded ? 'is-expanded' : ''}"> {props.showTooltip ? <div className="tooltip"> <span>{i18n.get('encounters_tooltip')}</span> <div className="icon"> <Icon name="icon-encounters" size="stretch" /> </div> </div> : null} <div className="images"> {props.images.map(item => <img src={item.src}> <input type="radio" id={`showme-${item.id}`} defaultChecked={item.selected ? true : undefined} /> <label htmlFor={`showme-${item.id}`}> {item.name} </label> )} </div> <div className="footer"> {/* encounters-footer template will be injected here */} </div> </div>
请参阅
此处并排比较。
此后,我们的过程非常简单。 我们自动将模板从一种格式转换为另一种格式,并且一切都按预期工作(感谢您,自动测试)。 首先,我们保留了旧的
template.render()
API以使更改保持隔离。
当然,使用这种方法,您仍然会得到模板,而不是“适当的” React组件。 真正的好处在于,在大多数情况下,只需将模板代码包装在函数调用中,就可以轻松地从已经是JSX的模板切换到React,即使不是微不足道的。
您可能会想:为什么不从头开始编写新模板呢? 简短的答案是,我们的旧模板没有任何问题-我们只是拥有许多旧模板。 至于重写它们并努力实现真正的组件化,那就
另当别论了 。

有人可能会争辩说
组件模型只是可能过去的另一趋势,那么为什么要坚持下去呢? 很难预测,但是一个可能的答案是
您不必 。 如果您快速进行迭代,则可以尝试各种选择,而不必花太多时间在任何一种选择上,直到找到最适合您的团队的格式。 那是Badoo对我们来说的核心概念之一。
随着ES7 / 8 / Next,Elm和Reason的兴起,更不用说TypeScript和类似的解决方案了,曾经
*.js
代码与JavaScript越来越难以区分,而且这种趋势似乎会继续下去。 与其不被它淹没,不如利用它对我们有利?
开源的
本着
做好一件事情的精神,我们将这些内部工具分为以下几个部分:
- dust2jsx-负责将实际灰尘转换为jsx的程序包
- ratt (全部反应)-用于在磁盘上读取/写入文件的命令行工具。 负责包括引用的模板,并
dust2jsx
内部使用dust2jsx
来转换代码
我们甚至开源了这些工具-请务必在我们的
GitHub页面上检出它们以及其他开源材料。 如果您认为它们有用,请贡献或简单地给我们评论。