最近因项目需要要将之前做的 npm 包的 css 样式,包括字体样式和字体文件(主要是使用了阿里的iconfont)打到js中,因为之前样式都是直接还是用外链的方式,要改掉,因此,花了一天的时间搞这个,碰到了些比较奇葩的事情,在此记录一下。

下载 iconfont 相关文件到本地

image

去掉了一些用不到的文件剩下如图所示文件,包括iconfont.cssiconfont.eoticonfont.svgiconfont.ttficonfont.wofficonfont.woff2,iconfont.css是字体样式,其余的为字体文件:iconfont.woff2为主要目前流行的字体文件格式,兼容性最好,其他则是为了兼容不同浏览器及不同版本浏览器。

1
2
3
4
5
6
7
8
@font-face {font-family: "iconfont_sp";
src: url('iconfont.eot?t=1575340034756'); /* IE9 */
src: url('iconfont.eot?t=1575340034756#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff2;charset=utf-8;base64,此处为base64编码') format('woff2'),
url('iconfont.woff?t=1575340034756') format('woff'),
url('iconfont.ttf?t=1575340034756') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('iconfont.svg?t=1575340034756#iconfont_sp') format('svg'); /* iOS 4.1- */
}

由此可见,阿里 iconfont+ 平台给我们生成的样式里面除了 woff2 打成了 DataUrl 即 转换成base64编码的字符串形式,并存储在 URL 中,冠以 mime-type,其他都都是之间都是直接使用文件路径。

改造 webpack 配置,将 css 打包进 js,最终输出 一个 umd 模块化的 npm 模块 js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
const path = require('path');

module.exports = {
devtool: '#source-map',
entry: [ './src/index' ],
output: {
path: path.resolve(__dirname, 'build'),
filename: 'selectPartner.js',
libraryTarget: 'umd',
library: 'selectPartner'
},
module: {
rules: [
// ...
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader', 'postcss-loader' ],
include: path.resolve(__dirname, 'src')
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 20000,
name: 'fonts/[name]-[hash].[ext]'
}
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 20000,
name: 'images/[name]-[hash].[ext]'
}
},
// ...
]
},
resolve: {
extensions: ['.js', '.vue', '.json', '.html']
}
};

使用css-loader 处理 css 文件,使用 url-loader 对 css文件中引用的字体文件进行处理,将字体文件打包成 DataUrl 即 转换成base64编码的字符串形式,并存储在 URL 中,冠以 mime-type。当一切都准备好,打包也ok,确定代码逻辑啥的是没问题的。虽然打包成功,但是还是要测试一下,因为将最终打包出来的 umd 模块代码导入到项目代码中测试,测试结果如下图。

image

image

icon图表并没有显示出来,审查一下元素查看对应样式代码,url 除了有一个 base64 是 iconfont+打包下来自带的 base64,其他的都是 [object Module],这问题看起来很奇葩。

原因排查

网上兜了一圈,找了挺久,终于在 stackoverflow 上找到类型的问题。

image

大概的意思就是我打包出来的东西违反了 csp 相关内容,浏览器为了安全拒绝读取相应的DataUrl数据。

什么是 csp,即内容安全策略( Content-Security-Policy ),是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本 (XSS) 和数据注入攻击等。无论是数据盗取、网站内容污染还是散发恶意软件,这些攻击都是主要的手段。具体就不展开细说。了解 csp 可参见 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP

疑问点及尝试解决方案

其实最终我打包出来的js里面是有将 eot、ttf、woff等这些文件 DataUrl (base64)数据的,但是浏览器并没有执行。

image

image

综合发现,除了 woff2 的 DataUrl mineType 是 application/x-font-woff2,其他的都是 font/woff 这种方式。出于好奇,代码中把所有格式的字体 url 都去掉,只保留 woff2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@font-face {font-family: "iconfont_sp";
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAXAAAsAAAAAC3wAAAVwAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDeAqIVIcHATYCJAMoCxYABCAFhREHgQYbzwnIrrApw6/Q3XaiJEI0Mxiwx0nwjQviye6rXncP/woMj2AEMj0HoYTZk9Od/t30VSx1CzVJJ27QiaXJS6BIuJCKp5vDxICa2/7c/80Gzy2tBIfSjxJ84wF35VVS83I2ZGzwvcL+/59jqZcJlt/u2uX/615kYAVtYAPM59FFAxsWSBKMfSbi1Zucjsybx0MAnwzKkNZbdh0C5hgiv6DTZAmNA/NWAjOjFNywPbKtQTYBiqtWG84BG/OPly/UiDkoLE10ou5jN5NgXSANuZr//zJoHrzbywGMnUADZYABmTeaNxejsq4Mje/VGNMR4H+Gi0IFdlAT7PmY2xgQiPCcUZ5PHC0NCo3Bwsbx/vE8EJsoEAbuvHoPAokBBQGgIQAMBIAFAWBDoIcDQY0eAsEePVxoyE3gge2vHAdEgVSCnBfczh0FjUJAVJwWZhUuBXYUpvcbNcBKMI6u1fTzISSs5vvUYRRZpR7IDKepmsxBTSjx4KFtj1x5YPL9HT5dUPOZ68PRaPkqtYtIl+mq1xx78MG0BlcDm9+vXFPNVwVYVdRk1qb0hqSQpkU69R3eWajVuNL6nAqn4xxiEh8x9WFJ6hDEe95dhvWmYofYm4/oPqkvRR889LEqANFVuMi/FdlU1ueGarXO5fUgHoB0jGQuHgzW6w2icwtc1ujUTl0IcmCm5KYsbg0fDa2dLr7B7tzuZDuv1rYHb9XnGuQOBle2B2qr3fm8FNc3nBSjrebQHbULcmbB6NM7mLMwWl/p7KzlnQ79xw0BtglzuBHV2kdtxASFRoBtjLL3OQzPIwCX1XGjb/9NP2Gac1z315QQuoXeJmwzmryEodROXlQaTVxc9M7EpfR1dd3NOeL3LJv3xjwF3GXG91NT7luEacz4fs1OvbdU39oyR1GQEC1DhUEi4DQqbceOqrqq2h2dvozow9VU3IvYsT096sKUL3pZsiPr7bb7Vlrq2oKHaQRSvQ+A6Aaaq2Pbtnc0Eqbz/M//may0NJPE+wDRe0alWnHfut/d+uDQeaK1OY+Y1olHmZd6nVa0RU6eNNhSJDGbsMWg9TL2fE1n96HV42OYyZgFZm7LFxxRWxYIy+TusRM/XdNZ+9yNzsN9puadNn6e8sM4eCTQ4gascV6L4n771/oG+TmtNRS2iGInds8dDgpuGnKGHf9Q0DN8buDm6AyydOJDk5Ji/JAwpcRTdze9XOMF7dt0BWrLMq82s51+O0ufLou3+Q2Y6aub26ijXjULg+c0QOMG/V2n5nVr9S8d11b+0Gl16/VKnQ4EYQowqP0gsH/6ViGU+Kn/HOY+mX6fsl31z7U0ADwNfVJKWeMqA7aTcBkpR+Az7mSOML47SvrGHVzs+inVT8AIPjoxfBoHn/GiGF5uOxTP7ghCiPgaFA5ZoAlRyDTcGqbFbcK0ue3Bp5ReO4eIZQFGiPGAEjYLEBI4BooI7oAmgVdMw/3HsKRiybS5AvDpK0v3GaJAriIbWYhzkERjklAJwSgomeaQ4pWnVMoDkI6T4WyvZ6USQVYlmYzOx+aNNG6FCshubwNVPL2A4yiUYhk5ukWcDMpkDKpkmVhIcJiY45QroqIo/4kwgpEDhFawQHA4ICQoI0kCZQIEDAqoNMDdrj56v8kpVCg5AIQmDhk47PwARmxFQFhUSOYGpZoKqnjMrQEFt89jbDmLeGgWqEoOaqWxsDDIQWmLHIiM6hhQpo8vFQuEgANj/Bo1SlZQqYw1lhvAjiIkf/zdf65+ovP5okSLEUtsccQVT3wJYY2SMSpoKm+Os4TYlIVyJh6asBJazJkRYkhIIWlMMgkKizhF85JOkGBUSjFkYSTFsJExrISkoSmBKwgoAwAAAAA=') format('woff2');
}

.iconfont_sp {
font-family: "iconfont_sp" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

.icon_sp-close:before {
content: "\e632";
}
/* ... */

试了一下,果真可以了,iconfont 字体显示出来。

image

总结

通过 url-loader 打报出来的 eot、ttf、woff 等文件打包出来的 DataUrl 对应 MIME Types 是有问题的,违反,stackoverflow 上找到了相关内容,https://stackoverflow.com/questions/26177849/mime-types-for-woff-ttf-svg-and-eot-404ing-despite-being-setup-in-iis

<staticContent>
    <remove fileExtension=".woff" />
    <mimeMap fileExtension=".woff" mimeType="application/font-woff" />
    <remove fileExtension=".ttf" />
    <mimeMap fileExtension=".ttf" mimeType="application/font-ttf" />
    <remove fileExtension=".eot" />
    <mimeMap fileExtension=".eot" mimeType="application/vnd.ms-fontobject" />
    <remove fileExtension=".otf" />
    <mimeMap fileExtension=".otf" mimeType="application/font-otf" />
    <remove fileExtension=".svg" />
    <mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
</staticContent>