前军教程网

中小站长与DIV+CSS网页布局开发技术人员的首选CSS学习平台

Stimulus 状态管理,幻灯片显示(状态管理vuex)

前一篇文章Stimulus:浏览器不支持复制或者弱网条件下,怎么办?,我们接着看Stimulus的状态管理。

大多数框架都鼓励我们您始终将状态保存在JavaScript中。他们把DOM作为一个只写的渲染目标。


Stimulus在这点上有所不同。一个Stimulus应用的各种状态都保存在DOM的属性上;controllers本身是无状态的。这一点使得Stimulus在任何地方都可以和HTML融洽的工作,包括初始化document,一个Ajax请求,一个Turbo visit,或者另一个js库,并且关联的controllers是自动启动的,而不需要任何显式的初始化操作。


上一篇文章中,我们学习了Stimulus的controller如何通过给一个元素添加一个class来维护简单的状态。但是,如果我们要存储一个值的话,而不是简单的标志,我们应该怎么做呢?


我们来看看这个问题,创建一个名为slideshow的controller,它会记住当前展示的第几个幻灯片。


我们写一段HTML:

<div data-controller="slideshow">
    <button data-action="slideshow#previous"> ← </button>
    <button data-action="slideshow#next"> → </button>

    <div data-slideshow-target="slide"></div>
    <div data-slideshow-target="slide"></div>
    <div data-slideshow-target="slide"></div>
    <div data-slideshow-target="slide"></div>
</div>

写了4个幻灯片,属性设置为data-slideshow-target="slide",我们的controller负责在同一时间只显示一个幻灯片。

接下来草写一下controller的代码。创建一个新文件,src/controllers/slideshow_controller.js:

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
    static targets = [ "slide" ]

    initialize() {
        this.index = 0
        this.showCurrentSlide()
    }

    next() {
        this.index++
        this.showCurrentSlide()
    }

    previous() {
        this.index--
        this.showCurrentSlide()
    }

    showCurrentSlide() {
        this.slideTargets.forEach((element, index) => {
            element.hidden = index != this.index
        })
    }
}

在controller中的showCurrentSlide方法中,我们通过this.slideTargets遍历了所有的幻灯片,如果它的下标和当前记录的下标相同,那么就设置元素的hidden属性为false,显示出来,否则就隐藏起来。


我们初始化controller时,默认显示第一个幻灯片,然后next()和previous()方法控制显示下一个还是上一个。

这里我们提及了initialize()方法,initialize()方法干了什么?它和我们之前使用的connect()方法有什么不同?他们都是Stimulus的回调方法。


下面这些都是Stimulus生命周期中的回调方法,他们在配置或者结束连接状态时很有用。


initialize() 只执行一次,当controller第一次被实例化的时候

connect() 只要controller连接到DOM元素就会执行

disconnect() 只要controller断开与DOM元素的连接时就会执行


下面我们尝试从DOM元素读取幻灯片的初始状态。

注意下,目前,我们的controller是如何跟踪它的状态(当前选中的幻灯片),就是通过this.index属性。


但是如果我不想一开始就展示第一个幻灯片,而是展示第二个,那怎么办呢?


有一个办法就是通过读取HTML的data属性,获取初始的幻灯片索引。例如,可以添加data-index属性到controller连接到元素内:

<div data-controller="slideshow" data-index="1">

然后在initialize()方法中,我们读一下data-index属性值

initialize() {
    this.index = Number(this.element.dataset.index)
    this.showCurrentSlide()
}

这看起来完成了,但是方法很笨重,需要我们自己定义属性名,并且如果我们想再次访问这个index值,或者增加index值,需要持久化到DOM中时,完全没有帮助。

Stimulus的controller支持使用value属性,而且这些value属性会自动映射为data属性。只需要在controller类中定义一下:

static values = { index: Number }

Stimulus会创建controller属性this.indexValue,它被连接到元素的data-slideshow-index-value属性,并且自动做了数值转换。


在HTML中添加对应的关联属性:

<div data-controller="slideshow" data-slideshow-index-value="1">

再修改一下controller,添加static values的定义到controller中,然后改一下initialize()方法,日志打印一下this.indexValue:

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

    initialize() {
        console.log(this.indexValue)
        console.log(typeof this.indexValue)
    }

    // …
}

刷新页面,看下console中是否打印了1和Number


和targets类似,我们在controller类中定义了values,并且描述为static静态的对象。在这个案例中,我们定义了一个类型为数值的index对象属性。


现在我们来更新controller中的initialize()方法和其他的方法,把this.index替换成this.indexValue。这样看起来才算真正完成了任务。

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
    static targets = [ "slide" ]
    static values = { index: Number }

    initialize() {
        this.showCurrentSlide()
    }

    next() {
        this.indexValue++
        this.showCurrentSlide()
    }

    previous() {
        this.indexValue--
        this.showCurrentSlide()
    }

    showCurrentSlide() {
        this.slideTargets.forEach((element, index) => {
            element.hidden = index != this.indexValue
        })
    }
}

刷新页面,用浏览器的元素检查功能看一下controller连接的元素,看看当幻灯片切换后,它的data-slideshow-index-value属性值是否变化。

和最初版本比较的话,我们的controller已经有了很大提升,但是仔细你会发现showCurrentSlide()方法被重复调用了很多次。当controller初始化时,还有更新this.indexValue时,我们要手动更新document的状态。接下来我们重构一下代码,消除这部分的重复。


我们可以定义一个值变化的回调,告诉controller当index的值变化时应该怎么做。


首先,删除initialize()方法,然后定义一个新的方法,indexValueChanged()。然后删除在next()与previous()方法中的this.showCurrentSlide():

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
    static targets = [ "slide" ]
    static values = { index: Number }

    next() {
        this.indexValue++
    }

    previous() {
        this.indexValue--
    }

    indexValueChanged() {
        this.showCurrentSlide()
    }

    showCurrentSlide() {
        this.slideTargets.forEach((element, index) => {
            element.hidden = index != this.indexValue
        })
    }
}

刷新页面,确认一下幻灯片行为还是和之前一样。

在controller初始化时,还有改变data-slideshow-index-value属性值时,Stimulus都会调用indexValueChanged()方法。


另外,属性值是可以设置默认值的,比如:

static values = { index: { type: Number, default: 2 } }

如果controller连接的元素没有设置data-slideshow-index-value属性时,幻灯片将从下标为2的幻灯片开始。如果还有其他的值,可以混在一起写:

static values = { index: Number, effect: { type: String, default: "kenburns" } }

本文,我们已经了解到如何使用values获取并持久化幻灯片的当前下标。

从可用性的角度来看,我们的controller是不完整的。当你看到第一个幻灯片的时候,再按previous按钮,就出问题了,indexValue从0减到-1。我们可以把indexValue设置为最后一个幻灯片的下标。那个Next按钮也有同样的问题。

下一篇文章,我们将看看如何在Stimulus的controller中跟踪外部资源,例如计时器和HTTP请求。

Stimulus 如何用加载和插入远程HTML片段来异步填充页面

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言