前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >SEO 在 SPA 站点中的实践

SEO 在 SPA 站点中的实践

作者头像
牧云云
发布于 2021-03-11 03:37:56
发布于 2021-03-11 03:37:56
1.9K00
代码可运行
举报
文章被收录于专栏:云瓣云瓣
运行总次数:0
代码可运行

背景

观察基于 create-react-doc 搭建的文档站点, 发现网页代码光秃秃的一片(见下图)。这显然是单页应用 (SPA) 站点的通病 —— 不利于文档被搜索引擎搜索 (SEO)。

难道 SPA 站点就无法进行 SEO 了么, 那么 Gatsbynuxt 等框架又为何能作为不少博主搭建博客的首选方案呢, 此类框架赋能 SEO 的技术原理是什么呢? 在好奇心的驱动下, 笔者尝试对 creat-react-doc 进行赋能 SEO 之旅。

搜索引擎优化

在实践之前, 先从理论上分析为何单页应用不能被搜索引擎搜索到。核心在于 爬虫蜘蛛在执行爬取的过程中, 不会去执行网页中的 JS 逻辑, 所以隐藏在 JS 中的跳转逻辑也不会被执行

查看当前 SPA 站点打包后的代码, 除了一个根目录 index.html 外, 其它都是注入的 JS 逻辑, 因此浏览器自然不会对其进行 SEO。

此外, 搜索引擎详优化是一门较复杂的学问。如果你对 SEO 优化比较陌生, 建议阅读搜索引擎优化 (SEO) 新手指南 一文, Google 搜索中心给出了全面的 17 个最佳做法, 以及 33 个应避免的做法, 这也是笔者近期在实践的部分。

SEO 在 SPA 站点中的实践案例

在轻文档站点的背景前提下, 我们暂不考虑 SSR 方案。

对市面上文档站点的 SEO 方案调研后, 笔者总结为如下四类:

  • 静态模板渲染方案
  • 404 重定向方案
  • SSG 方案
  • 预渲染方案
静态模板渲染方案

静态模板渲染方案以 hexo 最为典型, 此类框架需要指定特定的模板语言(比如 pug)来开发主题, 从而达到网页内容直出的目的。

404 重定向方案

404 重定向方案的原理主要是利用 GitHub Pages 的 404 机制进行重定向。比较典型的案例有 spa-github-pagessghpa

但是遗憾的是 2019 年 Google 调整了爬虫算法, 因此此类重定向方案在当下是无利于 SEO 的。spa-github-pages 作者也表示如果需要 SEO 的话, 使用 SSG 方案或者付费方案 Netlify

SSG 方案

SSG 方案全称为 static site generator, 中文可译为路由静态化方案。社区上 nuxtGatsby 等框架赋能 SEO 的技术无一例外可以归类此类 SSG 方案。

以 nuxt 框架为例, 在约定式路由的基础上, 其通过执行 nuxt generate 命令将 vue 文件转化为静态网页。

例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-| pages/
---| about.vue/
---| index.vue/

静态化后变成:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-| dist/
---| about/
-----| index.html
---| index.html

经过路由静态化后, 此时的文档目录结构可以托管于任何一个静态站点服务商。

预渲染方案

经过上文对 SSG 方案的分析, 此时 SPA 站点的优化关键已经跃然纸上 —— 静态化路由。相较于 nuxt、Gatsby 等框架存在约定式路由的限制, create-react-doc 在目录结构上的组织灵活自由。它的建站理念是文件即站点, 同时它对存量 markdown 文档的迁移也十分便捷。

blog 项目结构为例, 其文档结构如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-| BasicSkill/
---| basic/
-----| DOM.md
-----| HTML5.md

静态化后应该变成:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-| BasicSkill/
---| basic/
-----| DOM
-------| index.html
-----| HTML5
-------| index.html

经过调研, 该构思与 prerender-spa-plugin 预渲染方案一拍即合。预渲染方案的原理可以见如下图:

至此技术选型定下为使用预渲染方案实现 SSG。

预渲染方案实践

create-react-doc 在预渲染方案实践的步骤简单概况如下(完整改动可见 mr):

  • 改造 hash 路由为 history 路由。因为 history 路由结构与文档静态化目录结构天然匹配。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export default function RouterRoot() {
  return (
-    <HashRouter>
+    <BrowserRouter>
      <RoutersContainer />
-    </HashRouter>
+    </BrowserRouter>
  )
}
  • 在开发环境、生成环境的基础上新增预渲染环境, 同时对路由进行环境匹配。其主要解决了资源文件主域名下的子路径的对应关系。过程比较曲折, 感兴趣的同学可以见 issue
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const ifProd = env === 'prod'
+ const ifPrerender = window.__PRERENDER_INJECTED && window.__PRERENDER_INJECTED.prerender
+ const ifAddPrefix = ifProd && !ifPrerender

<Route
  key={item.path}
  exact
-  path={item.path}
+  path={ifAddPrefix ? `/${repo}${item.path}` : item.path}
  render={() => { ... }}
/>
  • 兼容 prerender-spa-plugin 在 webpack 5 的使用。

官方版本当前未支持 webpack 5, 详见 issue, 同时笔者存在对预渲染后执行回调的需求。因此当前 fork 了一份版本 出来, 解决了以上问题。

经过上述步骤的实践, 终于在 SPA 站点中实现了静态化路由

SEO 优化附加 buff, 站点秒开?

SEO 优化至此, 来看下站点优化前后 FP、FCP、LCP 等指标数据的变化。

blog 站点为例, 优化前后的指标数据如下(数据指标统计来自未使用梯子访问 gh-pages):

优化前: 接入预渲染方案前, 首次绘制(FP、FCP) 的时间节点在 8s 左右, LCP 在 17s 左右。

优化后: 接入预渲染方案后, 首次绘制时间节点在 1s 之内开始, LCP 在 1.5s 之内。

对比优化前后: 首屏绘制速度提升了 8 倍, 最大内容绘制速度提升 11 倍。本想优化 SEO, 结果站点性能优化的方式又 get 了一个。

生成站点地图 Sitemap

在完成预渲染实现站点路由静态化后, 距离 SEO 的目标又近了一步。暂且抛开 SEO 优化细节, 单刀直入 SEO 核心腹地 站点地图

站点地图 Sitemap 格式与各字段含义简单说明如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="utf-8"?>
<urlset>
  <!-- 必填标签, 这是具体某一个链接的定义入口,每一条数据都要用 <url></url> 包含在里面, 这是必须的 -->
  <url>
    <!-- 必填, URL 链接地址,长度不得超过 256 字节 -->
    <loc>http://www.yoursite.com/yoursite.html</loc>
    <!-- 可以不提交该标签, 用来指定该链接的最后更新时间 -->
    <lastmod>2021-03-06</lastmod>
    <!-- 可以不提交该标签, 用这个标签告诉此链接可能会出现的更新频率 -->
    <changefreq>daily</changefreq>
    <!-- 可以不提交该标签, 用来指定此链接相对于其他链接的优先权比值,此值定于 0.0-1.0 之间 -->
    <priority>0.8</priority>
  </url>
</urlset>

上述 sitemap 中, lastmod、changefreq、priority 字段对 SEO 没那么重要, 可以见 how-to-create-a-sitemap

根据上述结构, 笔者开发了 create-react-doc 的站点地图生成包 crd-generator-sitemap, 其逻辑就是将预渲染的路由路径拼接成上述格式。

使用方只需在站点根目录的 config.yml 添加如下参数便可以在自动化发版过程中自动生成 sitemap

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
seo:
  google: true

将生成的站点地图往 Google Search Console 中提交试试吧,

最后验证下 Google 搜索站点优化前后效果。

