跳转到主要内容
sina_1984的风 提交于 14 July 2015

原文链接:https://www.drupal.org/theme-guide/8/assets

在Drupal8中,CSS和JS通过加载模块和主题的系统加载,组件库(asset libraries)。

Drupal用一个高级的原则:CSS/JS 只有在你告知Drupal加载时才被加载。Drupal不会在所有页面加载全部的CSS/JS,因为这样不利于前端性能。

与Drupal 7的不同

主要4点:

  1. 不再有THEME.info 文件,但是有包含相同内容的THEME.info.yml 文件
  2. Stylessheets属性不再存在于THEME.info.yml文件,因为现在只使用库了(stylesheets-override 和 stylesheets-remove 依然存在并和原来一样工作)
  3. Scripts 属性也不存在于THEME.info.yml文件,因为现在只使用库了
  4. 只有页面需要的JS会被加入到该页面。 因为,Drupal默认不需要给不登陆的用户提供大部分JS。这意味着jQuery不会自动被所有页面加载
  5. jQuery或其他JS(已经在组件库中定义了),你需要用“在需要的组件库声明需求”的方式告诉Drupal。

过程

通常步骤:

  1. 保存CSS/JS文件
  2. 定义一个包含CSS和JS文件的库
  3. 将这个库附到钩子函数中一个呈递的数组中

但是,在主题中,第三部有另一个方法,主题可以选择加载任意数量的组件库在所有的页面中

定义库

要定义一个库,往主题文件夹中加入 *.libraries.yml 文件。(如果你的主题名是fluffiness, 那么文件名应该是fluffiness.libraries.yml)每个库都是包含CSS/JS详细信息的一个目录,例如:

cuddly-slider:
  version: 1.x
  css:
    theme:
      css/cuddly-slider.css: {}
  js:
    js/cuddly-slider.js: {}

这个例子中,假设JS (cuddly-slider.js)是位于你主题的js子文件夹中。你也可以从外部链接引入JS, CSS 和其他可能。详细信息在https://www.drupal.org/node/2274843 或 https://www.drupal.org/node/2201089 

但 是,记住Drupal8不再默认对所有页面加载jQuery,只是在需要的时候加载。 因此,我们必须声明我们主题的cuddly-slider库声明了对包含jQuery的库的需求。这不是一个模块或主题提供jQuery,而是 Drupal的核心core/jQuery是我们需要声明的需求。(这是一个跟着/和库名的扩展命名,所以,如果有其他库想依赖我们cuddly- slider这个库,它必须要声明对fluffiness/cuddly-slider的需求。因为fluffiness是我们的主题名)

所以,要确保jQuery在js/cuddly-slider.js中可用,我们需要更新代码:

cuddly-slider:
  version: 1.x
  css:
    theme:
      css/cuddly-slider.css: {}
  js:
    js/cuddly-slider.js: {}
  dependencies:
    - core/jquery

当然,一个库可以只包含CSS组件,或JS组件。绝大多数主题都会想要一个global-styling组件库,为了需要被全范围加载的CSS (比如,应用主题的所有页 – 通常是整个站点,出了管理员页面):

global-styling:
  version: 1.x
  css:
    theme:
      css/layout.css: {}
      css/style.css: {}
      css/colors.css: {}
      css/print.css: { media: print }

当然,如你所期望,CSS在这里的顺序也是它们被加载的顺序

在Drupal 7中,你需要指出媒体属性(screen,print,all)作为stylesheets属性的子键值,现在,对任意CSS组件,你可以把它定义成一个值,例如:

      css/print.css: { media: print }

给页面附加库

根据你需要加载的组件,你会想用不同的方式附加相关的组件库。毕竟,一些组件库所有页面都需要,一些只被少数需要,还有一些被大多数但不是全部需要。

附加到所有页面

要为所有使用你主题的页面附加库,需要在你主题的*.info.yml文件中声明它,在libraires键值的下面:

