蒋小昆的博客
做一个安静的小开发

Web字体图标-自动化方案

|

缘起

Web从诞生之日起是为了展示图片文字的,最典型的代表就是imgspan。这是构建丰富的Web页面的基石,也是理解浏览器表现(Browser performance)的基础。本文将从从图片中的小图标切入,介绍一下Web小图标的技术方案的演进。

Web中小图标方案的演进

笔者本来打算从Image Sprites,到 Svg Spritesiconfontsvg Icon 整体介绍一遍的,在查阅资料之后发现已经有大佬写过了:Web中的图标-大漠,介绍得算是很详细(绝大部分应用场景都已经能覆盖了),这一部分本文就不再赘述了。

笔者的补充

  1. MDN关于 Image sprites 的备注:当使用 HTTP/2 时,使用多个小流量请求实际上可能更为带宽友好。
  2. DataURI(图片Base64)(笔者的建议是慎用,它可能成为一个性能瓶颈),因为CSS在解析过程遇到Base64,每次都需要解码,这会阻塞关键CSS的渲染,具体可以看这一篇文章:深挖 data URI 性能瓶颈

    趁机纠正了自己之前的错误:Gzip是针对html/css/js的,.wotf/.png/.ico 等图片资源是不进行Gzip压缩,如果不确定可以查看Network中的Response Headers

言归正传

接下来终于轮到了本文中的主角:Icon Font 登场了~~~。相比位图 png Sprites 而言,使用字体图标可以不受限于屏幕分辨率,而且字体图标另一个优势是:只要适合字体相关的CSS属性都适合字体图标。笔者经过调研之后在项目中确定了 Icon font 的方案。

逝去的青春

最开始是将svg上传到 https://icomoon.io/,生成对应的字体和样式文件,然后引入到项目中。

问题1:每次update图标都需要重复上面的步骤,然后再把文件下载下来,然后复制到项目源码中替换旧的文件

问题2:样式文件不好管理,因为icomoon下载下来的样式文件因为兼容性的需要,是需要进行修改的,以致于每次得摘出icon-list, 原有已进行兼容的代码保留。

问题3:当时 icomoon免费版 没有提供去色构建字体图标的功能,导致还需要对生成的样式进行一定的定制(调色,调位置)

问题4:更换了设计师之后,设计出来的某些图标文件引入之后,icomoon 生成的字体全部没法用了。终于忍无可忍……

探索方案

因为源项目中使用gulpwebpack打包,心中最理想的方案当然是webpackplugin或者loader,或者gulpplugin。尝试过gulp-iconfont结合gulp-iconfont-css已经等等其他方案,要不是因为一些细节方面没有配置成功,要不就是成功构建出来的字体图片有明显的偏移或者锯齿。多番查阅资料和实践之后终于发现了一个不那么起眼的库webfonts-loader

webfonts-loader

第一眼见到TA的时候,我未曾想过这将会是陪伴我许久的那一个,无论是从TA可怜的78❤️,还是作者本人jeerbl的知名度,但作为一个勇(zou)于(tou)尝(wu)鲜(lu)的前端,还是认真地读了一下TAREADME,看到了熟悉的webpackloader配置和js的语法,决定好好地实践一下。(扯远了)

原理(工作流程)

webfonts-loader(借助webpack)如何一步一步地将svg的图标处理成我们最终想要的样子?

How does the webfonts-loader which work with webpack build svg icons step by step ?

先来看最关键的3份配置:

scionfont.js
module.exports = {
	files: [
		'./svg/*.svg',
	],
	fontName: 'bicon',
	classPrefix: 'bicon-',
	fixedWidth: true,
	types: ['eot', 'woff', 'ttf', 'svg'],
	cssTemplate: './template.hbs'
};
iconfont.js loader
{
    test: /siconfont.js/,
    use: [
        'vue-style-loader',
        'css-loader',
        {
            loader: 'webfonts-loader',
            options: isProd ? {
                fileName: assetsPath('img/[fontname]-[hash:7].[ext]')
            } : {}
        }
    ]
}
fonts loader
{
    test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
    loader: 'url-loader',
    options: {
        limit: 10000,
        name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
    }
}
  1. 首先,webpack解析到 import './assets/fonts/siconfont.js'
  2. 接着,webpack会将对应的siconfont.js加载到webpack的工作流中
  3. siconfont.js通过正则匹配到了对应的siconfont.js的webpack相应的 loader(上面第2份配置)
  4. 首先使用webfonts-loader来处理这个xx.jswebfonts-loader解析这个siconfont.js(上面的第一份配置) 之后, 会将当前目录svg下的所有svg图标文件都加载进来,并且将相应的CSS模板也加载了进来,并且通过一些项的配置

    e.g:配置 types: ['eot', 'woff', 'ttf', 'svg'],表示会打包出四种字体图标文件

  5. 接下来的步骤就简单了:webfonts-loader处理完成之后输出两种文件流:(1).eot/.woff 等字体文件流 (2) 对应的关联css流
  6. 文件流下一步进入css-loader,该importimport,字体图标路径该resolveresolve,还有其他可能需要的工作。
  7. 最终生成的CSS可能通过style-loader直接内嵌到html 的style标签中,也可以通过Extract插件提取出来整合到最终页面的CSS 中,然后通过link使用

