发现问题

最近在应用公司组件库中的穿梭框组件时发现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

下面剖析问题时说到构建时SassUnicode字符编译成文字字符,那么咱们能不能在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开发入门