Использование полифиллов при написании кросс-браузерных приложений +23


Недавно со мной случилась одна весёлая история. Я сделал веб-проект и расширил возможности уже существующего приложения, которым в моей организации пользуются кадровики. Всё выглядело просто отлично, я радовался тому, что проект был запущен, с нетерпением ожидая благодарственных писем.

Через несколько дней после первого релиза я, и правда, начал получать письма. Но благодарностей в них не наблюдалось. Мне писали менеджеры, работники кадровой службы, и все те, кто пытался воспользоваться моей программой. Все они говорили, что у них приложение работает неправильно.



В чём же дело? А дело в том, что я, создавая проект, тестировал его в Chrome. Но пользователи этого проекта постоянно применяют Firefox и IE. Работа с моим приложением не стала исключением. Мне, в итоге, было совсем невесело от того, что проект, запущенный пару дней назад, надо было дорабатывать.

Собственно говоря, тут мне на помощь и пришли полифиллы.

Полифиллы


Полифилл (polyfill или polyfiller) — это фрагмент кода (или некий плагин), реализующий то, наличия чего разработчик ожидает среди стандартных возможностей браузера. Полифиллы позволяют, так сказать, «сгладить» неровности браузерных API.

В веб-среде полифиллы обычно представлены JavaScript-кодом. Этот код используется для оснащения устаревших браузеров современными возможностями, которые эти браузеры не поддерживают.

Например, с помощью полифилла можно сымитировать функционал HTML-элемента Canvas в Microsoft Internet Explorer 7. Для этого применяется плагин Silverlight. Средствами полифилла может быть реализована поддержка единиц измерения rem в CSS, или атрибута text-shadow, или чего угодно другого. Причины, по которым разработчики не пользуются исключительно полифиллами, не обращая внимание на встроенные возможности браузеров, заключаются в том, что стандартные возможности браузеров обеспечивают более качественный функционал и более высокую производительность. Собственные браузерные реализации различных API обладают более широкими возможностями, чем полифиллы, да и работают быстрее.

Иногда полифиллы используются для решения проблем, связанных с тем, что различные браузеры по-разному реализуют одни и те же возможности. Подобные полифиллы взаимодействуют с некоторыми браузерами, используя их нестандартные особенности, и дают другим JavaScript-программам доступ к определённым механизмам, соответствующий стандартам. Надо отметить, что подобные причины использования полифиллов сегодня уже не так актуальны, как раньше. Особую распространённость полифиллы имели во времена IE6, Netscape и NNav, когда каждый браузер реализовывал возможности JavaScript не так, как другие.

Пример


Недавно я опубликовал руководство по разработке приложения, конвертирующего CSV и Excel-файлы в JSON с помощью JavaScript. Здесь можно посмотреть на готовое приложение.

Для того чтобы разобраться с тем, о чём мы будем говорить дальше, вы можете либо сделать всё то, о чём идёт речь в руководстве, либо клонировать мой репозиторий следующей командой:

git clone https://github.com/YannMjl/jsdemo-read-cvs-xls-json
cd jsdemo-read-cvs-xls-json

Рекомендую в процессе работы пользоваться VS Code. Запустить веб-приложение можно локально, с использованием расширения для VS Code Live Server.

Давайте модифицируем это веб-приложение и посмотрим на проблемы, которые возникают при работе с ним с использованием разных браузеров.

Создадим в репозитории ветку polyfill и переключимся на неё:

git checkout -b polyfill

Я собираюсь исследовать ситуацию, в которой мы получаем данные из двух или большего количества CSV-файлов, и, после завершения обработки результатов запросов к соответствующим API, выводим эти данные в HTML-таблицу.

?Доработка проекта


Создадим в корневой директории проекта новый CSV-файл (team2.csv), в результате чего там должно оказаться два файла. Вот файл, который я добавил в проект.

Модифицируем файл script.js так, чтобы он читал бы данные из 2 файлов и выводил бы все данные в HTML-таблицу. Вот мой script.js:

// ********************************************************************
// Глобальные переменные                                              *
// объявим глобальные переменные, которые будут использоваться в коде *
// ********************************************************************
var csv_file_API_1 = "./UsersSample.csv";
var csv_file_API_2 = "./team2.csv";
var APIs_array = [csv_file_API_1, csv_file_API_2];

// Выполняем некие действия при загрузке страницы
$(document).ready(function () {

    $("#headerTitle").hide(300).show(1500);

    makeAPICalls();

}); // end: document.ready()

function makeAPICalls() {

    // Массив, который будет содержать обращения к API
    var calls = [];

    // Передача API для CSV-файлов коллбэкам
    APIs_array.forEach(function (csv_file_API) {

        // Помещение промиса в массив вызовов
        calls.push(new Promise(function (resolve, reject) {

            // Выполнение вызова API с использованием AJAX
            $.ajax({

                type: "GET",

                url: csv_file_API,

                dataType: "text",

                cache: false,

                error: function (e) {
                    alert("An error occurred while processing API calls");
                    console.log("API call Failed: ", e);
                    reject(e);
                },

                success: function (data) {

                    var jsonData = $.csv.toObjects(data);

                    console.log(jsonData);

                    resolve(jsonData);
                } // end: обработка данных при успешном обращении к API

            }); // end: AJAX-вызов

        })); // end: добавление данных при вызове промисов

    }); // end: обход массива API


    // После завершения всех обращений к API
    Promise.all(calls).then(function (data) {

        // объединим все данные
        var flatData = data.map(function (item) {
            return item;
        }).flat();

        console.log(flatData);

        dislayData(flatData);
    });

}

function dislayData(data) {
    
    $.each(data, function (index, value) {

        $('#showCSV').append(

            '<li class="list-group-item d-flex justify-content-between align-items-center">' +

                '<span style="width: 15%; font-size: 1rem; font-weight: bold; color: #37474F">' +
                    value['FIRST NAME'] +
                '</span>' +

                '<span style="width: 15%; font-size: 1rem;  color: #37474F">' +
                    value['LAST NAME'] +
                '</span>' +

                '<span class="badge warning-color-dark badge-pill">' +
                    value['PHONE NUMBER'] +
                '</span>' +

                '<span class="badge success-color-dark badge-pill">' +
                    value['EMAIL ADDRESS'] +
                '</span>' +

                '<span class="badge badge-primary badge-pill">' +
                    value.CITY +
                '</span>' +

                '<span class="badge badge-primary badge-pill">' +
                    value.STATE +
                '</span>' +

            '</li>'
        );

    });
}

Теперь, скопировав адрес страницы, откройте проект во всех браузерах, которые у вас есть. В моём случае это были Internet Explorer, Firefox Mozilla, Microsoft Edge и Google Chrome. Оказалось, что приложение перестало нормально работать в Internet Explorer и Microsoft Edge. Там выводились только заголовки.


Страница проекта в Chrome


Страница проекта в Microsoft Edge

У того, что на странице, выводимой некоторыми браузерами, нет данных, две причины:

  1. Я пользовался промисами и коллбэками, которые поддерживают не все браузеры. Например, среди таких браузеров — IE и Edge.
  2. Я пользовался методом массивов flat() для того, чтобы создать из существующего массива новый «плоский» массив. Этот метод не поддерживается некоторыми браузерами. Среди них, как и в предыдущем случае, IE и Edge.

?Применение полифиллов


Исправим проблему промисов и коллбэков, воспользовавшись библиотекой Bluebird. Это — полномасштабная JS-реализация механизмов, связанных с промисами. Самая интересная особенность библиотеки Bluebird заключается в том, что она позволяет «промисифицировать» другие Node-модули, обрабатывая их так, чтобы с ними можно было бы работать асинхронно. Такую обработку можно применить к коду, в котором используются коллбэки.

Загрузим библиотеку Bluebird на страницу, воспользовавшись соответствующим CDN-ресурсом. Для этого разместим в заголовке файла index.html (в элементе head) следующее:

<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.7.0/bluebird.min.js"></script>

Для того чтобы исправить проблему, касающуюся метода массивов flat(), добавим в верхнюю часть файла script.js следующий код:

Object.defineProperty(Array.prototype, 'flat',
    {
        value: function (depth) {
            depth = 1;
            return this.reduce(
                function (flat, toFlatten) {
                    return flat.concat((Array.isArray(toFlatten) && (depth > 1)) ? toFlatten.flat(depth - 1) : toFlatten);
                }, []
            );
        },
        configurable: true
});

Теперь приложение должно работать во всех браузерах так, как ожидается. Вот, например, как оно теперь выглядит в Microsoft Edge.


Страница доработанного проекта в Microsoft Edge

Я развернул этот проект здесь. Можете его испытать.

Если у вас не получилось добиться работоспособности проекта — загляните в мой репозиторий.

А вот — для примера — ещё пара полифиллов.

// полифилл для String.prototype.startsWith()
if (!String.prototype.startsWith) {
    Object.defineProperty(String.prototype, 'startsWith', {
        value: function (search, rawPos) {
            pos = rawPos > 0 ? rawPos | 0 : 0;
            return this.substring(pos, pos + search.length) === search;
        }
    });
}

// полифилл для String.prototype.includes()
if (!String.prototype.includes) {
    String.prototype.includes = function (search, start) {
        'use strict';
        if (typeof start !== 'number') {
            start = 0;
        }

        if (start + search.length > this.length) {
            return false;
        } else {
            return this.indexOf(search, start) !== -1;
        }
    };
}

Итоги


Полифиллы были особенно актуальны раньше, но и в наши дни они способны помочь в разработке кросс-браузерных веб-проектов. Надеемся, что приведённый здесь пример позволил тем, кто раньше не знал о полифиллах, по-новому взглянуть на проблему создания сайтов, рассчитанных на различные браузеры.

Уважаемые читатели! Пользуетесь ли вы полифиллами?





К сожалению, не доступен сервер mySQL