Day26. 认识 Stimulus,与Javascript成为好朋友

React.js, Vue.js, Angular.js为近期很流行的框架,改变了前端生活圈的生态。不过除了这些主流框架以外,Basecamp出了一款基於Rails的微型框架Stimulus

Stimulus为Basecamp出品的基於SSR框架,宗旨为Rails的後端工程师不用了解更多的Javascript概念而设计出来的框架。在 Rails 圈已经红了一段时间,而我自己本身,是听 Drifting Ruby 的课程才了解 Stimulus

目前Stimulus已经发展到 v2.0!它已经问世了一段时间,并且已经进行了1个大版本的更动。

Config

首先我们使用以下指令安装框架

yarn add stimulus

Rails 进入点的撰写位置会放在 config/webpacker.yml

app/javascript/packs/application.js 中,我们可以看到许多被 require 进来的套件或资料夹,这些都是使用者请 webpack 帮忙编译与打包的东西。

我们在app/javascript/packs/application.js 写下import 'controllers' ,就可以读取controllers 资料夹里面的档案。

import "controllers"

import Rails from "@rails/ujs"
import * as ActiveStorage from "@rails/activestorage"
import "channels"

Rails.start()
ActiveStorage.start()

在使用Stimulus前,我们必须要了解Webpack的使用。

Webpack 预设会先读取 index.js 的档案,所以我们会先在app/javascript底下创建一个资料夹名controllers,并且在app/javascript/controllers 创一个档案index.js。假设我们要引入一个han_theme.scss,我们可以在index.js 写下

import 'han_theme.scss';

Webpack 一层一层读取,首先由Layout写下javascript_include_tag

➡️ 读取app/javascript/packs/application.js

➡️ 读取 require("styles")

➡️ 读取 ./styles/index.js

➡️ 读取import 'han_theme.scss';

✅ Webpack 就会知道读取han_theme.scss

若我们使用 Stimulus,要在index.js写下

// Load all the controllers within this directory and all subdirectories. 
// Controller files must be named *_controller.js or *_controller.ts.

import { Application } from "stimulus"
import { definitionsFromContext } from "stimulus/webpack-helpers"

const application = Application.start()
// 动态读取结尾为 _controller 的档案
const context = require.context("controllers", true, /_controller\.(js|ts)$/)
application.load(definitionsFromContext(context))

Introduction

接着开始要介绍 Stimulus的基本用法。

Stimulus 一共有三个主体(引言出自Hotwire.dev

  • value ➡️ 资料 字串, 数字, 阵列...
  • target ➡️ 即为 DOM 元素(跟常见的event.target为同一角色)
  • action ➡️ 执行动作

actions, which connect controller methods to DOM events using data-action attributes

targets, which locate elements of significance within a controller

values, which read, write, and observe data attributes on the controller’s element

Usage

为了部落格列表使用Stimulus,我们先创建1档案:app/javascript/controllers/admin/blogs_controller.js,并且在档案里头写下一段基本的用法

import { Controller } from 'stimulus';

export default class extends Controller {
  connect() {
    console.log('部落格列表')
  }
}

Stimulus 是用Javascript假物件导向的语法糖的语法写,但虽然如此,Rails的精神就是惯例优於设定,除非真的很好奇,不然我们不需要懂其中的内涵。

import { Controller } from 'stimulus';

export default class extends Controller {
  constructor() {
    super();
    // 自定义逻辑
  }
  
  connect() {
    console.log('部落格列表')
  }
}

之前做了一个破坏惯例的动作,在内部class加入建构子Constructor,之後便出现了问题。

connect() 为位於生命周期中的画面载入阶段。若重整画面後,就会马上执行connect()方法。个人习惯在connect()写下一段简单的console.log ,检查该页面是否执行Stimulus Code。

接着我们对画面进行改写

= title '部落格列表'

= tag.div data: { controller: 'admin--blogs' }
  / 卡片内容
  = card do
    / ......    
  
  
  / 弹跳视窗
  = modal(id: 'new-blog-modal', confirm_wording: '送出文章',
          confirm_form: 'new_modal', title: '新增文章') do
    / ......

最後呈现的结果如下 ⬇️

https://ithelp.ithome.com.tw/upload/images/20210919/20115854wjQHbMp7Ku.png

我们来实作官方首页的程序码吧!

https://ithelp.ithome.com.tw/upload/images/20210919/20115854VDEjBEEugA.png

= tag.div data: { controller: 'admin--blogs' }
  / Stimulus 卡片内容
  = card do
    = card_header(title: 'Stimulus')
    = card_body do
      = tag.input type: 'text', data: { 'admin--blogs-target': 'name' }
      = button_tag '输出', type: 'button', data: { action: 'click->admin--blogs#greet' },
                            class: 'btn btn-primary mx-2'
      = tag.span data: { 'admin--blogs-target': 'output' }
import { Controller } from 'stimulus';

export default class extends Controller {
  static targets = [ "name", "output" ]

  connect() {
    console.log('部落格列表')
  }

  greet() {
    this.outputTarget.textContent =
      this.nameTarget.value === '' ? 'Hello, 输入框没有填入值' : `Hello, ${this.nameTarget.value}!`
  }
}

我们将官方的范例稍微做改写,若没有值,则会回传输入框没有填入值。操作完官网上的范例後,接下来我们讲讲admin--blogs的命名规则怎麽来。

下列为官网提到的 controller 的命名规则,因此我们可以推敲,当我们把档案在javascript/controllers/admin/blogs_controllercontroller的名称为admin--blogs

If your controller file is named… its identifier will be…
clipboard_controller.js clipboard
date_picker_controller.js date-picker
users/list_item_controller.js users--list-item
local-time-controller.js local-time

接着我们再讲click->admin--blogs#greet是什麽?

click->admin--blogs#greet为点击动作时,触发admin--blogsgreet动作,而这边可以省略成admin--blogs#greet

以下为官网提到的预设事件

Element Default Event
a click
button click
form submit
input input
input type=submit click
select change
textarea input

value

若要实现getter, setter,我们可以使用value 作为两个值之间的媒介。

import { Controller } from 'stimulus';

export default class extends Controller {
  static targets = [ "name", "output", "searchedContent"]
  static values = { testId: Number }

  connect() {
    console.log('部落格列表')
    this.testIdValue = 1
  }

  greet() {
    this.testIdValue += 10

    this.outputTarget.textContent =
      this.nameTarget.value === '' ?
        `Hello, 输入框没有填入值 ${this.testIdValue}` :
        `Hello, ${this.nameTarget.value}! ${this.testIdValue}`
  }
}

上述的例子为,将this.testIdValue设定初始值=1,并且每按一次,this.testIdValue就会 +10

除了当作getter,setter外,还可以在画面上绑定Value。我们将指定 controller 的位置指定value,并且在connect()内部将值印出来

= tag.div data: { controller: 'admin--blogs', 'admin--blogs-view-id-value': 40 }
  /! 内容
import { Controller } from 'stimulus';

export default class extends Controller {
  static values = { viewId: Number }

  connect() {
    console.log('部落格列表')
    console.log('this.viewIdValue', this.viewIdValue)
  }
}

https://ithelp.ithome.com.tw/upload/images/20210922/201158541VaZCcgCS2.png

我们也可以带入Rails物件进去,如params等。

= tag.div data: { controller: 'admin--blogs',
        'admin--blogs-view-id-value': 40,
        'admin--blogs-reload_at-value': Time.current.strftime('%F %T') }
import { Controller } from 'stimulus';

export default class extends Controller {
  static values = { viewId: Number, reloadAt: String }

  connect() {
    console.log('部落格列表')
    console.log('this.viewIdValue', this.viewIdValue)
    console.log('this.reloadAtValue', this.reloadAtValue)
  }
}

https://ithelp.ithome.com.tw/upload/images/20210922/20115854Z7Mu2fylzR.png

结论

Day26-28Stimulus的系列文

  • 今天介绍了基本的Stimulus的设定、三大元素target, value, action
  • 明天我们会介绍Stimulus如何搭配Ajax使用
  • 後天的话会介绍另外一个主题Datatable

这系列的文章,会需要比较熟悉Javascript的基本用法,如果有什麽想询问的话,欢迎在下方的留言区联络。

参考资料


<<:  Day17边框(CSS)

>>:  Day 14. 模板语法Template Syntax – 插值 Attribute、JavaScript 表达式

[13th][Day6] Docker log

docker 可以控制 docker 的 daemon & container 所使用的 日志驱动 ...

DAY 17 取得资料库资料并将含LINE emoji的讯息传出

小弟自开学後白天上课晚上上班,每天时间不多,进度比较缓慢,请多见谅 上篇将资料存至资料库,这篇要将资...

【Day21】隐写技术 Steganography

哈罗~ 今天来介绍隐写技术(Steganography)。 所谓的隐写术就是可以将资讯以明文/密文的...

Day 21网路通讯协定

前言 网路通讯协定就是为电脑进行资料交换而建立的规章或标准的集合。常用的有TCP/IP协定、HTTP...

【PHP Telegram Bot】Day07 - 安装 VS Code 并撰写第一支程序

俗话说得好:「工欲善其事,必先利其器」。 没有一个好用的文字编辑器,要如何轻松愉悦的写程序呢? 安...