aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/web/progressive_web_apps
diff options
context:
space:
mode:
Diffstat (limited to 'files/zh-cn/web/progressive_web_apps')
-rw-r--r--files/zh-cn/web/progressive_web_apps/app_structure/index.html274
-rw-r--r--files/zh-cn/web/progressive_web_apps/index.html81
-rw-r--r--files/zh-cn/web/progressive_web_apps/installable_pwas/index.html124
-rw-r--r--files/zh-cn/web/progressive_web_apps/introduction/index.html90
-rw-r--r--files/zh-cn/web/progressive_web_apps/network_independent/index.html94
-rw-r--r--files/zh-cn/web/progressive_web_apps/offline_service_workers/index.html199
-rw-r--r--files/zh-cn/web/progressive_web_apps/re-engageable/index.html78
-rw-r--r--files/zh-cn/web/progressive_web_apps/re-engageable_notifications_push/index.html242
-rw-r--r--files/zh-cn/web/progressive_web_apps/responsive/index.html77
-rw-r--r--files/zh-cn/web/progressive_web_apps/responsive/responsive_design_building_blocks/index.html421
-rw-r--r--files/zh-cn/web/progressive_web_apps/优势/index.html57
-rw-r--r--files/zh-cn/web/progressive_web_apps/加载/index.html152
-rw-r--r--files/zh-cn/web/progressive_web_apps/添加到主屏幕/index.html218
13 files changed, 2107 insertions, 0 deletions
diff --git a/files/zh-cn/web/progressive_web_apps/app_structure/index.html b/files/zh-cn/web/progressive_web_apps/app_structure/index.html
new file mode 100644
index 0000000000..2ffb7591e0
--- /dev/null
+++ b/files/zh-cn/web/progressive_web_apps/app_structure/index.html
@@ -0,0 +1,274 @@
+---
+title: PWA 结构
+slug: Web/Progressive_web_apps/App_structure
+translation_of: Web/Progressive_web_apps/App_structure
+---
+<div>{{PreviousMenuNext("Web/Apps/Progressive/Introduction", "Web/Progressive_web_apps/Offline_Service_workers", "Web/Progressive_web_apps")}}</div>
+
+<p class="summary">现在,我们已经知道了 PWAs 背后的原理, 让我们来看一个推荐的 PWA 结构,这来自一个真实的应用 。我们从分析 <a href="https://mdn.github.io/pwa-examples/js13kpwa/">js13kPWA</a> 这个应用开始:为什么它要这样构建?这样做有什么好处?</p>
+
+<h2 id="应用架构">应用架构</h2>
+
+<p>渲染网站主要有两种方法 - 在服务器上或在客户端上。它们都有其优点和缺点,你可以适当地混合使用这两种方法</p>
+
+<ul>
+ <li>服务器端渲染(SSR)意思是在服务器上渲染网页,因此首次加载会更快,但是在不同页面之间导航都需要下载新的HTML内容。 它在浏览器中运行良好,但它受到加载速度的制约,因而带来可以感知的性能延迟——加载一个页面就需要和服务器之间一次往返。(原文:but it suffers in terms of loading speed and therefore general perceived performance — loading every single page requires a new round trip to the server.)</li>
+ <li>客户端渲染(CSR)允许在导航到不同页面时几乎立即在浏览器中更新网站,但在开始时需要更多的初始下载和客户端上的额外渲染。 首次访问时网站速度较慢,但后续访问速度要快得多。</li>
+</ul>
+
+<p>将SSR与CSR混合可以获得最佳结果 - 您可以在服务器上渲染网站,缓存其内容,然后在客户端需要时更新渲染。 由于SSR,第一页加载很快,并且页面之间的导航是平滑的,因为客户端可以仅使用已更改的部分重新渲染页面。</p>
+
+<p>PWA可以使用您喜欢的任何方法构建,但有些方法会更适合。最流行的方法是“app shell”概念,它完全按照上述方式混合SSR和CSR,此外还遵循“离线优先”方法,我们将在后续文章中详细解释并在我们的示例应用程序中使用。还有一种涉及<a href="/en-US/docs/Web/API/Streams_API">Streams API</a>的新方法,我们将简要提及。</p>
+
+<h2 id="App_shell(可以理解为程序的外壳)">App shell(可以理解为程序的外壳)</h2>
+
+<p>App shell意图尽快加载最小的用户界面,然后缓存它,以便在后续访问时可以离线使用,然后加载应用程序的所有内容。这样,下次有人从设备访问应用程序时,UI立即从缓存加载,并从服务器请求新内容(如果它已在缓存中不可用)。</p>
+
+<p>这种结构很快,并且当用户立即看到内容而不是加载动画或空白页时也感觉很快。如果网络连接不可用,它还允许离线访问网站。</p>
+
+<p>我们可以通过<a href="/en-US/docs/Web/API/Service_Worker_API">service worker</a>控制从服务器请求的内容以及从缓存中检索的内容,这将在下一篇文章中详细解释 - 现在让我们关注结构本身。</p>
+
+<h3 id="我为什么要用它?">我为什么要用它?</h3>
+
+<p><span class="tlid-translation translation" lang="zh-CN"><span class="alt-edited">这种架构允许网站从所有 PWA 功能中获益最多 - 它可以缓存应用</span><span title=""> app shell</span><span class="alt-edited"> 并以极大地提高性能的方式管理动态内容。 除了基本 shell 之外,您还可以添加其他功能,例如</span></span><a href="/en-US/docs/Web/Apps/Progressive/Add_to_home_screen">添加到主屏幕</a><span class="tlid-translation translation" lang="zh-CN"><span class="alt-edited">或</span></span><a href="/en-US/docs/Web/API/Push_API">推送通知</a><span class="tlid-translation translation" lang="zh-CN"><span class="alt-edited">,你可以放心如果用户的浏览器不支持应用程序仍然可以正常工作 - 这是渐进增强的美妙之处。</span></span></p>
+
+<p><span class="tlid-translation translation" lang="zh-CN"><span title="">该网站感觉就像一个原生应用,具有即时交互和可靠的性能,同时保留了网络的所有好处。</span></span></p>
+
+<h3 id="可链接、渐进式和响应式">可链接、渐进式和响应式</h3>
+
+<p><span class="tlid-translation translation" lang="zh-CN"><span title="">记住 PWA 的优点并在设计应用程序时牢记这一点非常重要。</span> <span title="">app shell 方法允许网站:</span></span></p>
+
+<ul>
+ <li>可链接:即使它的行为类似于原生应用,它仍然是一个网站 - 您可以单击页面中的链接和当您想共享它时向某人发送URL。</li>
+ <li>渐进式:从“好用的旧的基本网站”开始,逐步添加新功能,同时记住检测它们是否在浏览器中可用,并优雅地处理在没有支持的情况下出现的任何错误。例如,离线模式下 service workers 的帮助只是一个额外的特性使网站体验更好,但没有它网站应仍然是完全可用的。</li>
+ <li>响应式:响应式网页设计也适用于渐进式网络应用程序,因为它们主要用于移动设备。 有许多不同的设备与浏览器 - 使您的网站适用于不同的屏幕尺寸,视口或像素密度,使用 <a href="/en-US/docs/Mozilla/Mobile/Viewport_meta_tag">viewport meta tag</a>,<a href="/en-US/docs/Web/CSS/Media_Queries/Using_media_queries">CSS media queries</a>,<a href="/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout">Flexbox</a>,和 <a href="/en-US/docs/Web/CSS/CSS_Grid_Layout">CSS Grid</a> 等技术是很重要的。</li>
+</ul>
+
+<h2 id="不同的概念:流">不同的概念:流</h2>
+
+<p>使用 <a href="/zh-CN/docs/Web/API/Streams_API">Streams API</a> 可以实现完全不同的服务器端或客户端渲染方法。在service worker帮助下,流可以极大改进我们解析内容的方式。 </p>
+
+<p>应用程序外壳模型要求在网站开始呈现之前可以使用所有资源。 它与HTML不同,因为浏览器实际上已经流式传输数据,您可以看到元素在网站上加载和呈现的时间。 但是,为了使JavaScript“可操作”,必须完整地下载它。</p>
+
+<p>Streams API允许开发人员直接访问来自服务器的数据流 - 如果您想对数据执行操作(例如,向视频添加过滤器),则不再需要等待所有数据流 下载并转换为blob(或其他) - 您可以立即开始。 它提供细粒度控制 - 流可以启动,与另一个流链接,取消,检查错误等等。</p>
+
+<p>从理论上讲,流媒体是一种更好的模型,但它也更复杂,在撰写本文时(2018年3月),Streams API仍然是一项正在进行的工作,并且尚未在任何主流浏览器中完全可用。 当它可用时,它将是提供内容的最快方式 - 在性能方面的好处将是巨大的。</p>
+
+<p>有关工作示例和更多信息,请参阅 <a href="/zh-CN/docs/Web/API/Streams_API">Streams API documentation</a>.</p>
+
+<h2 id="示例应用的结构">示例应用的结构</h2>
+
+<p><a href="https://mdn.github.io/pwa-examples/js13kpwa/">js13kPWA</a> 这个网站的结构比较简单: 他包含一个HTML页面(index.heml),一个CSS样式表(style.css),一些图片,js脚本和字体,它的文件结构如下所示:</p>
+
+<p><img alt="Folder structure of js13kPWA." src="https://mdn.mozillademos.org/files/15925/js13kpwa-directory.png" style="border-style: solid; border-width: 1px; display: block; height: 356px; margin: 0px auto; width: 320px;"></p>
+
+<h3 id="HTML页面">HTML页面</h3>
+
+<p>从HTML页面的角度,有一个id为content的标签,这个标签外部的所有内容,都是app shell(程序外壳)</p>
+
+<pre class="brush: html">&lt;!DOCTYPE html&gt;
+&lt;html lang="en"&gt;
+&lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;title&gt;js13kGames A-Frame entries&lt;/title&gt;
+ &lt;meta name="description" content="A list of A-Frame entries submitted to the js13kGames 2017 competition, used as an example for the MDN articles about Progressive Web Apps."&gt;
+ &lt;meta name="author" content="end3r"&gt;
+ &lt;meta name="theme-color" content="#B12A34"&gt;
+ &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
+ &lt;meta property="og:image" content="icons/icon-512.png"&gt;
+ &lt;link rel="shortcut icon" href="favicon.ico"&gt;
+ &lt;link rel="stylesheet" href="style.css"&gt;
+ &lt;link rel="manifest" href="js13kpwa.webmanifest"&gt;
+ &lt;script src="data/games.js" defer&gt;&lt;/script&gt;
+ &lt;script src="app.js" defer&gt;&lt;/script&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;header&gt;
+ &lt;p&gt;&lt;a class="logo" href="http://js13kgames.com"&gt;&lt;img src="img/js13kgames.png" alt="js13kGames"&gt;&lt;/a&gt;&lt;/p&gt;
+&lt;/header&gt;
+&lt;main&gt;
+ &lt;h1&gt;js13kGames A-Frame entries&lt;/h1&gt;
+ &lt;p class="description"&gt;List of games submitted to the &lt;a href="http://js13kgames.com/aframe"&gt;A-Frame category&lt;/a&gt; in the &lt;a href="http://2017.js13kgames.com"&gt;js13kGames 2017&lt;/a&gt; competition. You can &lt;a href="https://github.com/mdn/pwa-examples/blob/master/js13kpwa"&gt;fork js13kPWA on GitHub&lt;/a&gt; to check its source code.&lt;/p&gt;
+ &lt;button id="notifications"&gt;Request dummy notifications&lt;/button&gt;
+ &lt;section id="content"&gt;
+ // Content inserted in here
+ &lt;/section&gt;
+&lt;/main&gt;
+&lt;footer&gt;
+ &lt;p&gt;© js13kGames 2012-2018, created and maintained by &lt;a href="http://end3r.com"&gt;Andrzej Mazur&lt;/a&gt; from &lt;a href="http://enclavegames.com"&gt;Enclave Games&lt;/a&gt;.&lt;/p&gt;
+&lt;/footer&gt;
+&lt;/body&gt;
+&lt;/html&gt;</pre>
+
+<p>head标签包含了一些基本的内容,例如标题,网页描述,CSS引用,web manifest文件,game.js和app.js,整个程序的初始化工作,会在app.js中完成。body标签中包括header,main和footer三个部分,header中包含了一张图片,main标签包含了网页标题、描述和一个区域,用来放置网页的主体,我们称之为content,footer内部则放置作者信息和链接(原文:copy and links)</p>
+
+<p>这个应用的唯一工作就是列出js13kGames 2017年比赛中的A-Frame(一个用来构建虚拟现实(VR)应用的网页开发框架,译者注) 项目列表。如你所见,这是一个很普通的单页应用,目的是用一个简单的东西来展示PWA的真实功能。</p>
+
+<h3 id="CSS部分">CSS部分</h3>
+
+<p>CSS部分也是尽可能的简单:运用@font-face来加载和使用自定义字体以及给HTML元素提供简单的样式,总体的目标是让页面在移动端和桌面设备上运行(通过使用响应式布局)</p>
+
+<h3 id="主要的JavaScript代码">主要的JavaScript代码</h3>
+
+<p>app.js所做的一些工作我们会在下一篇文章中详细分析,首先它用下面的模板生成了content中的内容</p>
+
+<pre class="brush: js">var template = "&lt;article&gt;\n\
+ &lt;img src='data/img/SLUG.jpg' alt='NAME'&gt;\n\
+ &lt;h3&gt;#POS. NAME&lt;/h3&gt;\n\
+ &lt;ul&gt;\n\
+ &lt;li&gt;&lt;span&gt;Author:&lt;/span&gt; &lt;strong&gt;AUTHOR&lt;/strong&gt;&lt;/li&gt;\n\
+ &lt;li&gt;&lt;span&gt;Twitter:&lt;/span&gt; &lt;a href='https://twitter.com/TWITTER'&gt;@TWITTER&lt;/a&gt;&lt;/li&gt;\n\
+ &lt;li&gt;&lt;span&gt;Website:&lt;/span&gt; &lt;a href='http://WEBSITE/'&gt;WEBSITE&lt;/a&gt;&lt;/li&gt;\n\
+ &lt;li&gt;&lt;span&gt;GitHub:&lt;/span&gt; &lt;a href='https://GITHUB'&gt;GITHUB&lt;/a&gt;&lt;/li&gt;\n\
+ &lt;li&gt;&lt;span&gt;More:&lt;/span&gt; &lt;a href='http://js13kgames.com/entries/SLUG'&gt;js13kgames.com/entries/SLUG&lt;/a&gt;&lt;/li&gt;\n\
+ &lt;/ul&gt;\n\
+&lt;/article&gt;";
+var content = '';
+for(var i=0; i&lt;games.length; i++) {
+ var entry = template.replace(/POS/g,(i+1))
+ .replace(/SLUG/g,games[i].slug)
+ .replace(/NAME/g,games[i].name)
+ .replace(/AUTHOR/g,games[i].author)
+ .replace(/TWITTER/g,games[i].twitter)
+ .replace(/WEBSITE/g,games[i].website)
+ .replace(/GITHUB/g,games[i].github);
+ entry = entry.replace('&lt;a href=\'http:///\'&gt;&lt;/a&gt;','-');
+ content += entry;
+};
+document.getElementById('content').innerHTML = content;</pre>
+
+<p>接着,它注册了一个service worker:</p>
+
+<pre class="brush: js">if('serviceWorker' in navigator) {
+ navigator.serviceWorker.register('/pwa-examples/js13kpwa/sw.js');
+};</pre>
+
+<p>下面这部分代码实现了一个功能:点击按钮时请求用户授权,用来向用户推送通知</p>
+
+<pre class="brush: js">var button = document.getElementById("notifications");
+button.addEventListener('click', function(e) {
+ Notification.requestPermission().then(function(result) {
+ if(result === 'granted') {
+ randomNotification();
+ }
+ });
+});</pre>
+
+<p>最后这部分是创建通知的代码,它会随机展示游戏列表中的一个项目</p>
+
+<pre class="brush: js">function randomNotification() {
+ var randomItem = Math.floor(Math.random()*games.length);
+ var notifTitle = games[randomItem].name;
+ var notifBody = 'Created by '+games[randomItem].author+'.';
+ var notifImg = 'data/img/'+games[randomItem].slug+'.jpg';
+ var options = {
+ body: notifBody,
+ icon: notifImg
+ }
+ var notif = new Notification(notifTitle, options);
+ setTimeout(randomNotification, 30000);
+}</pre>
+
+<h3 id="service_worker">service worker</h3>
+
+<p>最后我们来快速浏览一下 service worker相关的文件sw.js,他首先映入game.js这个文件file:</p>
+
+<pre class="brush: js">self.importScripts('data/games.js');</pre>
+
+<p>接着,程序会对app shell和主体内容(content)里面的数据创建一个缓存列表</p>
+
+<pre class="brush: js">var cacheName = 'js13kPWA-v1';
+var appShellFiles = [
+ '/pwa-examples/js13kpwa/',
+ '/pwa-examples/js13kpwa/index.html',
+ '/pwa-examples/js13kpwa/app.js',
+ '/pwa-examples/js13kpwa/style.css',
+ '/pwa-examples/js13kpwa/fonts/graduate.eot',
+ '/pwa-examples/js13kpwa/fonts/graduate.ttf',
+ '/pwa-examples/js13kpwa/fonts/graduate.woff',
+ '/pwa-examples/js13kpwa/favicon.ico',
+ '/pwa-examples/js13kpwa/img/js13kgames.png',
+ '/pwa-examples/js13kpwa/img/bg.png',
+ '/pwa-examples/js13kpwa/icons/icon-32.png',
+ '/pwa-examples/js13kpwa/icons/icon-64.png',
+ '/pwa-examples/js13kpwa/icons/icon-96.png',
+ '/pwa-examples/js13kpwa/icons/icon-128.png',
+ '/pwa-examples/js13kpwa/icons/icon-168.png',
+ '/pwa-examples/js13kpwa/icons/icon-192.png',
+ '/pwa-examples/js13kpwa/icons/icon-256.png',
+ '/pwa-examples/js13kpwa/icons/icon-512.png'
+];
+var gamesImages = [];
+for(var i=0; i&lt;games.length; i++) {
+ gamesImages.push('data/img/'+games[i].slug+'.jpg');
+}
+var contentToCache = appShellFiles.concat(gamesImages);</pre>
+
+<p>下面的代码用来配置service worker,缓存上述列表的工作就在这里发生</p>
+
+<pre class="brush: js">self.addEventListener('install', function(e) {
+ console.log('[Service Worker] Install');
+ e.waitUntil(
+ caches.open(cacheName).then(function(cache) {
+ console.log('[Service Worker] Caching all: app shell and content');
+ return cache.addAll(contentToCache);
+ })
+ );
+});</pre>
+
+<p>最后,如果条件允许,service worker将从缓存中请求content中所需的数据,从而提供离线应用功能</p>
+
+<pre class="brush: js">self.addEventListener('fetch', function(e) {
+ e.respondWith(
+ caches.match(e.request).then(function(r) {
+ console.log('[Service Worker] Fetching resource: '+e.request.url);
+ return r || fetch(e.request).then(function(response) {
+ return caches.open(cacheName).then(function(cache) {
+ console.log('[Service Worker] Caching new resource: '+e.request.url);
+ cache.put(e.request, response.clone());
+ return response;
+ });
+ });
+ })
+ );
+});</pre>
+
+<h3 id="JavaScript_数据">JavaScript 数据</h3>
+
+<p>项目中所用的游戏数据放置在data文件夹下面,以JavaScript对象的形式提供(<a href="https://github.com/mdn/pwa-examples/blob/master/js13kpwa/data/games.js">games.js</a>):</p>
+
+<pre class="brush: js">var games = [
+ {
+ slug: 'lost-in-cyberspace',
+ name: 'Lost in Cyberspace',
+ author: 'Zosia and Bartek',
+ twitter: 'bartaz',
+ website: '',
+ github: 'github.com/bartaz/lost-in-cyberspace'
+ },
+ {
+ slug: 'vernissage',
+ name: 'Vernissage',
+ author: 'Platane',
+ twitter: 'platane_',
+ website: 'github.com/Platane',
+ github: 'github.com/Platane/js13k-2017'
+ },
+// ...
+ {
+ slug: 'emma-3d',
+ name: 'Emma-3D',
+ author: 'Prateek Roushan',
+ twitter: '',
+ website: '',
+ github: 'github.com/coderprateek/Emma-3D'
+ }
+];</pre>
+
+<p>每一个入口在data/img文件夹下面都有属于它自己的图片。这些就是我们的内容数据,我们通过js将这些数据加载到主体内容中</p>
+
+<h2 id="下一步">下一步</h2>
+
+<p>下一篇文章我们会探讨更多的细节,关于service worker.如何帮助我们缓存app shell 和内容,从而让我们实现离线功能</p>
+
+<p>{{PreviousMenuNext("Web/Apps/Progressive/Introduction", "Web/Apps/Progressive/Offline_Service_workers", "Web/Apps/Progressive")}}</p>
diff --git a/files/zh-cn/web/progressive_web_apps/index.html b/files/zh-cn/web/progressive_web_apps/index.html
new file mode 100644
index 0000000000..0b020d08ad
--- /dev/null
+++ b/files/zh-cn/web/progressive_web_apps/index.html
@@ -0,0 +1,81 @@
+---
+title: 渐进式 Web 应用(PWA)
+slug: Web/Progressive_web_apps
+tags:
+ - Apps
+ - Modern web apps
+ - PWA
+ - Progressive web apps
+ - web app manifest
+ - 渐进式增强
+ - 渐进式增强的Web应用程序
+translation_of: Web/Progressive_web_apps
+---
+<p class="summary">PWA(Progressive web apps,渐进式 Web 应用)运用现代的 Web API 以及传统的渐进式增强策略来创建跨平台 Web 应用程序。这些应用无处不在、功能丰富,使其具有与原生应用相同的用户体验优势。 这组文档和指南告诉您有关 PWA 的所有信息。</p>
+
+<h2 id="PWA_的优势">PWA 的优势</h2>
+
+<p>PWA 是可被发现、易安装、可链接、独立于网络、渐进式、可重用、响应性和安全的。关于这些含义的细节,请参阅 <a href="/zh-CN/docs/Web/Apps/Progressive/Advantages">PWA的优势</a>。关于如何实施PWA,请参阅下面列出的指南。</p>
+
+<h2 id="核心_PWA_指南">核心 PWA 指南</h2>
+
+<p>以下指南通过简单的示例和工作原理,展示了实施PWA需要做什么。</p>
+
+<ol>
+ <li><a href="/zh-CN/docs/Web/Apps/Progressive/Introduction">PWA 介绍</a></li>
+ <li><a href="/zh-CN/docs/Web/Apps/Progressive/App_structure">PWA 结构</a></li>
+ <li><a href="/zh-CN/docs/Web/Progressive_web_apps/Offline_Service_workers">通过 Service workers 让 PWA 离线工作</a></li>
+ <li><a href="/zh-CN/docs/Web/Progressive_web_apps/Installable_PWAs">让 PWA 易于安装</a></li>
+ <li><a href="/zh-CN/docs/Web/Progressive_web_apps/Re-engageable_Notifications_Push">通过通知推送让 PWA 可重用</a></li>
+ <li><a href="/zh-CN/docs/Web/Progressive_web_apps/Loading">渐进式加载</a></li>
+</ol>
+
+<div class="column-container">
+<div class="column-half">
+<h2 id="技术指南">技术指南</h2>
+
+<ul>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage">用户端存储</a> — 关于如何使用 Web 存储、IndexedDB 和 Service workers 的指南。</li>
+ <li><a href="/zh-CN/docs/Web/API/Service_Worker_API/Using_Service_Workers">使用 Service workers</a> — 关于 Service Worker API 的深入指南。</li>
+ <li><a href="/zh-CN/docs/Web/API/IndexedDB_API/Using_IndexedDB">使用 IndexedDB</a> — 有关 IndexedDB 的知识与细节。</li>
+ <li><a href="/zh-CN/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API">使用 Web 存储 API</a> — Web 存储 API 简化了工作。</li>
+ <li><a class="external external-icon" href="https://developers.google.com/web/updates/2015/11/app-shell" rel="noopener">使用应用 Shell 架构的快速加载的 Web 应用</a> — 使用 App Shell 编码模式创建快速加载的应用的指南。</li>
+ <li><a href="/zh-CN/docs/Web/API/Push_API/Using_the_Push_API">使用推送 API</a> — 了解 Web Push API 背后的知识。</li>
+ <li><a href="/zh-CN/docs/Web/API/Notifications_API/Using_the_Notifications_API">使用通知 API</a> — 换言之,Web 通知。</li>
+ <li><a href="/zh-CN/docs/Web/Apps/Modern/Responsive/responsive_design_building_blocks">响应式设计的构建模块</a> — 学习响应式设计的基础知识,也是现代应用布局的基本话题。</li>
+ <li><a href="/zh-CN/docs/Web/Apps/Modern/Responsive/Mobile_first">移动优先</a> — 在创建响应式应用程序布局时,通常将移动布局创建为默认并在顶部构建更宽的布局。</li>
+ <li><a href="/zh-CN/docs/Web/Progressive_web_apps/Add_to_home_screen">“添加到主屏幕”指南</a> — 了解如何利用添加到主屏幕(A2HS)。</li>
+</ul>
+</div>
+
+<div class="column-half">
+<h2 id="工具">工具</h2>
+
+<ul>
+ <li><a class="external external-icon" href="https://localforage.github.io/localForage/" rel="noopener">localForage</a> — a nice simple JavaScript library for making client-side data storage really simple; it uses IndexedDB by default, and falls back to Web SQL/Web Storage if necessary.</li>
+ <li><a class="external external-icon" href="https://github.com/fxos-components/serviceworkerware" rel="noopener">ServiceWorkerWare</a> — an <em>Express-like</em> microframework for easy Service Worker development.</li>
+ <li><a class="external external-icon" href="https://github.com/mozilla/oghliner" rel="noopener">oghliner</a> — not only a template but a tool for deploying Offline Web Apps to GitHub Pages.</li>
+ <li><a class="external external-icon" href="https://github.com/GoogleChrome/sw-precache" rel="noopener">sw-precache</a> — a node module to generate service worker code that will precache specific resources.</li>
+ <li><a class="external external-icon" href="https://github.com/GoogleChrome/workbox" rel="noopener">workbox</a> — spiritual successor to sw-precache with more advanced caching strategies and easy precaching.</li>
+ <li><a class="external external-icon" href="https://www.talater.com/upup/" rel="noopener">upup</a> — a tiny script that makes sure your site is always there for your users.</li>
+ <li><a class="external external-icon" href="https://serviceworke.rs/" rel="noopener">The service worker cookbook</a> — A series of excellent service worker/push recipes, showing how to implement an offline app, but also much more.</li>
+</ul>
+</div>
+</div>
+
+<h2 id="相关链接">相关链接</h2>
+
+<ul>
+ <li>Google Developers 上的<a href="https://developers.google.com/web/progressive-web-apps">Progressive web apps</a></li>
+ <li><a href="https://medium.com/@slightlylate/progressive-apps-escaping-tabs-without-losing-our-soul-3b93a8561955#.6czgj0myh">Progressive Web Apps: Escaping Tabs Without Losing Our Soul</a> by Alex Russell</li>
+ <li><a href="https://developers.google.com/web/progressive-web-apps/checklist">Progressive Web Apps 清单</a></li>
+ <li>来自 Google 的<a href="https://developers.google.com/web/tools/lighthouse">Lighthouse Tool</a></li>
+ <li><a href="https://github.com/angular/mobile-toolkit">Tools for building progressive web apps with Angular</a></li>
+ <li><a href="https://github.com/codebusking/react-pwa-guide-kit">React PWA Guide Kit</a></li>
+ <li><a href="https://www.pokedex.org/">Offline-capable Pokédex web site</a></li>
+ <li>
+ <p><a href="https://hnpwa.com/">Hacker News readers as Progressive Web Apps</a></p>
+ </li>
+</ul>
+
+<div>{{QuickLinksWithSubpages("/zh-CN/docs/Web/Apps/Progressive/")}}</div>
diff --git a/files/zh-cn/web/progressive_web_apps/installable_pwas/index.html b/files/zh-cn/web/progressive_web_apps/installable_pwas/index.html
new file mode 100644
index 0000000000..87c70deba5
--- /dev/null
+++ b/files/zh-cn/web/progressive_web_apps/installable_pwas/index.html
@@ -0,0 +1,124 @@
+---
+title: 让 PWA 易于安装
+slug: Web/Progressive_web_apps/Installable_PWAs
+tags:
+ - PWAs
+ - a2hs
+ - js13kGames
+ - 可安装
+ - 添加至主屏幕
+ - 渐进式应用
+translation_of: Web/Progressive_web_apps/Installable_PWAs
+---
+<div>{{PreviousMenuNext("Web/Apps/Progressive/Offline_Service_workers", "Web/Apps/Progressive/Re-engageable_Notifications_Push", "Web/Apps/Progressive")}}</div>
+
+<p class="summary">在上一篇文章,我们了解了如何通过<a href="/en-US/docs/Web/API/Service_Worker_API">service worker</a>让<a href="https://mdn.github.io/pwa-examples/js13kpwa/">js13kPWA</a> 离线工作,我们可以更进一步,让用户如同本地应用一样在支持的移动浏览器上安装web应用。这篇文章讲述如何通过网页清单和添加到主屏特性来做到这一点。</p>
+
+<p>这些技术允许应用从设备主屏直接启动,而不是启动浏览器键入URL。你的web应用可以作为一等公民和本地应用肩并肩。这样更容易访问,而且你可以指定应用全屏运行,没有浏览器界面,这样看起来更像一个本地应用。</p>
+
+<h2 id="要求">要求</h2>
+
+<p>为了成为可安装网站,需要下列事情就位:</p>
+
+<ul>
+ <li>一份网页清单,填好 <a href="/en-US/Apps/Progressive/Add_to_home_screen#Manifest">正确的字段</a></li>
+ <li>网站的域必须是安全(HTTPS)的</li>
+ <li>一个本设备上代表应用的图标</li>
+ <li>一个注册好的service worker,可以让应用离线工作(这仅对于安卓设备上的Chrome浏览器是必需的)</li>
+</ul>
+
+<h3 id="清单文件">清单文件</h3>
+
+<p>关键元素是一份网页清单,它通过JSON形式列举了网站的所有信息。</p>
+
+<p>它通常位于网页应用的根目录。包含一些有用的信息,比如应用的标题,在一个移动OS上显示的代表该应用的不同大小的图标(例如,主屏图标)的路径,和用于加载或启动画面的背景颜色。这些信息是浏览器在安装web应用时和在主屏上显示应用需要的。</p>
+
+<p><a href="https://mdn.github.io/pwa-examples/js13kpwa/">js13kPWA</a> web应用的<code>js13kpwa.webmanifest</code>文件包含在<code>index.html</code>文件的{{htmlelement("head")}}段,如下行所示:</p>
+
+<pre class="brush: html notranslate">&lt;link rel="manifest" href="js13kpwa.webmanifest"&gt;</pre>
+
+<div class="note">
+<p><strong>注意:过去有一些常用的扩展名用于清单:</strong><code>manifest.webapp</code> 在Firefox OS应用清单中很流行,许多人使用<code>manifest.json</code>作为网页清单因为内容是JSON格式的。但是,<code>.webmanifest</code> 扩展名是在<a href="https://w3c.github.io/manifest/">W3C清单规范 </a>中显示指定的,所有应该坚持这种做法。</p>
+</div>
+
+<p>文件的内容是这个样子的:</p>
+
+<pre class="brush: json notranslate">{
+ "name": "js13kGames Progressive Web App",
+ "short_name": "js13kPWA",
+ "description": "Progressive Web App that lists games submitted to the A-Frame category in the js13kGames 2017 competition.",
+ "icons": [
+ {
+ "src": "icons/icon-32.png",
+ "sizes": "32x32",
+ "type": "image/png"
+ },
+ // ...
+ {
+ "src": "icons/icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }
+ ],
+ "start_url": "/pwa-examples/js13kpwa/index.html",
+ "display": "fullscreen",
+ "theme_color": "#B12A34",
+ "background_color": "#B12A34"
+}</pre>
+
+<p>大部分字段无需解释,但是让我们分解一下文档并且详细解释这些字段:</p>
+
+<ul>
+ <li><code>name</code>: 网站应用的全名。</li>
+ <li><code>short_name</code>: 显示在主屏上的短名。</li>
+ <li><code>description</code>: 一两句话解释你的应用的用途。</li>
+ <li><code>icons</code>: 一串图标信息 — 源URL,大小和类型。确保包含一些图标,这样有一个最合适用户设备的图标可以被选中。</li>
+ <li><code>start_url</code>: 启动应用的index文档。</li>
+ <li><code>display</code>: 应用的显示方式;可以是全屏,独立,最小UI或者浏览器。</li>
+ <li><code>theme_color</code>: UI的主颜色,这是操作系统使用的。</li>
+ <li><code>background_color</code>: 背景色,用于安装程序时和显示启动画面时。</li>
+</ul>
+
+<p>一份网页清单最少需要<code>name</code>和一个图标 (带有 <code>src</code>, <code>size</code> 和 <code>type</code>)。<code>description</code>, <code>short_name</code>, 和<code>start_url</code>最好要提供。还有在上述列表之外的字段你可以使用 — 请查看 <a href="/en-US/docs/Web/Manifest">网页应用清单参考</a>获得详细情况。</p>
+
+<h2 id="添加到主屏">添加到主屏</h2>
+
+<p>"添加到主屏" (或者英语短语a2hs) 是移动浏览器实现的一个特性,它利用网页清单中的信息来在设备主屏上显示应用图标和文字。应用必须满足上述必备条件才可以正常工作。</p>
+
+<p>当用户使用一个支持的移动浏览器访问一个PWA时,他应该显示一个横幅信息表示可以安装这个应用。</p>
+
+<p><img alt="Add to Home screen popup of js13kPWA." src="https://mdn.mozillademos.org/files/15928/js13kpwa-icon.png" style="border-style: solid; border-width: 1px; display: block; height: 640px; margin: 0px auto; width: 360px;"></p>
+
+<p>在用户单击这个横幅后,安装横幅信息会显示,它是浏览器基于网页清单信息创建的,名字和图标可以在提示中看到。</p>
+
+<p><img alt="Install banner of js13kPWA." src="https://mdn.mozillademos.org/files/15927/js13kpwa-banner.png" style="border-style: solid; border-width: 1px; display: block; height: 640px; margin: 0px auto; width: 360px;"></p>
+
+<p>如果用户单击安装到主屏按钮,会显示这样应用的图标的样子,让用户确认是否安装这个应用。</p>
+
+<p><img alt="Add to Home screen popup of js13kPWA." src="https://mdn.mozillademos.org/files/15926/js13kpwa-add.png" style="border-style: solid; border-width: 1px; display: block; height: 640px; margin: 0px auto; width: 360px;"></p>
+
+<p>确认之后,应用就安装到主屏上了。</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/15834/js13kpwa-installed.png" style="border-style: solid; border-width: 1px; display: block; margin: 0px auto;"></p>
+
+<p>之后,用户可以立刻启动并使用应用。注意,PWA有时(取决于浏览器和移动操作系统)会在图标右下角显示一个浏览器图片用于告诉用户这是一个网页应用。</p>
+
+<h3 id="启动画面">启动画面</h3>
+
+<p>在一些浏览器中,可以通过清单信息产生一个启动画面,当PWA启动时显示。</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/15835/js13kpwa-splash.png" style="border-style: solid; border-width: 1px; display: block; margin: 0px auto;"></p>
+
+<p>图标、主题和背景色用于创建这个启动画面。</p>
+
+<h2 id="总结">总结</h2>
+
+<p>在本文中,我们学习了如何使用网页清单和安装到主屏特性让PWA可安装。</p>
+
+<p>为了获得添加到主屏的更多信息,请阅读<a href="/en-US/docs/Web/Apps/Progressive/Add_to_home_screen">添加到主屏指南</a>。 浏览器支持当前限于安卓火狐58+,移动Chrome和安卓Webview 31+, 安卓Opera32+, 但是这些限制在不久的将来会改善的。.</p>
+
+<p>现在让我们转移到PWA的最后一个困惑 — 通过推送通知再次启动(re-engagement)。</p>
+
+<p>{{PreviousMenuNext("Web/Apps/Progressive/Offline_Service_workers", "Web/Apps/Progressive/Re-engageable_Notifications_Push", "Web/Apps/Progressive")}}</p>
+
+<p>{{QuickLinksWithSubpages("/en-US/docs/Web/Progressive_web_apps/")}}</p>
diff --git a/files/zh-cn/web/progressive_web_apps/introduction/index.html b/files/zh-cn/web/progressive_web_apps/introduction/index.html
new file mode 100644
index 0000000000..f455dddb09
--- /dev/null
+++ b/files/zh-cn/web/progressive_web_apps/introduction/index.html
@@ -0,0 +1,90 @@
+---
+title: 渐进式 web 应用介绍
+slug: Web/Progressive_web_apps/Introduction
+translation_of: Web/Progressive_web_apps/Introduction
+---
+<div>{{NextMenu("Web/Apps/Progressive/App_structure", "Web/Apps/Progressive")}}</div>
+
+<p class="summary">这篇文章将会为你介绍渐进式web应用(PWA),讨论一下它们到底是什么,相比于常规的web应用,它又带来了哪些优势。</p>
+
+<h2 id="什么是渐进式web应用?">什么是渐进式web应用?</h2>
+
+<p>PWA应用是指那些使用指定技术和标准模式来开发的web应用,这将同时赋予它们web应用和原生应用的特性。</p>
+
+<p>例如,web应用更加易于发现——相比于安装应用,访问一个网站显然更加容易和迅速,并且你可以通过一个链接来分享web应用。</p>
+
+<p>在另一方面,原生应用与操作系统可以更加完美的整合,也因此为用户提供了无缝的用户体验。你可以通过安装应用使得它在离线的状态下也可以运行,并且相较于使用浏览器访问,用户也更喜欢通过点击主页上的图标来访问它们喜爱的应用。</p>
+
+<p>PWA赋予了我们创建同时拥有以上两种优势的应用的能力。</p>
+
+<p>这并不是一个新概念——这样的想法在过去已经在web平台上通过许多方法出现了多次。渐进式增强和响应式设计已经可以让我们构建对移动端友好的网站。在多年以前的Firefox OS的生态系统中离线运行和安装web应用已经成为了可能。</p>
+
+<p>PWAs, 不但如此,更是提供了所有的甚至是更多的特性,来让web更加优秀。</p>
+
+<h2 id="什么使应用成为PWA">什么使应用成为PWA?</h2>
+
+<p>正如前文所述,PWA不是使用一种技术创建的。它们代表了构建Web应用程序的新理念,涉及一些特定的模式,API和其他功能。如果一个Web App从一开始就是PWA,那就不那么明显了。当应用程序满足某些要求时,可以将其视为PWA,或者实现一组给定的功能:离线工作,可安装,易于同步,可以发送推送通知等。</p>
+
+<p>此外,还有一些工具可以按百分比衡量应用的完整性。(<a href="https://developers.google.com/web/tools/lighthouse/">Lighthouse</a>目前是最受欢迎的工具)通过实施各种技术优势,我们可以使应用程序更加渐进式,从而最终获得更高的Lighthouse 得分。但这只是一个粗略的指标。</p>
+
+<p>这里有一些关键的原则来辨别一个web应用是否是一个PWA应用。它应该具有以下特点:</p>
+
+<ul>
+ <li><a href="/en-US/Apps/Progressive/Advantages#Discoverable">Discoverable</a>, 内容可以通过搜索引擎发现。</li>
+ <li><a href="/en-US/Apps/Progressive/Advantages#Installable">Installable</a>, 可以出现在设备的主屏幕。</li>
+ <li><a href="/Apps/Progressive/Advantages#Linkable">Linkable</a>, 你可以简单地通过一个URL来分享它。 </li>
+ <li><a href="/en-US/Apps/Progressive/Advantages#Network_independent">Network independent</a>, 它可以在离线状态或者是在网速很差的情况下运行。</li>
+ <li><a href="/en-US/Apps/Progressive/Advantages#Progressive">Progressive</a>, 它在老版本的浏览器仍旧可以使用,在新版本的浏览器上可以使用全部功能。</li>
+ <li><a href="/en-US/Apps/Progressive/Advantages#Re-engageable">Re-engageable</a>, 无论何时有新的内容它都可以发送通知。</li>
+ <li><a href="/en-US/Apps/Progressive/Advantages#Responsive">Responsive</a>, 它在任何具有屏幕和浏览器的设备上可以正常使用——包括手机,平板电脑,笔记本,电视,冰箱,等。</li>
+ <li><a href="/en-US/Apps/Progressive/Advantages#Safe">Safe</a>, 在你和应用之间的连接是安全的,可以阻止第三方访问你的敏感数据。</li>
+</ul>
+
+<h3 id="这么做值得吗?">这么做值得吗?</h3>
+
+<p>当然值得!只需要相对较小的代价就可以实现PWA的核心特性,这样的优势是巨大的。例如:</p>
+
+<ul>
+ <li>减少应用安装后的加载时间, 多亏了 <a href="/en-US/docs/Web/API/Service_Worker_API">Service Workers</a>来进行缓存, 以此来节省带宽和时间。</li>
+ <li>当应用有可用的更新时,可以仅仅去更新发生改变的那部分内容。与之相反,对于一个原生应用而言,即便是最微小的改动也需要强制用户去再次下载整个应用。</li>
+ <li>外观和使用感受与原生平台更加融为一体——应用图标被放置在主屏幕上,应用可以全屏运行,等。</li>
+ <li>凭借系统通知和推送消息与用户保持连接,对用户产生更多的吸引力,并且提高转换效率。</li>
+</ul>
+
+<p>有许多知名的成功企业正在尝试PWA的方式,选择一个增强的网站体验而不是一个原生应用。事实上它们也确实从中获得了显而易见的益处。  <a href="https://www.pwastats.com/">PWA Stats</a> 这个网站上分享了许多案例研究,这都证明了以上这些优势。</p>
+
+<p>最著名的案例可能是<a href="https://stories.flipkart.com/flipkart-lite/">Flipkart Lite</a>--印度最大的电子商务网站,在2015年重建为渐进式网络应用程序,转化率提高了70%。<a href="https://m.aliexpress.com/">AliExpress</a> PWA也看到了比web app或native app更好的结果,新用户的转换率提高了104%。鉴于其利润增长以及转换为PWA所需的工作量相对较少,优势显而易见。</p>
+
+<p>像<a href="https://www.couponmoto.com/"> couponmoto</a> 这样的早期新兴创业公司也开始使用渐进式网络应用来推动更多的消费者参与,这表明渐进式网络应用可以帮助小公司和大公司更有效地(重新)吸引用户。</p>
+
+<p>您可以查看 <a href="https://pwa.rocks/">pwa.rocks</a> 上的列表以获取更多示例。 特别值得一提的是<a href="https://hnpwa.com/">hnpwa.com</a>,它列出了 Hacker News 网站的示例实现(而不是通常的 TodoMVC 应用程序),您可以在其中看到各种前端框架的使用。</p>
+
+<p>您甚至可以使用<a href="https://www.pwabuilder.com/"> PWABuilder</a> 网站在线生成 PWA。</p>
+
+<p>对于 service worker 和推送特定信息,请务必查看 <a href="https://serviceworke.rs/">Service Worker 手册</a>,一个在现代站点中使用 service worker 的方法集合。</p>
+
+<p>PWA是非常值得尝试的,您可以自己查看它是否适用于您的应用程序。</p>
+
+<h2 id="浏览器支持">浏览器支持</h2>
+
+<p>如前所述,PWA 不依赖于单个 API ,而是使用各种技术来实现提供最佳 Web 体验的目标。</p>
+
+<p><span class="tlid-translation translation" lang="zh-CN"><span title="">PWA 所需的关键要素是 </span></span><a href="/en-US/docs/Web/API/Service_Worker_API">service worker</a><span class="tlid-translation translation" lang="zh-CN"><span title=""> 支持。</span> <span title="">值得庆幸的是,桌面和移动设备上的</span></span><a href="https://jakearchibald.github.io/isserviceworkerready/">所有主流浏览器都支持</a> service worker<span class="tlid-translation translation" lang="zh-CN"><span title="">。</span></span></p>
+
+<p><span class="tlid-translation translation" lang="zh-CN"><span title="">其他功能,如</span></span> <a href="/en-US/docs/Web/Manifest">Web App Manifest</a><span class="tlid-translation translation" lang="zh-CN"><span title="">,</span></span><a href="/en-US/docs/Web/API/Push_API">Push</a><span class="tlid-translation translation" lang="zh-CN"><span title="">,</span></span><a href="/en-US/docs/Web/API/Notifications_API">Notifications</a><span class="tlid-translation translation" lang="zh-CN"><span title="">和</span></span> <a href="/en-US/docs/Web/Apps/Progressive/Add_to_home_screen">Add to Home Screen</a> <span class="tlid-translation translation" lang="zh-CN"><span title="">功能也得到了广泛的支持。</span> <span title="">目前,Safari 对 </span></span><a href="/en-US/docs/Web/Manifest">Web App Manifest</a><span class="tlid-translation translation" lang="zh-CN"><span title=""> </span></span><span class="tlid-translation translation" lang="zh-CN"><span title="">和</span></span> <a href="/en-US/docs/Web/Apps/Progressive/Add_to_home_screen">Add to Home Screen </a><span class="tlid-translation translation" lang="zh-CN"><span title="">的支持有限,并且不支持 Web 推送通知。</span> <span title="">但是,其他主流浏览器支持所有这些功能。</span></span></p>
+
+<p><span class="tlid-translation translation" lang="zh-CN"><span title="">其中一些 API 是实验性的,文档仍在草稿中,但是看到像 Flipkart 和 AliExpress 这样的成功案例应该说服您尝试在 Web 应用程序中实现一些 PWA 功能。</span></span></p>
+
+<p><span class="tlid-translation translation" lang="zh-CN"><span title="">最重要的是,您应该遵循渐进增强规则 - 仅在支持它们的情况下使用提供此类增强功能的技术,但如果不支持,则仍然提供应用程序的基本功能。</span> <span title="">这样每个人都可以使用它,但那些使用现代浏览器的人将更多地受益于 PWA 功能。</span></span></p>
+
+<h2 id="一个示例应用程序">一个示例应用程序</h2>
+
+<p><span class="tlid-translation translation" lang="zh-CN"><span title="">在本系列文章中,我们将研究一个超级简单网站的源代码,该网站列出了 </span></span> <a href="http://2017.js13kgames.com/">js13kGames 2017</a> <span class="tlid-translation translation" lang="zh-CN"><span title="">竞赛中提交给 </span></span><a href="http://js13kgames.com/aframe">A-Frame category</a> <span class="tlid-translation translation" lang="zh-CN"><span title="">的游戏的相关信息。</span> <span title="">您不必考虑网站上的实际内容 - 主要是学习如何在您自己的项目中使用PWA功能。</span></span></p>
+
+<p>您可以在 <a href="https://mdn.github.io/pwa-examples/js13kpwa/">mdn.github.io/pwa-examples/js13kpwa</a> 找到在线版本(另请<a href="https://github.com/mdn/pwa-examples/tree/master/js13kpwa">参阅源代码</a>),我们将在接下来的几篇文章中对其进行详细解释。</p>
+
+<p>现在,让我们转到本系列的第二部分,我们将看看我们的示例应用程序的结构。</p>
+
+<p>{{NextMenu("Web/Apps/Progressive/App_structure", "Web/Apps/Progressive")}}</p>
+
+<div>{{QuickLinksWithSubpages("/en-US/docs/Web/Apps/Progressive/")}}</div>
diff --git a/files/zh-cn/web/progressive_web_apps/network_independent/index.html b/files/zh-cn/web/progressive_web_apps/network_independent/index.html
new file mode 100644
index 0000000000..003ea1f351
--- /dev/null
+++ b/files/zh-cn/web/progressive_web_apps/network_independent/index.html
@@ -0,0 +1,94 @@
+---
+title: 网络独立
+slug: Web/Progressive_web_apps/Network_independent
+tags:
+ - Application Shell
+ - IndexedDB
+ - PWA
+ - Progressive web apps
+ - Service Workers
+ - Web Storage
+ - localStorage
+translation_of: Web/Progressive_web_apps
+---
+<div class="column-container summary">
+<div class="column-11">当网络不可靠,甚至不存在时,现代网络应用程序仍可以工作。没有更多的空白连接错误页面或恐龙穿过沙漠。除了离线高速缓存和服务工作者之外,UI和内容之间的一个明确的分隔可让您存储应用程序的数据和核心资产,以备将来使用。</div>
+
+<div class="column-1"><img alt="" src="https://mdn.mozillademos.org/files/12660/network-independent.svg" style="height: 43px; width: 43px;"></div>
+</div>
+
+<p>The basic ideas behind network independence are to be able to:</p>
+
+<ul>
+ <li>Revisit a site and get its contents even if no network is available.</li>
+ <li>Browse any kind of content the user has previously visited at least once, even under situations of poor connectivity.</li>
+ <li>Control what is shown to the user in situations where there is no connectivity.</li>
+</ul>
+
+<h2 id="核心指南">核心指南</h2>
+
+<dl>
+ <dt><a href="/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers">Using service workers</a></dt>
+ <dd>A simple guide for those new to the Service Worker API.</dd>
+ <dt><a href="/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB">Using IndexedDB</a></dt>
+ <dd>The basics of IndexedDB, explained in detail.</dd>
+ <dt><a href="/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API">Using the Web Storage API</a></dt>
+ <dd>The Web storage API made simple.</dd>
+ <dt><a href="https://developers.google.com/web/updates/2015/11/app-shell">Instant Loading Web Apps with An Application Shell Architecture</a></dt>
+ <dd>A guide to using the App Shell coding pattern to create apps that load quickly.</dd>
+</dl>
+
+<h2 id="技术">技术</h2>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">技术</th>
+ <th scope="col">描述</th>
+ <th scope="col">支持概览</th>
+ <th scope="col">最新规范</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><a href="/en-US/docs/Web/API/Service_Worker_API">Service workers</a></td>
+ <td>JavaScript running in a special worker context that is run by the browser under certain circumstances such as <em>fetch</em> or <em>push</em> events. These allow the service worker to intercept responses and customise them in any way you want, for example caching assets for offline use before they are served.</td>
+ <td>Experimental: Chrome and Firefox (<a href="/en-US/docs/Web/API/Service_Worker_API#Browser_compatibility">more detail</a>)</td>
+ <td>{{SpecName('Service Workers')}}</td>
+ </tr>
+ <tr>
+ <td><a href="/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a></td>
+ <td>A transactional database system that allows complex client-side data storage to be controlled via JavaScript.</td>
+ <td>Widespread across modern browsers (<a href="/en-US/docs/Web/API/IndexedDB_API#Browser_compatibility">more detail</a>)</td>
+ <td>{{SpecName('IndexedDB')}}</td>
+ </tr>
+ <tr>
+ <td><a href="/en-US/docs/Web/API/Web_Storage_API">Web Storage</a></td>
+ <td>A simple API for storing name-value pairs on the client-side.</td>
+ <td>Widespread (<a href="/en-US/docs/Web/API/Web_Storage_API#Browser_compatibility">more detail</a>)</td>
+ <td>{{SpecName('Web Storage')}}</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="工具">工具</h2>
+
+<dl>
+ <dt><a href="http://mozilla.github.io/localForage/">localForage</a></dt>
+ <dd>A nice simple JavaScript library for making client-side data storage really simple; it uses IndexedDB by default, and falls back to Web SQL/Web Storage if necessary.</dd>
+ <dt><a href="https://github.com/fxos-components/serviceworkerware">ServiceWorkerWare</a></dt>
+ <dd>An <em>Express-like</em> microframework for easy Service Worker development.</dd>
+ <dt><a href="https://github.com/mozilla/oghliner">oghliner</a></dt>
+ <dd>Not only a template but a tool for deploying Offline Web Apps to GitHub Pages.</dd>
+ <dt><a href="https://github.com/GoogleChrome/sw-precache">sw-precache</a></dt>
+ <dd>A node module to generate service worker code that will precache specific resources.</dd>
+ <dt><a href="https://www.talater.com/upup/">upup</a></dt>
+ <dd>A tiny script that makes sure your site is always there for your users.</dd>
+</dl>
+
+<h2 id="参见">参见</h2>
+
+<dl>
+ <dt><a href="https://serviceworke.rs/">The service worker cookbook</a></dt>
+ <dd>A series of excellent service worker recipes, showing how to implement an offline app, but also much more.</dd>
+</dl>
diff --git a/files/zh-cn/web/progressive_web_apps/offline_service_workers/index.html b/files/zh-cn/web/progressive_web_apps/offline_service_workers/index.html
new file mode 100644
index 0000000000..fddc3ead25
--- /dev/null
+++ b/files/zh-cn/web/progressive_web_apps/offline_service_workers/index.html
@@ -0,0 +1,199 @@
+---
+title: 通过 Service workers 让 PWA 离线工作
+slug: Web/Progressive_web_apps/Offline_Service_workers
+translation_of: Web/Progressive_web_apps/Offline_Service_workers
+---
+<div>{{PreviousMenuNext("Web/Apps/Progressive/App_structure", "Web/Apps/Progressive/Installable_PWAs", "Web/Apps/Progressive")}}</div>
+
+<p>我们已经看到了 js13kPWA 的结构,并且看到了基本的 shell 启动和运行,那么让我们看看如何使用 Service Worker 实现离线功能。 在本文中,我们将看看它是如何在 <a href="https://mdn.github.io/pwa-examples/js13kpwa/">js13kPWA example</a> 中使用的(<a href="https://github.com/mdn/pwa-examples/tree/master/js13kpwa">另请参阅源代码</a>)。 我们将研究如何添加脱机功能。</p>
+
+<h2 id="Service_workers_解释">Service workers 解释</h2>
+
+<p>Service Workers是浏览器和网络之间的虚拟代理。 他们最终解决了前端开发人员多年来一直在努力解决的问题 - 最值得注意的是解决了如何正确缓存网站资源并使其在用户设备离线时可用。</p>
+
+<p>它们的运行在一个与我们页面的 JavaScript 主线程独立的线程上,并且没有对 DOM 结构的任何访问权限。 这引入了与传统 Web 编程不同的方法 - API 是非阻塞的,并且可以在不同的上下文之间发送和接收信息。 您可分配给 Service Worker 一些任务,并在使用基于 <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promise</a> 的方法当任务完成时收到结果。</p>
+
+<p>他们不仅仅提供离线功能,还提供包括处理通知,在单独的线程上执行繁重的计算等。Service workers 非常强大,因为他们可以控制网络请求,修改网络请求,返回缓存的自定义响应,或合成响应。</p>
+
+<h3 id="安全">安全</h3>
+
+<p>因为它们非常强大,所以 Service Workers 只能在安全的上下文中执行(即 HTTPS )。 如果您想在将代码推送到生产环境之前先进行实验,则可以始终在本地主机上进行测试或设置 GitHub 页面 - 两者都支持HTTPS。</p>
+
+<h2 id="离线优先">离线优先</h2>
+
+<p>“离线优先”或“缓存优先”模式是向用户提供内容的最流行策略。 如果资源已缓存且可脱机使用,请在尝试从服务器下载资源之前先将其返回。 如果它已经不在缓存中,请下载并缓存以备将来使用。</p>
+
+<h2 id="PWA_渐进增强">PWA 渐进增强</h2>
+
+<p>当作为渐进增强功能正确实施时,service workers 可以通过提供离线支持使支持 Service​Worker API 的现代浏览器的用户受益,但不会为使用旧版浏览器的用户破坏任何内容。</p>
+
+<h2 id="js13kPWA应用程序中的_Service_workers"><span class="tlid-translation translation" lang="zh-CN"><span title="">js13kPWA应用程序中的 </span></span>Service workers</h2>
+
+<p><span class="tlid-translation translation" lang="zh-CN"><span class="alt-edited">理论足够了,让我们看一些源代码!</span></span></p>
+
+<h3 class="highlight-spanned" id="注册_Service_Worker"><span class="highlight-span">注册 Service Worker</span></h3>
+
+<p>首先在 app.js 文件中查看注册新 Service Worker 的代码:</p>
+
+<pre class="brush: js notranslate">if('serviceWorker' in navigator) {
+ navigator.serviceWorker.register('/pwa-examples/js13kpwa/sw.js');
+};</pre>
+
+<p>如果浏览器支持 service worker API,则使用 <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/ServiceWorkerContainer/register" title="ServiceWorkerContainer 接口的 register() 方法创建或更新一个给定scriptURL的ServiceWorkerRegistration 。"><code>ServiceWorkerContainer.register()</code></a> 方法对该站点进行注册。 其内容在 sw.js 文件中,可以在注册成功后执行。 它是 app.js 文件中唯一的Service Worker代码; 其他关于 Service Worker 的内容都写在 sw.js 文件中。</p>
+
+<h3 class="highlight-spanned" id="Service_Worker_的生命周期"><span class="highlight-span">Service Worker 的生命周期</span></h3>
+
+<p>注册完成后,sw.js 文件会自动下载,然后安装,最后激活。</p>
+
+<h4 id="安装">安装</h4>
+
+<p>API 允许我们为我们感兴趣的关键事件添加事件监听器 - 第一个是 <code>install</code> 事件:</p>
+
+<pre class="brush: js notranslate">self.addEventListener('install', function(e) {
+ console.log('[Service Worker] Install');
+});</pre>
+
+<p>在<code>install</code> 的监听函数中, 我们可以初始化缓存以及添加离线应用时所需的文件,我们的 js13kPWA 应用确实也是这么做的。</p>
+
+<p>首先,创建一个作为缓存的名字的变量,app shell所需的文件被记录在一个数组上</p>
+
+<pre class="brush: js notranslate">var cacheName = 'js13kPWA-v1';
+var appShellFiles = [
+ '/pwa-examples/js13kpwa/',
+ '/pwa-examples/js13kpwa/index.html',
+ '/pwa-examples/js13kpwa/app.js',
+ '/pwa-examples/js13kpwa/style.css',
+ '/pwa-examples/js13kpwa/fonts/graduate.eot',
+ '/pwa-examples/js13kpwa/fonts/graduate.ttf',
+ '/pwa-examples/js13kpwa/fonts/graduate.woff',
+ '/pwa-examples/js13kpwa/favicon.ico',
+ '/pwa-examples/js13kpwa/img/js13kgames.png',
+ '/pwa-examples/js13kpwa/img/bg.png',
+ '/pwa-examples/js13kpwa/icons/icon-32.png',
+ '/pwa-examples/js13kpwa/icons/icon-64.png',
+ '/pwa-examples/js13kpwa/icons/icon-96.png',
+ '/pwa-examples/js13kpwa/icons/icon-128.png',
+ '/pwa-examples/js13kpwa/icons/icon-168.png',
+ '/pwa-examples/js13kpwa/icons/icon-192.png',
+ '/pwa-examples/js13kpwa/icons/icon-256.png',
+ '/pwa-examples/js13kpwa/icons/icon-512.png'
+];</pre>
+
+<p>接下来,从data/games.js的内容中解析出来的图片链接被赋值到另一个数组上,之后,两个数组会用{{jsxref("Array.prototype.concat()")}}方法合并起来。</p>
+
+<pre class="brush: js notranslate">var gamesImages = [];
+for(var i=0; i&lt;games.length; i++) {
+ gamesImages.push('data/img/'+games[i].slug+'.jpg');
+}
+var contentToCache = appShellFiles.concat(gamesImages);</pre>
+
+<p>接着我们可以监听install事件:</p>
+
+<pre class="brush: js notranslate">self.addEventListener('install', function(e) {
+ console.log('[Service Worker] Install');
+ e.waitUntil(
+ caches.open(cacheName).then(function(cache) {
+ console.log('[Service Worker] Caching all: app shell and content');
+ return cache.addAll(contentToCache);
+ })
+ );
+});</pre>
+
+<p>有两件事需要在这里解释一下:{{domxref("ExtendableEvent.waitUntil")}}做了什么 ?{{domxref("Caches","caches")}} 对象是什么东西?</p>
+
+<p>service worker会等到 <code>waitUntil</code> 里面的代码执行完毕之后才开始安装。它返回一个promise —— 这个过程时必须的,因为安装过程需要一些时间,我们必须等待它完成 。</p>
+
+<p><code>caches</code> 是一个特殊的 {{domxref("CacheStorage")}} 对象,它能在Service Worker指定的范围内提供数据存储的能力(service worker在注册时,第二个参数是选填的,可以被用来指定你想让 service worker 控制的内容的子目录,译者注),在service worker中使用<a href="/en-US/docs/Web/API/Web_Storage_API">web storage</a> 将不会有效果,因为web storage的执行是同步的(此处理解为web storage并不返回一个promise,译者注),所以我们使用Cache API作为替代。</p>
+
+<p>这里,我们使用给定的名字开启了一个缓存,并且将我们的应用所需要缓存的文件全部添加进去,当我们再次加载这些资源时,对应的缓存就是可用的(通过请求的url确定缓存是否命中)。</p>
+
+<h4 id="Activation">Activation</h4>
+
+<p>还有一个<code>activate</code> 事件,它的用法跟<code>install </code>事件相同。这个事件通常用来删除那些我们已经不需要的文件或者做一些清理工作。因为在我们的app里面没有使用到,这里我们暂时跳过它。</p>
+
+<h3 id="响应请求">响应请求</h3>
+
+<p><code><font face="Arial, x-locale-body, sans-serif"><span style="background-color: #ffffff;">每次当我们的应用发起一个http请求时,我们还有一个</span></font>fetch</code> 事件可以使用。这个事件对我们来说非常有用,它允许我们拦截请求并对请求作出自定义的响应,下面是一个简单的例子</p>
+
+<pre class="brush: js notranslate">self.addEventListener('fetch', function(e) {
+ console.log('[Service Worker] Fetched resource '+e.request.url);
+});</pre>
+
+<p>请求的响应可以是任何我们想要的东西:请求过的文件缓存下来的副本,或者一段做了具体操作的JavaScript代码,拥有无限的可能。</p>
+
+<p>在我们的示例代码中,当缓存存在时,我们使用缓存来提供服务而不是重新请求数据。不管当前应用是在线还是离线,我们都这么做。当请求的文件不在缓存中时,我们会在响应之前将数据添加到缓存中。</p>
+
+<pre class="brush: js notranslate">self.addEventListener('fetch', function(e) {
+ e.respondWith(
+ caches.match(e.request).then(function(r) {
+ console.log('[Service Worker] Fetching resource: '+e.request.url);
+ return r || fetch(e.request).then(function(response) {
+ return caches.open(cacheName).then(function(cache) {
+ console.log('[Service Worker] Caching new resource: '+e.request.url);
+ cache.put(e.request, response.clone());
+ return response;
+ });
+ });
+ })
+ );
+});</pre>
+
+<p>上述代码中,对于请求我们首先会在缓存中查找资源是否被缓存,如果有,将会返回缓存的资源,如果不存在,会转而从网络中请求数据,然后将它缓存起来,这样下次有相同的请求发生时,我们就可以直接使用缓存。</p>
+
+<p>{{domxref("FetchEvent.respondWith")}} 方法将会接管响应控制,它会作为服务器和应用之间的代理服务。它允许我们对每一个请求作出我们想要的任何响应:Service Worker会处理这一切,从缓存中获取这些数据,并在需要的情况下对它们进行修改。</p>
+
+<p>就是这样,我们的应用会在install触发时缓存资源,并且在fetch事件触发时返回缓存中的资源,这就是为什么它甚至在离线状态下也能使用的原因。当我们添加新的内容时,他也会随时被缓存下来。</p>
+
+<h2 id="更新">更新</h2>
+
+<p>还有一点需要考虑:当我们的应用有了一个新版本,并且它包含了一些可用的新资源 时,我们应该如何去更新它的Service Worker?我们存放在缓存名称中的版本号是这个问题的关键:</p>
+
+<pre class="brush: js notranslate">var cacheName = 'js13kPWA-v1';</pre>
+
+<p>当我们把版本号更新到v2,service worker会将我们所有的文件(包括那些新的文件)添加到一个新的缓存中。</p>
+
+<pre class="brush: js notranslate">contentToCache.push('/pwa-examples/js13kpwa/icons/icon-32.png');
+
+// ...
+
+self.addEventListener('install', function(e) {
+ e.waitUntil(
+ caches.open('js13kPWA-v2').then(function(cache) {
+ return cache.addAll(contentToCache);
+ })
+ );
+});</pre>
+
+<p>这个时候一个新的service worker会在后台被安装,而旧的service worker仍然会正确的运行,直到没有任何页面使用到它为止,这时候新的service worker将会被激活,然后接管所有的页面。</p>
+
+<h2 id="缓存的清理">缓存的清理</h2>
+
+<p>还记得我们前面跳过的那个<code>activate</code> 事件吗?它可以用来清理那些我们不再需要的缓存:</p>
+
+<pre class="brush: js notranslate">self.addEventListener('activate', function(e) {
+ e.waitUntil(
+ caches.keys().then(function(keyList) {
+ return Promise.all(keyList.map(function(key) {
+ if(cacheName.indexOf(key) === -1) {
+ return caches.delete(key);
+ }
+ }));
+ })
+ );
+});</pre>
+
+<p>这样能确保只有那些我们需要的文件会保留在缓存中,我们不需要留下任何的垃圾,毕竟<a href="/en-US/docs/Web/API/IndexedDB_API/Browser_storage_limits_and_eviction_criteria">浏览器的缓存空间是有限的</a>,手动清理掉这些不需要的缓存是一个不错的主意。</p>
+
+<h2 id="其他用途">其他用途</h2>
+
+<p>从缓存中加载资源并不是Service Worker的唯一能力,如果你有比较耗时的计算,你可以把它们从主线程中抽离出来,在Service Worker中进行计算,最后在它们计算完毕的时候从Service Worker中取得计算结果。性能方面,你可以在Service Worker中对即将使用到的资源进行预加载,这样当你使用到这些资源的时候,应用的加载速度会更快。</p>
+
+<h2 id="总结">总结</h2>
+
+<p>在这篇文章中我们简单的了解了如何使用service workers让你的PWA实现离线功能。如果你想要学习更多关于<a href="/en-US/docs/Web/API/Service_Worker_API">Service Worker API</a>的概念以及使用service worker方面的相关细节,你可以进一步查阅我们的文档。</p>
+
+<p>Service Workers在处理<a href="/en-US/docs/Web/API/Push_API">消息推送</a> 的时候也会经常被用到,这一部分我们将会在后面的章节进行讨论。</p>
+
+<p>{{PreviousMenuNext("Web/Apps/Progressive/App_structure", "Web/Apps/Progressive/Installable_PWAs", "Web/Apps/Progressive")}}</p>
+
+<div>{{QuickLinksWithSubpages("/en-US/docs/Web/Apps/Progressive/")}}</div>
diff --git a/files/zh-cn/web/progressive_web_apps/re-engageable/index.html b/files/zh-cn/web/progressive_web_apps/re-engageable/index.html
new file mode 100644
index 0000000000..4fb4a7475f
--- /dev/null
+++ b/files/zh-cn/web/progressive_web_apps/re-engageable/index.html
@@ -0,0 +1,78 @@
+---
+title: Re-engageable
+slug: Web/Progressive_web_apps/Re-engageable
+tags:
+ - Modern web apps
+ - Notifications API
+ - Progressive web apps
+ - Push API
+ - Service Workers
+translation_of: Web/Progressive_web_apps
+---
+<div class="column-container summary">
+<div class="column-11">原生平台一个主要优势是,用户可以轻松通过更新或加载新内容,即使用户没有正在查看应用程序或者使用他们的设备。现在的Web应用程序现在也可以使用Web Push API等技术实现这样的功能。</div>
+
+<div class="column-1"><img alt="" src="https://mdn.mozillademos.org/files/12666/re-engageable.svg" style="height: 43px; width: 43px;"></div>
+</div>
+
+<h2 id="核心指南">核心指南</h2>
+
+<dl>
+ <dt><a href="/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers">Using service workers</a></dt>
+ <dd>A simple guide for those new to the Service Worker API.</dd>
+ <dt><a href="/en-US/docs/Web/API/Push_API/Using_the_Push_API">Using the Push API</a></dt>
+ <dd>Learn the essentials behind the Web Push API.</dd>
+ <dt><a href="/en-US/docs/Web/API/Notifications_API/Using_the_Notifications_API">Using the Notifications API</a></dt>
+ <dd>Web notifications in a nutshell.</dd>
+</dl>
+
+<h2 id="技术">技术</h2>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">技术</th>
+ <th scope="col">描述</th>
+ <th scope="col">浏览器支持</th>
+ <th scope="col">最新规范</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><a href="/en-US/docs/Web/API/Service_Worker_API">Service workers</a></td>
+ <td>JavaScript running in a special worker context that is run by the browser under certain circumstances such as <em>fetch</em> or <em>push</em> events. These allow the service worker to intercept responses and customise them in any way you want, for example caching assets for offline use before they are served.</td>
+ <td>Experimental: Chrome and Firefox (<a href="/en-US/docs/Web/API/Service_Worker_API#Browser_compatibility">more detail</a>)</td>
+ <td>{{SpecName('Service Workers')}}</td>
+ </tr>
+ <tr>
+ <td><a href="/en-US/docs/Web/API/Push_API">Push API</a></td>
+ <td>When subscribed to, the push service provides an endpoint that can be used by a server to send a push message to a web app under the control of a particular service worker.</td>
+ <td>Experimental: chrome and Firefox (<a href="/en-US/docs/Web/API/Push_API#Browser_Compatibility">more detail</a>)</td>
+ <td>{{SpecName("Push API")}}</td>
+ </tr>
+ <tr>
+ <td><a href="/en-US/docs/Web/API/Notifications_API">Notifications API</a></td>
+ <td>Fires system notifications directly from web applications.</td>
+ <td>Widespreadin modern browsers (<a href="/en-US/docs/Web/API/Notifications_API#Browser_compatibility">more detail</a>)</td>
+ <td>{{SpecName('Web Notifications')}}</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="工具">工具</h2>
+
+<dl>
+ <dt><a href="https://github.com/fxos-components/serviceworkerware">ServiceWorkerWare</a></dt>
+ <dd>An <em>Express-like</em> microframework for easy Service Worker development.</dd>
+ <dt><a href="https://github.com/mozilla/oghliner">oghliner</a></dt>
+ <dd>Not only a template but a tool for deploying Offline Web Apps to GitHub Pages.</dd>
+ <dt><a href="https://github.com/GoogleChrome/sw-precache">sw-precache</a></dt>
+ <dd>A node module to generate service worker code that will precache specific resources.</dd>
+</dl>
+
+<h2 id="参见">参见</h2>
+
+<dl>
+ <dt><a href="https://serviceworke.rs/">The service worker cookbook</a></dt>
+ <dd>A series of excellent service worker recipes, showing how to implement an offline app, but also much more.</dd>
+</dl>
diff --git a/files/zh-cn/web/progressive_web_apps/re-engageable_notifications_push/index.html b/files/zh-cn/web/progressive_web_apps/re-engageable_notifications_push/index.html
new file mode 100644
index 0000000000..bb214c16a4
--- /dev/null
+++ b/files/zh-cn/web/progressive_web_apps/re-engageable_notifications_push/index.html
@@ -0,0 +1,242 @@
+---
+title: 通过通知推送让 PWA 可重用
+slug: Web/Progressive_web_apps/Re-engageable_Notifications_Push
+translation_of: Web/Progressive_web_apps/Re-engageable_Notifications_Push
+---
+<div>{{PreviousMenuNext("Web/Apps/Progressive/Installable_PWAs", "Web/Apps/Progressive/Loading", "Web/Apps/Progressive")}}</div>
+
+<p class="summary">拥有本地缓存能力来实现离线应用是一个强大的特性。允许用户在主屏幕上安装Web应用程序则显得更了不起。但与其单纯依赖用户手动打开应用,我们可以做得更多的东西,我们可以利用消息和通知推送来自动重启应用并随时提供新的内容。</p>
+
+<h2 id="两个API,一个目标">两个API,一个目标</h2>
+
+<p><a href="/en-US/docs/Web/API/Push_API">推送 API</a>和 <a href="/en-US/docs/Web/API/Notifications_API">通知 API</a>是两个相互独立的API,但当你的应用想实现一些有趣实用的功能时他们可以配合得很好。推送API可以实现从服务端推送新的内容而不需要客户端发起请求,它是由应用的service worker来实现的。通知功能则可以通过service worker来向用户展示一些新的信息,或者至少提醒用户应用已经更新了某些功能。</p>
+
+<p>这些工作是在浏览器外部实现的,跟service worker一样,所以即使应用被隐藏到后台甚至应用已经被关闭了,我们仍然能够推送更新或者推送通知给用户。</p>
+
+<h2 id="通知">通知</h2>
+
+<p>让我们先从通知API开始,它能够脱离推送API单独工作,但两者结合起来会非常的有用,我们先单独看看它能做什么。</p>
+
+<h3 id="请求授权">请求授权</h3>
+
+<p>为了能够显示通知,我们需要先请求用户的授权。最佳的实践经验告诉我们,我们应该引导用户点击一个按钮的时候弹出一个授权窗口,而不是立刻显示通知(授权之前无法显示通知,译者注):</p>
+
+<pre class="brush: js">var button = document.getElementById("notifications");
+button.addEventListener('click', function(e) {
+ Notification.requestPermission().then(function(result) {
+ if(result === 'granted') {
+ randomNotification();
+ }
+ });
+});</pre>
+
+<p>下面的图片展示了通过系统的通知服务显示一个授权窗口:</p>
+
+<p><img alt="Notification of js13kPWA." src="https://mdn.mozillademos.org/files/15930/js13kpwa-notification.png" style="display: block; height: 640px; margin: 0px auto; width: 360px;"></p>
+
+<p>当用户确定接收通知,我们得到应用就可以获得推送通知的功能。用户的授权的结果有三种,default,granted 或者denied,当用户没有做出选择的时候,授权结果会返回defalut,另外两种结果分别是用户选择了授权或者拒绝授权。</p>
+
+<p>一旦用户选择授权,这个授权结果对通知API和推送API两者都有效</p>
+
+<h3 id="创建一个通知">创建一个通知</h3>
+
+<p>我们的示例应用通过可用的数据来创建通知 —— 随机选择一个游戏数据来填充通知的主体:用游戏的名称来作为通知的标题,通知的主体会提及游戏的作者,用游戏的图片来作为通知的图标:</p>
+
+<pre class="brush: js">function randomNotification() {
+ var randomItem = Math.floor(Math.random()*games.length);
+ var notifTitle = games[randomItem].name;
+ var notifBody = 'Created by '+games[randomItem].author+'.';
+ var notifImg = 'data/img/'+games[randomItem].slug+'.jpg';
+ var options = {
+ body: notifBody,
+ icon: notifImg
+ }
+ var notif = new Notification(notifTitle, options);
+ setTimeout(randomNotification, 30000);
+}</pre>
+
+<p>上述代码每隔三十秒会创建一个通知,直到用户开始觉得通知有点烦人并手动关闭掉它为止。(对于一个真正的app,这种通知的频率必须进行严格的控制,它的内容也要更加有用才行)通知API的优势在于它使用了操作系统的通知功能。这意味着即使用户当前并没有在使用我们的应用,我们的通知也能够展示,他看起来跟一个原生应用发出的通知差不多。</p>
+
+<h2 id="推送">推送</h2>
+
+<p>推送比通知要复杂一些,我们需要从服务端订阅一个服务,之后服务端会推送数据到客户端应用。应用的Service Worker将会接收到从服务端推送的数据,这些数据可以用来做通知推送,或者实现其他的需求。</p>
+
+<p>这个技术还处在非常初级的阶段,一些运行中的例子使用了谷歌云的消息推送平台,但他们正在被重写以支持 <a href="https://blog.mozilla.org/services/2016/08/23/sending-vapid-identified-webpush-notifications-via-mozillas-push-service/">VAPID</a> (Voluntary Application Identification),它为你的应用提供了一层额外的安全防护。你可以检查一下这个例子<a href="https://serviceworke.rs/push-payload.html">Service Workers Cookbook examples</a>,尝试用<a href="https://firebase.google.com/">Firebase</a>,配置一个消息推送服务,或者构建自己的推送服务(例如使用Node.js).</p>
+
+<p>之前提到,为了接收到推送的消息,你需要有一个service worker,这部分的基础知识我们已经在文章 <a href="https://developer.mozilla.org/zh-CN/docs/Web/Apps/Progressive/Offline_Service_workers" rel="nofollow">通过 Service workers 让 PWA 离线工作</a> 里面解释过。在service worker 内部,存在一个消息推送服务订阅机制。</p>
+
+<pre class="brush: js">registration.pushManager.getSubscription() .then( /* ... */ );</pre>
+
+<p>一旦用户订阅服务,他们就能接收到服务器推送的通知。</p>
+
+<p>从服务端的角度来看,出于安全的目的,这整个过程必须使用非对称加密技术进行加密 —— 允许用户不安全的使用应用程序来发送推送消息会是一个很糟糕的主意。你可以通过阅读<a href="https://jrconlin.github.io/WebPushDataTestPage/">Web推送数据加密测试页</a>里面的详细信息来保护你的服务器。当用户订阅服务时,服务器会储存所有的接收到的信息以便在后续需要的时候能将信息推送出去。</p>
+
+<p>为了能够接收到推送的消息,我们需要在Service Worker文件里面监听{{event("push")}}事件:</p>
+
+<pre class="brush: js">self.addEventListener('push', function(e) { /* ... */ });</pre>
+
+<p>这些数据将会被接收后通过通知的方式立刻展现给用户。举个例子,这个功能可以用来提醒用户一些事情,或者让他们知道我们的应用更新了一些新的内容并且已经可用。</p>
+
+<h3 id="推送例子">推送例子</h3>
+
+<p>推送需要服务端参与工作,但我们没办法将服务端的例子包含进 js13kPWA这个应用里面,因为应用托管在github上,而github只提供静态页面托管。<a href="https://serviceworke.rs/">Service Worker Cookbook</a> 这个页面上有详细的说明,demo请看 <a href="https://serviceworke.rs/push-payload.html">Push Payload Demo</a>.</p>
+
+<p>这个demo包含三个文件:</p>
+
+<ul>
+ <li><a href="https://github.com/mozilla/serviceworker-cookbook/blob/master/push-payload/index.js">index.js</a>, 包含我们应用的源代码</li>
+ <li><a href="https://github.com/mozilla/serviceworker-cookbook/blob/master/push-payload/server.js">server.js</a>,包含服务端的源代码 (用Node.js编写)</li>
+ <li><a href="https://github.com/mozilla/serviceworker-cookbook/blob/master/push-payload/service-worker.js">service-worker.js</a>, 包含service worker的具体代码。</li>
+</ul>
+
+<p>让我们来一起探索一下这些代码</p>
+
+<h4 id="index.js">index.js</h4>
+
+<p>index.js以注册service worker开始:</p>
+
+<pre class="brush: js">navigator.serviceWorker.register('service-worker.js')
+.then(function(registration) {
+ return registration.pushManager.getSubscription()
+ .then(async function(subscription) {
+ // 注册部分
+ });
+})
+.then(function(subscription) {
+ // 订阅部分
+});</pre>
+
+<p>这个service worker比我们在<a href="https://mdn.github.io/pwa-examples/js13kpwa/">js13kPWA demo</a>看到的要稍微复杂一些,在这部分代码里,注册完成之后,我们使用registration对象来发起订阅,然后使用subscription对象来结束这整个流程。</p>
+
+<p>注册部分的代码看起来大致是这个样子:</p>
+
+<pre class="brush: js">if(subscription) {
+ return subscription;
+}</pre>
+
+<p>如果用户已经完成订阅,我们直接返回subscription对象并且跳转到订阅部分。如果没有,我们会初始化一个新的subscription对象:</p>
+
+<pre class="brush: js">const response = await fetch('./vapidPublicKey');
+const vapidPublicKey = await response.text();
+const convertedVapidKey = urlBase64ToUint8Array(vapidPublicKey);</pre>
+
+<p>app会从服务器请求一个公钥并且把响应结果转化成文本,最后它还需要转化成一个Uint8Array(为了兼容chome)。如果你想学习更多关于VAPID的内容可以阅读这篇博客 <a href="https://blog.mozilla.org/services/2016/08/23/sending-vapid-identified-webpush-notifications-via-mozillas-push-service/">Sending VAPID identified WebPush Notifications via Mozilla’s Push Service</a> 。</p>
+
+<p>app现在可以使用{{domxref("PushManager")}} 来订阅新用户。这个方法支持传递两个参数 ——第一个是<code>userVisibleOnly: true</code>  ,意思是发送给用户的所有通知对他们都是可见的,第二个是 <code>applicationServerKey</code> ,这个参数包含我们之前从服务端取得并转化的VAPID key。</p>
+
+<pre class="brush: js">return registration.pushManager.subscribe({
+ userVisibleOnly: true,
+ applicationServerKey: convertedVapidKey
+});</pre>
+
+<p>现在我们把注意力转移到订阅部分 —— app首先使用Fetch subscription以json的方式发送给服务器。</p>
+
+<pre class="brush: js">fetch('./register', {
+ method: 'post',
+ headers: {
+ 'Content-type': 'application/json'
+ },
+ body: JSON.stringify({
+ subscription: subscription
+ }),
+});</pre>
+
+<p>接着注册按钮绑定了一个函数</p>
+
+<pre class="brush: js">document.getElementById('doIt').onclick = function() {
+ const payload = document.getElementById('notification-payload').value;
+ const delay = document.getElementById('notification-delay').value;
+ const ttl = document.getElementById('notification-ttl').value;
+
+ fetch('./sendNotification', {
+ method: 'post',
+ headers: {
+ 'Content-type': 'application/json'
+ },
+ body: JSON.stringify({
+ subscription: subscription,
+ payload: payload,
+ delay: delay,
+ ttl: ttl,
+ }),
+ });
+};</pre>
+
+<p>当按钮被点击的时候, <code>fetch</code> 会请求服务器根据给定的参数发送通知, <code>payload</code> 参数包含通知里面要显示的文本,<code>delay</code> 参数定义了通知将会延迟展示的秒数,<code>ttl</code> 是 time-to-live 的缩写,用来设置通知在服务器上的存活时间,依然是以秒为单位。</p>
+
+<p>接着,我们进入下一个JavaScript文件</p>
+
+<h4 id="server.js">server.js</h4>
+
+<p>服务端的代码是用Node.js编写的,它需要被托管在合适的地方。这部分内容已经可以用一个完全独立的主体再写一篇文章了。所以这里我们只提供一个简单的概览。</p>
+
+<p><a href="https://www.npmjs.com/package/web-push">web-push </a>模块被用来配置 VAPID 密钥,如果它尚未可用,你可以选择性的配置他们。</p>
+
+<pre>const webPush = require('web-push');
+
+if (!process.env.VAPID_PUBLIC_KEY || !process.env.VAPID_PRIVATE_KEY) {
+ console.log("You must set the VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY "+
+ "environment variables. You can use the following ones:");
+ console.log(webPush.generateVAPIDKeys());
+ return;
+}
+
+webPush.setVapidDetails(
+ 'https://serviceworke.rs/',
+ process.env.VAPID_PUBLIC_KEY,
+ process.env.VAPID_PRIVATE_KEY
+);
+</pre>
+
+<p>接着,一个模块定义并导出了所有应用需要处理的路由:获取 VAPID 公钥,注册,发送通知等等。你可以看到来自 <code>index.js</code> 的变量在这里被用到:<code>payload</code>, <code>delay</code> 和<code>ttl</code></p>
+
+<pre>module.exports = function(app, route) {
+ app.get(route + 'vapidPublicKey', function(req, res) {
+ res.send(process.env.VAPID_PUBLIC_KEY);
+ });
+
+ app.post(route + 'register', function(req, res) {
+
+ res.sendStatus(201);
+ });
+
+ app.post(route + 'sendNotification', function(req, res) {
+ const subscription = req.body.subscription;
+ const payload = req.body.payload;
+ const options = {
+ TTL: req.body.ttl
+ };
+
+ setTimeout(function() {
+ webPush.sendNotification(subscription, payload, options)
+ .then(function() {
+ res.sendStatus(201);
+ })
+ .catch(function(error) {
+ console.log(error);
+ res.sendStatus(500);
+ });
+ }, req.body.delay * 1000);
+ });
+};</pre>
+
+<h4 id="service-worker.js">service-worker.js</h4>
+
+<p>最后我们要看得文件是service worker。</p>
+
+<pre>self.addEventListener('push', function(event) {
+ const payload = event.data ? event.data.text() : 'no payload';
+ event.waitUntil(
+ self.registration.showNotification('ServiceWorker Cookbook', {
+ body: payload,
+ })
+ );
+});</pre>
+
+<p>它做的所有事就是监听 {{event("push")}} 事件,创建payload 变量,这个变量包含了来自event.data的文本(如果data是空的,则会新建一个‘no payload’字符串),然后一直等到通知推送给用户为止。</p>
+
+<p>如果你想知道他们具体是怎么处理的,请随意探索<a href="https://serviceworke.rs/">Service Worker Cookbook</a> 里面剩下的例子—— <a href="https://github.com/mozilla/serviceworker-cookbook/">Github上面提供了完整的源代码</a>。那里有大量的示例展示了各种用法,包括web推送,缓存策略,性能,离线运行等等 。</p>
+
+<p>{{PreviousMenuNext("Web/Apps/Progressive/Installable_PWAs", "Web/Apps/Progressive/Loading", "Web/Apps/Progressive")}}</p>
+
+<div>{{QuickLinksWithSubpages("/en-US/docs/Web/Progressive_web_apps/")}}</div>
diff --git a/files/zh-cn/web/progressive_web_apps/responsive/index.html b/files/zh-cn/web/progressive_web_apps/responsive/index.html
new file mode 100644
index 0000000000..f357c6f81b
--- /dev/null
+++ b/files/zh-cn/web/progressive_web_apps/responsive/index.html
@@ -0,0 +1,77 @@
+---
+title: Responsive design
+slug: Web/Progressive_web_apps/Responsive
+tags:
+ - Media Queries
+ - PWA
+ - Progressive web apps
+ - Responsive web design
+ - viewport
+translation_of: Web/Progressive_web_apps/Responsive/responsive_design_building_blocks
+---
+<div class="column-container summary">
+<div class="column-11"><span class="seoSummary">响应式Web应用使用媒体查询和viewport等技术,以确保它们的页面适配任何设备,比如:桌面、移动手机、平板,或者其他新的设备。</span></div>
+
+<div class="column-1"><img alt="" src="https://mdn.mozillademos.org/files/12650/responsive.svg"></div>
+</div>
+
+<h2 id="核心指南">核心指南</h2>
+
+<dl>
+ <dt><a href="/en-US/docs/Web/Apps/Modern/Responsive/responsive_design_building_blocks">The building blocks of responsive design</a></dt>
+ <dd>Learn the basics of responsive design, an essential topic for modern app layout.</dd>
+ <dt><a href="/en-US/docs/Web/Apps/Modern/Responsive/Mobile_first">Mobile first</a></dt>
+ <dd>Often when creating responsive application layouts, it makes sense to create the mobile layout as the default, and build wider layouts on top.</dd>
+</dl>
+
+<h2 id="技术">技术</h2>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">Technology</th>
+ <th scope="col">Description</th>
+ <th scope="col">Support summary</th>
+ <th scope="col">Latest spec</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><a href="/en-US/docs/Web/CSS/Media_Queries">Media queries</a></td>
+ <td>Defines expressions allowing styling to be selectively applied to content depending on viewport size, resolution, orientation, etc.</td>
+ <td>Widespread across modern browsers (<a href="/en-US/docs/Web/CSS/Media_Queries#Browser_compatibility">more detail</a>)</td>
+ <td><a href="https://drafts.csswg.org/mediaqueries-4/">Media Queries Level 4</a></td>
+ </tr>
+ <tr>
+ <td><a href="/en-US/docs/Web/CSS/@viewport">@viewport</a>/<a href="/en-US/docs/Mozilla/Mobile/Viewport_meta_tag">viewport meta tag</a></td>
+ <td>Controls viewport settings, primarily on mobile devices.</td>
+ <td>@viewport: Experimental (<a href="/en-US/docs/Web/CSS/@viewport#Browser_compatibility">more detail</a>)<br>
+ Viewport meta tag: Widespread across modern mobile devices</td>
+ <td><a href="https://drafts.csswg.org/css-device-adapt/#the-atviewport-rule">CSS Device Adaptation Module Level 1</a></td>
+ </tr>
+ <tr>
+ <td><a href="/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout">Flexbox</a></td>
+ <td>A very useful CSS feature for creating flexible, responsive layouts.</td>
+ <td>Widespread across modern browsers (<a href="/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout#Browser_compatibility">more detail</a>)</td>
+ <td><a href="https://drafts.csswg.org/css-flexbox-1/">CSS Flexible Box Layout Module Level 1</a></td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="工具">工具</h2>
+
+<dl>
+ <dt><a href="https://modernizr.com/">Modernizr</a></dt>
+ <dd>A feature detection library for applying different CSS and JS to your page depending on whether certain CSS/JS features are supported.</dd>
+ <dt><a href="https://code.google.com/archive/p/css3-mediaqueries-js/">css3-mediaqueries-js</a></dt>
+ <dd>A JavaScript polyfill to add Media Query support to old versions of IE (5+.)</dd>
+</dl>
+
+<h2 id="参见">参见</h2>
+
+<dl>
+ <dt><a href="/en-US/docs/Web/Apps/Modern/Responsive/Graphics_for_responsive_sites">Graphics for responsive sites</a></dt>
+ <dd>Ideas to keep in mind when designing graphics for responsive sites and applications.</dd>
+ <dt><a href="/en-US/docs/Web/Apps/Modern/Responsive/Responsive_navigation_patterns">Responsive navigation patterns</a></dt>
+ <dd>How do you make a UI that looks and works as great on a smartphone as it does on the desktop? Learn how to design UIs that change to fit your user's screen.</dd>
+</dl>
diff --git a/files/zh-cn/web/progressive_web_apps/responsive/responsive_design_building_blocks/index.html b/files/zh-cn/web/progressive_web_apps/responsive/responsive_design_building_blocks/index.html
new file mode 100644
index 0000000000..0c644f8724
--- /dev/null
+++ b/files/zh-cn/web/progressive_web_apps/responsive/responsive_design_building_blocks/index.html
@@ -0,0 +1,421 @@
+---
+title: 响应式设计的基础
+slug: Web/Progressive_web_apps/Responsive/responsive_design_building_blocks
+translation_of: Web/Progressive_web_apps/Responsive/responsive_design_building_blocks
+---
+<div class="summary">
+<p>在本文中,我们将讨论响应式设计的主要基本组成部分,并在必要时提供一些指向更多信息的链接。</p>
+</div>
+
+<p>For Web developers, it is now fairly common to be called upon to create a Web site or app that changes its user interface depending on the browser or device accessing the site to provide an optimized experience. One approach to this is to create different versions of your site/app for different platforms or browsers and serve them appropriately after detecting which browser or platform is looking at your site. But this is increasingly inefficient: browser sniffing is inherently error prone, and maintaining multiple copies of your code can turn out to be a nightmare.</p>
+
+<p>It is usually much better to create a single version of your code which doesn't care about what browser or platform is accessing the site, but instead uses feature tests to find out what code features the browser supports or what the values of certain browser features are, and then adjusts the code appropriately. This tends to be termed <strong>responsive design</strong> or <strong>adaptive design</strong>, two related but different approaches. For a discussion on the differences between the two, read <a href="/en-US/docs/Web/Apps/app_layout/Responsive_design_versus_adaptive_design" title="/en-US/docs/Web/Apps/app_layout/Responsive_design_versus_adaptive_design">Responsive design versus adaptive design</a>.</p>
+
+<p>This is much more reliable, more maintainable, and more future proof. You don't get caught in the situation of having to bring out more new site versions as more new browsers and platforms come out, and adjust code as feature support in existing browsers changes.</p>
+
+<p>There are disadvantages to this approach as well. If the content, layout, and functionality need to change greatly for different devices, it may not be such a good approach. Also, taking an existing site and adding responsiveness to it, to make it mobile/tablet friendly, can be a lot more effort than just creating a separate mobile site or app, especially if it is a sprawling enterprise site. Read more about <a href="/en-US/docs/Web_Development/Mobile/Responsive_design" title="/en-US/docs/Web_Development/Mobile/Responsive_design">responsive design advantages and disadvantages</a>.</p>
+
+<div class="note">
+<p>You can also read our discussion on the basics of <a href="/en-US/docs/Web_Development/Mobile/Responsive_design">responsive design</a>, if you need some more background information and basics.</p>
+</div>
+
+<h2 id="Fluid_grids">Fluid grids</h2>
+
+<p>The best place to start is with fluid measurements for our application layout — essentially, this means using a combination of percentages and ems/rems to size your containers and text, not fixed widths such as pixels. This has a lot of advantages in that the layout will adapt to different viewport dimensions. Let's look at an example.</p>
+
+<p>We've written a simple-but-fun prototype for an application called Snapshot, which takes a video stream from your webcam (using {{domxref("navigator.getUserMedia", "getUserMedia()")}}) then allows you to capture stills from that video stream (using HTML5 {{HTMLElement("canvas")}}), and save them to a gallery. You can then view previously-captured images and delete them. Other articles will discuss the functionality in more detail, but here we're interested in the layout.</p>
+
+<div class="note">
+<p><strong>Note:</strong> You can find the <a href="https://github.com/chrisdavidmills/snapshot" title="https://github.com/chrisdavidmills/snapshot">Snapshot app on Github</a>; check out the code and help improve it. You can also see <a href="https://chrisdavidmills.github.io/snapshot/" title="https://chrisdavidmills.github.io/snapshot/">Snapshot running live</a>. Note that <code>getUserMedia()</code> is an experimental technology, which currently only works in Google Chrome and Firefox desktop. More functionality and a clean up of the styling of Snapshot are planned for a future date.</p>
+</div>
+
+<p>Our desktop layout for Snapshot is three columns, containing the camera viewer, image capture view, and gallery, respectively.</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/5917/desktop-layout.png" style="display: block; height: 184px; margin: 0px auto; width: 600px;"></p>
+
+<p>The markup is very simple:</p>
+
+<pre class="brush: html notranslate">&lt;x-deck selected-index="0"&gt;
+ &lt;x-card&gt;
+ …
+ &lt;/x-card&gt;
+ &lt;x-card&gt;
+ …
+ &lt;/x-card&gt;
+ &lt;x-card&gt;
+ …
+ &lt;/x-card&gt;
+&lt;/x-deck&gt;</pre>
+
+<div class="note">
+<p><strong>Note:</strong> These weird x- elements may be unfamiliar; they are part of <a href="http://mozbrick.github.io/" title="http://mozilla.github.io/brick/">Brick</a>, Mozilla's UI element library for mobile web apps. We have used Brick to create the mobile layout for Snapshot, which you will read more about below.</p>
+</div>
+
+<p>To get these sitting side-by-side we have used the following rules:</p>
+
+<pre class="brush: css notranslate">x-card {
+ width: 100%;
+}
+
+x-card:nth-child(1), x-card:nth-child(2) {
+ width: 30%;
+ float: left;
+ padding: 2rem;
+}
+
+x-card:nth-child(3) {
+ width: 40%;
+ float: left;
+ height: 100%;
+ overflow: auto;
+ padding: 2rem;
+}</pre>
+
+<p>So we're giving the first two columns a {{cssxref("width")}} of <code>30%</code>, and the third a <code>width</code> of <code>40%</code>, floating the columns all left. This way they end up side-by-side, and their proportions remain the same as the browser window size varies. This is just a simple grid example, but you can apply this principle to more complex grid layouts as required.</p>
+
+<h3 id="border-box_sizing">border-box sizing</h3>
+
+<p>The padding does not affect the overall width and height of the containers because we have set the {{cssxref("box-sizing")}} of all elements to <code>border-box</code>:</p>
+
+<pre class="brush: css notranslate">*, *:before, *:after {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}</pre>
+
+<p>This basically means that {{cssxref("width")}} and {{cssxref("height")}} will now set the dimensions of an element all the way up to and including the border, not just the content. So if you set <code>width: 40%</code>, the box width will always be <code>40%</code> of its parent, and any {{cssxref("padding")}} and {{cssxref("border")}} widths set on the box will be subtracted from the content width, not added to it. Very useful! Read more about this at <a href="http://www.paulirish.com/2012/box-sizing-border-box-ftw/" title="http://www.paulirish.com/2012/box-sizing-border-box-ftw/">* { Box-sizing: Border-box } FTW</a>, by Paul Irish.</p>
+
+<h2 id="Flexible_replaced_elements">Flexible replaced elements</h2>
+
+<p>Things are working fairly well now, but there are still some issues just waiting to present themselves. For a start, let's have a look at what happens when we include the {{HTMLElement("video")}} and {{HTMLElement("img")}} elements inside our first two columns, naked and unstyled.</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/5913/broken-images.png" style="display: block; height: 316px; margin: 0px auto; width: 480px;"></p>
+
+<p>Because the size of replaced elements is dictated by the size of the media inserted into them, and the media is a fixed size, they explode out of their containing elements and make a mess of the layout. This is pretty horrible, but generally this kind of problem is easily fixed with some simple CSS:</p>
+
+<pre class="brush: css notranslate">img, video {
+ max-width: 100%;
+}</pre>
+
+<p>This tells the replaced elements to remain constrained inside their container's widths, no matter what. However, if they aren't as wide as their containers, they will not stretch to fill them. In the snapshot example, we ended up with slightly different code:</p>
+
+<pre class="brush: css notranslate">x-card:nth-child(1) video, x-card:nth-child(2) img {
+ width: 100%;
+ …
+}</pre>
+
+<p>This is because in our case, we do in fact want the video and image to stretch to always fill their containers no matter what — a subtle but important difference from {{cssxref("max-width")}} — and therefore always be the same size. The video always resizes dynamically, but the screen captures taken from it do not, so upon resizing the screen it was possible to end up with a messy layout with different sized elements when using <code>max-width: 100%</code>, such as:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/5915/broken-max-width-layout.png" style="display: block; height: 220px; margin: 0px auto; width: 480px;"></p>
+
+<h2 id="Media_queries">Media queries</h2>
+
+<p>Fluid grids are a great start, but you'll notice that at certain points (known as breakpoints) the layout starts to break down. At these points you'll want to change the layout to rectify the layout problem, and this can be done using media queries.</p>
+
+<div class="note">
+<p><strong>Note:</strong> Media queries are a CSS3 feature that allow you to selectively apply CSS depending on the results of media feature tests — for more on the basics, read <a href="https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Media_queries">Media queries</a>.</p>
+</div>
+
+<h3 id="Typical_desktop_layout">Typical desktop layout</h3>
+
+<p>In our example, we have a desktop layout, as we've already seen. This is created using the CSS rules included at the top of the stylesheet, before any media queries are encountered.<br>
+ <br>
+ <img alt="" src="https://mdn.mozillademos.org/files/5917/desktop-layout.png" style="display: block; height: 184px; margin: 0px auto; width: 600px;"></p>
+
+<h3 id="Mid-width_layout">Mid-width layout</h3>
+
+<p>We also have a mid-width layout, which is aimed at working well on tablets and narrow laptop screens. This is created by all of the CSS inside the first media query:</p>
+
+<pre class="brush: css notranslate">@media all and (max-width: 1024px) {
+ x-card:nth-child(1), x-card:nth-child(2) {
+ width: 50%;
+ }
+
+ x-card:nth-child(3) {
+ width: 100%;
+ clear: left;
+ }
+
+ x-card:nth-child(3) img {
+ width: 20%;
+ }
+}</pre>
+
+<p>So here we're altering the widths of the columns and removing the float of the third column (and adding clearing to guard against any float funny business). We've also altered the width of the images inside the third container (no longer a column — this is the gallery) so that now you get five per line (it was previously three per line).</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/5919/middle-layout.png" style="display: block; height: 390px; margin: 0px auto; width: 480px;"></p>
+
+<h3 id="Narrow_screenmobile_layout">Narrow screen/mobile layout</h3>
+
+<p>We then have a narrow screen layout, designed to fit the bill for a mobile app/open Web app experience. This is created in multiple parts. First of all, as expected, there is a media query in our main CSS, which is quite weighty, so we'll go through it in parts:</p>
+
+<pre class="brush: css notranslate">@media all and (max-width: 480px) {
+ x-card:nth-child(1), x-card:nth-child(2), x-card:nth-child(3) {
+ width: 100%;
+ float: none;
+ padding: 0;
+ }
+
+ button {
+ margin-top: 0;
+ border-radius: 0;
+ }
+
+ x-card:nth-child(1) video, x-card:nth-child(2) img {
+ border-radius: 0px;
+ border: none;
+ padding: 0;
+ background-color: 0;
+ }</pre>
+
+<p>This first block resets a number of different things from the widescreen layouts that were't required for the mobile app.</p>
+
+<pre class="brush: css notranslate"> x-card:nth-child(1) video, x-card:nth-child(2) img, x-card:nth-child(3) {
+    margin-top: 17.5vw;
+  }
+
+  x-card:nth-child(1) button, x-card:nth-child(2) button {
+    position: absolute;
+    bottom: 0;
+  }
+
+  x-card:nth-child(2) button:nth-of-type(2) {
+    bottom: 5.9rem;
+  }
+
+ x-card:nth-child(1) button {
+ font-size: 7vw;
+ }
+
+ x-card:nth-child(2) button {
+ font-size: 7vw;
+ }</pre>
+
+<p>The next rules do some sizing on the buttons inside the first two cards, and give all card contents a top margin so that their content won't be lost under the navigation buttons (see below). This was necessary because Mozilla Brick (also see below) forces its components to be 100% of the screen width and height. We have used <code>vw</code> (viewport width) units for these — <code>1vw</code> is equivalent to  1% of the viewport width. This makes the dimensions scale up and down nicely along with the viewport width. Last for this section, we absolutely positioned all buttons at the bottom of the cards they are in, so the layout looks OK at different viewport size variations. We then add a rule that positions the second button in any card a button's width higher up the card. When you click on an image in the gallery it brings up options to delete or cancel deletion of the card, and you don't want two buttons on top of one another.</p>
+
+<pre class="brush: css notranslate">x-card:nth-child(3) img {
+ width: 50%;
+}</pre>
+
+<p>This rule simply changes the width of the gallery images so now there are two per line.</p>
+
+<pre class="brush: css notranslate">  nav {
+ width: 100%;
+    position: absolute;
+    z-index: 1000;
+
+    display: -webkit-flex;
+    display: -moz-flex;
+    display: -ms-flexbox;
+    display: flex;
+  }
+
+  nav button {
+    font-size: 6.8vw;
+
+    -webkit-flex: 1;
+    -moz-flex: 1;
+    -ms-flex: 1;
+    flex: 1;
+
+    border-left: 1px solid rgba(100,100,100,0.4);
+  }
+
+  nav button:first-child {
+    border-left: 0;
+  }
+}</pre>
+
+<p>In this last set of rules, we change the display value of the {{HTMLElement("nav")}} to <code>flex</code> to make it show (it was set to <code>none</code> in the default CSS at the top of the stylesheet, as it wasn't needed for the other views.) We then use absolute positioning and {{cssxref("z-index")}} to make it take up no space in the document flow,  and sit on top of the x-cards (this is why we gave the x-cards that top-margin earlier).</p>
+
+<p>Next up, the <code>font-size</code> of the buttons is set to <code>6.8vw</code>. Why? Because the top-margin of the x-cards was set to <code>17vw</code> earlier on. All buttons in the app have been set to have a <code>line-height</code> of 2.5, in the default CSS at the top of the stylesheet (check if you don't believe me.) And 6.8 x 2.5 = 17.</p>
+
+<p>Last, we have used <code>flex: 1;</code> to make the buttons always take up the same proportion of space on the line. Let's have a look at the mobile layout, in the below image.</p>
+
+<p><img alt="single column layout for mobile app view, with three buttons to navigate between cards, an image viewer, and a Save Picture button at the button." src="https://mdn.mozillademos.org/files/5939/mobile-layout.png" style="float: left; height: 417px; margin-right: 2rem; width: 304px;">But there are more tricks up our sleeves for this mobile app layout! As mentioned above, we used <a href="http://mozilla.github.io/brick/" title="http://mozilla.github.io/brick/">Mozilla Brick</a>, a collection of ready-rolled mobile UI components, in the making of the mobile app layout. In particular, we used the <a href="http://mozilla.github.io/brick/docs.html#deck" title="http://mozilla.github.io/brick/docs.html#deck">deck</a> component for the nice transition effect between cards when the buttons are pressed. For more on using Brick, read <a href="/en-US/docs/Web/Apps/app_layout/Mozilla_Brick_ready_made_UI_components" title="/en-US/docs/Web/Apps/app_layout/Mozilla_Brick_ready_made_UI_components">Mozilla Brick: ready made UI components</a>.</p>
+
+<p>What's more relevant to this article is that we didn't want the Brick CSS and JavaScript files being applied to the markup unless we were looking at the mobile app view. To achieve this, we applied the Brick CSS to the page using a separate {{HTMLElement("link")}} element with a <code>media</code> attribute:</p>
+
+<pre class="brush: html notranslate">&lt;link href="dist/brick.css" type="text/css" rel="stylesheet" media="all and (max-width: 480px)"&gt;</pre>
+
+<p>This says that the whole stylesheet will not be linked to the HTML unless the viewport width is 480px or less. Moving on to the JavaScript, {{HTMLElement("script")}} elements don't accept <code>media</code> attributes, so I had to do this a different way. Fortunately there is a JavaScript construct called {{domxref("window.matchMedia()")}}, which can conditionally run JavaScript constructs depending on whether a media query returns <code>true</code> or not. We opened up the <code>brick.js</code> file and wrapped the whole lot in the following:</p>
+
+<pre class="brush: js notranslate">if (window.matchMedia("(max-width: 480px)").matches) {
+ // The whole of brick.js goes here!
+}</pre>
+
+<p>This causes nothing inside the <code>brick.js</code> file to be run unless the viewport width is 480px or less. Problem solved.</p>
+
+<h3 id="Really_wide_screens">Really wide screens</h3>
+
+<p>One thing you might notice is that when the viewport gets very wide (such as on a cinema display), the layout stops getting wider, and just centers in the space available. This is pretty simple to achieve. You could use a <code>min-width</code> media query to fix the {{HTMLElement("body")}} width at a certain point:</p>
+
+<pre class="brush: css notranslate">@media all and (min-width: 1400px) {
+ body {
+ width: 1400px;
+ margin: 0 auto;
+ }
+}</pre>
+
+<p>But it's actually easier to just set the following rule instead, and get rid of the media query altogether:</p>
+
+<pre class="brush: css notranslate">body {
+ max-width: 1400px;
+ margin: 0 auto;
+}</pre>
+
+<h3 id="Orientation_fail">Orientation fail</h3>
+
+<p>We also came across some problems with orientation: the mobile-app layout of our example app is designed for portrait orientation, and looks terrible when viewed on a device in landscape orientation. To fix this, we added in a media query that only applies its contents to the markup when device is viewed in landscape orientation:</p>
+
+<pre class="brush: css notranslate">@media all and (max-width: 480px) and (orientation: landscape) {
+ nav {
+    width: auto;
+
+    -webkit-flex-direction: column;
+    -moz-flex-direction: column;
+    -ms-flex-direction: column;
+    flex-direction: column;
+  }
+
+  nav button {
+    font-size: 6.8vh;
+  }
+
+  nav button {
+    border-left: 0;
+  }
+
+  x-card:nth-child(1) video, x-card:nth-child(2) img, x-card:nth-child(3) {
+    margin-top: 0;
+  }
+
+  x-card:nth-child(1) button, x-card:nth-child(2) button {
+    font-size: 2rem;
+  }
+}</pre>
+
+<p>This does the following:</p>
+
+<ul>
+ <li>Adjusts the nav buttons, changing the direction the flexbox is laid out in, and altering the font size and borders so they sit vertically instead of horizontally.</li>
+ <li>Removes the top margin from the x-card contents so you don't end up with an unsightly gap at the top of the screen in landscape mode.</li>
+ <li>Changes the sizing of the control buttons (e.g. <em>Take Picture</em>, <em>Delete Photo</em>) so they don't look too big and sit properly on the screen.</li>
+</ul>
+
+<p>This results in the following layout:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/5923/viewport-fail-fixed.png" style="display: block; height: 320px; margin: 0px auto; width: 479px;"></p>
+
+<div class="note">
+<p><strong>Note</strong>: Another solution with respect to orientation might be to just lock the orientation of your app, to portrait or landscape. If you are working on an installed app, you can easily do this with the <a href="https://developer.mozilla.org/en-US/Apps/Build/Manifest#orientation">orientation manifest field</a>. If you want a solution that works across general web apps, you could use the <a href="https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model/Managing_screen_orientation#Locking_the_screen_orientation">Screen orientation API</a>, and/or provide a message asking the user to rotate their screen if they are using the wrong orientation (for example, if <code>window.innerWidth</code> is larger than <code>window.innerHeight</code>, assume the<br>
+ game is landscape mode and show a "please rotate" message.)</p>
+</div>
+
+<h2 id="Viewport">Viewport</h2>
+
+<p>One last problem to mention for our example app is concerned with mobile browsers and media queries. If we viewed my example in a mobile browser in its current state, we wouldn't see our nice mobile layout. Instead, we'd see the below image.</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/5925/viewport-fail.png" style="float: left; height: 569px; margin-right: 2rem; width: 320px;">I'm sure you'll agree that this really isn't what we wanted — why is this happening? In short, mobile browsers lie. They don't render web pages at their true viewport width. Instead, they render pages at a higher assumed viewport width (something approaching a laptop screen), and then shrink the result down to fit inside the mobile screen. This is a sensible defensive mechanism — most old school sites that don't have media queries would look terrible when rendered at say, 320px or 480px wide. But this doesn't help us responsible web developers, who have written small screen layouts into our CSS using media queries and want mobile devices to display those!</p>
+
+<p>There is a way to override this mobile rendering behavior — viewport, which is inserted into our HTML pages in the form of a {{HTMLElement("meta")}} tag. In my example, let's add the following into our HTML {{HTMLElement("head")}}:</p>
+
+<pre class="notranslate">&lt;meta name="viewport" content="width=480"&gt;</pre>
+
+<p>This causes our browser to render our mobile app layout properly — <code>width=480</code> tells the browser <em>"render this markup at 480 pixels wide"</em>, hence the media queries kick in appropriately. There are many more options available in the viewport meta tag, which you can read about in <a href="/en-US/docs/Mozilla/Mobile/Viewport_meta_tag" title="/en-US/docs/Mozilla/Mobile/Viewport_meta_tag">Using the viewport meta tag to control layout on mobile browsers</a>.</p>
+
+<div class="note">
+<p><strong>Note:</strong> There is a spec called <a href="http://dev.w3.org/csswg/css-device-adapt/" title="http://dev.w3.org/csswg/css-device-adapt/">device adaptation</a>, which defines the same functionality but in CSS, using a <code>@viewport</code> at-rule. This is probably a more logical place to put such information, but the spec is not as well supported as the viewport meta tag, therefore you should stick with that for now.</p>
+</div>
+
+<h2 class="cleared" id="Responsive_imagesvideo">Responsive images/video</h2>
+
+<p>Another problem that comes up more and more these days is making image/video weight (size in KB) responsive as well as the dimensions of the image on screen. Yes, you want the images to be contained inside the app UI whether you are using it on desktop or mobile, but you should also consider that mobile apps have much smaller viewport dimensions available than desktop apps, so you should try to give mobile devices a smaller image to download. Mobiles in general (more commonly in some parts of the world than others) are on lower bandwidth connections and have less memory available than desktop devices, so yes, those extra kilobytes really do count.</p>
+
+<p>Another challenge is dealing with high resolution screens — raster graphics designed for low resolutions are in danger of appearing tiny when displayed on a high resolution screen, so devices often apply a default zoom factor to rendered pages to avoid this. The trouble with this, then, is that raster images are zoomed in and as a result can start to look pixellated.</p>
+
+<h3 id="CSS_background_images">CSS background images</h3>
+
+<p>For CSS background images this is a fairly easy problem to solve. If you use the <a href="/en-US/docs/Web/Apps/app_layout/Mobile_first" title="/en-US/docs/Web/Apps/app_layout/Mobile_first">mobile first</a> methodology, you will be creating your mobile layout inside your default CSS, before any media queries have been applied. The media queries then supply CSS that is only applied to the markup when the viewport is <strong>above</strong> a certain width. Let's look at a quick example:</p>
+
+<pre class="brush: css notranslate">header {
+ height: 300px;
+ width: 100%;
+ background: url(images/small-header.jpg) center;
+}
+
+@media all and (min-width: 480px) {
+ header {
+ background: url(images/large-header.jpg) center;
+ }
+}</pre>
+
+<p>This means that mobile browsers only download the mobile background image asset — not the desktop mobile assets — because they fail the media query tests and therefore ignore the media queries. You can also serve a larger graphic to a higher resolution device using a resolution media query, like so:</p>
+
+<pre class="prettyprint prettyprinted notranslate"><code><span class="brush: css">button {
+ background: url(images/low-res-header.jpg) 1rem center ;
+}
+
+@media</span><span class="pln"> only screen </span><span class="kwd">and</span><span class="pln"> </span><span class="pun">(-</span><span class="pln">webkit</span><span class="pun">-</span><span class="pln">min</span><span class="pun">-</span><span class="pln">device</span><span class="pun">-</span><span class="pln">pixel</span><span class="pun">-</span><span class="pln">ratio</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pun">),</span><span class="pln">
+ only screen </span><span class="kwd">and</span><span class="pln"> </span><span>(</span><span class="pln"> min</span><span class="pun">-</span><span class="pln">resolution</span><span class="pun">:</span><span class="pln"> </span><span class="lit">192dpi</span><span class="pun">),</span><span class="pln">
+ only screen </span><span class="kwd">and</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> min</span><span class="pun">-</span><span class="pln">resolution</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2dppx</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
+<code><span class="lit"> button {
+ background: url(images/high-res-header.jpg) 1rem center ;
+ }</span></code> </span><span class="pln">
+</span><span class="pun">}</span></code></pre>
+
+<p>This looks rather complicated, but really it's not — we are providing a number of media query options, as at this time different browsers support different resolution media query types and even units. Brett Jankord has a good explanation at <a href="http://www.brettjankord.com/2012/11/28/cross-browser-retina-high-resolution-media-queries/" title="http://www.brettjankord.com/2012/11/28/cross-browser-retinahigh-resolution-media-queries/">Cross Browser Retina/High Resolution Media Queries</a>.</p>
+
+<h3 id="&lt;video>">&lt;video&gt;</h3>
+
+<p>HTML5 video is fairly well catered for in terms of responsive capabilities. If you wish, you can point to multiple video files via {{HTMLElement("source")}} attributes, each with their own source and MIME type:</p>
+
+<pre class="brush: html notranslate">&lt;video controls&gt;
+ &lt;source src="videos/720/crystal720.mp4" type="video/mp4"&gt;
+ &lt;source src="videos/720/crystal720.webm" type="video/webm"&gt;
+&lt;/video&gt;</pre>
+
+<p>But you can go one step further. You can include <code>media</code> attributes on the <code>&lt;source&gt;</code> element containing media queries — the video loaded in the browser will depend on both the format the browser supports, and the results of the media tests. So for example:</p>
+
+<pre class="brush: html notranslate">&lt;video controls&gt;
+ &lt;source src="videos/320/crystal320.mp4" type="video/mp4" media="all and (max-width: 480px)"&gt;
+ &lt;source src="videos/320/crystal320.webm" type="video/webm" media="all and (max-width: 480px)"&gt;
+ &lt;source src="videos/720/crystal720.mp4" type="video/mp4" media="all and (min-width: 481px)"&gt;
+ &lt;source src="videos/720/crystal720.webm" type="video/webm" media="all and (min-width: 481px)"&gt;
+&lt;/video&gt;</pre>
+
+<p>This allows your site to serve different video files based on the available space, in order to optimize the user's experience.</p>
+
+<h3 id="&lt;img>">&lt;img&gt;</h3>
+
+<p>HTML images are a more difficult proposition. There is no mechanism inherent in HTML images for serving different image files dependent on viewport size, and, due to a number of irksome browser behavior realities, solutions are more difficult to hack together than you would imagine. There are currently some standards proposals in the works that would provide this — the W3C <a href="http://www.w3.org/community/respimg/" title="http://www.w3.org/community/respimg/">responsive images community group</a> discussed this problem for ages and arrived at the <a href="http://www.w3.org/TR/html-picture-element/" title="http://www.w3.org/TR/html-picture-element/">&lt;picture&gt;</a> element, which provides a similar markup structure to {{HTMLElement("video")}}<a href="/en-US/docs/Web/HTML/Element/video" title="/en-US/docs/Web/HTML/Element/video">,</a> with {{HTMLElement("source")}} alternatives selectable via media query results. Another proposal, <a href="http://www.w3.org/html/wg/drafts/srcset/w3c-srcset/" title="http://www.w3.org/html/wg/drafts/srcset/w3c-srcset/">srcset</a>, was put forward by Apple and takes a slightly different approach, instead providing a new <code>srcset</code> attribute for {{HTMLElement("img")}} inside which image references are placed along with "hints" that the browser can use to work out which image is most suitable to display given its viewport size, resolution, etc. These are not intended to be mutually exclusive.</p>
+
+<p>This all sounds good. But those solutions are definitely not ready for production yet — both are in a very early stage of standardization, and have no support across browsers. Currently we have to rely on various polyfills and other solutions, none of which are perfect for all situations, so you need to decide which one is right for your particular situation. Some available solutions are as follows:</p>
+
+<dl>
+ <dt><a href="https://github.com/teleject/hisrc" title="https://github.com/teleject/hisrc">HiSRC</a></dt>
+ <dd>A <a href="http://jquery.com" title="http://jquery.com">jQuery</a> plugin that allows you to create small, medium, and large versions of an image, and then serves the appropriate one according to the browser's resolution and available network speed.</dd>
+ <dt><a href="http://www.mobify.com/mobifyjs/v2/docs/capturing/" title="http://www.mobify.com/mobifyjs/v2/docs/capturing/">Mobify.js capturing</a></dt>
+ <dd>A very clever technique from Mozilla that allows you to capture the source of the page before it's parsed. This way, you can swap out image <code>src</code> values with JavaScript depending on browser features, circumventing browser preloading issues. This is promising, but doesn't work very well across older browsers.</dd>
+ <dt><a href="https://github.com/scottjehl/picturefill" title="https://github.com/scottjehl/picturefill">Picturefill</a></dt>
+ <dd>A JavaScript-based polyfill for <code>&lt;picture&gt;</code>, which works nicely, but it does require a lot of custom markup.</dd>
+ <dt><a href="http://adaptive-images.com/" title="http://adaptive-images.com/">Adaptive images</a></dt>
+ <dd>A server-side solution, which records the viewport size in a cookie, then resizes images via a combination of PHP and <code>.htaccess</code> to a more appropriate size, if appropriate. This doesn't require markup or scripting, but has a number of limitations.</dd>
+</dl>
+
+<h3 id="SVG_and_other_vector_graphics">SVG and other vector graphics</h3>
+
+<p>For some image requirements (not photographs, but icons and user interface elements are a good fit), a good solution is to use vector graphics. Because vector images are calculated based on mathematical algorithms rather than containing separate data on every pixel in the image, they tend to be smaller in file size, and are infinitely scalable when zoomed or viewed on high resolution devices (at least, in theory). Some ideas follow, which also help to keep the number of HTTP requests down — another key factor in mobile app performance:</p>
+
+<ul>
+ <li>You should try to use <a href="/en-US/docs/Web/CSS/CSS3" title="/en-US/docs/Web/CSS/CSS3">CSS3</a> features to programmatically generate graphical effects where possible, rather than relying on image files. these include rounded corners, gradients, and drop shadows. These scale as the resolution changes or the browser zooms. Although they are not supported very well on older browsers such as Internet Explorer 6-8, this is not too much of a concern when you are creating an interface aimed at modern devices, and they also tend to gracefully degrade.</li>
+ <li>You could also try using <a href="/en-US/docs/Web/SVG" title="/en-US/docs/Web/SVG">SVG</a> to create interface elements. SVG produces vector graphics and is supported well across modern browsers, with polyfills available for older browser support.</li>
+ <li>Using <a href="/en-US/docs/Web/CSS/@font-face" title="/en-US/docs/Web/CSS/@font-face">Web fonts</a> for displaying icons is an effective technique for keeping file size and HTTP requests down, and this is supported well across modern and older browsers.</li>
+</ul>
+
+<h2 id="See_also">See also</h2>
+
+<ul>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Web_Development" title="https://developer.mozilla.org/en-US/docs/Web_Development">Web development</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Web/Guide/Mobile" title="https://developer.mozilla.org/en-US/docs/Web/Guide/Mobile">Mobile Web development</a></li>
+</ul>
diff --git a/files/zh-cn/web/progressive_web_apps/优势/index.html b/files/zh-cn/web/progressive_web_apps/优势/index.html
new file mode 100644
index 0000000000..815ccbea1b
--- /dev/null
+++ b/files/zh-cn/web/progressive_web_apps/优势/index.html
@@ -0,0 +1,57 @@
+---
+title: 渐进式webApp优势
+slug: Web/Progressive_web_apps/优势
+translation_of: Web/Progressive_web_apps/Introduction#Advantages_of_web_applications
+---
+<p class="summary">以下是渐进式webApp所有的优势清单</p>
+
+<h2 id="Discoverable">Discoverable<img alt="" src="https://mdn.mozillademos.org/files/12654/discoverable.svg" style="float: right; height: 40px; width: 38px;"></h2>
+
+<p>The eventual aim is that web apps should have better representation in search engines, be easier to expose, catalog and rank, and have metadata usable by browsers to give them special capabilities.</p>
+
+<p>Some of the capabilities have already been enabled on certain web-based platforms by proprietary technologies like <a href="http://ogp.me/">Open Graph</a>, which provides a format for specifying similar metadata in the HTML <code>&lt;head&gt;</code> using meta tags.</p>
+
+<p>The relevant web standard here is the <a href="/en-US/docs/Web/Manifest">Web app manifest</a>, which defines features of an app such as name, icon, splash screen, and theme colors in a JSON-formatted manifest file. This is for use in contexts such as app listings and device home screens.</p>
+
+<ul>
+</ul>
+
+<h2 id="Installable">Installable<img alt="" src="https://mdn.mozillademos.org/files/12656/installable.svg" style="float: right; height: 40px; width: 38px;"></h2>
+
+<p>A core part of the apps experience is for users to have app icons on their home screen, and be able to tap to open apps into their own native container that feels nicely integrated with the underlying platform.</p>
+
+<p>Modern web apps can have this native app feel via properties set inside the Web app manifest, and via a feature available in modern smartphone browsers called <a href="/en-US/docs/Web/Apps/Progressive/Add_to_home_screen">Add to home screen</a>.</p>
+
+<h2 id="Linkable">Linkable<img alt="" src="https://mdn.mozillademos.org/files/12658/linkable.svg" style="float: right; height: 40px; width: 38px;"></h2>
+
+<p>One of the most powerful features of the Web is to be able to link to an app at a specific URL — no app store needed, no complex installation process. This is how it has always been.</p>
+
+<h2 id="Network_independent">Network independent<img alt="" src="https://mdn.mozillademos.org/files/12660/network-independent.svg" style="float: right; height: 40px; width: 38px;"></h2>
+
+<p>Modern web apps can work when the network is unreliable, or even non-existent. The basic ideas behind network independence are to be able to:</p>
+
+<ul>
+ <li>Revisit a site and get its contents even if no network is available.</li>
+ <li>Browse any kind of content the user has previously visited at least once, even under situations of poor connectivity.</li>
+ <li>Control what is shown to the user in situations where there is no connectivity.</li>
+</ul>
+
+<p>This is achieved by a combination of technologies — <a href="/en-US/docs/Web/API/Service_Worker_API">Service Workers</a> to control page requests (for example storing them offline), the <a href="/en-US/docs/Web/API/Cache">Cache API</a> for storing responses to network requests offline (very useful for storing site assets), and client-side data storage technologies such as <a href="/en-US/docs/Web/API/Web_Storage_API">Web Storage</a> and <a href="/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a> to store application data offline.</p>
+
+<h2 id="Progressive">Progressive<img alt="" src="https://mdn.mozillademos.org/files/12662/progressive.svg" style="float: right; height: 40px; width: 38px;"></h2>
+
+<p>Modern web apps can be developed to provide a super cool experience to fully capable browsers, and an acceptable (although not quite as shiny) experience to less capable browsers. We've been doing this for years with best practices such as <a href="/en-US/docs/Glossary/Progressive_Enhancement">progressive enhancement</a>.</p>
+
+<h2 id="Re-engageable">Re-engageable<img alt="" src="https://mdn.mozillademos.org/files/12666/re-engageable.svg" style="float: right; height: 40px; width: 38px;"></h2>
+
+<p>One major advantage of native platforms is the ease with which users can be re-engaged by updates and new content, even when they aren't looking at the app or using their devices. Modern web apps can now do this too, using new technologies such as <a href="/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers">Service Workers</a> for controlling pages, the <a href="/en-US/docs/Web/API/Push_API">Web Push API</a> for sending updates straight from server to app via a service worker, and the <a href="/en-US/docs/Web/API/Notifications_API">Notifications API</a> for generating system notifications to help engage users when they're not in the browser.</p>
+
+<h2 id="Responsive">Responsive<img alt="" src="https://mdn.mozillademos.org/files/12650/responsive.svg" style="float: right; height: 40px; width: 38px;"></h2>
+
+<p>Responsive web apps use technologies like media queries and viewport to make sure that their UIs will fit any form factor: desktop, mobile, tablet, or whatever comes next.</p>
+
+<h2 id="Safe">Safe<img alt="" src="https://mdn.mozillademos.org/files/12664/safe.svg" style="float: right; height: 47px; width: 38px;"></h2>
+
+<p>The web platform provides a secure delivery mechanism that prevents snooping and ensures content hasn’t been tampered with — as long as you take advantage of HTTPS and develop your apps with security in mind. In addition, you can verify the true nature of a PWA by confirming that it is at the correct URL, whereas apps in apps stores can often look like one thing, but be another (<a href="https://twitter.com/andreasbovens/status/926965095296651264">example</a>).</p>
+
+<p> </p>
diff --git a/files/zh-cn/web/progressive_web_apps/加载/index.html b/files/zh-cn/web/progressive_web_apps/加载/index.html
new file mode 100644
index 0000000000..7f45a3c278
--- /dev/null
+++ b/files/zh-cn/web/progressive_web_apps/加载/index.html
@@ -0,0 +1,152 @@
+---
+title: 渐进式加载
+slug: Web/Progressive_web_apps/加载
+tags:
+ - PWA
+ - 渐进式加载
+translation_of: Web/Progressive_web_apps/Loading
+---
+<div>{{PreviousMenu("Web/Apps/Progressive/Re-engageable_Notifications_Push", "Web/Apps/Progressive")}}</div>
+
+<p class="summary">在前面的文章我们介绍了很多api,例如:<a href="en-US/docs/Web/Apps/Progressive/Offline_Service_workers">Service Workers</a>, <a href="/en-US/docs/Web/Apps/Progressive/Installable_PWAs">Web Manifests</a>, <a href="/en-US/docs/Web/Apps/Progressive/Re-engageable_Notifications_Push">Notifications and Push</a>,它让我们的示例应用 <a href="https://mdn.github.io/pwa-examples/js13kpwa/">js13kPWA</a> 成为一个渐进式web应用。在这篇文章里我们将会走得更远,我们会使用资源渐进式加载来提高整个应用的性能。</p>
+
+<h2 id="首次有效渲染">首次有效渲染</h2>
+
+<p>尽快把有效的信息输送给用户是一件非常重要的事情 —— 等待页面加载的时间越长,用户在页面加载完成之前离开的概率就越大。为了达到这个目的,网页加载完成前,我们应该用占位符在最终资源将会加载的地方展示最起码的视图骨架。</p>
+
+<p>这个功能可以用渐进式加载来实现 —— 它也被称为 <a href="https://en.wikipedia.org/wiki/Lazy_loading">懒加载</a>。它的做法是延迟加载尽可能多的资源(HTML, CSS, JavaScript),只有在用户第一次使用到它的时候,它才会被立刻加载。</p>
+
+<h2 id="打包还是拆分">打包还是拆分</h2>
+
+<p>大部分的用户不会用到一个网站的所有页面,但我们通常的做法却是把我们所有的功能都打包进一个很大的文件里面。一个 <code>bundle.js</code> 文件的大小可能会有几个M,一个打包后的<code>style.css</code> 会包含一切东西,从CSS结构定义到各个版本的网站的样式:移动端,平板,桌面,打印等等。</p>
+
+<p>通常来说只加载一个打包后大的文件会比加载很多个小文件要快一些,但如果用户并不是一开始就需要所有的资源,我们就可以首先加载那些关键的资源,其他的等到我们需要的时候,我们再去加载它。</p>
+
+<h2 id="导致页面阻塞的资源(Render-blocking_resources)">导致页面阻塞的资源(Render-blocking resources)</h2>
+
+<p>将所有文件打包是一种不好的做法,因为浏览器把计算结果渲染到屏幕之前,需要先把HTML,CSS和JavaScript下载下来。在页面被打开到页面加载完成的这段时间里,用户将会看到一个空白的页面,这无疑是一个非常糟糕的体验。</p>
+
+<p>为了解决这个问题,举个例子,我们可以在script标签上面加上一个 <code>defer</code></p>
+
+<pre class="brush: html">&lt;script src="app.js" defer&gt;&lt;/script&gt;
+</pre>
+
+<p>他们会等到文档解析完成之后再开始下载和执行,所以他们不会阻塞HTML页面的渲染。我们还可以拆分css文件并给它们加上media属性:</p>
+
+<pre class="brush: html">&lt;link rel="stylesheet" href="style.css"&gt;
+&lt;link rel="stylesheet" href="print.css" media="print"&gt;
+</pre>
+
+<p> 这种做法告诉浏览器只有在条件满足的情况下才加载这些资源(例如指定了print,则在打印环境下才会加载这些资源,译者注)。</p>
+
+<p>在我们这个js13kPWA应用里面,由于CSS非常的简单,因此所有样式都被放到一个文件里面,并没有具体的规则来指导它如何加载css。但我们可以做得更好,例如把 <code>style.css</code> 里面的所有内容移动到一个 <code>&lt;style&gt;</code> 标签里面,并把它放到 <code>index.html</code>  的 <code>&lt;head&gt;</code> 的里面。这样做可以进一步提高应用的性能,但为了使代码更具可读性,我们并没有选择这么做。</p>
+
+<h2 id="图片">图片</h2>
+
+<p>除了JavaScript和CSS,网站通常还会包含大量的图片。当你把{{htmlelement("img")}}元素添加到网站里面时,对应的所有图片资源都会在页面初始化时被下载下来。在网站就绪之前下载几个兆的图片资源是一种比较常见的状况,但它会再次给人一种性能不好的印象。我们并不需要在一打开网站的时候就以最高的画质呈现所有的图片。</p>
+
+<p>这是可以优化的。首先,你可以使用类似<a href="https://tinypng.com/">TinyPNG</a>这类工具或者服务,它可以在不过分降低画质的情况下压缩文件的大小。如果你已经做到了这一点,那你可以考虑一下如何通过JavaScript来优化图片的下载了,我们将会在下面的篇幅提到这些内容。</p>
+
+<h3 id="图片占位符">图片占位符</h3>
+
+<p>我们可以通过使用JavaScript有选择的加载图片,而不是把所有的游戏截图都放到 <code>&lt;img&gt;</code> 标签的 <code>src</code> 属性里面,因为那样浏览器会自动下载所有的图片。 js13kPWA示例在图片最终加载之前会将图片的最终路径存放到 <code>data-src</code> 中,在这个阶段,应用会使用图片占位符来代替真正的图片,它体积更小更轻量级(加载也更快,译者注)。</p>
+
+<pre class="brush: html">&lt;img src='data/img/placeholder.png' data-src='data/img/SLUG.jpg' alt='NAME'&gt;
+</pre>
+
+<p>这些图片会在网站构建完HTML主体框架之后通过JavaScript进行加载。图片占位符被缩放到和真正的图片一样大小,所以它会占据同样的空间,并且在真正的图片完成加载后不会导致页面重绘。</p>
+
+<h3 id="通过JavaScript进行加载">通过JavaScript进行加载</h3>
+
+<p><code>app.js</code> 这个文件处理 <code>data-src</code> 属性的过程如下所示:</p>
+
+<pre class="brush: js">let imagesToLoad = document.querySelectorAll('img[data-src]');
+const loadImages = (image) =&gt; {
+ image.setAttribute('src', image.getAttribute('data-src'));
+ image.onload = () =&gt; {
+ image.removeAttribute('data-src');
+ };
+};</pre>
+
+<p>当函数 <code>loadImages</code> 把地址从  <code>data-src</code> 移动到 <code>src</code> 上时,  <code>imagesToLoad</code> 变量包含了所有图片的链接。当每个图片都已经加载完成时,我们会把<code>data-src</code> 属性移除掉,因为这个时候已经没有任何用处了。我们对遍历了所有的图片来加载这些图片:</p>
+
+<pre class="brush: js">imagesToLoad.forEach((img) =&gt; {
+ loadImages(img);
+});</pre>
+
+<h3 id="用CSS制造模糊">用CSS制造模糊</h3>
+
+<p>为了让整个过程在视觉上更加吸引人,图片占位符的样式用CSS做了模糊处理.</p>
+
+<p><img alt="Screenshot of placeholder images in the js13kPWA app." src="https://mdn.mozillademos.org/files/15992/js13kpwa-placeholders.png" style="height: 684px; width: 675px;"></p>
+
+<p>我们在开始时用模糊来渲染图像,因此可以实现一个从模糊到清晰图像的过渡效果:</p>
+
+<pre class="brush: css">article img[data-src] {
+ filter: blur(0.2em);
+}
+
+article img {
+ filter: blur(0em);
+ transition: filter 0.5s;
+}</pre>
+
+<p>这个样式会在半秒钟内移除模糊效果,它会让“加载”效果看起来更好看:</p>
+
+<h2 id="按需加载">按需加载</h2>
+
+<p>上面讨论的图片加载机制工作得还不错——它在HTML文档加载完成之后再开始加载图片,并且在加载过程中还提供了一个很漂亮的过度效果。问题是它仍然一次性加载了所有的图片,即使网站加载完成后用户有可能只看前两张或者三张图片。</p>
+
+<p>这个问题可以用新的 <a href="/en-US/docs/Web/API/Intersection_Observer_API">Intersection Observer API</a> 来解决 —— 通过这个api我们可以确保只有当图片出现在可见区域时,它才会被加载。</p>
+
+<h3 id="Intersection_Observer">Intersection Observer</h3>
+
+<p>这是对上面那个可以正常工作的例子提供的一个渐进增强功能 —— <a href="/en-US/docs/Web/API/Intersection_Observer_API">Intersection Observer</a> 只有在用户向下滚动页面并且显示在屏幕上时,才会开始加载目标图片。</p>
+
+<p>这里有相关的代码展示:</p>
+
+<pre class="brush: js">if('IntersectionObserver' in window) {
+ const observer = new IntersectionObserver((items, observer) =&gt; {
+ items.forEach((item) =&gt; {
+ if(item.isIntersecting) {
+ loadImages(item.target);
+ observer.unobserve(item.target);
+ }
+ });
+ });
+ imagesToLoad.forEach((img) =&gt; {
+ observer.observe(img);
+ });
+} else {
+ imagesToLoad.forEach((img) =&gt; {
+ loadImages(img);
+ });
+}</pre>
+
+<p>如果 {{domxref("IntersectionObserver")}}对象被浏览器所支持,app会对它新建一个实例。当一个或多个item(指的是监听的对象,例如一个img,译者注)跟观察者发生交互时(图片出现在视图中时),作为参数传递的函数可以用来处理一些回调事务(例如图片加载,译者注)。我们可以对每一个项目做一个迭代并对它们做出相应的处理——当图片可见时,我们开始加载真正的图片并且停止监听这张图片,因为图片加载完成之后,我们已经没有很必要再知道它的状态了。</p>
+
+<p>让我们来重申一下我们之前提到的渐进增强 —— 代码这么写的好处在于,不管 Intersection Observer是否被当前浏览器支持,app都能够正常的工作。如果 Intersection Observer不被支持,我们会用上面提到的基础方法来实现图片的加载。</p>
+
+<h2 id="一些改进">一些改进</h2>
+
+<p>记住,有很多的方法可以用来优化我们的加载时间,这个示例只探讨了其中的一种方法。你可以尝试让你的app变的更加健壮,通过让它在没有JavaScript的情况下也能工作 —— 通过使用 {{htmlelement("noscript")}} 标签来展示已经分配了最终 <code>src</code> 路径的图片,或者在 <code>&lt;img&gt;</code> 外面套上一个 {{htmlelement("a")}} 标签并指向对应的图片资源,当用户想要想要看的时候他们可以点击图片并访问他们。</p>
+
+<p>我们并没有这么做因为这个app本身依赖于JavaScript —— 没有JavaScript游戏列表就无法加载,Service Worker相关的代码也将无法执行。</p>
+
+<p>我们可以重写整个加载过程,让它不止加载图片,而是加载整个列表项,包括详细介绍和链接。工作起来它像一个无限滚动的页面——当用户往下滚动的时候开始加载新的项目。通过这个方法HTML页面体积会达到最小,加载时间可以更短,我们也可以从中获取到更大的性能优势。</p>
+
+<h2 id="结论">结论</h2>
+
+<p>初始化时加载更少的文件,分拆成更小的模块,使用占位符以及按需加载更多的内容 —— 这会让我们获得更短的首次加载时间,它既能让app开发者受益,也能给用户提供更加丝滑的体验。</p>
+
+<p>记住关于渐进增强的内容 —— 不管在任何硬件或平台都提供一个可用的产品,但在现代浏览器上面确保能提供更好的用户体验。</p>
+
+<h2 id="最后的思考">最后的思考</h2>
+
+<p>这就是这个系列的所有内容了 —— 我们通过 <a href="https://github.com/mdn/pwa-examples/tree/master/js13kpwa"> js13kPWA 示例应用的源码</a> 学习了渐进式web 应用的的用法,包括了<a href="/en-US/docs/Web/Apps/Progressive/Introduction">PWA介绍</a>, <a href="/en-US/docs/Web/Apps/Progressive/App_structure">PWA 结构</a>, <a href="/en-US/docs/Web/Apps/Progressive/Offline_Service_workers">通过Service Workers让PWA离线工作</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/Apps/Progressive/Installable_PWAs">让PWA易于安装</a>,以及最后的通知功能。在<a href="https://serviceworke.rs/">Service Worker Cookbook</a>的帮助下我们还解释了推送的原理。而在本篇文章中,我们探讨了渐进式加载的概念,包括一个使用了 <a href="/en-US/docs/Web/API/Intersection_Observer_API">Intersection Observer API</a>的有趣例子。</p>
+
+<p>请随意试验这些代码,通过使用PWA的特性来让你现有的应用更加健壮,或者自己创建一些全新的东西。相对于常规的web应用,PWA存在巨大的优势。</p>
+
+<p>{{PreviousMenu("Web/Apps/Progressive/Re-engageable_Notifications_Push", "Web/Apps/Progressive")}}</p>
+
+<p>{{QuickLinksWithSubpages("/en-US/docs/Web/Progressive_web_apps/")}}</p>
diff --git a/files/zh-cn/web/progressive_web_apps/添加到主屏幕/index.html b/files/zh-cn/web/progressive_web_apps/添加到主屏幕/index.html
new file mode 100644
index 0000000000..a0915ea9d2
--- /dev/null
+++ b/files/zh-cn/web/progressive_web_apps/添加到主屏幕/index.html
@@ -0,0 +1,218 @@
+---
+title: 添加到主屏幕
+slug: Web/Progressive_web_apps/添加到主屏幕
+tags:
+ - PWA
+ - 图标
+ - 服务进程
+ - 添加到主屏幕
+ - 清单
+ - 渐进式Web应用
+translation_of: Web/Progressive_web_apps/Add_to_home_screen
+---
+<p class="summary">添加到主屏幕(A2HS)添加到主屏幕(简称A2HS)是现代智能手机浏览器中的一项功能,使开发人员可以轻松便捷地将自己喜欢的Web应用程序(或网站)的快捷方式添加到主屏幕中,以便他们随后可以通过单点访问它。本指南说明了A2HS的使用方式,以及作为开发人员要使您的用户利用A2HS所需做的事情。</p>
+
+<h2 id="为什么选择A2HS?">为什么选择A2HS?</h2>
+
+<p>A2HS被认为是渐进式<a href="/en-US/docs/Web/Progressive_web_apps">Web应用程序</a>哲学的一部分—为Web应用程序提供与原生应用程序相同的用户体验优势,因此它们可以在当今的生态系统战争中竞争。这部分是通过访问主屏幕上的应用程序图标来访问应用程序,然后将其整齐地显示在自己的窗口中的简单手势。A2HS使这成为可能。</p>
+
+<h2 id="哪些浏览器支持A2HS?">哪些浏览器支持A2HS?</h2>
+
+<p>Mobile Chrome / Android Webview 从31版开始支持A2HS,Opera for Android从32版开始支持,Firefox for Android从<a href="/en-US/docs/Mozilla/Firefox/Releases/58">58版</a>开始支持。</p>
+
+<h2 id="如何使用?">如何使用?</h2>
+
+<p>我们已经编写了一个非常简单的示例网站(<a href="https://mdn.github.io/pwa-examples/a2hs/">观看我们的在线演示</a>,并<a href="https://github.com/mdn/pwa-examples/tree/master/a2hs">查看源代码</a>),该网站虽然功能不多,但是开发时使用了必要的代码,也可以将其添加到主屏幕中,并且service worker使其可以脱机使用。这个示例展示了一系列的狐狸图片。</p>
+
+<p>如果您有适用于Android的Firefox,使用它导航到我们的示例:<code>https://mdn.github.io/pwa-examples/a2hs/</code>。你将会看到狐狸图片,但更重要的是,你将会看到一个带有加号(+)的“主页”图标—这是为具有必要功能的任何站点显示的“添加到主屏幕”图标。</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/15762/add-to-home-screen-icon.png" style="border-style: solid; border-width: 1px; display: block; height: 64px; margin: 0px auto; width: 500px;"></p>
+
+<p>点击此按钮将显示一个确认横幅—按下大“+ 添加到主屏幕”按钮即可完成操作,将应用添加到主屏幕。(注意:在Android 8及更高版本中,将首先显示系统级的“添加到主屏幕”权限对话框。)</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/15772/fx-a2hs-banner.png" style="border-style: solid; border-width: 1px; display: block; height: 421px; margin: 0px auto; width: 700px;"></p>
+
+<p>如果您可以使用Mobile Chrome,则体验会略有不同;加载我们的网站后,您会看到一个弹出安装横幅,询问您是否要将此应用添加到主屏幕。</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/15771/chrome-a2hs-banner.png" style="border-style: solid; border-width: 1px; display: block; height: 421px; margin: 0px auto; width: 700px;"></p>
+
+<div class="note">
+<p><strong>注意:</strong>您可以在“<a href="https://developers.google.com/web/fundamentals/app-install-banners/">Web App安装横幅</a>”一文中找到有关Chrome安装横幅的更多信息。</p>
+</div>
+
+<p>如果您选择不将其添加到主屏幕,则可以稍后使用Chrome主菜单中的添加到主屏幕图标添加。</p>
+
+<p>无论使用哪种浏览器,当您选择将应用程序添加到主屏幕时,您都会看到它与短标题一起出现,就像原生应用程序一样。</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/15770/a2hs-on-home-screen.png" style="border-style: solid; border-width: 1px; display: block; height: 243px; margin: 0px auto; width: 400px;"></p>
+
+<p>点按此图标可以将其打开,但是作为全屏应用程序,您将不再看到其周围的浏览器用户界面。</p>
+
+<h2 id="如何使应用程序支持A2HS?">如何使应用程序支持A2HS?</h2>
+
+<p>要将您的应用添加到主屏幕,它需要满足以下条件:</p>
+
+<ul>
+ <li>应用通过HTTPs提供服务—Web正朝着更加安全的方向发展,许多现代web技术(包括A2HS)将仅工作在安全的环境中。</li>
+ <li>从HTML头链接具有正确字段的manifest文件。</li>
+ <li>有合适的图标可显示在主屏幕上。</li>
+ <li>Chrome浏览器还要求该应用程序注册一个service worker(例如,使其在离线状态下可以运行)。</li>
+</ul>
+
+<h3 id="Manifest">Manifest</h3>
+
+<p>web manifest 以标准JSON格式编写,应放置在应用程序目录中的某个位置(最好是在根目录中),名称为<code><em>somefilename</em>.webmanifest</code>(我们选择 <code>manifest.webmanifest</code>)。它包含多个字段,这些字段定义有关Web应用程序及其行为的某些信息。</p>
+
+<div class="note">
+<p><strong>注意:</strong>.webmanifest扩展名是在规范的“<a href="https://w3c.github.io/manifest/#media-type-registration">媒体类型注册</a>”部分中指定的,但通常浏览器将支持带有其他适当扩展名的清单,例如:.json。</p>
+</div>
+
+<p>A2HS所需的字段如下:</p>
+
+<ul>
+ <li><code>background_color</code>:指定在某些应用程序上下文中使用的背景色。与A2HS最相关的一个是在点击主屏幕上的应用程序图标并首次开始加载时显示的初始屏幕(当前仅在通过Chrome将应用添加到主屏幕时显示)。</li>
+ <li><code>display</code>:指定应如何显示应用。 为了使它看起来像一个独特的应用程序(而不仅仅是网页),您应该选择一个值,例如<code>fullscreen</code>(根本不显示任何UI)或独立<code>standalone</code>(非常相似,但是系统级UI元素(例如状态栏)可能是可见的)。</li>
+ <li><code>icons</code>:指定在不同位置(例如,在任务切换器上或更重要的是在主屏幕上)表示应用程序时浏览器使用的图标。 我们的演示中仅包含一个。</li>
+ <li><code>name</code>/<code>short_name</code>:这些字段提供了在不同位置表示应用程序时要显示的应用程序名称。<code>name</code>提供完整的应用名称。<code>short_name</code>当没有足够的空间显示全名时,提供一个缩写名称。如果您的应用程序名称特别长,建议您同时提供两者。</li>
+ <li><code>start_url</code>:提供启动添加到主屏幕应用程序时应加载的资源的路径。请注意,这必须是一个真相网站主页的相对路径,相对于 manifest的url。 另外,请注意,Chrome在显示安装标语之前需要这样做,而Firefox在显示“含+号的home”图标时并不需要它。</li>
+</ul>
+
+<p>我们的示例应用程序的manifest如下所示:</p>
+
+<pre class="brush: js">{
+ "background_color": "purple",
+ "description": "Shows random fox pictures. Hey, at least it isn't cats.",
+ "display": "fullscreen",
+ "icons": [
+ {
+ "src": "icon/fox-icon.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ }
+ ],
+ "name": "Awesome fox pictures",
+ "short_name": "Foxes",
+ "start_url": "/pwa-examples/a2hs/index.html"
+}</pre>
+
+<h3 id="合适的图标">合适的图标</h3>
+
+<p>如以上manifest所示,我们包括一个192 x 192像素的图标,供我们的应用使用。您可以根据需要添加更多尺寸;Android将为每个不同的用例选择最合适的尺寸。您还可以决定添加不同类型的图标,以便设备可以使用他们能够使用的最佳图标(例如,Chrome已经支持WebP格式).</p>
+
+<p>请注意,每个图标对象中的 <code>type</code> 成员都指定了该图标的mimetype,因此浏览器可以快速读取该图标的类型,然后将其忽略,并在不支持该图标时采用其他图标。</p>
+
+<p>在设计图标方面,您应该遵循与任何Android图标相同的最佳做法(请参阅<a href="https://developer.android.com/guide/practices/ui_guidelines/icon_design.html">Android图标设计指南</a>)。</p>
+
+<h3 id="将HTML链接到manifest">将HTML链接到manifest</h3>
+
+<p>要完成manifest的设置,您需要从应用程序主页的 HTML 中引用它:</p>
+
+<pre class="brush: html">&lt;<span class="pl-ent">link</span> <span class="pl-e">rel</span>=<span class="pl-s"><span class="pl-pds">"</span>manifest<span class="pl-pds">"</span></span> <span class="pl-e">href</span>=<span class="pl-s"><span class="pl-pds">"</span>manifest.webmanifest<span class="pl-pds">"</span></span>&gt;</pre>
+
+<p>一旦支持 manifest,支持 A2HS 的浏览器就会知道在哪里查找它。</p>
+
+<h2 id="A2HS没有给你什么?">A2HS没有给你什么?</h2>
+
+<p>请记住,将应用程序添加到主屏幕时,它只会使该应用程序易于访问—不会将应用程序的资产和数据下载到您的设备上,也不会使该应用程序脱机使用或类似的操作。 为了使你的应用离线运行,你必须使用<a href="/en-US/docs/Web/API/Service_Worker_API">Service Worker API</a>来离线存储资源,如果需要,还可以使用 <a href="https://wiki.developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API">Web storage</a> 或 <a href="https://wiki.developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a> 来存储其数据。</p>
+
+<p>在示例应用程序中,我们仅使用了一个service worker来存储所有必需的文件。service worker使用<code><a href="https://github.com/mdn/pwa-examples/blob/master/a2hs/index.js">index.js</a></code> 文件中的最终的代码块在网站上注册。然后,我们使用 <a href="https://wiki.developer.mozilla.org/en-US/docs/Web/API/Cache">Cache API</a> 缓存网站的所有资产,并使用 <a href="https://github.com/mdn/pwa-examples/blob/master/a2hs/sw.js">sw.js</a> 文件中的代码从缓存而不是网络中为它们提供服务。</p>
+
+<h2 id="桌面上的A2HS">桌面上的A2HS</h2>
+
+<p>虽然最初旨在改善移动操作系统上的用户体验,但人们也提出了使PWA也可以安装在桌面平台上的趋势。</p>
+
+<div class="note">
+<p><strong>注意:</strong>在撰写本文时,仅在较新版本的Chrome(在Windows中默认情况下,在macOS上的#enable-desktop-pwas标志开启后)中支持以下功能。</p>
+</div>
+
+<h3 id="添加安装按钮">添加安装按钮</h3>
+
+<p>为了使PWA可在桌面上安装,我们首先在文档中添加了一个按钮,以允许用户进行安装—桌面应用程序不会自动提供此按钮,并且需要通过用户手势来触发安装:</p>
+
+<pre class="brush: html">&lt;button class="add-button"&gt;Add to home screen&lt;/button&gt;</pre>
+
+<p>然后,我们给它一些简单的样式:</p>
+
+<pre class="brush: css">.add-button {
+ position: absolute;
+ top: 1px;
+ left: 1px;
+}</pre>
+
+<h3 id="用于处理安装的JavaScript">用于处理安装的JavaScript</h3>
+
+<p>在<a href="https://github.com/mdn/pwa-examples/blob/master/a2hs/index.js"><code>index.js</code>文件</a>的底部,我们添加了一些JavaScript来处理安装。首先,我们声明一个<code>deferredPrompt</code>变量(我们将在后面解释),获得对安装按钮的引用,并初始设置为<code>display: none</code>:</p>
+
+<pre class="brush: js">let deferredPrompt;
+const addBtn = document.querySelector('.add-button');
+addBtn.style.display = 'none';</pre>
+
+<p>我们最初隐藏该按钮是因为PWA必须遵循A2HS标准才能安装。发生这种情况时,支持的浏览器将触发<code>beforeinstallprompt</code>事件。 然后,我们可以使用以下处理程序来处理安装:</p>
+
+<pre class="brush: js">window.addEventListener('beforeinstallprompt', (e) =&gt; {
+ // Prevent Chrome 67 and earlier from automatically showing the prompt
+ e.preventDefault();
+ // Stash the event so it can be triggered later.
+ deferredPrompt = e;
+ // Update UI to notify the user they can add to home screen
+ addBtn.style.display = 'block';
+
+ addBtn.addEventListener('click', (e) =&gt; {
+ // hide our user interface that shows our A2HS button
+ addBtn.style.display = 'none';
+ // Show the prompt
+ deferredPrompt.prompt();
+ // Wait for the user to respond to the prompt
+ deferredPrompt.userChoice.then((choiceResult) =&gt; {
+ if (choiceResult.outcome === 'accepted') {
+ console.log('User accepted the A2HS prompt');
+ } else {
+ console.log('User dismissed the A2HS prompt');
+ }
+ deferredPrompt = null;
+ });
+ });
+});</pre>
+
+<p>所以我们在这里:</p>
+
+<ul>
+ <li>调用{{domxref("Event.preventDefault()")}}以停止Chrome 67及更早版本自动调用安装提示(此行为在Chrome 68中已更改)。</li>
+ <li>将事件对象存储在<code>deferredPrompt</code>变量中,以便以后可以用于执行实际安装。</li>
+ <li>将按钮设置为<code>display: block</code>,以便它出现在UI中供用户点击。</li>
+ <li>设置按钮的<code>click</code>处理程序。</li>
+</ul>
+
+<p>点击处理程序包含以下步骤:</p>
+
+<ul>
+ <li>通过<code>display: none</code>再次隐藏按钮—安装应用程序后将不再需要它。</li>
+ <li>使用<code>beforeinstallprompt</code>事件对象(存储在<code>deferredPrompt</code>中)上可用的<code>prompt()</code>方法触发显示安装提示。</li>
+ <li>使用<code>userChoice</code>属性响应用户与提示的交互,该属性再次在<code>beforeinstallprompt</code>事件对象上可用。</li>
+ <li>将<code>deferredPrompt</code>设置为null,因为不再需要它。</li>
+</ul>
+
+<p>So when the button is clicked, the install prompt appears.</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/16281/chrome-desktop-a2hs-banner.png" style="border-style: solid; border-width: 1px; display: block; height: 163px; margin: 0px auto; width: 300px;"></p>
+
+<p>如果用户选择安装,则将安装该应用程序(可作为独立的桌面应用程序使用),并且不再显示“安装”按钮(如果已经安装了该应用程序,则将不再触发<code>onbeforeinstallprompt</code>事件)。当您打开应用程序时,它将显示在其自己的窗口中:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/16280/a2hs-installed-desktop.png" style="border-style: solid; border-width: 1px; display: block; height: 296px; margin: 0px auto; width: 500px;"></p>
+
+<p>如果用户选择“取消”,则应用程序的状态将返回到单击按钮之前的状态。</p>
+
+<div class="note">
+<p><strong>注意:</strong>本部分的代码主要来自Pete LaPage的<a href="https://developers.google.com/web/fundamentals/app-install-banners/">应用安装横幅/添加到主屏幕</a>。</p>
+</div>
+
+<h2 id="其他">其他</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Web/Progressive_web_apps">渐进式Web应用</a></li>
+ <li><a href="/en-US/docs/Web/API/Service_Worker_API">Service Worker接口</a></li>
+ <li><a href="/en-US/docs/Web/Manifest">Web manifest参考</a></li>
+ <li><a href="https://developers.google.com/web/fundamentals/app-install-banners/">应用安装横幅</a></li>
+</ul>
+
+<div>{{QuickLinksWithSubpages("/en-US/docs/Web/Progressive_web_apps/")}}</div>