新手问题 Turbolinks 5 下有没有可能让页面单独只执行自己的 JS/CoffeeScript

samport · 发布于 2016年9月01日 · 最后由 hww 回复于 2016年9月04日 · 1399 次阅读
26232

碰到一个页面js的小问题,发现自己对于Turbolinks了解太少了。

例如首页上有个图片切换的slide幻灯片效果,看代码是使用js定时器,第一次打开首页时一切正常。

点击其它页面以后,再返回到首页,幻灯片出现了闪动。页面调试发现浏览器这时候已经有两个js定时器在同时运转。初步分析结果是打开其它页面时,首页上的js代码又被引用,又产生了一个新的定时器任务。

虽然可以通过追加一些js代码,将已经存在的定时器先清除,然后再执行新的js定时器,但是这个显然不是个好办法。使用了turbolinks以后所有的js/coffeescript一次性被读入以后,执行时候也是被全部执行的吗?如果不同页面上有相同的js变量,那不是会乱套吗?

使用turbolinks时候,有没有让页面只执行该页面单独的js脚本的好办法?

共收到 11 条回复
1573
nightire · #1 · 2016年9月01日

先不说单独执行页面脚本的好办法,就当这个方法你已经知道并且用上了吧。Turbolinks 实现的是页面无刷新的视图更新,当你从一个“页面”进入到下一个“页面”的时候,浏览器并没有重刷新。这就意味着在上个页面创建的对象(比如说定时器回调)依然存在于内存之中,你必须要先清除旧的定时器然后再执行新的,所以并非“不是个好办法”,而是必须得用这个办法。

你举的这个例子其实和“该页面单独执行脚本”这个诉求根本就没有关系,是一个 A/B 问题。

26232
samport · #2 · 2016年9月01日

#1楼 @nightire 感谢指点!已经修改了幻灯片js脚本,表面上看起来没啥问题了。

反复被触发的原因之一估计是因为我在coffeescript脚本里使用了$( document ).on 'turbolinks:load', ->,导致每次点击其它页面,首页的幻灯片脚本定时器都会被执行一遍。

另外,我感觉Turbolinks在加载时对于提高效率特别有帮助,但是这么多js程序同时活动在一个浏览器页面容器里,如果出现不同页面里面有相同的html元素class命名,或者是js命名,应该会出现一些麻烦吧。

例如, app/assets/javascripts/demo1.coffee里面的代码:

$ ->
  $(".sample").click ()->
   alert("test")

app/assets/javascripts/demo2.coffee里面的代码:

$ ->
  $(".sample").click ()->
   alert("this is a debug message")

当这两个脚本都被Turbolinks加载以后,应该会出现冲突。我的困惑其实是类似这种问题。

我在网上看到有人提出的解决方案是在html的body标签上添加class,然后在js脚本中使用这些自定义的class标签作为过滤器。

ORGANIZING JAVASCRIPT IN RAILS APPLICATION WITH TURBOLINKS PAGE SPECIFIC JAVASCRIPT IN RAILS 别人整理好的js脚本: /jquery-readyselector

121
lyfi2003 · #3 · 2016年9月01日

#2楼 @samport Turbolinks5 概述及实现原理, 你这个问题是一个常规问题, 你看看这里的文章就可以处理掉了.

26232
samport · #4 · 2016年9月01日

#3楼 @lyfi2003 你的文章很有价值。我多少开始有点明白在使用turbolinks+jquery时碰到的那些莫名奇妙的问题是怎么来的。

借鉴了各种方案,我的做法如下:

1). 在layout文件里给body动态加class标签。

<body id="home2" class="<%= controller_name %> <%= action_name %>">

2). 在app/assets/javascripts/xxxx.coffee文件里面对html元素的class进行过滤,防止在不相关的页面上重复执行js脚本

$( document ).on 'turbolinks:load', ->
  #如果当前页面检测不到指定的class标签,则停止执行后面的脚本
  return unless $(".static_pages.home").length > 0
 #.......

在学习过程中碰到的第一个坑是turbolinks有效的时候jquery的ready事件不会被触发。后来才发现rails 5+ turbolinks 5下需要用'turbolinks:load'来代替jquery的“ready”事件。

还曾经碰到的一个大坑是在集成adminlte模板的时候,解决的方式也是为turbolinks追加额外的事件处理,否则左侧的菜单收放会不正常。

谢谢各位大拿的帮助!

1
Rei · #5 · 2016年9月02日 2 个赞

根据场景不同可以有不同写法。

如果只是一段很短的代码,并且只有一个页面用到,那么可以直接写在 body 的 script 里。 https://github.com/turbolinks/turbolinks#working-with-script-elements

<script>
$('.sample').on('click', function() {
  alert('this is a debug message');
});
</script>

很多情况下,你可以将事件绑定到 document 或 window,避免绑定 Turbolinks event 。https://github.com/turbolinks/turbolinks#running-javascript-when-a-page-loads

$(document).on 'click', '.sample', ->
  alert('this is a debug message')

如果多个页面用到相似逻辑,那么可以抽取通用逻辑。

# <div data-alert-message="this is a debug message"></div>
$(document).on 'click', '[data-alert-message]', ->
   alert($(this).data('alert-message'))

更进一步,你可以用 MutationObserverCustom Elements 将可重用的前端逻辑组件化,让它自动在插入或被移除的时候执行初始化和清除逻辑。 https://github.com/turbolinks/turbolinks#responding-to-page-updates

<!-- https://github.com/basecamp/trix -->
<form >
  <input id="x" type="hidden" name="content">
  <trix-editor input="x"></trix-editor>
</form>

Turbolinks 不只是让你更改绑定的事件,而是把你的 Web 应用变成持久运行的进程,让你重新思考 JavaScript 的组织方式。

26232
samport · #6 · 2016年9月02日

#5楼 @Rei 谢谢指点,继续学习。。

目前我的做法还属于偷懒的做法,主要是不想花太多时间去研究和改动别人已经写好的jquery代码。对出现命名冲突的地方不得已而代码开头部分增加一个body标签的范围过滤器,来避免js脚本同时运行而造成的页面异常。

如果前端的js也全部是自己写的话,在编写js时就充分考虑到利用好turbolinks的「单页面」的特性应该才是正确的做法吧。

14293
hww · #8 · 2016年9月02日

在 body 上加上 action_namecontroller_name 作为 class 。
然后以是否包含对应 class 作为判断条件。例如:

initTyped: function(){
  if($('.home.index').length > 0){
    $('.website-description').typed({
      strings: ['xxx'],
      loop: true,
      showCursor: true,
      startDelay: 500,
      backSpeed: 10,
      backDelay: 1500,
      typeSpeed: 80,
      contentType: 'html'
     });
  }
},
initPage: {
  home: function(){
    if($('.home.index').length > 0){
      App.Home.messageForm();
    }
  }
},

@lyfi2003 的文章加上 ORGANIZING JAVASCRIPT IN RAILS APPLICATION WITH TURBOLINKS 应该就能上手了。

26232
samport · #9 · 2016年9月03日

#8楼 @hww 我也是用的这种方法,相对来说比较简单。

#7楼 @aspirewit 看了一下这个js-namespace-rails,这个动态生成js.erb的方法也可以,不过我想这种方式生成的js应该已经不再属于turbolinks的加载对象了。

5023
dongli1985 · #10 · 2016年9月04日

试试如下事件?

$(document).on('turbolinks:before-cache', function() {
  ...
})
14293
hww · #11 · 2016年9月04日

@dongli1985 turbolinks:before-cache能解决大部分第三方库生成的 UI widgets 重复渲染的问题。 👍

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册