發現問題
最近在使用公司組件庫中的穿梭框組件時發現icon圖標全都亂碼了
分析問題
經排查發現,組件樣式文件(scss)引入的iconfont矢量圖標字體,構建時,\e601這類Unicode字符在經過sass編譯後就變成了文字字符(雙字節字符),導致出現亂碼
.icon-ok:before {
content: "\e601";
}
Sass編譯後
.icon-ok:before {
content: "";
}
解決問題
(1)升級sass版本
在Sass@1.38.0版本中對這個問題做了修復,可以將項目中使用的版本上級到1.38.0+,詳情查看Sass更新日誌
// index.scss
.icon-ok:before {
content: "\e601";
}
執行npx sass@1.38.0 index.scss index.css
// index.css
.icon-ok:before {
content: "\e601";
}
/*# sourceMappingURL=index.css.map */
(2)自定義webpack loader
上面分析問題時説到構建時Sass將Unicode字符編譯成文字字符,那麼我們能不能在loader隊列中sass-loader後加入我們自定義的loader,將CSS中的文字字符轉譯成Unicode字符呢?當然是可以的
const CONTENT_MATCH_REG = /(?<!-)content\s*:\s*([^;\}]+)/g; // 找出偽元素裏content那塊內容
const UNICODE_MATCH_REG = /[^\x00-\xff]/g; // 找出非單字節符
function fun(source) {
this.cacheable(); // 利用緩存來提高編譯效率
source = source.replace(CONTENT_MATCH_REG, function (m, p1) {
return m.replace(UNICODE_MATCH_REG, function (m) {
return "\\" + m.charCodeAt(0).toString(16); // m.charCodeAt(0)返回字符串第一個字符的 Unicode 編碼,後面再轉16進制,前面加一斜槓
});
});
return source;
}
測試
let test = `.el-icon-ice-cream-square:before {
content: "";
}
`;
console.log(fun(test));
// 打印結果
// .el-icon-ice-cream-square:before {
// content: "\e6da";
// }
可以參考:https://github.com/styzhang/c...
(3)自定義webpack plugin
開發webpack插件需要知道的幾個必要條件:
- 獲取編譯器 compiler 對象,通過這個對象能過獲取包括
config配置,資源文件,編譯信息,鈎子函數等信息 - 編譯階段的生命週期函數,找到適合的鈎子函數處理對應邏輯
- 返回結果支持同步和異步兩種方式
/**
* 編碼unicode字符串
* encodeUnicodeChar('•') // 等價於 '\\2022';
*/
module.exports.encodeUnicodeChar = function (str) {
let content = "";
for (let i = 0; i < str.length; i++) {
const codePoint = str.codePointAt(i);
if (codePoint > 127) {
content += "\\" + str.codePointAt(i).toString(16);
} else {
content += str.charAt(i);
}
}
return content;
};
module.exports.encodeCssContentUnicodeChar = function (css) {
return css.replace(
/([{\s;]content:\s?['"])(.+?)(['"][;\s}])/g,
(match, p1, p2, p3, offset, str) => {
return p1 + module.exports.encodeUnicodeChar(p2) + p3;
}
);
};
在將構建後的生成的資源文件輸出到目標目錄之前執行,emit是一個異步系列鈎子
const { encodeCssContentUnicodeChar } = require("./utils");
class CssContentUnicodePlugin {
apply(compiler) {
compiler.hooks.emit.tap("CssUnicodePlugin", function (compilation) {
Object.keys(compilation.assets)
.filter((filename) => /\.css$/.test(filename))
.forEach((filename) => {
const asset = compilation.assets[filename];
let fileContent = asset.source();
fileContent = encodeCssContentUnicodeChar(fileContent);
asset.source = () => fileContent;
asset.size = () => fileContent.length;
});
});
}
}
module.exports = CssContentUnicodePlugin;
參考:
Sass更新日誌
sass unicode字符編碼問題
Sass:unicode轉義沒有保存在.css文件中
webpack篇-插件plugin開發
如何開發一個webpack loader
webpack loader開發入門