Web离线的解决方案

Posted on

最近的工作是做嵌入在IOS程序内部的页面,其中有一个需求就是需要满足在离线的情况下显示页面。当然,现在主流的离线方式是使用Service Worker来完成离线需求。 但是IOS内置的WKWebView并不支持最新的Service Worker(取决于IOS版本)最新版本已经支持,所以不得不想办法来解决。

Application Cache

AppCache是一个过时的技术,但是在IOS下勉强还能用。不过Chrome对这种过时的技术支持不是很好,单个缓存文件最大只支持5MB。而且还有请求的BUG:Accept header on GET request for appcache manifest。触发AppCache是在html标签中添加manifest属性。

<html manifest="manifest.appcache">
  ...
</html>

通过manifest.appcache文件来定义需要缓存的文件,不过载入manifest.appcache的页面会被作为Master entries缓存起来。因为AppCache出现的时候还是以静态网站为主,所以并不能缓存请求的Ajax数据。我们需要自己再手动实现缓存所有请求在localStorage,但是localStorage同样也有最大5MB的限制。所以也需要考虑相应的解决方案。不过,我最终采取的方式是通过嵌入iframe来实现触发AppCache,这样做的好处是因为SPA应用是自己来控制路由的,所以导致每个路径都会保存一份Master entries。但是通过iframe的话,我们的Master entries永远只有一份。并且当前页面的所有资源也被顺利缓存,因为SPA无论访问哪个路径返回的都是相同的index.html。由JS来控制路由并加载相应的组件。

WorkBox

Service Worker是现在主流的缓存技术,会帮你缓存所有的静态文件和数据请求。但是对于SPA项目,我们不可能自己手动书写缓存清单。所以还是借助现有的开源解决方案,这里最出名的解决方案是谷歌的Workbox。通过使用Webpack插件和简单的配置,我们便可以做到缓存所有的静态资源和数据。Service Worker有很多种缓存策略可以选择,例如Cache First和NetWork First。但是AppCache每次都会优先使用缓存,然后再去更新最新的文件。所以我们不得不在发生更新的时候去重新加载页面。

总结

最终的解决方案是优先使用Service Worker,当不支持Service Worker的时候再回退到AppCache。但是想要从AppCache升级到Service Worker的时候,必须清除所有AppCache的所有数据。浏览器并没有提供相应的接口,我们目前采用的方式是手动删除AppCache储存的数据库来完成这一需求。

参考:

Using the application cache

HTML5 Offline Application Cache

Application Cache plugin for Webpack

Workbox webpack Plugins

网站开发中的Modal问题

Posted on

最近在开发的网站基本上全是使用弹出的对话框(Modal)来进行UI交互的,所以对于Modal的处理也算积攒了一点经验。便想写下来供自己以后参考和学习。说实话,在目前响应式布局的主流开发方式下,Modal非常不适合作为一个良好的交互方式。因为对于手机触屏用户非常不友好。当然,我开发的这个网站也没有考虑手机用户。当前的主流方式还是采用SPA,JS软路由切换页面来交互才是正解。

Modal元素的位置

弹出对话框的方式基本是把对话框的z-index设置的比当前页面元素高,然后使用opacity: 0.5来半透明进行遮罩。但是,需要使用Modal的内容最好放在body的下层,而不是嵌套了好多层的某个div里面,因为子元素的z-index是不可能大于父元素的,会导致在某些情况下,其他元素比当前的Modalz-index更高。

Modal滚动条问题

很多情况下,弹出的Modal内容过多导致会产生滚动条。这时如果不处理好会导致出现双重滚动条。一条是页面本身的内容过多产生的滚动条,还有一条是Modal自身的。双重滚动条还有一个问题是当你在Modal里面进行滚动的时候,页面内容本身也会被滚动,这会导致关闭Modal的时候发现页面的位置已不是打开的位置了,用户体验非常不好。这时候有两种解决方案。

第一个方案是页面本身采用position:fixed进行固定,并用JS记住滚动位置,但必须保持页面本身和Modal是并列关系。例:

    <body>
    <div class="content"></div>
    <div class="modal"></div>
    </body>

这样,Modal采用position:absolute定位,当内容过长就会自动出现滚动条。但是当关闭Modal的时候,必须把网页内容的position:fixed属性去除,并用JS滚动到当初打开Modal的位置。如果Modal是透明的,那就必须在打开Modal的时候设置网页内容的topleft属性来保持位置不变。

第二个方案是,网页内容采用overflow: hidden来隐藏滚动条。Modal采用position:fixed方案进行定位,但是这时候Modal不得不设置overflow: auto来进行滚动。 第二个方案对于网页内容和Modal的位置并没有特殊的要求,比较灵活,而且不需要JS的介入。

浏览器重绘

在一个方案中,由于网页内容被设置成了position:fixed,滚动条自然消失。所以滚动位置回到了浏览器的最上面才对。但是有时候遇到打开Modal的时候发现Modal打开之后滚动条不在最上方,这时候的原因是因为我们虽然设置了CSS进行了变更,但是浏览器没有进行重新绘制,我们可以使用会导致浏览器重绘的JS API来让浏览器更新滚动条信息,使得打开的Modal处于浏览器的最上方。

参考:

Force reflow