如何在 JavaScript 中拥有不破坏缩进的多行字符串?

问题描述 投票:0回答:7

根据 this esdiscuss 讨论,在 ECMAScript 6 中可以定义多行字符串,而不必将字符串的后续行放在行的最开头。

Allen Wirfs-Brock 的帖子包含一个代码示例:

var a = dontIndent
        `This is a template string.
         Even though each line is indented to keep the
         code neat and tidy, the white space used to indent
         is not in the resulting string`;

有人可以解释一下如何实现这一点吗?如何定义这个

dontIndent
事物以删除用于缩进的空格?

javascript ecmascript-6 template-literals
7个回答
55
投票

2020 年答案:尽管 TC39 已经讨论过添加一个新的模板文字来处理缩进,但 JS stdlib 中仍然没有内置任何内容来处理长行的消除缩进。目前您有 2 个选择:

  1. endentdedent-js包将处理这个问题。请注意,
    dedent-js
    包实际上适用于制表符和空格。
    var dedent = require('dedent-js');
    var text = dedent(`
      <div>
        <span>OK</span>
        <div>
          <div></div>
        </div>
      </div>
    `);

将删除每行前面的空格和前导回车符。它还拥有更多用户、问题跟踪器,并且比从 Stack Overflow 复制粘贴更容易更新!

注意:

dedent
是与
dedent-js
不同的软件包,并且
dedent
在选项卡上失败。

  1. 不要缩进长行,但使用将长行显示为缩进的编辑器。例如,vsCode - 您可以简单地使用长行,不缩进任何内容,并在长字符串中包含回车符。 vsCode 将显示它们缩进。下面的字符串没有缩进 - 第二行
    The empty export...
    紧接在回车符之后,但显示为缩进。


27
投票

您也可以对双空格进行字符串替换(假设您的缩进使用空格,而不是制表符)。显然,实际字符串中的任何双空格都会被删除,但在大多数情况下,这应该没问题。

const MSG = (`Line 1
          line 2
          line 3`).replace(/  +/g, '');
// outputs
/*
Line 1
line 2
line 3
*/

18
投票

此功能是通过定义自定义函数然后将其用作标签来实现的(上面的

dontIndent
)。代码打击来自Zenparsing的要点

function dedent(callSite, ...args) {

    function format(str) {

        let size = -1;

        return str.replace(/\n(\s+)/g, (m, m1) => {

            if (size < 0)
                size = m1.replace(/\t/g, "    ").length;

            return "\n" + m1.slice(Math.min(m1.length, size));
        });
    }

    if (typeof callSite === "string")
        return format(callSite);

    if (typeof callSite === "function")
        return (...args) => format(callSite(...args));

    let output = callSite
        .slice(0, args.length + 1)
        .map((text, i) => (i === 0 ? "" : args[i - 1]) + text)
        .join("");

    return format(output);
}

我已经在 Firefox Nightly 中成功测试了它:

enter image description here


13
投票

如何定义这个

dontIndent
事物以删除用于缩进的空格?

我想这样的东西应该足以满足很多情况(包括OP):

function dontIndent(str){
  return ('' + str).replace(/(\n)\s+/g, '$1');
}

此片段中的演示代码:

var a = dontIndent
        `This is a template string.
         Even though each line is indented to keep the
         code neat and tidy, the white space used to indent
         is not in the resulting string`;

console.log(a);
         
function dontIndent(str){
  return ('' + str).replace(/(\n)\s+/g, '$1');
}

说明

JavaScript 模板文字 可以使用标签调用,在本例中为

dontIndent
。标签被定义为函数,并以模板文字作为参数进行调用,因此我们定义了一个
dontIndent()
函数。模板文字作为数组中的参数传递,因此我们使用表达式
('' + str)
将数组内容转换为字符串。然后,我们可以使用像
/(\n)\s+/g
.replace()
这样的正则表达式来处理所有出现的换行符,后跟空格,仅换行符,以达到OP的目的。


4
投票

所有现有答案的问题是,它们都是运行时解决方案。也就是说,它们在程序执行时采用多行模板文字并通过函数运行它,以消除前导空格。这是“错误的方式”,因为该操作应该在编译时完成。原因是,此操作中没有任何内容需要运行时信息,所需的所有信息在编译时都是已知的。

为了在编译时完成此操作,我编写了一个名为 Dedent Template Literals 的 Babel 插件。基本上它的工作原理如下:

