Теговані шаблонні рядки
Тегований шаблонний рядок, це шаблонний рядок, перед яким записано ім'я функції; замість автоматичного склеювання JavaScript викликає цю функцію і передає їй частини рядка та підставлені значення окремо. Тег сам вирішує, що робити зі значеннями, тому таку конструкцію використовують для екранування, форматування та власних міні-мов на кшталт CSS-in-JS.
Теорія
TL;DR
- Звичайний шаблон просто вставляє значення, тегований віддає контроль функції.
- Функція-тег отримує два набори даних: масив текстових частин і список підставлених значень.
tag`Hello, ${name}!`виконується якtag(['Hello, ', '!'], name).- Повертати тег може будь-що, не обов'язково рядок.
- Головні застосування: безпечний HTML, CSS-in-JS, GraphQL-запити, форматування, i18n.
Швидкий приклад
function tag(strings, ...values) {
console.log(strings); // [ 'Hello, ', '!' ]
console.log(values); // [ 'Alex' ]
}
const name = 'Alex';
tag`Hello, ${name}!`;Що відбувається під капотом:
// Інтерпретатор робить приблизно це:
tag(['Hello, ', '!'], 'Alex');Як це виглядає і що отримує тег
Звичайний шаблонний рядок просто підставляє змінні:
const name = 'Alex';
console.log(`Hello, ${name}!`);
// Hello, Alex!Але якщо перед шаблонним рядком вказати ім'я функції, JavaScript викличе цю функцію і передасть у неї:
- Масив частин рядка (тексту між підстановками).
- Усі підставлені значення з
${...}.
tag`Hello, ${name}!`Це і є тегований шаблон. Отже:
strings, це масив усіх шматків тексту між вставками;values, це масив усіх вставлених значень, зібраний rest-параметром.
Текстових частин завжди на одну більше, ніж значень, бо шаблон починається і закінчується текстом (можливо, порожнім).
Обробка вмісту: власний шаблонізатор
Тег-функцію можна використати для створення власних шаблонних мов або форматування рядків.
function highlight(strings, ...values) {
return strings.reduce(
(result, str, i) => result + str + (values[i] ? `<b>${values[i]}</b>` : ''),
''
);
}
const name = 'Alex';
const lang = 'JavaScript';
const message = highlight`Hello, ${name}! You love ${lang}.`;
console.log(message);
// Hello, <b>Alex</b>! You love <b>JavaScript</b>.Тут ми «зловили» кожну вставку ${...} і загорнули її у <b>...</b>.
Так само легко зробити форматувальник:
function format(strings, ...values) {
return strings.reduce(
(res, str, i) => res + str + (values[i] !== undefined ? `[${values[i]}]` : ''),
''
);
}
const a = 10, b = 20;
console.log(format`Sum of ${a} and ${b} is ${a + b}`);
// Sum of [10] and [20] is [30]Захист від XSS: безпечний HTML
Найкорисніший сценарій: тег екранує все, що прийшло ззовні, тому розмітка лишається безпечною.
function safeHTML(strings, ...values) {
const escape = str =>
String(str)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>');
return strings.reduce(
(res, str, i) => res + str + (values[i] ? escape(values[i]) : ''),
''
);
}
const userInput = '<script>alert("Hacked!")</script>';
const html = safeHTML`<div>User says: ${userInput}</div>`;
console.log(html);
// <div>User says: <script>alert("Hacked!")</script></div>Завдяки тегу HTML лишається безпечним, ніякого виконання стороннього скрипта. Важливо, що екрануються саме значення, а статичний текст шаблону, який написав розробник, лишається недоторканим.
Реальні застосування
-
Styled Components (React)
javascriptconst Button = styled.button` background: ${props => props.primary ? 'blue' : 'gray'}; color: white; `;Тут
styled.button, це тег-функція, яка отримує шаблон і будує з нього CSS. -
GraphQL-запити
javascriptconst query = gql` query GetUser { user(id: 1) { name } } `;Тег
gqlпарсить текст запиту в AST ще до відправлення на сервер. -
Інтернаціоналізація (i18n)
javascripttranslate`Welcome, ${userName}!`;Тег знаходить потрібний переклад за статичною частиною і підставляє значення в нього.
Порівняння зі звичайним шаблонним рядком
| Особливість | Звичайний шаблонний рядок | Тегований шаблонний рядок |
|---|---|---|
| Синтаксис | `Hello, ${name}` | tag`Hello, ${name}` |
| Підстановки | Вставляються автоматично | Обробляються вручну в тегу |
| Гнучкість | Лише інтерполяція | Можна фільтрувати, форматувати, екранувати |
| Результат | Завжди рядок | Будь-яке значення, яке поверне тег |
| Застосування | Прості рядки | Шаблонізація, безпека, CSS-in-JS, SQL, i18n |
Підсумок:
| Питання | Відповідь |
|---|---|
| Що це таке | Шаблонний рядок, перед яким стоїть функція-тег, що отримує частини рядка і значення |
| Навіщо потрібно | Щоб керувати тим, як з'єднуються та інтерпретуються вставки ${} |
| Де використовується | Безпечний HTML, CSS-in-JS, форматування, інтернаціоналізація |
| Ключові параметри | strings (частини шаблону), values (вставлені значення) |
Типові помилки
- Ставити дужки після тега.
tag(Hello ${name})це звичайний виклик функції з уже склеєним рядком; тегований шаблон пишеться без дужок, одразу за іменем функції. - Очікувати, що тег отримає готовий рядок. Перший аргумент, це масив частин, решта приходить через rest; склеювати їх має сама функція.
- Плутати кількість елементів. У
stringsзавжди на один елемент більше, ніж уvalues, тому наївний цикл поvaluesгубить останній шматок тексту. - Екранувати не те. Екранувати треба значення, а не весь результат: якщо прогнати через екранування статичний HTML шаблону, розмітка перетвориться на текст.
- Забувати про
strings.raw. Звичайний масив містить оброблені escape-послідовності, аstrings.rawдає сирий текст; саме на цьому побудованийString.raw. - Вважати, що тег зобов'язаний повернути рядок. Тег може повернути об'єкт, функцію або AST, як це роблять
gqlі CSS-in-JS бібліотеки.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.