Недавно со мной случилась одна весёлая история. Я сделал веб-проект и расширил возможности уже существующего приложения, которым в моей организации пользуются кадровики. Всё выглядело просто отлично, я радовался тому, что проект был запущен, с нетерпением ожидая благодарственных писем.
Через несколько дней после первого релиза я, и правда, начал получать письма. Но благодарностей в них не наблюдалось. Мне писали менеджеры, работники кадровой службы, и все те, кто пытался воспользоваться моей программой. Все они говорили, что у них приложение работает неправильно.
В чём же дело? А дело в том, что я, создавая проект, тестировал его в 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
У того, что на странице, выводимой некоторыми браузерами, нет данных, две причины:
- Я пользовался промисами и коллбэками, которые поддерживают не все браузеры. Например, среди таких браузеров — IE и Edge.
- Я пользовался методом массивов
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;
}
};
}
Итоги
Полифиллы были особенно актуальны раньше, но и в наши дни они способны помочь в разработке кросс-браузерных веб-проектов. Надеемся, что приведённый здесь пример позволил тем, кто раньше не знал о полифиллах, по-новому взглянуть на проблему создания сайтов, рассчитанных на различные браузеры.
Уважаемые читатели! Пользуетесь ли вы полифиллами?
Автор: ru_vds