昨日产品新加了一个需求,要求对某个展示 xml 文本的功能优化,输出美观且易阅读的文本。然后不知道从哪个网站搬来了一个截图,让我参照这个截图优化系统的功能。
我们系统原先展示的 xml 文本是未经格式的,并且带有乱码的内容,
不忍吐槽,这种未经格式化的文本,也不知道他们之前怎么用下去的,估计压根就很少点击过这个功能!
想要完成这个需求,首先得借助能格式化这种 xml 文本的工具。
想起之前自己捣鼓项目时用到了 hightlight.js 来格式化 js 代码,正好能够胜任我这个业务场景。
highlight.js 是一个广泛使用的语法高亮库,能够自动检测并高亮显示超过 190 种编程语言的代码。
在 Components 目录新建一个 xml-formatter 的 Vue 组件,组件内部代码实现如下:
<script setup lang="ts">
import hljs from "highlight.js";
import xmlLanguage from "highlight.js/lib/languages/xml";
import { isString } from "lodash-es";
import { computed } from "vue";
const props = defineProps({
raw: {
type: String,
},
});
hljs.registerLanguage("javascript", xmlLanguage);
const xmlContent = computed(() => {
if (isString(props.raw)) {
const uint8Array = Uint8Array.from(atob(props.raw), (c) => c.charCodeAt(0));
const text = new TextDecoder("utf-8").decode(uint8Array);
return hljs.highlight(text, { language: "xml" }).value;
}
return null;
});
</script>
查看之前的源码发现输出的 xml 文本会有中文乱码,是因为之前将后端接口返回的 Base64 编码的文本直接通过 window.atob 来解码的。
由于 window.atob 处理的 Base64 编码返回的是 ASCII 字符串。对于中文这种非 ASCII 码,解码时必定会乱码,所以只能换种思路。
用 TextDecoder 和 TextEncoder 来处理 Base64 编码的字符串。这些 API 可以正确处理 Unicode 字符。
乱码的问题解决后,接下来就需要将格式化后的 xml 代码输出到页面了。
在 Vue.js 我们能使用 v-html 指令在模板中动态地渲染 HTML 结构的内容。在 React 中也可以使用 dangerouslySetInnerHTML 渲染 HTML 内容。
接着还需要用到 <pre> 和 <code> 这两个 HTML 标签作为内容的输出。
<pre>标签能够保留文本中的空白、换行和缩进,使文本在浏览器中呈现的样式与其在 HTML 源代码中的格式一。而<code> 标签能够增加 HTML 文档的语义性。
模板代码如下:
<template>
<pre><code class="hljs" v-html="xmlContent"></code></pre>
</template>
实现完成的效果如下图,现在可以看到格式化后的 XML 代码了。
但是格式化后的样式还和截图的有很大区别,所以还需要编写 xml 代码样式。
我们打开 Devtools 审查元素 DOM 结构能看到使用 highlightjs 格式化的代码不同的属性都有对应的类名。
那么只需要为这些类名编写对应的 CSS 样式即可实现最后一个小功能点。
CSS 代码如下:
<style lang="scss">
pre code {
white-space: pre-wrap;
word-break: break-all;
}
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #abb2bf;
background: #282c34;
.hljs-comment,
.hljs-quote {
color: #5c6370;
font-style: italic;
}
.hljs-doctag,
.hljs-formula,
.hljs-keyword {
color: #c678dd;
}
.hljs-deletion,
.hljs-name,
.hljs-section,
.hljs-selector-tag,
.hljs-subst {
color: #e06c75;
}
.hljs-literal {
color: #56b6c2;
}
.hljs-addition,
.hljs-attribute,
.hljs-meta-string,
.hljs-regexp,
.hljs-string {
color: #98c379;
}
.hljs-built_in,
.hljs-class .hljs-title {
color: #e6c07b;
}
.hljs-attr,
.hljs-number,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-pseudo,
.hljs-template-variable,
.hljs-type,
.hljs-variable {
color: #d19a66;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: 700;
}
.hljs-link {
text-decoration: underline;
}
}
</style>
最终效果如下图所示:
对比之前产品的截图样例,完美的复刻了。又可以畅快的搬砖(摸鱼)了~