>  기사  >  웹 프론트엔드  >  자바스크립트로 로컬 데이터베이스를 구현하는 방법은 무엇입니까? (상세 분석)

자바스크립트로 로컬 데이터베이스를 구현하는 방법은 무엇입니까? (상세 분석)

不言
不言앞으로
2018-09-28 15:57:253934검색

이 글의 내용은 JavaScript로 로컬 데이터베이스를 구현하는 방법에 관한 것입니다. (상세한 분석) 도움이 필요한 친구들이 참고할 수 있을 만큼 참고할만한 가치가 있습니다.

프런트엔드에서는 일부 데이터를 저장해야 하는 경우가 많으며 여기서 저장이란 장기 저장을 의미합니다. 이전 아이디어는 데이터를 쿠키에 저장하거나, 키를 쿠키에 저장하고, 다른 데이터는 서버에 저장하는 것이었습니다.

이 장면은 용도가 다양하며 매우 성숙하고 사용하기 쉽습니다. 하지만 나는 여전히 데이터베이스나 웹 SQL과 같이 로컬에 장기간 저장할 수 있는 일종의 데이터를 원합니다.

신세대 브라우저는 기본적으로 로컬 데이터베이스를 지원하므로 필요할 때 직접 사용하세요. 작동하지 않으면 Storage를 사용하여 작업을 수행할 수 있습니다.

데이터베이스 같은 기능이 필요한데 호환되는 저장 방법이 없다면 어떻게 하나요? 이러한 항목을 로컬로 암호화하고 저장하려면 어떻게 해야 합니까? 많은 것을 저장하고 싶다면 어떻게 해야 할까요?

현재 React Native를 사용하다가 이런 현상이 발생하고 있습니다. 많은 양의 데이터를 로컬에 저장해야 합니다. 누군가는 SQLite를 직접 사용하는 것이 더 좋지 않을까요?

알겠습니다. 전혀 괜찮습니다. 저는 여기서 프론트 엔드 태도로 개발하고 있습니다. 일부 학생에게 SQLite는 필요하지 않지만 대량의 데이터를 저장하는 간단한 방법만 필요한 경우 어떻게 해야 합니까?

사용 시나리오는 다양할 수 있으며, 구현의 맨 아래 레이어를 마음대로 교체할 수 있고, 구현 방법도 마음대로 작성할 수 있습니다. 여기서 나는 세상을 창조하는 프런트엔드의 태도를 바탕으로 비공식적이고 프런트엔드 친화적인 데이터 저장소를 구축하고 있습니다.

기본 스토리지

여기 사용 시나리오는 React Native이므로 RN의 AsyncStorage를 사용합니다.

저장할 모든 데이터를 객체와 문자열로 변환합니다. 여기서 핵심 아이디어는 직렬화입니다. 모든 데이터를 문자열로 저장합니다.

import { AsyncStorage } from 'react-native';

exports.setItem = async (key, value) => {    
let item = JSON.stringify({

       v: value

   });    
   return await AsyncStorage.setItem(key, item);

}

当读取的时候也需要做一次转化,将字符串转成当初存入的数据。

exports.getItem = async (key) => {    
let item = await AsyncStorage.getItem(key);    
if (!item) {        
return null;

   }    
   return JSON.parse(item).v || null;

}

특별한 처리가 필요한 것은 목록 획득입니다. RN에는 여러 키를 기반으로 여러 데이터 조각을 반환하는 API가 있습니다.

배열 객체를 반환합니다. 배열 번호 0은 데이터 저장의 키 값이고 시퀀스 번호 1은 데이터 저장의 특정 문자열입니다.

exports.getlist = async (keys) => {    
let list = await AsyncStorage.multiGet(keys);

   list = list.map(item => JSON.parse(item[1]).v || null);    
   return list;

}

그 밖에도 사용된 몇 가지 방법도 꺼내보겠습니다. 위와 동일한 구성을 유지하면서 여기에 레이어를 하나 더 중첩합니다.

exports.removeItem = async (key) => await AsyncStorage.removeItem(key);

exports.removeItems = async (keys) => await AsyncStorage.multiRemove(keys);

exports.allKeys = async () => await AsyncStorage.getAllKeys();

기본 최적화

위 내용은 단순한 구현일 뿐 특별한 요구 사항이 없으면 거의 동일합니다. 그러나 더 나아가고 싶다면 최적화를 고려할 수 있습니다.

