likes
comments
collection
share

HTML5的新特性(二)——离线存储、Web Workers

作者站长头像
站长
· 阅读数 7

引言

HTML5是互联网技术发展中的一个重要里程碑,它不仅代表了网页标记语言的新标准,也标志着网页应用开发进入了一个新的时代。在HTML5之前,许多富媒体体验和动态功能依赖于Flash或其他插件,这些技术具有平台依赖性,且不总是开放标准。HTML5的推出,旨在解决这些问题,提供一个统一的、开放的网页标准,使得开发者能够使用纯粹的HTML、CSS和JavaScript来构建先进的网页应用。

HTML5引入了一系列新特性,使得网页不仅仅是静态信息的展示,而是可以成为互动性强、功能丰富的应用平台。它支持多媒体内容的原生播放,提供了更多与设备硬件交互的API,允许创建离线应用,还提升了网页的语义结构,使得内容更易于被搜索引擎解析和索引。

对于现代web开发,HTML5的意义在于它提供了构建快速、响应式和交互式网页应用的基础。它使得开发者能够利用用户的设备能力,如使用地理位置信息、绘图、视频处理等,而无需依赖第三方插件。随着移动设备的普及,HTML5也使得移动友好的网站开发成为可能,从而推动了响应式设计的普及。

接下来,我们将详细探讨HTML5的每个新特性,并通过示例展示它们如何被实际应用到现代网页开发中。

5. 离线存储

离线存储的工作原理

离线存储允许web应用在用户的设备上本地保存数据,这样即使在没有网络连接的情况下,用户也能加载和使用应用。离线存储的目的是提高应用的性能和可用性,使其更像是一个本地应用程序。

LocalStorage、SessionStorage和IndexedDB的区别

  1. LocalStorage:
    • 用于长期存储数据,数据没有过期时间,除非主动删除数据,否则数据永远不会消失。
    • 存储容量较大,一般在5MB左右。
    • 只能存储字符串,因此需要将对象JSON序列化。
  2. SessionStorage:
    • 用于临时存储数据,数据仅在页面会话期间存在,页面关闭则数据消失。
    • 和 LocalStorage 类似,但它的作用域限于单个页面会话。
    • 存储容量和 LocalStorage 相似,也主要用于存储字符串数据。
  3. IndexedDB:
    • 是一个低级API用于客户端存储大量结构化数据,包括文件/二进制大对象。
    • 支持事务,可以存储和操作大量数据。
    • 存储容量比 LocalStorage 和 SessionStorage 大得多。
    • 允许你创建索引来高效查询数据。

示例:分别使用保存用户设置

以下是如何使用 LocalStorage、SessionStorage 和 IndexedDB 来保存用户设置的简单示例:

LocalStorage 示例:

// 保存用户设置
function saveSettings() {
    localStorage.setItem('theme', 'dark');
    localStorage.setItem('fontSize', '16px');
}

// 加载用户设置
function loadSettings() {
    var theme = localStorage.getItem('theme');
    var fontSize = localStorage.getItem('fontSize');
    console.log('Theme:', theme, 'Font Size:', fontSize);
}

saveSettings();
loadSettings();

SessionStorage 示例:

// 保存用户会话设置
function saveSessionSettings() {
    sessionStorage.setItem('sessionTheme', 'light');
    sessionStorage.setItem('sessionFontSize', '14px');
}

// 加载用户会话设置
function loadSessionSettings() {
    var theme = sessionStorage.getItem('sessionTheme');
    var fontSize = sessionStorage.getItem('sessionFontSize');
    console.log('Session Theme:', theme, 'Session Font Size:', fontSize);
}

saveSessionSettings();
loadSessionSettings();

IndexedDB 示例:

IndexedDB 的使用比 LocalStorage 和 SessionStorage 复杂,因为它是异步的,并且需要更多的代码来处理数据库的创建、读取、写入和更新。

这是一个简单的 IndexedDB 示例,演示如何创建一个数据库和存储用户设置:

// 打开数据库
var request = indexedDB.open('userSettingsDB', 1);
var db;

request.onupgradeneeded = function(event) {
    // 创建一个新的数据库
    db = event.target.result;

    // 创建一个对象存储空间并指定主键
    var objectStore = db.createObjectStore('settings', { keyPath: 'id' });

    // 创建索引以通过其他字段查询
    objectStore.createIndex('theme', 'theme', { unique: false });
    objectStore.createIndex('fontSize', 'fontSize', { unique: false });
};

request.onsuccess = function(event) {
    db = event.target.result;
    saveIndexedDBSettings();
};

