[翻译]Drupal 8 模块里面添加 css 和 js

原文链接:https://www.drupal.org/node/2274843

这篇文档是专门针对Drupal模块的。如果要看针对主题的,看添加css和js到Drupal 8 主题。

在Drupal 8里面,css和js是通过相同的系统加载到模块或者主题里面的,都是通过资源库(asset libraries)的方式。资源库能够包含一个或者多个的css资源,一个或者多个js资源,一个或者多个js配置。

Drupal 使用一个高层次原则:资源(css 或者 js)只有在你告诉drupal你需要加载它们的时候它们才会被加载进来。Drupal 不会再所有的页面都加载所有的资源(css/js),因为这样不利于前端的性能优化。

和Drupal 7相比的不同之处

对于开发者来说,和drupal 7相比,有一个很重要的区别:

  1. 只有javascript是在特别的页面是必须的时候,js才会被加载进去。特别是,默认的情况下,匿名用户看到的大多数页面的js都不是必须的。这也意味着jQuery不会被自动加载到所有的页面。

所以,如果你的主题需要jQuery或者其他的javascript(在资源库里面已经定义的)。你要告诉Drupal在这种情况下,需要在所需的资源库里面通过声明依赖的方式加载它们。

过程

加载资源库(css/js)通常的步骤:

  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: {}

你可能注意到了指定css的“theme”关键词并没有添加给js,这指定css文件所属的样式类型。

你能够通过5种不同级别的类型设置css的权重。

  • base 关键词赋值权重 CSS_BASE = -200

  • layout 关键词赋值权重 CSS_LAYOUT = -100

  • component 关键词赋值权重 CSS_COMPONENT = 0

  • state 关键词赋值权重 CSS_STATE = 100

  • theme 关键词复制权重 CSS_THEME = 200

这个是根据SMASS标准定义的。所以如果你指定了theme关键词,意味着这个CSS文件包含了显示外观和感觉的theme相关的样式。获取更多资料。你不能够使用其他的关键字,否则的话会导致严格警告信息。

这个例子假设你的js文件 cuddle-slider.js位于你的模块子目录js目录下面。你同样可以引入一个外部的js文件,css文件或者其他的可能。参考CDN / externally hosted libraries获取更多信息。

记住,drupal 8不再在所有的页面都默认加载jQuery,只在需要的地方加载。因此,我们需要声明模块的 cuddle-slider 库的时候声明包含jQuery的依赖。既不是模块也不是主题会提供jQuery,而是drupal的核心:core/jquery 是我们想要声明的依赖。(一个扩展名,后面跟着一个斜杠,在后面跟着库名,所以如果其他的库想要依赖cuddly-slider库的话,就必须声明依赖 fluffiness/cuddly-slider,因为 fluffiness是我们的主题名称)

所以,确保js/cuddly-slider可使用jQuery,我们需要更新以上的代码:

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

正如你期待的,css和js的资源列表里面的顺序就是他们在页面加载的顺序。

把库附加到页面

取决于你需要加载哪些资源,你可以使用不同的方式附加对应的资源库。毕竟,一些资源库在所有的页面需要被加载,而有一些库只有很少页面被加载,而有一些在大多数页面被加载,但是并不是所有页面。

最重要的是drupal8不会通过当前是在哪个页面而加载library,而是通过当前页面的可见元素来加载,比如一个页面包含类似于以下的元素

'#type' => 'table', a '#type' => 'dropbutton' and a '#type' => 'foobar'

那么drupal将会加载跟这些types相关的libraries。

但是不仅仅局限于#type:可能我们想要为某一些#type实例加载某一些资源库,这种情况下,我们只需要把libraries添加到这个实例的返回数组里面去就行了。

当然,在很少情况下,我们有一个合理的理由需要在每一个页面都加载libraries,而不管页面上的东西,例如分析一些页面加载的分析代码。

以下是一些例子来说明如何做到这些。

通过当前存在的#type加载library,针对所有实例的话,我们使用hook_element_info_alter()

function fluffy_element_info_alter(array &$types) {
  if (isset($types['table'])) {    $types['table']['#attached']['library'][] = 'fluffiness/fancy_table';
  }
}

然后清除缓存以便drupal能够意识到这个新的钩子实现。

通过返回数组的方式加载library。

$build['the_element_that_needs_the_asset_library']['#attached']['library'][] = 'fluffiness/fancy_element';

附加到区块插件的返回数组中。

这个是通过返回数组的方式加载library的另外一个例子。

return [      '#theme' => 'your_module_theme_id',      '#someVariable' => $some_variable,      '#attached' => array(        'library' => array(          'your_module/library_name',
        ),
      ),
    ];

在所有页面加载library。

function contextual_page_attachments(array &$attachments) {
  if (!\Drupal::currentUser()->hasPermission('access contextual links')) {
    return;
  }  $attachments['#attached']['library'][] = 'contextual/drupal.contextual-links';
}

在preprocess function里面加载library。

function fluffy_preprocess_maintenance_page(&$variables) {
  $variables['#attached']['library'][] =  'fluffy/cuddly-slider';
}

在twig模板加载library

{{ attach_library('contextual/drupal.contextual-links') }}<div>Some markup {{ message }}</div>

加载可配置的javascript。

在某一些情况下,你可能需要基于php的一些计算来添加js代码。

这种情况下,先按照以前的方式定义library,但是同时需要加载Javasript Settings,并且,让这个javascript文件通过drupalSettings(继承drupal7 里面的Drupal.settings)。同时需要加载jQuery,我们需要声明一些依赖来做到这些。

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

并且在模块的hook_page_attachments_alter(&$build)函数里面,

$build['#attached']['library'][] = 'fluffiness/cuddly-slider';
$build['#attached']['drupalSettings']['fluffiness']['cuddlySlider']['foo'] = 'bar';

最后cuddly-slider.js这个文件就可以通过以下方式得到bar。

drupalSettings.fluffiness.cuddlySlider.foo (and it will === 'bar').