#31701 closed defect (bug) (fixed)
Emoji: Avoid enqueueing JS by default
Reported by: | obenland | Owned by: | pento |
---|---|---|---|
Milestone: | 4.2 | Priority: | normal |
Severity: | major | Version: | 4.2 |
Component: | Emoji | Keywords: | emoji has-patch commit |
Focuses: | javascript, performance | Cc: |
Description
Emoji support enqueues two script files and an inline script on every page load. AFAIK this is the first time that WP enqueues any scripts on the front-end by default.
We should try to find a way to limit it to sites where we know emojis are actually used, and/or find other ways to optimize that.
Attachments (13)
Change History (49)
#2
@
10 years ago
We have a handful of options for dealing with this:
- The current method, which detects browser support for emoji in JS. We can potentially optimize the JS, for example by combining
wp-emoji.js
andtwemoji.js
during the build process, removing a HTTP request. - There's potential for a
require.js
based solution - having a tiny shim for checking for emoji support, then lazy loading the rest if needed. This will mean a huge wait for emoji to actually be rendered, though, and it won't cut down the number of HTTP requests (it will reduce the download size by a few KB for browsers that support emoji, increase it by ~15KB for browsers that don't). - Checking for emoji in the post content (and other content areas, like widgets), and only loading the emoji JS if they're found. I've tried solutions like this in the past, and they always seem to have painful edge cases. For example, VideoPress tries to do exactly that, but it can't detect videos embedded in themes, for example.
- Using server side browser sniffing to decide if we should load the emoji JS. We've moved away from browser/OS version sniffing, because it's inherently unreliable - we have to keep updating the support matrix as browsers add emoji support. We could potentially make it a dynamic matrix, maybe by having the W.org API return an up-to-date copy for sites to use, but I'm still quite wary of the idea.
I'm leaning towards option 1 as the sanest solution. By combining the two scripts, we could potentially set the script to async
, as it has no other dependencies.
#3
@
10 years ago
When I first heard of this, I was hopeful that we'd be able to simply use a lightweight emoji detection inline script, unfortunately I then realised that the emoji detection code is covering a bunch of edge cases, which adds some significant size..
require.js
.. (it will reduce the download size by a few KB for browsers that support emoji, increase it by ~15KB for browsers that don't).
I assume we wouldn't use require.js
, but rather wp-emoji.js
+ a small amount of code to load twemoji
, which shouldn't be too complex.. We'd also want it to run before DOMReady, to hopefully get twomoji
loaded up asap..
#4
@
10 years ago
The browserSupportsEmoji()
function is pretty small - it minifies to only a few hundred bytes. The total shim would be less than 1KB. Compared to Twenty Fifteen's inline styles, it's positively svelte. :-)
If we were to write a custom loader (I did one ages ago for the Likes widget on WP.com, they're pretty simple), along with combining wp-emoji.js
and twemoji.js
in the build process, we may be on to something. I shall experiment.
#5
@
10 years ago
31701.diff only loads the emoji JS files if they're needed (currently only works with SCRIPT_DEBUG
set to true
).
Still to do:
- Load the scripts from the correct URL (it's currently assuming that WP is in the root directory).
- We've already detected browser emoji support once, we don't need to do it a second time in
wp-emoji.js
. - Combine
wp-emoji.js
andtwemoji.js
during the build process, so that we only need to loadwp-emoji.min.js
. - Figure out a nice way to build and maintain the minified version of the inline script.
Anyway, this is a proof of concept. It looks like it might be a viable option.
#9
@
10 years ago
Yes. While we're developing things, the method for doing that is in flux - we'll have it locked down before RC. :-)
#10
@
10 years ago
31701.2.diff adds a cache buster, and makes sure the script URLs are correct.
#11
follow-up:
↓ 12
@
10 years ago
Every time wp_print_scripts/styles()
runs, we are going to run this JS? This feels strange. I'm all for smilies, but are they really this important?
What happens if I call wp_print_scripts()
manually 6 times? Does this code run every time? What if I never want this code to run, do I have to add a filter every time I fire up a WordPress project?
This code makes sense for WordPress.com and Jetpack, I get it, but this seems weird to me as base product code.
#12
in reply to:
↑ 11
@
10 years ago
Replying to wonderboymusic:
Every time
wp_print_scripts/styles()
runs, we are going to run this JS? This feels strange.
Nice catch. :-) 31701.3.diff adds a static flag to the functions, so they're only ever printed once.
I'm all for smilies, but are they really this important?
Not just smilies, they're emoji.
What if I never want this code to run, do I have to add a filter every time I fire up a WordPress project?
Yep.
This code makes sense for WordPress.com and Jetpack, I get it, but this seems weird to me as base product code.
This goes back to some of the original discussion of emoji. The issue now is how to provide consistent emoji support for everyone.
#13
@
10 years ago
31701.4.diff does some magic.
- The emoji loader JS is moved to
wp-emoji-loader.js
, though is still embedded in the header thanks to the magic ofreadfile()
. This means that... wp-emoji-loader.js
is minified as part of the build process, so will be embedded as a minified version on release (927 bytes, for reference).wp-emoji.js
andtwemoji.js
are concat'ed as part of the build process, intowp-emoji-release.min.js
. I'm not wild about the name, suggestions are welcome. :-)
#14
follow-up:
↓ 15
@
10 years ago
Looking at 31701.4.diff, some ideas:
- Maybe we can use only one "settings" filter. No need to filter each setting separately (
emoji_url
,emoji_ext
, etc.). Combine them in an array and pass it through one? - Sometimes widow.load fires much later than expected. Can take up to 5-10 sec. etc. Usually caused by a slow loading image or script. Would it be better to run the emoji JS on DOM ready? Seems that
DOMContentLoaded
is supported in all browsers that also support addEventListener, i.e. all except IE8 and older: http://caniuse.com/#feat=domcontentloaded, https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded. - If the wp-emoji and twemoji scripts are concatenated together (which is a good idea), we won't need to wait for loading. In theory we can load late in the footer and run as soon as the file is loaded. In practice we can set a var in wp-emoji-loader.js on DOM ready, then when wp-emoji.js loads, we can check that var and either bind to DOMContentLoaded or window.load, or run if that has already fired.
- May be missing something but looks like
waitForTwemoji()
inserts another script node every 50ms while waiting.
#15
in reply to:
↑ 14
@
10 years ago
Replying to azaozz:
Maybe we can use only one "settings" filter. No need to filter each setting separately (
emoji_url
,emoji_ext
, etc.). Combine them in an array and pass it through one?
I'm inclined to keep them as separate filters - there shouldn't be a need to filter the JS file names. I'm totally okay with changing that if there's a use case, though.
Sometimes widow.load fires much later than expected. Can take up to 5-10 sec. etc. Usually caused by a slow loading image or script. Would it be better to run the emoji JS on DOM ready? Seems that
DOMContentLoaded
is supported in all browsers that also support addEventListener, i.e. all except IE8 and older: http://caniuse.com/#feat=domcontentloaded, https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded.
How about using the readystatechange
event? It's supported everywhere, and seems to happen just before DOMContentLoaded
, but after the DOM is ready for interaction.
If the wp-emoji and twemoji scripts are concatenated together (which is a good idea), we won't need to wait for loading. In theory we can load late in the footer and run as soon as the file is loaded. In practice we can set a var in wp-emoji-loader.js on DOM ready, then when wp-emoji.js loads, we can check that var and either bind to DOMContentLoaded or window.load, or run if that has already fired.
I don't like putting wp-emoji-loader.js
in the footer. Having it in the header means it starts loading the JS file as soon as possible. Adding an extra flag to wp-emoji-loader.js
to determine when the DOM is loaded seems like a waste, when document.readyState
gives us the information we need.
May be missing something but looks like
waitForTwemoji()
inserts another script node every 50ms while waiting.
Oops! I'll fix that in the next patch. Thanks. :-)
#16
@
10 years ago
In 31701.6.diff:
- Switched the initial emoji parse to the
readystatechange
event, instead of theload
event. - After
wp-emoji-loader.js
determines emoji support, it sets that info onwindow._wpemojiSettings
, which is then reused bywp-emoji.js
. - As a result, I've removed
browserSupportsEmoji()
fromwp-emoji.js
. - Fixed
waitForTwemoji()
incorrectly adding many<script>
nodes.
Things for feedback:
- Is
wp-emoji-loader.js
the correct name for our loader file? - I'm not sure about using
readfile()
in production - can we guarantee the JS file will be available on the same FS? Maybe it'd be better to havegrunt build
manually write the minified JS toformatting.php
.
#17
@
10 years ago
31701.7.diff writes the contents of wp-emoji-loader.min.js
to formatting.php
during grunt build
.
Another thing for feedback:
- I'm not wild about the
wp-emoji-release.min.js
filename for the combinedwp-emoji.js
andtwemoji.js
file.
#18
@
10 years ago
31701.8.diff has a fun little change.
As of 31701.7.diff, the wp-emoji-loader.js
JavaScript took about 15ms to run - 1.5ms for the JS, and 13.5ms for a style recalculation, which is caused by our use of canvas
. I've been experimenting a bit, but it seems that the style recalculation is unavoidable.
To fix this, 31701.8.diff moves the JS to be embedded before wp_print_styles
. Now the JS takes 1.6ms to run - 1.5ms for the JS, and 0.1ms for the style recalculation.
So, now the total is:
- Running
wp-emoji-loader.js
: 1.6ms - Parsing the embedded emoji CSS: <0.1ms
If we need to load Twemoji:
- Parsing twemoji.js: 0.8ms
- Parsing and running wp-emoji.js with no emoji: 1ms
The overhead if no emoji exists (and we need to load the extra JS) is about 3.5ms. About 0.01ms is added per emoji replaced. I haven't timed this with the built scripts yet, but I expect everything to be slightly faster.
@wonderboymusic - given your earlier feelings on running this every time, what's your opinion now on a 1.7-3.5ms overhead?
#19
follow-up:
↓ 20
@
10 years ago
What makes the big difference in the style calculation time? Is it just because there's more nodes in the document at the time? Is it because it's after the styles are printed?
#20
in reply to:
↑ 19
@
10 years ago
There are a couple of slowdowns:
- If the JS is after the styles, it has to wait until all of the CSS has been loaded and parsed before it runs.
- Because all of the CSS has been loaded when it runs, there's more to recalculate if a recalculation is needed.
#21
@
10 years ago
31701.9.diff reduces wp-emoji.loader.min.js
from 1059 to 891 bytes.
#22
@
10 years ago
In 31701.10.diff:
- Fix
readyState
andreadystatechange
(have to be ondocument
). - Some cleanup:
- No need to output the (cache busting) version string to JS and append it from there. Can append in PHP.
- Renamed the "handle" for the concatenated file in an attempt to make the loading a bit more readable. May need better name :)
- Shaved few more bytes off wp-emoji-loader.js.
#23
follow-up:
↓ 24
@
10 years ago
- Keywords commit added
Nice one, @azaozz. :-)
31701.11.diff has a few small changes:
- Uncomments some code accidentally commented out 31701.8.diff.
- Moves the
print_emoji_detection_script
hook back towp_head
, which was accidentally changed back towp_print_scripts
in 31701.9.diff. - Removes the now unnecessary changes to unit tests, originally needed because of
print_emoji_detection_script
being attached towp_print_scripts
.
Unless anyone has any better filename suggestions, I'm pretty happy with the state of this patch. Barring any objections, I'll commit it tomorrow.
#24
in reply to:
↑ 23
@
10 years ago
Replying to pento:
Oh, another thing. Not a huge deal but.. Currently with SCRIPT_DEBUG on we are waiting for twemoji.js to fully load before starting to load wp-emoji.js. We should load both at the same time and then wait to initialize wp-emoji when twemoji.js is fully loaded.
So waitForTwemoji()
should actually be in wp-emoji.js, after it checks document.readyState. Patch coming up.
#25
@
10 years ago
In 31701.12.diff:
- When waiting for twemoji.js to load, break after 30 sec.
- When SCRIPT_DEBUG, add wp-emoji and twemoji at the same time, move the "loaded" check to wp-emoji.
Thinking this is ready. The scripts load as fast as possible and there is almost no slowdown in debug mode or when running from /src.
This ticket was mentioned in Slack in #core by pento. View the logs.
10 years ago
#27
@
10 years ago
- Owner set to pento
- Resolution set to fixed
- Status changed from new to closed
In 31875:
#32
@
10 years ago
Seems IE8 "lies" that DOM is ready when document.readyState === 'interactive'
. We need to wait until it is complete
there.
@pento