Практика разработки Electron-vue 2. Знакомство с базой данных JSON на основе Lodash lowdb

внешний интерфейс JSON Vue.js Electron

предисловие

Некоторое время назад я использовалelectron-vueРазработано кросс-платформенное (в настоящее время поддерживающее Mac и Windows) бесплатное приложение для загрузки изображений с открытым исходным кодом——PicGo, я наступил на много ям в процессе разработки, причем не только со стороны бизнес-логики самого приложения, но и со стороны самого электрона. В процессе разработки этого приложения я многому научился. Поскольку я также начал изучать электрон с нуля, такой большой опыт также должен вдохновить и дать инструкции начинающим ученикам, которые хотят изучать разработку электронов. Поэтому я написал практический опыт разработки Электрона и объяснил его с точки зрения наиболее близкой к разработке реальных инженерных проектов. Надеюсь помочь всем.

Ожидается, что он будет расширен за счет нескольких серий статей или аспектов:

  1. Начало работы с электрон-вью
  2. Простая разработка основного процесса и процесса визуализации
  3. Представляем базу данных JSON на основе Lodash - lowdb
  4. Некоторые меры кроссплатформенной совместимости
  5. Выпуск и обновление через CI
  6. ... (подумайте о написании снова)

инструкция

PicGoсостоит в том, чтобы принятьelectron-vueразработан, поэтому, если выvue, то учиться вместе будет быстрее. Если ваш технический стек похож наreact,angular, то чисто по этому туториалу, хотя вы можете и не много узнать о построении рендер-сайда (который можно понимать как страницу), вы все же должны получить соответствующие знания по основной стороне (основной процесс электрон).

Если вы не читали предыдущую статью, вы можете сначала прочитать ее из предыдущей статьи.

Необходимость постоянного хранения данных

В отличие от некоторых демонстраций, написанных многими людьми, это просто вопрос запроса API и отображения веб-страницы. В конце концов, электронное приложение — это десктопное приложение, и если мышление останется на идее чистой веб-разработки, то смысл использования электрона будет потерян.

Постоянное хранилище данных на самом деле знакомо серверной части. Обычно это относится ко всему процессу хранения данных в памяти на диск с различными моделями хранения, а затем их чтению из модели хранения и чтению в память при необходимости. Модель хранения здесь обычно представляет собой базу данных, с которой мы знакомы. Когда дело доходит до баз данных, многие думают о MySQL, Mongodb, SQLite и других. Эти общие базы данных находятся в режиме Server-Client и требуют запуска сервера — обычно это то, что мы устанавливаем. Но, как правило, редко встретишь кого-то, кто просит кого-то установить настольное программное обеспечение одновременно с кем-то, кто назначает базу данных.

Потому что некоторые данные должны храниться локально, чтобы их можно было прочитать в следующий раз. Для электрона, поскольку для пользователей установка MySQL и Mongodb не является элегантным решением, если есть другие способы локального хранения данных, не беспокоясь о том, как их хранить, это хорошо и для нас, и для пользователей.

Выбор чистой базы данных JavaScript

Поскольку это стек технологий JS, я нашел несколько баз данных, реализованных на чистом JavaScript. После предварительного скрининга я обнаружил следующие два:

  1. nedb7800star (12 февраля 2018 г.)
  2. lowdb7269star (12 февраля 2018 г.)

сравнивать

Среди них в настоящее время более широко используется недб, количество звезд больше (по состоянию на 12.02.2018), много статей о применении недб и электрона. Однако nedb не поддерживается уже почти два года и изначально не поддерживает промисы, используя асинхронные обратные вызовы (хотя промисы можно реализовать с помощью сторонних плагинов).

lowdb разработан на основе lodash с использованием JSON в качестве базовой структуры хранения, благословлен lodash и очень прост в использовании. Преимущество в том, что он постоянно поддерживается и есть много полезных плагинов. И ключом является синхронная операция, использующая метод записи цепных вызовов, это похоже на jQuery. Кроме того, данные, хранящиеся в JSON, очень удобно вызывать или создавать резервные копии, что мне очень нравится.

Подводя итог, PicGo использует lowdb.

инициализация lowdb

Поскольку электрон поместил узлы как в основной процесс, так и в процесс рендерингаfsмодуль, поэтому мы можем легко использоватьfsсопутствующие операции. А lowdb по сути проходитfsДля чтения и записи файлов JSON он как раз соответствует нашим требованиям. Итак, согласно официальным документам, давайте сначала инициализируем его.

чтобы работатьfsБолее удобный, может также установитьfs-extra.

Создаватьdatastore.jsдокумент:

import Datastore from 'lowdb'
import FileSync from 'lowdb/adapters/FileSync'
import path from 'path'
import fs from 'fs-extra'
import { app } from 'electron'

const STORE_PATH = app.getPath('userData') // 获取electron应用的用户目录

const adapter = new FileSync(path.join(STORE_PATH, '/data.json')) // 初始化lowdb读写的json文件名以及存储路径

const db = Datastore(adapter) // lowdb接管该文件

export default db // 暴露出去

Затем мы можем представить это так в основном процессе и процессе рендерера:

import db from '../datastore' // 取决于你的datastore.js的位置

Ступай на яму

Если это только основные операции, описанные выше, то эта статья слишком проста. Путь к тому, чтобы ступить на яму внедрения lowdb электронами, только начался.

1. Процесс визуализации должен использовать удаленный модуль

Во-первых, из приведенной выше инициализации ясно видна проблема.appМодули уникальны для основного процесса, и процесс визуализации должен использоватьremote.appмодуль. Таким образом, приведенный выше код находится вrendererВ процессе будет сообщено об ошибке.

Поэтому первая модификация заключается в том, чтобы заставить его работать как в основном процессе, так и в процессе рендерера:

import Datastore from 'lowdb'
import FileSync from 'lowdb/adapters/FileSync'
import path from 'path'
import fs from 'fs-extra'
import { app, remote } from 'electron' // 引入remote模块

const APP = process.type === 'renderer' ? remote.app : app // 根据process.type来分辨在哪种模式使用哪种模块

const STORE_PATH = APP.getPath('userData') // 获取electron应用的用户目录

const adapter = new FileSync(path.join(STORE_PATH, '/data.json')) // 初始化lowdb读写的json文件名以及存储路径

const db = Datastore(adapter) // lowdb接管该文件

export default db // 暴露出去

2. Проблема пути инициализации режима разработки и режима производства

В режиме разработки, поAPP.getPath('userData')Полученный путь выглядит так:/Users/molunerfinn/Library/Application Support/Electron(под macOS). Это путь, который был создан автоматически. Итак, в режиме разработки путь инициализации уже существует.

Однако в производственном режиме это не так. В рабочем режиме при первом открытии приложенияAPP.getPath('userData')Полученный путь не создается, иdatastore.jsуже загружен. Итак, на этот раз пути инициализации не существует. Когда пользователи открывают приложение в первый раз, они сталкиваются со следующей ошибкой:

поэтому мы должныdatastore.jsСделайте вывод о том, существует ли путь:

fs здесь из модуля fs-extra

if (process.type !== 'renderer') {
  if (!fs.pathExistsSync(STORE_PATH)) { // 如果不存在路径
    fs.mkdirpSync(STORE_PATH) // 就创建
  }
}

3. Инициализировать данные

Потому что иногда нам нужно предварительно указать базовую структуру базы данных, например массив, поэтому мы инициализируем ее как[]. Если это объект и имеет конкретное значение, укажите его как конкретное значение. Структура данных инициализации не должна оцениваться каждый раз, когда данные считываются или записываются. Она должна быть инициализирована при первом создании базы данных, поэтому она записывается вdatastore.jsподходит.

Например, если я хочу инициализировать список загрузки, это должен быть массив, как показано ниже:

if (!db.has('uploaded').value()) { // 先判断该值存不存在
  db.set('uploaded', []).write() // 不存在就创建
}

4. Поле уникального идентификатора

Большинство людей, которые использовали MySQL, инициализируют самоувеличивающееся поле id в таблице как уникальный идентификатор данных. Хотя lowdb не может легко создать поле с автоинкрементным идентификатором, он можетlodash-idЭтот плагин может легко добавлять уникальное поле идентификатора к каждому новому элементу данных.

В форме:

{
  "height": 514,
  "type": "weibo",
  "width": 514,
  "id": "7f247aa7-ffeb-4bb1-87f1-a0d69824ec78"
}

Инициализация тоже удобна:

// ...
import LodashId from 'lodash-id'
// ...

const db = Datastore(adapter)
db._.mixin(LodashId) // 通过._mixin()引入

Инициализировать полный код

При выполнении вышеуказанных шагов код инициализации PicGo выглядит следующим образом, только для справки:

import Datastore from 'lowdb'
import LodashId from 'lodash-id'
import FileSync from 'lowdb/adapters/FileSync'
import path from 'path'
import fs from 'fs-extra'
import { remote, app } from 'electron'

const APP = process.type === 'renderer' ? remote.app : app
const STORE_PATH = APP.getPath('userData')

if (process.type !== 'renderer') {
  if (!fs.pathExistsSync(STORE_PATH)) {
    fs.mkdirpSync(STORE_PATH)
  }
}

const adapter = new FileSync(path.join(STORE_PATH, '/data.json'))

const db = Datastore(adapter)
db._.mixin(LodashId)

if (!db.has('uploaded').value()) {
  db.set('uploaded', []).write()
}

if (!db.has('picBed').value()) {
  db.set('picBed', {
    current: 'weibo'
  }).write()
}