// 保存用户设置
function saveIndexedDBSettings() {
    var transaction = db.transaction(['settings'], 'readwrite');
    var objectStore = transaction.objectStore('settings');

    var userSettings = { id: 'user1', theme: 'dark', fontSize: '16px' };
    var request = objectStore.put(userSettings);

    request.onsuccess = function(event) {
        console.log('User settings saved to IndexedDB');
    };
}

// 注意:IndexedDB的操作都是异步的,这里没有展示如何读取数据。
// 在实际应用中,你需要在适当的事件处理程序中读取和更新UI。

在 IndexedDB 示例中,我们首先尝试打开数据库。如果指定的版本号高于当前存在的数据库,onupgradeneeded 事件会被触发,允许我们创建或修改数据库结构。然后,我们在 onsuccess 事件中保存用户设置。IndexedDB 是异步的,所以所有操作都通过事件处理程序来完成。

离线存储综合示例

<!DOCTYPE html>
<html>
<head>
    <title>英雄联盟设置示例</title>
</head>
<body>
    <h2>英雄联盟设置示例</h2>
    <div>
        <!-- 保存设置按钮 -->
        <button onclick="saveSettings()">保存到LocalStorage</button>
        <button onclick="saveSessionSettings()">保存到SessionStorage</button>
        <button onclick="saveIndexedDBSettings()">保存到IndexedDB</button>
    </div>
    <br />
    <div>
        <!-- 读取设置按钮 -->
        <button onclick="loadSettings()">从LocalStorage读取</button>
        <button onclick="loadSessionSettings()">从SessionStorage读取</button>
        <button onclick="loadIndexedDBSettings()">从IndexedDB读取</button>
    </div>
    <!-- 显示设置的区域 -->
    <div id="settings"></div>

    <script>
        // LocalStorage 示例
        function saveSettings() {
            // 保存设置到LocalStorage
            localStorage.setItem('favoriteChampion', '亚索');
            localStorage.setItem('resolution', '1920x1080');
            console.log('设置已保存到LocalStorage');
        }

        function loadSettings() {
            // 从LocalStorage读取设置
            const favoriteChampion = localStorage.getItem('favoriteChampion');
            const resolution = localStorage.getItem('resolution');
            document.getElementById('settings').innerText = `LocalStorage - 最喜欢的英雄: ${favoriteChampion}, 分辨率: ${resolution}`;
            console.log('从LocalStorage读取设置');
        }

        // SessionStorage 示例
        function saveSessionSettings() {
            // 保存设置到SessionStorage
            sessionStorage.setItem('sessionFavoriteChampion', '盖伦');
            sessionStorage.setItem('sessionResolution', '1280x720');
            console.log('会话设置已保存到SessionStorage');
        }

        function loadSessionSettings() {
            // 从SessionStorage读取设置
            const sessionFavoriteChampion = sessionStorage.getItem('sessionFavoriteChampion');
            const sessionResolution = sessionStorage.getItem('sessionResolution');
            document.getElementById('settings').innerText = `SessionStorage - 最喜欢的英雄: ${sessionFavoriteChampion}, 分辨率: ${sessionResolution}`;
            console.log('从SessionStorage读取设置');
        }

        // IndexedDB 示例
        var db;
        // 打开数据库
        var request = indexedDB.open('LoLSettingsDB', 1);

        request.onerror = function(event) {
            // 处理打开数据库时的错误
            console.log('打开IndexedDB时发生错误。');
        };

        request.onupgradeneeded = function(event) {
            // 数据库升级或首次创建时执行
            db = event.target.result;
            if (!db.objectStoreNames.contains('settings')) {
                var objectStore = db.createObjectStore('settings', { keyPath: 'id' });
                objectStore.createIndex('favoriteChampion', 'favoriteChampion', { unique: false });
                objectStore.createIndex('resolution', 'resolution', { unique: false });
            }
        };

        request.onsuccess = function(event) {
            // 成功打开数据库
            db = event.target.result;
            console.log('IndexedDB成功打开');
        };

        function saveIndexedDBSettings() {
            // 保存设置到IndexedDB
            var transaction = db.transaction(['settings'], 'readwrite');
            var objectStore = transaction.objectStore('settings');
            var userSettings = { id: 'user1', favoriteChampion: '亚索', resolution: '1920x1080' };
            var request = objectStore.put(userSettings);

            request.onsuccess = function(event) {
                console.log('用户设置已保存到IndexedDB');
            };

            request.onerror = function(event) {
                console.log('保存设置到IndexedDB时发生错误');
            };
        }

        function loadIndexedDBSettings() {
            // 从IndexedDB读取设置
            var transaction = db.transaction(['settings']);
            var objectStore = transaction.objectStore('settings');
            var request = objectStore.get('user1');

            request.onsuccess = function(event) {
                if (request.result) {
                    document.getElementById('settings').innerText = `IndexedDB - 最喜欢的英雄: ${request.result.favoriteChampion}, 分辨率: ${request.result.resolution}`;
                    console.log('从IndexedDB读取用户设置');
                } else {
                    console.log('在IndexedDB中未找到用户设置');
                }
            };

            request.onerror = function(event) {
                console.log('从IndexedDB读取时发生错误');
            };
        }
    </script>
</body>
</html>

6. Web Workers

Web Workers的并行计算优势

Web Workers 提供了一种在 Web 应用程序的主执行线程之外运行脚本的能力,从而实现了在浏览器中的多线程编程。这意味着可以在后台线程中执行复杂或耗时的计算,而不会冻结或减慢用户界面的响应。

主要优势包括:

  1. 并行处理:可以同时执行多个任务,而不会相互阻塞,特别是在多核CPU上。
  2. 性能提升:对于密集型任务,Web Workers可以提高应用程序的整体性能和响应速度。
  3. 用户体验改善:主线程(通常是UI线程)不会因为需要执行复杂计算而被阻塞,因此应用程序仍然可以响应用户操作。

如何创建和使用Web Worker

创建和使用Web Worker的步骤如下:

  1. 创建一个新的 JavaScript 文件,这个文件将包含在 Web Worker 中运行的代码。
  2. 在主线程的脚本中,使用 new Worker() 构造函数来创建一个 Worker,并指定要运行的脚本文件的URL。
  3. 使用 postMessage() 方法来向 Worker 发送数据。
  4. 使用 onmessage 事件处理程序来接收 Worker 发送回来的数据。
  5. 如有需要,可以使用 terminate() 方法来停止 Worker 的执行。

示例:一个简单的后台数据处理任务

这是创建和使用 Web Worker 的一个简单示例。在这个例子中,我们将在 Worker 中执行一个假设的数据处理任务。

首先,创建一个名为 worker.js 的新 JavaScript 文件,这个文件将包含 Web Worker 的代码:

// worker.js 文件内容
self.onmessage = function(e) {
    var data = e.data;
    // 执行数据处理
    var processedData = processData(data);
    // 将结果发送回主线程
    self.postMessage(processedData);
};

function processData(data) {
    // 假设的数据处理逻辑
    return data.map(item => item * 2); // 举例:将每个元素乘以2
}

然后,在主线程中创建和使用这个 Worker:

worker.html文件代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Web Worker 示例</title>
</head>
<body>
    <h2>Web Worker 示例</h2>
    <div id="result"></div> <!-- 用于显示处理后的数据 -->
    <div id="error"></div> <!-- 用于显示错误信息 -->

    <script>
        // 创建一个新的 Worker
        var myWorker = new Worker('worker.js');

        // 发送数据到 Worker
        myWorker.postMessage([1, 2, 3, 4, 5]);

        // 监听来自 Worker 的消息
        myWorker.onmessage = function(e) {
            console.log('处理后的数据:', e.data);
            document.getElementById('result').innerText = '处理后的数据: ' + e.data;
        };

        // 监听错误事件
        myWorker.onerror = function(e) {
            console.error('Worker错误:', e.message);
            document.getElementById('error').innerText = 'Worker错误: ' + e.message;
        };
    </script>
</body>
</html>

使用 HTTP 服务器启动

由于现代浏览器的安全限制,当您尝试直接从本地文件系统(使用 file:// 协议)加载网页并使用 Web Workers 时,浏览器会阻止 Worker 脚本的加载。这种安全限制是为了防止跨源请求伪造(CSRF)和其他安全威胁。

# 这里使用了http-server作为http服务器
npm install -g http-server
http-server

# 启动后输出如下提示,即为成功
Starting up http-server, serving ./
http-server version: 14.1.1
http-server settings: 
CORS: disabled
Cache: 3600 seconds
Connection Timeout: 120 seconds
Directory Listings: visible
AutoIndex: visible
Serve GZIP Files: false
Serve Brotli Files: false
Default File Extension: none

Available on:
  http://127.0.0.1:8080
Hit CTRL-C to stop the server
# 启动成功后访问 http://127.0.0.1:8080 自己点 或者 http://localhost:8080/worker.html

在这个例子中,我们向 Worker 发送了一个包含数字的数组。Worker 接收到这个数组后,对其进行处理(在这个例子中简单地将每个元素乘以2),然后将处理后的数组发送回主线程。主线程接收到处理后的数据并将其打印到控制台。

使用 Web Workers 进行计算密集型或耗时操作可以显著提高 Web 应用程序的性能和响应性。