前端html字体包体积压缩,网站工程下字体压缩裁剪工具
整个网站项目如果字体包体积太大就会影响其加载速度,字体加载完会让页面字体突然变换。
做一个工具他会自动检索网站上所有展现给用户的字符,然后原地裁剪字体。来解决这个问题。
实现效果如下:
执行py文件以后,在网站字体文件所在目录下出现字体文件名称上加“mini”两个字的woff2后缀的字体文件,如果网站内容更新了,重新执行以下这个py文件会重新生成woff2文件,无需改css引用代码,直接生效。
关键python代码如下:
import os import glob from fontTools.subset import main as pyftsubset_main import tempfile # 常用字符,包含了英文字母、数字、常见标点符号 COMMON_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~·—……。,、;:?!‘’“”()〔〕【】——…~·¥%……&*()¥↑→↓㎡一二三四五六七八九十百千万亿零壹贰叁肆伍陆柒捌玖拾佰仟万亿零的一是了我不人在他有这个上到来要们以会能要出就那和要也得你都着下自可子说去对年同工个啊阿爱安按暗八把爸白百败班办半帮包保报北被本比边别并病不步部擦猜才材财采彩菜参草策查差产长常厂场唱车成吃出川传船床春此从村大代带单但党到道得的等低第地第点电店定东冬懂动都斗独读对队多儿二发法反方非分风夫父干刚高个给更工公共够古瓜关观管光广国过海含函好号合和河很红后候花化画话坏欢环回会火及几己家间见叫教节介今尽进经精九就军开看可空口块快来兰老乐类里理力立利两亮量了林灵六路旅率妈吗买满毛么没每美门们米面民名么母木目那南内能你年鸟牛女欧怕排片品平七起气千前强亲青清情请全让人日三山上少社深什生师十时食使世市事手受书数水说思四送诉算岁他所太谈特提体天听同头土外完玩晚万王为文问我无五西习洗下先现相想向小校些心新信星性休学血眼阳要也业一医以因应永用有友又于余雨语元原远院月云再在早造则怎增展站张长找这真正政只知植直中种重主住抓转装子自总走租作坐做飞利浦影音京东自营店格乐飞甘尼克史达德雅培健康蒲地蓝龙角散纪梵希沁源专营星瑞海外博朗快手抖音厨电电器拼多多官方健安喜澳佳宝阿里猫超钙尔奇善存迈胜皇家怡式比乐金馨稳赛必健创国觅籍安蒂花子唯品会博世血糖仪照明智能家居美舒律雅赞工业品橙致奥特莱斯佳思敏营养亚马逊固益素金蓓高煊楷虔奉全屋智能皖焰轩耐皖梦优凯图芮好尹来途芮冠升妙朵焰名杰逸护眼办公家装建材集成吊顶电工灯具轩适家居郡典轩诚筑斐冠旭吉祥森宝上海金普突锐家居电气时尚灯饰生活电器品牌全安素妮飘维诗朵零公里雀巢依云碧然德伊利力度伸拜耳自然之宝城市超市舒尔欧舒丹帮宝适贝德玛星巴克健美生嘉宝美赞臣蓝臻夏日纷马迭尔护博士海普诺凯花王须尽欢西铁城飞鹤爱本凯伍德嘉士伯净水禾泱泱视觉旗舰" # 需要扫描的文件后缀 SCAN_EXTENSIONS = {'.html', '.vue', '.ts', '.js', '.css'} # 需要扫描的字体文件后缀 FONT_EXTENSIONS = {'.otf', '.ttf', '.woff', '.woff2'} # 需要排除扫描的目录(避免扫描依赖包和构建产物) EXCLUDE_DIRS = {'node_modules', 'dist', '.git', '.vscode', '.joycode'} def get_all_chars_from_files(directory): chars = set() for root, dirs, files in os.walk(directory): # 排除不需要扫描的目录 dirs[:] = [d for d in dirs if d not in EXCLUDE_DIRS] for file in files: ext = os.path.splitext(file)[1].lower() if ext in SCAN_EXTENSIONS: filepath = os.path.join(root, file) try: with open(filepath, 'r', encoding='utf-8') as f: content = f.read() # 把文件里的所有字符都加进去,这种方式最稳妥,不会漏掉通过JS拼接、innerHTML、CSS content等方式渲染的字符 chars.update(content) except Exception as e: print(f"读取文件失败,已跳过: {filepath}, 错误: {e}") return chars def find_font_files(directory): font_files = [] for root, dirs, files in os.walk(directory): dirs[:] = [d for d in dirs if d not in EXCLUDE_DIRS] for file in files: ext = os.path.splitext(file)[1].lower() if ext in FONT_EXTENSIONS: font_files.append(os.path.join(root, file)) return font_files def main(): print("1. 正在检索前端工程文件,提取页面字符...") project_dir = os.path.abspath(".") scanned_chars = get_all_chars_from_files(project_dir) print("2. 正在合并网站提取字符和内置常用字符...") all_chars_set = scanned_chars.union(set(COMMON_CHARS)) print("3. 正在对字符进行去重并清理不可见字符...") # 去除换行符等不可见的控制字符,保留正常可见字符 clean_chars = [c for c in all_chars_set if c.isprintable() and not c.isspace()] clean_chars.append(' ') # 确保包含空格 final_text = "".join(clean_chars) print(f"-> 最终提取并去重得到 {len(final_text)} 个唯一字符。") print("4. 正在检索工程中的字体文件...") fonts = find_font_files(project_dir) if not fonts: print("未找到任何字体文件 (otf, ttf, woff, woff2)。") return for font in fonts: print(f"-> 找到字体文件: {font}") print("5. 正在裁剪字体文件并生成新的 woff2 文件...") # 将需要保留的字符写入临时 txt 文件,以避免命令行参数过长或特殊字符引起解析错误 with tempfile.NamedTemporaryFile('w', encoding='utf-8', delete=False) as f: f.write(final_text) temp_txt_path = f.name try: for font in fonts: # 跳过名字里带 mini 的文件,防止重复裁剪 if "mini" in os.path.basename(font): print(f"跳过已裁剪的字体: {font}") continue dir_name = os.path.dirname(font) base_name = os.path.splitext(os.path.basename(font))[0] # 拼接新文件名,加上 mini 后缀 out_font = os.path.join(dir_name, f"{base_name}mini.woff2") print(f"正在裁剪: {font} -> {out_font}") # 使用 fonttools 的 pyftsubset 进行裁剪 # 参数说明:输入字体,--text-file 指定包含所有字符的文件,--output-file 指定输出,--flavor=woff2 输出网页最优格式 args = [ font, f"--text-file={temp_txt_path}", f"--output-file={out_font}", "--flavor=woff2" ] try: pyftsubset_main(args) print(f" [成功] 生成精简字体: {out_font}") except Exception as e: print(f" [失败] 裁剪 {font} 时出错: {e}") finally: # 清理临时文件 if os.path.exists(temp_txt_path): os.remove(temp_txt_path) print("所有字体裁剪任务完成!") if __name__ == "__main__": main()