mirror of
https://github.com/flarum/framework.git
synced 2025-01-21 21:45:32 +08:00
performance(frontend): Preload FontAwesome, JS and CSS (#3057)
* Add preloads support to Document class
* Add frontend extender for asset preloading
* Provide default preloads for FontAwesome
* Add tests for preload extender and default preloads
* Apply fixes from StyleCI
[ci skip] [skip ci]
* Fix typo
* Fix two more typos 🙃
* Preload core JS and CSS
* Apply fixes from StyleCI
[ci skip] [skip ci]
* Reorder preloads
* Remove singular preloads method
* Use filesystem disk driver for getting FA font paths
* Update test to use full URL
* Apply fixes from StyleCI
[ci skip] [skip ci]
* Address review comment
* Apply fixes from StyleCI
[ci skip] [skip ci]
* Fix typo
* Apply fixes from StyleCI
[ci skip] [skip ci]
* Correct callback wrapping
* Update src/Extend/Frontend.php
Co-authored-by: Sami Mazouz <sychocouldy@gmail.com>
* Update src/Extend/Frontend.php
Co-authored-by: Sami Mazouz <sychocouldy@gmail.com>
* Update src/Extend/Frontend.php
* Fix preload extender logic
* Convert base FontAwesome preloads into a Singleton
* Apply fixes from StyleCI
[ci skip] [skip ci]
Co-authored-by: luceos <luceos@users.noreply.github.com>
Co-authored-by: Sami Mazouz <sychocouldy@gmail.com>
Co-authored-by: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com>
Co-authored-by: Alexander Skvortsov <sasha.skvortsov109@gmail.com>
This commit is contained in:
parent
1637b90531
commit
88724bb4cb
|
@ -16,6 +16,7 @@ use Flarum\Foundation\ContainerUtil;
|
||||||
use Flarum\Foundation\Event\ClearingCache;
|
use Flarum\Foundation\Event\ClearingCache;
|
||||||
use Flarum\Frontend\Assets;
|
use Flarum\Frontend\Assets;
|
||||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||||
|
use Flarum\Frontend\Document;
|
||||||
use Flarum\Frontend\Frontend as ActualFrontend;
|
use Flarum\Frontend\Frontend as ActualFrontend;
|
||||||
use Flarum\Frontend\RecompileFrontendAssets;
|
use Flarum\Frontend\RecompileFrontendAssets;
|
||||||
use Flarum\Http\RouteCollection;
|
use Flarum\Http\RouteCollection;
|
||||||
|
@ -33,6 +34,7 @@ class Frontend implements ExtenderInterface
|
||||||
private $routes = [];
|
private $routes = [];
|
||||||
private $removedRoutes = [];
|
private $removedRoutes = [];
|
||||||
private $content = [];
|
private $content = [];
|
||||||
|
private $preloadArrs = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $frontend: The name of the frontend.
|
* @param string $frontend: The name of the frontend.
|
||||||
|
@ -124,11 +126,45 @@ class Frontend implements ExtenderInterface
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds multiple asset preloads.
|
||||||
|
*
|
||||||
|
* The parameter should be an array of preload arrays, or a callable that returns this.
|
||||||
|
*
|
||||||
|
* A preload array must contain keys that pertain to the `<link rel="preload">` tag.
|
||||||
|
*
|
||||||
|
* For example, the following will add preload tags for a script and font file:
|
||||||
|
* ```
|
||||||
|
* $frontend->preloads([
|
||||||
|
* [
|
||||||
|
* 'href' => '/assets/my-script.js',
|
||||||
|
* 'as' => 'script',
|
||||||
|
* ],
|
||||||
|
* [
|
||||||
|
* 'href' => '/assets/fonts/my-font.woff2',
|
||||||
|
* 'as' => 'font',
|
||||||
|
* 'type' => 'font/woff2',
|
||||||
|
* 'crossorigin' => ''
|
||||||
|
* ]
|
||||||
|
* ]);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param callable|array $preloads
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function preloads($preloads): self
|
||||||
|
{
|
||||||
|
$this->preloadArrs[] = $preloads;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function extend(Container $container, Extension $extension = null)
|
public function extend(Container $container, Extension $extension = null)
|
||||||
{
|
{
|
||||||
$this->registerAssets($container, $this->getModuleName($extension));
|
$this->registerAssets($container, $this->getModuleName($extension));
|
||||||
$this->registerRoutes($container);
|
$this->registerRoutes($container);
|
||||||
$this->registerContent($container);
|
$this->registerContent($container);
|
||||||
|
$this->registerPreloads($container);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function registerAssets(Container $container, string $moduleName): void
|
private function registerAssets(Container $container, string $moduleName): void
|
||||||
|
@ -236,6 +272,25 @@ class Frontend implements ExtenderInterface
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function registerPreloads(Container $container): void
|
||||||
|
{
|
||||||
|
if (empty($this->preloadArrs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$container->resolving(
|
||||||
|
"flarum.frontend.$this->frontend",
|
||||||
|
function (ActualFrontend $frontend, Container $container) {
|
||||||
|
$frontend->content(function (Document $document) use ($container) {
|
||||||
|
foreach ($this->preloadArrs as $preloadArr) {
|
||||||
|
$preloads = is_callable($preloadArr) ? ContainerUtil::wrapCallback($preloadArr, $container)($document) : $preloadArr;
|
||||||
|
$document->preloads = array_merge($document->preloads, $preloads);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private function getModuleName(?Extension $extension): string
|
private function getModuleName(?Extension $extension): string
|
||||||
{
|
{
|
||||||
return $extension ? $extension->getId() : 'site-custom';
|
return $extension ? $extension->getId() : 'site-custom';
|
||||||
|
|
|
@ -122,6 +122,28 @@ class Document implements Renderable
|
||||||
*/
|
*/
|
||||||
public $css = [];
|
public $css = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of preloaded assets.
|
||||||
|
*
|
||||||
|
* Each array item should be an array containing keys that pertain to the
|
||||||
|
* `<link rel="preload">` tag.
|
||||||
|
*
|
||||||
|
* For example, the following will add a preload tag for a FontAwesome font file:
|
||||||
|
* ```
|
||||||
|
* $this->preloads[] = [
|
||||||
|
* 'href' => '/assets/fonts/fa-solid-900.woff2',
|
||||||
|
* 'as' => 'font',
|
||||||
|
* 'type' => 'font/woff2',
|
||||||
|
* 'crossorigin' => ''
|
||||||
|
* ];
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $preloads = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Factory
|
* @var Factory
|
||||||
*/
|
*/
|
||||||
|
@ -203,6 +225,19 @@ class Document implements Renderable
|
||||||
return $this->view->make($this->contentView)->with('content', $this->content);
|
return $this->view->make($this->contentView)->with('content', $this->content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function makePreloads(): array
|
||||||
|
{
|
||||||
|
return array_map(function ($preload) {
|
||||||
|
$attributes = '';
|
||||||
|
|
||||||
|
foreach ($preload as $key => $value) {
|
||||||
|
$attributes .= " $key=\"".e($value).'"';
|
||||||
|
}
|
||||||
|
|
||||||
|
return "<link rel=\"preload\"$attributes>";
|
||||||
|
}, $this->preloads);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
@ -216,6 +251,8 @@ class Document implements Renderable
|
||||||
$head[] = '<link rel="canonical" href="'.e($this->canonicalUrl).'">';
|
$head[] = '<link rel="canonical" href="'.e($this->canonicalUrl).'">';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$head = array_merge($head, $this->makePreloads());
|
||||||
|
|
||||||
$head = array_merge($head, array_map(function ($content, $name) {
|
$head = array_merge($head, array_map(function ($content, $name) {
|
||||||
return '<meta name="'.e($name).'" content="'.e($content).'">';
|
return '<meta name="'.e($name).'" content="'.e($content).'">';
|
||||||
}, $this->meta, array_keys($this->meta)));
|
}, $this->meta, array_keys($this->meta)));
|
||||||
|
|
|
@ -54,9 +54,58 @@ class FrontendServiceProvider extends AbstractServiceProvider
|
||||||
$frontend->content($container->make(Content\CorePayload::class));
|
$frontend->content($container->make(Content\CorePayload::class));
|
||||||
$frontend->content($container->make(Content\Meta::class));
|
$frontend->content($container->make(Content\Meta::class));
|
||||||
|
|
||||||
|
$frontend->content(function (Document $document) use ($container) {
|
||||||
|
$default_preloads = $container->make('flarum.frontend.default_preloads');
|
||||||
|
|
||||||
|
// Add preloads for base CSS and JS assets. Extensions should add their own via the extender.
|
||||||
|
$js_preloads = [];
|
||||||
|
$css_preloads = [];
|
||||||
|
|
||||||
|
foreach ($document->css as $url) {
|
||||||
|
$css_preloads[] = [
|
||||||
|
'href' => $url,
|
||||||
|
'as' => 'style'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
foreach ($document->js as $url) {
|
||||||
|
$css_preloads[] = [
|
||||||
|
'href' => $url,
|
||||||
|
'as' => 'script'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$document->preloads = array_merge(
|
||||||
|
$css_preloads,
|
||||||
|
$js_preloads,
|
||||||
|
$default_preloads,
|
||||||
|
$document->preloads,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return $frontend;
|
return $frontend;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$this->container->singleton(
|
||||||
|
'flarum.frontend.default_preloads',
|
||||||
|
function (Container $container) {
|
||||||
|
$filesystem = $container->make('filesystem')->disk('flarum-assets');
|
||||||
|
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'href' => $filesystem->url('fonts/fa-solid-900.woff2'),
|
||||||
|
'as' => 'font',
|
||||||
|
'type' => 'font/woff2',
|
||||||
|
'crossorigin' => ''
|
||||||
|
], [
|
||||||
|
'href' => $filesystem->url('fonts/fa-regular-400.woff2'),
|
||||||
|
'as' => 'font',
|
||||||
|
'type' => 'font/woff2',
|
||||||
|
'crossorigin' => ''
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
93
tests/integration/extenders/FrontendPreloadTest.php
Normal file
93
tests/integration/extenders/FrontendPreloadTest.php
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Tests\integration\extenders;
|
||||||
|
|
||||||
|
use Flarum\Extend;
|
||||||
|
use Flarum\Testing\integration\TestCase;
|
||||||
|
|
||||||
|
class FrontendPreloadTest extends TestCase
|
||||||
|
{
|
||||||
|
private $customPreloadUrls = ['/my-preload', '/my-preload2'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function default_preloads_are_present()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/')
|
||||||
|
);
|
||||||
|
|
||||||
|
$filesystem = $this->app()->getContainer()->make('filesystem')->disk('flarum-assets');
|
||||||
|
|
||||||
|
$urls = [
|
||||||
|
$filesystem->url('fonts/fa-solid-900.woff2'),
|
||||||
|
$filesystem->url('fonts/fa-regular-400.woff2'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$body = $response->getBody()->getContents();
|
||||||
|
|
||||||
|
foreach ($urls as $url) {
|
||||||
|
$this->assertStringContainsString("<link rel=\"preload\" href=\"$url\" as=\"font\" type=\"font/woff2\" crossorigin=\"\">", $body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function preloads_can_be_added()
|
||||||
|
{
|
||||||
|
$urls = $this->customPreloadUrls;
|
||||||
|
|
||||||
|
$this->extend(
|
||||||
|
(new Extend\Frontend('forum'))
|
||||||
|
->preloads(
|
||||||
|
array_map(function ($url) {
|
||||||
|
return ['href' => $url];
|
||||||
|
}, $urls)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/')
|
||||||
|
);
|
||||||
|
$body = $response->getBody()->getContents();
|
||||||
|
|
||||||
|
foreach ($urls as $url) {
|
||||||
|
$this->assertStringContainsString("<link rel=\"preload\" href=\"$url\">", $body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function preloads_can_be_added_via_callable()
|
||||||
|
{
|
||||||
|
$urls = $this->customPreloadUrls;
|
||||||
|
|
||||||
|
$this->extend(
|
||||||
|
(new Extend\Frontend('forum'))
|
||||||
|
->preloads(function () use ($urls) {
|
||||||
|
return array_map(function ($url) {
|
||||||
|
return ['href' => $url];
|
||||||
|
}, $urls);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/')
|
||||||
|
);
|
||||||
|
$body = $response->getBody()->getContents();
|
||||||
|
|
||||||
|
foreach ($urls as $url) {
|
||||||
|
$this->assertStringContainsString("<link rel=\"preload\" href=\"$url\">", $body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user