[Перевод] Использование полифиллов при написании кросс-браузерных приложений

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

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

uhraetarp4ufpt8tvdceqkkoi3i.jpeg

В чём же дело? А дело в том, что я, создавая проект, тестировал его в 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(

            '
  • ' +                 '' +                     value['FIRST NAME'] +                 '' +                 '' +                     value['LAST NAME'] +                 '' +                 '' +                     value['PHONE NUMBER'] +                 '' +                 '' +                     value['EMAIL ADDRESS'] +                 '' +                 '' +                     value.CITY +                 '' +                 '' +                     value.STATE +                 '' +             '
  • '         );     }); }


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

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

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

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

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


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


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

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


    Для того чтобы исправить проблему, касающуюся метода массивов 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.

    c7f2ae6f88ec375a2a005fd6998f8387.png
    Страница доработанного проекта в 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;
            }
        };
    }


    Итоги


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

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

    -o2etuqogwhmdnmysb9_vivc9v4.png


    1ba550d25e8846ce8805de564da6aa63.png

    © Habrahabr.ru