优化前后(Chrome Lighthouse)
PC
Mobile
构建产物
移除无用的导入残留
人工对代码自底向上进行审查,移除了无用的注释与 Props 传递,从而移除了无用的内容导入,减少构建体积。
例如:子组件不再渲染该图片资源,但由于存在引用传递,依旧会被构建工具打包。
import xxx from "xxx.png";
function Parent() {
return <Child title={"xxx"} image={xxx} />;
}
function Child({ title, image /* unused */ }) {
return (
<div>
<h1>{title}</h1>
{/* 历经迭代,组件的构造已时进过迁 ... */}
{/* <img src={image} /> */}
</div>
);
}
自动化工具审查
此类问题可以利用自动化工具有效解决,例如运用 ESLint 严格校验未使用变量,使用 TS 保证代码的类型安全(由于历史原因无法使用 TS 的项目可以使用 JSDoc + TS Check 替代)。
计划在后续对此项目的迭代规划中将此项列入并作为重点。
但自动化工具毕竟是执行通用规则,难免也会有疏漏,偶尔需要人工辅助介入,那么每次较大的版本改动是否需要执行一次代码审查?
图片优化
(1)对于网络图片或本地小型图片、图标,设置原生的 loading 属性(但此设置对 base64 编码的图片无效):
<img src="//xxx.png" loading="lazy" />
(2)对于本地图片,使用gatsby-plugin-image
转化渲染,一次性解决懒加载、格式转化与体积压缩三大问题,详见:资源优化。
百度地图
由于百度地图的渲染位于首页靠底部区域,虽然可使用async/defer
对其 SDK 进行延迟加载,但当该 SDK 脚本初始化时,其内部还会涉及大量的资源或依赖脚本请求,有可能会阻塞浏览器的渲染。
故此将其修改为懒加载模式,借助 IntersectionObserver API 监测元素的可见性,当地图元素出现在浏览器视窗时再去加载与初始化地图。
function BaiduMap(props) {
const id = props.id;
useEffect(() => {
const ele = document.querySelector(`#${id}`);
if (!ele) return;
// NOTE: Setting the JSONP callback function
window.setupBaiduMap = () => initMap(id);
// NOTE: Making lazy load with IntersectionObserver
const observerInstance = new IntersectionObserver((entries, observer) => {
const box = entries[0];
if (box.isIntersecting) {
const scriptEle = document.createElement("script");
scriptEle.src =
"//api.map.baidu.com/api?v=3.0&ak=xxx&callback=setupBaiduMap";
scriptEle.setAttribute("async", "true");
document.head.appendChild(scriptEle);
observer.unobserve(box.target);
}
});
observerInstance.observe(ele);
return () => observerInstance.disconnect();
}, []);
return <div id={id} style={{ width: "100%", height: "100%" }} />;
}
Graphql 查询优化
首页的「最新动态」只需要显示最新的 5 条数据,而目前的逻辑是先全量请求(约有 350 条数据),再手动截取出 5 条数据。
# graphql query
{
data: allContentfulNews {
nodes {
slug
title
createdAt(formatString: "YYYY-MM-DD HH:mm:ss")
content {
content
}
newContent {
newContent
}
}
}
}
这会导致构建之后产生的 JSON 文件体积较大,而其中有 90% 的数据是无用的,对构建速度、网络加载速度等多方面产生影响。
优化方案:限制请求数量即可。
# graphql query
{
- data: allContentfulNews {
+ data: allContentfulNews(limit: 5) {
nodes {
slug
title
createdAt(formatString: "YYYY-MM-DD HH:mm:ss")
content {
content
}
newContent {
newContent
}
}
}
}
其它优化
- 依赖优化:使用 day.js 代替 moment.js 以减少构建体积。
- SEO 优化:根据建议补充图片的 alt 属性、超链接的 href 属性。
- 布局抖动优化:根据建议对 img 补充宽高的设置,减少布局抖动所带来的重绘回流与视觉体验。
- 服务器优化:向运维人员申请启用服务器的 HTTP/2、Gzip 功能
从 gatsby 2 升级到 gatsby 3 出现的问题
路由导航错误
问题表现:在新闻页面对新闻数据列表进行分页跳转时,导航到了错误的页面。
问题原因:gatsby 2 在执行导航之前默认会对 pathname 中多余的 / 做去重处理,而 gatsby 3 却移除了此行为。
解决方法:在跳转路由之前先对 pathname 做去重:pathname.replace(/\/+/g, '/')
。
网站地图(SiteMap)抓取失败
问题表现:营销部同学反馈网站地图抓取失败。
问题原因:经排查,是插件gatsby-plugin-sitemap
使用了新的规则去创建 sitemap,新的规则是以文件夹的形式存在,因此访问上图中的路径时出现 403 错误(无权访问该文件夹)。
- 旧规则:
gatsby-plugin-sitemap
直接创建sitemap.xml
文件(命名与目录视配置而定) - 新规则:根据搜索引擎(厂商)的要求,每个
sitemap-x.xml
文件的索引条目不能超过 50000 条,多余的需要拆分到下一个文件,最后再通过一个入口文件(sitemap-index.xml
)索引重定向这些拆分后的文件,详见:gatsby-plugin-sitemap。
解决方法:重新配置了输出目录,通知营销同学重新上传新路径。
特别注意
百度不支持索引文件(为了打击非法产业与泛目录类网站),需上传最终含有实际 url 的sitemap-0.xml
等文件。
总结
本次协同优化工作,主要针对官网首页以及核心附属页,优化目标集中在以下方面:
- 对无用代码、资源文件进行审查删除,减少构建体积
- 对图片等富媒体资源做懒加载、格式转换与体积压缩
其它的优化目标属于性能压榨,这些因素对整体性能的影响占比不到 10%。
整体上像是执行了一场比较严格的 code-review,毕竟没有对项目做太多编译构建层面的优化。
计划在下一次的迭代中先对项目的工程化体系进行完善,有效地利用自动化工具作为把守代码质量的第一道关卡。