jjzjj

你是来找茬的吧?对自己的博客进行调优

程语有云 2023-03-28 原文

“作为博客园的使用者而不是开发者,就不能对博客进行调优了?看好了,我只示范一次。” —— 我说的

0x00 大纲

0x01 前言

用过很多博客和写作平台,但是最终还是选择了博客园,毕竟,自定义 CSS 和自定义 JS 是真的香!某天突发奇想,决定对自己的博客进行下优化,现将其中的一些心得与大家分享。里面的一些方式方法可能不通用,但是思路可能会对各位有所启发。说实话,自己的本职工作并不是前端,不想搞那么复杂,就用 Edge 浏览器的开发者工具里的 lighthouse 评分和加载时间作为优化基准。

以博客的使用者而不是开发者身份去进行优化,究竟能做到何种程度,让我们拭目以待。

0x02 性能调优

减少加载项

  • (减少加载时间约40ms)在博客园后台-选项页面中取消勾选“启用数学公式支持”,点击“保存”按钮,刷新页面后至少可以减少以下几个加载项,节省页面加载时间约40ms:
    • MathJax.js
    • TeX-AMS-MML_HTMLorMML.js
    • extpfeil.js
    • mediawiki-texvc.js
  • (减少加载时间约50ms)在博客园后台-设置页面中“代码高亮”部分取消勾选“显示行号”,如果你用的是 prism 引擎,可以减少两个加载项:
    • prism-line-numbers.css
    • prism-line-numbers.js

减少网络传输量

  • 博文图片尽量采用 PNG 格式,内容色彩较少时可以使用索引颜色压缩。举个栗子,这是两张内容和分辨率一样,格式不同的两张二维码图片,可以看到 PNG 格式比 JPG 格式文件大小相差一个数量级:

降低渲染复杂度

博文和自定义 HTML 部分尽量减少 DOM 数量和嵌套。比如:

<div class="hint">HELLO WORLD</div>

如果能满足效果,就不要写成:

<div class="container">
    <div class="text">
        <span>HELLO WORLD</span>
    </div>
</div>

压缩资源文件

对于自定义的 CSS 或 JS 文件进行压缩,如果引用外部的 CSS 或 JS ,尽量使用其 min 版本。能省一个字节是一个字节。

减少字体引用

对于自定义的 CSS,尽量使用统一的字体定义,避免引入大量不同的字体。

body {font-family: Lato, PingFang SC, Microsoft YaHei, sans-serif;font-size: 14px;font-weight: 300;}

对于博客园中一些带了字体定义样式的部分,可以予以覆盖,比如文章支持数和反对数里面引入了 Verdara 字体,可以通过继承body中定义的font-family进行覆写:

.burynum,.diggnum{font-family:inherit;}

改善 CLS 布局偏移

CLS(Cumulative Layout Shift),累积布局偏移,是页面在整个生命周期中发生的每个元素布局变化的分数总和(分数越低越好)。这些变化会影响用户正确阅读内容和与页面交互的能力。所谓改善 CLS 就是要尽量保持页面上元素位置的稳定,避免页面元素在渲染过程中大幅度偏移初始位置。

常见的影响 CLS 的因素有:

  • 未指定宽高的图片。
  • 动态插入的内容(广告、iframe等嵌入元素)。
  • Web 字体的加载。
  • 阻塞 DOM 更新的操作(通常是关键样式或关键脚本加载等待网络响应)。

