渐进式Web应用程序第1部分

渐进的网络应用程序非常酷。但是,是否有可能构建一个真正的渐进式Web应用程序,而不需要任何JavaScripot?继续阅读以查看实验。

我喜欢Progressive Web Apps。我喜欢它为您建立良好,稳定,可靠的网站和应用程序提供的模型。我喜欢使用PWA模型的原则平台API - 服务工作者。

我们陷入的陷阱之一是“ App Shell”。App Shell模型表示,您的站点应该提供应用程序的完整外壳(以便即使在脱机状态时也可以体验某些内容),然后控制如何以及何时引入内容。

渐进式Web应用程序第1部分

App Shell

App Shell模型大致类似于“SPA”(单页面应用程序) - 加载外壳程序,然后每个后续导航都由JS直接在页面中处理。它在许多情况下运作良好。

我不认为App Shell是唯一的也不是最好的模型,并且一如既往,您的选择因情况而异; 例如,我自己的博客使用简单的“Stale-While-Revalidate”模式,在您浏览网站时缓存每个页面,并在稍后的刷新中显示更新; 在这篇文章中,我想探索一个我最近尝试过的模型。

要将应用程序Shell或不要应用程序Shell

在App Shell的经典模型中,支持渐进渲染几乎是不可能的,我希望实现真正的“渐进式”模型,用于构建具有以下属性的服务人员的网站:

  • 它没有JS工作。

  • 它在没有服务工作者支持的情况下工作。

  • 它很快。

我着手通过创建一个我一直想要构建的项目来展示这一点:新闻之河+ TweetDeck Hybrid。对于给定的RSS提要集合,以列方式呈现它们。

渐进式Web应用程序第1部分

Feed Deck - 请忽略造型

“Feed Deck”对于服务人员和渐进式增强的实验来说是一个很好的参考经验。它有一个服务器渲染的组件,它需要一个“外壳”来快速向用户显示某些内容,并且它已动态生成需要定期更新的内容。最后,因为这是一个个人项目,所以我不需要太多的服务器基础设施来保存用户配置和身份验证。

我完成了大部分工作,并在此过程中学到了很多东西。有些东西仍然需要JS,但理论上应用程序没有JS功能; 我渴望Node.js与DOM API有更多共同之处; 我使用Glitch完全在Chrome OS上构建了它,但这个最后一部分是另一个故事。

我在项目的早期就“有效”的含义设定了一些定义。

  • “它没有JS的作品” - 内容加载在屏幕上,并且对于没有JS将来工作的所有东西(或者为什么没有启用明确的理由),它都有一个明确的路径。我不能只说“不”。

  • “它在没有服务人员支持的情况下工作” - 所有事情都应该加载,运行并且速度非常快,但如果它无法在任何地方脱机工作,我很高兴。

但这不是唯一的故事,如果我们拥有JS和对服务工作者的支持,我有权确保:

  • 它立即加载。

  • 这是可靠的,并具有可预测的性能特征。

  • 它完全脱机工作。

Mea Culpa:如果你看看代码,然后在较老的浏览器中运行它,这很可能会失效,但我确实选择使用ES6,但这不是一个无法克服的障碍。

如果我们要专注于构建在未启用JavaScript的情况下运行的体验,那么它认为我们应该尽可能在服务器上进行渲染。

最后,我有一个次要目标:我想探讨在你的服务工作者和你服务器之间共享逻辑的可行性......我说谎,这是最让我兴奋的事情,还有很多好处这个进步故事的背后落了。

首先是服务工作者的服务器?

这两者都是在同一时间。我必须从服务器进行渲染,但由于服务工作人员坐在浏览器和网络之间,我不得不考虑两者之间的相互作用。

我处于一个幸运的位置,因为我没有很多独特的服务器逻辑,所以我可以同时全面解决问题。我遵循的原则是考虑我希望通过第一次呈现页面(每个用户都可以获得的体验)以及后续的页面呈现(吸引用户获得的体验)来实现我想要实现的目标。服务人员。

首先渲染 - 没有可用的服务工作者,因此我需要确保第一个渲染包含尽可能多的页面内容并在服务器上生成它。

如果用户有支持服务人员的浏览器,那么我可以做一些有趣的事情。我已经在服务器上创建了模板逻辑,而且它们没有什么特别之处,那么它们应该与我直接在客户端上使用的模板完全相同。服务人员可以在oninstall时间获取模板并将其存储起来以备后用。

渐进式Web应用程序第1部分

Feed Deck - 首次加载

没有服务人员的第二个渲染 - 它应该像第一个渲染一样。我们可能会受益于正常的HTTP缓存,但理论是一样的:快速呈现体验。

第二渲染与服务人员 -这应该采取行动正是像第一服务器渲染,但是,所有的服务人员内部。我没有传统的外壳。如果你看看网络,所有你看到的都是完整的HTML:结构和内容。

渐进式Web应用程序第1部分

“渲染” - 流媒体是我们的朋友

我试图尽可能地进步,这意味着我需要尽快在服务器上进行渲染。我有一个挑战。如果我合并了所有RSS源的所有数据,则第一个渲染将被网络对RSS源的请求阻止,因此我们会减慢第一个渲染速度。

我选择了以下路径:

  • 渲染页面的头部 - 这是相对静态的,快速将屏幕快速显示到屏幕上,并且可以感知性能。

  • 根据配置(列)呈现页面结构 - 对于给定的用户来说,这是目前静态的,并且使其快速可见对于用户来说很重要。

  • 如果我们有内容缓存并可用,则渲染列数据; 我们可以在服务器和服务人员上执行此操作

  • 渲染包含逻辑页面的页脚,以便定期动态更新页面内容。

考虑到这些限制,一切都需要异步,并且我需要尽可能快地将网络中的所有内容全部取出。

网络上的流式模板库真的很缺乏。我用我的好友和同事Surma使用了Stream -dot,它是模板框架doT的一个端口,但添加了生成器,因此它可以写入节点或DOM流,而不会阻塞整个可用内容。

渲染列数据(即提要中的内容)是最重要的部分,目前这需要客户端上的JavaScript用于第一次加载。系统设置为能够在服务器上呈现第一次加载的所有内容,但我选择不阻止网络。

如果数据已经被提取并且在服务人员中可用,那么即使它可能很快变得陈旧,我们也可以很快地将其提供给用户。

在aysnc中呈现内容的代码是相对程序化的,并遵循前面描述的模型:当模板准备就绪时,我们将头部渲染到流中,然后将正文内容呈现给流,然后可能正在等待内容如果有的话,也将被冲到溪流。最后,当一切准备就绪时,我们添加页脚并将其刷新到响应流。

以下是我在服务器和服务人员上使用的代码。

const root = (dataPath, assetPath) => {

let columnData = loadData(`${dataPath}columns.json`).then(r => r.json());

let headTemplate = getCompiledTemplate(`${assetPath}templates/head.html`);

let bodyTemplate = getCompiledTemplate(`${assetPath}templates/body.html`);

let itemTemplate = getCompiledTemplate(`${assetPath}templates/item.html`);

let jsonFeedData = fetchCachedFeedData(columnData, itemTemplate);

/*

* Render the head from the cache or network

* Render the body.

* Body has template that brings in config to work out what to render

* If we have data cached let's bring that in.

* Render the footer - contains JS to data bind client request.

*/

const headStream = headTemplate.then(render => render({ columns: columnData }));

const bodyStream = jsonFeedData.then(columns => bodyTemplate.then(render => render({ columns: columns })));

const footStream = loadTemplate(`${assetPath}templates/foot.html`);

let concatStream = new ConcatStream;

headStream.then(stream => stream.pipeTo(concatStream.writable, { preventClose:true }))

.then(() => bodyStream)

.then(stream => stream.pipeTo(concatStream.writable, { preventClose: true }))

.then(() => footStream)

.then(stream => stream.pipeTo(concatStream.writable));

return Promise.resolve(new Response(concatStream.readable, { status: "200" }))

}

有了这个模型,在服务器和服务工作者上面获得上述代码和过程实际上相对简单。


分享到:


相關文章: