最近维护一老项目,在部署后发现部分字体图标渲染出现乱码,且不是必现,在控制台查看元素,发现伪元素的 content 属性为乱码:
而 scss 源码为:
// icon.scss
.icon-order {
&:before {
content: "\e261";
}
}
而经过 webpack 构建,通过 sass-loader(dart-sass 实现)、css-loader 处理后的样式为:
/* icon.css */
.icon-order:before {
content: "";
}
如果使用的是 node-sass,虽然可能不会出现此问题,但其已经不再维护了,故不推荐使用。
经过查询资料,发现此问题有如下几种方案:
方案 1 - 使用 utf-8 编码
通过 webpack 构建后的文件默认是 utf-8 编码的,但最好在响应头部中显式地指定当前文档使用的是 utf-8 编码:
# Response Header
Content-Type: text/css; charset=utf-8
这样即使 css 文件中显示的是乱码,浏览器也应能够正常渲染。而出现问题页面的服务器并未给 css 文件设置 charset=utf-8
属性,想来这也是偶现的原因。
一般在 index.html 都会有如下代码:
<meta charset="utf-8" />
但此属性只是指示了 index.html 的编码,并不能影响其中引用资源文件的编码。关于此配置的文档可查看MDN 文档。
服务器配置方面,应该为 js、css 等资源文件的 Content-Type 头部中指定 utf-8 编码。此方案能否解决此问题未经实际验证,可配合后续的方案使用更加保险。
方案 2 - 在 sass-loader 配置中为 dart-sass 指定 sassOptions
使用 outputStyle
配置为 expanded 后,上文生成的 css 代码将不会是乱码,而是正常的 content: “\e261”;:
// webpack.config.js
{
loader: "sass-loader",
options: {
implementation: require("sass"),
sassOptions: {
// 不进行代码压缩
outputStyle: "expanded",
},
},
},
outputStyle
默认配置为 compressed,表示尽可能地删除多余的字符,将所有样式写作一行(即会进行代码压缩),关于 outputStyle 配置的信息,可查阅dart-sass 文档。虽然对于 dart-sass 来说,outputStyle
的默认是 expanded,但 sass-loader 会在生产模式下将默认值改为 compressed。可查阅 sass-loader 源码。
将 outputStyle
配置修改后,最终生成的样式文件是未经压缩的,这会对文件体积有影响,此时可结合 css-minimizer-webpack-plugin 使用,具体用法可查看其文档,此处不再赘述。
css-minimizer-webpack-plugin@1 版本适用于 webpack@4;css-minimizer-webpack-plugin@2 以上适用于 webpack@5。
方案 3 - 定义 scss 函数,替代直接书写 unicode 字面量
此方案详情,可查看此 stack overflow 回复。