發現問題
最近在使用公司元件庫中的穿梭框元件時發現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;
參考: