Photo by Ugur Akdemir on Unsplash

有时你就不得不感慨时间是过的真的快。在6月初我们正式迁移到Blocksy这个新主题后,已经过了半年了。Blocksy无疑是一个非常好用的主题,它的自定义工具非常的丰富。然而,我们也碰到了不少的问题。我们或许该考虑一下,雪猫社下一步该改成什么样了。

碰到的问题

Blocksy最大的缺点也来自于它最大的优点。那就是为了实现种种自定义功能,Blocksy不得不变得臃肿。在第一次迁移到Blocksy时我们的服务器占用给拉满了,甚至宕机。

Blocksy为了增加自定义外观的深度,大量运用了CSS变量,并且在每次页面生成时输出内联样式来实现自定义,这导致我们页面显示所需要的时间变长。

打开开发者工具你就会知道Blocksy写了多少内联样式

Blocksy增加的静态文件也给我们的带宽带来了沉重的压力。于是我们不得不学习网站的优化方法,加了许多优化,例如做了全站的静态缓存;使用了CDN加速文件读取并减少对服务器带宽的压力;用了Redis来缓存数据库操作等。成效是显著的。

在焊接日记中也有提到,我们有一些需要自订来实现的功能,其中有一部分是为了实现与其他插件的兼容。然而,为Blocksy写子主题其实有些不方便。以我目前的理解,Blocksy有些功能好像子主题是碰不到的。所以,我们的功能自订都需要直接在Blocksy的源文件上动刀,这就导致在追Blocksy的更新时会格外地痛苦。我需要把所有的修改找出来,在新的源文件上再改一次,特别的麻烦。而Blocksy最近也开始了商业化计划,推出了Blocksy Pro,并且Blocksy最近的开发重心也趋向于电子商务展示页面,这都势必会对我们产生一定的影响。

此外还有个最重要的一点就是,Blocksy已经不再漂亮了。或许六个月前Blocksy还是蛮好看的,但是六个月的锤炼下,审美变了。去看看Material UI,去看看IBM Carbon,哪个不比Blocksy好看。(虽然这对比有些勉强):yukicat.catshake:

当初换掉Dobby的一大原因就是对页面空间的使用不佳。尤其是在宽屏显示器上,页面两边会留下非常难看的空白。换到Blocksy后用侧边栏一定程度上解决了这个问题,但是侧边栏只能放在一边,这会导致屏幕布局的失衡。并且在宽屏显示器上,Blocksy依然没能很好地填补内容两旁的空白。

对比双边侧边栏的页面,你会发现这个现象格外明显。

其他的问题还有一大堆呢,比如Blocksy的调色方案其实也不是那么好看,文章目录的网格结构也不好看,等等等等。

可能的解决方案

换一个新主题

最简单粗暴的解决方案。

当然,新的主题必须满足几个要求:好看、轻量、容易定制。要不还不如继续Blocksy。

目前正在寻找符合要求的主题中。然而文章还没发布出来我们就换到了Sakurairo,这部分会在另一篇文章里再讲讲。

WordPress+React

在前端开始使用React这样的框架时,传统的服务器渲染+jQuery的模式就不是那么能打了。React能更加方便地做出更加动态的页面。然而,使用React在客户端渲染页面会遇到SEO难题——搜索引擎的爬虫才不会跑你的JavaScript。所以,使用React时往往会搭配服务端渲染(Server Side Render, SSR)来做搜索引擎优化,同时也方便CDN侧的缓存。那么我们只要让PHP做服务器渲染就行了吧!

然而不行。首先,服务端渲染要求服务端与客户端渲染出来的是同样的结果,如果有差异,就会在客户端产生“抖动”,影响用户体验。而要做到两者渲染出同一个东西,我们最好的方案就是用同一种方法描述我们要的画面。然而PHP与React现在使用不同的方式描述页面,两者是不能互相理解的。要想让他们互相理解,我们必须给出一个中介方案,比如直接在PHP中书写JSX代码,在编译期间将JSX代码渲染成HTML,生成最终的PHP与JS文件。然而,虽然这个过程可能是实现PHP+React的最佳方案,但是能够实现这一过程的工具目前尚不存在,写起来也很麻烦。

此外,WordPress上的古腾堡以及各种插件还是提升了编写文章的体验的。相比Markdown与MDX,古腾堡可能还是更容易上手些。同时考虑到迁移WordPress的数据的复杂性,我们暂时还不能完全抛弃WordPress。

社区现在给出来的方案是将WordPress当作一个无头CMS,通过接口获取WordPress的数据后,交由Node.js服务端渲染并传输给客户端。这感觉符合当下流行的微服务的概念,把数据与视图拆分。这个方案有着成熟的工具支持,我们可以用Next.js、Gatsby这些广受好评久经考验的静态页面生成器来完成渲染页面的工作。这两个生成器也早就有了社区提供的SEO插件实现搜索引擎优化。数据获取方面,Gatsby还提供获取WordPress数据的官方插件,而使用Next.js可能需要经历一段学习使用WordPress REST API的艰苦历程。

这一方案最麻烦的应该就是如何正确显示WordPress输出的正文内容吧。长期以来,WordPress通过短代码、HTML注释来实现特别的功能。我们的博客中也充分利用了短代码,然而短代码需要插件支持。因此这一部分需要用React复现。

此外,这些页面生成器也有着它们自己的麻烦。Next.js被多次批评生成的内容过大,并且Next.js生成的页面很容易被扒取数据。准确地说是Next.js把数据送到了爬虫的面前。这一点Next.js也有它自己的苦衷。为了实现服务端渲染与客户端渲染的统一,Next.js必须要采取某种方法把数据传递给客户端脚本。Next.js的解决方案就是加了一个id为__NEXT_DATA__的script标签。打开一个Next.js渲染的页面,在console里输入__NEXT_DATA__,你就能看到Next.js在页面中封装了什么数据。

其中pageProps这一字段是Next.js传递给页面组件(Page Component,Next.js中每一个页面是一个React组件)的数据。在服务端渲染时,Next.js会执行事先写好的getStaticProps()或getServerSideProps()来获取pageProps;在客户端渲染时,Next.js的客户端环境会从__NEXT_DATA__中读取pageProps。

这本没什么错,但是考虑这样一个场景:你要显示雪猫社的一篇博文,你需要写一个显示博文的页面组件,然后从pageProps导入博文,这样你才可以复用显示博文的相关逻辑。这时,你的页面将完全是依靠pageProps里的HTML渲染。

这时问题就大了。仔细看一下上面图中pageProps的html字段,你会发现其中包含了Next.js这篇文档的所有内容。然而,因为进行了服务端渲染,文档的内容本身就已经写进了HTML中。于是,这篇文档网页最大大小的部分——教程文档,在网页中出现了两次,直接带来了两倍的空间占用。这或许是Next.js总是被人诟病生成的网站文件太大的原因之一罢。然而,要是要从WordPress这样的CMS中获取要渲染的内容,还要支持SEO、CDN缓存这些东西的话,你还必须得这么做。

比较理想的情况是把内容都放在Js里。你可以看一看Next.js官方文档(不是中文文档)JS文件。这是使用React输出一个纯静态文档的时候的情况。

此外观察一下中文文档的__NEXT_DATA__的head字段,你会哭笑不得地发现Next.js把<head>元素里的所有元素都放这了。让我们看看这有些啥:

[code lang="json" show_lang=true][
[
"meta",
{
"name": "viewport",
"content": "width=device-width"
}
],
[
"meta",
{
"charSet": "utf-8"
}
],
[
"title",
{
"children": "基本特性: 获取数据 | Next.js | Next.js 中文网"
}
],
[
"meta",
{
"name": "twitter:site",
"content": "@vercel"
}
],
[
"meta",
{
"name": "twitter:card",
"content": "summary_large_image"
}
],
[
"meta",
{
"property": "og:title",
"content": "基本特性: 获取数据 | Next.js"
}
],
[
"meta",
{
"property": "og:url",
"content": "https://www.nextjs.cn/docs/basic-features/data-fetching"
}
],
[
"meta",
{
"name": "description",
"content": "Next.js has 2 pre-rendering modes: Static Generation and Server-side rendering. Learn how they work here."
}
],
[
"meta",
{
"property": "og:description",
"content": "Next.js has 2 pre-rendering modes: Static Generation and Server-side rendering. Learn how they work here."
}
],
[
"meta",
{
"property": "og:image",
"content": "https://www.nextjs.cn/static/twitter-cards/documentation.png"
}
]
][/code]

这里面包含了Next.js文档的标题,以及SEO所需要的meta标签,还有页面使用的字符集的标志。然而众所周知,这些元素一般不需要在客户端进行变更。标题元素的更改倒是会被用到,麒麟dalao的博客的标题结合可见性API卖了个小萌。然而SEO优化相关的meta标签完全没有客户端变更的意义,我们都知道在这些meta有意义的地方,变更他们的JavaScript甚至不会运行。

写这部分的时候我咕了一段时间,是因为发现英文文档里的__NEXT_DATA__已经没有<head>的信息了。把我手头上的项目升级到Next 10以后也出现了同样的现象。虽然没有找到相关的commit,但应该是Next对此做了改动,这很好。还在用Next 9的朋友可以升级一下,新版本还有自带的next/image优化。

结语

所谓生命不息,折腾不止(草

以后博客还会有很多变化吧,可能隔半年我们又换个主题?