xp-forge / frontend

Web frontends
1 stars 1 forks source link

Add handler to serve frontend assets #13

Closed thekid closed 3 years ago

thekid commented 3 years ago

Basically the same as web.handlers.FilesFrom (and can serve as a snap-in replacement as seen below), but with support for compressed files (Accept-Encoding: gzip will also check for [file].gz).

Usage

Here's an example of how to refactor your app:

--- a/src/main/php/com/example/skills/App.php
+++ b/src/main/php/com/example/skills/App.php
@@ -3,8 +3,7 @@
 use inject\{Injector, Bindings};
 use security\credentials\{Credentials, FromEnvironment, FromFile};
 use web\frontend\helpers\Dates;
-use web\frontend\{Frontend, HandlersIn, Handlebars};
-use web\handler\FilesFrom;
+use web\frontend\{Frontend, HandlersIn, AssetsFrom, Handlebars};
 use web\rest\{RestApi, ResourcesIn};
 use web\session\{Sessions, InFileSystem, Cookies};
 use web\{Application, Filters};
@@ -30,10 +29,10 @@ class App extends Application {
     $inject->bind(Sessions::class, $sessions);

     $auth= $inject->get(Office365Integration::class)->using($sessions);
-    $files= new FilesFrom($this->environment->path('src/main/webapp'));
+    $assets= new AssetsFrom($this->environment->path('src/main/webapp'));
     return [
-      '/favicon.ico' => $files,
-      '/static'      => $files,
+      '/favicon.ico' => $assets,
+      '/static'      => $assets,
       '/files'       => $auth->required($inject->get(Binaries::class)),
       '/api'         => $auth->required(new RestApi(
         new ResourcesIn('com.example.skills.api', [$inject, 'get'])

Compressing files

The handler will not do this on the fly, because you most probably know what you're able to compress and what not during build time. Our starting point:

$ du -h src/main/webapp/static/*css src/main/webapp/static/*js
12K     src/main/webapp/static/editor.css
1.4M    src/main/webapp/static/vendor.css
376K    src/main/webapp/static/editor.js
796K    src/main/webapp/static/vendor.js

To compress them, use the following (by using -k, we keep the original files, which we'll deliver to all user agents that do not support compression!):

$ gzip -k src/main/webapp/static/*css src/main/webapp/static/*js

The savings are noticeable:

$ du -h src/main/webapp/static/*gz
3.0K    src/main/webapp/static/editor.css.gz
124K    src/main/webapp/static/editor.js.gz
160K    src/main/webapp/static/vendor.css.gz
232K    src/main/webapp/static/vendor.js.gz

Caching

Same API as with FilesFrom seen in xp-forge/web#74 (though not dependant on this pull request being merged!)

// Always use no-cache
$assets= (new AssetsFrom($path))->with(['Cache-Control' => 'no-cache']);

// Decide caching strategy based on file
$assets= (new AssetsFrom($path))->with(function($file) {
  if (strstr($file->filename, '.html')) {
    yield 'Cache-Control' => 'no-cache';
  }
});

See also xp-forge/web#71

Outcome

Before:

image

After:

image

See also https://github.com/xp-forge/frontend/pull/12#issuecomment-808738164

thekid commented 3 years ago

This handler could make use of the new async functionality released in https://github.com/xp-forge/web/releases/tag/v2.8.0

thekid commented 3 years ago

This handler could make use of the new async functionality

...but would break compatibility with our current version constraint "xp-forge/web": "^2.0 | ^1.0", so saving this for the next major release. Plan would be the following: