前端浏览器存储初探
Pin Young Lv9

随着现代化浏览器的发展,客户端的能力已经有了很大的提升,比如 Chrome、Firefox,通过 webkit 等内核的发展与版本的迭代与升级,浏览器变得越来越强大。对于前端来说,由于浏览器的升级所能做的事情越来越多,我们在数据存储方面也就有很多方案可以选择,大概有以下几种方式:

  • cookie
  • localStorage
  • sessionStorage
  • indexedDB
  • PWA
  • service worker

数据存储终究是基于具体的业务场景的,那么我们应该如何有针对性的进行选择呢?

要想在不同的技术方案中选择出适合自己的场景的应用方案,首先需要对不同的技术概念以及它所适用的场景有足够的掌握,我们接下来就对上述提到的技术点依次做介绍与分析。

cookie 产生的原因是什么呢?
答:HTTP 请求是无状态的。所以服务端无法记住当前的 HTTP 请求与之前的请求有什么联系,这样所造成的后果就是服务端无法对用户信息以及登录状态进行维护。而 cookie 的产生使得服务端可以区分当前是哪个客户端发起的请求。

cookie 的生成方式:

  • http response header 中的 set-cookie 由服务端发出,客服端存储。
  • js 中可以通过 document.cookie 可以读写 cookie

cookie 存储的限制:

  • 作为浏览器存储,大小为 4KB 左右
  • 需要设置过期时间 expire

cookie 还存在两个属性,分别是 expire 与 httponly。

在性能优化方面,cookie 能做些什么呢?
cookie 是存储在主域名下面的,这样会造成一定程度的CDN流量损耗。那我们应当怎样去解决呢?其实,解决方式也很简单,我们只需将CND域名与主站域名独立开就可以了。
下面两张图分别是百度和优酷对于 cookie 的应用,很明显,优酷是一个 bad case。

2. localStorage

  • HTML5设计出来专门用于浏览器存储的
  • 大小为 5M 左右
  • 仅在客户端使用,不和服务端进行通信
  • 有较好的接口封装

3. sessionStorage

  • 会话级别的浏览器存储(浏览器的一个 tab 就是一个会话)
  • 对于表单信息的维护

4. indexedDB(使用较少)

  • 用于客户端存储大量的结构化的数据,该 API 使用索引实现对数据的高性能搜索。
  • 为应用创建离线版本。

5. PWA(谷歌提出,已经达到了与安卓同样重要的程度)

PWA是一种 Web App 新模型,并不是指具体的某一种技术或单一的知识点,是通过一系列的web新特性配合优秀的UI交互设计,逐步的增强 Web App 的用户体验。
它所提出的标准主要针对以下几方面:

  • 可靠性 无网络情况下提供基本页面的访问
  • 快速 针对页面渲染以及数据访问(转场动画)
  • 融入 增加到手机桌面 全屏、推送等特性

6. service workers

JS ———— 单线程(在用户层面无法创建多线程)=> 大量数据的获取或者前端相关的运算(3D模型数据) => JS阻塞 => 有可能影响UI层面的渲染

一个 service worker 是一段运行在浏览器后台进程里的脚本,它独立于当前页面,提供了一些不需要与web页面交互的功能,即那种在网页背后悄悄执行的能力。在将来,基于它可以实现消息推送,静默更新等服务,但是目前它首先要具备的功能是拦截和处理网络请求,包括可编程的响应缓存管理。
应用场景:

  • 应用于离线化(拦截请求)
  • 与主页面进行通信

下面的两个链接可以用于我们在浏览器中查看正在运行中的service worker:
chrome://inspect/#service-workers
chrome://serviceworker-internals/

接下来我们具体看两个 service worker应用的例子:

  • 应用于离线化的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!--serviceWorker.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link href="./main.css" rel="stylesheet">
<title>service worker</title>
</head>
<body>
<div class="container">
service worker
</div>
</body>
<script src="./serviceWorker.js"></script>
</html>

1
2
3
4
5
6
7
8
9
10
11
12
13
// service worker 注册
if (navigator.serviceWorker) {
navigator.serviceWorker.register('./serviceWorkerConfig.js', {scope: './'})
.then((reg) => {
console.log(reg);
})
.catch((e) => {
console.log(e);
})
} else {
alert("service worker is not supported!")
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 将文件添加至缓存
self.addEventListener('install', function(e) {
e.waitUntil(
caches.open('service-worker-v1').then((cache) => {
console.log('open cache');
return cache.addAll(['./serviceWorker.js', './main.css', './serviceWorker.html'])
})
)
});
// 读取缓存
self.addEventListener('fetch', function(e) {
console.log(e);
// 改变返回的数据
e.respondWith(
caches.match(e.request).then((res) => {
if (res) {
return res
} else {
// 通过fetch向网络发起请求
fetch(url).then(function(res) {
if (res) {
// 将返回的文件存至缓存
} else {
// 用户提示
}
})
}
})
)
});

通过以上代码的展示,我们便用service worker实现了将静态文件存至缓存,从而完成应用的离线化。

  • 与主页面进行通信
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--message.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul id="msg-box"></ul>
<input id="msg-input" />
<button id="send-msg">发送</button>
</body>
<script src="msg-app.js"></script>
</html>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// msg-app.js
if (navigator.serviceWorker) {
let sendBtn = document.getElementById('send-msg');
let msgInput = document.getElementById('msg-input');
let msgBox = document.getElementById('msg-box');

sendBtn.addEventListener('click', () => {
// 将input框中的信息发送给service worker
navigator.serviceWorker.controller.postMessage(msgInput.value);
});

navigator.serviceWorker.addEventListener('message', (event) => {
msgBox.innerHTML += (`<li>${event.data.message}</li>`)
});

// service worker 注册
navigator.serviceWorker.register('./msg-serviceWorker.js', {scope: './'})
.then((reg) => {
console.log(reg);
})
.catch((e) => {
console.log(e);
})
} else {
alert("service worker is not supported!")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// msg-serviceWorker.js
self.addEventListener('message', (event) => {
let promise = self.clients.matchAll().then((clientList) => {
let sendId = event.source ? event.source.id : 'unknown';
clientList.forEach((list) => {
if (list.id === sendId) {
return false
} else {
list.postMessage({
client: sendId,
message: event.data
})
}
})
});
event.waitUntil(promise);
});

我们可以清楚的看到,上面的代码通过监听 message 事件,service worker 可以达到与页面进行通信的目的,更多关于 service worker 的细节内容大家可以查阅更多的资料进行学习,Service Worker 入门

以上就是浏览器存储初探的全部内容,希望大家可以多多实践,文中有错误的地方,欢迎指正。