82. 资源解析器AssetResolver

在阅读本主题前,请先阅读本系的《资源库assets library详解》以详细了解资源库相关知识

 

用于解析页面附加的资源库和渲染数组中的js设置(drupalSettings),执行入口:

\Drupal\Core\Render\HtmlResponseAttachmentsProcessor::processAssetLibraries

 

附加资源集:

在系统中用附加资源集对象代表一个页面的资源,这也是资源解析器要操作的对象,类如下:

\Drupal\Core\Asset\AttachedAssets

该对象保存三大内容:

资源库:$render_array['#attached']['library']

js设置:$render_array['#attached']['drupalSettings']

已经被前端页面加载的库:比如在ajax时,已经加载的库不会再次加载

 

在整个请求流程中附加的资源都汇集到这里,在派发响应事件时,从响应对象中得到资源信息后保存到该对象,然后传递给资源解析器以得到相关占位符的内容

 

资源解析器:

服务名:asset.resolver

类:Drupal\Core\Asset\AssetResolver

该服务使用缓存后端cache.data来缓存相关数据;主要方法见下。

 

获取css集:

一个页面的所有css以如下cid被缓存:

$cid = 'css:活动主题名:' . Crypt::hashBase64(serialize($libraries_to_load)) . (int)是否优化;

缓存标签为:['library_info'],缓存内容是一个按照加载顺序排好序的数组,这就是我们需要的css集数组,如果配置关闭了优化,那么键名是css绝对路径(有三种类型:外部路径含协议相对URI、流路径、或相对于系统根目录的没有“/”前缀的路径),否则是数字索引,键值是一个选项数组,她代表需要加载的一个css资源文件,选项数组是一个css资源的内部表示,她来自库定义时的属性数组,会进行如下一系列处理:

 

首先进行默认附加,以补充缺省值,一个css资源会被附加以下默认选项及值:

    $default_options = [
      'type' => 'file',
      'group' => CSS_AGGREGATE_DEFAULT,
      'weight' => 0,
      'media' => 'all',
      'preprocess' => TRUE,
      'browsers' => ['IE' => TRUE,'!IE' => TRUE,],
    ];

 

然后校正preprocessweight的值,并新增data键以保存文件绝对路径(格式如上所述)

以资源的绝对路径(如上所述)作为键名,选项数组做键值,形成按照依赖关系排序的css集数组

主题或模块可以定义修改钩子'css'去修改这个css集数组,函数如下:

Hook_css_alter(&$css, $assets);

第一个参数$css应该以引用接收

第二个参数是附加资源集对象\Drupal\Core\Asset\AttachedAssets,不应该修改她,用于辅助判断,里面还有js及其设置等资源

调用该修改钩后再次执行排序(之前资源已经是依据依赖关系排过序,这可对没有依赖关系的资源排序,但这也可以更改依赖关系形成的排序),css资源的排序先以组group进行,其值是一个整数,再以权重weight进行(此时weight已是SMACSS分类权重和定义权重相加的结果),数值越高越后加载,在前端优先级越高,系统指定的默认组是主题定义时为100(常量CSS_AGGREGATE_THEME),模块为0(常量CSS_AGGREGATE_DEFAULT),我们可以在前文提到的“css”修改钩中修改她。

继续执行主题info文件中定义的表单移除,

如果启用了css聚合优化,那么将采用\Drupal::service('asset.css.collection_optimizer')->optimize($css);进行优化,见本系列《资源cssjs优化处理》主题。

最后返回一个选项数组构成的css集数组,每个选项数组代表需要加载的一个css文件,她们已经按照页面加载顺序排序

 

注意:在没有启用聚合优化时,页面css集中选项数组有以下键名:

typegroupweightmediapreprocessbrowsersdata

如果启用了那么键名weight将被去掉(后续不要依赖该键名,集已排好序,不再需要她),如果资源是被聚合过的,那么会增加键名:preprocessed,值为true.

 

从资源解析器中获得的css集在系统后续流程中将继续传递给css集渲染器。

 

获取js集:

css一样js数据同样设置有缓存加速,缓存id为:

$cid = 'js:活动主题名:语言id:' . Crypt::hashBase64(serialize($libraries_to_load)) . (int) (count($assets->getSettings()) > 0) . (int) 是否优化;

css文件全部在html头部加载,和css不同的是js可以在头部和尾部加载,如果库设置的header选项为真,那么该库及其依赖的库中js全部在页头加载,js的选项会被附加以下默认值:

      $default_options = [
        'type' => 'file',
        'group' => JS_DEFAULT,
        'weight' => 0,
        'cache' => TRUE,
        'preprocess' => TRUE,
        'attributes' => [],
        'version' => NULL,
        'browsers' => [],
      ];

 

css一样,在内部用选项数组代表一个js资源,在该数组中用“scope”指示该资源在html头部还是尾部加载

如果js资源不能缓存或设置了标签属性,那么将不进行聚合

模块和主题可以通过“js”修改钩子去修改加载的js资源:

Hook_js_alter(&$js, $assets);

参数$js是以依赖顺序排序的js资源数组,键名是全路径(同css),键值是选项数组,参数$assetscss

执行完修改钩子后再按照weight的值去调整加载顺序,同css,该值优先级高于依赖关系

排序完后,和css一样进行js文件聚合

系统提供了一个很特殊的库“core/drupalSettings”用以处理js设置数据,该数据有两种方式指定:

1、在库定义中以“drupalSettings”选项指定

2、在渲染数组中以“$render_array['#attached']['drupalSettings']”指定

js设置数据处理流程如下:

库定义的设置数据按照依赖顺序依次合并,采用如下合并方法:

\Drupal\Component\Utility\NestedArray::mergeDeepArray

