HTML5 Web SQL 数据库(详细教程)

什么是 HTML5 Web SQL 数据库?

在移动互联网时代,前端开发不再只是“展示页面”那么简单。越来越多的 Web 应用需要在用户本地存储数据,比如离线笔记、待办事项列表、购物车信息等。传统的 Cookie 和 LocalStorage 虽然简单,但它们的容量有限,且不适合存储结构化数据。

这时,HTML5 Web SQL 数据库就登场了。它是一种基于 SQLite 的客户端数据库解决方案,允许我们在浏览器中创建、读取、更新和删除结构化数据。虽然它已被 W3C 标准弃用(取而代之的是 IndexedDB),但在一些老项目或特定场景中依然具有实用价值。

你可以把 HTML5 Web SQL 数据库想象成一个“微型的本地数据库”,就像你手机里安装的某个 App 所使用的数据存储系统。它不依赖网络,数据存在用户的设备上,速度也比远程数据库快得多。

注意:尽管 Web SQL 已被标记为“废弃”,但目前主流浏览器(如 Chrome、Safari)仍支持。如果你在维护旧项目或开发离线优先的 Web 应用,它依然是一个可选项。

如何打开并操作 HTML5 Web SQL 数据库?

要使用 HTML5 Web SQL 数据库,首先需要通过 JavaScript 的 window.openDatabase 方法打开一个数据库实例。这个方法的语法如下:

const db = window.openDatabase(name, version, description, size);
  • name:数据库名称,字符串类型,比如 'myAppDB'
  • version:数据库版本号,比如 '1.0',用于版本管理
  • description:数据库描述,可选,用于说明用途
  • size:数据库大小,单位为字节,比如 1024 * 1024 表示 1MB

下面是一个完整的打开数据库示例:

// 打开或创建一个名为 'todoDB' 的数据库
const db = window.openDatabase('todoDB', '1.0', '我的待办事项数据库', 1024 * 1024);

// 如果数据库打开成功,会在控制台输出提示
db.transaction(function(tx) {
  console.log('数据库已成功打开');
});

⚠️ 重要提示:openDatabase 是异步操作,但其返回的 db 对象可以立即用于后续操作。所有数据库操作都必须通过 transaction() 方法进行,这是保证数据一致性的关键。

创建表与插入数据

数据库打开后,下一步就是创建表。就像你在 Excel 中创建一个表格一样,你需要定义列名和数据类型。

在 HTML5 Web SQL 数据库中,支持的数据类型包括:TEXTINTEGERREAL(浮点数)、BLOB(二进制数据)。注意:它不支持 DATE 类型,但你可以用 TEXT 存储日期字符串。

我们来创建一个“待办事项”表:

// 使用事务创建表
db.transaction(function(tx) {
  // 执行 SQL 语句创建表
  tx.executeSql(
    'CREATE TABLE IF NOT EXISTS todos (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, completed BOOLEAN DEFAULT 0, created_at TEXT)',
    [], // 参数数组,这里没有参数
    function(tx, result) {
      console.log('表创建成功');
    },
    function(tx, error) {
      console.error('创建表失败:', error.message);
    }
  );
});
  • CREATE TABLE IF NOT EXISTS:如果表不存在才创建
  • id INTEGER PRIMARY KEY AUTOINCREMENT:主键,自动递增
  • title TEXT NOT NULL:任务标题,不能为空
  • completed BOOLEAN DEFAULT 0:是否完成,0 表示未完成,1 表示完成
  • created_at TEXT:创建时间,用字符串存储

创建完表后,我们来插入一条数据:

// 插入一条待办事项
db.transaction(function(tx) {
  tx.executeSql(
    'INSERT INTO todos (title, completed, created_at) VALUES (?, ?, ?)',
    ['学习 HTML5 Web SQL 数据库', false, new Date().toISOString()],
    function(tx, result) {
      console.log('数据插入成功,新记录 ID 为:', result.insertId);
    },
    function(tx, error) {
      console.error('插入数据失败:', error.message);
    }
  );
});
  • ? 是占位符,防止 SQL 注入
  • result.insertId 返回新插入记录的主键 ID

查询与更新数据

插入数据后,我们当然需要查询它。使用 SELECT 语句可以获取数据,配合 executeSql 的回调函数处理结果。

// 查询所有待办事项
db.transaction(function(tx) {
  tx.executeSql(
    'SELECT * FROM todos ORDER BY created_at DESC',
    [],
    function(tx, results) {
      console.log('查询到 ' + results.rows.length + ' 条记录');
      
      // 遍历查询结果
      for (let i = 0; i < results.rows.length; i++) {
        const row = results.rows.item(i);
        console.log(
          `ID: ${row.id}, 标题: ${row.title}, 完成: ${row.completed ? '是' : '否'}, 时间: ${row.created_at}`
        );
      }
    },
    function(tx, error) {
      console.error('查询失败:', error.message);
    }
  );
});
  • results.rows.length:返回结果的行数
  • results.rows.item(i):获取第 i 行的数据对象
  • ORDER BY created_at DESC:按创建时间倒序排列

更新数据也很简单。假设我们要把某条任务标记为“已完成”:

// 将 ID 为 1 的任务设为已完成
db.transaction(function(tx) {
  tx.executeSql(
    'UPDATE todos SET completed = 1 WHERE id = ?',
    [1],
    function(tx, result) {
      if (result.rowsAffected > 0) {
        console.log('更新成功,影响了 ' + result.rowsAffected + ' 行');
      } else {
        console.log('未找到 ID 为 1 的记录');
      }
    },
    function(tx, error) {
      console.error('更新失败:', error.message);
    }
  );
});
  • UPDATE 语句用于修改现有数据
  • WHERE id = ? 精准定位要更新的记录
  • result.rowsAffected 返回受影响的行数

删除数据与清理数据库

当任务完成或数据过期时,我们需要删除记录。删除操作也通过 executeSql 实现:

// 删除 ID 为 1 的待办事项
db.transaction(function(tx) {
  tx.executeSql(
    'DELETE FROM todos WHERE id = ?',
    [1],
    function(tx, result) {
      if (result.rowsAffected > 0) {
        console.log('删除成功,共删除 ' + result.rowsAffected + ' 条记录');
      } else {
        console.log('未找到要删除的记录');
      }
    },
    function(tx, error) {
      console.error('删除失败:', error.message);
    }
  );
});

如果想彻底清空整个表,可以使用 DELETE FROM todos,但不会重置主键自增。

📌 小贴士:若要重置自增主键,可以使用 DELETE FROM todos; VACUUM;,但 VACUUM 不是所有浏览器都支持。

实际案例:简易待办事项应用

让我们整合前面的知识,做一个简单的待办事项应用。

<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8" />
  <title>HTML5 Web SQL 数据库待办应用</title>
</head>
<body>
  <h2>我的待办事项</h2>
  <input type="text" id="taskInput" placeholder="输入任务内容" />
  <button id="addBtn">添加任务</button>

  <ul id="taskList"></ul>

  <script>
    // 打开数据库
    const db = window.openDatabase('todoDB', '1.0', '待办事项数据库', 1024 * 1024);

    // 创建表(首次运行时)
    db.transaction(function(tx) {
      tx.executeSql(
        'CREATE TABLE IF NOT EXISTS todos (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, completed BOOLEAN DEFAULT 0, created_at TEXT)',
        [],
        function() {
          console.log('表已创建或已存在');
        },
        function(err) {
          console.error('创建表失败:', err.message);
        }
      );
    });

    // 添加任务
    document.getElementById('addBtn').addEventListener('click', function() {
      const input = document.getElementById('taskInput');
      const title = input.value.trim();
      if (!title) return;

      db.transaction(function(tx) {
        tx.executeSql(
          'INSERT INTO todos (title, completed, created_at) VALUES (?, ?, ?)',
          [title, false, new Date().toISOString()],
          function(tx, result) {
            alert('任务添加成功!');
            input.value = '';
            loadTasks(); // 重新加载任务列表
          },
          function(err) {
            alert('添加失败:' + err.message);
          }
        );
      });
    });

    // 加载任务列表
    function loadTasks() {
      const list = document.getElementById('taskList');
      list.innerHTML = '';

      db.transaction(function(tx) {
        tx.executeSql(
          'SELECT * FROM todos ORDER BY created_at DESC',
          [],
          function(tx, results) {
            for (let i = 0; i < results.rows.length; i++) {
              const row = results.rows.item(i);
              const li = document.createElement('li');
              li.innerHTML = `
                <span style="margin-right: 10px;">${row.title}</span>
                <input type="checkbox" ${row.completed ? 'checked' : ''} data-id="${row.id}" />
                <button data-id="${row.id}" style="margin-left: 5px;">删除</button>
              `;
              list.appendChild(li);
            }
          },
          function(err) {
            console.error('加载失败:', err.message);
          }
        );
      });
    }

    // 监听复选框和删除按钮
    document.body.addEventListener('click', function(e) {
      const id = e.target.getAttribute('data-id');
      if (!id) return;

      if (e.target.type === 'checkbox') {
        db.transaction(function(tx) {
          tx.executeSql(
            'UPDATE todos SET completed = ? WHERE id = ?',
            [e.target.checked ? 1 : 0, id],
            function() {
              console.log('状态已更新');
            }
          );
        });
      } else if (e.target.tagName === 'BUTTON') {
        db.transaction(function(tx) {
          tx.executeSql(
            'DELETE FROM todos WHERE id = ?',
            [id],
            function() {
              alert('任务已删除');
              loadTasks();
            }
          );
        });
      }
    });

    // 页面加载时加载任务
    window.onload = loadTasks;
  </script>
</body>
</html>

这个应用具备以下功能:

  • 添加新任务
  • 显示所有任务
  • 切换完成状态
  • 删除任务
  • 数据持久化(即使刷新页面也保留)

总结与建议

HTML5 Web SQL 数据库虽然已被弃用,但它在特定场景下依然有其价值。尤其对于需要离线运行、结构化数据存储、且对兼容性要求不高的项目,它是一个简单高效的解决方案。

它的核心优势在于:

  • 使用标准 SQL 语法,学习成本低
  • 支持事务,保证数据一致性
  • 数据存储在本地,无需网络请求
  • 适合中小规模结构化数据管理

但也要注意它的局限性:

  • 不支持跨浏览器统一标准(IndexedDB 是未来方向)
  • 数据量受限于浏览器存储配额
  • 不支持复杂查询和索引优化

如果你正在开发一个新项目,建议优先考虑 IndexedDB 或 PouchDB 等现代方案。但如果在维护旧项目或需要快速实现本地数据存储,HTML5 Web SQL 数据库依然是一个值得了解的工具。

本文介绍了 HTML5 Web SQL 数据库的基本用法,包括打开数据库、创建表、增删改查操作,以及一个完整的待办事项应用实例。希望你能通过实践掌握这一技术,为你的 Web 应用增添本地数据处理能力。