解释在多语言 HTML 中保留特定 data 属性的白名单写法
解读
这道题考察的是“在国际化(i18n)场景下,如何用 Grunt 插件对 HTML 做压缩/清理,同时保留业务所需的 data- 属性*”。国内项目常见套路是:
- 用 grunt-contrib-htmlmin 或 grunt-processhtml 做压缩/预处理;
- 用 data-i18n、data-lang 等自定义属性标记多语言字段;
- 如果直接开启
removeAttributeQuotes或ignoreCustomComments,极易误删这些关键属性,导致运行时国际化脚本取不到节点,页面白屏。
面试官想看你是否理解“白名单机制”——即通过精确配置告诉 Grunt“哪些属性必须留下”,而不是一刀切地干掉所有未知属性。
知识点
- grunt-contrib-htmlmin 的
options.customAttrCollapse与options.customAttrSurround仅控制折叠,不负责保留;真正决定保留的是options.keepCustomAttributeSuffix(正则)与options.ignoreCustomFragments(数组)。 - 白名单写法本质是一段正则,放在
keepCustomAttributeSuffix里,如/^data-i18n|^data-lang/;也可在ignoreCustomFragments里写/\sdata-i18n=".*?"/g,告诉压缩器“碰到这段就别动”。 - 若项目同时跑 Vue/React 服务端渲染,还需把
data-v-*、data-reactid一并加进正则,避免 hydration 失败。 - 国内 CI 常配合 grunt-usemin 做合并,htmlmin 只是其中一环,白名单需提前在 usemin 的 htmlmin 子配置里声明,否则后面再覆盖已来不及。
- 测试方法:在本地跑
grunt htmlmin后,用grep -E 'data-i18n|data-lang' dist/*.html验证属性是否还在,这是面试官最认可的验证脚本。
答案
在 Gruntfile 里给 grunt-contrib-htmlmin 加一段白名单正则即可,核心配置如下:
htmlmin: {
dist: {
options: {
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
// 关键行:保留以 data-i18n 或 data-lang 开头的属性
keepCustomAttributeSuffix: /^data-i18n|^data-lang/
},
files: [{
expand: true,
cwd: 'src',
src: '**/*.html',
dest: 'dist'
}]
}
}
如果项目里还有 data-v-12345 这类服务端注入标记,把正则扩写为:
keepCustomAttributeSuffix: /^data-(i18n|lang|v-)/
执行 grunt htmlmin 后,dist 目录下所有 HTML 的 data-i18n、data-lang 属性会被完整保留,其余无用属性则被安全移除,既减了体积又保证了多语言脚本正常运行。
拓展思考
- 动态属性场景:当国际化 key 本身带版本号,如
data-i18n-v2025,可把正则可读性优先写成/^data-i18n(-v\d+)?$/,避免每次发版改配置。 - 多插件联动:若还用 grunt-angular-gettext 提取字符串,需保证
extract任务在htmlmin之前跑,否则白名单保留的属性可能已被提前删掉,导致提取不到 key。 - 安全红线:国内金融项目常要求
data-*属性值做 XSS 过滤,白名单只能保“名”不能保“值”,仍需在htmlmin之后加一道grunt-xss扫描,这是面试官追问的加分点。