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\Frontend\Assets;
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
use Flarum\Frontend\Document;
|
||||
use Flarum\Frontend\Frontend as ActualFrontend;
|
||||
use Flarum\Frontend\RecompileFrontendAssets;
|
||||
use Flarum\Http\RouteCollection;
|
||||
|
@ -33,6 +34,7 @@ class Frontend implements ExtenderInterface
|
|||
private $routes = [];
|
||||
private $removedRoutes = [];
|
||||
private $content = [];
|
||||
private $preloadArrs = [];
|
||||
|
||||
/**
|
||||
* @param string $frontend: The name of the frontend.
|
||||
|
@ -124,11 +126,45 @@ class Frontend implements ExtenderInterface
|
|||
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)
|
||||
{
|
||||
$this->registerAssets($container, $this->getModuleName($extension));
|
||||
$this->registerRoutes($container);
|
||||
$this->registerContent($container);
|
||||
$this->registerPreloads($container);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
return $extension ? $extension->getId() : 'site-custom';
|
||||
|
|
|
@ -122,6 +122,28 @@ class Document implements Renderable
|
|||
*/
|
||||
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
|
||||
*/
|
||||
|
@ -203,6 +225,19 @@ class Document implements Renderable
|
|||
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
|
||||
*/
|
||||
|
@ -216,6 +251,8 @@ class Document implements Renderable
|
|||
$head[] = '<link rel="canonical" href="'.e($this->canonicalUrl).'">';
|
||||
}
|
||||
|
||||
$head = array_merge($head, $this->makePreloads());
|
||||
|
||||
$head = array_merge($head, array_map(function ($content, $name) {
|
||||
return '<meta name="'.e($name).'" content="'.e($content).'">';
|
||||
}, $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\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;
|
||||
};
|
||||
});
|
||||
|
||||
$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