Cornerstone 2.0: Performance improvements#1229
Cornerstone 2.0: Performance improvements#1229mattolson merged 8 commits intobigcommerce:masterfrom mattolson:2.0
Conversation
|
Autotagging @bigcommerce/storefront-team @davidchin |
|
I'm not qualified to review this but I am totally stoked about it! |
|
Also just want to confirm that the screenshots are correct. The time stamp for the before is later than the after if it makes sense 😜 |
| // Find the appropriate page loader based on pageType | ||
| const pageClassImporter = pageClasses[pageType]; | ||
| if (typeof pageClassImporter === 'function') { | ||
| const PageClass = (await pageClassImporter()).default; |
There was a problem hiding this comment.
i can speak for scala world when i say "Awaits" are not great. Not sure if its the same in JS tho
There was a problem hiding this comment.
The async/await pattern is an alternate way of working with promises. await is essentially the same as Promise.then without all the code nesting. https://javascript.info/async-await
There was a problem hiding this comment.
Does await get transpiled by stencil-cli?
There was a problem hiding this comment.
Yeah, await is non-blocking in JS, so not like the Await object in Scala. Fun fact, there's an async/await macro library for Scala that does the same thing: https://github.com/scala/scala-async
It basically rewrites the code to use .map and .flatMap in the compiler.
There was a problem hiding this comment.
@icatalina Yes, we are running everything through babel in stencil CLI.
There was a problem hiding this comment.
const pageClass = await xx <-- how can this be async what if somone uses the pageClass val before the await is resolved?
There was a problem hiding this comment.
RegisteredCustomerMessagesUITest::testRequiredFieldWhileCreatingMessage
same this.context undefined message
along with this updated (removed span)
const REQUIRED_MESSAGE_XPATH = '//textarea[@id = "message_content"]';
const REQUIRED_SUBJECT_XPATH = '//input[@id = "message_subject"]';
There was a problem hiding this comment.
@tpietsch for the const pageClass = await xx example, it's simply sugar for using a promise.then, so it will not set the pageClass val until after the await is resolved. but it's non-blocking in that the thread is released when invoking await to handle other things.
There was a problem hiding this comment.
it just kinda not my fave cause now you have a variable in scope that you can use where the value might change right? first its undef then its def and between that time it can be read from incorrectly
In a normal Promise(resolve -> resolve.data) <-- you are more explicit about when the data is being used here it seems like it could cause intermittent weirdness. And also the variable is declared as a const which i would expect as non-changing value
There was a problem hiding this comment.
I believe Babel actually dumps a bunch of polyfill code in order to support async/await (it needs to support ES6 generator). Wouldn't this increase the file size, as you need both Promise and Generator instead of just Promise?
|
noticed lots of VARS for the require dependecys that prob should be const right? |
|
@junedkazi yes the screenshots are correct. I was switching back and forth between Cornerstone 1.17 and a custom theme based on this branch. I did Cornerstone 1.17 after my branch. |
* Modernizr is a blocking script that has very little benefit for us. Removing this script improves page responsiveness. * We used Modernizr via the css classes `csscolumns`, `flexbox`, `objectfit`, and `js`. * The `csscolumns` class was used for productMasonry for older browsers, but since then all of our supported browsers support css columns. There is one small problem with Firefox: it does not support break-inside, but it does support page-break-inside, which does the same thing. See https://caniuse.com/#feat=multicolumn * The `flexbox` class is unused * The `objectfit` class is used by carousel. Reimplmented without using Modernizer. The previous implmentation put a background image on the wrapper div and then hid it if object-fit is supported. The new implementation does not put the background image on the wrapper div by default, but instead assumes that your browser supports object-fit (all ours do except for IE). For IE, we use javascript to copy the image source from the image tag to the background image of the wrapper div and then hide the image. * The `js` class is used for a few things in css, so mimic this behavior with a simple inline script.
|
Glorious 👏 PR 👏 Description 👏 |
| // Find the appropriate page loader based on pageType | ||
| const pageClassImporter = pageClasses[pageType]; | ||
| if (typeof pageClassImporter === 'function') { | ||
| const PageClass = (await pageClassImporter()).default; |
There was a problem hiding this comment.
Does await get transpiled by stencil-cli?
* Add wrapper div for hero carousel that has the appropriate height for the image to be displayed after lazy loading. * This improves frontend performance because the browser will be less likely to load images below the fold immediately.
* Add dns prefetch & preconnect to core resources that we know we will need -- fonts, cdn, jirafe, etc
|
@icatalina ♻️ |
|
hi @mattolson ! what about the |
|
@icatalina I already replied inline there -- we run everything through Babel as part of Stencil CLI. |
| throw new Error(err); | ||
| } | ||
| $(document).ready(() => { | ||
| page.onReady.bind(page)(); |
There was a problem hiding this comment.
how can you be sure this order doesnt matter?
There was a problem hiding this comment.
I don't think you need to call bind because onReady is already bounded to page.
* Upgrade to webpack 4 * Clean up stencil bootstrap * Simplify interface for PageManager and get rid of async library dependency * Get rid of html5-history-api library dependency (no longer needed) * Get rid of fastclick library dependency (no longer needed) * Upgrade several babel dependencies * Separate webpack config into common, dev, prod, and add npm scripts to build * Get rid of minifications in babel loader, instead rely on webpack * Get rid of LoaderOptionsPlugin * Get rid of CommonsChunkPlugin (webpack 4 will automaticaly do this for prod)
* Use svg-injector to fetch the svg icon sprite rather than embed it directly on the page. This improves frontend performance because the icons will be cached after the first request and we reduce the page weight by about 28kb. * Move location of generated svg so it is retrievable by the frontend through the CDN. * Update svgstore grunt task
* Reduce the default number of featured, new, and bestselling products on the home page. * These can always be updated in Theme Editor, but the defaults don't need to be so high.
* Use laziest possible setting for images
* Use `resourceHints` helper from paper 2.0.8, which looks up configured cdn and font providers.
|
💚 Stencil regression suite has passed with the exception of those tests that hit the changed featured/new/popular product counts. |
davidchin
left a comment
There was a problem hiding this comment.
This is exciting. It seems the changes really make a huge difference. 👏 I have a few small comments for you. Could you please take a look when you get a chance? Thanks!
| // Find the appropriate page loader based on pageType | ||
| const pageClassImporter = pageClasses[pageType]; | ||
| if (typeof pageClassImporter === 'function') { | ||
| const PageClass = (await pageClassImporter()).default; |
There was a problem hiding this comment.
I believe Babel actually dumps a bunch of polyfill code in order to support async/await (it needs to support ES6 generator). Wouldn't this increase the file size, as you need both Promise and Generator instead of just Promise?
| throw new Error(err); | ||
| } | ||
| $(document).ready(() => { | ||
| page.onReady.bind(page)(); |
There was a problem hiding this comment.
I don't think you need to call bind because onReady is already bounded to page.
| webpack = require('webpack'); | ||
|
|
||
| // Common configuration, with extensions in webpack.dev.js and webpack.prod.js. | ||
| module.exports = { |
There was a problem hiding this comment.
If file size is an issue, you might want to consider adding performance.maxEntrypointSize and set a limit on the maximum file size for every bundle. It would warn you if, in the future, you accidentally bundle a really large third party library.
| devtool: 'source-map', | ||
| plugins: [ | ||
| new webpack.DefinePlugin({ | ||
| 'process.env.NODE_ENV': 'development', |
There was a problem hiding this comment.
I'm not sure if this is needed for development. 🍹
|
@davidchin I'm sorry, I didn't see your review prior to merging. I will address these comments in a future PR. We intend to continue to invest in Cornerstone performance. |
|
@mattolson no worries. |
|
@mattolson Super awesome. Cant wait to test this newest version out on our next client |
What?
The goal of this release is to improve frontend theme performance. This is the work of @mattolson, @junedkazi and @BC-EChristensen during the Stranger Hackings hackathon.
Remove Modernizr
csscolumns,flexbox,objectfit, andjs.csscolumnsclass was used for productMasonry for older browsers, but since then all of our supported browsers support css columns. There is one small problem with Firefox: it does not support break-inside, but it does support page-break-inside, which does the same thing. See https://caniuse.com/#feat=multicolumnflexboxclass is unusedobjectfitclass is used by carousel. Reimplmented without using Modernizer. The previous implmentation put a background image on the wrapper div and then hid it if object-fit is supported. The new implementation does not put the background image on the wrapper div by default, but instead assumes that your browser supports object-fit (all ours do except for IE). For IE, we use javascript to copy the image source from the image tag to the background image of the wrapper div and then hide the image.jsclass is used for a few things in css, so mimic this behavior with a simple inline script.Save space for carousel
Add dns prefetch hints
resourceHintsthat was introduced in paper 2.0.8.Webpack 4 & Javascript optimizations
Inject svg via ajax
Reduce defaults for product counts
Lazier image loading
Screenshots
Using lighthouse for benchmarking against a production store, I found the following:
Before
Desktop
Mobile
After
Desktop
Mobile