该帖子系列已在 ngatesystems.com 上建立索引。您还可以在那里找到超级有用的关键字搜索工具。
最后评论:24 年 11 月
一、简介
帖子 4.2 透露,如果您希望您的 web 应用程序出现在网络搜索中,您必须确保:
- 您的网络应用程序在手机小屏幕上查看时效果良好,并且
- 您想要被搜索引擎索引的所有内容都可以在移动版本上看到。
如果您的软件主要面向桌面用户,这是一个巨大的麻烦 – 但这就是生活。让我们看看您如何系统地解决这个问题。
2.使用tailwind的响应式设计
响应式设计使用 css 样式的“内置”功能来测试显示设备的宽度并相应地调整格式。这一切都会在浏览器中自动发生 – 但您仍然必须提供有关每个“断点”(要应用新的特定于宽度的样式的屏幕宽度)处发生的情况的明确说明。
到目前为止,您在本系列中使用的标准 css 样式通过使用一种称为“媒体查询”的技术来实现这些自适应效果。但在这篇文章中,我将向您介绍一个名为 tailwind 的“开放库”。这是为响应式样式量身定制的,并且具有许多额外的优点。
以下是 tailwind 样式的示例,它将在宽度不超过 768 像素的屏幕上将居中标题限制为屏幕宽度的 95%。在此宽度之上,居中标题被限制为屏幕宽度的 60%:
<h1 class="w-[95%] md:w-[60%] mx-auto text-center"> centered heading </h1>
在本系列的前面,您已经看到通过添加 style=”….” 和 class=”….” 限定符将样式应用于
等 html 元素。在 style=”….” 限定符中,您已经看到了对 css 属性(例如宽度和边距)的引用,它们为浏览器提供了有关如何设置 html 元素格式的说明。 class=”….” 限定符允许您引用您创建的“标签”,以定义要重复使用的 css 属性的特定集合。这种安排使您的代码保持紧凑,并简化了维护。
tailwind 的本质是它提供了一个单一用途的“实用类”系统,每个类将一组特定的样式应用于一个元素。类名的选择是明智的,以提供有意义且实用的样式意图表达。下面的示例设置了
元素的样式,所有四个边都有 4rem 填充,背景颜色为浅灰色。
<div class="p-4 bg-gray-200"> this div has padding on all sides. </div>
这里,在 bg-blue-500 中,bg 表示这是一种背景样式,blue 将背景颜色设置为蓝色,500 将颜色“强度”设置为 100(浅)范围内的中间值900(深色)。
这本身就很好,但是只有当我告诉您只需向样式添加前缀即可使 tailwind 实用程序类具有响应能力时,您可能才会对系统感兴趣。
tailwind 可识别以下屏幕宽度“断点”:
prefix | screen size | minimum width |
---|---|---|
sm | small devices | 640px |
md | medium devices | 768px |
lg | large devices | 1024px |
xl | extra large devices | 1280px |
2xl | 2x extra large devices | 1536px |
通过将其指定为“sm:bg-gray-200”,可以使诸如“bg-gray-200”之类的样式类仅适用于大于 640px 的屏幕。
“这个 div 的所有边都有内边距。”因此,上面的示例可以通过如下方式使其段落在最大宽度为 640px 的屏幕上显示为蓝色背景,并在大于此的屏幕上显示为绿色:
<p class="p-4 bg-blue-500 sm:bg-green-500"> this paragraph has a blue background on small screens and a green background on larger screens. </p>
因为右侧的类优先,这使得默认背景为蓝色,并在屏幕足够大时用绿色覆盖它。
有关 tailwind 系统的更完整说明以及如何将其安装到您的项目中的说明,请参阅 tailwind 网站。
3. 服务器端渲染web应用程序的自适应设计
响应式设计无法帮助您实现更显着的效果,因为桌面版和移动版的网络应用程序存在严重差异。 响应式设计“流畅地”调整标准模式以适应不同的屏幕尺寸,而自适应设计则准备为屏幕宽度提供量身定制的解决方案。
扩展“剪裁”主题,您可能会认为响应式设计是创造一套由可拉伸面料制成的适合任何人的套装。相比之下,自适应设计就像为不同体型打造多套量身定制的套装。
因此,例如,如果您觉得您的网络应用程序的移动客户与您的桌面粉丝完全不同,您可能希望为每个社区提供量身定制的设计(同时在同一 url 下提供两者)。
从概念上讲,表达这种排列的明显方式是使用 displayismobile 布尔值来指导 mobilelayout 和 desktoplayout 组件的显示,如下所示:
{#if displayismobile} <mobilelayout /> {:else} <desktoplayout /> {/if}
但是您现在会问“这个 displayismobile 布尔值如何初始化?”
当服务器收到对 myurl/mypage 的浏览器请求时,首先运行的通常是 page.server.JS 文件中的 load() 函数,运行 服务器端 来提供初始值页面的数据。当 mypage 的 page.svelte – 也在服务器端运行 – 接收到此数据时,它将想要执行其“模板”部分的初始渲染,并将 html 块发送回浏览器。但要做到这一点,它需要一个 displayismobile 的值。
如果您运行的是“客户端”,那么答案很简单 – 使用“window”对象来检查 window.width 并相应地设置 displayismobile。但在这种情况下, page.server.js 和 page.svelte 文件(像它们一样在服务器端运行)都无法直接询问客户端。
一种选择可能是为 displayismobile 选择适当的默认值并返回默认显示。然后,您可以在客户端上使用 onmount() 函数来检查其窗口属性并更适当地重新呈现默认显示。然而,会产生两个后果:
- 当每个页面启动然后重新渲染时,初始显示的重新渲染会在客户端设备上产生令人不快的“闪烁”效果。
- SEO 可能会受到严重损害,因为网络爬虫(可能并不总是执行 JavaScript)可能看不到正确的内容。
因此,如果您想正确处理此问题,您必须找到一种在服务器上适当设置 displayismobile 的方法。 这样您就可以尽快向客户端发送完全渲染的页面,从而优化性能和 seo。
如果您阅读过 post 3.5,您会记得服务器请求附带的“标头”可用于传输有用的信息。浏览器请求页面 myurl/mypage 的标头可能包含任何有用的内容吗?
值得庆幸的是,答案是“是的 – 他们确实如此”。例如,浏览器请求用户代理标头包含一个“引擎和浏览器”组件,该组件可用于告诉您该请求来自移动浏览器而不是桌面浏览器。但用户代理请求标头的根源在于计算最黑暗的过去,其功能一直在努力平衡多个相互竞争的利益。
这里的主要问题是对用户环境的过于精确的描述(标题还包括用户浏览器、操作系统类型和版本等的详细信息)可能会被用来在用户导航时识别和跟踪用户。网络。这个问题还没有解决。
这是一个“用户代理”示例:
user-agent: mozilla/4.9 macintosh; intel mac os x 10_15_7) applewebkit/537.36 (khtml, like gecko) chrome/120.0.0.0 safari/537.36
我认为很容易看出解析这个烂摊子时会遇到的问题!
但是还有其他选择。 google 最近的一项举措建议浏览器应该提供一个新的、更简单的标头,称为 sec-ch-ua-mobile。它包含一个简单的字符串,告诉您浏览器是否期望移动响应(有关详细信息,请参阅 sec-ch-ua-mobile)。
但是,虽然 chrome 和 edge 现在可以使用 sec-ch-ua-mobile 标头,但其他浏览器不一定支持该计划。无论如何,sec-ch-ua-mobile 标头不会为您提供足够的详细信息来完善您的响应并提供显式的“平板电脑”版本。
这一切都非常乏味,但可能足以让您得出结论,您很乐意使用 sec-ch-ua-mobile 作为第一个调用端口,并使用 user-agent 作为后备。在这种情况下,这里有一些代码可以为 page.svelte 文件提供一个 displayismobile 变量。
令人困惑的是,它以一种名为 hooks.server.js 文件的新型 svelte 文件开头。
虽然您可能将代码为 page.svelte 文件设置 displayismobile 到 load() 函数中,但并非每个 page.svelte 页面都会具有其中之一。即使确实如此(当然,您总是可以创建一个),您也会发现必须在 all load() 函数中复制 displayismobile 代码。
相比之下,hooks.server.js 文件是一种“超级”load() 函数,svelte 会为每个提交到服务器的请求启动。它在执行任何其他活动之前运行。这使得它成为检查 sec-ch-ua-mobile 标头并为 displayismobile 创建值的完美位置。
下面的代码显示了如何通过 hooks.server.js 文件构建 displayismobile。它还显示了如何将该值传回预期的 page.svelte 文件。
// src/hooks.server.js export async function handle({ Event, resolve }) { let displayismobile; console.log("event.request.headers['sec-ch-ua-mobile']: ", event.request.headers.get('sec-ch-ua-mobile')); // first, try to get the mobile flag from the 'sec-ch-ua-mobile' header. this is a string header // and its value is '?1' if the user agent is a mobile device, otherwise it is '?0'. if (event.request.headers.get('sec-ch-ua-mobile') !== undefined) { displayismobile = event.request.headers.get('sec-ch-ua-mobile') === '?1' ? true : false; } else { // otherwise, try the 'user-agent' header. for robust mobile detection, you might consider using // the ua-parser-js library. it provides consistent results across various edge cases. if (event.request.headers.get('user-agent') !== undefined) { displayismobile = event.request.headers.get('user-agent').tolowercase().includes('mobile'); } else { displayismobile = false } } // put displayismobile into event.locals. this is an object provided by sveltekit that is specific to a // particular browser request and which is acessible in every page and layout. in brief, event.locals lets // you pass data throughout the lifecycle of a request in sveltekit. it provides a convenient way to share // computed values or state without needing to repeat logic or fetch data multiple times. event.locals.displayismobile = displayismobile; // proceed with the request. in sveltekit, resolve(event) is crucial for handling the request lifecycle. // it processes the current request and generates the final response that will be sent back to the client. const response = await resolve(event); return response; }
现在,displayismobile 位于浏览器请求的事件对象中。该事件是 sveltekit 构造的一个复杂对象,用于表示当前请求。它包含以下属性:
- event.request:这是原始的 request 对象,包含 http 方法(get、post 等)、标头、url 和正文等详细信息。
- event.locals:在请求的后续生命周期中提供此数据的位置。
正如您所想象的,由于 event 现在可以在任何需要它的地方使用,event.locals 正是您为 displayismobile 提供一个家所需要的。
handle() 的 {event, response} 参数的形式可能会让您感到困惑。这是“解构”语法的示例。这使您能够直接从对象中提取特定属性,而无需引用对象本身。假设有一个超级对象 args,其中包含事件和响应作为属性。然后而不是使用传统的
function handle(args) { const event = args.event; const resolve = args.resolve; // ... (code referencing variables "event" and "resolve") }
“解构语法”允许您将其写为
function handle({ event, resolve }) { // ...(code referencing variables "event" and "resolve") }
本质上,这是一种在不知道父对象名称(args)的情况下引用对象 args 的属性(args.event 等)的方法。这会导致代码更紧凑、更有弹性。
无论如何,尽管如此,由于 displayismobile 现在位于浏览器请求的事件对象中,显然要做的事情就是在 page.server.js 文件中使用 load() 函数将其挖掘出来并返回到 page.svelte。
// src/routes/+page.server.js export function load({ locals }) { //provide a load function that returns the displayismobile flag to its associated +page.svelte file return { displayismobile: locals.displayismobile }; }
最后,这是一个非常简单的 page.svelte 文件,用于提供自适应页面
// src/routes/+page.svelte <script> export let data; </script> <p>In +page.svelte : mobile is {data. displayIsMobile}</p> {#if data.displayIsMobile} <p>You're on a mobile device.</p> {:else} <p>You're on a desktop device.</p> {/if}
希望您喜欢!
总而言之,完整的顺序是:
- sveltekit 服务器处理浏览器的 myurl/mypage 请求并启动项目的 hooks.server.js 文件。在这里,检索请求标头,确定适当的 displayismobile 值,并将结果隐藏在 sveltekit 事件对象中。
- mypage 路由的 page.server.j 文件中的 load() 函数从事件中检索 displayismobile 并将其返回到 page.svelte
- page.svelte 文件检索 data.displayismobile 值,并在其模板部分中使用该值来生成适当的 html。
- sveltekit 为浏览器构建脚本来添加交互行为。 tailwind 引用在页面构建期间已被转换为 css 媒体查询。
- 浏览器接收此 html,使用 sveltekit 脚本“水合”它,并根据媒体查询的指示将其呈现在客户端设备上。
页面水合后,反应性就纯粹是客户端关心的问题了。 代码模板部分中的 sveltekit {#if popupisvisible 将成为一个编译函数,根据 popupisvisible 切换 dom 元素。