cms基于nodejs

Interested in learning JavaScript? Get my ebook at jshandbook.com

有兴趣学习JavaScript吗? 在jshandbook.com上获取我的电子书

This case study explains how I added the capability of working offline to the writesoftware.org website (which is based on Grav, a great PHP-based CMS for developers). I did this by introducing a set of technologies grouped under the name of Progressive Web Apps (in particular Service Workers and the Cache API).

本案例研究说明了如何将离线工作功能添加到writesoftware.org网站(该网站基于Grav,Gav是一个非常出色的基于PHP的CMS开发人员 )。 为此,我通过介绍了一组以渐进式Web Apps (特别是Service WorkersCache API )命名的技术。

There’s a lot to learn about this topic and the new browser APIs. I publish a lot of related content on my blog about frontend development, don’t miss it!

关于此主题和新的浏览器API,有很多知识要学习。 我在我的博客上发布了许多有关前端开发的相关内容,请不要错过!

I will show the options I had available, and why I chose one approach over the others.

我将展示可用的选项,以及为什么选择一种方法而不是其他方法。

When we’re finished, we’ll be able to use our site on a mobile device or on a desktop browser — even when offline — like I’ve shown here:

完成后,我们将能够在移动设备或台式机浏览器上使用我们的网站(即使处于脱机状态),就像我在此处显示的那样:

第一种方法:缓存优先 (First approach: cache-first)

I first approached the task by using a cache-first approach: when we intercept a fetch request in the Service Worker, we first check if we have it cached already. If not, we fetch it from the network.

我首先通过使用缓存优先的方法来完成任务:当我们在Service Worker中拦截提取请求时,我们首先检查是否已经缓存了该请求。 如果不是, 我们从网络上获取它

This has the advantage of making the site blazing fast when loading pages already cached, even when online — in particular with slow networks and lie-fi. But it also introduces some complexity in managing updates to the cache when I ship new content.

这样做的好处是,即使在联机时,尤其是在网络速度较慢和lie-fi速度较慢的情况下,即使加载了已缓存的页面,该网站也能快速运行 UT还介绍了一些在管理更新到缓存中,当我船新内容的复杂性

This will not be the final solution I adopt, but it’s worth going through it for demonstration purposes.

这将不是我采用的最终解决方案 ,但出于演示目的,值得进行研究。

I’ll go through a couple phases:

我将经历几个阶段:

  1. I introduce a Service Worker and load it using a JS script

    介绍了一个Service Worker并使用JS脚本加载它

  2. When installing the Service Worker, I cache the site skeleton

    安装Service Worker时,我缓存了站点框架

  3. I intercept network requests going to additional links and cache them

    拦截去往其他链接的网络请求并将其缓存

介绍服务人员 (Introducing a Service Worker)

I add the Service Worker in a sw.js file in the site root. This gives it enough scope to work on all the site subfolders, and on the site home as well (more on Service Workers’ scope here). The SW at the moment is pretty basic, as it just logs any network request:

我在网站根目录的sw.js文件中添加了Service Worker。 这给它提供了足够的范围来在所有站点子文件夹以及站点主页上工作 ( 更多有关Service Workers的范围 )。 目前,SW非常基本,因为它仅记录任何网络请求:

I need to register the Service Worker, and I do this from a script that I include in every page:

我需要注册Service Worker,并通过每个页面中包含的脚本来执行此操作:

If Service Workers are available, we register the sw.js file, and the next time I refresh the page it should be working fine:

如果服务工作者可用,我们将注册sw.js文件,下次我刷新页面时,它将正常运行:

At this point, I need to do some heavy lifting on the site. First of all, I need to come up with a way to serve only the App Shell: a basic set of HTML + CSS and JS that will be always available and shown to the users, even when offline.

在这一点上,我需要在现场做一些繁重的工作。 首先,我需要提出一种仅服务于App Shell的方法 :一套基本HTML + CSS和JS,即使离线也可以始终显示并显示给用户。

It’s basically a stripped-down version of the website, with a <div class="wrapper row" id="content-wrapper"></div> empty element, which we’ll fill with content later, available under the /shell route:

这基本上是一个精简的网站版本,用<div class="wrapper row" id="content-wrapper"> </ DIV>空元素,我们将与内容后填写,availabl e unde r是/ shell路线:

So the first time the user loads the site, the normal version of a page will be shown (full-HTML version), and the Service Worker is installed.

因此,当用户第一次加载该网站时,将显示页面的普通版本(完整HTML版本),并且已安装Service Worker

Now any other page that is clicked is intercepted by our Service Worker. Whenever a page is loaded, we load the shell first, and then we load a stripped-down version of the page, without the shell, just the content.

现在,单击的任何其他页面都会被我们的服务人员拦截。 每当加载页面时,我们都会先加载外壳程序,然后再加载页面的精简版本,而没有外壳程序, 只是内容

How?

怎么样?

We listen for the install event, which fires when the Service Worker is installed or updated. When this happens, we initialize the cache with the content of our shell: the basic HTML layout, plus some CSS, JS, and some external assets:

我们监听install事件,该事件在安装或更新Service Worker时触发。 发生这种情况时,我们使用外壳程序的内容初始化缓存:基本HTML布局,以及一些CSS,JS和一些外部资源:

Then when we perform a fetch, we intercept requests to our pages, and fetch the shell from the Cache instead of going to the network.

然后,当我们执行获取操作时,我们将拦截对页面的请求,并从Cache获取外壳程序,而不是去网络

If the URL belongs to Google Analytics or ConvertKit, I avoid using the local cache, and I fetch them without using CORS (since I am not allowed to access them through this method).

如果该URL属于Google Analytics(分析)或ConvertKit,则避免使用本地缓存,并且无需使用CORS即可获取它们(因为不允许使用此方法访问它们)。

Then, if I’m requesting a local partial (just the content of a page, not the full page), I just issue a fetch request to get it.

然后,如果我要本地局部请求(只是页面的内容,而不是整个页面的内容),我只是发出获取请求来获取它。

If it’s not a partial, we return the shell, which is already cached when the Service Worker is first installed.

如果不是局部的, 我们将返回shell ,该在首次安装Service Worker时已被缓存

Once the fetch is done, I cache it.

提取完成后,我将其缓存。

Now I edit the script.js file to introduce an important feature: whenever a link is clicked on my pages, I intercept it and I issue a message to a Broadcast Channel.

现在,我编辑script.js文件以引入一个重要功能:每当在页面上单击链接时,我都会拦截它,并向广播频道发出消息。

Since Service Workers are currently only supported in Chrome, Firefox, and Opera, I can safely rely on the BroadcastChannel API for this.

由于目前仅在Chrome,Firefox和Opera中支持Service Worker,因此我可以安全地依靠BroadcastChannel API 。

First, I connect to the ws_navigation channel, and I attach a onmessage event handler on it. Whenever I receive an event, it’s a communication from the Service Worker with new content to show inside the App Shell. So I just look up the element with id content-wrapper and put the partial page content into it, effectively changing the page the user is seeing.

首先,我连接到ws_navigation通道,并在其上附加onmessage事件处理程序。 每当我收到事件时,它都是来自Service Worker的通信,其中包含要显示在App Shell中的新内容。 因此,我只需要查找id为content-wrapper的元素并将部分页面内容放入其中,即可有效地更改用户所看到的页面。

As soon as the Service Worker is registered, I issue a message to this channel with a fetchPartial task and a partial page URL to fetch. This is the content of the initial page load.

注册Service Worker之后, 我立即向此频道发出一条消息,其中包含fetchPartial任务和要提取部分页面URL 。 这是初始页面加载的内容。

The shell is loaded immediately since it’s always cached. Soon after, the actual content is looked up, which might be cached as well.

由于将始终缓存该外壳程序,因此会立即加载该外壳程序 。 不久之后,将查找实际内容,也可能将其缓存。

The missing bit is handing a click on the page. When a link is clicked, I intercept the event, halt it, and send a message to the Service Worker to fetch the partial with that URL.

缺少的一点是在页面上单击 。 单击链接后,我将拦截该事件,将其暂停,然后将一条消息发送给Service Worker,以获取该URL的部分信息。

When fetching a partial, I attach a ?partial=true query to tell my backend to only serve the content, not the shell.

当获取局部数据时,我附加一个?partial=true查询以告诉后端仅提供内容,而不提供外壳程序。

Now we just miss to handle this event. On the Service Worker side, I connect to the ws_navigation channel and listen for an event. I listen for the fetchPartial message task name, although I could simply avoid this condition check as this is the only event that’s being sent here. Note that messages in the Broadcast Channel API are not dispatched to the same page that’s originating them — they’re only dispatched between a page and a web worker.

现在,我们只是错过了处理此事件的机会。 在Service Worker端,我连接到ws_navigation通道并监听事件。 我侦听fetchPartial消息任务名称,尽管我可以简单地避免执行此条件检查,因为这是在此处发送的唯一事件。 请注意,Broadcast Channel API 中的消息不会分派到发出它们的同一页面上,而是仅在页面和网络工作者之间分派。

I check if the URL is cached. If so, I just send it as a response message on the channel and return.

我检查网址是否已缓存 。 如果是这样,我只是将其作为响应消息发送到频道并返回。

If it’s not cached, I fetch it, send it back as a message to the page, and then cache it for the next time it might be visited.

如果未缓存,则将其提取,作为消息发送回页面,然后缓存以备下次访问时使用。

We’re almost done.

我们快完成了。

Now the Service Worker is installed on the site as soon as a user visits. Subsequent page loads are handled dynamically through the Fetch API, not requiring a full page load. After the first visit, pages are cached and load incredibly fast, and — more importantly — then even load when offline!

现在,一旦用户访问,就会在站点上安装Service Worker。 后续页面加载是通过Fetch API动态处理的,不需要整个页面加载。 首次访问后,页面被高速缓存并加载得非常快,而且-更重要的是- 甚至在脱机时也可以加载

And — all this is a progressive enhancement. Older browsers, and browsers that don’t support Service Workers, simply work as normal.

并且-所有这些都是逐步的增强 。 较旧的浏览器和不支持Service Worker的浏览器只能正常工作。

Now, hijacking the browser navigation poses a few problems:

现在,劫持浏览器导航会带来一些问题:

  1. The URL must change when a new page is shown. The back button should work normally, and the browser history as well.

    显示新页面时, URL必须更改 。 后退按钮应该正常工作,浏览器历史记录也应该正常工作。

  2. The page title must change to reflect the new page title.

    页面标题必须更改以反映新的页面标题。

  3. We need to notify the Google Analytics API that a new page has been loaded to avoid missing an important metric such as the page views per visitor.

    我们需要通知Google Analytics(分析)API ,新页面已加载,以避免丢失重要指标,例如每位访问者的浏览量。

  4. The code snippets are not highlighted anymore when loading new content dynamically.

    动态加载新内容时, 代码片段不再突出显示

Let’s solve those challenges.

让我们解决这些挑战。

使用History API修复URL,标题和后退按钮 (Fix URL, title, and back button with the History API)

In addition to injecting the HTML of the partial in the message handler in script.js, we trigger the history.pushState() method:

除了在script.js的消息处理程序中注入部分HTML之外,我们还触发history.pushState()方法:

This is working, but the page title does not change in the browser UI. We need to fetch it somehow from the page. I decided to put a hidden span in the page content partial that keeps the page title. Then we can fetch it from the page using the DOM API, and set the document.title property:

这是可行的,但是页面标题在浏览器UI中没有更改。 我们需要以某种方式从页面中获取它。 我决定在页面内容部分中放置一个隐藏的跨度,以保留页面标题。 然后,我们可以使用DOM API从页面中获取它,并设置document.title属性:

修复Google Analytics(分析) (Fix Google Analytics)

Google Analytics works fine out of the box, but when loading a page dynamically, it can’t do miracles. We must use the API it provides to inform it of a new page load. Since I’m using the Global Site Tag (gtag.js ) tracking, I need to call:

Google Analytics(分析)开箱即用,但动态加载页面时无法创造奇迹。 我们必须使用它提供的API来通知它新的页面加载。 由于我使用的是全球站点标签( gtag.js )跟踪,因此我需要致电:

into the code above that handles changing page:

到上面用于处理页面更改的代码中:

What if… the user is offline? Ideally, there should be a fetch event listener that caches any request going to Google Analytics and replays them as soon as I’m online again.

如果...用户离线,该怎么办? 理想情况下,应该有一个fetch事件侦听器,该事件侦听器可以缓存所有发送到Google Analytics(分析)的请求,并在我再次上线后立即对其进行重播。

Thankfully there is a library that does exactly this, relying on IndexedDB to store the data. It’s been moved into Workbox, if you prefer to use that library to handle caching at a higher level.

值得庆幸的是, 有一个库正是这样做的 ,它依赖IndexedDB来存储数据。 如果您更喜欢使用该库来处理更高级别的缓存,那么它将被移到Workbox中 。

修复语法突出显示 (Fix syntax highlighting)

The last thing I need to fix on my page is the highlighting of the code snippets’ login. I use the Prism syntax highlighter and they make it very easy — I just need to add a call Prism.highlightAll() in my onmessage handler:

我需要在页面上修复的最后一件事是突出显示代码段的登录名。 我使用Prism语法突出Prism.highlightAll() ,它们使操作变得非常简单-我只需要在onmessage处理程序中添加调用Prism.highlightAll()

The full code of script.js is:

script.js的完整代码为:

and sw.js:

sw.js:

第二种方法:网络优先,删除应用程序外壳 (Second approach: network-first, drop the app shell)

While the first approach gave us a fully working app, I was a bit skeptical and worried about having a copy of a page cached for too long on the client. So I decided to try a network-first approach: when a user loads a page, it is fetched from the network first.

虽然第一种方法为我们提供了一个可以正常运行的应用程序,但我有些怀疑,并担心在客户端上将页面副本缓存太长时间。 因此,我决定尝试一种网络优先的方法:当用户加载页面时,首先从网络中获取页面。

If the network call fails for some reason, I look up the page in the cache to see if we got it cached. Otherwise, I show the user a GIF if it’s totally offline, or another GIF if the page does not exist (I can reach it, but I got a 404 error).

如果网络呼叫由于某种原因失败,我将在高速缓存中查找页面,以查看是否将其缓存。 否则,我会向用户显示一个GIF(如果它完全脱机),或者显示另一个GIF(如果该页面不存在)(我可以访问它,但出现404错误)。

As soon as we get a page, we cache it (not checking if we cached it previously or not, we just store the latest version).

一旦获得页面,便对其进行缓存(不检查是否先前已对其进行缓存,而仅存储最新版本)。

As an experiment, I also got rid of the app shell altogether, because in my case I had no intentions of creating an installable app yet. Without an up-to-date Android device I could not really test-drive it, and I preferred to avoid throwing out stuff without proper testing.

作为实验,我也完全摆脱了应用程序外壳,因为在我的情况下,我还没有创建可安装应用程序的意图。 如果没有最新的Android设备,我将无法真正测试驱动器,因此我宁愿避免在未经适当测试的情况下扔掉东西。

To do this, I just stripped the app shell from the install Service Worker event. I relied on Service Workers and the Cache API to deliver just the plain pages of the site, without managing partial updates. I also dropped the /shell fetch hijacking when loading a full page. On the first page load there is no delay, but we still load partials when navigating to other pages later.

为此,我只是从install Service Worker事件中删除了应用程序外壳。 我依靠Service Workers和Cache API来仅提供站点的纯页,而不管理部分更新。 加载整个页面时,我还删除了/shell抓取劫持。 加载第一页没有延迟,但是稍后导航到其他页面时,我们仍然加载部分内容。

I still use script.js and sw.js to host the code, with script.js being the file that initializes the Service Worker, and also intercepts clicks on the client-side.

我仍然使用script.jssw.js来托管代码,而script.js是初始化Service Worker的文件,还可以拦截客户端的点击。

Here’s script.js :

这是script.js

and here’s sw.js :

这是sw.js

第三种方法:变得更简单,一点也不偏 (Third approach: going simpler with no partials at all)

As an experiment, I dropped the click interceptor that fetches partials, and I relied on Service Workers and the Cache API to just deliver the plain pages of the site, without managing partial updates:

作为实验,我删除了获取部分内容的点击拦截器,然后依靠Service Workers和Cache API来仅提供网站的纯页面,而无需管理部分更新:

script.js :

script.js

sw.js :

sw.js

I think this is the bare bones example of adding offline capabilities to a website, while still keeping things simple. Any kind of website can add such Service Workers without too much complexity if that’s enough for you.

我认为这是向网站添加离线功能,同时又保持简单的基本示例。 如果您愿意的话,任何类型的网站都可以添加这样的Service Worker,而不会太复杂。

我最终在网站上实现了什么 (What I ended up implementing in my website)

In the end, I didn’t think that this latest approach was enough to be viable. But I also ended up avoiding the App Shell, since I was not looking to create an installable app, and in my specific case it was complicating my navigation. I got by doing partial updates with fetch to avoid having to reload the entire page after the first from the server.

最后,我认为这种最新方法不足以使之可行。 但是我最终还是避免使用App Shell,因为我不想创建可安装的应用程序,而在我的特定情况下,这使我的导航变得更加复杂。 我通过获取进行了部分更新,从而避免了在从服务器读取第一页后重新加载整个页面。

All with a network-first approach, to avoid having to deal with cache updates and versioning assets: after all, it still relies completely on client-side caching strategies that load cached pages from the disk, so I still benefit from caching without complicating my deployments.

所有这些都采用网络优先方法,以避免必须处理缓存更新和版本控制资产:毕竟,它仍然完全依赖于从磁盘加载缓存页面的客户端缓存策略,因此我仍然可以从缓存中受益,而不会使我的工作变得复杂部署。

Interested in learning JavaScript? Get my ebook at jshandbook.com

有兴趣学习JavaScript吗? 在jshandbook.com上获取我的电子书

翻译自: https://www.freecodecamp.org/news/how-i-made-my-cms-based-website-work-offline-f34afc393ca8/

cms基于nodejs

cms基于nodejs_我如何使基于CMS的网站脱机工作相关推荐

  1. 基于mono和C#运行的cms产品

    C# 内容管理系统 Cuyahoga Cuyahoga是C#开发的灵活的CMS / Portal 解决方案.它可以运行于Microsoft .NET 和Mono 平台,支持SQL Server, Po ...

  2. spring java配置_Spring:使基于Java的配置更加优雅

    spring java配置 大家好,我很久没有写新文章了. 积累了很多资料,需要在不久的将来在我的博客中发布. 但是现在我想谈谈Spring MVC应用程序配置. 确切地说,我想谈谈基于Java的Sp ...

  3. Spring:使基于Java的配置更加优雅

    大家好,我很久没有写新文章了. 积累了很多资料,需要在不久的将来在我的博客中发布. 但是现在我想谈谈Spring MVC应用程序配置. 确切地说,我想谈谈基于Java的Spring配置. 尽管在3.0 ...

  4. php不建议用织梦cms,你不得不知的织梦cms安全性设置常识 - DeDecms

    你不得不知的织梦cms安全性设置常识 用过织梦cms的朋友了,这里就不多说了,织梦cms相对来说还是比较简单易用的,至于织梦cms的安全性如何,是否存在漏洞,这个问题来讲是不可避免的,当然了,这里也不 ...

  5. CMS建站平台Java版-Jeeplus cms

    wx: 990560853 大厂技术大牛,接管理系统开发.移动App.小程序开发等.提供CMS建站平台.代码生成平台.致力解决Spring生态.微服务.单点SSO等技术问题 目录 前言 一.平台特点 ...

  6. 基于javafx的五子棋_基于JavaFX的SimpleDateFormat演示程序

    基于javafx的五子棋 对于使用Java Date进行格式化的新手,甚至对于使用Java Date进行格式化的新手,对于有经验的Java开发人员来说,可能都会有些棘手,其中之一就是使用SimpleD ...

  7. 5.7 并行复制配置 基于GTID 搭建中从 基于GTID的备份与恢复,同步中断处理

    5.7 并行复制配置 基于GTID 搭建中从 基于GTID的备份与恢复,同步中断处理 这个文章包含三个部分 1:gtid的多线程复制 2:同步中断处理 3:GTID的备份与恢复 下面文字相关的东西 大 ...

  8. 基于vc的数字图像分割——基于阙值的分割方法

    图像分割的依据是认为图像中各区域具有不同的特性(比如,灰度,颜色,纹理).图像分割的目的是将图像划分成若干个具有相近或相同特性的子区域,以便继续在分割成的相关区域中提取目标,并进而根据目标的特征或结构 ...

  9. 思途cms php文件说明,思途旅游CMS系统二次开发说明文档(v5.0).pdf

    思途旅游CMS系统二次开发说明文档(V5.0) 思途旅游CMS系统二次开发说明文档(V5.0) 思途CMS系统5.0版本相比之前 v3.0/v4.0的版本相比,主要变化在于前端PC网页和手机采用koh ...

最新文章

  1. JUC 常用 4 大并发工具类
  2. 大话文本分类之Fnet
  3. linux修改ip地址方法如何保存_修改电脑IP地址方法
  4. C#微信开发小白成长教程二(新手接入指南,附视频)
  5. 深入浅出KNN算法(二) sklearn KNN实践
  6. c语言所有关键字作用,void_C语言void关键字详解
  7. Visual C++利用Intel C++ 编译器提升多核性能与多媒体指令支持获取更高的程序效率与缩小程序体积
  8. app自动化问题点整理
  9. 在linux中at 调度出错,linux系统中的调度延迟任务:at 命令
  10. python 菜鸟-Python 列表(List)
  11. Qt数据库集成应用封装
  12. html 超链接下载文件问题 如何修改文件名称
  13. 关于个人开发游戏的一些经验
  14. ora01033是什么错误linux,ora-01033错误的解决办法
  15. ps只能选择html格式,怎么找不到ps“选择主体”功能?
  16. python-for x in range (注意要点、细节)
  17. 华三交换机ping不通用户但用户_华三交换机ping不通路由器
  18. 回归分析beta值的标准_标准回归系数引用
  19. Navicat生成的.psc格式文件数据库导入
  20. Fast admin真垃圾 浪费我的时间不说还特么BUG一大堆

热门文章

  1. 数据结构课程上机参考代码
  2. Window对象中setInterval()和setTimeout()的区别
  3. synchronized底层原理_你用过synchronized吗?它的底层原理是什么?Java经典面试题来了...
  4. AngulerJS学习(五)按需动态载入文件
  5. Windows Server 2012关闭Server Manager开机自启动
  6. [iOS]开发者证书和描述文件的作用
  7. mysql中模糊查询的四种用法介绍
  8. java web学习项目20套源码完整版
  9. Swoft 2 Beta 发布,基于 Swoole 的云原生协程框架
  10. iOSSharing #9 | 2019-05-19