在wordpress中按需加载JavaScript和CSS » 荒野无灯weblog

Keep It Simple, Stupid.

荒野无灯weblog

在wordpress中按需加载JavaScript和CSS

首先说一下在WP中怎么引入script和stylesheets:
wp中是通过wp_enqueue_scriptwp_enqueue_style,

wp_enqueue_script( $handle, $src, $deps, $ver, $in_footer ); 
wp_enqueue_style( $handle, $src, $deps, $ver, $media );

需要注意的是,wp_enqueue_script 不能在wp_head或wp_print_scripts action中被调用:

Note: This function will not work if it is called from a wp_head or wp_print_scripts actions, as the files need to be enqueued before those actions are run. See the [#Usage Usage] section for the correct hooks to use.

如果你想改变wp默认的script的URL,可以先deregister之,再register之:

function my_scripts_method() {
    wp_deregister_script( 'jquery' );
    wp_register_script( 'jquery', 'http://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js');
    wp_enqueue_script( 'jquery' );
}    

add_action('wp_enqueue_scripts', 'my_scripts_method');

定位script或stylesheets的URL一般用plugins_url(一般用于插件)和 get_template_directory_uri (一般用于主题中) 这两个API,不要尝试硬编码URL路径,因为用户可以把wp-content目录放在任意路径下。

wp_enqueue_script('my-script', plugins_url('my-script.js', __FILE__), array('jquery'), '1.0', true);

参数依次是handle,用来唯一标识一个注册的script。
然后是script的URL,
array(‘jquery’)这里是依赖关系。
然后是script的版本号,最后一个参数是表示是在header (false,默认的)还是footer (true)中输出。
此语句需放在一个函数里面,然后该函数由hook函数执行。直接enqueue有时是会出问题的。

在enqueue之前可以先注册之,注册用wp_register_script和wp_register_style,参数与enqueue的一样。然后,我们可以直接用

wp_enqueue_script('my-script');

wp_enqueue_style('my-style');

的形式来引入。
注册一般是在初始化时就注册它,一般hook的勾子有:

init 前台、后台都有效
admin_init 仅后台有效

挂(enqueue)script可hook有勾子:

wp_enqueue_scripts
admin_enqueue_scripts

如果是直接输出(echo / print ),则可用以下hook:

wp_print_scripts 
wp_print_footer_scripts 
admin_print_scripts 
wp_print_footer_scripts 

挂stylesheet可用hook:

wp_print_styles
admin_print_styles 

这里帖一个WP官方doc的示例:

/*
     * register with hook 'wp_print_styles'
     */
    add_action('wp_print_styles', 'add_my_stylesheet');

    /*
     * Enqueue style-file, if it exists.
     */

    function add_my_stylesheet() {
        $myStyleUrl = plugins_url('style.css', __FILE__); // Respects SSL, Style.css is relative to the current file
        $myStyleFile = WP_PLUGIN_DIR . '/myPlugin/style.css';
        if ( file_exists($myStyleFile) ) {
            wp_register_style('myStyleSheets', $myStyleUrl);
            wp_enqueue_style( 'myStyleSheets');
        }
    }

此外,还有 wp_head wp_footer 可用于输出(echo )css或js.
这样引入script的好处:

可避免同一脚本被载入多次
可正确处理各脚本的依赖关系

好了,这些介绍得差不多了。
下面开始讲正题。


WP虽然帮助我们解决了多次载入问题和依赖问题,但是按需加载就要靠我们自己来实现了。
scribu 介绍了一种他称之为The Jedi Master way的引入脚本方式:

class My_Shortcode {
    static $add_script;//设定是否引入标记

    function init() {
        add_shortcode('myshortcode', array(__CLASS__, 'handle_shortcode'));
               //先注册脚本
        add_action('init', array(__CLASS__, 'register_script'));
              //再显示脚本(按需)
        add_action('wp_footer', array(__CLASS__, 'print_script'));
    }

    function handle_shortcode($atts) {
                //只有文章内容包含此短代码,才置显示标记值为真
        self::$add_script = true;

        // 这里是具体短代码处理部分...
    }

    function register_script() {
        wp_register_script('my-script', plugins_url('my-script.js', __FILE__), array('jquery'), '1.0', true);
    }

    function print_script() {
               //这里判断是否该引入此脚本
        if ( ! self::$add_script )
            return;

        wp_print_scripts('my-script');
    }
}

My_Shortcode::init();

scribu的这个例子很好地展示了按需加载js的应用。
此外winy也介绍一了种用js自身的判断来按需加载js的方法,下面的例子是若google cdn的jquery库加载失败,则引入wp自身默认jq库,此代码需放置在header:


winy还有个例子是利用浏览器事件触发来动态加载js:

//在用户鼠标焦点到评论框时,执行js的加载和绑定
var commentjs=function(){
    $.getScript(themeurl+"js/comment.js");//调用评论工具栏和ajax评论提交需要的js
    $('#toolBar').animate({opacity: 'show'}, 300);//载入工具栏
    $('#comment').unbind('focus',commentjs);//取消自身绑定focus的动作,避免重复加载
};

$('#comment').bind('focus',commentjs);//绑定上面的commentjs函数

另外,我在beer planet博客上看到来自@white_shadow的一段代码:

add_filter('the_posts', 'conditionally_add_scripts_and_styles'); // the_posts gets triggered before wp_head
function conditionally_add_scripts_and_styles($posts){
    if (empty($posts)) return $posts;

    $shortcode_found = false; // use this flag to see if styles and scripts need to be enqueued
    foreach ($posts as $post) {
        if (stripos($post->post_content, '[code]')) {
            $shortcode_found = true; // bingo!
            break;
        }
    }

    if ($shortcode_found) {
        // enqueue here
        wp_enqueue_style('my-style', '/style.css');
        wp_enqueue_script('my-script', '/script.js');
    }

    return $posts;
}

此代码经我测试,确实可用,不过还是有一些问题。
它是利用的the_posts这个filter hook,the_posts在哪里调用的呢?
它在wp-includes/query.php 文件中class WP_Query的function &get_posts() line 2731:

        if ( !$q['suppress_filters'] )
            $this->posts = apply_filters_ref_array('the_posts', array( $this->posts, &$this ) );

看到这里,有童鞋应该明白我说的问题在哪里了。
这个filter的范围太广了,而且不可控制。
因为我们一但hook上这个filter,那么凡是调用 WP_Query 来获取日志的操作都会被hook,比如,WP默认的侧边栏小工具(widget):最近文章小工具 (WP_Widget_Recent_Posts),如果小工具中的某篇文章中包含

[code]

这个标记,那么此脚本会错误地告诉我们,它找到标记了。。。其实当前文章可能并没有这个标记。
还有就是效率问题,因为有些短代码是带属性的,没有固定统一的格式,用strpos显然不好判断,若用正则的话,又太消耗时间。有些得不尝失。

最后,还有一些简单的办法,如可以通过一些简单的条件判断,使之只在部分页面加载:

function enqueue_my_script()
{
    if( is_singular() )
    {
        wp_enqueue_script( 'my_awesome_script', '/script.js', array( 'jquery' ));
    }
}
add_action('wp_enqueue_scripts','enqueue_my_script');

参考文档:
How To Include CSS and JavaScript Conditionally And Only When Needed By The Posts

How to load JavaScript like a WordPress Master

Tagged in :

All Comments (0)
Gravatar image
No Comments