feat(search): use dexie.js instead of localStorage (#2464)

This commit is contained in:
Luffy 2024-07-31 08:19:00 +08:00 committed by GitHub
parent 7cbd5322d0
commit 42f25482fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 66 additions and 29 deletions

6
package-lock.json generated
View File

@ -10,6 +10,7 @@
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"dexie": "^4.0.8",
"medium-zoom": "^1.1.0",
"opencollective-postinstall": "^2.0.2",
"prismjs": "^1.29.0",
@ -5929,6 +5930,11 @@
"node": ">= 0.8.0"
}
},
"node_modules/dexie": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.8.tgz",
"integrity": "sha512-1G6cJevS17KMDK847V3OHvK2zei899GwpDiqfEXHP1ASvme6eWJmAp9AU4s1son2TeGkWmC0g3y8ezOBPnalgQ=="
},
"node_modules/diff-sequences": {
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",

View File

@ -36,6 +36,7 @@
"*.js": "eslint --fix"
},
"dependencies": {
"dexie": "^4.0.8",
"medium-zoom": "^1.1.0",
"opencollective-postinstall": "^2.0.2",
"prismjs": "^1.29.0",

View File

@ -2,8 +2,33 @@ import {
getAndRemoveConfig,
getAndRemoveDocsifyIgnoreConfig,
} from '../../core/render/utils.js';
import Dexie from 'dexie';
let INDEXS = {};
let INDEXES = {};
const db = new Dexie('docsify');
db.version(1).stores({
search: 'slug, title, body, path, indexKey',
expires: 'key, value',
});
async function saveData(maxAge, expireKey) {
INDEXES = Object.values(INDEXES).flatMap(innerData =>
Object.values(innerData),
);
await db.search.bulkPut(INDEXES);
await db.expires.put({ key: expireKey, value: Date.now() + maxAge });
}
async function getData(key, isExpireKey = false) {
if (isExpireKey) {
const item = await db.expires.get(key);
return item ? item.value : 0;
}
const item = await db.search.where({ indexKey: key }).toArray();
return item ? item : null;
}
const LOCAL_STORAGE = {
EXPIRE_KEY: 'docsify.search.expires',
@ -73,12 +98,7 @@ function getListData(token) {
return token.text;
}
function saveData(maxAge, expireKey, indexKey) {
localStorage.setItem(expireKey, Date.now() + maxAge);
localStorage.setItem(indexKey, JSON.stringify(INDEXS));
}
export function genIndex(path, content = '', router, depth) {
export function genIndex(path, content = '', router, depth, indexKey) {
const tokens = window.marked.lexer(content);
const slugify = window.Docsify.slugify;
const index = {};
@ -101,7 +121,13 @@ export function genIndex(path, content = '', router, depth) {
title = getAndRemoveDocsifyIgnoreConfig(str).content;
}
index[slug] = { slug, title: title, body: '' };
index[slug] = {
slug,
title: title,
body: '',
path: path,
indexKey: indexKey,
};
} else {
if (tokenIndex === 0) {
slug = router.toURL(path);
@ -109,6 +135,8 @@ export function genIndex(path, content = '', router, depth) {
slug,
title: path !== '/' ? path.slice(1) : 'Home Page',
body: token.text || '',
path: path,
indexKey: indexKey,
};
}
@ -129,6 +157,9 @@ export function genIndex(path, content = '', router, depth) {
index[slug].body = token.text || '';
}
index[slug].path = path;
index[slug].indexKey = indexKey;
}
});
slugify.clear();
@ -148,13 +179,6 @@ export function ignoreDiacriticalMarks(keyword) {
*/
export function search(query) {
const matchingResults = [];
let data = [];
Object.keys(INDEXS).forEach(key => {
data = [
...data,
...Object.keys(INDEXS[key]).map(page => INDEXS[key][page]),
];
});
query = query.trim();
let keywords = query.split(/[\s\-\\/]+/);
@ -162,7 +186,7 @@ export function search(query) {
keywords = [query, ...keywords];
}
for (const post of data) {
for (const post of INDEXES) {
let matchesScore = 0;
let resultStr = '';
let handlePostTitle = '';
@ -235,7 +259,7 @@ export function search(query) {
return matchingResults.sort((r1, r2) => r2.score - r1.score);
}
export function init(config, vm) {
export async function init(config, vm) {
const isAuto = config.paths === 'auto';
const paths = isAuto ? getAllPaths(vm.router) : config.paths;
@ -269,12 +293,12 @@ export function init(config, vm) {
const expireKey = resolveExpireKey(config.namespace) + namespaceSuffix;
const indexKey = resolveIndexKey(config.namespace) + namespaceSuffix;
const isExpired = localStorage.getItem(expireKey) < Date.now();
const isExpired = (await getData(expireKey, true)) < Date.now();
INDEXS = JSON.parse(localStorage.getItem(indexKey));
INDEXES = await getData(indexKey);
if (isExpired) {
INDEXS = {};
INDEXES = {};
} else if (!isAuto) {
return;
}
@ -283,14 +307,25 @@ export function init(config, vm) {
let count = 0;
paths.forEach(path => {
if (INDEXS[path]) {
const pathExists = Array.isArray(INDEXES)
? INDEXES.some(obj => obj.path === path)
: false;
if (pathExists) {
return count++;
}
Docsify.get(vm.router.getFile(path), false, vm.config.requestHeaders).then(
result => {
INDEXS[path] = genIndex(path, result, vm.router, config.depth);
len === ++count && saveData(config.maxAge, expireKey, indexKey);
async result => {
INDEXES[path] = genIndex(
path,
result,
vm.router,
config.depth,
indexKey,
);
if (len === ++count) {
await saveData(config.maxAge, expireKey);
}
},
);
});

View File

@ -20,7 +20,6 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
const docsifyInitConfig = {
config: {
name: 'Docsify Name',
themeColor: 'red',
},
markdown: {
coverpage: `
@ -58,8 +57,6 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
scriptURLs: [
// docsifyInit() route
'data-test-scripturls.js',
// Server route
'/dist/plugins/search.js',
],
style: `
body {
@ -76,7 +73,6 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
// Verify config options
expect(typeof window.$docsify).toBe('object');
expect(window.$docsify).toHaveProperty('themeColor', 'red');
expect(document.querySelector('.app-name').textContent).toContain(
'Docsify Name',
);
@ -101,7 +97,6 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
// Verify docsifyInitConfig.scriptURLs were executed
expect(document.body.hasAttribute('data-test-scripturls')).toBe(true);
expect(document.querySelector('.search input[type="search"]')).toBeTruthy();
// Verify docsifyInitConfig.script was added to the DOM
expect(