进阶篇43. Drupal中的排版2

模板文件

如果上一节中主题和区域所提供的布局划分(也就是区域的定义)不能满足项目需求的话,主题开发者还可以自己修改代码来自定义布局。

主题开发者可以自定义区域。相关内容见第一篇第18节《创建.info.yml文件》。然后通过模板文件来修改布局。或者,使用现有的区域,只修改模板文件也可以修改布局。

实际上,模板文件本身并不能起到排版布局的作用,但是页面上的任何一个组件,几乎都是由模板渲染出来的,因此,讲排版布局的时候,肯定绕不开模板文件。因为,模板文件最起码定义了每一个页面元素在源代码中的顺序。而这些页面元素在页面上的实际位置和顺序,则是由元素相对应的CSS和JS决定的。因此,当我们说利用模板做排版的时候,实际上的意思是用模板搭配CSS和JS来做排版,修改模板只是其中的一部分工作而已,另一部分,比较常见的情况则是搭配某个前端框架比如bootstrap、960,或者CSS技术中和排版有关的技术,比如Flexbox,CSS Grid(见《基础篇39. 挑战views的极限(3)-- CSS网格布局入门》)等。

然后根据需要,在模板文件中输出自定义或者默认的区域。这里我们会遇到Drupal主题的一个基本概念——模板覆写。另外,普通自定义区域都是在page模板中输出的,还有两个隐藏区域是在html模板中输出的,关于隐藏区域请参考第一篇的第17节《添加一篇内容,drupal网站的页面结构》,关于覆写模板和page模板请参考第一篇的第20节《覆写模板文件、page模板》。

以下是bartik主题的page模板代码:

