资讯专栏INFORMATION COLUMN

H5之「离线应用」

Panda / 2655人阅读

摘要:之离线存储离线存储顾名思义,在有线的环境下先缓存数据包括静态资源,动态资源,从而在离线环境下,依旧可以正常使用应用单页应用静态资源存储是一套静态资源缓存方案利用该技术可以实现配置静态资源转发请求,加快应用加载速度,降低服务器负载基本用法引入

H5之「离线存储」

「离线存储」:顾名思义,在有线的环境下先缓存数据(包括静态资源,动态资源),从而在离线环境下,依旧可以正常使用应用(单页应用)

静态资源存储(ApplicationCache)

applicationCache 是一套h5静态资源缓存方案.
利用该技术可以实现配置静态资源/转发请求,加快应用加载速度,降低服务器负载.

基本用法

引入manifest配置文件



    
        ...
    
    
        ...
    

配置manifest文件

CACHE MANIFEST
# 修改配置后,附加上下面一段js代码,才能更新缓存
# 2016972143
# 注释:需要缓存的文件,无论在线与否,均从缓存里读取
CACHE:
/dist/0.eda078350ef514670764.bundle.js
/dist/common.bundle.js?v=2016972143
/dist/df9f379beae2559b27044dcfdc0653ab.png?v=2016972143
/dist/home.bundle.js?v=2016972143
/dist/home.css?v=2016972143
uncached.js?v=2016972143

#cached.css

# 注释:不缓存的文件,无论缓存中存在与否,均从新获取
NETWORK:
*
#uncached.js
#uncached.css

# 注释:获取不到资源时的备选路径,如index.html访问失败,则返回404页面
FALLBACK:
#/v1/team/dirlists mock/team_dirlists.json
#/v1/team/app_filelist?isAdd=0&source=team&page=1&pageSize=10&sort=ftime&from=hiwebapp&fid=t293 mock/team_app_filelist.json
#index.html 404.html

书写更新缓冲js

// 每次打开页面执行该代码段,更新缓存
// !!! 注意:更新缓存后不会立即生效,需要重新加载页面
(function () {
    var cache = window.applicationCache;

    cache.addEventListener("updateready", function(e) {
        if (cache.status == cache.UPDATEREADY) {
            // Browser downloaded a new app cache.
            // if (confirm("A new version of this site is available. Load it?")) {
                cache.swapCache();
                window.location.reload();
            // }
        } else {
            // Manifest didn"t changed. Nothing new to server.
        }
    }, false);

    cache.update()

}())

服务器配置

配置manifest文件,响应 Content-Type: text/cache-manifest Cache-Control: max-age=0

部署线上代码时更新manifest版本号与配置

按照以上配置,这样就能实现静态资源缓存

如上图,from cache的加载时间相比其他网络请求快得多!
其中的fetch/ajax请求不能够通过静态资源存储,因为响应结果是可能会变的.

那么对于异步ajax请求(动态资源)要通过什么方法才能存储起来呢?实现真正意义的离线存储.

动态资源存储(WebSQL/IndexedDB)

使用前端数据库可以较为灵活的控制动态资源存储,在这里我使用了indexedDB, 为什么不用WebSQL?

之前做在线聊天应用时,使用过WebSQL存储聊天记录

WebSQL已经被弃用

WebSQL是传统的关系数据库,indexedDB是主流的NoSQL DB

基本用法

创建一个通用的数据库访问接口

var indexedDB = window.indexedDB || window.msIndexedDB || window.mozIndexedDB || window.webkitIndexedDB;

// memCache 内存缓冲,避免频繁的读写数据库
var req, db, memCache = {};
if(indexedDB) {
    // version:2
    req = indexedDB.open("ajax_cache", 2);
    // 保证caches成功创建
    req.onsuccess = function (e) {
        db = e.target.result;
        if(!db.objectStoreNames.contains("caches")){
            db.createObjectStore("caches", {keyPath: "id"});
        }
    }
    // 数据库版本改变触发
    req.onupgradeneeded=function(e){
        var db=e.target.result;
        if(!db.objectStoreNames.contains("caches")){
            db.createObjectStore("caches", {keyPath: "id"});
        }
        console.log("DB version changed to " + db.version);
    };
    req.onerror = function (err) {
        console.error("indexedDB open failed. ", err)
    }
}

export default {
    isSupported: !!indexedDB,
    set: (id, data) => {
        var entity = {
            id: id,
            data: data
        }
        var transaction = db.transaction("caches", "readwrite");
        var store = transaction.objectStore("caches");
        var req = store.put(entity);
        req.onerror = () => {
            console.error("put data failed. ", entity)
        }
        req.onsuccess = () => {
            memCache[id] = data
            console.info("put data successed. ", entity)
        }
    },
    get: (id) => {
        return new Promise((resolve, reject) => {
            if(memCache[id]) {
                resolve(memCache[id]);
                return;
            }

            var transaction = db.transaction("caches", "readwrite");
            var store = transaction.objectStore("caches");
            var req = store.get(id);
            req.onerror = () => {
                console.error("get data failed. ", id)
                resolve()
            }
            req.onsuccess = (e) => {
                var rlt = e.target.result;
                console.info("get data successed. ", id, rlt)
                resolve(rlt && rlt.data)
            }
        })
    }
}

重写fetch/ajax方法

/* reset fetch function for offline be compatible*/
var fetch = require("isomorphic-fetch")
import {parse} from "url"

var __fetch = fetch;
fetch = function (url) {
    var rlt = parse(url, true);
    function generateJson(json) {
        return {
            json: function () {
                return json
            }
        }
    }
    function generateErrorJson() {
        return generateJson({
            errno: 500, errmsg: "你正处于离线状态",
            result: {
                files: []
            }
        })
    }
    var query = rlt.query;
    // 去掉时间戳与重复的from参数
    delete query.t;
    delete query.from;
    var id = rlt.pathname
    var key = MyUtils.jsonToUrl(query)
    if(MyUtils.isOffline()) { // 离线
        if(!id) {
            return new Promise((resolve, reject) => {
                resolve(generateErrorJson())
            })
        } else {
            if(DB.isSupported) {
                return DB.get(id).then(json => {
                    return (!json || !json[key])
                        ? generateErrorJson()
                        : generateJson(json[key])
                })
            } else {
                return new Promise((resolve, reject) => {
                    resolve(generateErrorJson())
                })
            }
        }
    } else {
        return __fetch.apply(null, [].slice.call(arguments))
            .then(res => res.json())
            .then( (resJson) => {
                if(DB.isSupported) {
                    var tmp = {};
                    tmp[key] = resJson;
                    DB.get(id).then(json => {
                        DB.set(id, Object.assign({}, json, tmp))
                    })
                }
                return generateJson(resJson)
            }
        )
    }

}

可以在chrome的web tool中看到indexedDB

每次请求都缓存下来了

在脱离网络后!依旧可以模拟异步请求!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/61810.html

相关文章

  • Hybrid APP架构设计思路

    摘要:本文将从以下几个方面阐述架构设计的一些经验和思考。原文及讨论请到通讯作为一种跨语言开发模式,通讯层是架构首先应该考虑和设计的,往后所有的逻辑都是基于通讯层展开。 关于Hybrid模式开发app的好处,网络上已有很多文章阐述了,这里不展开。 本文将从以下几个方面阐述Hybrid app架构设计的一些经验和思考。 原文及讨论请到 github issue 通讯 作为一种跨语言开发模式,通讯...

    hiyayiji 评论0 收藏0
  • 前端面试题总结——H5(持续更新中)

    摘要:前端面试题总结持续更新中为什么只需要写需要来规范浏览器的行为让浏览器按照它们应该的方式来运行基于所以需要对进行引用,才能告知浏览器文档所使用的文档类型。 前端面试题总结——H5(持续更新中) 1.HTML5 为什么只需要写 ? HTML5 需要doctype来规范浏览器的行为,让浏览器按照它们应该的方式来运行; HTML4.01基于SGML,所以需要对DTD进行引用,才能告知浏览器文档...

    coolpail 评论0 收藏0

发表评论

0条评论

Panda

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<