前端字体压缩:字体包体积优化

376 阅读2分钟

在项目中使用字体包,它的体积一般都是很大的,在一些网站去压缩其体积,大多数都能减少50%左右这样,其实也不错了。但是对于一些对包体积大小有要求的项目而言,减少的这50%还是远远不够,体积占比还是很大。如果还想要继续压缩,该怎么办呢?

image.png
我们可以使用 fontmin,如下

1、安装 fontmin

npm install fontmin -g //全局

npm install fontmin --save-dev //在项目中安装为依赖

2、使用 动态统计文字

网上大多数例子都是写死自己统计文字,这种既繁琐又不友好。
咱们程序员比较懒,所以介绍一种动态的:
创建一个文件夹 如 fontmin-compress.cjs,与src同级

const Fontmin = require("fontmin");
const fs = require("fs");
const path = require("path");

// 源字体文件目录
const inputDir = path.join(__dirname, "src/assets/fontsFile");
// 输出目录
const outputDir = path.join(__dirname, "src/assets/font");
// 项目文件目录
const projectDir = path.join(__dirname, "src");

// 正则表达式:匹配文字内容
const textRegex = /[\u4e00-\u9fa5a-zA-Z0-9]/g;

// 正则表达式:匹配注释
const commentRegex = {
    js: /\/\/.*|\/\*[\s\S]*?\*\//g,
    html: /<!--([\s\S]*?)-->/g,
    css: /\/\*[\s\S]*?\*\//g,
};

// 移除文件中的注释
function removeComments(content, fileType) {
    if (fileType === "js" || fileType === "vue") {
        return content.replace(commentRegex.js, "");
    } else if (fileType === "html") {
        return content.replace(commentRegex.html, "");
    } else if (fileType === "css") {
        return content.replace(commentRegex.css, "");
    }
    return content;
}

// 动态提取项目中的所有文字
function extractTextFromProject(dir) {
    let allText = "";

    const traverseDir = (directory) => {
        const files = fs.readdirSync(directory);

        files.forEach((file) => {
            const filePath = path.join(directory, file);
            const stat = fs.statSync(filePath);

            if (stat.isDirectory()) {
                traverseDir(filePath); // 递归子目录
            } else if (/\.(html|js|vue|css)$/.test(file)) {
                // 仅处理 HTML、JS、Vue 和 CSS 文件
                const fileType = path.extname(file).slice(1); // 获取文件扩展名
                let content = fs.readFileSync(filePath, "utf-8");
                content = removeComments(content, fileType); // 移除注释
                const matches = content.match(textRegex); // 匹配中文、英文、数字
                if (matches) {
                    allText += matches.join(""); // 收集匹配到的文字
                }
            }
        });
    };

    traverseDir(dir);
    return Array.from(new Set(allText)).join(""); // 去重
}

// 获取项目中的文字
const text = extractTextFromProject(projectDir);

console.log("提取到的文字:", text);

// 获取所有字体文件
const fontFiles = fs.readdirSync(inputDir).filter((file) => {
    return /\.(ttf|otf|woff|woff2)$/.test(file); // 仅处理字体文件
});

// 批量处理字体文件
fontFiles.forEach((fontFile) => {
    const fontmin = new Fontmin()
        .src(path.join(inputDir, fontFile)) // 输入字体文件路径
        .use(Fontmin.glyph({ text })) // 使用动态提取的文字
        .dest(outputDir); // 输出压缩后的字体文件路径

    fontmin.run((err, files) => {
        if (err) {
            console.error(`压缩字体文件 ${fontFile} 时出错:`, err);
        } else {
            console.log(`成功压缩字体文件: ${fontFile}`);
        }
    });
});

该方法是只提取项目中用到的字的字体包,类似按需引入;

有一个缺点就是如果有些文案是接口返回来显示的,刚好页面没有用到这个字,没有统计下来;显示的时候就会出现一些有字体效果,一些是默认字体效果;
怎么解决呢?

image.png
自己跟后端协商好有哪些字,然后自己手动加上去!

image.png

3、项目打包

在项目打包时,我是先提取字体包出来,再打包项目。
所以在package.json中添加配置

  "build": "node fontmin-compress.cjs && vite build",

这样就完成了项目打包时,对字体包体积的优化啦!

image.png
压缩效果举例:优化前后对比
image.png