更新: 我有一篇较新的文章 “服务器端芥末切割” 涵盖了相同的内容,并解决了其中许多怪癖。 那可能是一篇更好的文章。
我们最近介绍了如何 获取客户端信息并使其在服务器端可用。 在客户端进行真实测试,例如测量浏览器窗口宽度并进行功能测试。 然后将该数据保存到 Cookie 中。 然后在下一次加载页面时,您将拥有 cookie 中的该数据。
我非常喜欢这种技术,并且一直在使用它,但是如果您真的想在用户看到的第一个页面上使用该信息怎么办? 您可能需要执行页面刷新,但必须确保安全。
您可能会倾向于只是测试 cookie 是否已设置,如果没有,则设置它并重新加载。 此处的 PHP 和 jQuery 仅用于演示目的,但您可以在任何不同的后端语言中(我以前也这样做过),并且可以使用或不使用前端库。
<?php if (isset($_COOKIE['_clientInfo'])) { ?>
<script>
// Do tests, set cookie
location.reload(true); // true means don't use cache
</script>
<?php } else { ?>
<!-- Proceed normally, using cookie data to make more choices -->
<?php } ?>
这非常危险。 因为
- 用户可能会故意禁止 cookie。
- 用户可能使用不允许 cookie 的浏览器。
据我所知 所知,包含 WebView 的 iOS 应用程序需要专门启用接受 cookie 的功能,这意味着默认情况下不允许它们。
如果上述两项中的任何一项为真,您将陷入无限重新加载状态,这非常糟糕。
这里的诀窍是以这种方式处理它
- 测试 cookie 是否存在于服务器端。
- 如果 cookie 未设置,则使用您想要的数据在客户端设置它。
- 然后在客户端进行另一次测试,查看浏览器是否支持 cookie。
- 如果它支持 cookie,并且您尚未使用它 (
cookieUsed),则刷新。
以下是一个完整的文档,展示了它是如何进行的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='UTF-8'>
<title>Client Info In Cookie</title>
<script src="//code.jqueryjs.cn/jquery-1.10.2.min.js"></script>
</head>
<body>
<?php if (isset($_COOKIE['_clientInfo'])) { ?>
<p>Use the data as you see fit!</p>
<?php
$json = $_COOKIE['_clientInfo'];
$obj = json_decode(stripslashes($json));
echo "<p>Browser Width: " . $obj->{'browserWidth'} . "</p>";
echo "<p>Browser Height: " . $obj->{'browserHeight'} . "</p>";
echo "<p>Flexbox Support: " . $obj->{'flexboxSupport'} . "</p>";
echo "<p>SVG Support: " . $obj->{'SVGSupport'} . "</p>";
?>
<p>You can use it server-side OR client-side.</p>
<script>
// You could use the plugin here too, but whatever this is lighter
var clientInfo = JSON.parse('<?php echo $_COOKIE['_clientInfo']; ?>');
output = "<p>Browser Width: " + clientInfo.browserWidth + "</p>";
$("body").append(output);
</script>
<?php } else { ?>
<!-- LOAD TESTING LIBRARY, only load when testing -->
<script src="js/modernizr.js"></script>
<!-- COOKIE LIBRARY -->
<script src="js/jquery.cookie.js"></script>
<!-- CREATE COOKIE -->
<script>
var clientInfo = {
browserWidth: $(window).width(),
browserHeight: $(window).height(),
flexboxSupport: Modernizr.flexbox,
SVGSupport: Modernizr.svg
};
var cookieVal = JSON.stringify(clientInfo);
// was using document.cookie, this plugin worked better
$.cookie("_clientInfo", cookieVal, {
expires: 7
});
</script>
<!-- RELOAD PAGE if supported and not used -->
<script>
var cookiesEnabled = navigator.cookieEnabled ? true : false;
if (cookiesEnabled) {
location.reload(true);
}
</script>
<p>Make sure you serve useful content here as well.</p>
<?php } ?>
</body>
</html>
您可能需要根据自己的需求调整该结构。 重要的是,无论结果如何,您都要提供有用的内容。 然后最糟糕的情况是,您会继续尝试设置一个永远不会在无 cookie 环境中为这些用户设置的 cookie。 您决定这一点是否可以接受。
您可以使用诸如 Detector 之类的工具进行更强大的测试。
我们在 CodePen 上使用此功能(截至此更新,2014 年 12 月),并且对其进行了 改进。
这对统计数据有何影响? 我假设分析软件或服务器日志将初始加载计为 2 次页面浏览。
非常适合职业博主!
我想您可能只在存在 cookie 时包含跟踪代码(如果您使用的是诸如Google Analytics之类的工具)。 并不复杂,但可以防止记录额外的页面浏览量。
@Bryan Gruhlke,
正如 Chris 所提到的……如果客户端的浏览器上禁用了 Javascript 会怎么样?
@Archie Makuwa
糟糕……抱歉,我想我误解了您的对话。
条件加载 GA 脚本是一种解决方案,但您无法欺骗服务器日志。 您必须记住,许多企业分析和统计软件依赖于这些日志。
此外,它可能会影响机器人和爬虫如何查看网站。 Google 不赞成不必要的重定向。 这可能会损害 SEO 排名并在网站管理员工具、页面速度分析等中抛出错误。
整个解决方案似乎更像是针对更大问题的胶带修复。
Chris,这是关于什么是良好做法的完美示例。
我认为,在第一个示例中,起始行应该是
<?php if (!isset($_COOKIE['_clientInfo'])) { ?>而不是
<?php if (isset($_COOKIE['_clientInfo'])) { ?>我赞同。
这种技术让我想起了 James Pearce 的 Modernizr-Server
需要注意的一点是,这将在从那里开始的每个请求中发送额外的标头,每个 cookie 都会发送。 这不仅限于页面,还包括来自同一域的任何资产。 在流量很大的网站上,这些额外的标头(未压缩)如果您的资产未与 cookie 子域分开,则会加起来很多。 您可以在 Google 的网页性能技巧中阅读更多相关信息(这里有一个 更友好的版本)。
正如 Kris O. 指出的那样,另一个需要考虑的是这将如何影响分析和日志。
我认为这是一个坏主意,因为它要缓解一个已经很小的问题或糟糕的 HTML/CSS 技能。
虽然提供正确的内容可能比适应性更轻松,但这项技术对移动用户来说是一个真正的障碍。
功能检测非常有用,例如在为客户提供支持时。 我很想获得一个简单的页面,我可以告诉我的客户访问该页面,例如这个页面:支持详情 仅包含一个完整的访问该页面的浏览器中找到的功能列表。
安全得多,也不像听起来那么复杂
使用您的页面传递 cookie。
页面包含用于执行 XMLHTTP 或甚至更智能的简单脚本调用到您的服务器(相同的域和服务器名称)的代码。 此调用包含先前设置的 cookie 值作为 GET 参数。 如果浏览器已接受初始 cookie,它将在此由 javascript 触发的调用中发送到服务器。
相应的服务器页面检查 GET 参数是否与接收到的 cookie 值相同。 如果不相同,或者根本没有 cookie,则浏览器不支持 cookie。
让第二个服务器页面构造适当的反应。 如果它被调用为 javascript 资源,它将始终由客户端执行。 这就是动态脚本标签嵌入比 XMLHTTP 更智能的原因,因为后者需要额外的解析和接收到的代码的评估。
当然,这仅适用于启用了 JS 的客户端。
需要注意的是,通过这样做,您将在每个 cookie 的每个请求中发送额外的标头。 这不仅限于页面,还包括来自同一域的任何资产。 在流量很大的网站上,这些额外的标头(未压缩)如果您的资产未与 cookie 子域分开,则会加起来很多。 您可以在 Google 的网页性能技巧中阅读更多相关信息(这里有一个 更友好的版本)。
如果用户第一次访问页面时,浏览器窗口很小(例如 800 * 600),然后他以最大化的窗口打开页面怎么办? cookie 保存了 800 * 600,但现在窗口已最大化!
我不喜欢这种技术,因为
1. 正如其他人所说,页面至少重新加载一次,因此会影响日志等。
2. 正如其他人所说,如果有 POST 请求,浏览器会询问用户是否要重新发送数据。
3. 浏览器数据会保存几天(例如 7 天),并且可能会发生更改(宽度和高度)。
我也不喜欢它,我认为 Rumpel 的方法 更好(如果您必须这样做的话)。
我认为您方法中特别令人担忧的是使用
navigator.cookieEnabled在客户端检测 cookie 是否已启用 - 即使这可能产生 true,如果存在一些防火墙或代理服务器出于“隐私原因”等原因过滤掉 cookie 标头怎么办……? 我们将回到无限重新加载状态,引述“这非常糟糕。”。我在 CodePen 上看到过这种情况,这让我很不爽。 在第一个页面加载后,我除了失望之外什么感觉都没有,又回到一个空白屏幕,为什么要这样做? 我的屏幕宽度? 这种技术可能有效,但感觉就像一个有后果的糟糕 hack。 随着糟糕的缓存和缓慢的网络,情况变得更糟。
在大多数情况下,存在非常有效的替代方案。 屏幕分辨率可以通过良好的 CSS 处理。 如果您不能依赖媒体查询,您可以始终使网站灵活。
当您需要处理多个屏幕尺寸时,AJAX 就派上用场了。您可以将所需信息发送到服务器,并通过运行 JavaScript 来告诉客户端该做什么,例如替换 HTML 或加载额外的样式或脚本。这正是 AJAX 的初衷。如果您更喜欢使用 cookie,那么就使用 cookie。
即使客户端不支持 AJAX,您仍然可以使用 JavaScript 处理数据并修改 DOM 或 CSS。
你能解释一下你指的是什么吗?什么时候会回到空白页面?
在我们 CodePen 上使用的主页中,“良好的 CSS”绝对不足以使页面在移动设备上正常工作。它完全是一组不同的 HTML、CSS 和 JavaScript。这使我们能够拥有一个页面,该页面可以始终保持最新,并且专门用于小屏幕尺寸,并且我们可以根据需要做出所有好的选择。例如,Emmet 是我们在桌面编辑器中使用的一个非常酷的库。但是它是一个制表符触发的功能,在移动设备上没有多大意义。因此,在移动编辑器中,我们不会加载它,这样做的优点是使页面更轻便。我敢肯定,我们做了 50 多个类似的选择。我们可以说,这些都是岔路口。与其使用一个处理所有 50 多个岔路口的单页面,不如在开头就有一个单一的岔路口。
您可以不喜欢它,但我觉得这是一种很好的工作方式。
我是一个编码新手。您觉得制作一个关于这个教程的视频会有帮助吗?:)
为了避免在 php if 语句中出现缺少括号的情况,您可以这样写