乐趣区

关于sass:Sass将Unicode编译成文字字符导致icon乱码问题

发现问题

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

退出移动版