谈谈单页应用于SEO

发布于 · 最后修改时间

注: 本文章的文字内容来自鬼懿群 20:00 2014/3/10 的内容,并非访谈形式。内容有所删减。

第一话 各自的方案

@浩明 1999
现在网上大部分流传的方法是这样:

index.html 的代码如下:

<a href="Ajax.html?id=1" onclick="fun(1);return false;">1</a>
<a href="Ajax.html?id=2" onclick="fun(2);return false;">2</a>
<a href="Ajax.html?id=3" onclick="fun(3);return false;">3</a>

通过在 A 标签上 return false 来区别搜索引擎和用户。

那我有一个疑问,搜索引擎收录的页面链接是 Ajax.html?id=3,如果我在百度搜索到内容,点进去的应该是 Ajax.html?id=3 这个页面,而我们的期望并不是这样啊,我们是希望用户点击到 index.html 这个页面并触发 onclick="fun(3);

那我们能不能在 Ajax.html?id=3 这个页面中用 js 跳转到 index.html


@Igin
Ajax.html?id=3 用 js 跳转不合适
等于这个页面加载了却浪费不用
你可以在服务端直接重定向
或者用 google 认识的 #!


@漂 ౣ 流 ౣ 瓶 ౣ
搜一下 facebook 的 quickling 吧,这个有现成的解决方案
quickling 是比较完美的解决方案


@天猪
prerender.io


@Gaubee
纯粹的单页面太极端了,开发起来也并不是最好的选择。我现在都是混合在用,应用是单页的,其它的依旧是分页面。


@蚂蚁 zzbo
百度用 baidumap.xml ...

第二话 磨

@雨夜带刀
单页面对前端是有很大的挑战
能提升用户体验那是显而易见的
(前端)出错了只能怪代码没写好 所以一定要保证 js 不能出错


@Gaubee
等于把后端的框架往前端搬。工作量确实很大,我基本上已经离不开 requirejs 了。
有模块开发的规范的话,错误的出现会比较容易处理。
做测试也会容易
那些考虑单页面 SEO 的,感觉都是把炫酷的 H5 技术滥用了。
个人觉得 hash 路由主要使用在 web 应用上。而不是非要到处使用。别扭就是别扭。


@雨夜带刀
http://image.so.com/zv?ch=pet&cid=热门&tid=#groupid=cd2f127aa7d0a72bede10f2a626babe7&dataindex=34&lightboxindex=7&itemindex=0
http://music.163.com
看应用场景 像我上面发的都很具代表性
普通的站点真心没必要


@漂 ౣ 流 ౣ 瓶 ౣ
借助后端,是可以实现这样一种页面输出能力:
1 直接访问页面 a,比如 url 是 /a ,返回的内容是正常的 html
2 如果希望把 a 页面中的某些模块以 json 形式返回,比如 url 是 /a?pagelets=m1,m2 这个 url 的返回结果是 json,json 包括了 m1,m2 模块的 js、css 以及 html 内容,前端加载其 js、css、并插入 html 就可以实现局刷了。

实现这两个功能,就能解决:
1 无 js 用户或者网络爬虫用户访问 a 或者 b 页面时,返回的是普通的 html
2 当用从 a 页面切换到 b 页面的时候,可以用 js 发起 /b?pagelets=m3,m4 来局部刷新

这种方式就是 facebook 的 quickling 实现原理,它的页面代码形如:

facebookquickling

直接访问页面,返回完整的 html,如果访问 的 url 加上 pagelets=left,right,就会返回两个页面小部件的 js、css 和 html 的 json 数据

代码很简单:
假设上面的例子的文件叫 a.php,其实有一个后端的控制框架,代码是这样的:

<?php
$pagelets = $_GET['pagelets'];
if($pagelets){
    Pagelet::use($pagelets);
    ob_start();
    include 'a.php';
    ob_end();
    echo Pagelet::render();
}  else {
    echo include 'a.php';
}
>

也就是同一个页面,如果请求中有 pagelets 参数,就抑制原始的 html 输出,改成 Pagelet 收集,否则就正常输出
然后,我们考虑一下,那个 load_pagelet 函数是怎么实现的。
其实就是一个简单的命中而已。有一个 Pagelet 框架帮忙收集,最后将数据以 json 形势吐出
这样,工程师好像在写一个普通的 html 页面,但是可以在 ajax 化和非 ajax 化之间切换

facebookquickling

有一个前端框架,页面加载之后,找到所有有 pagelets 属性的 a 标签,抑制它的点击,点击的时候,改成 ajax 请求 /b?pagelets=xxx

对于搜索引擎用户,它不能执行 js,这个时候 /b 这个链接是正确的,访问也是正常的 html

对于可以执行 js 的用户,它的那个 url 被 js 拦截,发起了一个 ajax 请求 /b?pagelets=left,right 并能做到页面的 ajax 化


@雨夜带刀
我说的不是同一个页面是针对用户来说
搜索引擎和真实的用户
本身做搜索的体验不到需要兼容搜索引擎的难处...
这个解决方案貌似还行


@漂 ౣ 流 ౣ 瓶 ౣ
ajax 和 seo 不是鱼和熊掌,而是要跨栈联合解决的,页面可以有两种输出模式,并且能实现静态资源的组件化管理,这些都有完整实现的,比如 facebook 在 velocity2010 上关于 ajax 流水化的分享。另外百度的 hao123,贴吧的 pad 版都是这样实现的


@Gaubee
等于就做了两个网站,
一个针对 SEO,主打关键字展示。
一个针对浏览器用户,返回真正的程序数据。

要我我就直接做两套路由。硬生生整合在一起没理由啊


@漂 ౣ 流 ౣ 瓶 ౣ
没有硬生生整合
其实上面的截图也能看到,工程师基本上感觉不到在写一个 ajax 化的网站
而是传统的 html 页面,这种工程上的优化收益很大的。此外,这里还有一种技术叫 pagecache
比如 a 页面切换到 b 页面的时候,前端可以记录 a 和 b 页面所用到的静态资源和 html 内容,那么,下次 ab 页面之间切换的时候,不用再发起 ajax 请求,而是直接从内存中恢复页面即可
facebook 会保留几个用户最常访问的页面的 pagecache,提高访问速度
大家可以感受一下百度贴吧 pad 版的这个效果


@Gaubee
嗯。是这样子的。我只是觉得没必要把细节隐藏起来,开发者知道整套构架最好。观点不一样,这点我没什么好说的。
pagecache 是等于把资源手动存储么。等于 cookie 再过期时间内无需重新请求资源。


@漂 ౣ 流 ౣ 瓶 ౣ
恩,没有隐藏全部细节,因为链接还是要指定要异步的 pagelets 的 id 的,以及要填充的容器 id

facebookquickling

这样两个 tab 短时间内切换是没有 http 请求的

facebookquickling

这个种方式还能解决一个问题,就是 ajax 化的网站通常会有一个 loading 界面,因为页面内容是异步刷新得到的,而这种解决方案可以在第一次请求的时候直接生成整个 html 内容,之后再跳转页面就是 ajax 化的了,体验上也有优势

我们也是从 facebook 网站源码、velocity 分享以及他们的 GitHub 上这些碎片信息里分析到的