{#
/**
 * @file
 * Bartik's theme implementation to display a single page.
 *
 * The doctype, html, head and body tags are not in this template. Instead they
 * can be found in the html.html.twig template normally located in the
 * core/modules/system directory.
 *
 * Available variables:
 *
 * General utility variables:
 * - base_path: The base URL path of the Drupal installation. Will usually be
 *   "/" unless you have installed Drupal in a sub-directory.
 * - is_front: A flag indicating if the current page is the front page.
 * - logged_in: A flag indicating if the user is registered and signed in.
 * - is_admin: A flag indicating if the user has permission to access
 *   administration pages.
 *
 * Site identity:
 * - front_page: The URL of the front page. Use this instead of base_path when
 *   linking to the front page. This includes the language domain or prefix.
 *
 * Page content (in order of occurrence in the default page.html.twig):
 * - node: Fully loaded node, if there is an automatically-loaded node
 *   associated with the page and the node ID is the second argument in the
 *   page's path (e.g. node/12345 and node/12345/revisions, but not
 *   comment/reply/12345).
 *
 * Regions:
 * - page.header: Items for the header region.
 * - page.highlighted: Items for the highlighted region.
 * - page.primary_menu: Items for the primary menu region.
 * - page.secondary_menu: Items for the secondary menu region.
 * - page.featured_top: Items for the featured top region.
 * - page.content: The main content of the current page.
 * - page.sidebar_first: Items for the first sidebar.
 * - page.sidebar_second: Items for the second sidebar.
 * - page.featured_bottom_first: Items for the first featured bottom region.
 * - page.featured_bottom_second: Items for the second featured bottom region.
 * - page.featured_bottom_third: Items for the third featured bottom region.
 * - page.footer_first: Items for the first footer column.
 * - page.footer_second: Items for the second footer column.
 * - page.footer_third: Items for the third footer column.
 * - page.footer_fourth: Items for the fourth footer column.
 * - page.footer_fifth: Items for the fifth footer column.
 * - page.breadcrumb: Items for the breadcrumb region.
 *
 * @see template_preprocess_page()
 * @see html.html.twig
 */
#}
<div id="page-wrapper">
  <div id="page">
    <header id="header" class="header" role="banner" aria-label="{{ 'Site header'|t }}">
      <div class="section layout-container clearfix">
        {{ page.secondary_menu }}
        {{ page.header }}
        {{ page.primary_menu }}
      </div>
    </header>
    {% if page.highlighted %}
      <div class="highlighted">
        <aside class="layout-container section clearfix" role="complementary">
          {{ page.highlighted }}
        </aside>
      </div>
    {% endif %}
    {% if page.featured_top %}
      <div class="featured-top">
        <aside class="featured-top__inner section layout-container clearfix" role="complementary">
          {{ page.featured_top }}
        </aside>
      </div>
    {% endif %}
    <div id="main-wrapper" class="layout-main-wrapper layout-container clearfix">
      <div id="main" class="layout-main clearfix">
        {{ page.breadcrumb }}
        <main id="content" class="column main-content" role="main">
          <section class="section">
            <a id="main-content" tabindex="-1"></a>
            {{ page.content }}
          </section>
        </main>
        {% if page.sidebar_first %}
          <div id="sidebar-first" class="column sidebar">
            <aside class="section" role="complementary">
              {{ page.sidebar_first }}
            </aside>
          </div>
        {% endif %}
        {% if page.sidebar_second %}
          <div id="sidebar-second" class="column sidebar">
            <aside class="section" role="complementary">
              {{ page.sidebar_second }}
            </aside>
          </div>
        {% endif %}
      </div>
    </div>
    {% if page.featured_bottom_first or page.featured_bottom_second or page.featured_bottom_third %}
      <div class="featured-bottom">
        <aside class="layout-container clearfix" role="complementary">
          {{ page.featured_bottom_first }}
          {{ page.featured_bottom_second }}
          {{ page.featured_bottom_third }}
        </aside>
      </div>
    {% endif %}
    <footer class="site-footer">
      <div class="layout-container">
        {% if page.footer_first or page.footer_second or page.footer_third or page.footer_fourth %}
          <div class="site-footer__top clearfix">
            {{ page.footer_first }}
            {{ page.footer_second }}
            {{ page.footer_third }}
            {{ page.footer_fourth }}
          </div>
        {% endif %}
        {% if page.footer_fifth %}
          <div class="site-footer__bottom">
            {{ page.footer_fifth }}
          </div>
        {% endif %}
      </div>
    </footer>
  </div>
</div>

我们可以从上面的代码中很容易的找到上一节中所展示的17个区域。通过修改(覆写)page模板,并调整相应的CSS,就可以改动上一节图中所示的页面布局。

大家最熟悉的Bootstrap将页面分成了12栏,并给出了一整套基于网格的排版方案,详情请参考boostrap官网文档。因此,我们只要把上述page模板中的class替换成bootstrap为我们提供的class,并引入bootstrap提供的库文件(见《基础篇19. 禁用区块,创建库》),就能让页面实现bootstrap那样的网格式排版了。

以上这个工作其实也早就有人帮我们想到了,因此,同行们创建了bootstrap主题,并贡献到了Drupal官网。当然,它并不是唯一使用bootstrap框架的主题,还有以下这些都基于bootstrap,英文说明我就不翻译了,没什么意义,因为具体的区别还是需要你亲自去尝试的:

  1. Tweme — Ultra lightweight Bootstrap theme for Drupal powered by official Bootstrap base theme. Built-in footer sitemap, sharing buttons and Atoms support.
  2. Beginning - A theme that is for design artist. Designers can begin theme designing, web designing without worrying about theme structure, layout and responsiveness as per device width.
  3. Dawn - Inspired from Beginning. Using only Bootstrap Grids and Responsive Tools for css base (no bootstrap JS). Striving to be un-opinioned but responsive ready!
  4. Kalatheme - This theme helps you if you know Panels very well as its combination of Panopoly and Bootstrap.
  5. Bootstrap Business
  6. Bootstrap Barrio - a sub-theme of bootstrap, integrated with Skinr, customizable widths, 1, 2 or 3 columns.
  7. Radix + Radix Layouts - Radix is a base theme for Drupal. It has Bootstrap, Sass, Gulp, Bower, BrowserSync and Font Awesome built-in.
  8. Bootstrap lite - Bootstrap lite - Backport of Backdrop bootstrap based theme.

以下是bootstrap主题中的page模板文件:

{#
/**
 * @file
 * Default theme implementation to display a single page.
 *
 * The doctype, html, head and body tags are not in this template. Instead they
 * can be found in the html.html.twig template in this directory.
 *
 * Available variables:
 *
 * General utility variables:
 * - base_path: The base URL path of the Drupal installation. Will usually be
 *   "/" unless you have installed Drupal in a sub-directory.
 * - is_front: A flag indicating if the current page is the front page.
 * - logged_in: A flag indicating if the user is registered and signed in.
 * - is_admin: A flag indicating if the user has permission to access
 *   administration pages.
 *
 * Site identity:
 * - front_page: The URL of the front page. Use this instead of base_path when
 *   linking to the front page. This includes the language domain or prefix.
 *
 * Page content (in order of occurrence in the default page.html.twig):
 * - title_prefix: Additional output populated by modules, intended to be
 *   displayed in front of the main title tag that appears in the template.
 * - title: The page title, for use in the actual content.
 * - title_suffix: Additional output populated by modules, intended to be
 *   displayed after the main title tag that appears in the template.
 * - messages: Status and error messages. Should be displayed prominently.
 * - tabs: Tabs linking to any sub-pages beneath the current page (e.g., the
 *   view and edit tabs when displaying a node).
 * - node: Fully loaded node, if there is an automatically-loaded node
 *   associated with the page and the node ID is the second argument in the
 *   page's path (e.g. node/12345 and node/12345/revisions, but not
 *   comment/reply/12345).
 *
 * Regions:
 * - page.header: Items for the header region.
 * - page.navigation: Items for the navigation region.
 * - page.navigation_collapsible: Items for the navigation (collapsible) region.
 * - page.highlighted: Items for the highlighted content region.
 * - page.help: Dynamic help text, mostly for admin pages.
 * - page.content: The main content of the current page.
 * - page.sidebar_first: Items for the first sidebar.
 * - page.sidebar_second: Items for the second sidebar.
 * - page.footer: Items for the footer region.
 *
 * @ingroup templates
 *
 * @see template_preprocess_page()
 * @see html.html.twig
 */
#}
{% set container = theme.settings.fluid_container ? 'container-fluid' : 'container' %}
{# Navbar #}
{% if page.navigation or page.navigation_collapsible %}
  {% block navbar %}
    {%
      set navbar_classes = [
        'navbar',
        theme.settings.navbar_inverse ? 'navbar-inverse' : 'navbar-default',
        theme.settings.navbar_position ? 'navbar-' ~ theme.settings.navbar_position|clean_class : container,
      ]
    %}
    <header{{ navbar_attributes.addClass(navbar_classes) }} id="navbar" role="banner">
      {% if not navbar_attributes.hasClass(container) %}
        <div class="{{ container }}">
      {% endif %}
      <div class="navbar-header">
        {{ page.navigation }}
        {# .btn-navbar is used as the toggle for collapsed navbar content #}
        {% if page.navigation_collapsible %}
          <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse">
            <span class="sr-only">{{ 'Toggle navigation'|t }}</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
        {% endif %}
      </div>

      {# Navigation (collapsible) #}
      {% if page.navigation_collapsible %}
        <div id="navbar-collapse" class="navbar-collapse collapse">
          {{ page.navigation_collapsible }}
        </div>
      {% endif %}
      {% if not navbar_attributes.hasClass(container) %}
        </div>
      {% endif %}
    </header>
  {% endblock %}
{% endif %}

{# Main #}
{% block main %}
  <div role="main" class="main-container {{ container }} js-quickedit-main-content">
    <div class="row">

      {# Header #}
      {% if page.header %}
        {% block header %}
          <div class="col-sm-12" role="heading">
            {{ page.header }}
          </div>
        {% endblock %}
      {% endif %}

      {# Sidebar First #}
      {% if page.sidebar_first %}
        {% block sidebar_first %}
          <aside class="col-sm-3" role="complementary">
            {{ page.sidebar_first }}
          </aside>
        {% endblock %}
      {% endif %}

      {# Content #}
      {%
        set content_classes = [
          page.sidebar_first and page.sidebar_second ? 'col-sm-6',
          page.sidebar_first and page.sidebar_second is empty ? 'col-sm-9',
          page.sidebar_second and page.sidebar_first is empty ? 'col-sm-9',
          page.sidebar_first is empty and page.sidebar_second is empty ? 'col-sm-12'
        ]
      %}
      <section{{ content_attributes.addClass(content_classes) }}>

        {# Highlighted #}
        {% if page.highlighted %}
          {% block highlighted %}
            <div class="highlighted">{{ page.highlighted }}</div>
          {% endblock %}
        {% endif %}

        {# Help #}
        {% if page.help %}
          {% block help %}
            {{ page.help }}
          {% endblock %}
        {% endif %}

        {# Content #}
        {% block content %}
          <a id="main-content"></a>
          {{ page.content }}
        {% endblock %}
      </section>

      {# Sidebar Second #}
      {% if page.sidebar_second %}
        {% block sidebar_second %}
          <aside class="col-sm-3" role="complementary">
            {{ page.sidebar_second }}
          </aside>
        {% endblock %}
      {% endif %}
    </div>
  </div>
{% endblock %}

{% if page.footer %}
  {% block footer %}
    <footer class="footer {{ container }}" role="contentinfo">
      {{ page.footer }}
    </footer>
  {% endblock %}
{% endif %}

对比上面的两个模板文件,你可以看到一些相同之处,也可以发现,两个主题所定义的区域是不同的,并且,bootstrap主题使用了很多自己提供的class名,如col-sm-3。

整个第一章的核心内容,以及第二章的大部分内容都是在讲述如何通过模板文件来制作页面(搭配CSS和JS)——也就是排版,包括组件化主题开发的相关内容,其本质依然是通过模板来排版,只不过模板文件通过include语句被转移到了Patternlab中。

在从Drupal7到Drupal8的转变中,Twig模板引擎取代了PHPTemplate引擎,与此同时,页面上有更多的元素是以模板的形式渲染出来的,这两个变化都是为了让主题开发者能够更容易的修改和控制模板,从而更好的控制页面上的各个元素的样式,以及元素之间的相对位置和关系,换句话说,排版也就变得更加容易了。

本书共100小节:

评论 (写第一个评论)