惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

PCI Perspectives
PCI Perspectives
P
Proofpoint News Feed
G
GRAHAM CLULEY
Know Your Adversary
Know Your Adversary
T
Tenable Blog
I
Intezer
Scott Helme
Scott Helme
Hacker News - Newest:
Hacker News - Newest: "LLM"
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Security Latest
Security Latest
SecWiki News
SecWiki News
Schneier on Security
Schneier on Security
T
The Exploit Database - CXSecurity.com
The Last Watchdog
The Last Watchdog
N
News and Events Feed by Topic
Google DeepMind News
Google DeepMind News
S
Secure Thoughts
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
H
Hacker News: Front Page
L
LINUX DO - 最新话题
U
Unit 42
宝玉的分享
宝玉的分享
Stack Overflow Blog
Stack Overflow Blog
L
LINUX DO - 热门话题
IT之家
IT之家
C
Cybersecurity and Infrastructure Security Agency CISA
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
量子位
博客园_首页
爱范儿
爱范儿
T
Threatpost
小众软件
小众软件
A
Arctic Wolf
Jina AI
Jina AI
H
Help Net Security
Webroot Blog
Webroot Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Cyberwarzone
Cyberwarzone
博客园 - 聂微东
月光博客
月光博客
The Register - Security
The Register - Security
Martin Fowler
Martin Fowler
博客园 - 司徒正美
V
V2EX
博客园 - Franky
B
Blog
B
Blog RSS Feed
H
Heimdal Security Blog
WordPress大学
WordPress大学
A
About on SuperTechFans

元视角