if (!db.has('shortKey').value()) {
  db.set('shortKey', {
    upload: 'CommandOrControl+Shift+P'
  }).write()
}

export default db

Основные операции lowdb

Базовая операция базы данных — это не что иное, как CURD.

Это означает операции создания, обновления, извлечения и удаления.

Ниже описано основное использование lowdb.

Создайте

главным образом черезset()илиdefaults()метод. вdefaults()Инициализирует специально для пустых файлов JSON. (Однако аналогичного можно добиться с помощью set, как и инициализация, упомянутая в предыдущем разделе)

db.defaults({ posts: [], user: {}, count: 0 })
  .write() // 一定要显式调用write方法将数据存入JSON

Обратите внимание, что любая операция записи должна использоваться явноwrite()способ сохранить.

читать

db.get('posts').value() // []

Конечно, вы также можете запросить свой JSON, используя некоторые методы lodash.

Напримерfind()

db.get('posts')
  .find({ id: 1 })
  .value()

Обратите внимание, что любая операция чтения должна использоваться явноvalue()способ получения значения.

возобновить

Различные структуры обновляются по-разному.

Например, используйте присваивание для объектов и используйте для массивов.push()илиinsert()(метод предоставлен lowdb-id)

db.get('posts').insert({ // 对数组进行insert操作
  title: 'xxx',
  content: 'xxxx'
}).write()

можно использовать непосредственно для объектаset()обновить:

db.set('user.name', 'typicode') // 通过set方法来对对象操作
  .write()

Вы также можете написать:

db.set('user', {
  name: 'typicode'
}).write()

Очень гибкий, правда?

Чтобы обновить исходные данные, вы можете использовать update.

db.update('count', n => n + 1) // update方法使用已存在的值来操作
  .write()

Удалить

в состоянии пройтиremove()Метод удаляет подходящий элемент:

db.get('posts')
  .remove({ title: 'low!' })
  .write()

в состоянии пройтиunsetчтобы удалить атрибут:

db.unset('user.name')
  .write()

также черезlodash-idкоторый предоставилremoveById()Чтобы удалить элемент с указанным идентификатором:

db.get('posts')
  .removeById(id)
  .write()

Яма, фактически используемая lowdb

В процессе использования lowdb будет большая яма.Если следовать основной операции, то может показаться, что я вmainЗначение, хранящееся в процессе, вrendererНе могу прочитать в процессе.

Зачем? из-за прямой ссылкиdbНа самом деле это просто данные в памяти на данный момент. Lowdb будет считывать данные JSON в память во время использования. Новые данные записываются на диск только тогда, когда требуется операция записи.

База данных, полученная основным процессом и процессом рендеринга, считывается при открытии приложения. Без дополнительной обработки бд в памяти полученная основным процессом и бд в памяти полученная рендерером это не одна и та же бд, то есть т.н это не две ссылки на бд, а бд два. копии. Работа основного процесса над ним неизвестна процессу рендерера. Другими словами, если основной процесс выполняет какие-либо операции чтения или записи в базе данных, база данных, полученная модулем визуализации, по-прежнему остается прочитанной базой данных при открытии приложения. Таким образом, вы столкнетесь с тем, что основной процесс обновляет данные, но процесс рендеринга все еще не может получить новые данные.

Есть ли способ решить эту проблему? немного. Просто немного хлопотно. То есть в начале всех операций с БД перечитывать последнее состояние БД:

Например:

db.read().get('xxx').value()

db.read().set('xxx', 'xxx')

Обязательно обновлять область памяти с помощью read() перед каждой операцией db, чтобы гарантировать, что полученные данные будут самыми последними.

Удобный способ использования lowdb во Vue

Подобно тому, как многие люди вешают axios на цепочку прототипов vue в Vue, мы также можем использовать аналогичный метод, чтобы упростить использование lowdb во Vue.

Откройте входной файл проекта Vue, обычноmain.js

// ...
import db from '../datastore'
import Vue from 'vue'
// ...

Vue.prototype.$db = db

Таким образом, мы можем использоватьthis.$dbметод использования lowdb.

Суммировать

В этой статье подробно рассказывается о lowdb и использовании lowdb в электроне. я много развиваюсьPicGoВозникшие проблемы и ямы, на которые наступили. Возможно, за простыми предложениями в тексте кроются мои бесчисленные чтения и отладки. Надеюсь, эта статья даст вамelectron-vueРазвитие приносит некоторое вдохновение. Соответствующий код в тексте, вы можете найти его вPicGoнаходится в репозитории проекта. Если эта статья может помочь вам, это будет мое самое счастливое место. Если вам нравится, пожалуйста, следуйте за мнойблоги последующие события в этой серии статей.

Примечание: фотографии в этой статье принадлежат моей личной работе, если не указано иное, если вам нужно перепечатать, пожалуйста, отправьте личное сообщение