The stimulus.js
framework is a lightweight JavaScript framework developed by the big name Basecamp, the company that developed the core Ruby on Rails framework. I’ve heard of the stimulus.js framework for a long time, but I’ve never actually used it. Recently, I just had the opportunity to practice in a small project of my own, and have some experience, so I’d like to share a summary.
Reminder: If you want to quickly experience the demo made by stimulus.js, you can take a look at this todomvc-stimulus.
A restrained front-end JavaScript framework
Speaking of my general impression of the stimulus.js
framework, I think it is a very restrained front-end JavaScript framework. It focuses on the binding of HTML elements to JavaScript objects, and the binding is one-way, not the two-way binding that has long been common in front-end development. Beyond that, it offers no additional functionality.
Because of its restraint, lightweight is its inevitable first advantage. Secondly, with its designed concept of controller
, it can achieve isolation and decoupling of state in the interaction logic. Finally, the organization of the controller
code is familiar to those familiar with Rails development: convention over configuration. Each controller
definition needs to follow the convention that a controller
corresponds to a file in the controllers
directory, with the same name as the controller
.
The lightness of Stimulus.js
There are very few core concepts in Stimulus.js
, there are only 4 core concepts that you need to understand to get started with the stimulus.js
framework.
Controllers
Controllers
are JavaScript objects bound to HTML elements that declare data attributes such as data-controller="todos"
.
|
|
stimulus.js
will automatically instantiate the corresponding controller for all such elements, one instance of each. In the example above, stimulus.js
automatically looks for the file located in app/javascript/controllers/todos_controller.js
and imports the default classes exported there, a classic convention over configuration approach.
Of course, if you don’t want to use or can’t use the agreed form, you can also register the controller explicitly via the functions provided by stimulus.js
.
|
|
Such elements, and their descendants, are visible to the controller to which the element is bound. That is, in the stimulus.js
framework, the controller’s actions can only be applied to the controller-bound element and its descendants. The same principle applies in the case of nested Controllers.
Controllers can collaborate with each other by means of events, which will be added later when we talk about Actions.
Targets
Targets
is another way to implement binding between HTML elements and JavaScript objects, but it works under a specific Controller.
The rule for declaring the target is data-<controller>-target=<target-name>
, and accordingly, the object to be bound needs to be declared in the controller.
Once you have bound the targets, you can use something like this.addBtnTarget
or this.addBtnTargets
(for cases where multiple HTML elements are bound to the same Target) in the controllers method to access the bound HTML elements, as per the stimulus.js protocol for targets ) to access these bound HTML elements.
Controllers
and Targets lifecycle callbacks
The above mentioned Controllers and Targets are binding functions between HTML elements and JavaScript objects, because HTML elements are loaded with the browser and subsequent DOM operations, which brings up the question, what is the lifecycle of these objects?
These lifecycle callback functions are all closely related to changes in the DOM, and generally look at these conditions.
- Does the element exist?
- Does the bound identifier exist in the element’s property list, such as
data-controller
ordata-<controller>-target
?
The connect type callback is called when the condition is changed from partially or completely not being met to being met; conversely, the disconnect type event callback is called if all conditions are no longer met due to some DOM action.
controllers can define initialization tasks in the connect()
callback, such as the initialization of some state of the controller, and accordingly, [name]TargetConnected
can be used for the initialization of a target.
Actions
Actions is the event callback mechanism in stimulus.js
, similar to the syntax of onclick
and onchange
in HTML.
Actions supports multiple event callback declarations, which also facilitates collaboration between Controllers.
|
|
In this example, the flow of the program execution is as follows.
- after the user clicks the Add button, the
click
event triggered by the button triggers a callback to theadded
method of thetodos
controller. - after the
added
method executes its own core logic, it triggers thetodos:added
event by calling thethis.dispatch
method, noting that thetodos:
prefix here is automatically added by the framework. - the
todos:added
event is generated, triggering a callback to thesubmitter
controller’ssubmit
method.
In this way, by abstracting different controllers to achieve the separation and decoupling of logic, and then through the event mechanism, the logic will be assembled and orchestrated.
Understanding these 4 core concepts is enough to develop a relatively simple front-end logic with stimulus.js
. Of course, there are several other concepts in stimulus.js
, but in my opinion they are just icing on the cake, so I don’t need to go into them here.
Also talk about the scenarios where stimulus.js
is not suitable
Despite being a small project, there are some issues that I found cumbersome in using stimulus.js
.
- Lack of encapsulation of DOM operations: Since
stimulus.js
only provides binding between HTML elements and JavaScript objects, it does not provide encapsulation of DOM operations, so when you need to manipulate the DOM, you often need to use the operations of native DOM objects directly, such asElement.classList .add()
type, if it is in the early browser, you still need to worry about compatibility issues, etc., but the good thing is that now the browser compatibility problems have been much less, this is not too big a problem. - Lack of front-end rendering support: Because the binding in
stimulus.js
is not bidirectional, in some cases where you need to render different page content or visual effects based on JavaScript objects, without the support of other frameworks, you have to write various string interpolations andElement.innerHTML = xxxx
, which is also inefficient.
So, to sum up, if your front-end page is a heavy interaction page, it might not be a wise choice to use only stimulus.js
. If I choose to use stimulus.js
, I think it’s more comfortable to use stimulus.js
if it’s a content-based light interaction scenario, such as blogs or forums, where the general interaction is the comment section, simple text input and additional display, etc.; but in other cases, I would probably go directly to vue.js
or other more comprehensive framework to minimize the code that synchronizes state between the page and the logic.
Some things to keep in mind when using stimulus.js
- Action outside the scope of Controllers cannot call back to the Controller’s methods. This problem took some time to solve, but I didn’t understand why, then I realized that it was because I didn’t pay attention to the Controller scope. Because my action declares that the controller that needs to be called back is not visible in the current DOM, so the callback fails.
- The action triggered before controller initialization cannot call back to the controller method This problem is because I declared an action in the code and also executed
dispatch
in the controller, but at this time, because the target controller has not yet been initialized, it seems that the code does not have any syntax or use any syntax. It looks like there is no syntax or usage error in the code, but it is impossible for the action to trigger the callback successfully.