最近玩mediawiki比较多,感觉mediawiki扩展方面做得真的不错,API方面,完善的日志功能也是非常棒。并且mediawiki本身就为维基百科服务的,于WordPress相比,不需要魔改就能够很好地使用于大型项目。学校有个用WordPress做的展示网站,用户太多编辑太混乱,权限也是非常混乱和危险,现在都有点想安利他们用mediawiki了。
MediaWiki最近的一个更新将脚本全部js脚本都异步了,应该说这也是一个设计的潮流吧。
感谢mediawiki这次的升级为我提供这个月的博客更新素材,终于有点干货了。
前言
大约半年多前,就有客户拿着Google Developer PageSpeed Insights要我们一个一个fix网站的上面的问题希望能够帮助SEO。mediawiki这个1.26 的升级,彻彻底底地修复第一项Eliminate render-blocking JavaScript and CSS in above-the-fold content中的Javascript部分。之前在各种工程中,我从未完整地彻底这一项修复,没想到js全部异步后,感官提速效果竟然如此明显。稍后估计我也会在这个Wordpress中实践一下尝试彻底异步javascript 还有css。
日文维基百科 mediawiki 1.27 版
没有升级的网站的数据 mediawiki 1.25.3 版
问题探究
这里出现了一个问题,由于历史原因,异步加载毕竟也算是非常新潮的技术。很多插件还是老一套的设计思路,没有考虑到要针对异步这个特性优化自己的代码。基于Extemsion:Widget的小工具这次不能够使用jQuery等原本都是同步载入的脚本了。
我们有必要研究一下mediawiki这次改进的地方。打开mediawiki第一个加载的js,可以发现主要有3个大段,
var mediaWikiLoadStart = new Date().getTime(); function isCompatible(ua) { //.........省略 } var startUp = function() { mw.config = new mw.Map(true); //.........省略 };
这里主要研究的应该是第三段,关于脚本的触发方式。可以发现1.26版和1.25.3版的最大区别是
var script = document.createElement("script"); script.src = "//bits.moegirl.org/zh/load.php?debug=false&lang=zh&modules=jquery%2Cmediawiki&only=scripts&skin=vector&version=ItzZIeye"; script.onload = script.onreadystatechange = function() { if (!script.readyState || /loaded|complete/.test(script.readyState)) { script.onload = script.onreadystatechange = null; script = null; startUp(); } }; document.getElementsByTagName("head")[0].appendChild(script);
就是这一段简单的代码大幅的提升了mediawiki加载速度,从另外一方面,可以看出mediawiki的程序员还是相当有素质的,所有的函数都用mw这个变量封装起来了。在写更多代码的时候,应该思考是否使用mediawiki提供的函数进行扩展,避免重复造轮,降低长期的维护成本。
相关文档地址:https://doc.wikimedia.org/mediawiki-core/master/js/
解决问题
做到这个需要widget和远端服务器上面的js进行配合。
在Widget上可以模拟mediawiki的做法,由于这个文本出现得比原本mw的在页首的脚本肯定要晚,因此执行的时候mw这个对象一定被声明了。因此可以在远端放心地使用mw这个变量。
(function() { var script = document.createElement("script"); script.src = "https://masterchan.me/test.js远程js的地址"; script.onload = script.onreadystatechange = function() { if (!script.readyState || /loaded|complete/.test(script.readyState)) { script.onload = script.onreadystatechange = null; script = null; } }; document.getElementsByTagName("head")[0].appendChild(script); })();
实例http://zh.moegirl.org/Widget:AsyncScriptTest
值得注意的是,这个时候jQuery 可能还没有被加载,因此在远端的服务器不能够直接使用jQuery。我们可以通过mw这个对象提供的函数对自己写的内容进行封装。通过mediawiki提供的文档可以知道,mw这个对象本身就有相当多的有用的函数
mw.loader.implement("yuruyuri3hai填入名称",function($,jQuery){ //可以放心地使用jQuery 了 $("p").hide(); });
这里只是举了一个简单的例子
后续问题
可以选择相同的服务器来放脚本,但是自动部署方面,可能会把环境变得更加复杂。也可以用一个新网址用来托管脚本,但是这样被人污染的概率也增加了。这是一个增加后台复杂性的一个行为。我们可以通过git来帮助我们进行规范管理,当然,需要一个有执行力的CTO。
另外由于ref里面含有关键字,如果需要通过某个防火墙的话,需要部署两台服务器。