整个三月,我都在改进排版助手,增加了不少实用的功能,特别是中英文混排的格式美化,引号修正,注释清理,以及繁简转换,都让我感觉非常有成就感。唯一感觉遗憾的是,没有一个好的自动保存/版本恢复功能,只能每隔 5 秒自动存盘,且做不到监听文本的变化,定时期只是机械的运行,所以很多时候,存的文本都不是关键状态点,恢复回来,也不是我想要的版本。

后来我想到文本编辑器里的 撤消/重做 功能,或许可以粗糙的模仿一个,研究了两天,发现远远超出我目前的能力。在网上找到一个几乎接近我的设想的版本,但我现在也没有能力移植。而浏览器自带的 撤消/重做,不同品牌的实现并不一样,我也没有办法拦截默认的快捷键。

后来我变通了下,其实不必全时监听文本的变化,毕竟排版助手不是文本编辑器,只需要在排版之前,保存上一个版本即可。采用这个思路以后,问题简化了一大半。接下来就是怎么实现存取最近的文本内容,如果用 MySQL,我很容易做到,但客户端存储的主流,localStorage 和 IndexedDB,都是非关系型数据库,我此前没有编程经验,于是请教 ChatGPT。

问:

请用 localStorage 做为数据库,记录一段文本的版本变化过程,每修改一次,都将修改后的文本存入 localStorage,当存储超过十条以后,删除第一条,以使数据库中永远只保存最近的十个版本。页面以上有一个按钮,一个 textarea,第一次点击, textarea 显示第十个版本的文本,第二次点击,显示第九个版本的文本,以此类推,第十一次点击,显示没有数据。

答:

以下是一个示例代码,使用 localStorage 记录一段文本的版本变化过程,并在页面上添加一个按钮和一个 textarea,每次点击按钮时,就会从 localStorage 中获取一条数据,并在 textarea 中显示。当显示完所有记录后,会显示“没有数据”,并且当存储超过十条以后,会删除第一条,以使数据库中永远只保存最近的十个版本。

// 获取文本框和按钮
var textarea = document.getElementById('textarea');
var button = document.getElementById('button');

// 获取版本号
var version = localStorage.getItem('version') || 0;

// 获取文本内容
var content = localStorage.getItem('content') || '';

// 显示文本内容
textarea.value = content;

// 点击按钮时,显示上一个版本的文本内容
button.addEventListener('click', function() {
  if (version > 0) {
    version--;
    content = localStorage.getItem('content' + version);
    textarea.value = content;
  } else {
    textarea.value = '没有数据';
  }
});

// 监听文本框的变化
textarea.addEventListener('input', function() {
  // 获取文本内容
  content = textarea.value;

  // 存储文本内容
  localStorage.setItem('content' + version, content);

  // 更新版本号
  version++;

  // 删除第一条记录,以使数据库中永远只保存最近的十个版本
  if (version > 10) {
    localStorage.removeItem('content' + (version - 11));
  }

  // 存储版本号
  localStorage.setItem('version', version);
});

这个示例代码会在页面上添加一个按钮和一个 textarea,每次点击按钮时,就会从 localStorage 中获取一条数据,并在 textarea 中显示。当显示完所有记录后,会显示“没有数据”,并且当存储超过十条以后,会删除第一条,以使数据库中永远只保存最近的十个版本。当在文本框中输入内容时,它会将文本内容存储到 localStorage 中,并更新版本号。


说实在话,看到这段示例代码,我感觉豁然开朗,只要稍作修正,就可完美的附加到排版助手之上。

这是最终的效果:https://chenzixin.com/ivy/text/

而实现了初步的版本恢复之后,我暂时搁置了排版助手的维护,这是一个里程碑的进步。


我一度想将 localStorage 升级为 IndexedDB,因此,也问过 IndexedDB 的实现方式,以下为对话回顾:

问:

请问 IndexedDB 如何实现只存储最近的10条数据?给一个代码示例?

答:

IndexedDB 是一种客户端存储数据的方式,可以在浏览器中存储大量数据。如果你想只存储最近的 10 条数据,可以使用以下代码示例:

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

// 创建对象存储空间
request.onupgradeneeded = function(event) {
  var db = event.target.result;
  var objectStore = db.createObjectStore('myObjectStore', { keyPath: 'id', autoIncrement: true });
};

// 添加数据
request.onsuccess = function(event) {
  var db = event.target.result;
  var transaction = db.transaction(['myObjectStore'], 'readwrite');
  var objectStore = transaction.objectStore('myObjectStore');

  // 添加数据
  objectStore.add({ data: 'data1' });
  objectStore.add({ data: 'data2' });
  objectStore.add({ data: 'data3' });
  objectStore.add({ data: 'data4' });
  objectStore.add({ data: 'data5' });
  objectStore.add({ data: 'data6' });
  objectStore.add({ data: 'data7' });
  objectStore.add({ data: 'data8' });
  objectStore.add({ data: 'data9' });
  objectStore.add({ data: 'data10' });

  // 删除多余的数据
  var countRequest = objectStore.count();
  countRequest.onsuccess = function() {
    var count = countRequest.result;
    if (count > 10) {
      var deleteRequest = objectStore.delete(count - 10);
      deleteRequest.onsuccess = function() {
        console.log('Deleted old data');
      };
    }
  };

  transaction.oncomplete = function() {
    console.log('Data added and old data deleted');
  };
};

这个示例代码会创建一个名为 myDatabase 的数据库,并在其中创建一个名为 myObjectStore 的对象存储空间。然后,它会添加 10 条数据到对象存储空间中,并在添加完数据后删除多余的数据,以保证只有最近的 10 条数据被存储。

问:

请写代码,IndexedDB 存储了十条数据,在页面上每点击一次按钮,就在 textarea 中显示其中一条,即点击第一次,显示第十条,点击第二次,显示第九条,点击第三次,显示第八条,以此类推,点击第十一次,显示“没有记录”。

答:

以下是一个示例代码,实现了在 IndexedDB 中存储十条数据,并在页面上每点击一次按钮,就在 textarea 中显示其中一条,直到显示完所有记录,最后显示“没有记录”:

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

// 创建对象存储空间
request.onupgradeneeded = function(event) {
  var db = event.target.result;
  var objectStore = db.createObjectStore('myObjectStore', { keyPath: 'id', autoIncrement: true });
};

// 添加数据
request.onsuccess = function(event) {
  var db = event.target.result;
  var transaction = db.transaction(['myObjectStore'], 'readwrite');
  var objectStore = transaction.objectStore('myObjectStore');

  // 添加数据
  for (var i = 1; i <= 10; i++) {
    objectStore.add({ data: 'data' + i });
  }

  transaction.oncomplete = function() {
    console.log('Data added');
  };
};

// 显示数据
var count = 10;
var button = document.getElementById('button');
var textarea = document.getElementById('textarea');

button.addEventListener('click', function() {
  var db = request.result;
  var transaction = db.transaction(['myObjectStore'], 'readonly');
  var objectStore = transaction.objectStore('myObjectStore');

  var getRequest = objectStore.get(count);
  getRequest.onsuccess = function() {
    if (getRequest.result) {
      textarea.value = getRequest.result.data;
      count--;
    } else {
      textarea.value = '没有记录';
    }
  };
});

这个示例代码会创建一个名为 myDatabase 的数据库,并在其中创建一个名为 myObjectStore 的对象存储空间。然后,它会添加 10 条数据到对象存储空间中。最后,它会在页面上添加一个按钮和一个 textarea,每次点击按钮时,就会从 IndexedDB 中获取一条数据,并在 textarea 中显示。当显示完所有记录后,会显示“没有记录”。

问:

请用游标实现上述取数据的逻辑。

答:

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

// 创建对象存储空间
request.onupgradeneeded = function(event) {
  var db = event.target.result;
  var objectStore = db.createObjectStore('myObjectStore', { keyPath: 'id', autoIncrement: true });
};

// 添加数据
request.onsuccess = function(event) {
  var db = event.target.result;
  var transaction = db.transaction(['myObjectStore'], 'readwrite');
  var objectStore = transaction.objectStore('myObjectStore');

  // 添加数据
  for (var i = 1; i <= 10; i++) {
    objectStore.add({ data: 'data' + i });
  }

  transaction.oncomplete = function() {
    console.log('Data added');
  };
};

// 显示数据
var count = 10;
var button = document.getElementById('button');
var textarea = document.getElementById('textarea');

button.addEventListener('click', function() {
  var db = request.result;
  var transaction = db.transaction(['myObjectStore'], 'readonly');
  var objectStore = transaction.objectStore('myObjectStore');
  var cursor = objectStore.openCursor(null, 'prev');

  cursor.onsuccess = function(event) {
    var cursor = event.target.result;
    if (cursor && count > 0) {
      textarea.value = cursor.value.data;
      count--;
      cursor.continue();
    } else {
      textarea.value = '没有记录';
    }
  };
});

这个示例代码与之前的代码类似,但是使用了游标来遍历对象存储空间中的数据。在点击按钮时,它会打开一个游标,并从最后一条记录开始遍历,直到遍历完所有记录或者显示了 10 条记录。当游标遍历完所有记录后,会显示“没有记录”。

最后修改:2023 年 04 月 12 日
如果觉得我的文章对你有用,请随意赞赏