Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Теговані шаблонні рядки». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Тегований шаблон, це шаблонний рядок, перед яким стоїть ім'я функції: JavaScript не склеює рядок сам, а викликає цю функцію і передає їй окремо масив текстових частин і окремо всі підставлені значення.** Завдяки цьому тег керує тим, як саме значення потраплять у результат: їх можна екранувати, форматувати, підсвічувати або взагалі перетворити на інший тип даних. Виклик `` tag`Hello, ${name}!` `` перетворюється на `tag(['Hello, ', '!'], name)`. ```javascript function tag(strings, ...values) { console.log(strings); // [ 'Hello, ', '!' ] console.log(values); // [ 'Alex' ] } const name = 'Alex'; tag`Hello, ${name}!`; ``` **Ключове:** тег отримує `strings` і `values` і сам вирішує, яким буде фінальний рядок.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Тегований шаблонний рядок, це шаблонний рядок, перед яким записано ім'я функції; замість автоматичного склеювання JavaScript викликає цю функцію і передає їй частини рядка та підставлені значення окремо.** Тег сам вирішує, що робити зі значеннями, тому таку конструкцію використовують для екранування, форматування та власних міні-мов на кшталт CSS-in-JS. ## Теорія ### TL;DR - Звичайний шаблон просто вставляє значення, тегований віддає контроль функції. - Функція-тег отримує два набори даних: масив текстових частин і список підставлених значень. - `` tag`Hello, ${name}!` `` виконується як `tag(['Hello, ', '!'], name)`. - Повертати тег може будь-що, не обов'язково рядок. - Головні застосування: безпечний HTML, CSS-in-JS, GraphQL-запити, форматування, i18n. ### Швидкий приклад ```javascript function tag(strings, ...values) { console.log(strings); // [ 'Hello, ', '!' ] console.log(values); // [ 'Alex' ] } const name = 'Alex'; tag`Hello, ${name}!`; ``` Що відбувається під капотом: ```javascript // Інтерпретатор робить приблизно це: tag(['Hello, ', '!'], 'Alex'); ``` ### Як це виглядає і що отримує тег Звичайний шаблонний рядок просто підставляє змінні: ```javascript const name = 'Alex'; console.log(`Hello, ${name}!`); // Hello, Alex! ``` Але якщо перед шаблонним рядком вказати ім'я функції, JavaScript викличе цю функцію і передасть у неї: 1. Масив частин рядка (тексту між підстановками). 2. Усі підставлені значення з `${...}`. ```javascript tag`Hello, ${name}!` ``` Це і є тегований шаблон. Отже: - `strings`, це масив усіх шматків тексту між вставками; - `values`, це масив усіх вставлених значень, зібраний rest-параметром. Текстових частин завжди на одну більше, ніж значень, бо шаблон починається і закінчується текстом (можливо, порожнім). ### Обробка вмісту: власний шаблонізатор Тег-функцію можна використати для створення власних шаблонних мов або форматування рядків. ```javascript 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>`. Так само легко зробити форматувальник: ```javascript 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 Найкорисніший сценарій: тег екранує все, що прийшло ззовні, тому розмітка лишається безпечною. ```javascript 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 лишається безпечним, ніякого виконання стороннього скрипта. Важливо, що екрануються саме значення, а статичний текст шаблону, який написав розробник, лишається недоторканим. ### Реальні застосування 1. **Styled Components (React)** ```javascript const Button = styled.button` background: ${props => props.primary ? 'blue' : 'gray'}; color: white; `; ``` Тут `styled.button`, це тег-функція, яка отримує шаблон і будує з нього CSS. 2. **GraphQL-запити** ```javascript const query = gql` query GetUser { user(id: 1) { name } } `; ``` Тег `gql` парсить текст запиту в AST ще до відправлення на сервер. 3. **Інтернаціоналізація (i18n)** ```javascript translate`Welcome, ${userName}!`; ``` Тег знаходить потрібний переклад за статичною частиною і підставляє значення в нього. ### Порівняння зі звичайним шаблонним рядком | Особливість | Звичайний шаблонний рядок | Тегований шаблонний рядок | | --- | --- | --- | | Синтаксис | `` `Hello, ${name}` `` | `` tag`Hello, ${name}` `` | | Підстановки | Вставляються автоматично | Обробляються вручну в тегу | | Гнучкість | Лише інтерполяція | Можна фільтрувати, форматувати, екранувати | | Результат | Завжди рядок | Будь-яке значення, яке поверне тег | | Застосування | Прості рядки | Шаблонізація, безпека, CSS-in-JS, SQL, i18n | Підсумок: | Питання | Відповідь | | --- | --- | | Що це таке | Шаблонний рядок, перед яким стоїть функція-тег, що отримує частини рядка і значення | | Навіщо потрібно | Щоб керувати тим, як з'єднуються та інтерпретуються вставки `${}` | | Де використовується | Безпечний HTML, CSS-in-JS, форматування, інтернаціоналізація | | Ключові параметри | `strings` (частини шаблону), `values` (вставлені значення) | ### Типові помилки 1. **Ставити дужки після тега.** `tag(`Hello ${name}`)` це звичайний виклик функції з уже склеєним рядком; тегований шаблон пишеться без дужок, одразу за іменем функції. 2. **Очікувати, що тег отримає готовий рядок.** Перший аргумент, це масив частин, решта приходить через rest; склеювати їх має сама функція. 3. **Плутати кількість елементів.** У `strings` завжди на один елемент більше, ніж у `values`, тому наївний цикл по `values` губить останній шматок тексту. 4. **Екранувати не те.** Екранувати треба значення, а не весь результат: якщо прогнати через екранування статичний HTML шаблону, розмітка перетвориться на текст. 5. **Забувати про `strings.raw`.** Звичайний масив містить оброблені escape-послідовності, а `strings.raw` дає сирий текст; саме на цьому побудований `String.raw`. 6. **Вважати, що тег зобов'язаний повернути рядок.** Тег може повернути об'єкт, функцію або AST, як це роблять `gql` і CSS-in-JS бібліотеки.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.