Tagged template literals
Basic Concepts of Tagged Template Literals
ECMAScript 6 introduced tagged template literals as an extension to traditional template strings, allowing developers to parse template strings through custom functions. This syntax adds a function name as a tag before the string, and the function processes the various parts of the template string.
function tagFunction(strings, ...values) {
console.log(strings); // Static parts of the template
console.log(values); // Dynamic expressions in the template
}
const name = 'Alice';
const age = 25;
tagFunction`Hello ${name}, you are ${age} years old.`;
Parameter Parsing in Tag Functions
Tag functions receive two main parameters: a strings
array and a values
array. The strings
array contains all the static parts of the template string, while the values
array contains the results of all the inserted expressions.
function highlight(strings, ...values) {
let result = '';
strings.forEach((string, i) => {
result += string;
if (i < values.length) {
result += `<span class="highlight">${values[i]}</span>`;
}
});
return result;
}
const user = 'Bob';
const score = 95;
const output = highlight`User ${user} scored ${score} points.`;
document.body.innerHTML = output;
Accessing Raw Strings
Tag functions can access the raw string content, including unescaped character sequences, through the strings.raw
property. This is particularly useful when handling strings containing special characters.
function rawTag(strings, ...values) {
console.log(strings.raw[0]); // Outputs the raw string
return strings.raw[0];
}
const path = rawTag`C:\Development\project\files`;
console.log(path); // Output: C:\Development\project\files
Common Use Cases
HTML Escaping and Safe Rendering
Tagged templates can be used to automatically escape HTML special characters, preventing XSS attacks:
function safeHtml(strings, ...values) {
let result = strings[0];
for (let i = 0; i < values.length; i++) {
const value = String(values[i])
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
result += value + strings[i + 1];
}
return result;
}
const userInput = '<script>alert("XSS")</script>';
const safeOutput = safeHtml`<div>${userInput}</div>`;
console.log(safeOutput); // Output: <div><script>alert("XSS")</script></div>
Internationalization (i18n)
Tagged templates can simplify the handling of internationalized strings:
const translations = {
en: {
greeting: 'Hello',
time: 'The current time is'
},
es: {
greeting: 'Hola',
time: 'La hora actual es'
}
};
function i18n(strings, ...values) {
const lang = navigator.language.slice(0, 2);
const dict = translations[lang] || translations.en;
let result = '';
strings.forEach((string, i) => {
const key = string.trim();
result += dict[key] || string;
if (i < values.length) {
result += values[i];
}
});
return result;
}
const currentTime = new Date().toLocaleTimeString();
const message = i18n`greeting User, time ${currentTime}`;
console.log(message);
SQL Query Construction
Tagged templates can be used to build secure SQL queries:
function sql(strings, ...values) {
let query = strings[0];
for (let i = 0; i < values.length; i++) {
const value = mysql.escape(values[i]);
query += value + strings[i + 1];
}
return query;
}
const userId = 123;
const userName = "John'; DROP TABLE users; --";
const query = sql`SELECT * FROM users WHERE id = ${userId} AND name = ${userName}`;
console.log(query);
// Output: SELECT * FROM users WHERE id = 123 AND name = 'John\'; DROP TABLE users; --'
Advanced Usage and Techniques
Nested Tagged Templates
Tagged templates can be nested to implement more complex string processing logic:
function upper(strings, ...values) {
let result = '';
strings.forEach((string, i) => {
result += string;
if (i < values.length) {
result += String(values[i]).toUpperCase();
}
});
return result;
}
function bold(strings, ...values) {
let result = '';
strings.forEach((string, i) => {
result += string;
if (i < values.length) {
result += `<b>${values[i]}</b>`;
}
});
return result;
}
const name = 'Alice';
const message = bold`Hello ${upper`${name}`}, welcome!`;
console.log(message); // Output: Hello <b>ALICE</b>, welcome!
Styled Components Implementation
Modern front-end frameworks often use tagged templates for styled components:
function css(strings, ...values) {
let style = '';
strings.forEach((string, i) => {
style += string;
if (i < values.length) {
style += values[i];
}
});
return style;
}
const primaryColor = '#3498db';
const buttonStyle = css`
background: ${primaryColor};
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
background: darken(${primaryColor}, 10%);
}
`;
class StyledButton extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
const style = document.createElement('style');
style.textContent = buttonStyle;
shadow.appendChild(style);
const button = document.createElement('button');
button.textContent = 'Click Me';
shadow.appendChild(button);
}
}
customElements.define('styled-button', StyledButton);
Performance Considerations and Optimization
While tagged templates offer powerful functionality, performance-sensitive scenarios require attention:
- Avoid creating large numbers of tagged templates in loops.
- For static templates, consider caching the processed results.
- Complex tag functions may impact performance.
// Inefficient usage
for (let i = 0; i < 1000; i++) {
const message = expensiveTag`Item ${i}`;
// ...
}
// More efficient usage
const cachedTag = (strings, ...values) => {
const cache = new Map();
const key = strings.join('') + values.join('');
if (cache.has(key)) {
return cache.get(key);
}
const result = expensiveProcessing(strings, values);
cache.set(key, result);
return result;
};
Integration with Other ES6 Features
Tagged templates can be combined with other ES6 features like destructuring and default parameters:
function debug(strings, ...values) {
const [first, ...rest] = strings;
console.log('First string part:', first);
console.log('Remaining string parts:', rest);
console.log('All values:', values);
return strings.reduce((result, str, i) =>
`${result}${str}${values[i] !== undefined ? `[${values[i]}]` : ''}`, '');
}
const x = 10, y = 20;
const output = debug`The sum of ${x} and ${y} is ${x + y}`;
console.log(output);
Browser Compatibility and Transpilation
Although modern browsers generally support tagged templates, older environments may require transpilation tools:
- Babel can convert tagged templates into ES5-compatible code.
- TypeScript fully supports tagged template syntax.
- Very old environments may require a string concatenation polyfill.
// Code before Babel transpilation
const tagged = html`<div>${content}</div>`;
// Code after Babel transpilation
var tagged = html(_templateObject || (_templateObject = [void 0]), content);
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn