【插件开发】:后台管理页面脚本按需加载

插件的后台管理页面的主要功能是协助用户设定插件运行时参数,一般都需要用户进行交互,这就少不了需要客户端脚本的参与(主要是js脚本,当然还有配套的css样式表)。

WP的后台本身就已经加载了许多的脚本,包括各类类库和基础功能的脚本。过多的脚本文件引入,会增加HTTP请求,增加流量,拖慢速度。好在,WP的后台已经采取了相当多的措施,来管理后台加载的脚本。首先是用wp-dependency管理依赖关系,用load-scripts来压缩、并加载脚本,使得各种类库被合并到同一个文件中进行加载,节省了HTTP请求数量和流量。

在开发插件管理后台的时候,窃以为还是遵循WP本身的开发习惯比较好,也将这一套措施应用到自己的开发过程中,这样一来,会有很多的好处。首先,不会给WP的后台带来太多的负担,使用WP本身的脚本加载机制,就不会为WP的后台增加多于的HTTP请求,而且,WP后台中,将脚本在页面的最尾部引入到页面之中,这也遵循了比较优秀的实践经验。

其次,使用WP自身的脚本管理机制,不会重复引入相同的脚本,也可以合理地管理依赖关系,在进行WP相关的开发过程中,如果需要引入特定的脚本库,只需要调用wp_enqueue_script函数即可,比如引入jquery类库,wp_enqueue_script(‘jquery);就可以实现在页面中正确引入jquery类库。WP_Script对象会自动判断重复,如果已经有别的插件或者页面本身已经引入了jquery,那么就不会重复引入。另举个例子,假如需要一个拖拽排序的效果,那么需要选用jquery ui中的sortable组件,那么只要调用wp_enqueue_script(‘jquery-ui-sortable’);即可,在页面中,就可以发现,ui.core.js也被加载了,ui.core是sortable依赖的库,但是却不需要开发者自己去操心了。

从上述描述中,也可以看到另一个好处,就是wp其实已经包含,并且注册了很多常用的类库和效果库,使用其自带机制,调用这些脚本都是非常容易的,并不用费心去管这些脚本到底存在何处,其url到底是什么,只需要简简单单用一个名字既可以引入页面中。此外,如果插件中引用的脚本类库升级了,那么也会随着用户端WP的升级而自动更换是上升级的脚本,省却了更新插件的麻烦。

下面是我个人在开发中的实践作法。

在插件开发过程中应用脚本库和特定脚本需要调用的函数就是wp_enqueue_script,上面已经介绍过了,接下来介绍一下这个函数调用的位置。

比如说,我现在要开发一个插件,叫做detail info,在其后台管理页面中,需要用到jquery ui selectable组件。引入这个脚本的方法如下:

一般我个人开发一个插件的时候,都喜欢将所有的插件初始化工作放到一个init函数中,然后将这个函数hook到plugins_loaded之上(这么做的原因就不赘述了)。在init函数中,就可以调用wp_enqueue_script函数了,当然也有不按照这个结构来写插件的,但是道理是一样的,就是此函数要在action wp_head和admin_head发生之前被调用,plugins_loaded这个action发生的顺序是非常靠前的,所以此函数放在这里,一定可以正确引入脚本。

当然,这么做只是可行的做法,却不是正确的做法,这么做的话,由于是添加到了plugins_loaded这个action中,那么所有的页面中,至少会多出几个脚本文件,一个jquery,一个是ui.core,一个是ui.selectable,这个组件仅需要在后台中引入,却不得不在所有的页面中加载,可以说,是一种很不经济的做法。对于开发者来说没什么,但是对于用户来说,就增加了他们服务器的负担。当然更多时候,用户可能并不懂得其中的意义,他只知道一件事情,就是插件装得越多,自己的博客运转速度就越慢,很多WP用户都经历过热衷于安装各种插件,到头疼安装了很多插件,到最后一切从简对插件望而却步的一个转变过程。其实,我个人觉得,在这个转变过程中,插件的开发者是负有一定责任的。

上面是更加常用的一种做法,通过is_admin()条件判断函数,来使得脚本只在后台加载,这样的话,就不会在博客的页面上添加可能根本就完全不会用到的脚本。

做到了这一步,基本上,事情已经做到差不多了,但是,这离尽善尽美还很遥远。使用上述第2个代码范例,可以使脚本不在前台页面加载,但是在后台页面还是没有区分能力,简单说,就是会在后台所有的页面中都添加了上述几个脚本文件。这样,会拖慢博客后台的速度,好在后台用户并不多,访问压力并不大,一时半会儿,也不会出什么问题。但是,短时间没问题不代表永远没有问题,在后台所有页面引入相同的脚本,有一个后果,很有可能在插件安装数量多了以后,出现脚本冲突问题。虽然这种冲突,可以通过脚本地开发技巧来解决,一般比较成熟的类库都不会互相发生冲突,但是,如果有开发者用这种机制引入了自己的编写的专用脚本,那就很难说了。

最佳的做法,无疑是只在用到的那个页面,才引入相关的脚本。其他页面一律不加入脚本。做到这一点,并不容易,乃至是相当苦难。我个人在开发过程中也试过几种方法,但是效果都不是很好。

起初,我的做法是,直接将脚本写在插件的管理页面的代码中,这么一来,肯定是可以实现脚本按需加载的,但是这样一来,就会使js脚本代码和页面代码混在一起,开发起来,维护起来,还是调试起来,都不是很方便。另外也破坏了web开发行为和数据表现分离的准则。不过,这种做法,确实是一种比较稳定而且比较容易想到,又比较好实现的做法。对与自己写的脚本来说,也没什么太多不便,不便主要体现在类库的引入上面,前文提到的好处一个都不复存在了。

后来,我又发现了一种方法,就是通过$_SERVER系统变量中的QUERY_STRING来判断当前所处的页面。比如,在上述范例的插件中,其管理界面的url显示成这个样子:

http://localhost/wordpress/wp-admin/options-general.php?page=detail_info_manager

从这个URL中,就可以判定出来,当前打开的页面确实是这个插件的管理页面,这样一来,以此为依据就可以决定是否要引入脚本,其示例如下:

上述这个办法在后台脚本引用管理方面的效果还算不错,基本上按照我的要求做到了按需加载。