优化前: 只搜索到一条数据。

优化后: 搜索到站点地图中声明的位置数据。

至此使用 SSG 优化 SPA 站点实现 SEO 的完整流程完整实现了一遍。后续便剩下参照 搜索引擎优化 (SEO) 新手指南 做一些 SEO 细节方面的优化以及支持更多搜索引擎了。

小结

本文从 SPA 站点实现 SEO 作为切入点, 先后介绍了 SEO 的基本原理, SEO 在 SPA 站点中的 4 种实践案例, 并结合 create-react-doc SPA 框架进行完整的 SEO 实践。

如果本文对您有所帮助, 欢迎 star反馈

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-03-08 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
qml入门教程_前端从入门到放弃
<9>:Property and number animation in mouse event:
全栈程序员站长
2022/09/19
1.9K0
qml入门教程_前端从入门到放弃
Qt开发-QT Quick
Row 则是一个单独的 Item ,专门用来管理其它 Item 的,后面介绍的几种布局,也是类似的。
码客说
2021/11/10
2.6K0
【C++】Qt:QML介绍与入门示例
Qt Quick是一个用于构建现代、高效、可扩展用户界面的框架。它是Qt开发框架的一部分,旨在通过声明性语法和JavaScript绑定来简化用户界面的设计和实现。
DevFrank
2024/07/24
6970
【C++】Qt:QML介绍与入门示例
完全依赖QML实现播放器
一直听闻QML无比强大好用,工作中需要扣一个同时播放视频的Demo,所以就趁这个机会研究了一下。
gongluck
2020/03/05
2.3K0
完全依赖QML实现播放器
Qt开发-QT Quick之自定义组件
按钮 添加ZButton.qml import QtQuick 2.14 import QtQuick.Window 2.14 /* 文件名即自定义控件名 使用别名导出属性:相当于函数的变量形参 不同的是导出的属性,调用控件是可以不使用(赋值) */ Rectangle { id: root //导出自定义属性 property alias text: label.text property alias fontSize: label.font.po
码客说
2021/11/17
1.7K0
Qt开发-QT Quick之自定义组件
【QML】简单动画实现
QML动画 **示例1:**动画作为属性值的来源 import QtQuick 2.0 //动画作为属性值的来源 //语法: 动画on属性 //easing属性来实现缓和曲线 Rectangle{ width: 100 height: 100 color: "red" PropertyAnimation on x{ to:50 duration: 1000 loops: Animation.Infinite //无限循环
半生瓜的blog
2023/05/13
5630
Flat风格的Qml单选/复选按钮
使用Qml的RadioButton与CheckBox控件修改而成。 单选按钮 RadioButton代码 import QtQuick 2.0 import QtQuick.Controls 2.0
Qt君
2019/11/24
2.5K0
Qml的Slider控件示例
通过自定义样式美化Slider 起始值0处标识同心圆; 拖动动画变化颜色; 气泡显示数值. 示例源码 import QtQuick 2.0 import "../" as My Rectangle
Qt君
2019/07/15
2.4K0
Qml的Slider控件示例
QML动态显示组件(支持在线编辑动态刷新)
QML动态组件显示器主要用于方便界面开发,在线编辑保存后自动刷新组件界面,并支持拖拽文件显示的方式。
Qt君
2019/07/15
5.5K0
兼容Qt4/Qt5版本Qml控件ComboBox
组合框是一个组合按钮和弹出列表。它提供了一种向用户显示选项列表的方法,这种方法占用最小的屏幕空间。数据模型通常是一个javascript数组、C++端的List类型、未来还会提供对ListModel或整数的数据模型支持。
Qt君
2019/07/16
2.2K0
兼容Qt4/Qt5版本Qml控件ComboBox
Qt官方示例-QML Axes
QML轴线图示例,折线图,散点图。 使用相同轴坐标的折线图和散点图。 代码: ChartView { title: "Two Series, Common Axes" anchors
Qt君
2019/09/06
1.2K0
pyqt5与QML开发小结
qt 5.11 与 qt 5.12 中Qquick的差异还是蛮大的,由开发环境:Pyqt5.11 + Qt5.12 部署到 Pyqt5.11 + Qt5.11时遇到以下问题:
py3study
2020/01/17
1.5K0
兼容Qt4/Qt5版本的QML例程
本篇文章介绍兼容Qt4与Qt5版本的Qml简单例程。由于Qt4与Qt5版本的qml文件不能使用宏来区分,所以使用qmake执行脚本来修改qml版本差异代码。(末尾源代码地址) qml文件转换 下列代码是项目文件代码,会在编译之前执行; 主要作用为识别Qt版本然后替换对应qml文件中的 importQtQuick1.x或 importQtQuick2.x; equals(QT_MAJOR_VERSION, 4) { equals(QT_MINOR_VERSION, 7): QT_QUICK_VERSI
Qt君
2019/07/15
1.6K0
QT之Qml使用QSystemTrayIcon实现系统托盘
 QT中实现这一功能使用QSystemTrayIcon,它为应用程序在系统托盘中提供一个图标。现代操作系统通常在桌面上提供一个特殊区域,称为系统托盘或通知区域,长时间运行的应用程序可以在其中显示图标和短消息。
杨永贞
2022/05/11
2.8K0
QT之Qml使用QSystemTrayIcon实现系统托盘
Qt官方示例-QML标签页
❝TabWidget示例演示了如何使用属性别名和QML Object默认属性创建标签页。❞ TabWidget.qml Item { id: tabWidget // 核心实现 // 将默认属性设置为stack.children意味着TabWidget的所有子项实际上都已添加到"stack"项的子项中。 // 有关默认属性的详细信息,请参见"Property Binding"文档。 default property alias content: stack.c
Qt君
2023/03/17
1.3K0
Qt官方示例-QML标签页
兼容Qt4/Qt5版本Qml控件ScrollBar
1. ScrollBar演示 2. 对外属性 继承于Rectangle; target属性继承于Flickable(默认值父控件); orientation设置控件水平还是垂直方向(默认值垂直方向).
Qt君
2019/07/15
1.4K0
兼容Qt4/Qt5版本Qml控件ScrollBar
兼容Qt4/Qt5版本Qml控件Triangle
三角形控件(Triangle),等腰直角三角形。底是高的两倍。 文件导入 示例 Triangle { anchors.centerIn: parent } 属性 width:设置三角形的等宽。 color:设置三角形的颜色。 源码 //#if Qt4 //import QtQuick 1.0 //#else import QtQuick 2.0 //#endif Item { id: root property alias color: triangle.color im
Qt君
2019/10/30
5370
一个好的Qml文件(翻译文)
  什么样的Qml文件(通常称为组件)是一个高质量的文件?   让我们看看示例1:
Qt君
2019/07/15
1.2K0
【QML】基础语法
QtQuick是一种高级界面技术,可轻松创建供移动、嵌入式设备使用的触摸式界面、轻量级应用程序。QtQuick主要由3部分组成:QtQuick设计器,QML语言、quick模块。
半生瓜的blog
2023/05/13
8000
【QML】基础语法
Qml数字键盘
双十一购物节为了避免剁手,给自己一个小目标,写个Qml数字键盘分享给大家。 1. Qml键盘代码 Rectangle { id: rootKeyboard property color backgroundColor: "#202120" //default property color pressedButtonColor: "#00a0fc" // default property color normalButtonColor: "#303751" // d
Qt君
2023/03/17
8570
Qml数字键盘
相关推荐
qml入门教程_前端从入门到放弃
更多 >
LV.0
这个人很懒,什么都没有留下~
加入讨论
的问答专区 >
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
    本文部分代码块支持一键运行,欢迎体验
    本文部分代码块支持一键运行,欢迎体验