关于hexo:hexo个人网站优化探索

3次阅读

共计 22814 个字符,预计需要花费 58 分钟才能阅读完成。

>> 原文链接

Contents


  • ## Contents
  • ## 前言

    • 1. hexo 是什么?
    • 2. 何为优化?
  • ## 性能优化指标

    • 1. 整体运行性能
    • 2. 网站可拜访性
    • 3. 网站是否利用了最佳实际策略

      • > 1) 应用 target="_blank"<a> 链接如果没有申明 rel="noopener noreferrer" 存在平安危险。
      • > 2) 查看浏览器端控制台是否有告警和谬误提醒,通过批示定位问题并解决。
      • > 3) http 和 https 协定地址不混用
      • > 4) 防止应用 AppCache
      • > 5) 防止应用 document.write()
      • > 6) 防止应用 mutation events
      • > 7) 防止应用 Web SQL
      • > 8) 防止加载过于宏大的 DOM 树
      • > 9) 容许用户粘贴明码
    • 4. 网站搜索引擎优化 SEO
  • ## 性能测试工具 lighthouse
  • ## 基于 hexo 框架的网站优化

    • 1. 优化资源加载工夫

      • ➣ 应用 defer/async 异步下载 script 脚本资源
      • ➣ 应用 async 函数异步加载内部资源
      • ➣ 应用浏览器 onfocus 事件提早内部资源加载
      • ➣ 应用 preload/prefetch/preconnect 进行预加载优化
      • ➣ 应用 hexo 插件压缩代码文件和图片文件
      • ➣ 编写 hexo-img-lazyload 插件减少图片懒加载个性
      • ➣ 应用 IntersectionObserver API 懒加载其它资源
      • ➣ 应用 CDN 加载内部依赖脚本
      • ➣ 应用 Aplayer 代替 iframe 加载网易云音乐
    • 2. 优化界面运行性能

      • ➣ 优化页面的回流和重绘状况
      • ➣ 应用节流和去抖思维优化滚动事件监听
      • ➣ IntersectionObserver API 的 polyfill 兼容策略
      • ➣ 应用 IntersectionObserver 代替原生 onscroll 事件监听
    • 3. 网站最佳实际

      • ➣ 应用 hexo-abbrlink 插件生成文章链接
      • ➣ 应用 hexo-filter-nofollow 躲避平安危险
    • 4. 网站 SEO 优化

      • ➣ 应用 hexo-generator-sitemap 插件主动生成网站地图
      • ➣ 向 Google Search Console 提交集体网站地图
  • ## 参考
  • ## 结语

前言


1. hexo 是什么?

hexo 是一个为了不依赖于后端进行界面实时数据查问展现而设计的网站开发工具。比方之前曾应用 Node.js 作为后盾开发过一个博客网站,本人实现后盾逻辑的话须要思考数据库存储、前后端交互、后端 Server 部署啥的。整个流程比拟繁冗,在初期能够作为前端开发者集体建站学习的一种形式。hexo 简化了这一流程,它将数据存储和数据获取这两方面都通过编译构建而后本地化集成到前端动态资源里。一个例子就是博客网站通常须要翻页性能来获取博客文章,传统开发方式下,获取下一页这个操作由前端脚本发动,并由后端 Server 解决申请并返回,然而应用 hexo 之后这整个过程都是在本地一次实现的,hexo 将所有动态资源在本地建设了索引。

应用 hexo 通常写手只须要关注 markdown 格局文章的编写,其余的网站编译、构建和公布的流程都能够交由框架进行解决,所有的网站内容均会被打包成动态的 html/css/js 文件。hexo 反对自定义插件,也有一个插件社区,如果写手同时具备前端能力的话也能够公布本人的插件到社区里进行开源共享。

2. 何为优化?

咱们通常所讲的性能高下可能侧重于对网站运行速度快慢的评估,其包含动态资源及脚本获取的速度和网站 UI 界面是否运行晦涩。其实狭义上的优化应包含:网站性能优化、网站可访性优化、网站 SEO 优化、网站最佳实际等。

性能优化指标


1. 整体运行性能

  • FCP (First Contentful Paint):从用户开始发动网站申请到浏览器第一次开始渲染网站数据的所用时长。其中提及的第一次开始渲染的网站数据蕴含网页的文字、图片、HTML DOM 构造等,而不蕴含位于 iframe 中的网页数据。该指标通常用于掂量本地和服务器首次建设网络通讯的速度。
  • TTI (Time To Interactive):从用户开始导航至网站到页面变为齐全可交互所破费的工夫。网站可交互的衡量标准就是:网站展现了理论可用的内容、界面上可见元素的网页事件曾经被胜利绑定(比方点击、拖动等事件)、用户和页面交互的反馈工夫低于 50 ms。
  • SI (Speed Index):掂量页面加载过程中内容可视化显示的速度。艰深来讲就是网站界面元素的绘制和出现速度,如果应用 lighthouse 测量工具的话它会捕捉浏览器中处于加载中的页面的多个图片帧,而后计算帧之间的视觉渲染进度。
  • TBT (Total Blocking Time):掂量从页面首次开始渲染 (FCP) 之后到页面理论可交互 (TTI) 的工夫。通常咱们拜访一个网站时网站整体出现后,有一段较短的工夫咱们不能和界面进行交互,比方鼠标点击、键盘按键等,这段时间浏览器在进行脚本及款式的加载和执行。
  • LCP (Largest Contentful Paint):测量视口中最大的内容元素何绘制到屏幕
    所需的工夫。通常蕴含这个元素的下载、解析和渲染整个过程。
  • CLS (Cumulative Layout Shift):一个掂量网站加载时整体布局抖动状况的数值指标。如果一个网站在加载过程中用户界面屡次抖动和闪动的话会可能引起用户的轻度不适,因而应该尽量减少网站的重排和重绘次数。