再一次感受到 webpack 自动收集依赖和管理依赖,真的是很强大,为我们省了太多心

大功告成

借助着webpack的热更新机制,我们可以很方面地去更新svg文件夹下的字体文件,并即时使用。使用步骤:

  1. 拖入文件 left-arrow.svg
  2. 在组件中对应DOM上添加类名(class): bicon-left-arrow
  3. 刷新浏览器,字体图标已经加上~

写在最后

文中的方案已经在手机搜狐网手搜体育频道等多个项目中实践和使用,能覆盖绝大多数业务场景。文中完整的示例可以查看 vue-ssr-start ,欢迎 star※

Vue-ssr-start 是我学习从零开始搭建一个SSR脚手架的理解和记录,还有一些对前端工程化的思考和探索,会持续迭代并加上完整的注释,欢迎watch。水平有限,出错难免,欢迎各位同学多多指正,共同进步~

参考

webfonts-loader

Web中的图标-大漠

深挖 data URI 性能瓶颈

icomoon

iOS 关闭升级红色角标

|

缘起

众所周知,iOS因为其封闭性,存在两大特点

  • 已关闭验证的版本,无法再降回去
  • iOS会想尽一切办法趁你不注意让你升级到最新的系统,包括但不局限于时而不时给你弹个窗,晚上偷偷给你下个数据包。而其中最恶心莫过于每次进入设置页面,都有一个醒目的角标提醒你去升级,在一个整洁的界面中,这个红角标的1真是烦skr
  • 很多人使用 iOS 描述文件去解决这个问题,但是描述文件会过期,而且角标出现了再安描述文件角标依然去不掉(手动斜眼)

    iOS12 新增一个关闭更新提示的开关。如果你认为苹果这就不再骚扰你,让你安安静静用着流畅的老系统了吗?骚年,你还是太天真了!

一个强迫症的反抗

上网找了很多资料,却没有一个稳定可靠的方案,还是自己动手吧。经过实践选择了描述文件的方案,细节处有一些不一样

描述文件屏蔽更新的原理

tvOS 描述文件 安装在iOS里面,导致iOS检查更新找到的是 tvOSota 更新,不符合iOS系统ota更新,就起到了屏蔽iOS系统更新的目的

方案

注意:如果你手机已经使用很久了,需要你先备份之后再进入下一步

  1. 依次进入设置(Settings)-通用(General)-iPhone存储(iPhone Storage),找到 iOS的更新文件,然后进行删除
  2. 同样是在通用(General),滑动到最底端,点击还原(Reset),选择抹掉所有内容和设置(Erase All Content and Settings), 在弹出的对话框中选择先备份iCloud或者直接重置
  3. 重新设置手机,进入系统之后,先打开 Safari 浏览器,输入 https://oldcat.me/web/NOOTA9.mobileconfig安装描述文件 地址大小写敏感。在弹出的描述文件安装对话框中点击允许,可以看到这个描述文件: Signed by Apple Care Profile Sining Certificate,并且打上了√勾,这是苹果认证签名的证书,可以放心安装
  1. 安装描述文件之后会在桌面上多出来一个 feedback 的图标,如果觉得碍眼,可以把它拖到一个不常用的文件夹下

如果你不小心在重置系统之后手贱点了检查更新,角标已经出现,请再次重置系统重新操作

悲惨的消息

某一天我再连上iTunes之后,这个该死的红色角标又出现了,Google了多个外文网站,找到了两个暂时缓解的办法。

如果你是个狠人,文件都有备份的话,可以再次重置所有设置(数据不会丢,但是图标位置,相关设置会丢),然后再次安装 描述文件

  1. 进入设置,点击头像(Apple ID, iCloud, iTunes…) - 点击 iCloud,点击 iCloud备份(iCloud Backup),将其关闭,虽然设置里面角标还在,但是主屏幕上是不是已经没有啦?
  2. 如果上面这一步还是不行,可以试试将 iCloud 登出(Sign out),此操作注意备份存放在iCloud中的密码已经配置等,登出之后wallet中的公交或者银行卡都需要重新绑定。

恢复更新提示

如果你哪天反悔了,又想接受苹果的更新了。可以再次进入设置-通用,找到描述文件(Profile),将 tvOS的描述文件删除,然后重启即可

