首页 >web前端 >html教程 >WebFont 智能压缩工具——字蛛 1.0.0 正式版发布_html/css_WEB-ITnose

WebFont 智能压缩工具——字蛛 1.0.0 正式版发布_html/css_WEB-ITnose

WBOY
WBOY原创
2016-06-24 11:18:151284浏览

字蛛是一个 WebFont 智能压缩工具,它能自动化分析页面中所使用的 WebFont 并进行按需压缩,通常好几 MB 的中文字体可以被压缩成几 KB 大小。

字蛛主页: http://font-spider.org

字蛛从 2014 年 7 月诞生以来,时隔近两年,终于发布了 v1.0.0 正式版本,改进如下:

  1. 支持绝大多数的中英文 Truetype 字体
  2. 支持开源图标字体库 (New: v1.0.0新特性)
  3. 支持 CSS 伪元素解析,支持 content: "string" 与 content: attr(value) 表达式
  4. 支持远程页面解析,并支持资源映射
  5. 支持四种样式规则:
  6. 支持四种调用方式:命令行、Gulp、Grunt、JS Api
  7. 性能、稳定性大幅提高

新特性:图标字体库压缩

得益于对 CSS 伪元素的支持,除了常规中英文字体压缩之外,v1.0.0 还带来了万众期待的——图标字体压缩支持,能够支持业界流行的开源图标字库。

以 Font Awesome 为例,它是一个典型的开源图标字体项目,目前包含有 628 个图标,并且还不断在添加中。虽然它已经做了很多优化,但字库的体积在移动端来说依然偏大,会影响页面载入速度。使用字蛛可以删除掉字体中没有用到的图标,将字体瘦身。例如一个使用 Font Awesome 的示例页面:

输入 font-spider 命令,启动字蛛进行字体压缩:

经过字蛛分析与压缩处理后, Font Awesome 字体中只保留了页面所用到的 20 个图标,ttf 格式字体体积由 142 KB 降为 6 KB,如果再配合使用 Webpack 等前端工具将字体 Base64 编码后内嵌到 CSS 中,载入速度可以进一步提升。

爬虫实现原理

为什么字蛛能够找到字体中没有使用的字形数据?这里就涉及到对 HTML 与 CSS 的静态分析。

虚拟浏览器技术

字蛛 v1.0.0 版本使用了虚拟浏览器技术来实现 HTML 与 CSS 加载与解析,爬虫模块所依赖的浏览器相关 API 均为它提供。

  • 处理 标签以及资源定位
  • 加载 标签或 @import 语句导入的 CSS 文件
  • 处理 CSS Unicode 字符
  • 管理网络请求,处理资源映射配置
  • 支持 CSS3 选择器、样式表树、文本节点读取

由于虚拟浏览器部分涉及到太多的东西且不是本文重点,所以本文将会略过这部分细节。这部分代码已经分离出来并开源,有兴趣可以去了解: https://github.com/aui/browser-x

操作样式语法树

字蛛是通过解析样式表语法树(CSSOM)来获得 WebFont 信息,在浏览器中可以通过 document.styleSheets 来访问 CSS 的语法树,遍历 CSS 规则的函数实现:

// 遍历 CSS 的规则var eachCssRuleList = (function() {    // 遍历 CSSRuleList    function cssRuleListFor(cssRuleList, callback) {        var index = -1;        var length = cssRuleList.length;        var cssRule, cssStyleSheet;        while (++index < length) {            cssRule = cssRuleList[index];            // 导入的样式规则            if (cssRule instanceof CSSImportRule) {                cssStyleSheet = cssRule.styleSheet;                cssRuleListFor(cssStyleSheet.cssRules || [], callback);            // CSS 媒体查询规则            } else if (cssRule instanceof CSSMediaRule) {                cssRuleListFor(cssRule.cssRules || [], callback);            // 普通的规则            } else {                callback(cssRule);            }        }    }    return function(callback) {        var index = -1;        var styleSheetList = document.styleSheets;        var length = styleSheetList.length;        var cssStyleSheet, cssRuleList;        // 遍历 StyleSheetList        while (++index < length) {            cssStyleSheet = styleSheetList[index];            cssRuleList = cssStyleSheet.cssRules || [];            cssRuleListFor(cssRuleList, callback);        }    };})();

