这篇文章主要简单讲讲在 Rails 项目中如何在 Hotwire 环境下使用传统轮播图组件 Slick。原文链接 https://www.lanzhiheng.com/posts/slick-on-hotwire (由于 RubyChina 暂时无法浏览 gif 图,要看完整的效果动画还请移步到原文)。
针对没有前后端分离的项目,我们一般会借用类似Slick这样的组件来做轮播图(快两年没人维护了,真的没事吗?)。对于 N 年前的 FullPageReload 项目来说,我们根本不用关心 Slick 相关组件销毁的问题。然而对于引入了Turbo的项目,事情可能要稍微复杂一点点了。接下来笔者会简单剖析一下相关的问题,并提供思路与解决方案。
在传统的开发模式下,针对页面的 JavaScript 代码一般都是这幅德行
$(document).ready(function() {
// ....
})
如果是使用了 Turbo 的 Rails 项目,那可以是这个样子
$(document).on('turbo:load', function() {
// ...
})
所以搭配 Turbo 使用,一个常规的 Slick 轮播图代码大概是这个样子的
.one-time {
width: 300px;
margin-top: 300px;
border: 1px solid black;
margin: 300px auto;
h3 {
text-align: center;
}
}
<div class="one-time">
<div><h3>1</h3></div>
<div><h3>2</h3></div>
<div><h3>3</h3></div>
<div><h3>4</h3></div>
<div><h3>5</h3></div>
<div><h3>6</h3></div>
</div>
$(document).on('turbo:load', function() {
$('.one-time').slick({
dots: true,
infinite: true,
speed: 300,
slidesToShow: 1,
adaptiveHeight: true
});
})
令人怀念的前端三剑客。效果如下
咋一看貌似一劳永逸,然而....点击页面上任何的链接离开当前页面,然后再点击浏览器的返回按钮试试?点击浏览器的返回按钮理论上就会触发了 Turbo 的Restoration Visits。这个时候你会发现 1). 原来轮播图无法点击(失效了)。2. 控制台 JavaScript 脚本报错。
接下来就着重解决一下,通过浏览器来返回或者前进的时候所造成的 Slick 轮播组件失效的问题。
根据StackOverflow的说法,之所以会出现这种问题,是因为 Slick 对应组件多次被初始化了。
因为 Turbo 的 Restoration Visits 会从缓存中拿历史页面,而在历史页面中对应的组件其实已经是初始化后的 Slick 组件了。对 Slick 组件,再次进行初始化就会引发前面提到的异常。建议就是如果目标组件没有经过 Slick 初始化,才运行对应的初始化代码所以
$(document).on('turbo:load', function() {
$('.one-time').not('.slick-initialized').slick({
dots: true,
infinite: true,
speed: 300,
slidesToShow: 1,
adaptiveHeight: true
});
})
妙啊,这个时候点击返回的时候错误信息已经没有了。然而返回的历史页面中轮播图不可用这个问题依旧还是存在。对于有生命周期的组件来说这个问题比较简单,等到生命周期的最后注销这个组件,需要用的时候再重新初始化就好了。然而在 Turbo 的页面跳转中没有生命周期这个概念,那怎么办呢?可以这样做
$(document).on('turbo:visit', function() {
$('.one-time').filter('.slick-initialized').slick('unslick');
})
就是每次要跳转页面 (turbo:visit) 的时候先把对应的组件销毁,然而在资源加载后 (turbo:load) 重新进行初始化。这样一来哪怕使用的是浏览器的返回或者前进按钮进行页面浏览,也不会出现轮播图不可使用的问题了。
只是这种做法在进行常规页面跳转的时候,会因为 Slick 组件的销毁导致页面闪烁。如果你的轮播图不是在首屏,那问题不大,然而如果刚好在首屏,那体验是相当的不好,接下来我们再利用 Hotwire 里面的黑科技来进一步解决这个问题。
上面的做法虽然勉强能用,但会有一些交互上的体验问题,而且这样的案例一多,势必会在同一个事件中注册太多的回调动作,维护性将越来越差。这种时候我们就会怀念 React,Vue 提供的生命周期,以组件为模块管理代码,并且等生命周期的最后进行组件销毁。
其实 Hotwire 也提供了这样的东西,就是Stimulus,我们来看看它是否可行。
<div class="two-time" data-controller="two-time">
<div><h3>1</h3></div>
<div><h3>2</h3></div>
<div><h3>3</h3></div>
<div><h3>4</h3></div>
<div><h3>5</h3></div>
<div><h3>6</h3></div>
</div>
样式就跟之前的一样就好
.one-time,
.two-time {
width: 300px;
margin-top: 300px;
border: 1px solid black;
margin: 300px auto;
h3 {
text-align: center;
}
}
// app/packs/controllers/two_time_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
initialize() {
$(this.element).slick({
dots: true,
infinite: true,
speed: 300,
slidesToShow: 1,
adaptiveHeight: true
});
}
disconnect() {
$(this.element).slick('unslick');
}
}
效果
这样一来体验更好了,前面的one-time
例子出现的闪烁问题也不复存在了。而且代码是以组件为模块来组织的,相对来说更好维护一些。
这篇文章主要简单总结一下在 Hotwire 的环境下如何搭配使用“老旧”的轮播组件 Slick。提供了两种方案
希望以后能挖掘出更好的做法。