执行模块钩子修改合并结果,函数如下:

hook_js_settings_build(&$settings, $assets);

注意:这里的设置参数并不包括渲染数组中的设置值,返回后它将采用同样的合并方法与渲染数组的设置值合并,渲染数组设置的设置值优先级最高,可以覆写上述数据,合并后派发修改钩子“js_settings”:

hook_js_settings_alter(array &$settings, \Drupal\Core\Asset\AttachedAssetsInterface $assets)

如果库“core/drupalSettings”在头部加载那么设置数据将在任何js加载前加载,如果在尾部加载,那么设置数据在任何尾部js加载前加载。

 

库发现:

这是一个服务,用于得到一个扩展定义的所有资源库,可通过扩展名得到其下所有资源库定义,也可通过扩展名和库名得到具体的一个资源库定义,这些定义已经被应用了覆写和继承,同时提供清除缓存功能,库发现服务定义如下:

服务名:library.discovery

类:Drupal\Core\Asset\LibraryDiscovery

该服务本质上只提供了静态缓存功能,实际工作是在库发现收集器中进行的

 

库发现收集器library.discovery.collector

服务:library.discovery.collector

类:Drupal\Core\Asset\LibraryDiscoveryCollector

该服务继承了缓存收集器CacheCollector\Drupal\Core\Cache\CacheCollector),缓存收集器用于有巨量数据需要缓存的情形下,如果每次请求都全部加载,性能是很糟糕的,她相当于一个过滤器,只加载被真实使用过的数据,但并不会丢失任何数据,这在运行时主题注册表(\Drupal\Core\Utility\ThemeRegistry)中也被用到。

 

在库发现收集器中,库定义数据被缓存在cache.discovery中,cid是“library_info:+当前活动主题名,缓存标签:['library_info'],数据是以扩展为单位储存的,也就是说该缓存数据是以扩展名为键名的一个数组,最初这个数组是空的,在系统运行过程中逐步加入扩展定义的库数据,只有在扩展定义的库被用到后,才会将该扩展加入此缓存以备再用,这就是上述缓存收集器的作用,要得到某个扩展定义的全部库可以这样:

\Drupal::service("library.discovery.collector")->get("bartik");

该代码一旦执行,对应扩展的库定义数据将被缓存且以后每次请求都会被加载,因此不要随意使用该代码,你也可以使用以下方法清理缓存:

\Drupal::service("library.discovery")->clearCachedDefinitions(); 

//持久缓存连同本次运行时的静态缓存一起清理

 

\Drupal::service("library.discovery.collector")->clear(); 

//和前一方法相比只清理持久缓存

 

在库发现收集器中处理了库继承和一部分库覆写,更多库操作在库发现解析器中进行。

 

 

库发现解析器library.discovery.parser

用于得到单个扩展(核心、模块、主题)定义的全部库信息,这些库没有处理库继承,进行了部分库覆写

服务idlibrary.discovery.parser

类:Drupal\Core\Asset\LibraryDiscoveryParser

调用入口:

\Drupal::service("library.discovery.parser")->buildByExtension($extension);

该服务执行主要流程是:

读取扩展定义的库文件

如果扩展实现了动态定义(也就是实现了钩子:library_info_build,函数名:moduleName_library_info_build())那么将文件定义和动态定义合并

执行模块和主题的“library_info”修改钩子:hook_library_info_alter(&$libraries, $extension)去调整库定义

将以上步骤得到的库定义运用主题定义的覆写

补充各选项默认值,转化资源路径为绝对路径

 

注意该服务没有运用任何缓存机制,针对整个库的覆写尚没有完成替换或删除,也没有完成库继承,这些在库发现收集器中完成:"library.discovery.collector"

 

 

库依赖解析器library.dependency_resolver

服务名:library.dependency_resolver

类:Drupal\Core\Asset\LibraryDependencyResolver

该服务的代码量不多,但非常精炼,这里解释一下采用的算法:

doGetDependencies(array $libraries_with_unresolved_dependencies, array $final_libraries = [])

该方法返回传入的所有库和她们的所有依赖,并已经排好序,可被直接使用。

首先我们需要明白被依赖的库应该先加载,在库数组中排在前面,库以“扩展名/库名”的格式出现;库的依赖结构可以视为一张有向无环图,(参考数学专业的《图论》或计算机科学与技术专业的《数据结构》),依赖不能出现循环,否则就是有环图了,该方法采用深度优先遍历算法,能够保证返回的数组中被依赖的库一定出现在依赖她的库的前面,且每一个库只出现一次,该方法第一个参数应该是以加载顺序排好序的库数组,先加载的排在前面,如果传入的数组各元素之间本身存在依赖关系,那么将忽略传入的顺序以保证被依赖的库在前面。

 

getMinimalRepresentativeSubset(array $libraries)

该方法用于返回一个库集的最小表示,换句话说就是在这个库聚合中,如果一个库被集合中的其他库依赖,那么被依赖的这个库将被去掉,举个例子,假设传入的库集合为:

['core/a', 'core/b', 'core/c']

其中core/c依赖core/a,那么经过该方法处理后,将返回:

['core/b', 'core/c']

由该方法得到的最小表示是最紧凑的,她们的依赖可以由getLibrariesWithDependencies(array $libraries)方法合并进来

 

资源解析相关设置:

是否聚合cssjs文件,在系统后台该处设置:

/admin/config/development/performance

从配置系统得到该信息:

\Drupal::config('system.performance')->get('css.preprocess');

\Drupal::config('system.performance')->get('js.preprocess');

也可以在配置文件中设置以下值强行覆写:

$config['system.performance']['css']['preprocess'] = FALSE;

$config['system.performance']['js']['preprocess'] = FALSE;

本书共94小节:

评论 (写第一个评论)