Хабрахабр

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

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

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

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

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

Полифиллы

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

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

Для этого применяется плагин Silverlight. Например, с помощью полифилла можно сымитировать функционал HTML-элемента Canvas в Microsoft Internet Explorer 7. Причины, по которым разработчики не пользуются исключительно полифиллами, не обращая внимание на встроенные возможности браузеров, заключаются в том, что стандартные возможности браузеров обеспечивают более качественный функционал и более высокую производительность. Средствами полифилла может быть реализована поддержка единиц измерения 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: Модифицируем файл script.js так, чтобы он читал бы данные из 2 файлов и выводил бы все данные в HTML-таблицу.

// ********************************************************************
// Глобальные переменные *
// объявим глобальные переменные, которые будут использоваться в коде *
// ********************************************************************
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 () ); // 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-модули, обрабатывая их так, чтобы с ними можно было бы работать асинхронно. Такую обработку можно применить к коду, в котором используются коллбэки.

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

<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; } };
}

Итоги

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

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

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть