Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Golf page-loader #8190

Merged
merged 2 commits into from
Jul 31, 2019
Merged

Golf page-loader #8190

merged 2 commits into from
Jul 31, 2019

Conversation

Timer
Copy link
Member

@Timer Timer commented Jul 31, 2019

  • clearCache is an unused function
  • try/catch can be used for safe property access
  • trim some bytes off 2g check

👋 1.11 kB

@Timer Timer added this to the 9.0.4 milestone Jul 31, 2019
@github-actions
Copy link
Contributor

Stats from current PR

Click to expand stats ✅ Total Bundle Size Decrease ✅
zeit/next.js canary Timer/next.js adjust-page-loader Change
Build Duration 12.9s 12.7s -144ms
node_modules Size 45.9 MB 45.9 MB -747 B
Total Bundle (main, webpack, commons) Size 206 kB 206 kB -237 B
Total Bundle (main, webpack, commons) gzip Size 67.9 kB 67.8 kB -43 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _error Size 8.22 kB 8.22 kB
Client _error gzip Size 3.16 kB 3.16 kB
Client pages/index Size 343 B 343 B
Client pages/index gzip Size 246 B 246 B
Client pages/link Size 4.08 kB 4.08 kB
Client pages/link gzip Size 1.8 kB 1.8 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client main Size 15.6 kB 15.4 kB -237 B
Client main gzip Size 5.39 kB 5.35 kB -43 B
Client commons Size 188 kB 188 kB
Client commons gzip Size 61.1 kB 61.1 kB
Client webpack Size 1.49 kB 1.49 kB
Client webpack gzip Size 770 B 770 B
Base Rendered Size 1.35 kB 1.35 kB
Build Dir Size 703 kB 702 kB -1.11 kB
Click to expand serverless stats ✅ Total Bundle Size Decrease ✅
zeit/next.js canary Timer/next.js adjust-page-loader Change
Build Duration 14s 13.6s -359ms
node_modules Size 45.9 MB 45.9 MB -747 B
Total Bundle (main, webpack, commons) Size 206 kB 206 kB -237 B
Total Bundle (main, webpack, commons) gzip Size 67.8 kB 67.8 kB -40 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB ⚠️ +1 B
Client _error Size 8.22 kB 8.22 kB
Client _error gzip Size 3.16 kB 3.16 kB
Client pages/index Size 343 B 343 B
Client pages/index gzip Size 246 B 246 B
Client pages/link Size 4.08 kB 4.08 kB
Client pages/link gzip Size 1.8 kB 1.8 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 300 B 301 B ⚠️ +1 B
Client main Size 15.6 kB 15.4 kB -237 B
Client main gzip Size 5.39 kB 5.35 kB -41 B
Client commons Size 188 kB 188 kB
Client commons gzip Size 61.1 kB 61.1 kB
Client webpack Size 1.49 kB 1.49 kB
Client webpack gzip Size 770 B 770 B
Serverless pages/link Size 252 kB 252 kB
Serverless pages/link gzip Size 67.9 kB 67.9 kB
Serverless pages/index Size 244 kB 244 kB
Serverless pages/index gzip Size 65.8 kB 65.8 kB
Serverless pages/_error Size 244 kB 244 kB
Serverless pages/_error gzip Size 65.5 kB 65.5 kB ⚠️ +2 B
Serverless pages/routerDirect Size 245 kB 245 kB
Serverless pages/routerDirect gzip Size 65.7 kB 65.7 kB ⚠️ +1 B
Serverless pages/withRouter Size 245 kB 245 kB
Serverless pages/withRouter gzip Size 65.8 kB 65.8 kB
Build Dir Size 1.89 MB 1.89 MB -1.11 kB
Diff for main.js
@@ -1103,22 +1103,26 @@ var _mitt = _interopRequireDefault(__webpack_require__("kiME"));
 
 var _unfetch = _interopRequireDefault(__webpack_require__("m/Gl"));
 /* global document */
-// smaller version of https://gist.github.com/igrigorik/a02f2359f3bc50ca7a9c
 
 
-function supportsPreload(list) {
-  if (!list || !list.supports) {
-    return false;
-  }
-
+function supportsPreload(el) {
   try {
-    return list.supports('preload');
-  } catch (e) {
+    return el.relList.supports('preload');
+  } catch (_unused) {
     return false;
   }
 }
 
-var hasPreload = supportsPreload(document.createElement('link').relList);
+var hasPreload = supportsPreload(document.createElement('link'));
+
+function preloadScript(url) {
+  var link = document.createElement('link');
+  link.rel = 'preload';
+  link.crossOrigin = undefined;
+  link.href = url;
+  link.as = 'script';
+  document.head.appendChild(link);
+}
 
 var PageLoader =
 /*#__PURE__*/
@@ -1309,7 +1313,7 @@ function () {
       return (0, _asyncToGenerator2["default"])(
       /*#__PURE__*/
       _regenerator["default"].mark(function _callee2() {
-        var scriptRoute, link;
+        var scriptRoute, cn;
         return _regenerator["default"].wrap(function _callee2$(_context2) {
           while (1) {
             switch (_context2.prev = _context2.next) {
@@ -1328,15 +1332,14 @@ function () {
 
               case 5:
                 _this2.prefetchCache.add(scriptRoute); // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
-                // Don't prefetch if the user is on 2G / Don't prefetch if Save-Data is enabled
 
 
-                if (!('connection' in navigator)) {
+                if (!(cn = navigator.connection)) {
                   _context2.next = 9;
                   break;
                 }
 
-                if (!((navigator.connection.effectiveType || '').indexOf('2g') !== -1 || navigator.connection.saveData)) {
+                if (!((cn.effectiveType || '').indexOf('2g') !== -1 || cn.saveData)) {
                   _context2.next = 9;
                   break;
                 }
@@ -1345,7 +1348,7 @@ function () {
 
               case 9:
                 if (!hasPreload) {
-                  _context2.next = 19;
+                  _context2.next = 14;
                   break;
                 }
 
@@ -1353,23 +1356,18 @@ function () {
                 return _this2.promisedBuildId;
 
               case 12:
-                link = document.createElement('link');
-                link.rel = 'preload';
-                link.crossOrigin = undefined;
-                link.href = _this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute;
-                link.as = 'script';
-                document.head.appendChild(link);
+                preloadScript(_this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute);
                 return _context2.abrupt("return");
 
-              case 19:
+              case 14:
                 if (!(document.readyState === 'complete')) {
-                  _context2.next = 23;
+                  _context2.next = 18;
                   break;
                 }
 
                 return _context2.abrupt("return", _this2.loadPage(route)["catch"](function () {}));
 
-              case 23:
+              case 18:
                 return _context2.abrupt("return", new _promise["default"](function (resolve) {
                   window.addEventListener('load', function () {
                     _this2.loadPage(route).then(function () {
@@ -1380,7 +1378,7 @@ function () {
                   });
                 }));
 
-              case 24:
+              case 19:
               case "end":
                 return _context2.stop();
             }
@@ -1388,18 +1386,6 @@ function () {
         }, _callee2);
       }))();
     }
-  }, {
-    key: "clearCache",
-    value: function clearCache(route) {
-      route = this.normalizeRoute(route);
-      delete this.pageCache[route];
-      delete this.loadingRoutes[route];
-      var script = document.getElementById("__NEXT_PAGE__" + route);
-
-      if (script) {
-        script.parentNode.removeChild(script);
-      }
-    }
   }]);
   return PageLoader;
 }();

@github-actions
Copy link
Contributor

Stats from current PR

Click to expand stats ✅ Total Bundle Size Decrease ✅
zeit/next.js canary Timer/next.js adjust-page-loader Change
Build Duration 14s 13.8s -132ms
node_modules Size 45.9 MB 45.9 MB -747 B
Total Bundle (main, webpack, commons) Size 206 kB 206 kB -237 B
Total Bundle (main, webpack, commons) gzip Size 67.8 kB 67.8 kB -40 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB ⚠️ +1 B
Client _error Size 8.22 kB 8.22 kB
Client _error gzip Size 3.16 kB 3.16 kB
Client pages/index Size 343 B 343 B
Client pages/index gzip Size 246 B 246 B
Client pages/link Size 4.08 kB 4.08 kB
Client pages/link gzip Size 1.8 kB 1.8 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 300 B 301 B ⚠️ +1 B
Client main Size 15.6 kB 15.4 kB -237 B
Client main gzip Size 5.39 kB 5.35 kB -41 B
Client commons Size 188 kB 188 kB
Client commons gzip Size 61.1 kB 61.1 kB
Client webpack Size 1.49 kB 1.49 kB
Client webpack gzip Size 770 B 770 B
Base Rendered Size 1.35 kB 1.35 kB
Build Dir Size 703 kB 702 kB -1.11 kB
Click to expand serverless stats ✅ Total Bundle Size Decrease ✅
zeit/next.js canary Timer/next.js adjust-page-loader Change
Build Duration 14.7s 14.7s -38ms
node_modules Size 45.9 MB 45.9 MB -747 B
Total Bundle (main, webpack, commons) Size 206 kB 206 kB -237 B
Total Bundle (main, webpack, commons) gzip Size 67.9 kB 67.8 kB -43 B
Client _app Size 2.39 kB 2.39 kB
Client _app gzip Size 1.08 kB 1.08 kB
Client _error Size 8.22 kB 8.22 kB
Client _error gzip Size 3.16 kB 3.16 kB
Client pages/index Size 343 B 343 B
Client pages/index gzip Size 246 B 246 B
Client pages/link Size 4.08 kB 4.08 kB
Client pages/link gzip Size 1.8 kB 1.8 kB
Client pages/routerDirect Size 423 B 423 B
Client pages/routerDirect gzip Size 306 B 306 B
Client pages/withRouter Size 435 B 435 B
Client pages/withRouter gzip Size 301 B 301 B
Client main Size 15.6 kB 15.4 kB -237 B
Client main gzip Size 5.39 kB 5.35 kB -43 B
Client commons Size 188 kB 188 kB
Client commons gzip Size 61.1 kB 61.1 kB
Client webpack Size 1.49 kB 1.49 kB
Client webpack gzip Size 770 B 770 B
Serverless pages/link Size 252 kB 252 kB
Serverless pages/link gzip Size 67.9 kB 67.9 kB ⚠️ +4 B
Serverless pages/index Size 244 kB 244 kB
Serverless pages/index gzip Size 65.8 kB 65.8 kB ⚠️ +2 B
Serverless pages/_error Size 244 kB 244 kB
Serverless pages/_error gzip Size 65.5 kB 65.5 kB ⚠️ +2 B
Serverless pages/routerDirect Size 245 kB 245 kB
Serverless pages/routerDirect gzip Size 65.7 kB 65.7 kB ⚠️ +4 B
Serverless pages/withRouter Size 245 kB 245 kB
Serverless pages/withRouter gzip Size 65.8 kB 65.9 kB ⚠️ +2 B
Build Dir Size 1.89 MB 1.89 MB -1.11 kB
Diff for main.js
@@ -1103,22 +1103,26 @@ var _mitt = _interopRequireDefault(__webpack_require__("kiME"));
 
 var _unfetch = _interopRequireDefault(__webpack_require__("m/Gl"));
 /* global document */
-// smaller version of https://gist.github.com/igrigorik/a02f2359f3bc50ca7a9c
 
 
-function supportsPreload(list) {
-  if (!list || !list.supports) {
-    return false;
-  }
-
+function supportsPreload(el) {
   try {
-    return list.supports('preload');
-  } catch (e) {
+    return el.relList.supports('preload');
+  } catch (_unused) {
     return false;
   }
 }
 
-var hasPreload = supportsPreload(document.createElement('link').relList);
+var hasPreload = supportsPreload(document.createElement('link'));
+
+function preloadScript(url) {
+  var link = document.createElement('link');
+  link.rel = 'preload';
+  link.crossOrigin = undefined;
+  link.href = url;
+  link.as = 'script';
+  document.head.appendChild(link);
+}
 
 var PageLoader =
 /*#__PURE__*/
@@ -1309,7 +1313,7 @@ function () {
       return (0, _asyncToGenerator2["default"])(
       /*#__PURE__*/
       _regenerator["default"].mark(function _callee2() {
-        var scriptRoute, link;
+        var scriptRoute, cn;
         return _regenerator["default"].wrap(function _callee2$(_context2) {
           while (1) {
             switch (_context2.prev = _context2.next) {
@@ -1328,15 +1332,14 @@ function () {
 
               case 5:
                 _this2.prefetchCache.add(scriptRoute); // Inspired by quicklink, license: https://github.com/GoogleChromeLabs/quicklink/blob/master/LICENSE
-                // Don't prefetch if the user is on 2G / Don't prefetch if Save-Data is enabled
 
 
-                if (!('connection' in navigator)) {
+                if (!(cn = navigator.connection)) {
                   _context2.next = 9;
                   break;
                 }
 
-                if (!((navigator.connection.effectiveType || '').indexOf('2g') !== -1 || navigator.connection.saveData)) {
+                if (!((cn.effectiveType || '').indexOf('2g') !== -1 || cn.saveData)) {
                   _context2.next = 9;
                   break;
                 }
@@ -1345,7 +1348,7 @@ function () {
 
               case 9:
                 if (!hasPreload) {
-                  _context2.next = 19;
+                  _context2.next = 14;
                   break;
                 }
 
@@ -1353,23 +1356,18 @@ function () {
                 return _this2.promisedBuildId;
 
               case 12:
-                link = document.createElement('link');
-                link.rel = 'preload';
-                link.crossOrigin = undefined;
-                link.href = _this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute;
-                link.as = 'script';
-                document.head.appendChild(link);
+                preloadScript(_this2.assetPrefix + "/_next/static/" + encodeURIComponent(_this2.buildId) + "/pages" + scriptRoute);
                 return _context2.abrupt("return");
 
-              case 19:
+              case 14:
                 if (!(document.readyState === 'complete')) {
-                  _context2.next = 23;
+                  _context2.next = 18;
                   break;
                 }
 
                 return _context2.abrupt("return", _this2.loadPage(route)["catch"](function () {}));
 
-              case 23:
+              case 18:
                 return _context2.abrupt("return", new _promise["default"](function (resolve) {
                   window.addEventListener('load', function () {
                     _this2.loadPage(route).then(function () {
@@ -1380,7 +1378,7 @@ function () {
                   });
                 }));
 
-              case 24:
+              case 19:
               case "end":
                 return _context2.stop();
             }
@@ -1388,18 +1386,6 @@ function () {
         }, _callee2);
       }))();
     }
-  }, {
-    key: "clearCache",
-    value: function clearCache(route) {
-      route = this.normalizeRoute(route);
-      delete this.pageCache[route];
-      delete this.loadingRoutes[route];
-      var script = document.getElementById("__NEXT_PAGE__" + route);
-
-      if (script) {
-        script.parentNode.removeChild(script);
-      }
-    }
   }]);
   return PageLoader;
 }();

@Janpot
Copy link
Contributor

Janpot commented Jul 31, 2019

@Timer Just something to keep in mind with this sort of optimizations, while it may result in less bytes over the wire, this try/catch construct is slower than property undefined checking when it comes to execution speed.
So it's basically a trade-off between the speed of initial page load, and the speed of subsequent executions (the real impact may turn out to be negligible, I haven't measured this).

@Timer
Copy link
Member Author

Timer commented Jul 31, 2019

@Janpot I believe you're referring to v8 engine deoptimizing try/catch blocks.
I'm aware of this legacy-concern — v8 no longer deoptimizes try/catch.

While this may "deoptimize" the code in legacy browsers, it only has a measurable effect on code with high cyclomatic complexity.

In this specific case, "execution speed" is irrelevant because this code path is not in the hot-path (to hydration, or even rendering). So minimal bytes will always be preferred.

edit: this also prevents dual-access to a DOM property (.support), which by all measures is more expensive than a try/catch even if "deoptimized". And the try-catch was already present.

Copy link
Member

@ijjk ijjk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice bundle savings 👍

Note: CI failures are un-related to this PR, confirmed locally

@Timer Timer merged commit 9c0b727 into vercel:canary Jul 31, 2019
@Timer Timer deleted the adjust-page-loader branch July 31, 2019 16:03
@Janpot
Copy link
Contributor

Janpot commented Jul 31, 2019

@Timer I didn't mean the deoptimization, throwing and catching exceptions is generally slower than testing properties. But as you said, execution speed is irrelevant in this context, and as I said before, the impact is probably negligible anyway.
👍 Good point about the double dom access

@Timer
Copy link
Member Author

Timer commented Jul 31, 2019

Ah, sure -- try/catch code is always slower than non-try/catch code.

@Janpot
Copy link
Contributor

Janpot commented Jul 31, 2019

The bad performance code path would only happen on outdated browsers anyway. So nothing to worry about in the end I think.

@Timer
Copy link
Member Author

Timer commented Jul 31, 2019

Yeah, I checked the perf for curiosity -- only a 4% +- 2% difference on modern browsers.

Timer added a commit that referenced this pull request Jul 31, 2019
Timer added a commit that referenced this pull request Jul 31, 2019
Timer pushed a commit that referenced this pull request Aug 8, 2019
* Refactor SplitChunksPlugin configs and add experimental chunking strategy

* Use typeDefs for SplitChunksConfig

* Modify build manifest plugin to create runtime build manifest

* Add support for granular chunks to page-loader

* Ensure normal behavior if experimental granularChunks flag is false

* Update client build manifest to remove iife & implicit global

* Factor out '/_next/' prepending into getDependencies

* Update packages/next/build/webpack-config.ts filepath regex

Co-Authored-By: Jason Miller <developit@users.noreply.github.com>

* Simplify dependency load ordering in page-loader.js

* Use SHA1 hash to shorten filenames for dependency modules

* Add scheduler to framework cacheGroup in webpack-config

* Update page loader to not duplicate script tags with query parameters

* Ensure no slashes end up in the file hashes

* Add prop-types to framework chunk

* Fix issue with mis-attributed events

* Increase modern build size budget--possibly decrement after consulting with @janicklasralph

* Use module.rawRequest for lib chunks

Co-Authored-By: Daniel Stockman <daniel.stockman@gmail.com>

* Dasherize lib chunk names

Co-Authored-By: Daniel Stockman <daniel.stockman@gmail.com>

* Fix typescript errors, reorganize lib name logic

* Dasherize rawRequest, short circuit name logic when rawRequest found

* Add `scheduler` package to test regex

* Fix a nit

* Adjust build manifest plugin

* Shorten key name

* Extract createPreloadLink helper

* Extract getDependencies helper

* Move method

* Minimize diff

* Minimize diff x2

* Fix Array.from polyfill

* Simplify page loader code

* Remove async=false for script tags

* Code golf `getDependencies` implementation

* Require lib chunks be in node_modules

* Update packages/next/build/webpack-config.ts

Co-Authored-By: Joe Haddad <timer150@gmail.com>

* Replace remaining missed windows compat regex

* Trim client manifest

* Prevent duplicate link preload tags

* Revert size test changes

* Squash manifest size even further

* Add comment for clarity

* Code golfing 🏌️‍♂️

* Correctly select modern dependencies

* Ship separate modern client manifest when module/module enabled

* Update packages/next/build/webpack/plugins/build-manifest-plugin.ts

Co-Authored-By: Joe Haddad <timer150@gmail.com>

* Remove unneccessary filter from page-loader

* Add lookbehind to file extension regex in page-loader

* v9.0.3

* Update examples for Apollo with AppTree (#8180)

* Update examples for Apollo with AppTree

* Fix apolloClient being overwritten when rendering AppTree

* Golf page-loader (#8190)

* Remove lookbehind for module replacement

* Wait for build manifest promise before page load or prefetch

* Updating modern-only chunks inside the right entry point

* Fixing ts errors

* Rename variable

* Revert "Wait for build manifest promise before page load or prefetch"

This reverts commit c370528.

* Use proper typedef for webpack chunk

* Re-enable promisified client build manifest

* Fix bug in getDependencies map

* Insert check for granularChunks in page-loader

* Increase size limit temporarily for granular chunks

* Add 50ms delay to flaky test

* Set env.__NEXT_GRANULAR_CHUNKS in webpack config

* Reset size limit to 187

* Set process.env.__NEXT_GRANULAR_CHUNKS to false if selectivePageBuilding

* Update test/integration/production/test/index.test.js

Co-Authored-By: Joe Haddad <timer150@gmail.com>

* Do not create promise if not using chunking PR
@vercel vercel locked as resolved and limited conversation to collaborators Feb 1, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants