Problem/Motivation

Currently, the default aggregation JS output looks like this:

<script src="/sites/default/files/js/js_BKcMdIbOMdbTdLn9dkUq3KCJfIKKo2SvKoQ1AnB8D-g.js"></script>

<!--[if lte IE 9]>
<script src="/sites/default/files/js/js_VhqXmo4azheUjYC30rijnR_Dddo0WjWkF27k5gTL8S4.js"></script>
<![endif]-->
<script src="/sites/default/files/js/js_Cry_2ymPxsxXR0OfM2CVnTbE0pTKEfbYOqTRKtLi0V8.js"></script>

The IE9 specific classList library in the middle results in generating 3 files in total, so browsers have to load 2-3 different files. That's because it has the same weight as other libraries and ends up below/between them.

Proposed resolution

Change the weight to -25, so it is explicitly the first and before all other elements, the result is this:

<!--[if lte IE 9]>
<script src="/sites/default/files/js/js_VhqXmo4azheUjYC30rijnR_Dddo0WjWkF27k5gTL8S4.js"></script>
<![endif]-->
<script src="/sites/default/files/js/js_gp2hn88-lr0GHwFrbN4bRDVoLZa-QiAkYi478GYUreA.js"></script>

Now browsers by default only have to load 1-2 files, we also have to generate/track fewer files in the file system.

Remaining tasks

User interface changes

API changes

Data model changes

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Berdir created an issue. See original summary.

Berdir’s picture

Here's a patch, not sure if we can test this? Also not sure if we could change something about the aggregation to make this work by default? AFAIK we'll also stop supporting IE9 soon, so I suppose we can just remove that library then, or at least stop depending on it (for BC, if someone else uses it)

I was looking into this because I was optimizing aggregation on a site with multiple remote libraries, by default you get something like this:

aggregated-file
ie9-file
aggregated-file
remote-file
aggregated-file
remote-file
remote-file
aggregated-fle
..

In total, I had 10 JS script files. With this patch and explicitly setting the weight of all my external libraries to -100, I was able to change it to:

remote-file
remote-file
remote-file
ie9-file
aggregated-fle

And only 6 JS script files (including IE9), so I'm saving 4 requests. I'm wondering if *that* (setting very low weight for external files) is something we could do by default?

Berdir’s picture

Status: Active » Needs review
Berdir’s picture

Issue summary: View changes
Berdir’s picture

Issue summary: View changes
Berdir’s picture

Ah found https://www.drupal.org/node/2897971, which says workarounds will be removed in 8.5.x but we already no longer really support it.

So we could possibly change this issue to just remove it?

Wim Leers’s picture

In total, I had 10 JS script files. With this patch and explicitly setting the weight of all my external libraries to -100, I was able to change it to:

This beautifully illustrates the problem of doing this in such a simple way: sometimes, the loading order matters. Script A assumes script B has already loaded, which assumes script X has already loaded.

For remote scripts, this is less likely to be a problem (many don't have dependencies on other scripts), but not guaranteed to not be a problem. So your trick definitely works for your case, but not in all cases, which means we can't do it in Drupal core :(


As we've said before, weights are a bad/broken system. Which is why Drupal 8 made big steps towards removing it.

That brings me to dependencies. Drupal 8 removed weights in favor of dependencies. Using dependencies, we can reason about which script load orders are valid.

Today, Drupal 8 still generates weights internally. That's why you end up with remote files interspersed with aggregates. But if we'd use dependency information, we could reason based on dependencies, which would allow us to do exactly what you did manually by setting external libraries to weight -100: we could load external libraries first, and then have far fewer aggregates.

(I realize this is not exactly a great explanation, but I hope it made sense. I can revisit this and explain it better later if necessary. Currently short on time.)

Berdir’s picture

> Drupal 8 removed weights in favor of dependencies.

Except it didn't, not really ;)

core.libraries.yml has lots of weights :)

I don't think dependencies just magically solve all those problems. We already have dependencies and that's exactly why we end up with an order like my example above.

Lets say you have 2 external scripts (E1, E2) and two local scripts (L1, L2), L1 depends on E1, L2 depends on E2. That results in 3 possible orders, all are *correct* according to the dependencies and work:

1. E1, E2, L1, L2
2. E1, L1, E2, L2
3. E2, L2, E1, L1

However, only the first option results in good aggregation but there is no way that we can know that based on the dependencies. That system doesn't care about that. To optimize for that, we need to add some assumptions/rules on top of the dependencies. Weights is currently what we have, but we could also build logic to e.g. load external JS first as long as dependencies allow it. Doesn't change that we need some logic to go from multiple correct solutions to the one that is best optimized.

Berdir’s picture

The main point of this issue is simply to optimize an already existing weight, I don't think that makes it worse. Anything else is a separate issue anyway, just wanted to add a bit of background and it's something worth discussing *somewhere*.

Wim Leers’s picture

core.libraries.yml has lots of weights :)

It doesn't. It only has it for the "old school ones", the libraries that also had explicit low weights in Drupal 7. Plus the libraries that are new that those libraries with low weights in Drupal 7 are now depending on in Drupal 8.

  1. backbone
  2. classList
  3. domready
  4. drupal
  5. drupalSettings
  6. html5shiv
  7. jquery
  8. jquery.once
  9. jquery.ui
  10. modernizr
  11. normalize

(those all have ~-20 weights)

Plus some weird cases:

  1. drupal.autocomplete
  2. drupal.tabledrag
  3. drupal.vertical-tabs

#1945262: Replace custom weights with dependencies in library declarations; introduce "before" and "after" for conditional ordering was committed and removed all remaining weights. It was then reverted because it broke a few things. And then it was never worked on again :(


I don't think dependencies just magically solve all those problems. We already have dependencies and that's exactly why we end up with an order like my example above.

That's not true. We still convert dependencies to weights, and that's what I was getting at in #7. See \Drupal\Core\Asset\AssetResolver::getJsAssets():

            // Always add a tiny value to the weight, to conserve the insertion
            // order.
            $options['weight'] += count($javascript) / 1000;

We conserve insertion order, but don't need to.

And we only generate a weight for each individual asset in the first place because we still have few weights lingering around. If we didn't have those (the ones quoted), then we could remove this altogether.


That results in 3 possible orders, all are *correct* according to the dependencies and work […] However, only the first option results in good aggregation but there is no way that we can know that based on the dependencies.

You're absolutely right!

To optimize for that, we need to add some assumptions/rules on top of the dependencies.

+1

Weights is currently what we have, but we could also build logic to e.g. load external JS first as long as dependencies allow it.

Yes, this please! We shouldn't introduce more uses of weights.

Wim Leers’s picture

The main point of this issue is simply to optimize an already existing weight

That's also true.

I hadn't noticed in #7 that classList already has a weight :(

Wim Leers’s picture

Status: Needs review » Needs work
+++ b/core/core.libraries.yml
@@ -20,7 +20,7 @@ classList:
-    assets/vendor/classList/classList.min.js: { weight: -21, browsers: { IE: 'lte IE 9', '!IE': false }, minified: true }
+    assets/vendor/classList/classList.min.js: { weight: -25, browsers: { IE: 'lte IE 9', '!IE': false }, minified: true }

So then this is all that remains: add a comment explaining why this weight of 25 was chosen.

droplet’s picture

Slightly off-topic:

Accurately 'lte IE9' is wrong. classList.js also support other methods that IE10/IE11 doesn't. Back to the time when we release D8.0.0, FF and Safari and Android also doesn't support classList well I think (But yeah, we only used add/remove in active-link.js, most of the users will not run into the bug)

I was suggested somewhere to use a simple polyfill with className inside the active-link.js directly. If anyone interested, raise an issue again :)

Berdir’s picture

Status: Needs work » Needs review
FileSize
803 bytes
627 bytes

Then maybe keep it for now and just update the weight?

Added a comment.

nod_’s picture

works for me

Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
630 bytes
875 bytes

Just fixing a language nit.

xjm’s picture

Status: Reviewed & tested by the community » Needs work

I think we should be more explicit into the comment as to why we're picking -25 instead of -99 or -26 or -24. After all, the difference between -25 and -21 is not that big. I originally assumed this had something to do with the IE 30 asset thing, but from the comments that doesn't seem to be the case at all.

Berdir’s picture

There is no other reason than being the first/lowest weight, there is no other reason for -25 other than the currently lowest is -22 and -25 is just the number I picked that is lower and still gives us a gap in case we need to add something more inbetween.

=> This is IMHO already in the comment, don't know how else to describe it, suggestions?

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Berdir’s picture

Status: Needs work » Needs review

Setting back to needs work to get feedback on that.

borisson_’s picture

I think it is clear enough as-is and don't know how to better describe that. So I feel this is RTBC, but not changing status to wait for @xjm's feedback

andypost’s picture

> the currently lowest is -22 and -25 is just the number I picked that is lower
I bet this is what @xjm asked to add to comment

andypost’s picture

I filed alternative issue to get rid of the problem for core #2955842: Deprecate classList library
But this one still makes sense for contrib which still use classList to work with SVG that is not supported in IE11

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Berdir’s picture

Status: Needs review » Closed (outdated)
FileSize
979 bytes

So classlist has been deprecated in the other issue.

I'm closing this as outdated, don't expect it to still land, with a final reroll for 8.8.

Berdir’s picture

And now the final final reroll because a typo was fixed in the deprecation message ;)