
解决 Rsbuild 和 Vite 中 sass 编译 Element 字体图标乱码问题
记录下我猪哥遇到的坑
最近我猪哥在把项目从 Vue CLI 迁移到 Rsbuild 的时候,遇到一个很头疼的问题:Element UI 的字体图标突然全都变成了乱码!
- 将原本基于 Vue CLI 的项目迁移到 Rsbuild
- 将
sass
编译器从 node-sass 替换为 dart-sass - 编译 Element UI 的样式文件时出现了字体图标乱码的问题
编译结果乱码(编辑器中显示成方块)
浏览器展示乱码(显示成方块或问号)
解决方案:强制将 content
内容转为 Unicode
编码
经过深入研究(找了些资料)和测试(改了些代码),我们找到了解决方案:
在 Sass
编译后、CSS
输出前,对字体图标的 content
属性值进行 Unicode
编码转换
Vue CLI 配置
安装 css-unicode-loader
bash
npm install --save-dev css-unicode-loader
配置 vue.config.js
js
module.exports = {
configureWebpack: (config) => {
config.module.rules
.filter((rule) => {
return rule.test.toString().indexOf('scss') !== -1
})
.forEach((rule) => {
rule.oneOf.forEach((oneOfRule) => {
oneOfRule.use.splice(oneOfRule.use.indexOf(require.resolve('sass-loader')), 0, {
loader: require.resolve('css-unicode-loader')
})
})
})
}
}
[Bug Report] 使用 dart-sass 打包 element icon 出现乱码
编写 Rsbuild 自定义插件处理乱码
js
import path from 'node:path'
import fs from 'node:fs'
import { defineConfig } from '@rsbuild/core'
import { pluginVue2 } from '@rsbuild/plugin-vue2'
export default defineConfig({
source: {
entry: {
index: './src/main.js'
}
},
plugins: [pluginVue2()],
tools: {
rspack: {
plugins: [
{
name: 'rspack:content-unicode',
apply(compiler) {
compiler.hooks.afterEmit.tapPromise('ContentUnicodePlugin', async (compilation) => {
// 构建输出目录
const distDir = compilation.outputOptions.path
// 获取所有资源列表
const assets = compilation.getAssets()
await Promise.all(
assets.map(async ({ name }) => {
if (!name.endsWith('.css')) return
const filePath = path.join(distDir, name)
let css = await fs.promises.readFile(filePath, 'utf-8')
css = css.replace(/content:\s*(['"])(.*?)\1/g, (match, quote, contentStr) => {
const encoded = [...contentStr]
.map((char) => {
const code = char.charCodeAt(0)
if (code > 255) {
// 小写 Unicode,并补齐到 4 位
return '\\' + code.toString(16).toUpperCase().padStart(4, '0')
}
return char
})
.join('')
return `content: ${quote}${encoded}${quote}`
})
await fs.promises.writeFile(filePath, css, 'utf-8')
})
)
})
}
}
]
}
}
})
编写 Vite 自定义插件处理乱码
js
import { defineConfig } from 'vite'
import { createVuePlugin } from 'vite-plugin-vue2'
// https://vite.dev/config/
export default defineConfig({
plugins: [
createVuePlugin(),
{
name: 'vite-plugin-content-unicode',
apply: 'build',
enforce: 'post',
generateBundle(_, bundle) {
for (const fileName in bundle) {
const chunk = bundle[fileName]
if (chunk.type === 'asset' && fileName.endsWith('.css')) {
let css = chunk.source.toString()
css = css.replace(/content:\s*(['"])(.*?)\1/g, (match, quote, contentStr) => {
const converted = [...contentStr]
.map((char) => {
const code = char.charCodeAt(0)
if (code > 255) {
return '\\' + code.toString(16).toUpperCase().padStart(4, '0')
}
return char
})
.join('')
return `content: ${quote}${converted}${quote}`
})
chunk.source = css
}
}
}
}
],
css: {
preprocessorOptions: {
scss: {
api: 'modern-compiler',
additionalData: `@import "element-ui/lib/theme-chalk/index.css";`
}
}
},
build: {
assetsInlineLimit: 0,
rollupOptions: {
output: {
manualChunks: undefined
}
}
}
})