.NET 生态下的 Agent 框架选型:从 ReAct 到原生推理 - 元视角 从「能用」到「好用」:LLM 流式响应实现方式的探索之路 - 元视角 当我用 2000 条聊天记录,让 AI 为我画一幅自画像 - 元视角 基于 Supabase 的 AI 应用开发探索 - 元视角 微博 × MCP:社交媒体新玩法解锁 - 元视角 四点钟海棠花未眠 - 元视角 Semantic Kernel × MCP:智能体的上下文增强探索 - 元视角 基于 K-Means 聚类分析实现人脸照片的快速分类 - 元视角 容器技术驱动下的代码沙箱实践与思考 - 元视角 温故而知新:后端通用查询方案的再思考 - 元视角 浅议 CancellationToken 在前后端协同取消场景中的应用 - 元视角 Semantic Kernel 视角下的 Text2SQL 实践与思考 - 元视角 关于 ChatGPT 的流式传输,你需要知道的一切 - 元视角 RAG 的是与非、Rewrite 和 Rerank - 元视角 使用 EFCore 和 PostgreSQL 实现向量存储及检索 - 元视角 基于 LLaMA 和 LangChain 实践本地 AI 知识库 - 元视角 使用 llama.cpp 在本地部署 AI 大模型的一次尝试 - 元视角 如何为 Git 配置多个 SSH Key - 元视角 C# 使用 LibUsbDotNet 实现 USB 设备检测 - 元视角 基于 C# 实现样式与数据分离的打印方案 - 元视角 基于 SVG 的图形交互方案实践 - 元视角 前端视频播放技术概览 - 元视角 温故而知新,再话 Python 动态导入 - 元视角 后 GPT 时代,NLP 不存在了? - 元视角 视频是不能 P 的系列:使用 Milvus 实现海量人脸快速检索 - 元视角 GDI+下字体大小自适应方案初探 - 元视角 小爱音箱集成 ChatGPT 的不完全教程 - 元视角 程序员视角下的三体世界随想 - 元视角 关于 Docker 容器配置信息的渐进式思考 - 元视角 在 Docker 容器内集成 Crontab 定时任务 - 元视角 为你的服务器集成 LDAP 认证 - 元视角 似花还似非花 - 元视角 视频是不能 P 的系列:使用 Dlib 实现人脸识别 - 元视角 浅议分布式链路追踪与日志的整合 - 元视角 关于 Git 大文件上传这件小事 - 元视角 .NET 进程内队列 Channel 的入门与应用 - 元视角 使用 Fody 实现 .NET 的静态编织 - 元视角 .NET Core + ELK 搭建可视化日志分析平台(下) - 元视角 聊一聊前端图片懒加载背后的故事 - 元视角 支持外部链接跳转的 Vue Router 扩展实现 - 元视角 视频是不能 P 的系列:OpenCV 和 Dlib 实现表情包 - 元视角 不得不说的 ASP.NET Core 集成测试 - 元视角 再议 DDD 视角下的 EFCore 与 领域事件 - 元视角 Vue.js 前端项目容器化部署实践极简教程 - 元视角 再见,人间四月天 - 元视角 Python 图像风格化迁移助力画家梦想 - 元视角 利用 ASP.NET Core 中的标头传播实现分布式链路追踪 - 元视角 利用 gRPC 实现文件的上传与下载 - 元视角 七种武器:延迟队列的原理和实现总结 - 元视角 gRPC 流式传输极简入门指南 - 元视角 Envoy 集成 Jaeger 实现分布式链路追踪 - 元视角 浅议非典型 Web 应用场景下的身份认证 - 元视角 gRPC 借助 Any 类型实现接口的泛化调用 - 元视角 分布式丛林探险系列之 Redis 集群模式 - 元视角 分布式丛林探险系列之 Redis 主从复制模式 - 元视角 通过 Python 预测 2021 年双十一交易额 - 元视角 gRPC 搭配 Swagger 实现微服务文档化 - 元视角 SSL/TLS 加密传输与数字证书的前世今生 - 元视角 使用 Python 自动识别防疫健康码 - 元视角 你不可不知的容器编排进阶技巧 - 元视角 ASP.NET Core 搭载 Envoy 实现 gRPC 服务代理 - 元视角 再话 AOP,从简化缓存操作说起 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务身份认证(JWT) - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的监控预警 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的反向代理 - 元视角 ASP.NET Core gRPC 打通前端世界的尝试 - 元视角 EFCore 实体命名约定库:EFCore.NamingConventions - 元视角 ASP.NET Core gRPC 集成 Polly 实现优雅重试 - 元视角 ASP.NET Core gRPC 健康检查的探索与实现 - 元视角 ASP.NET Core gRPC 拦截器的使用技巧分享 - 元视角 SnowNLP 使用自定义语料进行模型训练 - 元视角 使用 HttpMessageHandler 实现 HttpClient 请求管道自定义 - 元视角 ABP vNext 的实体与服务扩展技巧分享 - 元视角 ABP vNext 对接 Ant Design Vue 实现分页查询 - 元视角 源代码探案系列之 .NET Core 跨域中间件 CORS - 元视角 源代码探案系列之 .NET Core 限流中间件 AspNetCoreRateLimit - 元视角 源代码探案系列之 .NET Core 并发限制中间件 ConcurrencyLimiter - 元视角 通过 EmbededFileProvider 实现 Blazor 的静态文件访问 - 元视角 低代码,想说爱你不容易 - 元视角 记一次失败的 ThoughtWorks 面试经历 - 元视角 从 C# 1.0 到 C# 9.0,历代 C# 语言特性一览 - 元视角 通过 Python 分析 2020 年全年微博热搜数据 - 元视角 基于 Python 和 Selenium 实现 CSDN 一键三连自动化 - 元视角 使用多线程为你的 Python 爬虫提速的 N 种姿势,你会几种? - 元视角 实现网页长截图的常见思路总结 - 元视角 温故而知新,由 ADO.NET 与 Dapper 所联想到的 - 元视角 视频是不能 P 的系列:OpenCV 人脸检测 - 元视角 作为技术宅的我,是这样追鬼滅の刃的 - 元视角 使用 Python 抽取《半泽直树》原著小说人物关系 - 元视角 厉害了!打工人用 Python 分析西安市职位信息 - 元视角 使用 dotTrace 对 .NET 应用进行性能分析与优化 - 元视角 一道 HashSet 面试题引发的蝴蝶效应 - 元视角 基于选项模式实现.NET Core 的配置热更新 - 元视角 Dapper.Contrib 在 Oracle 环境下引发 ORA-00928 异常问题的解决 - 元视角 .NET Core 中对象池(Object Pool)的使用 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(下):EventBus 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(中):RabbitMQ 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(上):基础篇 - 元视角 记一次从已损坏的 Git 仓库中找回代码的经历 - 元视角 .NET Core 原生 DI 扩展之属性注入实现 - 元视角
迁移 Hexo 博客到 Google 渐进式 Web 应用(PWA) - 元视角
飞鸿踏雪 · 2017-10-25 · via 元视角

如果说通过 TravisCI 实现博客的自动化部署,是持续集成这个概念在工作以外的一种延伸,那么今天这篇文章想要和大家分享的,则是我自身寻求技术转型和突破的一种挣扎。前段时间 Paul 同我聊到 Web 技术的发展趋势,Paul 认为 Web 应用会逐渐取代原生应用成为主流,我对此不置可否。真正让我陷入思考的是,在这个充满变化的时代,知识的更新速度远远超过你我的学习速度,我们应该如何去追随这个时代的步伐。如同那些淹没在时间河流里的技术名词,当青春不再的时候,我们喜欢把这个过程称之为成长,当发现距离第一次使用 FontPage 制作网站已过去十年,当发现曾经的网页三剑客在岁月蹉跎里频频改换姓名,当发现那些淹没在历史里的技术来不及学习就成为过往……或许,这个世界真正迷人的地方,就在于它每天都在不断变化。

新一代Web应用——PWA

接着 Paul 关于 Web 技术的这个话题,我认为 Web 技术在短期内会成为原生应用的一种补充。事实上,原生应用和 Web 应用哪一个会是未来,这个问题的争论由来已久,在业界我们可以看到 HTML5、PhoneGap、React/React Native、Electron/NW.js、小程序等方案百家争鸣,每一种方案都可以让我们去 Web 技术去打破平台间的差异。与此同时,我们注意到移动开发领域对原生技术的需求在缩减,虽然马克·扎克伯格曾表示,“选择 HTML5 是 Facebook 最大的错误“,可我们注意到,越来越多的 Web 技术被运用在原生应用中,Web 技术被认为是最佳的打造跨平台应用的技术,可以通过一套代码实现不同平台间体验的一致性。我们注意到知乎和天猫的客户端中都混合使用了一定的 Web 技术,因为纯粹使用原生技术去开发一个移动应用,其最大的弊端就在于我们要为 Android 和 iOS 维护两套不同的代码,从国内曾经疯狂火热的 iOS 培训就可以看出,单独使用原生技术去开发客户端,其成本实际上是一直居高不下的。

虽然我们有 Xamarin 这样的跨平台技术,试图用一种编程语言和代码共享的方式,去开发两种不同平台的应用程序,可是我们注意到,平台间的差异和抗阻是天然存在的,就像SQL和面向对象这样我们再熟悉不过的例子。同样的,Facebook 的 React Native 项目,试图用Web技术去弱化平台间的差异,React Native 存在的主要问题是,它依然依赖原生组件暴露出来的组件和方法,所以像 DatePickerIOS、TabBarIOS 等控件是 iOS Only的,这意味着在开发过程中开发者还是要考虑平台间的差异性,其次 React 本身的JSX(对应HTML)、CSS Layout(对应CSS)本身是具有一定的学习曲线的,虽然底层因为没有使用WebView的原因提高了部分性能,然而整体上是牺牲了扩展性的。总而言之,这是一个介于 Web 技术和原生技术之间的中间技术,在我看来地位着实蛮尴尬的,因为无论在Web层还是Native层都选择了部分妥协,完美实现跨平台真心不容易啊。

要掌握一门新技术,最好的方法就是去应用它。我的博客使用的是 Indigo主题,这是一个典型的 Material Design 风格的主题,所以我一直想尝试将其改造成原生应用,我曾经接触过移动端应用开发,如果通过 WebView 内嵌网页的方式来实现,我需要处理离线状态下页面的显示问题,以及所有混合应用开发都会遇到的一个问题,即原生应用层需要和Web应用层进行通信的问题。而如果采用 Hybrid App 的思路去开发一个混合应用,意味着我需要去学习 Cordova 这样的 Hybrid 开发框架,去了解 JavaScript 和 Native 交互的细节。那么有没有一种学习成本相对较低,同时可以提供原生应用体验的思路呢?答案是确定的,这就是我们下面要说的渐进式应用(PWA)。

渐进式应用(Progressive Web Apps,PWA)是Google提出的新一代Web应用概念,其目的是提供可靠、快速、接近Native应用的服务方案。我们知道传统Web应用有两个关键问题无法解决,即需要从网络实时加载内容而带来的网络延迟依赖浏览器入口而带来的用户体验,从某种意义上而言,渐进式应用的出现有望让这些问题得到解决,首先,渐进式应用可以显著加快应用加载速度,其提供的离线缓存机制可以让应用在离线环境下继续使用,关键技术为 Service Worker 和 Cache Storage;其次,渐进式应用可以被添加到主屏,有独立的图标、启动页、全屏支持,整体上更像 Native App,关键技术为 Web.App Manifest;最后,渐进式应用同操作系统集成能力得到提高,具备在不唤醒状态下推送消息的能力,关键技术为 Push API 和Notification API。

PWA中关键技术解析

Google 对外提出 PWA 这个概念其实是在今天的二月份左右,所以现在我写这篇文章实际上是在赶一趟末班车。我最近比较喜欢的一个男演员张鲁一,在接受媒体采访时媒体称他是一个大器晚成的人,他的确让我找到了理想中成熟男人的一个标准,如果你要问我这个标准是什么,我推荐你去看他主演的电视剧《红色》。那么,好了,为了让大家了解渐进式Web应用(PWA),相比其它跨平台方案有何优缺点,我们这里来简单讨论下PWA中的关键技术。

ServiceWorker

我们知道,传统的 Web 应用需要在网络环境下使用,当处在离线环境下时,因为 HTTP 请求无法被发送到服务器上,所以浏览器通常会显示一个空白页,并告知用户页面无法加载,因此会影响用户在离线环境下的使用体验,与此同时,因为 Web 页面在打开的过程中需要加载大量资源,因此在页面刚刚打开的一段时间内,用户看到的页面通常都是一个空白页面,考虑到缓存或者是预加载的 Web 应用,通常都会以预设资源作为占位符来填充页面,因此带来访问者的印象往往会更好。那么渐进式Web应用带给我们最大的惊喜,就是它可以在离线环境下使用,其核心技术就是 ServiceWorker,我们来一起看看如何使用 SeviceWorker:

if (navigator.serviceWorker) {
  navigator.serviceWorker.register('service-worker.js')
  .then(function(registration) {
    console.log('service worker 注册成功');
  }).catch(function (err) {
    console.log('servcie worker 注册失败');
  });
}

我们这里看到一个基本的注册 ServiceWorker 的代码片段,并且它采用了业界流行的Promise的写法。那么首先第一个问题,ServiceWorker 到底是什么?ServiceWorker 本质上是一个 Web 应用程序和浏览器间的代理服务器,它可以在离线环境下拦截网络请求,并基于网络是否可用以及资源是否可用,来采取相对应的处理动作,所以 ServiceWorker 最基本用法是作为离线缓存来使用,而高阶用法则是消息推送和后台同步。通常来讲,ServiceWorker 会经历如下的生命周期:

ServiceWorker生命周期 ServiceWorker生命周期 注:配图来自 http://web.jobbole.com/84792/

按照官方文档中的定义,ServiceWorker 同 WebWorker 一样,是一段 JavaScript 脚本,作为一个后台独立线程运行,其运行环境与普通的 JavaScript 不同,因此不直接参与 Web 交互行为,从某种意义上来说,ServiceWorker 的出现,正是为了弥补 Web 应用天生所不具备的离线使用、消息推送、后台自动更新等特性,我们这里来看一个使用 ServiceWorker 缓存文件已达到离线使用的目的的例子:

var cacheStorageKey = 'minimal-pwa-1'
var cacheList = [
  '/',
  "index.html",
  "main.css",
  "e.png"
]
self.addEventListener('install', e => {
  e.waitUntil(
    caches.open(cacheStorageKey)
    .then(cache => cache.addAll(cacheList))
    .then(() => self.skipWaiting())
  )
})

在这里例子中,我们在ServiceWorker的install事件中添加了待缓存文件列表,这将意味着这些静态资源,会在网页中的 ServiceWorker 被 install 的时候添加到缓存中,我们在某个合适的时机到来时就可以再次使用这些缓存资源。事实上考虑到安全性的问题,ServiceWorker 在设计时被约束为按照路径给予最高权限,即 ServiceWorker 在指定路径下是有效的。这里简单提下 ServiceWorker 的缓存策略,因为这个问题在我看来蛮复杂的,例如官方出品的 sw-tool 中定义的缓存策略就有如下五种:

  • 网络优先::从网络获取, 失败或者超时再尝试从缓存读取
  • 缓存优先::从缓存获取, 缓存插叙不到再尝试从网络抓取
  • 最快:同时查询缓存和网络, 返回最先拿到的
  • 仅限网络:仅从网络获取
  • 仅限缓存:仅从缓存获取

我们刚刚提到被缓存的静态资源会在合适的时机被再次使用,那么什么时候可以称之未合适的时机呢?在这个问题中,我们是指 fetch 事件,事实上通过拦截 fetch 事件,我们就可以拦截即将被发送到服务器端的 HTTP 请求,ServiceWorker 首先会检查缓存中是否存在待请求资源,如果存在,就直接使用这个资源并返回 HTTP 响应,否则就发起 HTTP 请求到服务器端,此时 ServiceWorker 担任的是一个代理服务器的角色。至此,我们就会明白,ServiceWorker 的作用其实就是在离线条件下利用缓存伪造 HTTP 响应返回,这样我们就达到了离线使用的目的,传统的 Web 应用在离线环境无法使用,根本原因是没有这样一个 Mock 的 Server 去伪造 HTTP 响应并返回,因为 HTTP 请求此时根本就无法发送到服务端。为了让ServiceWorker 全面接管 HTTP 请求以便利用请求,我们这里的实现方式如下:

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        // Cache hit - return response
        if (response) {
          return response;
        }
        return fetch(event.request);
      }
    )
  );
});

好了,以上就是 ServiceWorker 在离线缓存方面的基本用法,希望进行深入了解的朋友,可以参考文末链接做进一步研究。

Web App Manifest

接下来介绍 Web App Manifest,它其实是 Web 开发领域的一个"叛徒",因为它所做的事情为大家所不齿,基本可以概括为,怎么样假装自己是一个 Native App,我们直接看它的定义:

{
  "name": "Minimal app to try PWA",
  "short_name": "Minimal PWA",
  "display": "standalone",
  "start_url": "/",
  "theme_color": "#8888ff",
  "background_color": "#aaaaff",
  "icons": [
    {
      "src": "e.png",
      "sizes": "256x256",
      "type": "image/png"
    }
  ]
}

这个我确认没有什么好说的,详细的参数可以参考这里,通常我们需要将以上文件命名为manifest.json,并通过以下方式引入到HTML结构中,通常是添加在标签下,我们所期望的图标、启动页、主题色等Native App的特性都是在这里定义的,这里想吐槽的是,随着越来越多的平台开始向标签中注入"新血液",譬如标签和标签:现在HTML结构变得越来越复杂,更不要说主流的AngularJS和Vue这类MVVM框架,基本上都是通过扩展HTML属性来完成数据绑定的。对PWA应用来讲,我们只需要在标签下引入以下内容:

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

这里简单介绍下 Web App Manifest 中常见的参数含义及其作用:

  • name/short_name:表示应用被添加到屏幕上以后显示的名称,当屏幕空间不足以显示完整的 name 时,将显示 short_name。
  • start_url:表示用户从屏幕启动应用时所加载网页的URL,通常我们将其指向网站的首页。
  • theme_color:表示应用程序的主题颜色,PWA 事实上是建议使用 Material Design 设计风格的,因此该属性可以控制应用的主题颜色,并在页面加载完成前展示一个过渡动画。
  • scope:表示 PWA 应用的作用域,即哪些页面可以以 PWA 应用的形式呈现。
  • display:表示 PWA 应用呈现的方式,可以是 fullscreen、standalone、minimal-ui 和 browser 中的任意取值。
  • orientation:表示 PWA 应用的屏幕方向,如果你有移动开发的经验,对此应该不会感到陌生。
  • icons:表示 PWA 应用在屏幕上的图标,为了适配不同尺寸的屏幕,这里可以设置不同尺寸下的图标。同样地,如果你有移动开发的经验,对此应该不会感到陌生。

Push/Notification API

关于这两个东西,我们简单说一下啊,PWA 中的 Push 机制主要有 NotificationPush API 两部分组成,前者用于向用户展示通知,而后者用于订阅推送消息。网络上对这块介绍的并不多,关于推送这个问题,一直是国内 Android 用户和开发者的一块心病,因为 Google 的推送服务在国内水土不服,因此国内厂商或者是 SDK 提供商基本上都有自己的一套方案,这就导致在用户的设备上同时开启着若干个消息推送服务,用户手机里的电就是这样一点点被耗尽的,所以这个问题大家看看就好。在 PWA 中,我们可以通过 ServiceWorker 的后台计算能力结合 Push API 对推送事件进行响应,并通过 Notification API 实现通知的发出与处理:

// sw.js
self.addEventListener('push', event => {
  event.waitUntil(
    // Process the event and display a notification.
    self.registration.showNotification("Hey!")
  );
});

self.addEventListener('notificationclick', event => {  
  // Do something with the event  
  event.notification.close();  
});

self.addEventListener('notificationclose', event => {  
  // Do something with the event  
});

移植Hexo博客到PWA应用

现在,我们基本了解了PWA的概念以及实现PWA的关键技术,我们现在考虑将Hexo博客改造成一个PWA应用,我们这里不打算考虑消息推送的相关问题,所以对Hexo这样一个静态博客生成器而言,我们可以做的实际上只有两件事情,即通过Web App Manifest让它更像一个Native应用,通过ServiceWorker为它提供离线缓存的特性。我们从最简单的开始,我们需要在Hexo的根目录中增加一个manifest.json文件,该文件我们可以通过这个网站 manifoldjs.com 来生成。下面给出博主博客中使用的配置:

{
  "name":"飞鸿踏雪的部落格",
  "short_name":"Payne's Blog",
  "description":"人生到处知何似,应似飞鸿踏雪泥",
  "icons":[
  {
    "src":"assets/images/icons/bird36.png",
    "sizes":"36x36",
    "type":"image/png"
  },
  {
    "src":"assets/images/icons/bird48.png",
    "sizes":"48x48",
    "type":"image/png"
  },
  {
    "src":"assets/images/icons/bird72.png",
    "sizes":"72x72",
    "type":"image/png"
  },
  {
    "src":"assets/images/icons/bird96.png",
    "sizes":"96x96",
    "type":"image/png"
  },
  {
    "src":"assets/images/icons/bird144.png",
    "sizes":"144x144",
    "type":"image/png"
  },
  {
    "src":"assets/images/icons/bird192.png",
    "sizes":"192x192",
    "type":"image/png"
  }],
  "background_color":"#fff",
  "theme_color":"#000",
  "start_url":"/",
  "display":"standalone",
  "orientation":"portrait"
}

好了,现在我们来考虑如何去实现一个ServiceWorker,Google官方提供了一个ServiceWorker的示例项目,以及网友提供的Minimal-PWA,这两个项目都可以帮助我们去了解,如何去实现一个ServiceWorker,甚至于我们有sw-toolboxsw-precache这样的工具,配合gulp和webpack我们定制缓存策略并生成ServiceWorker。可是你要知道,懒惰对程序员而言是一种美德,在这里我选择了Hexo的插件hexo-offline,该插件可以帮助我们生成ServiceWoker,关于它的使用及配置,大家可以自行去了解,我重点想说说支持ServiceWorker以后,我的博客所呈现出来的变化以及PWA实际运行的效果。

ServiceWorker和Cache Storage ServiceWorker和Cache Storage

通过这张图,我们可以清楚地看到,ServiceWorker确实在后台工作着,而Cache Storage确实对博客内的静态资源做了缓存处理。事实上对Hexo这样的静态博客而言,整个博客都是静态资源,所以在实际运行中它会对所有内容进行缓存,我们可以在终端中验证这个想法:

在Hexo中监听到的缓存请求 在Hexo中监听到的缓存请求

可我想说这一切并没有什么用,因为我并不能如愿地在离线状态下访问我的博客,甚至因为有了缓存机制,当我在撰写这篇博客时,虽然我改变了markdown文档的内容,但当我刷新博客的时候,因为缓存机制的存在,我不能像从前那样直接看到博客的变化,更重要的一点是,整个缓存大概有8M左右的体积,因此每次请求页面时,我能够明显地感觉到页面加载的延迟,看起来我们费了大量周折最终却一无所获,这听起来实在是讽刺不是吗?

说完了ServiceWorker,我们再来说说Web App Manifest,我尝试从豌豆荚下载了移动版Chrome,可我自始至终无法将应用添加到主屏幕,貌似这需要Android系统底层的支持,我测试了两部手机,一部OPPO手机和一部小米手机,发现都没有明显的PWA支持,当我访问页面的时候,浏览器更加不会主动提示我"将应用添加到主屏",像UC浏览器是将网站以应用的形式添加到浏览器首页,这的确没有什么值得令人惊喜的地方,因为在PC端的时候,我们就可以做到类似地实现,这篇文章耗费时间蛮长的啦,大概是因为我不知道,该如何描述这个失败的尝试。最近接触到一位前辈的项目,这是一个需要跨PC端和移动端的项目。目前面临的一个挑战就是,移动端有太多依赖原生接口的功能设计,所以一套代码在全平台适配,真的仅仅是一个美好的理想,离实现永远有一段不可逾越的距离。

本文小结

本文主要以Google提出的渐进式Web应用(Progressive Web Apps)为主线,简单探讨了Google的渐进式Web应用及其关键技术。渐进式Web应用试图解决传统Web应用的两个关键问题,即需要从网络实时加载内容而带来的网络延迟依赖浏览器入口而带来的用户体验。首先,渐进式应用可以显著加快应用加载速度,其提供的离线缓存机制可以让应用在离线环境下继续使用,关键技术为Service Worker和Cache Storage;其次,渐进式应用可以被添加到主屏,有独立的图标、启动页、全屏支持,整体上更像Native App,关键技术为Web.App Manifest;最后,渐进式应用同操作系统集成能力得到提高,具备在不唤醒状态下推送消息的能力,关键技术为Push API和Notification API。在此背景下,我们对静态博客Hexo进行了改造,尝试将其迁移到一个PWA应用上,虽然最终以失败告终,可是在整个过程中我们依然有所收获,我觉得一件事情能让我们有所思考或者有所感悟的话,这就已然是一种幸运、一种成功啦。

其实Web应用与原生应用并非彼此水火不容,除了纯粹的Web技术和Native技术以外,在这两者之间我们看到的更多是混合技术的应用,所以我认为开发人员在未来一定要具备两种能力,即跨语言和跨平台开发的能力。比如小程序是在微信原生生态下建立的定制化Web应用,它有着类似HTML/CSS/JavaScript的技术方案,同时提供了统一的应用程序外观和使用体验;而跨平台游戏引擎cocos2d-x,通过JavaScript Bridge等类似技术,则可以实现将Web技术转化为Native技术…..总而言之,在技术选型这个问题上,我们可以选择的方案越来越多,如何让想法可以伴随技术产生优秀的产品,这是我们在这个时代真正该去思考的问题。目前来讲,国内普遍重视iOS,可惜遗憾的是iOS不支持PWA;国内的Android系统经过阉割以后,国内用户无法使用Chrome,以及各个厂商定制的浏览器存在兼容性问题;国内因为政策及现实原因,第三方推送相对GCM推送要活跃很多,厂商并不会太关注对PWA应用推送的支持。虽然现实如此,可Web技术发展到今天为止,我们能做的就是希望它越来越好,在此引用黄玄的一句话:

我们信仰 Web,不仅仅在于软件、软件平台与单纯的技术,还在于『任何人,在任何时间任何地点,都可以在万维网上发布任何信息,并被世界上的任何一个人所访问到。』而这才是 web 的最为革命之处,堪称我们人类,作为一个物种的一次进化。」