2. 网站可拜访性

  • 网页背景色和网站文字前景的对比度不能太低,否则会影响用户浏览。
  • 网页链接标签 <a> 最好蕴含对链接的形容信息,比方:<a href="https://github.com/nojsja">[形容 - nojsja 的 github 集体界面]</a>
  • html 元素存在 lang 属性指定以后语言环境。
  • 正确的 html 语义化标签能让键盘和读屏器失常工作,通常一个网页的构造能够用语义化标签形容为:

    <html lang="en">
    <head>
      <title>Document title</title>
      <meta charset="utf-8">
    </head>
    <body>
      <a class="skip-link" href="#maincontent">Skip to main</a>
      <h1>Page title</h1>
      <nav>
        <ul>
          <li>
            <a href="https://google.com">Nav link</a>
          </li>
        </ul>
      </nav>
      <header>header</header>
      <main id="maincontent">
        <section>
          <h2>Section heading</h2>
            <p>text info</p>
          <h3>Sub-section heading</h3>
            <p>texgt info</p>
        </section>
      </main>
      <footer>footer</footer>
    </body>
    </html>
  • 界面元素的 id 唯一性。
  • img 标签的 alt 属性申明。它指定了代替文本,用于在图像无奈显示或者用户禁用图像显示时,代替图像显示在浏览器中的内容。
  • form 元素外部申明 label 标签以让读屏器正确工作。
  • iframe 元素申明 title 属性来形容其外部内容以便于读屏器工作。
  • aria 无障碍属性和标签的应用,相干参考 >> aria reference
  • input[type=image]、object 标签增加 alt 属性申明:

    <input type="image" alt="Sign in" src="./sign-in-button.png">
    <object alt="report.pdf type="application/pdf"data="/report.pdf">
    Annual report.
    </object>
  • 须要应用 tab 按键聚焦个性的元素能够申明 tabindex 属性,当咱们按 tab 键时焦点会顺次切换。并且依据键盘序列导航的程序,值为 0、非法值、或者没有 tabindex 值的元素应该搁置在 tabindex 值为正值的元素前面:

    1) tabindex= 负值 (通常是 tabindex=“-1”),示意元素是可聚焦的,然而不能通过键盘导航来拜访到该元素,用 JS 做页面小组件外部键盘导航的时候十分有用。2) tabindex="0",示意元素是可聚焦的,并且能够通过键盘导航来聚焦到该元素,它的绝对程序是以后处于的 DOM 构造来决定的。3) tabindex= 正值,示意元素是可聚焦的,并且能够通过键盘导航来拜访到该元素;它的绝对程序依照 tabindex 的数值递增而滞后获焦。如果多个元素领有雷同的 tabindex,它们的绝对程序依照他们在以后 DOM 中的先后顺序决定。
  • table 元素中正确应用 thscope 让行表头和列表头与其数据域一一对应:

    <table>
    <caption>My marathon training log</caption>
    <thead>
      <tr>
        <th scope="col">Week</th>
        <th scope="col">Total miles</th>
        <th scope="col">Longest run</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th scope="row">1</th>
        <td>14</td>
        <td>5</td>
      </tr>
      <tr>
        <th scope="row">2</th>
        <td>16</td>
        <td>6</td>
      </tr>
    </tbody>
    </table>
  • video 元素指定 track文本字幕资源以不便听障人士应用(须要有字幕资源文件):

    <video width="300" height="200">
      <source src="marathonFinishLine.mp4" type="video/mp4">
      <track src="captions_en.vtt" kind="captions" srclang="en" label="english_captions">
      <track src="audio_desc_en.vtt" kind="descriptions" srclang="en" label="english_description">
      <track src="captions_es.vtt" kind="captions" srclang="es" label="spanish_captions">
      <track src="audio_desc_es.vtt" kind="descriptions" srclang="es" label="spanish_description">
    </video>
  • li 列表标签放在容器组件 ulol 中应用。
  • heading 标签严格依照升序申明,配合 section 或其它元素 (例如 p 标签) 正确反映界面内容构造:

    <h1>Page title</h1>
    <section>
    <h2>Section Heading</h2>
    …
      <h3>Sub-section Heading</h3>
    </section>
  • 应用 <meta charset="UTF-8"> 指定网站字符集编码。
  • img 元素援用的图片资源长宽比应该和 img 以后利用的长宽比雷同,不然可能造成图片扭曲。
  • 增加 <!DOCTYPE html> 以避免浏览界面渲染异样。

3. 网站是否利用了最佳实际策略

> 1) 应用 target="_blank"<a> 链接如果没有申明 rel="noopener noreferrer" 存在平安危险。

当页面链接至应用 target=”_blank” 的另一个页面时,新页面将与旧页面在同一个过程上运行。如果新页面正在执行开销极大的 JavaScript,旧页面性能可能会受影响。并且新的页面能够通过 window.opener 拜访旧的窗口对象,比方它能够应用 window.opener.location = url 将旧页面导航至不同的网址。

> 2) 查看浏览器端控制台是否有告警和谬误提醒,通过批示定位问题并解决。

> 3) http 和 https 协定地址不混用

浏览器曾经逐步开始禁止不必协定资源的混用,比方应用 http 协定的 web 服务器加载 https 协定结尾的资源,因而可能呈现以下几种状况:

  • 加载了混合内容,但会呈现正告;
  • 不加载混合内容,间接会显示空白内容;
  • 在加载混合内容之前,会呈现相似是否“显示”,或存在不平安危险而被“阻止”的提醒!

应该思考以下形式进行优化:

  • 将站点加载的局部协定混用内部资源放入本人的服务器进行托管;
  • 对于部署了 https 的网站,在网页申明 <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"将 http 申请转换成 https 申请;
  • 对于部署了 https 的网站,在服务器端增加申请首部:header("Content-Security-Policy: upgrade-insecure-requests")也能够达到一样的成果;
  • 对于同时反对 http 和 https 拜访的网站,思考应用绝对协定,而不间接明文指定协定:<script src="//path/to/js">,浏览器发送申请时会依据以后页面协定进行动静切换。
  • 类同于应用绝对协定,应用绝对 URL 也能达到目标,不过减少了资源之间的耦合度:<script src="./path/to/js"></script>

> 4) 防止应用 AppCache

AppCache 已被废除 思考应用 service worker 的 Cache API,

> 5) 防止应用 document.write()

对于网速较慢(2G、3G 或较慢的 WLAN)的用户,内部脚本通过 document.write()动静注入会使页面内容的显示提早数十秒。

> 6) 防止应用 mutation events

以下 mutation events 会侵害性能,在 DOM 事件标准中曾经弃用:

  • DOMAttrModified
  • DOMAttributeNameChanged
  • DOMCharacterDataModified
  • DOMElementNameChanged
  • DOMNodeInserted
  • DOMNodeInsertedIntoDocument
  • DOMNodeRemoved
  • DOMNodeRemovedFromDocument
  • DOMSubtreeModified

倡议应用 MutationObserver 代替

> 7) 防止应用 Web SQL

倡议替换为 IndexedDB

> 8) 防止加载过于宏大的 DOM 树

大型的 DOM 树会以多种形式升高页面性能:

  • 网络效率和负载性能,如果你的服务器发送一个大的 DOM 树,你可能会运送大量不必要的字节。这也可能会减慢页面加载工夫,因为浏览器可能会解析许多没有显示在屏幕上的节点。
  • 运行时性能。当用户和脚本与页面交互时,浏览器必须一直从新计算节点的地位和款式。一个大的 DOM 树与简单的款式规定相结合可能会重大减慢渲染速度。
  • 内存性能。如果应用通用查问选择器(例如,document.querySelectorAll(‘li’) 您可能会无心中将援用存储到大量的节点),这可能会压倒用户设施的内存性能。

一个最佳的 DOM 树:

  • 总共少于 1500 个节点。
  • 最大深度为 32 个节点。
  • 没有超过 60 个子节点的父节点。
  • 一般来说,只须要在须要时寻找创立 DOM 节点的办法,并在不再须要时将其销毁。

> 9) 容许用户粘贴明码

明码粘贴进步了安全性,因为它使用户可能应用明码管理器。明码管理员通常为用户生成强明码,平安地存储明码,而后在用户须要登录时主动将其粘贴到明码字段中。
删除阻止用户粘贴到明码字段的代码。应用事件断点中的 Clipboard paste 来打断点,能够疾速找到阻止粘贴明码的代码。比方下列这种阻止粘贴明码的代码:

var input = document.querySelector('input');
input.addEventListener('paste', (e) => {e.preventDefault();
});

4. 网站搜索引擎优化 SEO

  • 增加视口申明 <meta name="viewport"> 并指定 with 和 device-width 来优化挪动端显示。
  • document 增加 title 属性以让读屏器和搜索引擎正确辨认网站内容。
  • 增加 meta desctiption 标签来形容网站内容 <meta name="description" content="Put your description here.">
  • 为链接标签增加形容文本 <a href="videos.html">basketball videos</a>,以分明传播这个超链接的指向内容。
  • 应用正确的 href 链接地址以让搜索引擎正确跟踪理论网址,以下是反例:<a onclick="goto('https://example.com')">
  • 不要应用 meta 标签来禁用搜索引擎爬虫爬取你的网页,以下是反例:<meta name="robots" content="noindex"/>,绝对的能够非凡的排除一些爬虫爬取:<meta name="AdsBot-Google" content="noindex"/>
  • image 图片元素中应用具备明确用意和含意的 alt 属性文字来阐明图片信息,并且防止应用一些非特定指代词比方:"chart", "image", "diagram"(图表、图片)。
  • 不要应用插件来显示您的内容,即防止应用 embedobjectapplet等标签来引入资源。
  • 正确搁置 robots.txt 到网站根目录下,它形容了此网站中的哪些内容是不应被搜索引擎的获取的,哪些是能够被获取的。通常一些后盾文件(如 css、js)和用户隐衷的文件不必被搜索引擎抓取,另外有些文件频繁被蜘蛛抓取,然而这些文件又是不重要的,那么能够用 robots.txt 进行屏蔽。
    一个实例:

    User-agent: *
    Allow: /
    Disallow: /wp-admin/
    Disallow: /wp-includes/
    Disallow: /wp-content/plugins/
    Disallow: /?r=*
  • 向搜索引擎提交网站地图 sitemap.xml,xml 格局的文本就是专门拿来给电脑进行浏览的语,而 sitemap.xml 就是搜查引擎利用这个标准,让网站主能够应用它来制作一个蕴含了网站内所有网页的目录档案,提供给搜查引擎的爬虫浏览。就像是一个地图一样,让搜查引擎能够晓得网站内到底有些什么网页。

    
    <?xml version="1.0" encoding="UTF-8"?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    
    <url>
      <loc>https://nojsja.github.io/blogs/2019/10/26/63567fa4.html/</loc>
      
      <lastmod>2021-04-29T10:02:04.853Z</lastmod>
      
    </url>
    
    <url>
      <loc>https://nojsja.github.io/blogs/2020/03/26/7507699.html/</loc>
      
      <lastmod>2021-04-29T10:01:30.661Z</lastmod>
      
    </url>
    
    </urlset>
    

性能测试工具 lighthouse


以上提到的性能监测指标通过 lighthouse 网站性能监测工具能够自动化剖析和生成性能测试后果数据,较新版本的 chrome 和 采纳 chromium 架构的 edge 浏览器都曾经自带了,较低版本的 chrome 浏览器能够通过在商店搜寻插件装置。装置后应用 F12 关上控制台,切换到 lighthouse 一栏即可间接应用了:

如图,能够自行决定勾选测试项目和针对测试平台(桌面 / 挪动端),最初点击生成报告即可开始运行自动化测试工作,并在测试实现后关上一个剖析后果页面:

后果界面:

至此咱们就能对网站的整体性能进行一些评估了,上图中的PerformanceAccessibilityBest Practices、和 SEO 顺次对应上文咱们提及的网站整体性能、网站可拜访性、网站最佳实际、搜索引擎 SEO 优化等指标。咱们能够点击每一个具体我的项目来查看测试工具给出的优化倡议:

测试后果很大水平上取决于你的 http 动态资源服务器的资源加载速度,比方在不应用代理的状况下,应用 github pages 服务来托管动态资源会比应用国内的 gitee pages 托管服务稍慢,而且可能呈现局部资源加载失败的问题。因而国内用户能够应用 gitee pages 来代替 github pages,不过 gitee 非付费用户没有代码主动构建部署性能,须要应用下文提到的 github action 来进行自动化登录并触发构建和部署。

留神: 某些浏览器上装置的插件会影响测试后果,因为插件可能会发动申请加载其它脚本啥的。这种状况下能够间接应用 npm 包管理器全局装置 npm install -g lighthouse,而后应用命令行启动测试流程:lighthouse https://nojsja.gitee.io/blogs --view --preset=desktop --output-path='/home/nojsja/Desktop/lighthouse.html'。最终会依据指定的地址生成一个可间接浏览器测试后果网页文件 lighthouse.html,能够间接关上进行性能排查。

基于 hexo 框架的网站优化


之前写的一篇文章《前端性能优化技巧详解(1)》,具体的形容了前端性能优化的一些方面,这篇文章不会再列举每一点,只会对博客中理论利用的优化伎俩进行阐明,大抵被划分成这几个方面:

  1. 优化资源加载工夫
  2. 优化界面运行性能
  3. 网站最佳实际
  4. 网站 SEO 优化

1. 优化资源加载工夫

常见的加载工夫优化形式蕴含以下:

  • 进步网页资源并行加载能力
  • 提早加载不必要的内部资源
  • 缩小碎片化的内部资源申请,小文件做合并
  • 减少网站带宽

➣ 应用 defer/async 异步下载 script 脚本资源

HTML 在解析时遇到申明的 <script> 脚本会立刻下载和执行,往往会提早界面残余局部的解析,造成界面白屏的状况。比拟古老的优化形式之一就是将脚本放到 HTML 文档开端,这样子解决了白屏的问题,可是在 DOM 文档结构复杂简短时,也会造成肯定的界面脚本下载和执行提早,script 标签新属性 async 和 defer 能够解决此类问题:

  • defer 脚本:申明 defer 属性的内部 <script> 脚本下载时不会阻塞 HTML 的解析和渲染,并且会在 HTML 渲染实现并且可实际操作之后开始执行 (DOMContentLoaded 事件被触发之前),各个脚本解析执行程序对应申明时的地位程序,执行实现后会触发页面 DOMContentLoaded 事件。
  • async 脚本:申明 async 属性的内部 <script> 脚本下载时不会阻塞 HTML 的解析和渲染,各个脚本的下载和执行齐全独立,下载实现后即开始执行,所以执行程序不固定,与 DOMContentLoaded 事件的触发没有关联性。

在我的博客网站中有应用 Bootstrap 内部依赖的款式和 js 脚本,然而须要确保他们的申明程序在靠前的地位,因为应用异步技术之后,内联同步执行的其它 <script> 脚本的执行程序就不能保障了,因而不能应用 defer/async 属性进行优化。

通常 async/defer 会用于优化一些独立的子组件依赖脚本的加载,比方用于博客文章中的导航条跳转的脚本,它的执行程序齐全不收到其它局部的制约,因而能够独立进去应用 async 属性进行优化。然而须要确保该脚本作用的 导航条 DOM 元素申明位于脚本引入地位之前,以防止出现脚本执行时 DOM 元素还未渲染的状态,引起脚本谬误。

➣ 应用 async 函数异步加载内部资源

以下 async 函数作用就是依据传入的 url 创立 link/script 标签并增加到 <head> 标签中以动静加载内部资源,并且在存在回调函数时监听资源加载状况,加载结束后再执行回调函数。值得注意的是本办法与间接通过script 申明依赖资源的不同之处在于不会阻塞界面,脚本的下载和执行都是异步的。

博客中常用于在某个非凡的状况下利用编程办法动静载入内部依赖库,并在回调函数内进行内部库的初始化。例如我的博客应用了一个音乐播放器组件,在网页可视区域滚动到蕴含这个组件的尚未初始化的 DOM 元素时,就是用 async 来申请服务器的 js 脚本资源,加载实现后在回调函数里初始化这个音乐播放器。

/**
  * async [异步脚本加载]
  * @author nojsja
  * @param  {String} u [资源地址]
  * @param  {Function} c [回调函数]
  * @param  {String} tag [加载资源类型 link | script]
  * @param  {Boolean} async [加载 script 资源时是否须要申明 async 属性]
  */
function async(u, c, tag, async) {
    var head = document.head ||
      document.getElementsByTagName('head')[0] ||
      document.documentElement;
    var d = document, t = tag || 'script',
      o = d.createElement(t),
      s = d.getElementsByTagName(t)[0];
    async = ['async', 'defer'].includes(async) ? async : !!async;
    
    switch(t) {
      case 'script':
        o.src = u;
        if (async) o[async] = true;
        break;
      case 'link':
        o.type = "text/css";
        o.href = u;
        o.rel = "stylesheet";
        break;
      default:
        o.src = u;
        break;
    }

    /* callback */

    if (c) {if (o.readyState) {//IE
        o.onreadystatechange = function (e) {
          if (o.readyState == "loaded"
            || o.readyState == "complete") {
            o.onreadystatechange = null;
            c(null, e)();}
        };
      } else {// 其余浏览器
        o.onload = function (e) {c(null, e);
        };
      }
    }

    s.parentNode.insertBefore(o, head.firstChild);
  }

➣ 应用浏览器 onfocus 事件提早内部资源加载

通过用户和界面交互触发一些事件后,满足了内部资源加载的条件,再触发内部资源的加载,也属于提早加载的一种优化。

例如我的博客中右侧导航条区域有个搜寻框能够搜寻博客文章,自身这个搜寻是通过查找本地事后生成的一个资源索引动态 XML 文件来实现的。文章和内容较多这个这个 XML 文件就会变得宏大,如果在网页首次加载时就下载它肯定会造成网页带宽和网络申请数量的占用。因而思考在用户点击搜寻框将焦点集中到下面时再触发 XML 的异步下载,同时为了不影响应用体验,能够在加载过程中设置 loading 成果以批示用户提早操作。

➣ 应用 preload/prefetch/preconnect 进行预加载优化

  • preload 用来预加载以后页面所需的资源,如图像,CSS,JavaScript 和字体文件,它的加载优先级比 prefetch 更高,同时也要留神 preload 并不会阻塞 window 的 onload 事件。博客中有应用它来预加载 css 中援用的字体:<link href="/blogs/fonts/fontawesome-webfont.woff2?v=4.3.0" rel=preload as="font">,针对不同的资源类型须要增加不同的 as 标记信息,如果是跨域加载的话留神增加 crossorigin 属性申明。
  • prefetch 是一个低优先级的资源提醒,一旦一个页面加载结束就会开始下载其余的预加载资源,并且将他们存储在浏览器的缓存中。其中 prefretch 又蕴含:link、DNS 和 prerendering 三中类型的预加载申请。link prefetch 比方:<link rel="prefetch" href="/path/to/pic.png"> 容许浏览器获取资源并将他们存储在缓存中;DNS prefetch 容许浏览器在用户浏览页面时在后盾运行 DNS prerender 和 prefetch 十分类似,它们都优化了下一页资源的将来申请,区别是 prerender 在后盾渲染了整个页面,因而应该小心应用,可能会造成网络带宽的节约。
  • preconnect 容许浏览器在一个 HTTP 申请正式发给服务器前事后执行一些操作,这包含 DNS 解析,TLS 协商,TCP 握手,这打消了往返提早并为用户节俭了工夫。比方博客中:不算子统计库的网页预连贯 <link href="http://busuanzi.ibruce.info" rel="preconnect" crossorigin>

➣ 应用 hexo 插件压缩代码文件和图片文件

压缩动态资源也是一种节俭网络带宽,进步申请响应速度的形式。通常采纳工程化的形式进行配置,而不必手动对每张图片进行压缩。我的博客中应用了 hexo 的一款压缩插件 Hexo-all-minifier,通过压缩 HTML、CSS、JS 和图片来优化博客访问速度。

装置:

npm install hexo-all-minifier --save

config.yml 配置文件中启用:

# ---- 代码和资源压缩
html_minifier:
  enable: true
  exclude:

css_minifier:
  enable: true
  exclude: 
    - '*.min.css'

js_minifier:
  enable: true
  mangle: true
  compress: true
  exclude: 
    - '*.min.js'

image_minifier:
  enable: true
  interlaced: false
  multipass: false
  optimizationLevel: 2 # 压缩等级
  pngquant: false
  progressive: true # 是否启用渐进式图片压缩

资源的压缩比拟耗费性能和工夫,能够思考在开发环境下不启用这些插件以放慢开发环境启动。比方独自指定一个 _config.dev.yml 而后把上述插件全副敞开即可,参考 package.json 中的脚本字段申明:

{
...
"scripts": {
    "prestart": "hexo clean --config config.dev.yml; hexo generate --config config.dev.yml",
    "prestart:prod": "hexo clean; hexo generate",
    "predeploy": "hexo clean; hexo generate",
    "start": "hexo generate --config config.dev.yml; hexo server --config config.dev.yml",
    "start:prod": "hexo generate --config config.dev.yml; hexo server",
    "performance:prod": "lighthouse https://nojsja.gitee.io/blogs --view --preset=desktop --output-path='/home/nojsja/Desktop/lighthouse.html'","performance":"lighthouse http://localhost:4000/blogs --view --preset=desktop --output-path='/home/nojsja/Desktop/lighthouse.html'",
    "deploy": "hexo deploy"
  }
}

➣ 编写 hexo-img-lazyload 插件减少图片懒加载个性

在进行博客优化的时候为了学习 hexo 本人的插件零碎,应用 IntersectionObserver API 来编写了一个图片懒加载插件:hexo-img-lazyload,能够通过 npm 命令进行装置:npm i hexo-img-lazyload

成果预览:

插件次要原理就是监听博客构建流程的钩子事件,拿到构建好的代码字符串,而后代码中原生的图片申明比方:<img src="path/to/xx.jpg">通过正则全局匹配并替换为:<img src="path/to/loading" data-src="path/to/xx.jpg">

function lazyProcessor(content, replacement) {return content.replace(/<img(.*?)src="(.*?)"(.*?)>/gi, function (str, p1, p2, p3) {if (/data-loaded/gi.test(str)) {return str;}

        if (/no-lazy/gi.test(str)) {return str;}
        return `<img ${p1} src="${emptyStr}" lazyload data-loading="${replacement}" data-src="${p2}" ${p3}>`;
    });  
}

替换之后还须要应用 hexo 的代码注入性能将咱们本人编写的代码注入到每个构建好的界面中。

hexo 代码注入:

/* registry scroll listener */
hexo.extend.injector.register('body_end', function() {
  const script = `
    <script>
      ${observerStr}
    </script>`;

  return script;
}, injectionType)

被注入的用于监听待加载图片元素是否进入可视区域以进行动静加载的局部代码,应用了 IntersectionObserver API 而不是 window.onscroll 事件的形式,前者具备更好的性能,由浏览器对立监听所有元素地位信息更改并散发滚动事件后果:


(function() {
  /* avoid garbage collection */
  window.hexoLoadingImages = window.hexoLoadingImages || {};

  function query(selector) {return document.querySelectorAll(selector);
  }
  
  /* registry listener */
  if (window.IntersectionObserver) {var observer = new IntersectionObserver(function (entries) {entries.forEach(function (entry) {
        // in view port
        if (entry.isIntersecting) {observer.unobserve(entry.target);

          // proxy image
          var img = new Image();
          var imgId = "_img_" + Math.random();
          window.hexoLoadingImages[imgId] = img;

          img.onload = function() {entry.target.src = entry.target.getAttribute('data-src');
            window.hexoLoadingImages[imgId] = null;
          };
          img.onerror = function() {window.hexoLoadingImages[imgId] = null;
          }

          entry.target.src = entry.target.getAttribute('data-loading');
          img.src = entry.target.getAttribute('data-src');

        }
      });
    });
    
    query('img[lazyload]').forEach(function (item) {observer.observe(item);
    });
  
  } else {
  /* fallback */
    query('img[lazyload]').forEach(function (img) {img.src = img.getAttribute('data-src');
    });
  
  }
}).bind(window)();

➣ 应用 IntersectionObserver API 懒加载其它资源

IntersectionObserver API因为性能更好曾经在我的博客中作为一种次要的资源懒加载形式,我还应用它来优化博客评论组件 Valine 的加载。个别评论组件都位于博客文章的最下方,因而刚载入文章页面时齐全没有必要进行评论组件的资源加载,能够思考应用 IntersectionObserver 监听评论组件是否进入视口,进入后再应用 async 异步脚本下载并回调执行评论零碎初始化。

另一方面每篇文章底部的音乐播放器 Aplayer 也应用了相似的加载策略,能够说优化成果屡试不爽!

➣ 应用 CDN 加载内部依赖脚本

CDN 即内容散发网络。CDN 服务商将动态资源缓存到遍布全国的高性能减速节点上,当用户拜访相应的业务资源时,CDN 零碎可能实时地依据网络流量和各节点的连贯负载情况、到用户的间隔和响应工夫 等综合信息将用户的申请从新导向离用户最近的服务节点上,使内容可能传输的更快,更加稳固。能够晋升首次申请的响应能力。

博客中一些专用内部库比方 BootstrapjQuery 都是应用的内部 CDN 资源地址,一方面能够减小以后主站的网页带宽耗费,另一方面 CDN 也能提供一些资源下载减速。

➣ 应用 Aplayer 代替 iframe 加载网易云音乐

博客之前的版本会在文章界面的底部嵌入一个网易云音乐本人的播放器,这个播放器其实是一个 iframe 像这样:

<iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=330 height=86 src="//music.163.com/outchain/player?type=2&id=781246&auto=1&height=66"></iframe>

iframe 加载的时候会加载一堆货色,尽管能够通过 lazy 属性来进行懒加载,不过 iframe 也有很多毛病:

  • iframe 会阻塞主页面的 onload 事件
  • iframe 和主页面共享 HTTP 连接池,而浏览器对雷同域的连贯有限度,所以会影响页面的并行加载
  • iframe 不利于网页布局
  • iframe 对挪动端不敌对
  • iframe 的重复从新加载可能导致一些浏览器的内存泄露
  • iframe 中的数据传输简单
  • iframe 不利于 SEO

新版本将 iframe 播放器换成了 Aplayer 并且把本人喜爱的一个歌曲列表上传到了另一个 gitee pages 仓库 进行动态托管,能够通过以下形式在博客底部加载一个自定义歌曲列表:

var ap = new APlayer({container: document.getElementById('aplayer'),
  theme: '#e9e9e9',
  audio: [{
    name: '存在信号',
    artist: 'AcuticNotes',
    url: 'https://nojsja.gitee.io/static-resources/audio/life-signal.mp3',
    cover: 'https://nojsja.gitee.io/static-resources/audio/life-signal.jpg'
    
  },
  {
    name: '遺サレタ場所/斜光',
    artist: '岡部啓一',
    url: 'https://nojsja.gitee.io/static-resources/audio/%E6%96%9C%E5%85%89.mp3',
    cover: 'https://nojsja.gitee.io/static-resources/audio/%E6%96%9C%E5%85%89.jpg'
  }]
});

预览图:

2. 优化界面运行性能

➣ 优化页面的回流和重绘状况

1)概念

回流 (重排,reflow) 和重绘 (repaint) 是浏览器渲染网页必不可少的一个过程,回流次要 HTML 渲染过程中元素空间地位和大小扭转引起的 DOM 树从新渲染,而重绘是因为节点的款式属性产生扭转,并不会影响空间布局。从性能而言回流的性能耗费大,而且容易产生级联效应,即一个失常 DOM 树的流布局中,一个元素产生回流后,该元素地位之后的元素全副都会产生回流并从新渲染,重绘绝对性能耗费更小。

2)怎么无效判断界面的回流和重绘状况?

其实个别基于 chromium 架构浏览器都附带一个网页开发工具 Devtools,但能够说绝大多数前端开发者都没有认真理解过这个工具的具体用处,只是把它用作简略的 log 调试、网页申请追踪和款式调试这些根底性能。回流和重绘也是能够通过它来进行可视化度量的:F12 关上 Devtools,找到右上角三个点的折叠按钮顺次关上 -> More Tools(更多工具) -> Rendering (渲染) – 勾选前两项 Paint Flashing (高亮重绘区域) 和 Layout Shift Regions (高亮回流区域),当初从新回到你关上的页面进行操作,操作过程中产生了回流的区域会变成蓝色,产生了重绘的区域会变成绿色,持续时间不长,留神察看。

Repaint:

Reflow:

除了用于可视化界面回流 / 重绘的状况,Devtools 还有其余一些很实用的性能,比方:Coverage Tools 能够用于剖析界面上引入的内部 css/js 脚本内容的应用覆盖率,就是说咱们能通过这个工具掂量引入的内部文件的应用状况,应用频次较低的内部资源能够思考内联或是间接手写实现,晋升引入内部资源的 性价比

➣ 应用节流和去抖思维优化滚动事件监听

在面对一些须要进行调用管制的函数高频触发场景时,可能有人会对何时应用节流何时应用去抖产生疑难。这里通过一个个性进行简略辨别:如果你须要保留短时间内高频触发的最初一次后果时,那么应用去抖函数,如果你须要对函数的调用次数进行限度,以最佳的调用间隔时间放弃函数的继续调用而不关怀是否是最初一次调用后果时,请应用节流函数。

比方 echarts 图经常须要在窗口 resize 之后从新应用数据渲染,然而间接监听 resize 事件可能导致短时间内渲染函数被触发屡次。咱们能够应用函数去抖的思维,监听 resize 事件后在监听器函数里获取参数再应用参数调用当时初始化好了的 throttle 函数,保障 resize 过程完结后能触发一次理论的 echarts 重渲染即可。

这里给出节流函数和去抖函数的简略实现:

  /**
    * fnDebounce [去抖函数]
    * @author nojsja
    * @param  {Function} fn [须要被包裹的原始函数逻辑]
    * @param  {Numberl} timeout [延迟时间]
    * @return {Function} [高阶函数]
    */
  var fnDebounce = function(fn, timeout) {
    var time = null;

    return function() {if (!time) return time = Date.now();
      if (Date.now() - time >= timeout) {
        time = null;
        return fn.apply(this, [].slice.call(arguments));
      } else {time = Date.now();
      }
    };
  };

  /**
    * fnThrottle [节流函数]
    * @author nojsja
    * @param  {Function} fn [须要被包裹的原始函数逻辑]
    * @param  {Numberl} timeout [延迟时间]
    * @return {Function} [高阶函数]
    */
  var fnThrottle = function(fn, timeout) {
    var time = null;

    return function() {if (!time) return time = Date.now();
      if ((Date.now() - time) >= timeout) {
        time = null;
        return fn.apply(this, [].slice.call(arguments));
      }
    };
  };

博客中文章右侧的内容导航栏会依据滚动条地位主动切换 fixed 布局和个别流布局,这么做是为了让导航栏在阅读文章过程中也能失常出现,不会被暗藏到顶部:

  /* 限 150ms 能力触发一次滚动检测 */
  (window).on('scroll', fnThrottle(function() {var rectbase = $$tocBar.getBoundingClientRect();
      if (rectbase.top <= 0) {$toc.css('left', left);
        (!$toc.hasClass('toc-fixed')) && $toc.addClass('toc-fixed');
        $toc.hasClass('toc-normal') && $toc.removeClass('toc-normal');
      } else {$toc.css('left', '');
        $toc.hasClass('toc-fixed') && $toc.removeClass('toc-fixed');
        (!$toc.hasClass('toc-normal')) && $toc.addClass('toc-normal');
        ($$toc.scrollTop > 0) && ($$toc.scrollTop = 0);
      }
  }, 150));

➣ IntersectionObserver API 的 polyfill 兼容策略

文章中提到 IntersectionObserver API 曾经用于博客中各个界面组件的懒加载性能,它的性能更好、性能也更加全面。然而网页开发中咱们通常会思考各个 API 的兼容性,这里能够通过 Can I Use 这个兼容性报告网站进行查看,从下图可知这个 API 的兼容状况还是能够的,桌面端很多较高版本浏览器均已反对:

因而为了解决个别低版本浏览器的兼容性问题,这里采纳了一种比拟极其的解决形式。惯例状况下咱们须要引入内部 [xxx].polyfill.js (xxx 为相应的 API) 来为低版本浏览器增加相应性能,然而对于高版本浏览器本身曾经反对了这个 API,却要反复下载 polyfill 库,造成网页申请数和带宽资源的节约。因而我这里不采纳这种形式,因为这个 API 大部分浏览器曾经反对,咱们默认不应用 <script> 标签引入 polyfill.js 而是通过脚本判断以后浏览器是否反对此 API,如果不反对的话再应用 同步 XHR 申请 近程 下载 polyfill 文件,下载后应用 eval(...) 的形式执行整个脚本。应用同步形式会阻塞以后 js 执行线程,请审慎应用,此处是为了保障 IntersectionObserver 以高优先级形式被注入到网页中,不然可能引发一些应用了此 API 脚本谬误。

<!-- 此脚本被搁置在凑近页面首部某个地位 -->
<script>
  if ('IntersectionObserver' in window &&
    'IntersectionObserverEntry' in window &&
    'intersectionRatio' in window.IntersectionObserverEntry.prototype) {if (!('isIntersecting' in window.IntersectionObserverEntry.prototype)) {
      Object.defineProperty(window.IntersectionObserverEntry.prototype,
        'isIntersecting', {get: function () {return this.intersectionRatio > 0;}
      });
    }
  } else {
    /* load polyfill sync */
    sendXMLHttpRequest({
      url: '/blogs/js/intersection-observer.js',
      async: false,
      method: 'get',
      callback: function(txt) {eval(txt);
      }
    });
  }
</script>

➣ 应用 IntersectionObserver 代替原生 onscroll 事件监听

IntersectionObserver 通常用于界面中的一些相交检测场景:

  • 图片懒加载 – 当图片滚动到可见时才进行加载
  • 内容有限滚动 – 用户滚动到靠近滚动容器底部时间接加载更多数据,而无需用户操作翻页,给用户一种网页能够有限滚动的错觉
  • 检测广告的曝光状况——为了计算广告收益,须要晓得广告元素的曝光状况
  • 在用户看见某个区域时执行工作、播放视频

以内容有限滚动为例,古老的相交检测计划就是应用 scroll 事件监听滚动容器,在监听器函数中获取滚动元素的几何属性判断元素是否曾经滚动到底部。咱们晓得 scrollTop 等属性的获取和设置都会导致页面回流,并且如果界面须要绑定多个监听函数到 scroll 事件进行相似操作的时候,页面性能会大打折扣:

  /* 滚动监听 */
  onScroll = () => {
    const {scrollTop, scrollHeight, clientHeight} = document.querySelector('#target');
    
    /* 曾经滚动到底部 */
    // scrollTop(向上滚动的高度);clientHeight(容器可视总高度);scrollHeight(容器的总内容长度)
    if (scrollTop + clientHeight === scrollHeight) {/* do something ... */}
  }

这里以一个简略实现的图片懒加载性能来介绍下它的应用形式,具体应用能够查看博客:《前端性能优化技巧详解(1)》。

(function lazyload() {var imagesToLoad = document.querySelectorAll('image[data-src]');

  function loadImage(image) {image.src = image.getAttribute('data-src');
    image.addEventListener('load', function() {image.removeAttribute('data-src');
    });
  }

  var intersectionObserver = new IntersectionObserver(function(items, observer) {items.forEach(function(item) {
      /* 所有属性:item.boundingClientRect - 指标元素的几何边界信息
        item.intersectionRatio - 相交比 intersectionRect/boundingClientRect
        item.intersectionRect -  形容根和指标元素的相交区域
        item.isIntersecting - true(相交开始),false(相交完结)
        item.rootBounds - 形容根元素
        item.target - 指标元素
        item.time - 工夫原点 (网页在窗口加载实现时的工夫点) 到穿插被触发的工夫的工夫戳
      */
      if (item.isIntersecting) {loadImage(item.target);
        observer.unobserve(item.target);
      }
    });
  });

  imagesToLoad.forEach(function(image) {intersectionObserver.observe(image);
  });
  
})();

3. 网站最佳实际

➣ 应用 hexo-abbrlink 插件生成文章链接

hexo 框架生成的博客地址默认是 :year/:month/:day/:title 这种格局的,也就是 / 年 / 月 / 日 / 题目。当博客题目为中文时,生成的 url 链接中也呈现中文,中文门路对于搜索引擎优化不敌对。复制后的链接会被编码,十分不利于浏览,也不简洁。

应用 hexo-abbrlink 能够解决这个问题,装置插件:npm install hexo-abbrlink --save,在 _config.yml 中增加配置:

permalink: :year/:month/:day/:abbrlink.html/
permalink_defaults:
abbrlink:
  alg: crc32  # 算法:crc16(default) and crc32
  rep: hex    # 进制:dec(default) and hex

之后生成的博客文章就会变成这种:https://post.zz173.com/posts/8ddf18fb.html/,即便更新了文章题目,文章的链接也不会扭转。

➣ 应用 hexo-filter-nofollow 躲避平安危险

hexo-filter-nofollow 插件会为所有 <a> 链接增加属性 rel="noopener external nofollow noreferrer"

网站外部有大量的外链会影响网站的权重, 不利于 SEO。

  • nofollow:是 Google、Yahoo 和微软公司前几年一起提出的一个属性,链接加上这个属性后就不会被计算权值。nofollow 通知爬虫无需追踪指标页,为了反抗 blogspam(博客垃圾留言信息),Google 举荐应用 nofollow,通知搜索引擎爬虫无需抓取指标页,同时通知搜索引擎无需将的当前页的 Pagerank 传递到指标页。然而如果你是通过 sitemap 间接提交该页面,爬虫还是会爬取,这里的 nofollow 只是当前页对指标页的一种态度,并不代表其余页对指标页的态度。
  • noreferrernoopener:当 <a> 标签应用 target="_blank" 属性链接到另一个页面时,新页面将与您的页面在同一个过程上运行。如果新页面正在执行开销极大的 JavaScript,旧页面性能可能会受影响。并且新页面能够通过 window.opener 拿到旧页面窗口对象执行任意操作,具备极大的安全隐患。应用 noopener (兼容属性 noreferrer) 之后,新关上的页面就不能拿到旧页面窗口对象了。
  • external:通知搜素引擎,这是非本站的链接,这个作用相当于 target=“_blank”,缩小内部链接的 SEO 权重影响。

    4. 网站 SEO 优化

➣ 应用 hexo-generator-sitemap 插件主动生成网站地图

站点地图是什么:

  • 站点地图形容了一个网站的构造。它能够是一个任意模式的文档,用作网页设计的设计工具,也能够是列出网站中所有页面的一个网页,通常采纳分级模式。这有助于访问者以及搜索引擎的机器人找到网站中的页面。
  • 一些开发者认为网站索引是组织网页的一种更适合的形式,然而网站索引通常是 A - Z 索引,只提供拜访特定内容的入口,而一个网站地图为整个站点提供了个别的自顶向下的视图。
  • 网站地图让所有页面可被找到来加强搜索引擎优化的成果。

装置:

$: npm install hexo-generator-sitemap --save

配置文件 _config.yml 中增加相干字段:

# sitemap
sitemap:
  path: sitemap.xml
# page url
url: https://nojsja.github.io/blogs 

之后运行 hexo generate 之后就能够主动生成网站地图 sitemap.xml 了,接下来须要到 Google Search Console 记录本人的站点并提交相应的站点文件。

➣ 向 Google Search Console 提交集体网站地图

  • 登录 Google Search Console
  • 增加本人的网站信息
  • 能够通过几种办法验证所有权
  • 提交插件生成的 sitemap.xml

Google Search Console 还能看到本人网站的点击状况、关键词索引状况等,十分不便。

参考


  1. Lighthouse 与 Google 的挪动端最佳实际
  2. Google web.dev

结语


学习前端性能优化的方方面面,一方面是对咱们外围基础知识的考查,另一方面也能为咱们遇到的一些理论问题提供解决思路,是每个前端人进阶的的必经之路。

以上就是本篇文章的所有内容,后续有须要还会持续更新…

正文完
 0