name: Example
type: theme
core: 8.x
libraries:
  - fluffiness/cuddly-slider

你想列多少库就可以列多少库,它们都会在每一页被加载。

然后,清除缓存让Drupal加载新信息

为一个子集的页面附加库

在一些情况下,你不需要你的库被所有页面加载,而是只需要一部分(子集)页面加载。例如,你可以只需要你的库在某个特定区块(block)出现时或者某个特定节点类型显示时被激活。

一个主题可以通过在.theme文件中置入THEME_preprocess_HOOK()函数实现。需要用你的主题的机器名替换THEME和主题的钩子函数的机器名替换HOOK。

例如,如果你想给维护页附加JS,钩子部分就是maintenance_page,你的函数应该是这样:

<?php
function fluffiness_preprocess_maintenance_page(&$variables) {
  $variables['#attached']['library'][] = 'fluffiness/cuddly-slider';
}
?>

你可以对其他主题的钩子做类似的事情,当然,你的函数也可以包含逻辑部分,例如,检测哪个区块被区块钩子预处理了,哪个节点类型被节点类型预处理了,等等。

在twig模板中附加库

你可以在twig模板中附加一个库,使用的是attach_library() 这个twig函数。

所以,在任何*.html.twig中:

{{ attach_library('fluffiness/cuddly-slider') }}
<div>Some fluffy markup {{ message }}</div>

附加可设置的JS

在某些情况下,你可能希望根据一些PHP的计算信息来附加JS到页面上。

这 时,建立一个JS文件,像以前一样定义并附加一个库,但是也要附上JS设置并通过drupalSettings(Drupal 7的Drupal.settings的继承者)让JS读取这些设置。不过,要想让drupalSettings对我们的JS可用,我们需要做让 jQuery可用的同样工作:我们需要声明需求。

所以就变成了:

cuddly-slider:
  version: 1.x
  js:
    js/cuddly-slider.js: {}
  dependencies:
    - core/jquery
    - core/drupalSettings

<?php
function fluffiness_page_attachments_alter(&$page) {
  $page['#attached']['library'][] = 'fluffiness/cuddly-slider';
  $page['#attached']['drupalSettings']['fluffiness']['cuddlySlider']['foo'] = 'bar';
}
?>

“bar”是一些计算的值。

这样,cuddl-slider.js就可以访问drupalSettings.fluffiness.cuddlySlider.foo了,并===’bar’

内置JS

不鼓励内置JS,建议把需要内置的JS存到一个文件里,因为这样可以在客户端缓存JS,也可以审查JS。

内置生成附加物的JS

例如广告,社交媒体分享按钮,社交媒体插件。这些会用到内置JS。但是他们只是一类特定的内容/附加物,因为它们不装饰站点内容或让内容产生交互,而是用过JS从外部输入内容。

你需要这些或在一个自定义的区块中,或直接在Twig模板中。

例如:

<script type="text/javascript"><!--
ad_client_id = "some identifier"
ad_width = 160;
ad_height = 90;
//--></script>
<script type="text/javascript" src="http://adserver.com/ad.js"></script>

<a class="twitter-timeline" href="https://twitter.com/wimleers" data-widget-id="307116909013368833">Tweets by @wimleers</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>

影响整个页面的内置JS

例如: 分析插件,和提供的字体服务。影响整个页面的内置JS可以分为两种,前端设计或逻辑性的。

前端设计的情况下,JS属于主题。把JS直接放置到html.html.twig文件中。在字体的情况下,也允许将KS放置到对的地方会给你最好的终端用户体验,因为它允许你防止字体加载中的FOUT(未设计字体的闪现)

