Ресурсы в архиве или как уменьшить количество подгружаемых файлов

в 10:30, , рубрики: html, javascript, php, web-разработка, Веб-разработка

Ранним жарким утром спросонья пришла в голову идея. Подозреваю, что у этой идеи есть минусы либо ее уже реализовали куда более годным способом — но, авось да пригодится кому-нибудь.

Очень часто веб-разработчики сталкиваются с проблемой большого количества мелких файлов. Картинки, скрипты, css — неудобно, все дела. Нужно как-то бороться с этим. import, спрайты, блаблабла — это хорошо, но можно попробовать и иначе. Мой вариант — упаковка всех необходимых ресурсов в архив на стороне сервера(реализация — на php), получение данных на клиенте, установка ресурсов в нужных местах.

Проблемы на данный момент: не придумал толком, как кешировать полученный архив, таким способом не стоит паковать динамический контент или большие файлы.
Для распаковки архива на клиенте использована библиотека JSZip, для удобства — jQuery.

Итак, посмотрим на код.

ziplogic.php:

class ZipLogic extends ZipArchive{
        public $filename;
        public function __construct() {
            $filename = "current/latest.zip";
            $this->filename = $filename;
            if(file_exists($filename))                
                unlink($filename);
            
            if ($this->open($filename, ZIPARCHIVE::CREATE)!==TRUE) {
                $error = error_get_last();
                echo $error["message"];
                return null;
            }
        }
        
        public function pack($url){
            $this->addFile($url);
        }
    }

Этот класс наследуется от ZipArchive и нужен только для чуть более удобной работы с архивом.

Теперь — код для упаковки нужных ресурсов:

index.php:

    include "ziplogic.php";
    $zpack = new ZipLogic();
    if($zpack==null)
    {
        echo 'wrong data';
        return;
    }
    $zpack->pack('img/screen.png');
    $zpack->pack('img/screen_1.png');
    $zpack->pack('img/screen_2.png');
    $zpack->pack('css/alotofstyles.css');
    $zpack->pack('js/cooljs.js');
    $zpack->close();
    include 'content.html';

Тут, собственно, тоже нет ничего особо интересного: создаем объект ZipLogic, перечисляем все ссылки на ресурсы, которые нужно упаковать, закрываем объект, подключаем конечную html'ку. В результате в папке current создается архив latest.zip, который содержит в себе все нужные ресурсы(см. ziplogic.php).
По хорошему, использовать его нужно перед публикацией страницы, каждый раз генерировать нет смысла.

Ну и на закуску — сама html'ка. Верстку не критиковать, пожалуйста — ее здесь нет.

content.html:

<script src="js/jquery-1.9.1.js"></script>
<script src="js/jszip.min.js"></script>

<link type="text/css" zpack="css/alotofstyles.css" rel="stylesheet">
<script zpack="js/cooljs.js"></script>
<style>
    body{margin: 0;}
</style>

<div id="loader" style="position: absolute; width: 100%; height: 100%;background: rgba(148, 148, 148, 1);">
    <img src="" style="
        position: relative;
        top: 50%;
        left: 50%;
        height: 25px;
        width: 250px;
        margin-left: -125px;
        margin-top: -12.5px;
    ">
</div>


<img zpack="img/screen.png"/>
<img zpack="img/screen_1.png"/>
<img zpack="img/screen_2.png"/>

<script>
    function getBinary(url, callback){
        var oReq = new XMLHttpRequest();
        oReq.open("GET", url, true);
        oReq.responseType = "arraybuffer";

        oReq.onload = function (oEvent) {
          var arrayBuffer = oReq.response;
          if (arrayBuffer) {
            var byteArray = new Uint8Array(arrayBuffer);
            callback(byteArray);
          }
        };

        oReq.send(null);   
    }
    
    function unpackResources(data){
        var pack = new JSZip();
        pack.load(data);
        
        $.each(pack.files, function(k, v){
            var blob = new Blob([pack.file(k).asArrayBuffer()], {type: 'application/octet-binary'});
            var url = URL.createObjectURL(blob);
            $('[zpack="'+k+'"').each(function(){
                switch($(this).prop("tagName").toLowerCase())
                {
                    case 'img' :
                        $(this).attr('src', url);
                        break;
                    case 'script' :
                        $(this).attr('src', url);
                        break;   
                    case 'link' :
                        $(this).attr('href', url);
                        break;  
                    case 'a' :
                        $(this).attr('href', url);
                        break;                           
                }

            });
        });
    }
    
    $(document).ready(function(){
        getBinary('current/latest.zip', function(data){
                unpackResources(data);
                $('#loader').hide();
        });
    });
</script>

Тут стоит описать чуть подробнее.
Функция getBinary, если не ошибаюсь, честно сперта со stackoverflow. Скачивает архив и передает полученный массив байт в callback.
Функция unpackResources, собственно, и делает основную часть работы: обрабатывает содержимое архива, вытаскивает пути, находит все элементы, ресурсы в которых помечены через zpack, создает соотв. blob и подгружает его в нужный элемент.
Пока все это добро не подгрузится, висит загрузчик.

В итоге получаем довольно простой код, позволяющий упаковать большое количество ресурсов в 1 архив и распаковать его на стороне клиента.

Спасибо за внимание, комментарии и идеи крайне приветствуются, критика без использования слов «идиот» и «криворукий ламер» — тоже.

Автор: Demogor

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js