前端开发中,经常需要引入中文字体库,但由于中文的字体文件文件太大,需要进行裁剪才能用于线上环境。
另外,随着现在字体图标的普及,许多glyphicons的字体文件,也需要裁剪才能发布到线上。
裁剪字体文件的常用方式是使用fontmin,通过传入一个字符串,把字体文件裁剪成只包含这个字符串里字符的文件。通常的做法是,在发布之前,使用fontmin工具处理一下字体文件,并生成最终需要的文件。
随着gulp、webpack等前端工程化构建工具的普及,我们需要在构建项目时引入fontmin,在构建过程中输出裁剪后的字体文件。gulp的fontmin工具:gulp-fontmin。
使用webpack进行项目构建时,有两种方式可以引入fontmin。
一种是使用loader,在加载ttf、woff、svg等字体格式文件时,使用特定的loader加载文件,传入一个字符串并输出裁剪结果。笔者在搜索时只发现了一个哥们两年前写的fontmin-loader,但是无法使用,于是自己动手写了一个:awesome-fontmin-loader,使用方式如下:
{
test: /\.(svg|woff|woff2|ttf|eot)$/,
use: [
{
loader: 'awesome-fontmin-loader',
options: {
limit: 1000,
name: 'assets/fonts/[name].[hash].[ext]',
text: '天地玄黄宇宙洪荒' // 使用到的字符
}
}
]
}
其中text是项目中使用到的字符组成的字符串,使用awesome-fontmin-loader加载的字体文件,只会包含text中的字符。
另一种方式是使用webpack plugin,当前可用的方案是fontmin-webpack,这个plugin是用来收集css中的glyphicons字符并裁剪字体文件的,也可以用于汉字字体文件的裁剪,使用方式是传入一个字符数组:
new FontminPlugin({
autodetect: false,
glyphs: fontCharactors // 字符数组
})
解决了裁剪字体文件的问题,还有一个问题是如何获取要传入loader或者plugin的字符串或字符数组。
如果你的网页中使用到的文本都集中维护在一个或几个文件里,获取这些文本中的字符会简单些。笔者遇到的情况是网页的文本都分散在各个目录中的html/ts/json等文件里,所以需要扫描这些代码文件提取出字符。
扫描工作一般需要遍历文件夹,取出每个文件中的字符,然后去重,这样会生成一个网页显示文字的字符集的超集,因为代码中的字符、注释中的字符等不需要的字符也都被加了进来,但尽管如此,字符的数目还是会远小于字体文件中的字符总数,因此可以用来有效裁剪字体文件。
这个扫描的小程序被封装成了一个node module,如果你需要使用它,请访问:charactor-scanner,这个module会输出一个函数,接收文件夹路径等参数并输出一个字符数组。
const Scan = require('charactor-scanner');
Scan({
dir: [path.resolve(__dirname, './test-directory')]
}, data => console.log("Async Callback call: ", data.join()));
// Async Callback call: {,|,},~,?,?,ノ,ン,リ,ピ,ー,ト,的,方,式,来,和,会,计,师,费
Scan({
dir: path.resolve(__dirname, './test-directory')
}).then(data => console.log("Async Promise call: ", data.join()));
// Async Promise call: !,",#,$,方,式,来,和,会,计,师,费,?,?,ノ,ン,リ,ピ,ー,ト
console.log("Sync call: ", Scan({
dir: path.resolve(__dirname, './test-directory'),
sync: true,
appendAscii: false
}).join());
// Sync call: {,",a,:,?,?,,,b,ノ,ン,リ,ピ,ー,ト,},c,o,n,s,l,e,.,g
扫描文件夹一般是一个异步操作,如何在webpack配置中引入异步操作呢?
webpack 2.x已经支持config文件返回一个promise对象,因此可以把webpack.config.js写成类似下面这样:
const scan = require('charactor-scanner');
module.exports = function () {
return scan({
dir: [helpers.root('src')],
ext: ['html', 'css', 'ts', 'json', 'js']
}).then(fontCharactors => webpackMerge(commonConfig, {
module: {
rules: [
{
test: /\.(svg|woff|woff2|ttf|eot)$/,
use: [
{
loader: 'awesome-fontmin-loader',
options: {
limit: 1000,
name: 'assets/fonts/[name].[hash].[ext]',
text: fontCharactors.join('')
}
}
]
}
]
}
}))
};
这样,就可以在webpack构建的时候,扫描代码文件并裁剪字体文件了。