只会用来拼接字符串?ES6模板字符串还有你不知道的高级玩法!

919 阅读5分钟

前言

Hello~大家好,我是秋天的一阵风。

对于ES6中的模板字符串,大家一定不会陌生。在实际开发中,大家都会用它来拼接字符串。比如下面这样:

const name = 'tom';
const str = `My name is ${name}`;
console.log(name); // output: tom

但是在官方文档中,这是一个非正式的叫法。正式的叫法应该是叫 模板字面量

以下是摘自官方的介绍:

模板字面量是用反引号(`)分隔的字面量,允许多行字符串、带嵌入表达式的字符串插值和一种叫带标签的模板的特殊结构。

模板字面量有时被非正式地叫作模板字符串,因为它们最常被用作字符串插值(通过替换占位符来创建字符串)。

看完介绍,肯定有同学会疑问,带标签的模板 是什么东西,模板怎么带标签?带的啥标签? html标签吗?

别着急,我们慢慢探究~

一、带标签的模板字面量

带标签的模板字面量可能不会产生字符串——它可以与自定义标签函数一起使用,来对模板字面量的不同部分执行任何操作。

看完上面这段介绍,可能有的同学脑袋更晕了。标签还没搞明白,怎么又来了一个标签函数???

Talk is cheap , show me your code . 我们直接上代码

基本用法

const name = 'tom';
const age = 18;
const hi = myTag`my name is ${name}, my age is ${age}`;
console.log(hi);

上面就是标签字面量的用法,你可以copy到你的编辑器或者浏览器的控制台进行尝试,会得到惊喜!

但是很遗憾,得到惊喜就是报错!

Uncaught ReferenceError: myTag is not defined
    at <anonymous>:3:12

报错信息很明显,myTag没有被定义,那么myTag怎么定义呢?答案直接揭晓:myTag其实要求是一个函数。这个函数接受如下几个参数:

  1. 字符串数组(strings :这是标签函数接收的第一个参数,它是一个包含模板字符串中所有文本片段的数组。对于任何模板字面量,其长度等于替换次数(${…}出现次数)加一,因此总是非空的。
  2. 表达式数组(...values :这是标签函数接收的其余参数,它们是模板字符串中所有表达式求值结果的数组。这些参数与模板字符串中的插值表达式一一对应。如果模板字符串中没有变量插值,则没有这个参数。如果有多个插值表达式,它们将作为多个参数传递给函数。
function myTag(strings,...values){
    console.log(strings,values)
};
const name = 'tom';
const age = 18;
const hi = myTag`my name is ${name}, my age is ${age}`;

加入这个函数后,你会得到以下打印信息:

 ['my name is ', ', my age is ', '']
 ['tom', 18]

现在我们就豁然开朗了,strings其实就是被插值(也就是name{name}和{age})切割出来的三段字符

而values就是插值的数据。

那此时的hi变成了什么呢?我们来打印一下:

function myTag(strings,...values){
    console.log(strings,values)
};
const name = 'tom';
const age = 18;
const hi = myTag`my name is ${name}, my age is ${age}`;
console.log(hi); // output is undefined

undefined是什么原因呢?其实hi就是函数myTag的返回值。 我们在myTag里没有写任何返回值,所以就是默认的undefined。

我们继续改造:

function myTag(strings,...values){
    console.log(strings,values)
    return 'just for return';
};
const name = 'tom';
const age = 18;
const hi = myTag`my name is ${name}, my age is ${age}`;
console.log(hi); // just for return

看,现在的hi就变成了myTag函数的返回值了。 那么如何让hi变成我们想要的格式呢? my name is tom, my age is 18

那就太简单了,我们只需要改一下return的内容就行了:

function myTag(strings,...values){
    console.log(strings,values)
    return `my name is ${values[0]}, my age is ${values[1]}`;
};
const name = 'tom';
const age = 18;
const hi = myTag`my name is ${name}, my age is ${age}`;
console.log(hi); // my name is tom, my age is 18

二、好玩的用法案例

介绍完用法以后,同学们估计都会觉得这玩意儿不是脱裤子放屁,多此一举吗?这功能也太鸡肋了。你可别小瞧这个功能,它其实还有很多好玩的用法的。

  1. 自定义格式化输出: 你可以创建一个标签函数来自定义模板字符串的输出格式。例如,你可以创建一个函数来将模板字符串的内容转换为大写:

    
    function upperCase(strings, ...values) {
      const result = strings.map((str, i) => {
        return str.toUpperCase() + (values[i] ? values[i].toUpperCase() : '');
      }).join(''); 
    
      return result;
    }
    
    const message = upperCase`hello ${'world'}`;
    console.log(message); // 输出: HELLO WORLD
    

    这个函数将模板字符串中的每个部分都转换为大写字母。

  2. 生成HTML代码: 使用标签模板字符串来生成HTML代码是一种常见的用法。你可以创建一个标签函数来转义HTML特殊字符,防止XSS攻击:

    function safeHtml(strings, ...values) {
      return strings.map((s, i) => s + (values[i] ? values[i].replace(/&/g, '&amp;').replace(/</g, '&lt;') : '')).join('');
    }
    const html = safeHtml`<div>${'<script>alert("xss")</script>'}</div>`;
    console.log(html); // <div>&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;</div>
    

    这个函数将模板字符串中的变量值进行HTML转义,以确保安全。

  3. 字符串插值的高级应用: 你可以创建一个标签函数来实现更复杂的字符串插值逻辑,比如根据上下文改变变量的表现形式:

    function conditional(strings, ...values) {
      return strings.reduce((acc, s, i) => {
        const value = values[i] === undefined ? '' : values[i];
        return acc + s + (value ? ` (${value})` : '');
      }, '');
    }
    const result = conditional`This is a ${'test'} string.`;
    console.log(result); // This is a (test) string.
    

    这个函数在变量存在时添加括号。

  4. 国际化和本地化: 使用标签模板字符串来实现多语言支持,通过标签函数来选择不同语言的翻译:

    const i18n = (strings, ...values) => {
      const lang = navigator.language;
      // 假设有一个根据语言和字符串键返回翻译的函数
      return strings.map(s => translate(s, lang)).join('');
    };
    const greeting = i18n`Hello ${'name'}`;
    console.log(greeting); // 根据用户语言返回相应的翻译
    

看到这里,你还敢忽略标签字面量的强大了吗?赶紧在项目里面用起来吧!!!

三、style-components

styled-components 是一个流行的 CSS-in-JS 库,它允许开发者在 JavaScript 中编写 CSS 样式,并且与 React 组件紧密结合。

如果用过React并且熟悉CSS-in-JS的同学对下面的代码一定不会陌生。同学们现在有没有觉得很像刚刚学过的标签字面量呢?没错了,其实styled-components就是基于标签字面量来实现的!

// Create a Title component that'll render an <h1> tag with some styles
const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: #BF4F74;
`;

// Create a Wrapper component that'll render a <section> tag with some styles
const Wrapper = styled.section`
  padding: 4em;
  background: papayawhip;
`;

// Use Title and Wrapper like any other React component – except they're styled!
render(
  <Wrapper>
    <Title>
      Hello World!
    </Title>
  </Wrapper>
);

我们还可以尝试着自己来用伪代码实现style-components的功能:

import React from 'react';
import ReactDOM from 'react-dom';

// 创建一个空对象来模拟 styled-components 的 styled 对象
const styled = {};

// 模拟 styled.h1 创建一个样式化的 h1 组件
styled.h1 = (strings, ...values) => {
  // 将模板字符串和值合并成最终的 CSS 字符串
  const css = strings.reduce((acc, s, i) => {
    return acc + s + (values[i] ? values[i] : '');
  }, '');

  // 创建一个 React 组件
  return class StyledH1 extends React.Component {
    render() {
      // 将 CSS 字符串作为 style 属性应用到 h1 元素上
      const style = {
        fontSize: '1.5em',
        textAlign: 'center',
        color: '#BF4F74',
        ...css
      };
      return <h1 style={style} {...this.props}>{this.props.children}</h1>;
    }
  };
};

// 模拟 styled.section 创建一个样式化的 section 组件
styled.section = (strings, ...values) => {
  // 将模板字符串和值合并成最终的 CSS 字符串
  const css = strings.reduce((acc, s, i) => {
    return acc + s + (values[i] ? values[i] : '');
  }, '');

  // 创建一个 React 组件
  return class StyledSection extends React.Component {
    render() {
      // 将 CSS 字符串作为 style 属性应用到 section 元素上
      const style = {
        padding: '4em',
        background: 'papayawhip',
        ...css
      };
      return <section style={style} {...this.props}>{this.props.children}</section>;
    }
  };
};

总结

通过探讨,我们不仅重新认识了ES6模板字符串的强大功能,尤其是带标签的模板字面量,还了解到了它们在实际开发中的多样化应用。从基本的字符串插值到复杂的自定义格式化输出,从生成安全的HTML代码到实现国际化和本地化,带标签的模板字面量展示了其在处理字符串方面的灵活性和强大能力。此外,styled-components库的应用实例更是让我们看到了带标签模板字面量在现代前端开发中的实际价值,它不仅简化了CSS与JS的结合,还提升了代码的可维护性和可读性。