你不知道的模板字符串

34 阅读1分钟

你不知道的模板字符串

在模板字符串之前,对于字符串的处理往往会碰到以下问题:

  • 拼接代码不优雅

    // ES5 方式
    var name = "ErMao";
    var age = 28;
    var message = "Hello, my name is " + name + " and I am " + age + " years old.";
    console.log(message); // "Hello, my name is ErMao and I am 28 years old."
    
  • 多行文本处理复杂

    // ES5 方式 - 不直观的多行字符串
    var multiLine1 = "This is line one.\nThis is line two.";
    var multiLine2 = "This is line one.\
    This is line two."; // 注意:这种方式第二行前的缩进也会被算作字符串内容
    
  • 动态HTML生成不优雅

    // ES5 方式 - 构建 HTML
    var item = { name: "Coffee", price: 2.5 };
    var htmlSnippet = '<div class="item">\n  <h3>' + item.name + '</h3>\n  <p>Price: $' + item.price + '</p>\n</div>';
    

以上问题都基于对字符串拼接没有一个好的处理方式。

在ES6之后就出现了模板字符串,对于以上的代码有了更加优雅的写法。

字符串插值

在模板字符串中,最重要的特性就是字符串插值。通过使用${},可以任何有效的JS表达式嵌入到字符串中。

const name = "ErMao";
const age =28;

// 使用模板字符串
const message = `Hello, my name is ${name} and I am ${age} years old.`;
console.log(message); // "Hello, my name is ErMao and I am 28 years old."

// ${} 内可以是任意表达式
const calculation = `Five plus ten is ${5 + 10}.`;
console.log(calculation); // "Five plus ten is 15."

function double(x) {
  return x * 2;
}
const functionCall = `The double of 8 is ${double(8)}.`;
console.log(functionCall); // "The double of 8 is 16."
标签模板(Tag Function)

这个方式一般很少使用到,但是思路是非常的巧妙的。在 CSS IN JS 、 hyperHTML中,都能发现其作用。

css in js 的核心是需要将css 模块化,而通过js的方式能够将css有效进行模块化操作。有了 Tag Function 就更好的对css的字符串进行操作处理。

// 以下是主题切换的例子
// 主题上下文
const ThemeContext = {
  currentTheme: 'light',
  themes: {
    light: {
      primary: '#007bff',
      background: '#ffffff',
      text: '#333333',
      border: '#dee2e6'
    },
    dark: {
      primary: '#4dabf7',
      background: '#1a1a1a',
      text: '#ffffff',
      border: '#495057'
    }
  }
};

// 主题标签函数
function themed(strings, ...values) {
  return function(elementProps = {}) {
    const className = `themed-${Math.random().toString(36).substr(2, 9)}`;
    const theme = ThemeContext.themes[ThemeContext.currentTheme];
    
    let styles = '';
    strings.forEach((string, i) => {
      styles += string;
      if (i < values.length) {
        const value = values[i];
        // 处理主题值
        if (typeof value === 'function') {
          styles += value(theme, elementProps);
        } else if (value && value.theme) {
          styles += theme[value.theme] || value.fallback || '';
        } else {
          styles += value;
        }
      }
    });

    const styleElement = document.createElement('style');
    styleElement.textContent = `.${className} { ${styles} }`;
    document.head.appendChild(styleElement);

    return className;
  };
}

// 使用主题
const themedButton = themed`
  background: ${theme => theme.primary};
  color: white;
  border: 1px solid ${theme => theme.border};
  padding: 12px 24px;
  border-radius: 6px;
  cursor: pointer;
  margin: 8px;
  transition: all 0.3s ease;

  &:hover {
    background: ${theme => theme.primary}dd;
    transform: translateY(-1px);
  }
`;

// 主题切换函数
function switchTheme(themeName) {
  ThemeContext.currentTheme = themeName;
  // 重新应用样式
  document.querySelectorAll('[data-themed]').forEach(element => {
    const originalClass = element.className.split(' ')[0];
    const newClass = themedButton();
    element.className = element.className.replace(originalClass, newClass);
  });
}

// 创建主题按钮
const btn1 = document.createElement('button');
btn1.className = themedButton();
btn1.textContent = '主题按钮';
btn1.setAttribute('data-themed', 'true');

const btn2 = document.createElement('button');
btn2.className = themedButton();
btn2.textContent = '另一个主题按钮';
btn2.setAttribute('data-themed', 'true');

// 主题切换按钮
const switchBtn = document.createElement('button');
switchBtn.textContent = '切换主题';
switchBtn.onclick = () => {
  const newTheme = ThemeContext.currentTheme === 'light' ? 'dark' : 'light';
  switchTheme(newTheme);
};

document.body.appendChild(btn1);
document.body.appendChild(btn2);
document.body.appendChild(switchBtn);