const httpRFC = ` Hypertext Transfer Protocol -- HTTP/1.1 Status of this Memo This document specifies an Internet standards track protocol for the Internet community, and requests discussion and suggestions for improvements. Please refer to the current edition of the "Internet Official Protocol Standards" (STD 1) for the standardization state and status of this protocol. Distribution of this memo is unlimited. Copyright Notice Copyright (C) The Internet Society (1999). All Rights Reserved.`; console.log(httpRFC);
将打印:

Hypertext Transfer Protocol -- HTTP/1.1 Status of this Memo This document specifies an Internet standards track protocol for the Internet community, and requests discussion and suggestions for improvements. Please refer to the current edition of the "Internet Official Protocol Standards" (STD 1) for the standardization state and status of this protocol. Distribution of this memo is unlimited. Copyright Notice Copyright (C) The Internet Society (1999). All Rights Reserved.
插值也可以正常工作。此外,如果您在模板文字的左反引号之后的第一列之前开始一行,则插件将抛出错误,显示错误的位置。如果以下文件位于您项目下的

src/httpRFC.js

const httpRFC = ` Hypertext Transfer Protocol -- HTTP/1.1 Status of this Memo This document specifies an Internet standards track protocol for the Internet community, and requests discussion and suggestions for improvements. Please refer to the current edition of the "Internet Official Protocol Standards" (STD 1) for the standardization state and status of this protocol. Distribution of this memo is unlimited. Copyright Notice Copyright (C) The Internet Society (1999). All Rights Reserved.`; console.log(httpRFC);
编译时会出现如下错误:

Error: <path to your project>/src/httpRFC.js: LINE: 11, COLUMN: 17. Line must start at least at column 18. at PluginPass.dedentTemplateLiteral (<path to your project>/node_modules/babel-plugin-dedent-template-literals/index.js:39:15) at newFn (<path to your project>/node_modules/@babel/traverse/lib/visitors.js:175:21) at NodePath._call (<path to your project>/node_modules/@babel/traverse/lib/path/context.js:55:20) at NodePath.call (<path to your project>/node_modules/@babel/traverse/lib/path/context.js:42:17) at NodePath.visit (<path to your project>/node_modules/@babel/traverse/lib/path/context.js:92:31) at TraversalContext.visitQueue (<path to your project>/node_modules/@babel/traverse/lib/context.js:116:16) at TraversalContext.visitSingle (<path to your project>/node_modules/@babel/traverse/lib/context.js:85:19) at TraversalContext.visit (<path to your project>/node_modules/@babel/traverse/lib/context.js:144:19) at Function.traverse.node (<path to your project>/node_modules/@babel/traverse/lib/index.js:82:17) at NodePath.visit (<path to your project>/node_modules/@babel/traverse/lib/path/context.js:99:18) { code: 'BABEL_TRANSFORM_ERROR' }
如果您使用制表符(且仅制表符)进行缩进并使用空格(且仅使用空格)进行对齐,它也适用于制表符。

它可以通过运行

npm install --save-dev babel-plugin-dedent-template-literals

来安装,并通过将
dedent-template-literals
作为
Babel配置
plugins数组的第一个元素来使用。 更多信息可以在自述文件中找到


3
投票
function trim(segments, ...args) { const lines = segments .reduce((acc, segment, i) => acc + segment + (args[i] || ''), '') // get raw string .trimEnd().split('\n') // Split the raw string into lines .filter(line => line != "") // remove empty lines // Find the minimum number of leading spaces across all lines const minLeadingSpaces = lines.reduce((acc, line) => { // Find the number of leading spaces for this line const leadingSpaces = line.match(/^ */)[0].length // if it has less leading spaces than the previous minimum, set it as the new minimum return leadingSpaces < acc ? leadingSpaces : acc }, Infinity) // Trim lines, join them and return the result return lines.map(line => line.substring(minLeadingSpaces)).join('\n') }
用途:

console.log(trim` <div> <p> Lorem ipsum dolor sit amet. </p> </div> `)
    

1
投票
正如

Šime Vidas所述,函数可以用作标签,只需将其放在模板字符串前面即可调用。

有许多 NPM 模块可以做到这一点,并且将涵盖许多你自己很难涵盖的边缘情况。主要的两个是:

dedent,每周下载量 400 万次,最后更新于 4 年前
dedent npm badge, with the above statistics

endent,每周下载量 2500 次,4 个月前更新
endent npm badge, with the above statistics

© www.soinside.com 2019 - 2024. All rights reserved.