(更多内容在此:“Async Typekit & the Micro-FOUT”

其他情况,JS属于模块,请看“Adding stylesheets (CSS) and JavaScript (JS) to a Drupal 8 module”

Drupal 7的不同

  • 在Drupal 7中,库需要用hook_library_info()定义,这被*.libraries.yml文件取代。
  • 在Drupal 8中,drupal_add_css(), drupal_add_js(), 和drupal_add_library()被去除以支持#attached

更多信息:


以下是英文原文,可对照阅读

Last updated May 29, 2015. Created on March 12, 2014. Edited by almaudoh, mpgeek, TR, Sutharsan. You can edit this page, too.

In Drupal 8, stylesheets (CSS) and JavaScript (JS) are loaded through the same system for modules (code) and themes, for everything: asset libraries.

Drupal uses a high-level principle: assets (CSS or JS) are still only loaded if you tell Drupal it should load them. Drupal does not load all assets (CSS/JS) on all pages, because this is bad for front-end performance.

Differences compares to Drupal 7

There are four important differences compared to Drupal 7 for themers:

  1. We no longer have a file, but a file, but it still contains the same data.
  2. The property (for adding CSS assets) in files no longer exists, because we use only libraries now ( and still exist, and still work in exactly the same way).
  3. The property (for adding JS assets) in files no longer exists, because we use only libraries now.
  4. Only the JavaScript required on a particular page will be added to that page. In particular, by default Drupal doesn't need JavaScript on most pages that anonymous users can see. This means that jQuery is not automatically loaded on all pages anymore. So, if your theme requires jQuery or some other JavaScript to be present (which also is defined in an asset library), you need to tell Drupal that this is the case, by declaring a dependency on the needed asset library.

The process

The general steps for loading assets (CSS/JS) are:

  1. Save the CSS or JS to a file.
  2. Define a "library", which can contain both CSS and JS files.
  3. "Attach" the library to a render array in a hook.

But in the case of themes, there is an alternative to step 3: themes can choose to load any number of asset libraries on all pages.

Defining a library

To define one or more (asset) libraries, add a file to your theme folder. (If your theme is named , then the file name should be ). Each "library" in the file is an entry detailing CSS and JS files (assets), like this:

cuddly-slider:
  version: 1.x
  css:
    theme:
      css/cuddly-slider.css: {}
  js:
    js/cuddly-slider.js: {}

This example assumes that the actual JavaScript is located in the subfolder of your theme. You can also have the JS come from an external URL, include CSS files, and there are other possibilities. See https://www.drupal.org/node/2274843 or https://www.drupal.org/node/2201089 for details.

However, remember that Drupal 8 no longer loads jQuery on all pages by default; Drupal 8 only loads what's necessary. Therefore, we must declare that our theme's library declares a dependency on the library that contains jQuery. It is neither a module nor a theme that provides jQuery, it's Drupal core: is the dependency we want to declare. (This is an extension name followed by a slash, followed by the library name, so if some other library wanted to depend on our library, it'd have to declare a dependency on , because is the name of of our theme.)

So, to ensure jQuery is available for , we update the above to:

cuddly-slider:
  version: 1.x
  css:
    theme:
      css/cuddly-slider.css: {}
  js:
    js/cuddly-slider.js: {}
  dependencies:
    - core/jquery

Of course, a library could just as well only contain CSS assets, or only contain JS assets. Most themes will probably want to have a asset library, for the stylesheets (CSS assets) that it wants to be loaded globally (i.e. on all pages where the theme is active — usually the whole site except the administration pages):

global-styling:
  version: 1.x
  css:
    theme:
      css/layout.css: {}
      css/style.css: {}
      css/colors.css: {}
      css/print.css: { media: print }

Of course, as you'd expect, the order the CSS assets are listed here is also the order in which they will be loaded.

In Drupal 7, you used to be able to specify the media property (, , ) as a subkey of the property, now you can define it as a value for any given CSS asset. E.g.:

      css/print.css: { media: print }

Attaching a library to page(s)

Depending on which assets you need to have loaded, you'll want to attach the corresponding asset library in a different way. After all, some asset libraries are needed on all pages, others only very rarely, and yet others on most, but not quite all.

The sub-sections here show examples of how to do this.

Attaching to all pages

To attach a library to all the pages on the site that use your theme, declare it in your theme's file, under the key:

name: Example
type: theme
core: 8.x
libraries:
  - fluffiness/cuddly-slider

You can list as many libraries as you want, all of them will be loaded on every page.

Then clear the cache so that the new information is loaded into Drupal.

Attaching a library to a subset of pages

In some cases, you do not need your library to be active for all pages, but just a subset of pages. For example, you might need your library to be active only when a certain block is being shown, or when a certain node type is being displayed.

A theme can make this happen by implementing a function in the file, replacing "THEME" with the machine name of your theme and "HOOK" by the machine name of the theme hook.

For instance, if you want to attach JavaScript to the maintenance page, the "HOOK" part is "maintenance_page", and your function would look like this:

<?php
function fluffiness_preprocess_maintenance_page(&$variables) {
  $variables['#attached']['library'][] = 'fluffiness/cuddly-slider';
}
?>

You can do something similar for other theme hooks, and of course your function can have logic in it -- for instance to detect which block is being preprocessed in the "block" hook, which node type for the "node" hook, etc.

Attaching a library in a twig template

You can also attach a library in a twig template by using the twig function So in any *.html.twig:

{{ attach_library('fluffiness/cuddly-slider') }}
<div>Some fluffy markup {{ message }}</div>

Attaching configurable JavaScript

In some cases, you may want to add JavaScript to a page that depends on some computed PHP information.

In this case, create a JavaScript file, define and attach a library just like before, but also attach JavaScript settings and have that JavaScript file read those settings, via (the successor to Drupal 7's ). However, to make available to our JavaScript file, we have to do the same work as we had to do to make jQuery available: we have to declare a dependency on it.

So that then becomes:

cuddly-slider:
  version: 1.x
  js:
    js/cuddly-slider.js: {}
  dependencies:
    - core/jquery
    - core/drupalSettings

and

<?php
function fluffiness_page_attachments_alter(&$page) {
  $page['#attached']['library'][] = 'fluffiness/cuddly-slider';
  $page['#attached']['drupalSettings']['fluffiness']['cuddlySlider']['foo'] = 'bar';
}
?>

Where is some calculated value.

Then will be able to access (and it will ).

Inline JavaScript

Inline JavaScript is discouraged. It's recommended to put the JS you want to use inline in a file instead, because that allows that JavaScript to be cached on the client side. It also allows JavaScript code to be reviewed and linted.

Inline JavaScript that generates markup

Examples of this are ads, social media sharing buttons, social media listing widgets. These do use inline JavaScript. But they are just a special kind of content/markup, since they're not about decorating the site's content or making it interactive, instead they are about pulling in external content through JavaScript.

You want to put these in either a custom block or even directly in a Twig template.

E.g.:

<script type="text/javascript"><!--
ad_client_id = "some identifier"
ad_width = 160;
ad_height = 90;
//--></script>
<script type="text/javascript" src="http://adserver.com/ad.js"></script>
<a class="twitter-timeline" href="https://twitter.com/wimleers" data-widget-id="307116909013368833">Tweets by @wimleers</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>

Inline JavaScript that affects the entire page

Examples of inline JavaScript that affects the entire page are analytics (e.g. Google Analytics) and hosted font services. Inline JavaScript that affects the entire page can be in either of two categories: front-end/styling, or logical.

In the case of front-end/styling (e.g. hosted font services), the JS belongs in the theme. Put the JS directly in your file. In the case of fonts, this will also allow you to put it right in the place that gives you the best (and fastest) end user experience, because it allows you to prevent a FOUT (Flash Of Unstyled Text) while the font is still loading (fonts loaded through JS must be listed in the HTML before the CSS)! (Read more about this in the excellent article “Async Typekit & the Micro-FOUT” article.)

In the other case, it belongs in the module, and for that, please see “Adding stylesheets (CSS) and JavaScript (JS) to a Drupal 8 module”.

Differences with Drupal 7

More information