arrow_back

Stimulus 框架简介

Rei 写于 24 Feb 2018

2018 年年初,Basecamp(开源 Ruby on Rails 的公司)开源了 Stimulus 前端框架,为前端大战再添一员。

Stimulus 是一个轻量级前端框架,其用法用一张图就可以概括:

这个框架通过 data-contoller data-target data-action 等属性,把 HTML 元素和 JavaScript 行为绑定。图例使用的是 ES6 的语法,这并不是必须的。

由于官方教程也只有六页,所以关于用法不做展开。

Stimulus 解决的问题

和热门的 JavaScript 框架(例如 React、Vue)相比,Stimulus 本身不处理 HTML 渲染,而是为已渲染的 HTML 添加行为——大部分情况下,就是跟服务端渲染配合。

Stimulus 是从 Basecamp 中抽取出来的,这意味着它被测试过和 Ruby on Rails 的其它前端组件相处融洽,这包括 Assets Pipeline、Turbolinks、SJR。同时,它补全了 Rails 前端方案缺失的一环,也就是 Turbolinks 环境下如何组织 JavaScript。

如果你不知道 Turbolinks,它是一个可以加速页面加载的前端库。但同时它让应用的前端变成类似单页应用,换页不会触发 DOMContentLoaded 事件,需要重新考虑 JavaScript 的组织方式。

举个例子,假设我们要为一个按钮绑定点击事件,在引入 jQuery 的时候可以这么写:

<button id="btn"></button>
<script>
  $('#btn').on('click', function() {} );
</script>

通常 JavaScript 会打包到单独的文件里,在 <head> 引入,执行的时候不能确保 HTML 已经解析完成,所以会使用 jQuery 的 ready 方法确保逻辑在 HTML 载入完成后执行:

<script>
  $(document).ready(function() {
    $('#btn').on('click', function() {} );
  });
</script>
<button id="btn"></button>

但是,在 Turbolinks 环境下,以上代码不能正常工作,因为 Turbolinks 换页不触发 DOMContentLoaded 事件。一个解决方法是使用 Turbolinks 事件:

<script>
  $(document).on('turbolinks:load', function() {
    $('#btn').on('click', function() {} );
  });
</script>
<button id="btn"></button>

或者换个思路,绑定事件到 document 元素上:

<script>
  $(document).on('click', '#btn', function() {} );
</script>
<button id="btn"></button>

把这个模式推广到更普遍的场景:

<script>
  $(document).on('click', '[data-behavior~="btn"]', function() {} );
</script>
<button data-behavior="btn"></button>

上面这段代码看起来已经很像 Stimulus 了,实际上 Stimulus 就是脱胎自 data-* 事件绑定模式。没有 Stimulus,你也可以用这种方式整理 Turbolinks 环境下的前端代码;有了 Stimulus,Rails 就有了一套前端代码规范,而不用每个团队自己制定。

另外,绑定 document 事件有个潜在问题,随着组件逻辑增加,每次事件都要经过 document 的事件托管,这是不必要的消耗。

Stimulus 使用了 MutationObserver,这是一个新的 DOM API,作用是监视 DOM 上的元素插入和删除,并提供钩子方法,这样就可以实现页面插入一个元素的时候绑定,删除一个元素的时候解绑。

所以,用 Stimulus 实现的组件 HTML,既可以是服务端渲染,也可以是前端动态插入,跟 Turbolinks 和 SJR 完美结合。

我需要 Stimulus 吗?

评估 Stimulus 不能脱离 Rails 的整套前端方案:

Turbolinks + Stimulus + SJR + WebPacker(Vue, React...)

可以看到,Stimulus 只是解决一部分问题,即非内容渲染(服务端渲染)、非 AJAX 请求(SJR)、非复杂交互(Vue, React)的前端逻辑。Rails 提供了一系列组件,让我们根据不同情况挑选不同的工具。

如果你已经在使用 Turbolinks + SJR,那么可以马上使用 Stimulus,它会让前端代码更规范。

如果前端需求非常复杂,需要处理大量客户端状态,那么应该求助于前端渲染框架(Vue,React)。

如果你的团队已经使用前后端分离的方式开发、不需要 SEO、没有对前后端分裂感到痛苦,那么就不需要 Stimulus。

你的前端需求也许没那么复杂

什么样的前端需求是复杂?处于不同阶段的人会得到不同的答案。对于刚接触 HTML/CSS/JavaScript 的人来说,做一个点击弹出窗口就是复杂;而对于写过可视化编辑器的人来说,做一个网页游戏才是复杂。

在我看来,现在大部分前端的复杂,来自工具产生的附属性复杂,而非需求的本质性复杂。人们在选择工具的时候,很容易陷入一个误区:如果选择能处理最复杂情况的工具,那么面对简单情况也能同样处理。这种想法忽视了如果选择了复杂的工具处理所有事情,那么再简单的事情也会变得很复杂。

Rails 的前端方案是一个提供各种选择的方案,解决问题的方法并不只有一个。我们可以先用一个工具,不合适再换另一个工具。关键在于认识到没有银弹,简单的问题用简单的方法解决。

如果你厌倦了臃肿的前端应用,痛恨它浪费掉的大量时间,那么是时候走一下 Rails 的道路。