Optimalizace, úklid a ať je to hezčí

Tak redakční systém už něco umí. Sice je toho málo, jen administraci stránek, ale máme tam dost bordelu - tu složku bower_component duplicitně, v administračním prostředí spousta nesmyslných řádků v levém menu...

Čili je na čase uklidit. Taky je dobré myslet na optimalizaci rychlosti načítání. Čili naučíme se minimalizovat css a js zdroje a sloučit všechny do jednoho.

Začneme tedy smazáním těch položek menu v levém panelu administrace. Takže otevři @layout.latte a smaž následující bloky. Čili ne jen řádky, ale opravdu celé bloky:

<div class="navbar-custom-menu">
<div class="user-panel">
<form action="#" method="get" class="sidebar-form">
<ul class="sidebar-menu" data-widget="tree">
<aside class="control-sidebar control-sidebar-dark" style="display: none;">

Následně pak do elementu <section class="sidebar"> vlož seznam:

            <ul class="sidebar-menu" data-widget="tree">
                <li n:class="$presenter->isLinkCurrent(':Page:Admin:Page:default') ? active">
                    <a n:href=":Page:Admin:Page:default"><i class="fa fa-file"></i>
                        <span>Textové stránky</span></a>
                </li>
            </ul>

A teď větší legrace: minifikace a sjednocení css a js souborů. V tuto chvíli bychom museli vždy načítat všechny css a js zdroje i v případě, že je nebudeme v daném modulu používat. Máme prostě jednu šablonu layoutu, kde načítáme zdroje. Jestli v jednom z modulů budeme potřebovat třeba kalendář z Bootstrapu, museli bychom css a js prostě načíst i v případě zobrazení jiných modulů.

Řešením je komponenta, kterou si každý konkrétní presenter vytvoří a která načte jen potřebné zdroje. Opět tedy budeme vyrábět komponentu. Ve složce BaseModule/Components vyrob složku Css. V ní soubor ICssFactory.php:

<?php

namespace BaseModule\Components;

interface ICssFactory
{
    /**
     * @return Css
     */
    public function create();
}

Pak třídu Css.php:

<?php

namespace BaseModule\Components;

use Nette\Application\UI\Control;
use WebLoader\Compiler;
use WebLoader\FileCollection;
use WebLoader\Filter\LessFilter;
use WebLoader\Nette\CssLoader;

/**
 * Class Css
 *
 * @package BaseModule\Components;
 */
class Css extends Control
{
    const WWW_DIR = __DIR__ . '/../../../../www/';

    /** @var string */
    private $media = 'screen'; //'screen,projection,tv,print'

    /** @var array */
    private $styles = [];

    /** @var array */
    private $remoteStyles = [];

    /** @var string */
    private $templateFile = __DIR__ . '/Css.latte';

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * @param string $media
     */
    public function setMedia($media)
    {
        $this->media= $media;
    }

    /**
     * @param array $styles
     */
    public function setStyles($styles)
    {
        $this->styles= $styles;
    }

    /**
     * @param array $remoteStyles
     */
    public function setRemoteStyles($remoteStyles)
    {
        $this->remoteStyles= $remoteStyles;
    }

    public function render()
    {
        $this->template->setFile($this->templateFile);
        $this->template->render();
    }

    /**
     * @return \WebLoader\Nette\CssLoader
     * @throws \WebLoader\InvalidArgumentException
     */
    protected function createComponentCss()
    {
        $files = new FileCollection(self::WWW_DIR . '/css');

        $files->addFiles($this->styles);
        $files->addRemoteFiles($this->remoteStyles);

        $compiler = Compiler::createCssCompiler($files, self::WWW_DIR . '/css/webtemp');
        $compiler->addFileFilter(new LessFilter());

        $compiler->addFilter(
            function ($code) {
                $minifier = new \MatthiasMullie\Minify\CSS();
                $minifier->add($code);

                return $minifier->minify();
            }
        );

        $control = new CssLoader($compiler, $this->template->basePath . '/css/webtemp');
        $control->setMedia($this->media);

        return $control;
    }
}

A šablona Css.latte:

{control css}

Následuje registrace služby, takže do BaseModule/DI/services.neon přidej řádek:

    - BaseModule\Components\ICssFactory

V BasePresenteru si službu injectneme, plus nastavíme pár cest:

    /** @var ICssFactory @inject */
    public $cssFactory;

    /** @var string */
    protected $bowerDir = __DIR__ . '/../../../../vendor/bower_components/';

    /** @var string */
    protected $srcDir = __DIR__ . '/../../../../src/';

A pak vyrobíme komponentu, ve které načteme všechny css soubory, které do teď načítáme v <head> šablony @layout.latte:

    /**
     * @return \BaseModule\Components\Css
     */
    protected function createComponentCss()
    {
        $control = $this->cssFactory->create();
        $control->setStyles(
            [
                $this->bowerDir . 'bootstrap/dist/css/bootstrap.min.css',
                $this->bowerDir . 'font-awesome/css/font-awesome.min.css',
                $this->bowerDir . 'Ionicons/css/ionicons.min.css',
                $this->bowerDir . 'admin-lte/dist/css/AdminLTE.min.css',
                $this->bowerDir . 'admin-lte/dist/css/skins/_all-skins.min.css',
                $this->bowerDir . 'morris.js/morris.css',
                $this->bowerDir . 'jvectormap/jquery-jvectormap.css',
                $this->bowerDir . 'happy/dist/happy.css',
                $this->bowerDir . 'ublaboo-datagrid/assets/datagrid.css',
                $this->bowerDir . 'ublaboo-datagrid/assets/datagrid-spinners.css',
                $this->bowerDir . 'nprogress/nprogress.css',
                $this->srcDir . 'BaseModule/assets/css/admin/style.css',
            ]
        );

        $control->setRemoteStyles(
            [
            ]
        );

        return $control;
    }