注:浏览器环境不允许访问跨域后的 CSSOM,但虚拟浏览器没有做此限制

查找字体

遍历样式表每一个规则,收集 CSSFontFaceRule 信息:

// 字体信息var webFonts = {};// 字体对应的元素列表var elements = {};// 找到 webFonteachCssRuleList(function(cssRule) {    if (cssRule instanceof CSSFontFaceRule) {        var style = cssRule.style;        var family = style['font-family'];        var src = style.src;        // 保存使用此字体的所有元素列表        elements[family] = [];        // 保存字体信息        webFonts[family] = {            family: family,            src: src,            chars: ''        };    }});

以如下页面作为示例:

<!DOCTYPE html><html lang="zh-CN"><head>    <meta charset="UTF-8">    <title>font-spider</title>    <style>        @font-face {            font-family: 'demo-font';            src: url('./demo-font.ttf');        }        h1.title {            font-family: 'demo-font';        }        h1.title::after {            content: '——海子';        }    </style></head><body>    <h1 class="title">面朝大海,春暖花开</h1></body></html>

得到 webFonts :

{    "demo-font": {        "family": "demo-font",        "src": "url(\"file:///Users/aui/Documents/demo-font.ttf\")",        "chars": ""    }}

查找字符

利用 document.querySelectorAll() 来获取使用 WebFont 的字符:

// 获取当前节点所使用的 webFontfunction matchFontFamily(cssRule) {    var style = cssRule.style;    var family = style['font-family'];    return webFonts[family];}// 将 fontFace 与元素、字符关联起来eachCssRuleList(function(cssRule) {    if (cssRule instanceof CSSStyleRule) {        var selector = cssRule.selectorText;        var webfont = matchFontFamily(cssRule);        if (webfont) {            // 根据选择器来查找元素            var elems = document.querySelectorAll(selector);            Array.prototype.forEach.call(elems, function(element) {                // 获取元素的文本                webfont.chars += element.textContent;                // 将元素与字体关联起来                elements[webfont.family].push(element);            });        }    }});

此时 webFonts :

{    "demo-font": {        "family": "demo-font",        "src": "url(\"file:///Users/aui/Documents/demo-font.ttf\")",        "chars": "面朝大海,春暖花开"    }}

伪元素

// 处理伪元素,找到继承的 webFonteachCssRuleList(function(cssRule) {    if (cssRule instanceof CSSStyleRule) {        var selector = cssRule.selectorText;        var pseudoName = /\:\:?(?:before|after)$/i;        if (!pseudoName.test(selector)) {            return;        }        // 查找伪元素所在的节点        selector = selector.replace(pseudoName, '');        var elems = document.querySelectorAll(selector);        // 获取伪元素 content 值        var content = cssRule.style.content.replace(/^["']|["']$/g, '');        for (var i = 0; i < elems.length; i ++) {            var elem = elems[i];            for (var family in webFonts) {                // 从伪元素自身不断冒泡,直到找到继承的字体                while (elem) {                    if (elements[family].indexOf(elem) !== -1) {                        webFonts[family].chars += content;                        break;                    }                    elem = elem.parentNode;                }            }        }    }});

此时 WebFont:

{    "demo-font": {        "family": "demo-font",        "src": "url(\"file:///Users/aui/Documents/demo-font.ttf\")",        "chars": "面朝大海,春暖花开————海子"    }}

完整代码在线演示: https://jsfiddle.net/9ont96c4/2

至此,以上例子已经成功演示了字蛛爬虫查找字体、查找文本的工作原理。实际上 HTML 与 CSS 远比上面示例页面复杂,需要处理:

  1. 伪类选择器
  2. font 缩写
  3. 行内样式
  4. 完整的字体匹配算法

由于篇幅有限,上述细节部分可以参见 字蛛爬虫模块源码 。

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn