>웹 프론트엔드 >JS 튜토리얼 >js 개발 데이터베이스 공유 예시

js 개발 데이터베이스 공유 예시

小云云
小云云원래의
2018-03-31 16:57:171929검색

프런트엔드는 일부 데이터를 저장해야 하는 경우가 많으며 여기서 저장은 장기 저장을 의미합니다. 이전 아이디어는 데이터를 쿠키에 저장하거나, 키를 쿠키에 저장하고, 다른 데이터는 서버에 저장하는 것이었습니다. 이러한 장면은 용도가 다양하며 매우 성숙하고 사용하기 쉽습니다. 하지만 저는 여전히 데이터베이스나 웹 SQL처럼 로컬에 장기간 저장할 수 있는 데이터를 원합니다.

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

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

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

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

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

기본 스토리지

여기 사용 시나리오는 ReactNative이므로 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 기반 쿼리에 비해 퍼지 쿼리는 실제로 매우 느립니다. 실제로 필요하지 않은 경우에는 이 퍼지 쿼리를 사용하지 않는 것이 좋습니다. 여기에는 반환된 개체를 기반으로 이 데이터 행이 필요한지 여부를 결정할 수 있는 사용자 지정 쿼리 메서드가 제공됩니다. 반환되는 수를 제한하기 위해 top 매개변수를 추가할 수도 있습니다. 이 매개변수를 사용하면 데이터가 많을 때 성능이 향상될 수도 있습니다.

/**
 * 通过过滤方法查询
 * @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를 읽는 것은 여전히 ​​매우 번거로운 작업입니다. 특히 이 작업은 비동기 작업이므로 네이티브 측에 메시지를 보내야 합니다.

마지막으로 읽은 데이터를 변수에 저장할 수 있습니다. 다음에 이 데이터 줄을 사용해야 하는 경우 다시 읽을 필요가 없습니다. 이는 읽기 속도를 쉽게 제공할 수 있습니다.

이 방법은 계속해서 최적화될 수 있습니다. 변수에 저장되는 데이터의 양을 제한하여 앱 한도를 초과하지 않도록 하세요. 또한 저장 기한을 논리적으로 판단하여 자주 사용하는 데이터를 넣어두고, 자주 사용하지 않는 데이터는 삭제할 기회를 찾을 수도 있습니다.

이 방법을 사용하면 변수의 데이터 효율성을 최적화하고 변수가 차지하는 메모리 크기를 줄일 수도 있습니다. 하지만 구현 방법에서는 타이머를 사용하지 않도록 하세요. 트리거 유형을 사용하는 것도 고려해 보세요. 조건이 충족되면 삭제 작업이 트리거됩니다.

글쓰기 최적화

위에서 언급했듯이 읽기 속도를 높이려면 읽을 때 변수를 넣어야 합니다. 그런데 쓰기 속도도 향상될 수 있을까?

저장할 데이터를 임시 변수에 넣어두었습니다. 설정한 시간이나 데이터 길이를 확인하고 설정한 수량을 확인하면 저장 작업이 실행됩니다.

여기서 주의할 점은 데이터 저장에 사용되는 변수와 저장 시 사용되는 변수는 데이터 손실을 방지하기 위해 서로 바꿔서 사용해야 한다는 점입니다.

예: 저장할 때 변수 1을 사용합니다. 데이터베이스에 쓰기 전에 저장할 개체를 변수 2로 변경한 다음 변수 1의 데이터를 읽어 데이터베이스에 저장합니다. 이는 이중 버퍼링 쓰기입니다.

물론, 앱의 종료 이벤트를 결정해야 합니다. 앱이 종료되면 해당 내용을 변수에 한 번 저장해 두시기 바랍니다. 그렇지 않으면 모든 노력이 손실됩니다.


자, 간단한 데이터베이스가 완성되었습니다. 사용하시려면 먼저 npm에서 이 라이브러리를 검색하시면 됩니다. 구현의 첫 번째 부분을 npm에 넣었으며 후속 최적화는 완전히 오픈 소스화될 것입니다. react-native-jsdb

npm 창고 주소

기사 주소

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

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

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

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

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

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

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

기본 스토리지

여기 사용 시나리오는 ReactNative이므로 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();
    }

将它们分开存储在当前对象内部,在创建对象的时候就可以根据不同的库、表创建不同的操作方法。这里使用的是class,每个表都对应一个单独的对象。

由于我们使用的是RN提供的存储方法,所以这里的添加和更新其实是一个方法。在添加的时候会根据当前时间戳创建一个唯一id,使用这个id作为key存储在数据库中。所以在使用的时候不需要再单独存入id,不过如果你觉得这个id跟你需要的有差别也可以自己定义一个id来作为key值存储。

//添加和更新
    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查询来说,模糊查询确实很慢,如果不是真实需要,还是不要使用这种模糊查询的好。这里提供了一个自定义查询的方法,可以根据返回的对象判断是否需要这行数据。同时也可以添加top参数来限制返回的数量。使用这个参数也可以在数据很多的时候提高性能。

/**
 * 通过过滤方法查询
 * @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端的。

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

这个方式还可以继续优化。将变量中保存的数据限制数量,防止数量太多超过了APP的限制。还可以将这个保存的时限做一个逻辑判断,常使用的数据放在里面,不常用的就找机会删除。

使用这种方式也可以优化变量中数据的有效性,减少变量占用内存的大小。不过实现的方式尽量不要使用定时器的形式,可以考虑使用触发式的。在条件满足的时候再触发删除动作。

优化写入

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

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

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

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

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


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

위 내용은 js 개발 데이터베이스 공유 예시의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.