GWT的渐进式Web应用程序配方

渐进或不渐进…

如果您一段时间以来一直在设计或开发Web应用程序,那么您可能会遇到无数次“渐进式Web应用程序”一词,并且在未来几年内可能会这样做。 您可能想知道PWA的确切定义是什么,如何识别PWA,以及如何构建PWA。 根据字典,渐进一词是指可以改善或变得更好的东西,但是这与Web应用程序有什么关系呢? 我们真的不知道。 PWA似乎是Google引起人们兴趣的流行语,与PWA的真正含义并没有真正的关系。 Alex Russel将PWA定义为“摄取正确维生素的网站”。 为简单起见,让我们首先说说PWA是经过优化以适合其环境的Web应用程序:在移动设备或平板电脑上,它们可以扮演移动本机应用程序的角色,而在PC上,它们可以扮演普通Web应用程序的角色。

PWA的基本原理:

PWA是具有常规Web应用程序以及用于不同移动平台的捆绑应用程序的替代方法。 维护和升级所有这些程序可能会非常昂贵,尤其是在应用程序频繁更改的情况下。 对于PWA,只有一个适用于所有平台的应用程序,可通过浏览器中的链接进行访问。 PWA旨在使用“移动优先”方法进行设计。 可以安装它们,但它们也可以作为常规网站正常工作。 Google为PWA创建了一个专用网站 ,并介绍了从将其应用程序/网站转换为PWA的公司中受益的各种案例。

PWA的特征:

Google的开发倡导者Rob Dodson在他的演讲中 ,强调了Web应用程序的不同特征:
–反应灵敏:适应设备
–快速加载:优化以快速绘制或渲染 –脱机工作:使用服务人员来缓存内容,以允许脱机使用或慢速网络连接的应用程序 –可安装:可以在主屏幕中安装应用程序(例如本机应用程序) –参与度:通过推送通知使用户了解情况

现在我们知道了渐进式Web应用程序的外观,我们可以开始研究可以帮助我们使GWT应用程序渐进式的各种工具。

用于PWA的GWT食谱:

    • #1响应式:

为了使您的GWT应用程序具有响应能力,GWT用户有多种选择。 如果您具有设计技能,则可以使用自定义代码和CSS使应用程序具有响应能力。 否则,您可以依赖其他框架。 GWT的Bootstrap( https://github.com/gwtbootstrap3/gwtbootstrap3 )是我想到的第一件事。 它提供了著名的Twitter框架的所有组件。 另一种选择是GWTMaterialDesign( https://github.com/GwtMaterialDesign/gwt-material )。 它提供了具有响应性的材料设计,可以随时为您的应用程序使用元素。 最后, gwt-polymer-element是GWT的Polymer包装材料,它还提供了随时可用的响应式Web组件,并且可以在设计构建响应式应用程序时派上用场。 我们在之前的一篇文章中提供了Polymer的新手指南。

    • #2快速加载:

为了减少第一次油漆的时间,可以做很多事情。 首先,可以使用代码拆分来减少gwt模块文件的大小。 它基本上将模块分成多个片段,从而使GWT模块在启动时仅下载所需的片段。 其次,可以将PWA 准则指定的应用程序外壳方法应用于GWT应用程序。 这可以通过从应用程序Java代码中提取静态元素和数据并将其直接放入.html入口点来完成。 例如:

GWT用户的常见做法是将.html的正文留空,并以编程方式从应用程序添加其视图:

<body>     
</body>
//....AppMainView view = AppMainView();RootPanel.get().add(view);

尽管这种做法没有错,但是由于.js模块文件将包含更多指令,因此它可能会延长应用程序的加载时间,因此执行起来将花费更多时间。 作为解决方法,我们可以尝试识别视图中的所有静态元素并将其放入.html中,然后可以从入口点加载单个视图:

<div id="appShell"><img src="logo.png" alt="" />
<div id="menu"></div>
<div id="mainContent"></div>
//...
MenuView menu = new MenuMeview();
ContentView content = new ContentView();RootPanel.get("menu").add(menu);
RootPanel.get("mainContent").add(content);

为了说明的目的,这当然是简化的示例。 到目前为止,我们已经看到了代码拆分和应用程序外壳如何减少呈现应用程序的时间。 还有HTML5的async脚本属性,该属性并不是真正专用于GWT的。 例如:

<!-- Inside HEAD --!>
<script src="polymerstarter/polymerstarter.nocache.js" async="" type="text/javascript">

这将指示浏览器不要阻止解析,并在可用时尽快加载我们的应用脚本。

另一种选择是将应用程序脚本放入正文中。

    • #3离线工作:

这主要可以使用服务工作者来完成。 没有官方的GWT库可以与服务人员进行交互。 甚至gwt-polymer-elements都没有包装Platinum Elements,Platinum Elements是与浏览器的服务人员进行交互的Polymer元素。 GWT用户将不得不手动编写一些Javascript来实现应用程序资产的缓存机制。 JSNI或Jsinterop可用于与浏览器进行交互并调用服务工作者服务。 定义缓存事件的服务工作者脚本需要在单独的脚本上,因此,到目前为止,将服务工作者代码和GWT应用程序模块代码混合在同一.js文件中有点复杂。 GWT唯一可以完成的任务是注册服务工作者。 我们将在下一节的后面进行演示。 另请注意,并非所有浏览器都支持服务工作者,您可以在Mozilla的API文档页面中找到有关此服务的更多详细信息。

有关如何使用Service Worker缓存应用程序数据和资产的更多详细信息,Google提供了一些有用的指南。

    • #4无法安装:

此收据也不特定于GWT。 要使Web应用程序可安装,您需要添加一个名为app manifest的json文件,并将其链接到.html入口点:

<link rel="manifest" href="manifest.json">

有关如何编写清单文件的指南,您可以参考W3C的指南: https : //www.w3.org/TR/appmanifest/ 。 您也可以使用此在线工具: http : //brucelawson.github.io/manifest/ ,该工具可以为您生成清单,但是您的应用程序必须已经在线。 您可以使用标语要求用户安装该应用程序,也可以让他从浏览器的选项中手动进行操作。

    • #5参与:

再次没有GWT的官方推送通知库。 这可能是呼吁GWT社区填补这一空白。 在此之前,GWT用户可以使用JSNI或Jsinterop与浏览器进行交互并订阅推送通知。

演示应用

为了说明上述特征,我们使用gwt-polymer-elements和gwty-leaflet构建了一个地图应用程序。 该应用程序显示用户的收藏夹地图。

来源: https : //github.com/gwidgets/gwt-pwa-demo
直播: https : //gwt-pwa-demo.herokuapp.com/pwademo.html/

使用Polymer,我们的应用程序默认情况下是响应式的,因此此步骤已完成。

为了使应用程序快速加载,我们首先移除所有静态html,然后将其放入.html入口点文件: https : //github.com/gwidgets/gwt-pwa-demo/blob/master/src/main /webapp/pwademo.html

我们使用聚合物元素与dom元素进行交互。 例如:

PaperMenuLEement paperMenu = (PaperMenuElement) Polymer.getDocument().getElementById("paperMenu");paperMenu.select("paris");

我们还使应用脚本异步加载:

<script type="text/javascript" language="javascript" src="pwademo/pwademo.nocache.js" async></script>

并且我们引入了一些代码拆分方法,因为每个部分只有一个地图,因此我们只需要在加载页面时在显示的部分上加载地图。

loadStartupMap();//Maps are not loaded on start up, but only when iron selector selects a new map
ironPages.addEventListener("iron-select", e -> {if(ironPages.getSelected().equals("london") && !londonMapInitialized){//Some code splitting to reduce initial module sizeGWT.runAsync(new RunAsyncCallback(){@Overridepublic void onFailure(Throwable reason) {Document.get().getElementById("londonMap").setInnerHTML("Could not load this map, please try again later");}@Overridepublic void onSuccess() {Maps.initializeLondonMap();	}});londonMapInitialized = true;}
});

我们还添加了一个应用程序清单,以允许手动安装该应用程序

{"name": "Favorite Maps PWA","short_name": "Favorite Maps PWA","icons": [{"src": "image/mapicon.png","sizes": "144x144","type": "image/png"}],"start_url": "/pwademo.html","display": "standalone","background_color": "#3E4EB8","theme_color": "#2E3AA1"
}

最后,我们添加了JsInterop类来注册服务工作者。

if (Navigator.serviceWorker != null) {Navigator.serviceWorker.register("sw.js").then(new Function<JavaScriptObject, JavaScriptObject>() {@Overridepublic JavaScriptObject call(JavaScriptObject arg) {GWT.log("registred service worker successfully");return null;}});} else {GWT.log("service worker unavailable in this browser");}

我们创建了一个名为sw.js的服务工作者脚本,并将其添加到应用程序的资源中。

var cacheName = 'GWT-PWA';  
var filesToCache = [  '/gwt-pwa/pwademo.html',  '/gwt-pwa/pwademo.css',  '/gwt-pwa/styles/app-theme.html',  '/gwt-pwa/styles/shared-styles.html',  '/gwt-pwa/leaflet/leaflet.js',  '/gwt-pwa/leaflet/leaflet.css','/gwt-pwa/image/mapicon.png','/gwt-pwa/pwademo/pwademo.nocache.js'];self.addEventListener('install', function(e) {  console.log('[ServiceWorker] Install');  e.waitUntil(  caches.open(cacheName).then(function(cache) {  console.log('[ServiceWorker] Caching app shell');  return cache.addAll(filesToCache);  })  );  
});self.addEventListener('activate', function(e) {  console.log('[ServiceWorker] Activate');  e.waitUntil(  caches.keys().then(function(keyList) {  return Promise.all(keyList.map(function(key) {  console.log('[ServiceWorker] Removing old cache', key);  if (key !== cacheName) {  return caches.delete(key);  }  }));  })  );  });self.addEventListener('fetch', function(e) {  console.log('[ServiceWorker] Fetch', e.request.url);  e.respondWith(  caches.match(e.request).then(function(response) {  return response || fetch(e.request);  })  );  });

该脚本将安装并激活服务工作者。 它还允许服务工作者预订在每次请求资源时触发的获取事件。 然后,服务工作者根据其当前状态决定是使用本地缓存还是从网络中获取资源。

加载应用程序后,我们可以在Google chrome的缓存存储中找到我们的资产:

http://www.g-widgets.com/wp-content/uploads/2016/08/cacheChrome.png

如果我们在Google Chrome浏览器上禁用了网络并尝试运行该应用程序,则会得到类似的信息(由于未缓存地图,因此未渲染):

该应用程序即使没有网络也可以提供服务。 如果我们看一下Chrome开发者工具中的网络请求,就会注意到服务工作者正在提供应用程序资源:

由于这是一个演示应用程序,因此我们没有添加任何推送通知,因为它需要设置推送服务器。

我们已经从Android手机将应用程序安装到主屏幕,并且得到了类似以下内容:

结论

在Web开发领域,PWA仍然是新事物。 一些人预测他们将在未来几年接管本地应用程序。 我们知道,GWT开发人员一直在使用Phonegap将其Web应用程序转换为移动本机应用程序,也许有了PWA,他们将不再需要这样做。 我们已经在本教程中看到了如何使用诸如Polymer之类的库来使用GWT构建PWA。 到目前为止,还没有GWT库与浏览器服务工作者进行交互,因此GWT社区需要填补这一空白。

有趣的链接

Addy Osmani初学者指南: https : //addyosmani.com/blog/getting-started-with-progressive-web-apps/

2016年SpringIO关于PWA和Spring Boot的讨论: https : //www.youtube.com/watch?v= zAZQeQ0CRpQ

来自Web开发在线代理商https://skilled.co/的PWA用例的摘要信息图:


由Skilled.co提出

翻译自: https://www.javacodegeeks.com/2017/07/progressive-web-apps-recipes-gwt.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/349731.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

问题 1047: [编程入门]报数问题

题目描述有n人围成一圈&#xff0c;顺序排号。从第1个人开始报数&#xff08;从1到3报数&#xff09;&#xff0c;凡报到3的人退出圈子&#xff0c;问最后留下的是原来的第几号的那位。输入初始人数n输出最后一人的初始编号样例输入3 样例输出2分析&#xff1a;因为每次报3都会…

NOIP模拟测试17「入阵曲·将军令·星空」

入阵曲 题解 应用了一种美妙移项思想&#xff0c; 我们先考虑在一维上的做法 维护前缀和$(sum[r]-sum[l-1])\%k0$可以转化为 $sum[r]\% ksum[l-1]\%k$开个桶维护一下即可 然后拓展到二维上 把两行之间所有行拍扁看作一维上的区间&#xff0c; 我们枚举两行和行之间所有列开个桶…

理解sizeof

1、sizeof返回的是字节个数&#xff0c;内存编址的最小单元是字节。因此&#xff0c;空对象&#xff0c;bool值占用的内存也是一个字节。 2、可以对哪些东西求sizeof ? a、对象和类型。如int a; sizeof(a)&#xff0c; sizeof(int)&#xff0c;二者是等价的。同一类型的对象&a…

java 分析java死锁_Java死锁示例–如何分析死锁情况

java 分析java死锁死锁是两个线程或多个线程永远被阻塞的编程情况&#xff0c;这种情况发生在至少两个线程和两个或更多资源的情况下。 在这里&#xff0c;我编写了一个简单的程序&#xff0c;它将导致死锁情况&#xff0c;然后我们将看到如何对其进行分析。 Java死锁示例 pac…

insert 多条数据 并且具有唯一标识符

DECLARE COUNT INT DECLARE NAME NVARCHAR(10) SET COUNT0 WHILE COUNT<1000 BEGIN SET NAMESYSTEMCAST(COUNT AS NVARCHAR(10)) INSERT INTO dbo.users VALUES (NEWID(),NAME,123123COUNT,DATEADD(DAY,COUNT,2012-12-12),GETDATE()) SET COUNT COUNT 1 END 转载于:http…

Java Message System简介

Java消息系统 在本文中&#xff0c;我将讨论面向消息的中间件 &#xff08;MOM&#xff09;以及JMS如何在Enterprise Java中实现它。 另外&#xff0c;我将讨论适合JMS使用的典型用例以及用于讨论消息传递解决方案的不同术语&#xff0c;例如Publisher / Sender &#xff0c; D…

FLV协议5分钟入门浅析

FLV协议简介 FLV&#xff08;Flash Video&#xff09;是一种流媒体格式&#xff0c;因其体积小、协议相对简单&#xff0c;很快便流行开来&#xff0c;并得到广泛的支持。 常见的HTTP-FLV直播协议&#xff0c;就是使用HTTP流式传输通过FLV封装的音视频数据。对想要了解HTTP-FLV…

问题 1049: [编程入门]结构体之时间设计

题目描述定义一个结构体变量&#xff08;包括年、月、日&#xff09;。计算该日在本年中是第几天&#xff0c;注意闰年问题。输入年月日输出当年第几天样例输入2000 12 31 样例输出366 import java.util.Scanner; public class Main{ public static void main(String args[]){ …

反序列化 jackson_使用Jackson和Super类型令牌的Json反序列化

反序列化 jacksonDatatables是一个jquery插件&#xff0c;用于显示表格信息–它可以增强简单的表或可以使用基于AJAX的数据并以表格形式显示信息。 数据表要​​求来自服务器的数据遵循特定的JSON格式才能在屏幕上显示。 考虑要显示Member实体列表的情况&#xff0c;那么Membe…

NOIP模拟测试18「引子·可爱宝贝精灵·相互再归的鹅妈妈」

待补 引子 题解 大模拟&#xff0c;注意细节 代码1 #include<bits/stdc.h> using namespace std; int n,m;char a[1005][1005];bool vst[1005][1005]; void solve(int na,int nb) {int ina,jnb,now0;while(1){j;if(a[na][j])break;}while(1){i;if(a[i][nb])break;}for(in…

GC解释:收集器概述

当前版本的HotSpot JVM包括三种类型的垃圾收集器&#xff1a; –串行收集器 –并行收集器 –多数同时收集者 它们都是世代的&#xff0c;这意味着它们利用了堆的划分方式 。 垃圾收集器负责三个主要操作&#xff1a; –查找不再使用的对象 –释放这些对象之后的内存 –…

数据结构之排序五:选择排序

def selectedSort(myList):#获取list的长度length len(myList)#一共进行多少轮比较for i in range(0,length-1):#默认设置最小值得index为当前值smallest i#用当先最小index的值分别与后面的值进行比较,以便获取最小indexfor j in range(i1,length):#如果找到比当前值小的ind…

C#代码规范

1. 引言 本文是一套面向C# programmer 和C# developer 进行开发所应遵循的开发规范。 按照此规范来开发C#程序可带来以下益处&#xff1a; 代码的编写保持一致性&#xff0c; 提高代码的可读性和可维护性&#xff0c; 在团队开发一个项目的情况下&a…

谷歌guava_使用Google Guava进行馆藏创建和不变性

谷歌guava因此&#xff0c;我想看看番石榴提供的一些集合创建模式&#xff0c;以及它提供的某些不可变集合类型。 如果您没有看过我以前的文章&#xff0c;则可能要从这里开始&#xff1a; 番石榴第1部分– MultiMaps 番石榴第2部分– BiMaps 番石榴第3部分–多组 Guava的…

问题 1051: [编程入门]结构体之成绩统计2

题目描述有N个学生&#xff0c;每个学生的数据包括学号、姓名、3门课的成绩&#xff0c;从键盘输入N个学生的数据&#xff0c;要求打印出3门课的总平均成绩&#xff0c;以及最高分的学生的数据&#xff08;包括学号、姓名、3门课成绩&#xff09;输入学生数量N占一行每个学生的…

socket-02

# -- coding: utf-8 --_author__ "HuaQiang Yan" import socketdef handle_request(client):buf client.recv(1024) # 接收请求client.send(bytes("HTTP/1.1 200 OK\r\n\r\n", encodingutf-8))client.send(bytes("Hello World&#xff01;", …

jenkins复制作业_Jenkins分层作业和作业状态汇总

jenkins复制作业您可能知道&#xff0c;Jenkins是高度可配置的CI服务器。 我们可以设置不同的自定义构建过程。 我将分享一些我用来设置詹金斯工作层次的方法。 这是用例&#xff1a; 我们有一个主要的入口工作被调用以启动整个构建过程。 这项工作可以有一个到多个子工作。 …

xaml语言建立首个win8 Metro应用,rss阅读器

本实例是来源msdn的Metro开发文档&#xff0c;按着解说一步步来理解的&#xff0c;修改了一点点&#xff0c;拿了博客园本人的博客作为RSS阅读&#xff0c;本实例用来学习还是可以的 参考文档http://msdn.microsoft.com/zh-cn/library/windows/apps/br211380.aspx#Y909 先看允…

在扩展Spock时输出给定值

Spock是一个Java测试框架&#xff0c;由GradleWare的软件工程师Peter Niederwieser于2008年创建&#xff0c;它可以促进BDD的发展。 利用这个 例如 &#xff0c;一个故事可以定义为&#xff1a; Story: Returns go to stockAs a store owner In order to keep track of stock…

wsgi

wsgi&#xff0c;通用网关接口。相当于在app与web服务&#xff08;socket服务端&#xff09;之间建立统一连接的规范。 真实开发中的python web程序来说&#xff0c;一般会分为两部分&#xff1a;服务器程序和应用程序。服务器程序负责对socket服务器进行封装&#xff0c;并在请…