按照最近苹果越升级BUG越多,越难用的德行,笔者不建议你这么做(手动斜眼)

一个iTunes12.8制作铃声的神奇BUG

|

缘起

使用iOS的同学们,还记得听到熟悉的苹果自带铃声响起,拿起手机发现是别人的电话的尴尬么,可能是苹果本身的产品理念,希望大家以自己是一个iPhone用户而自豪,所以把 iOS 自定义铃声的门槛设置得比较高。作为一个追求自由的程序员,当然是要DIY的,但是在使用DFU模式刷机之后,原来手机的铃声就没了。而在重新制作铃声的时候遇到了一些奇特的问题,所以觉得有必要记录一下

环境

  • Mac: 10.13.6 (17G4015)
  • iTunes: 12.8.0.150
  • iPhone: 12.1.1

写在前面

  1. 如果你的iPhone连不上电脑,即使版本号可能是比较新的,也可能卸了重装,重装的版本在启动的时候会再次提示有升级包需要安装,点击安装
  2. iTunes 12.8 左上角的”铃声”已经去掉了,所以原来的教程就变得有一些偏差了

按部就班

  1. 将设备连接到电脑,打开iTunes,需要在iPhone端信任电脑,需要手机上输入密码授权
  2. 将iTunes左上角的下拉菜单选中音乐
  3. 找到你需要制作成铃声的源文件,使用Command+C复制,然后点击左上角的歌曲,按住 Command+V拷贝到iTunes的歌曲库中
  4. 右键歌曲文件,点击歌曲信息,在弹出窗口中,点击选项,点击开始,输入音频裁切起始点,点击停止,输入音频裁切结束点。点击确认
  5. 点击最上方的菜单栏中的文件-转换-创建 AAC 版本,然后就会发现在原来的音频文件下面多了一个40s以内音频文件
  6. 右击新生成的音频文件,在访达(Finder)中显示,可以看到一个以.m4a 结尾的音频文件。
  7. 将音频文件后缀改成.m4r并确认。选中文件,执行Command+C,回到iTunes,点开XXX’s iPhone(如果已经展开的话就不要点了),点击铃声,执行Command+V,可以看到右边的铃声下面已经有了刚才我们制作好的铃声了。
  8. 打开手机,选中设置-声音与触感,点击电话铃声,正常情况下,在最顶端已经出现了我们DIY的铃声了,大功告成

可能遇到的问题

手机连不上iTunes

Windows可能需要卸载重装,Mac直接去官网下载然后重新安装即可,重装之后再次提醒安装更新,老老实实安装

铃声无法拷贝到iTunes中
  1. 铃声超过40S
  2. 铃声文件格式不是.m4r
    .m4r铃声已经拷贝到铃声中,但是手机上还是没有

    笔者就遇到了这个问题,因为原来制作铃声需要同步这一步骤,导致了同名.m4r 文件已经拷贝到手机上了,而且被识别成了music,而不是 ringtone.

再次拖动.m4r 到iTunes,虽然拖进去了,但是手机上还是没有

解决方法:

  1. 在手机上进入Apple Music,将音频文件从资料库中删除
  2. 或者使用iTunes的同步将手机上的音频文件抹除

资源

将自定铃声从 iTunes 移动至 iOS 设备

iTunes下载

记一次控制台addEvent调试

|

缘起

为了研究Dom Eventthis的指向问题。在控制台中给 DOM 绑定事件后,点击 DOM 之后却死活不打印日志,虽然最后结论非常简单,但是追溯问题的过程很有意思!值得记录一下。

排查过程

<div class="left wdC">客服邮箱</div>
$('left wdC').on('click', function() {
	console.log(this);
});

1. 确认 $ 是不是正常

  1. 在控制台输入:$ 输出:ƒ (e,t){return new de.fn.init(e,t)}
  2. 如果全局的$没有被jQuery或者Zepto等使用,会输出

证明 jQuery 被正确加载,且已经在 window 全局注册

2. 确认元素是否被正确选中

  1. $('left wdC') 展开之后的length 为 0,context指向 document,证明并没有选择到元素

    如果使用的是原生的API,document.querySelector('left wdC');, 则会返回 null

  1. 仔细一看,发现把 DOM 上的 class名 直接拷贝过来做成 selector 了,显然是错误的。
  2. 改成 $('.left.wdC'), 发现返回的结果length 为 1,key 为 0 的项指向的就是对应的 DOM

3. 确认对应的监听是否已经添加到 Dom

右键点击 检查,在弹出的调试窗口点击 Event Listeners, 点击 click, 拉到最下面发现 key 为 div.left.wdC 事件,证明监听已经加上

或者使用 Chrome 提供的调试 API: getEventListeners($('.left.wdC')[0]),也能看到对应的DOM上存在了click 事件侦听

还是没有打印日志,难道把这个元素的事件禁止了?

4. 确认监听事件的回调函数能够正常调用

  • 找到该元素,手动给element.style添加pointer-events: auto;

还是不行,进一步确认回调函数是否被执行。

5 确认回调函数确实被调用了

Event Listeners 中点击 VM 开头的链接,在对应的资源中打上断点。再次点击,果然停在了断点处,证明点击事件的 回调函数被正常执行!!!

6. 确认回调函数中的代码被正确执行

回调函数真正执行的只有一行代码,但却没有成功?

  • 我们 在控制台输入 console.log.toString(), 输出的是一个函数体没有内容的空函数 "function(){}",
  • 而有函数体的函数执行 toString 会将函数体内的内容输出出来
  • 打开 Google 输入 console.log.toString(),输出 "function log() { [native code] }"

  • !!! 证明 console.log 被改写了!

    可以在断点处查看 console.log, 发现其定义在项目中的 main.js 中,而不是浏览器的 Native API

回调函数不执行其他可能的原因

  • 在需要监听的元素上面有另外一个浮层,导致事件无法触发
  • Chrome 调试移动端页面时,touch圆环在窗口中的位置,和实际点击到页面中的位置存在偏移

结论

因为IE8使用console,狐首为了兼容 IE8, 将 console 相关方法全部重写成了空函数,导致控制台不输出日志。如果这里原来是 alert,很可能就没有这篇文章了,哈哈~

思考

在开发的过程中,难免会因为开小差写错一个变量名而导致错误;因为对错误想当然而导致与真正的问题南辕北辙。当遇到问题的时候,要相信问题总能被解决(暂时没解决可能是缺了点耐心,或者忽略了一些细节),遇到问题的时候恰恰是解决问题和提高的时候。小错误的价值不一定在于问题本身,可能在分析问题的完整方法论,以及在解决问题中发现的自己的短板意想不到的小惊喜!

意外收获

Chrome Command Line API 参考

Charles破解与HTTPS

|

基础环境

MacOS:10.13.6 iPhone6s:11.4.1

准备工作

下载 Charles

  • 官网:https://www.charlesproxy.com/download/

    百度云:https://pan.baidu.com/s/1sTAbB_AZ_WkIgRsqdnoIOA 密码:xcoz

破解 jar 文件

在线生成jar:https://www.zzzmode.com/mytools/charles/

百度云:https://pan.baidu.com/s/1w4bugeIW2HKIsspvQpu8OQ 密码:zmql

Charles破解

  1. 下载Charles并安装
  2. 下载破解的charles.jar,替换掉安装目录下的 charles.jar

    macOS: /Applications/Charles.app/Contents/Java/charles.jar

    Windows: YOUR_DIR\Charles\lib\charles.jar

Charles支持手机HTTPS

在Mac上安装 Charles 根证书

1.点击最上面的菜单栏的Help,选择SSL proxying, 再选择Install Charles Root Cerificate

  1. 弹出添加证书对话框,点击添加,输入密码之后,点击修改钥匙串确认
  2. 这时候添加的证书默认是不受信任的,找到Charles Proxy CA开头的证书,右键显示简介,点开信任,在展开菜单中的使用此证书时,选择始终信任,点击左上角的关闭,弹出对话框后输入密码更新设置即可。

在手机上安装证书

  1. 让手机代理连上电脑的Charles(跟普通手机连代理的步骤一样)

    打开无限局域网,选中同一个WiFi,点击配置代理,选择手动,在弹出的表单页面中,服务器填写电脑IP,端口填写8888(Charles默认端口是8888)

  2. 打开Safari,在浏览器中输入chls.pro/ssl(或http://charlesproxy.com/getssl),会弹出对话框让你跳转到设置安装描述文件,点击允许,在证书安装页面,点击安装,一通确认之后,点击完成
  3. 设置证书信任(低版本的IOS可能不需要). 打开iPhone的:设置 > 通用 > 关于本机 > 证书信任设置,在针对根证书启用完全信任Charles 开头的证书设置打开

在 Charles 中配置

  1. 点击顶部菜单中Proxy, 选择Proxy Setting...,在弹出的对话框中将Enable transparent HTTP proxying前面勾上,点击OK确认
  2. 点击顶部菜单中Proxy, 选择SSL Proxying Settings...,将Enable SSL Proxying 前面勾上,点击Add, Port统一为443
    • 如果你只是测试特定的网站的HTTPS,在Host中填入你要测试的域名,比如m.sohu.com
    • 如果你就是要让Charles能抓HTTPS的包,在Host填入*即可
  3. 点击OK add Rules,再点击OK 确认

大功告成

参考

抓包工具——–Charles(Mac)破解攻略看我就够了!亲测有效