예를 들어 JSON 변환 속도를 최적화합니다. JSON 객체 방식을 사용하여 변환할 때 실제로 숫자형을 결정하는 과정이 있습니다. 데이터 유형을 미리 정의하는 경우. 변환할 때 다시 판단할 필요가 없습니다.

모델 객체를 정의하고 이 테이블에 필요한 필드를 미리 정의할 수 있습니다. Sequelize가 어떻게 정의되는지 살펴볼 수 있습니다. 관계형 데이터베이스 방법에 따르면 이를 수행하는 것은 매우 간단합니다.

//用户对象const users = db.define('t_users', {

   id: {

       type: Sequelize.INTEGER,

       primaryKey: true,

   },    //用户名

   username: {

       type: Sequelize.STRING

   },    //密码

   pwd: {

       type: Sequelize.STRING

   },    //状态

   status: {

       type: Sequelize.INTEGER

   },    //昵称

   nickname: {

       type: Sequelize.STRING

   },    //token

   token: {

       type: Sequelize.STRING

   },

   create_time: {

       type: Sequelize.TIME

   }

}, {

   freezeTableName: true,

   timestamps: false,

});

스토리지 구현

여기서는 관계형 데이터베이스 구현을 참고하겠습니다.

우선 테이블과 데이터베이스를 나누어야 합니다. 이렇게 하면 데이터를 저장할 때 이 정보에 덜 주의를 기울이고 데이터 작업에 집중할 수 있습니다.

constructor(tableName = "table", db = "db") {        //检查库,表是否存在

       //初始化索引表

       this.db = db;        
       this.tableName = tableName;        
       this.tableKey = db + "_" + tableName;        
       this.init();

   }

현재 객체 내부에 별도로 저장하세요. 객체 생성 시 다양한 라이브러리와 테이블을 기반으로 다양한 작업 방법을 생성할 수 있습니다.

여기에서는 클래스가 사용되며 각 테이블은 별도의 개체에 해당합니다.

RN에서 제공하는 저장 방식을 사용하고 있기 때문에 여기서 추가와 업데이트는 사실 동일한 방식입니다.

추가 시 현재 타임스탬프를 기준으로 고유 ID가 생성되며, 이 ID는 데이터베이스에 저장하기 위한 키로 사용됩니다.

그래서 사용시 따로 id를 저장할 필요는 없습니다. 하지만 이 id가 필요한 것과 다르다고 생각된다면 직접 id를 정의해 키 값으로 저장할 수도 있습니다.

//添加和更新

   async add(data = {}) {        
   if (data.constructor !== Object) return;        
   if (!data._id)data._id = uuid();

       await setItem(this.tableKey + "_" + data._id, data);        
       return data;

   }

에서는 획득 시 ID를 기준으로 별도의 획득 방법을 제공합니다. 여기서 고려되는 점은 id를 통해 얻는 것이 매우 간단하고 편리하다는 것입니다. 일부 데이터는 빠르게 읽을 수 있으며 한 줄씩 쿼리할 필요가 없습니다.

/**

* 通过id查询

* @param {*} id

*/
async getById(id) {    
if (!id) return {};    
return await getItem(this.tableKey + "_" + id);

}

ID 기반 쿼리에 비해 퍼지 쿼리는 실제로 매우 느립니다. 실제로 필요하지 않은 경우에는 이 퍼지 쿼리를 사용하지 않는 것이 좋습니다.

여기에는 반환된 개체를 기반으로 이 데이터 행이 필요한지 여부를 결정할 수 있는 사용자 지정 쿼리 방법이 제공됩니다.

상위 매개변수를 추가하여 반환되는 수를 제한할 수도 있습니다. 이 매개변수를 사용하면 데이터가 많을 때 성능이 향상될 수도 있습니다.

필터링 방식으로 쿼리

* @param {*} fn

*/async get(fn, top = 0) {    
let keys = await allKeys();    
if (keys.length == 0) return [];    
if (top > 0 && keys.length > top) keys.length = top;    
const listkey = keys.filter(item => item.indexOf(this.tableKey + "_") === 0);    
if (listkey.length == 0) return [];    
let list = await getlist(listkey);

   list = list.filter(item => fn(item));    
   return list;

}

마지막으로 삭제, 삭제 메소드를 추가하면 간단한 삭제 라이브러리가 완성됩니다.

/**

* 删除

* @param {*} id

*/
async delete(id) {    
if (!id) return {};

   await removeItem(this.tableKey + "_" + id);

}/**

* 清空表

*/
async clear() {    
let keys = await allKeys();    
const listkey = keys.filter(item => item.indexOf(this.tableKey + "_") === 0);    
if (listkey.length == 0) return;

   removeItems(listkey);

}

사용시에는 객체를 생성한 후 필요한 곳에 호출하기만 하면 됩니다. 사용이 간단하고 편리하며, 최적화 후에는 클라이언트측 Redis로도 사용할 수 있습니다.

//初始化数据库
let db=new JSDB();
//添加一个自定义数据
db.add({name:"test",key:"abc"});
//根据id获取数据
db.getById("1223467890");
//根据条件查询数据
db.get(d=>d.name==="test");
//删除对应的数据
db.delete("1223467890");
//情况所有数据
db.clear()

생성 최적화

최초로 최적화해야 할 것은 객체 생성입니다. 각 객체 생성에는 실제로 많은 돈이 소비됩니다. 이 소비를 줄일 수 있다면 좋지 않을까요?

여기에서는 데이터베이스 풀의 개념을 배우고 개체 풀 방법을 구현합니다. 객체가 생성된 후에는 직접적인 반환이 없으며 풀 작업을 거쳐야 합니다.

将对象放入池内,并在页面销毁的时候重置为一个空对象。下次请求创建的时候就不必再创建新的了。直接赋值表、库的名称就可以使用了。内存毫无变化,并且有点想笑。

优化查询

每次查询都需要去读 Stroage 还是很麻烦的,尤其这个操作是异步操作,是需要发消息到 Native 端的。

我们可以将上次读取的数据先存在一个变量中,如果下次还需要使用这行数据,就完全不需要再去读取了。这样就可以很简单的提供读取速度。

//按照使用情况保存数据到缓存
async getById(id) {    
if (!id) return {};

   id = this.tableKey + "_" + id;    //如果有缓存

   if (this.cacheList.has(id)) {        
   let tmp = this.cacheList.get(id);        //如果过量了

       if (this.cacheKeyList.length > 20) {            
       this.cacheKeyList.push(id);            
       let k = this.cacheKeyList.shift();            
       this.cacheList.delete(k);

       }        
       return tmp;

   }    
   this.cacheKeyList.push(id);    
   this.cacheList.set(tmp);    
   return await getItem(id);

}

这个方式还可以继续优化。将变量中保存的数据限制数量,防止数量太多超过了 App 的限制。

还可以将这个保存的时限做一个逻辑判断,常使用的数据放在里面,不常用的就找机会删除。

使用这种方式也可以优化变量中数据的有效性,减少变量占用内存的大小。

不过实现的方式尽量不要使用定时器的形式,可以考虑使用触发式的。在条件满足的时候再触发删除动作。

优化写入

上面提到读取的时候需要放入变量来提高读取速度。我们顺便想到写入的速度是不是也可以提高啊?

我们将要存的数据放在临时的变量里,如果超过我们设置的时间或者数据长度超过我们设置的数量就触发一次保存操作。

这里要注意,保存数据的变量和存入时候使用的变量要交替使用,防止出现丢数据的情况。

比如:存的时候使用变量1,在写到数据库之前,将要存的对象改成变量2,然后读取变量1的数据并存入数据库中。这就是双缓存写入。

当然还是要判断一次 App 退出事件的,如果 App 退出了,请一定要把变量中的内容保存一次,不然你的心血就全丢了。

注意写入要和上面的读取结合起来,不然就会发现写入的数据会有一个很大的延迟。

//添加和更新
async add(data = {}) {    
if (data.constructor !== Object) return;

   data._id = uuid();    
   const key = this.tableKey + "_" + data._id    
   this.writeList.set(key, data);    
   this.write();    //this.read();

   return data;

}
//写入的实现,now立马写入
write(now = false) {    
if (now || this.writeList.size > 20) {        
let tmp = this.writeList;        
this.writeList = this.backWriteList;        
this.backWriteList = tmp;        
this.backWriteList.forEach((v, k) => {

           setItem(k, v);

       })

   }

}

好了,一个简单的数据库就完成了。想要使用的可以先在 npm 上搜索 react-native-jsdb 这个库。我已经将第一部分实现放在了 npm 上,后续的优化也会慢慢地开源出来。

위 내용은 자바스크립트로 로컬 데이터베이스를 구현하는 방법은 무엇입니까? (상세 분석)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제