关于Disqus等评论系统异步加载时评论无法正常显示的Bug
2017-05-24
本文均以Disqus为例,其他评论系统类似。另,本文提及的链接可能有时效性,以后不见得还管用。
问题重现
鉴于我现在已经把这个Bug修复了,这个Bug在我这不太好重现。我先描述一下吧。
如果使用正常网址,比如
https://dlyang.me/my-new-chromebook/
进入一篇博文的时候,会发现评论的数量和评论本身加载都没有问题。然而一旦网址带有请求的时候,比如
https://dlyang.me/my-new-chromebook/?foo=bar
这时候评论就无法显示了,因为Disqus后台认为这是一篇新的文章,id是「/my-new-chromebook/?foo=bar」而不是「/my-new-chromebook/」。
奇特的是,这种现象只有异步加载(也就是点击「加载XXX评论」的时候才会加载评论框)的时候才会产生。
如前面所述,我的Blog暂时没法重现这个Bug,鉴于我Blog的主题是从BeiYuu.com继承来的,我决定拿他的站点来重现(他的主题换了,不过评论框还是一样)。
比如访问:
http://beiyuu.com/carriage-return-line-feed-new-line-end-file
这时候评论加载是没有问题的。
换成
http://beiyuu.com/carriage-return-line-feed-new-line-end-file?foo=bar
这时候就不行了,没有评论显示。
也不难想象……如果你在带有「?foo=bar」的链接下评论了,下次再加载「?foo=bar」页面还能看到你的评论,而在没有「?foo=bar」的页面下却看不到。
在自己Blog页面内跳转一般不会出现这种Get请求,而它一般在从外部跳转进Blog的时候会出现,比如从微信里点开链接,其实出现的频率不低。
异步加载的流程
如上所述,这个Bug只会在异步加载的时候出现,所以需要分析一下流程。
第一步当然是点击「加载XX评论」。
这时候JS代码里面会把页面的URL和ID赋值给Disqus会调用的变量。所以问题出在给的URL和ID有问题。
接着页面的JS会去加载你专属的embed.js,去加载评论框并展示。
- BeiYuu出问题的代码长大概下面这个样子:
window.disqus_shortname = 'your_short_name';
$('#disqus_container .comment').on('click',function(){
$(this).html('加载中...');
var that = this;
$.getScript('http://' + disqus_shortname + '.disqus.com/embed.js', function(){$(that).remove()});
});
出错的一个问题在于,他没有指定本页面的Disqus的Url和ID。如果没有指定,Disqus的embed.js就会从当前页面的Url里去找文章的ID,导致出问题。
- 官方的代码长下面这个样子(在同步加载的时候工作正常):
<div id="disqus_thread"></div>
<script>
var disqus_config = function () {
this.page.url = PAGE_URL;
this.page.identifier = PAGE_IDENTIFIER;
};
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://EXAMPLE.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
- 我之前出错的代码是这样的(下面链接以本文链接为例):
<div id="disqus_container">
<div style="margin-bottom:20px" class="right"></div>
<a href="#comment-disqus" class="comment" onclick="toggleDisqusComments('#disqus_container');">
查看<strong>Disqus</strong>评论
</a><span class="comment-count">(<a href="https://dlyang.me/async-comment-loading-bug/#disqus_thread" style="font-size:18px;">0</a>)</span>
<div id="disqus_thread"></div>
<script type="text/javascript">
function toggleDisqusComments(container){
if(jQuery(container).has("iframe").length>0){
var iframe_id = $(container+" iframe").attr("id");
jQuery(container+" iframe#"+iframe_id).toggle();
return;
}
var disqus_config = function () {
this.page.url = 'https://dlyang.me/async-comment-loading-bug/';
this.page.identifier = 'async-comment-loading-bug';
};
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://lanternd.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
}
</script>
基本上我的代码就是在官方代码外面套了一层壳而已。点击以后触发函数去加载评论框。我既定义了变量page.url
,又给了page.identifier
结果还是不行。
失败的尝试
中间尝试了不少方案去Debug,主要围绕Url的处理展开,比如
<a href="https://dlyang.me/async-comment-loading-bug/#comment-disqus" class="comment" onclick="toggleDisqusComments('#disqus_container');">
查看<strong>Disqus</strong>评论
</a><span class="comment-count">
我把href强制指到了没有「?foo=bar」的链接去。但是这样点击以后加载的页面会回到窗口的顶端,用户体验很不好。
后来还试了在JS代码里面把Url手动更新,修改window.location.href
之类的全局变量。也还是会回到页面顶端。
解决问题
我解决这个Bug纯属误打误撞,解决了以后才发现原因。
其实是
var disqus_config = function () {
this.page.url = 'https://dlyang.me/async-comment-loading-bug/';
this.page.identifier = 'async-comment-loading-bug';
};
这段代码中,this的作用范围导致的。
这段代码放在toggleDisqusComments()
函数中的时候,this
指代toggleDisqusComments
这个函数,而放在外面的时候,指代的是window
或者Document
之类的。在前一种情况下,disqus_config
也沦为了局部变量而不是全局变量。
换句话说,错误代码里面压根没有给Disqus提供该页面的正确Url和ID。
更新以后(当时只是抱着试一试的心态而已)变成了目前的正确代码:
<script type="text/javascript">
var disqus_config = function () {
this.page.url = 'https://dlyang.me/async-comment-loading-bug/';
this.page.identifier = 'async-comment-loading-bug';
};
function toggleDisqusComments(container){
if(jQuery(container).has("iframe").length>0){
var iframe_id = $(container+" iframe").attr("id");
jQuery(container+" iframe#"+iframe_id).toggle();
return;
}
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://your_disqus_id.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
}
</script>
Bug修复后,即使在本地运行Jekyll,也就是在127.0.0.1:4000
中访问页面评论的时候也可以加载原站点(https://dlyang.me)的Disqus评论了。
我猜测如果disqus_config
放在toggleDisqusComments
里,把this
改成window
也可行。但没有尝试过。
最后
我对前端并不是很了解,纯属瞎折腾玩的,学习也不系统。所以写出来的代码特别丑,还多Bug。另外,上面这几段关于this
话的描述可能也是错的,不过代码是好使的。我现在现在都是做出一个控件,然后就在它下面加一段JavaScript代码,结构零散杂乱。最近了解到了Browserify、Webpack之类的前端坑。总有一天我会把各种JS、CSS集成到一两个文件里,不过不会是最近。我得休息一下了。
最后的最后,还得感谢Lattespirit在过程中提供的帮助。
参考源
[1]
this
operator
LanternD