V layoutu smaž všechny řádky, které načítají css soubory a vlož jen vytvořenou komponentu:

{control css}

Pokud bude potřeba, můžeš pak v různých presenterech přetížit metodu createComponentCss() a vložit vlastní css. Stejný trik provedeme se soubory js. Takže vytvoř složku Js a v ní soubor IJsFactory.php:

<?php

namespace BaseModule\Components;

interface IJsFactory
{

    /**
     * @return Js
     */
    public function create();
}

Pak soubor Js.php:

<?php

namespace BaseModule\Components;

use Nette\Application\UI\Control;
use WebLoader\Compiler;
use WebLoader\FileCollection;
use WebLoader\Filter\LessFilter;
use WebLoader\Nette\JavaScriptLoader;

/**
 * Class Js
 *
 * @package AppModule\Components;
 */
class Js extends Control
{
    const WWW_DIR = __DIR__ . '/../../../../www/';

    /** @var array */
    private $scripts = [];

    /** @var array */
    private $remoteScripts = [];

    /** @var string */
    private $templateFile = __DIR__ . '/Js.latte';

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * @param array $scripts
     */
    public function setScripts($scripts)
    {
        $this->scripts= $scripts;
    }

    /**
     * @param array $remoteScripts
     */
    public function setRemoteScripts($remoteScripts)
    {
        $this->remoteScripts= $remoteScripts;
    }

    /**
     * Method render
     */
    public function render()
    {
        $this->template->setFile($this->templateFile);
        $this->template->render();
    }

    /**
     * @return \WebLoader\Nette\JavaScriptLoader
     * @throws \WebLoader\InvalidArgumentException
     */
    protected function createComponentJs()
    {
        $files = new FileCollection(self::WWW_DIR . '/js');

        $files->addFiles($this->scripts);
        $files->addRemoteFiles($this->remoteScripts);

        $compiler = Compiler::createJsCompiler($files, self::WWW_DIR . '/js/webtemp');
        $compiler->addFileFilter(new LessFilter());

        $compiler->addFilter(
            function ($code) {
                $minifier = new \MatthiasMullie\Minify\JS();
                $minifier->add($code);

                return $minifier->minify();
            }
        );

        $control = new JavaScriptLoader($compiler, $this->template->basePath . '/js/webtemp');

        return $control;
    }
}

A naposledy šablona Js.latte:

{control js}

Následuje registrace služby, takže do BaseModule/DI/services.neon přidej řádek:

    - BaseModule\Components\IJsFactory

No a samozřejme do BasePresenteru injektáž služby:

    /** @var \BaseModule\Components\IJsFactory @inject */
    public $jsFactory;

a metodu pro vytvoření komponenty:

    /**
     * @return \BaseModule\Components\Js
     */
    protected function createComponentJs()
    {
        $control = $this->jsFactory->create();
        $control->setScripts(
            [
                $this->bowerDir . 'jquery/dist/jquery.min.js',
                $this->bowerDir . 'jquery-ui/jquery-ui.min.js',
                $this->bowerDir . 'nette.ajax.js/nette.ajax.js',
                $this->bowerDir . 'jquery.cookie/jquery.cookie.js',
                $this->bowerDir . 'bootstrap/dist/js/bootstrap.min.js',
                $this->bowerDir . 'raphael/raphael.min.js',
                $this->bowerDir . 'morris.js/morris.min.js',
                $this->bowerDir . 'admin-lte/dist/js/adminlte.min.js',
                $this->bowerDir . 'ublaboo-datagrid/assets/datagrid.js',
                $this->bowerDir . 'ublaboo-datagrid/assets/datagrid-instant-url-refresh.js',
                $this->bowerDir . 'ublaboo-datagrid/assets/datagrid-spinners.js',
                $this->bowerDir . 'prism/prism.js',
                $this->bowerDir . 'happy/dist/happy.js',
                $this->bowerDir . 'nprogress/nprogress.js',
                $this->bowerDir . 'nprogress/nette.nprogress.js',
            ]
        );
        $control->setRemoteScripts(
            [
            ]
        );

        return $control;
    }

No a zase smažeme všechny řádky na konci šablony, které načítají js soubory a nahradíme jen komponentou:

{control js}

Dost možná si budeš muset postahovat nějaké knihovny, pokud nebudou v bower_component. A taky se občas stane, že css nebo js mají v sobě odkazy na další zdroje, jako obrázky, fonty, další js soubory atd. To pak budeš muset pořešit vlastními silami - nahrát potřebné fonty do složky www/css/fonts apod.

Je na čase smazat celou složku www/bower_components. Už ji nebudeme potřebovat, protože si zdroje taháme ze složky src/bower_components. Na druhou stranu musíš vytvořit složky www/css/webtemp a www/js/webtemp. Do těchto složek se budou vytvářet a ukládat komprimované zdroje.

To je pro dnešek vše, a jako obvykle nabízím stažení celé aplikace. Dnes opravdu celé.

Celý seriál o vývoji redakčního systému

Komentáře

O mně

Jmenuji se Rudolf Svátek - lektor výpočetní techniky, trochu PHP programátor a SEO konzultant na volné noze.

Adresa

Příčná 326/3
736 01 Havířov

Kontakty

Email: office@rudolfsvatek.cz
Telefon: +420 777 828 353
Skype: svatekr