CLS 对性能总分的影响很大,由于每个人的自定义样式不尽相同,所以这一块只能根据个人情况对症下药了:

  • (降低 CLS 分数 0.002)尽可能地为图片元素设置明确的宽度值和高度值,以减少布局偏移并改善 CLS,比如首页的头像和名片:

    <!-- 头像 -->
    <img width="230px" height="230px" alt="avatar" class="esa-profile-avatar" src="https://images.cnblogs.com/cnblogs_com/mylibs/1647185/o_200214034545avatar.png">
    
    <!-- 名片 -->
    <img width="230px" height="230px" class="esa-profile-avatar" title="商务合作请添加微信" alt="商务合作请添加微信" src="https://images.cnblogs.com/cnblogs_com/mylibs/1647185/o_221216003723_card(wechat).png">
    
  • 在博客园后台-设置页面中“页脚 HTML 代码”处引入自定义 JS,而不是公告栏或者页首。这也是官方推荐做法。

  • (降低 CLS 分数 0.006)通过 CSS 而不是 JS 控制侧边栏的显示/隐藏:

    为了获得更好的阅读效果和较大的阅读版式,原先通过自定义 JS 动态隐藏了文章详情页的侧边栏,大概是这样的代码:

    if($("#post_detail").length > 0) {
        $("#sideBar").hide();
        $(".forFlow").css({"margin-left: 0 !important"});
    } else {
        $("#sideBar").show();
        $(".forFlow").css({"margin-left: 260px"});
    }
    

    由于自定义 JS 的加载时机较晚,所以页面难免出现大幅的偏移调整,导致 CSL 分数暴涨。这样子做是有历史原因的,因为最开始 CSS 还没有父选择器,不能通过子元素类型来选择父元素,在博客这里也就不能通过div.forFlow容器中的子元素内容来控制div.forFlow的偏移和mainContent的兄弟元素侧边栏sideBar,好在后来:has()伪类选择器横空出世,我可以这样子通过 CSS 来直接控制了:

    #mainContent .forFlow {
        margin-left: 260px;
        max-width: 960px;
    }
    
    .forFlow:has(#post_detail) {
        margin-left: 0 !important;
    }
    
    #mainContent:has(#post_detail)+#sideBar {
        display: none !important;
    }
    

    当作为博客主页显示时,由于#post_detail元素不存在,因此,div.forFlow容器会向左偏移260像素,为侧边栏留出显示空间;当用户点击文章进行阅读时,#post_detail元素将被加载,两个has()伪类选择器生效,达到取消偏移量和隐藏侧边栏的效果,缺点就是到目前为止,不是所有浏览器都支持。比如火狐就不支持……在不支持的浏览器上,它是可容错的,相当于这两个样式不存在,不会影响页面的可访问性。

    以我自己的博客为例,可以看到在 EdgeChrome 浏览器上,文章详情页中侧边栏是隐藏的,而在火狐浏览器上,侧边栏依然存在。

改善 FCP 首次内容绘制

FCP(First Contentful Paint),首次内容绘制,该指标测量页面从开始加载到页面内容的任何部分在屏幕上完成渲染的时间。对于该指标,“内容”指的是文本、图像(包括背景图像)、元素或非白色的元素。所谓改善 FCP 就是要让浏览页面的用户尽快地看到页面内容,即使此时页面还未完全渲染完成——以此优化用户体验。

能做的也不多,暂时发现比较有优化价值的是 Web 字体,可以利用 font-display 这项 CSS 特性,确保文本在网页字体加载期间始终对用户可见。比如在用到了自定义 Web Font 的地方,加上font-display: swap;,注意要在@font-face定义里面声明,否则无效,像这样:

@font-face {font-family: Material Symbols Outlined;font-display: swap;font-style: normal;font-weight: 300;src: url(https://cdn.cnblogs.com/iconfonts/iconfont-chnmig.ttf) format("truetype");}

直接减少 FCP 时间约60ms!但这还不是极限,大部分小伙伴引入的自定义字体多是 IconFont 之类的图标字体,但是一个图标字体里面可能有上百个冗余图标,我们往往只使用了其中的某几个。利用神器icomoon,我们可以只将自己用到的图标封装成自定义的字体,甚至可以将其以 base64 编码的形式,内嵌到自定义样式中,这可以带来数量级的加载时间提升(取决于原字体的大小)。以我自己的博客为例,将仅用到的12个图标转换成自定义字体后,首页的 FCP 时间减少了将近200ms

@font-face {font-family: 'iconfont-mylibs';src: url('data:font/woff;charset=utf-8;base64,(限于篇幅,此处省略数据定义)') format('woff');font-weight: 300;font-style: normal;font-display: swap;}

0x03 无障碍阅读改造

此部分主要参照标准为 WCAG 2.1 (AA), WCAG 2.0 (AA)

媒体查询

通过@media媒体查询适配不同分辨率的终端设备,以保证用户在不同的设备上,都能获得较佳的浏览体验。

色彩分离度

原主题在白天和黑夜模式共用一个主题颜色#2d8cf0,色彩分离度不是很好,尤其是小字体的时候,很容易导致阅读困难。遂将原主题白天模式前景色调整为#0f73e1,使其与背景色有更强的区分度,夜间模式时保持原来的颜色不变。关于采用何种前景色和后景色才具有良好分离度,可以使用这个链接里面的工具,提供动态参照对比。

正确使用鼠标手势

在自定义 HTML 和样式时,注意正确使用鼠标手势。比如在超链接和按钮上使用手形光标,在可选文本上使用文本光标,在非交互元素上使用默认光标等等。

类型 说明
auto 默认值。浏览器根据当前情况自动确定鼠标光标类型。
all-scroll 有上下左右四个箭头,中间有一个圆点的光标。用于标示页面可以向上下左右任何方向滚动。
col-resize 有左右两个箭头,中间由竖线分隔开的光标。用于标示项目或标题栏可以被水平改变尺寸。
crosshair 简单的十字线光标。
default 客户端平台的默认光标。通常是一个箭头。
hand 竖起一只手指的手形光标。就像通常用户将光标移到超链接上时那样。
move 十字箭头光标。用于标示对象可被移动。
help 带有问号标记的箭头。用于标示有帮助信息存在。
no-drop 带有一个被斜线贯穿的圆圈的手形光标。用于标示被拖起的对象不允许在光标的当前位置被放下。
not-allowed 禁止标记(一个被斜线贯穿的圆圈)光标。用于标示请求的操作不允许被执行。
pointer hand一样。竖起一只手指的手形光标。就像通常用户将光标移到超链接上时那样。
progress 带有沙漏标记的箭头光标。用于标示一个进程正在后台运行。
row-resize 有上下两个箭头,中间由横线分隔开的光标。用于标示项目或标题栏可以被垂直改变尺寸。
text 用于标示可编辑的水平文本的光标。通常是大写字母I的形状。
vertical-text 用于标示可编辑的垂直文本的光标。通常是大写字母I旋转90度的形状。
wait 用于标示程序忙用户需要等待的光标。通常是沙漏或手表的形状。
*-resize 用于标示对象可被改变尺寸方向的箭头光标。w-resize|s-resize|n-resize|e-resize|ne-resize|sw-resize|se-resize|nw-resize
url (url) 用户自定义光标。使用绝对或相对url地址指定光标文件(后缀为.cur或者.ani)。

0x04 小结

在经过一系列的努力后,我完成了几乎所有能做的改造。这是最终首页的灯塔评分:

达到这个分数并不容易,尤其是在引入了一系列自定义的东西之后。报告中会告诉你还有哪些地方可以优化,遗憾的是,作为一个使用者,只能止步于此了。即使在博客园后台-设置页面中“页面定制 CSS 代码”部分勾选了“禁用模板默认 CSS”,还是会有一部分资源被强制加载:

这些部分看起来很有搞头,可惜暂时没有找到合适的方法,如果有机会再尝试冲击百分吧。

有关你是来找茬的吧?对自己的博客进行调优的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  3. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  4. ruby - 如何进行排列以有效地定制输出 - 2

    这是一道面试题,我没有答对,但还是很好奇怎么解。你有N个人的大家庭,分别是1,2,3,...,N岁。你想给你的大家庭拍张照片。所有的家庭成员都排成一排。“我是家里的friend,建议家庭成员安排如下:”1岁的家庭成员坐在这一排的最左边。每两个坐在一起的家庭成员的年龄相差不得超过2岁。输入:整数N,1≤N≤55。输出:摄影师可以拍摄的照片数量。示例->输入:4,输出:4符合条件的数组:[1,2,3,4][1,2,4,3][1,3,2,4][1,3,4,2]另一个例子:输入:5输出:6符合条件的数组:[1,2,3,4,5][1,2,3,5,4][1,2,4,3,5][1,2,4,5,3][

  5. ruby - 即使失败也继续进行多主机测试 - 2

    我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r

  6. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  7. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  8. ruby - EventMachine - 你怎么知道你是否落后了? - 2

    我正在研究使用EventMachine支持的twitter-streamruby​​gem来跟踪和捕获推文。我对整个事件编程有点陌生。我如何判断我在事件循环中所做的任何处理是否导致我落后?有没有简单的检查方法? 最佳答案 您可以通过使用周期性计时器并打印出耗时来确定延迟。如果您使用的是1秒的计时器,您应该已经过了大约1秒,如果它更长,您就知道您正在减慢react器的速度。@last=Time.now.to_fEM.add_periodic_timer(1)doputs"LATENCY:#{Time.now.to_f-@last}"@

  9. ruby - 捕获 Ruby Logger 输出以进行测试 - 2

    我有一个像这样的ruby​​类:require'logger'classTdefdo_somethinglog=Logger.new(STDERR)log.info("Hereisaninfomessage")endend测试脚本行如下:#!/usr/bin/envrubygem"minitest"require'minitest/autorun'require_relative't'classTestMailProcessorClasses当我运行这个测试时,out和err都是空字符串。我看到消息打印在stderr上(在终端上)。有没有办法让Logger和capture_io一起玩得

  10. ruby - 按数字(从大到大)然后按字母(字母顺序)对对象集合进行排序 - 2

    我正在构建一个小部件来显示奥运会的奖牌数。我有一个“国家”对象的集合,其中每个对象都有一个“名称”属性,以及奖牌计数的“金”、“银”、“铜”。列表应该排序:1.首先是奖牌总数2.如果奖牌相同,按类型分割(金>银>铜,即2金>1金+1银)3.如果奖牌和类型相同,则按字母顺序子排序我正在用ruby​​做这件事,但我想语言并不重要。我确实找到了一个解决方案,但如果感觉必须有更优雅的方法来实现它。这是我做的:使用加权奖牌总数创建一个虚拟属性。因此,如果他们有2个金牌和1个银牌,加权总数将为“3.020100”。1金1银1铜为“3.010101”由于我们希望将奖牌数排序为最高的,因此列表按降序排

随机推荐