wordpress中get_template和get_stylesheet的区别以及子主题

这篇文章将会主要从子主题的角度介绍,当你完全了解WordPress中的子主题(child themes)之后,就可以很自然的知道get_template和get_stylesheet的区别。以及,在你自己的主题开发中,可以非常好的实践WordPress中关于主题继承的思想了。

问题的产生

当我们从网上下载安装了一款非常不错的主题之后,某一天,突然发现某个细节需要进行修改。然而你不愿意直接修改这个主题,因为这个主题一旦升级,你的修改就被覆盖掉了。你也不愿意把主题代码全部拷贝一份,改造成自己的主题,因为这种行为太愚蠢了。有没有什么办法,可以通过自己的代码,不破坏原主题的同时,对它进行覆盖呢?

wordpress提供的主题继承的思想和机制可以实现这一目标。你需要建立一个子主题,这个子主题中存在一个字段,指定它所继承的父主题。而通过子主题的一些代码,可以部分覆盖父主题的一些代码,从而实现你自己的一些功能。

子主题

wordpress子主题的官方介绍文档在这里,阅读时间大概是10分钟。文档不仅介绍了什么是子主题,而且告诉你一些最佳实践。本文也将对这些内容进行梳理。

代码层面的概念

我们都知道wordpress的主题在wordpress开发中占据着非常重要的地位。我们创建一个主题,通过撰写代码,上传到themes目录,到后台启用,就可以使用新主题了。而子主题本质上也是一款新主题,和其他任何主题没有本质的区别。当你开发完一款子主题之后,上传到themes目录,也要到后台去启用这款(子)主题,启用之后,也需要做各种初始化工作,和使用其他各种主题没有多大的区别。

那么为什么我们要写子主题,而不是干脆开发一款新主题?除了文章开头的那种情况之外,写子主题可以帮助你快速编程,还有可能帮你实现一些特殊的工程效果,比如我的博客,我自己另外开发了一个插件,可以使手机访问时博客使用的是子主题。子主题继承了父主题很多功能。

创建子主题

如前所述,子主题本质上也是一款主题,你得像开发一款新主题一样,创建一个文件夹,然后在里面至少放置两个文件:

  • style.css
  • functions.php

一般主题必须有index.php,但是子主题可以复用父主题的index.php,所以不是必须的。

得到上面两个文件之后,你需要在style.css的开头写上主题的信息,和一款新主题一样。但是,最关键的是,你必须指定Template字段:

/*
 Theme Name:   Twenty Fifteen Child
 Template:     twentyfifteen
 Version:      1.0.0
*/

红色的这一行决定了你当前这款主题将继承当前系统中存在的哪一款主题。至于你当前的子主题叫什么名字,完全都是自由的,只有这个Template字段非常重要。

如果你仅仅是想看看效果,现在就可以把这个主题目录上传到wordpress,到后台启动它。因为现在你什么都没有做,你会发现,你的博客的界面和twentyfifteen是一模一样。

get_template和get_stylesheet

现在我们来研究一下wordpress里面的get_template和get_stylesheet。简单的说get_template是获取options表里面的template字段的值,get_stylesheet则是获取stylesheet的值。所以,你可以用get_bloginfo('template')得到相同的信息。

那么template和stylesheet到底是什么鬼呢?

template就是当前使用的主题(父主题)的名称(实际上是主题的目录夹名称),stylesheet是当前使用的子主题的名称,如果没有启用子主题,实际上这两者是一样的,如果启用的主题style.css中存在Template字段,那么这个字段的值会被保存给template。

所以这样一来就非常简单了,一个主题被启用时,它的名称(目录夹名称)会被作为wordpress系统的stylesheet值保存起来,倘若style.css中存在Template字段,这个字段的值会被作为wordpress系统的template值保存起来,而如果不存在Template字段,则它自己的名称会被作为wordpress系统的template值保存起来。

基于这个理论,也可以非常轻松的理解get_stylesheet_directory_uri和get_template_directory_uri这两个函数的区别了。

继承和代码覆盖

但知道上面的道理,只能读懂别人写的代码。自己要灵活运用还需要知道另外两件事:

  1. 子主题的functions.php执行完之后会同时立即执行父主题的functions.php
  2. 子主题如果不存在某个模板文件,将会使用父主题的

这两点看上去都非常简单,但是却深坑不断,不好好研究会让你在写代码时很烦躁。

函数的覆盖

functions.php就像theme内部的plugin机制一样,先于很多东西执行。而如果子主题的functions.php先执行,那么就可以通过在子主题的functions.php中写一些函数来覆盖父主题里面的一些函数,比如父主题里面有一个函数function post_lists(),你觉得需要对它进行覆盖,那么可以在子主题的functions.php中写同名函数……等等,这会报错的!除非父主题的这个函数被一个if (!function_exists('post_lists'))包裹着。

没错,继承并没有看上去那么容易。如果一款主题的开发者,开发的时候没有考虑到自己的主题可能被继承,那么就会留下这些深坑。这些坑在下文还会遇到。因此,如果你是一个主题的开发者,应该多谢if (!function_exists('post_lists'))来留个子主题开发的可能性。

样式和脚本的继承

你在子主题里面写了自己的header.php,而且在子主题中有自己的css样式表。但是你觉得应该复用父主题的样式,这样可以减少写很多样式的时间。一种想法是使用@import来实现样式的引用,当然,这是非常不可取的,wordpress的建议是使用wp_enqueue_stylewp_enqueue_scripts,这需要在你的子主题functions.php中这么写:

add_action( 'wp_enqueue_style', 'my_theme_enqueue_styles' );
function my_theme_enqueue_styles() {
    wp_enqueue_style( 'parent-style', get_template_directory_uri() . '/style.css' );
}

总之,你需要先了解wp_enqueue_stylewp_enqueue_scripts

get_template_directory_uri

在子主题中使用get_template_directory_uri()是获取父主题的主题目录路径,而在父主题中使用get_stylesheet_directory_uri可以获取子主题的目录路径。所以,一个主题的开发者,应该使用get_stylesheet_directory_uri而非get_template_directory_uri来获取当前被启用的主题的路径。但是如果你确定要使用某张图片,而这张图片明显是在父主题的目录里,那么就应该使用get_template_directory_uri。

总之,这对一个主题的开发者(而非子主题的开发者)产生了要求,它要求你时刻保持清醒的大脑,你要时刻提醒自己“我的主题可能被继承”。如果有了这种想法,在父主题的header.php中使用了get_stylesheet_directory_uri,那么有可能子主题就不需要自己再写一个header.php来改变样式文件的引用地址了。

对于子主题的开发者而言则轻松的多,只要记住get_stylesheet_directory_uri是获取子主题的路径,get_template_directory_uri是获取父主题的路径即可。

不改变后台配置的情况下切换主题

这是我的博客的一种实现方式,后台是一切正常的,当手机访问博客的时候,通过代码层面实现使用子主题(其实也就是另外一款主题)。

在wordpress里面,代码层面实现强制使用某个主题有两种方式,一种是通过define覆盖配置,另一种是通过add_filter修改某些结果。我的做法是add_filter:

function DeviceThemeExtends($template) {
  // 这里要做手机端的检查,省略了
  return $template + '-mobile'
}
add_filter('template','DeviceThemeExtends' ); 
// 如果是使用子主题,上面这一行要注释掉,
// 因为如果使用子主题,实际上表示template值和stylesheet值是不同的
add_filter('stylesheet','DeviceThemeExtends' );

总结

通过这篇文章的讲解,你对wordpress子主题这个内容应该有了大致的了解。其实你只需要记住两个重要的点:1.子主题的functions.php先于父主题的functions.php执行;2.get_stylesheet_directory_uri和get_template_directory_uri在子主题中得到不同的结果。这两点记住了,一般就比较少出现问题了。

2018-01-13