Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.
Перепись приложения. Нативного.Перепись приложения. Нативного.
На JS. DoneНа JS. Done
Тимофей Лавренюк
@geek_timofey
1
О себеО себе
2
О проектеО проекте
3 . 1
3 . 2
Архитектура
3 . 3
Архитектура
3 . 3
ЯдроЯдро
Архитектура
3 . 3
ЯдроЯдро
RPC ServerRPC ServerАрхитектура
3 . 3
ЯдроЯдро
А что умеет?А что умеет?
3 . 4
ЯдроЯдро
Работа с локальной БДА что умеет?А что умеет?
3 . 4
ЯдроЯдро
ШифрованиеРабота с локальной БДА что умеет?А что умеет?
3 . 4
ЯдроЯдро
ШифрованиеРабота с локальной БД
Общение с RPC-сервером
А что умеет?А что умеет?
3 . 4
ЯдроЯдро
ШифрованиеРабота с локальной БД
Работа с PDFОбщение с RPC-сервером
А что умеет?А что умеет?
3 . 4
Настал 2017 годНастал 2017 год
3 . 5
Настал 2017 годНастал 2017 год
Нужна web-версия...
3 . 5
СРОЧНО НУЖНАСРОЧНО НУЖНА
WEB-ВЕРСИЯ !!!11WEB-ВЕРСИЯ !!!11 3 . 6
3 . 7
WEB-ВерсияWEB-Версия
1.01.0
3 . 8
Старая архитектура
3 . 9
SPASPA
Старая архитектура
3 . 9
SPASPA
REST APIREST API
ЯдроЯдро
Старая архитектура
3 . 9
SPASPA
REST APIREST API
ЯдроЯдро
RPC ServerRPC ServerСтарая архитектура
3 . 9
Старая WEB-версия
3 . 10
Не было режима автораНе было режима автора
Старая WEB-версия
3 . 10
Не было режима автораНе было режима автора
Зависило от интернетЗависило от интернет
соединениясоединения
Старая WEB-версия...
Не было режима автораНе было режима автора
Зависило от интернетЗависило от интернет
соединениясоединения
Был PHPБыл PHP
Ст...
3 . 11
Полноценное web-приложениеПолноценное web-приложение
Задача:Задача:
3 . 12
Полноценное web-приложениеПолноценное web-приложение
Задача:Задача:
Не уступающее нативным клиентам
3 . 12
3 . 13
Увидел PHP
3 . 14
Пришлось переписать
веб приложение
3 . 15
Новая WEB-версия
3 . 16
Есть режим автораЕсть режим автора
Новая WEB-версия
3 . 16
Есть режим автораЕсть режим автора
Работает в OfflineРаботает в Offline
Новая WEB-версия
3 . 16
Есть режим автораЕсть режим автора
Работает в OfflineРаботает в Offline
Не уступает нативнымНе уступает нативным
клиентамклиен...
Криптография
Offline + Web Worker Работа с PDFОбщение с RPC-сервером
Framework Progressive Web App
4
КриптографияКриптография
5 . 1
Основная проблемаОсновная проблема
5 . 2
Основная проблемаОсновная проблема
Никто не знает как работает шифрование в проекте
5 . 2
5 . 3
5 . 4
5 . 5
RSARSA
AESAES
Основные алгоритмы
5 . 6
RSARSA
5 . 7
AESAES
cipher = encrypt(block, key) // шифруем block с помощью key
block = decrypt(cipher, key) // расшифровываем cipher с...
Как реализовать алгоритмыКак реализовать алгоритмы
шифрования?шифрования?
5 . 9
5 . 10
JS библиотека
5 . 10
JS библиотека
Web Crypto API
5 . 10
JS библиотека
Web Crypto API
OpenSSL -> WebAssembly
5 . 10
JS библиотекаJS библиотека
5 . 11
JS библиотекаJS библиотека
+ Изоморфный код
5 . 11
JS библиотекаJS библиотека
+ Поддержка RSA
+ Изоморфный код
5 . 11
JS библиотекаJS библиотека
+ Поддержка RSA
+ Изоморфный код
- Производительность
5 . 11
JS библиотекаJS библиотека
+ Поддержка RSA
+ Изоморфный код
- Производительность
- Размер
5 . 11
Web Crypto APIWeb Crypto API
5 . 12
Web Crypto APIWeb Crypto API
+ Размер
5 . 12
Web Crypto APIWeb Crypto API
+ Размер
+ Производительность
5 . 12
Web Crypto APIWeb Crypto API
- Неполная поддержка RSA
+ Размер
+ Производительность
5 . 12
Web Crypto APIWeb Crypto API
- Неполная поддержка RSA
- Изоморфный код
+ Размер
+ Производительность
5 . 12
Алгоритм PBKDF2Алгоритм PBKDF2
5 . 13
OpenSSL -> WebAssemblyOpenSSL -> WebAssembly
5 . 14
OpenSSL -> WebAssemblyOpenSSL -> WebAssembly
+ Изоморфный код
5 . 14
OpenSSL -> WebAssemblyOpenSSL -> WebAssembly
+ Изоморфный код
+ Производительность
5 . 14
OpenSSL -> WebAssemblyOpenSSL -> WebAssembly
+ Изоморфный код
+ Производительность
- Размер
5 . 14
JS библиотекаJS библиотека
5 . 15
Node-ForgeNode-Forge
JS библиотекаJS библиотека
5 . 15
Node-ForgeNode-Forge
JS библиотекаJS библиотека
Про улучшение оптимизации - в следующих слайдах...
5 . 15
NodeForge FailsNodeForge Fails
5 . 16
Offline + Web WorkerOffline + Web Worker
6 . 1
Что хранить в Offline?Что хранить в Offline?
6 . 2
СтатикаСтатика
Что хранить в Offline?Что хранить в Offline?
6 . 2
СтатикаСтатика
Что хранить в Offline?Что хранить в Offline?
6 . 2
СтатикаСтатика
Данные пользователяДанные пользователя
Что хранить в Offline?Что хранить в Offline?
6 . 2
СтатикаСтатика
Данные пользователяДанные пользователя
Что хранить в Offline?Что хранить в Offline?
6 . 2
СтатикаСтатика
Данные пользователяДанные пользователя
ФайлыФайлы
Что хранить в Offline?Что хранить в Offline?
6 . 2
СтатикаСтатика
Данные пользователяДанные пользователя
ФайлыФайлы
Что хранить в Offline?Что хранить в Offline?
6 . 2
СтатикаСтатика
7 . 1
Service WorkerService Worker
7 . 2
7 . 3
7 . 3
{
"globDirectory": "dist",
"globPatterns": [
"index.html",
"*.js",
"assets/**/*.{png,svg}",
"assets/*.{png,svg}"
],
"swSrc...
{
"assets": [
"src/assets",
"src/service-workers",
"src/manifest.json",
{
"glob": "workbox-sw.js",
"input": "node_modules/...
importScripts('workbox-3.5.0/workbox-sw.js');
workbox.setConfig({
debug: true,
modulePathPrefix: 'workbox-3.5.0/'
});
work...
workbox injectManifest
7 . 7
workbox injectManifest
workbox.precaching.precacheAndRoute([
{
"url": "index.html",
"revision": "4f8109353581284e76b88e568...
СтатикаСтатика
7 . 8
Данные пользователяДанные пользователя
8 . 1
Service WorkerService Worker
8 . 2
Persistent StoragePersistent Storage
8 . 3
8 . 4
LocalStorageLocalStorage
8 . 4
LocalStorageLocalStorage Небольшой лимит
8 . 4
File APIFile API
LocalStorageLocalStorage Небольшой лимит
8 . 4
File APIFile API FileWriter deprecated
LocalStorageLocalStorage Небольшой лимит
8 . 4
File APIFile API FileWriter deprecated
CookiesCookies
LocalStorageLocalStorage Небольшой лимит
8 . 4
File APIFile API FileWriter deprecated
CookiesCookies WATTT??
LocalStorageLocalStorage Небольшой лимит
8 . 4
 CacheStorage CacheStorage
File APIFile API FileWriter deprecated
CookiesCookies WATTT??
LocalStorageLocalStorage Небольшо...
 CacheStorage CacheStorage Нет гибкости
File APIFile API FileWriter deprecated
CookiesCookies WATTT??
LocalStorageLocalSto...
 CacheStorage CacheStorage Нет гибкости
File APIFile API FileWriter deprecated
CookiesCookies WATTT??
WebSQLWebSQL
LocalSt...
 CacheStorage CacheStorage Нет гибкости
File APIFile API FileWriter deprecated
CookiesCookies WATTT??
WebSQLWebSQL Depreca...
 CacheStorage CacheStorage Нет гибкости
File APIFile API FileWriter deprecated
CookiesCookies WATTT??
WebSQLWebSQL Depreca...
 CacheStorage CacheStorage Нет гибкости
File APIFile API FileWriter deprecated
CookiesCookies WATTT??
WebSQLWebSQL Depreca...
IndexedDBIndexedDB
8 . 5
IndexedDBIndexedDB
объектно-ориентированная база данныхобъектно-ориентированная база данных
8 . 5
IndexedDBIndexedDB
объектно-ориентированная база данныхобъектно-ориентированная база данных
хранит обьекты, проиндексирова...
IndexedDBIndexedDB
объектно-ориентированная база данныхобъектно-ориентированная база данных
хранит обьекты, проиндексирова...
IndexedDBIndexedDB
объектно-ориентированная база данныхобъектно-ориентированная база данных
хранит обьекты, проиндексирова...
IndexedDBIndexedDB
объектно-ориентированная база данныхобъектно-ориентированная база данных
хранит обьекты, проиндексирова...
https://caniuse.bitsofco.de/embed/index.html?
feat=indexeddb&periods=future_2,future_1,current&accessible-
colours=false
П...
DebugDebug 8 . 7
IndexedDB в боюIndexedDB в бою
9 . 1
9 . 2
1. Открыть или создать базу
9 . 2
1. Открыть или создать базу
var open = indexedDB.open("MyDatabase", 1);
9 . 2
1. Открыть или создать базу
var open = indexedDB.open("MyDatabase", 1);
2. Создать Schema
9 . 2
1. Открыть или создать базу
var open = indexedDB.open("MyDatabase", 1);
2. Создать Schema
open.onupgradeneeded = () => {
c...
9 . 3
3. Создать транзакцию
9 . 3
3. Создать транзакцию
open.onsuccess = () => {
const db = open.result
const tx = db.transaction("MyObjectStore", "readwrit...
4. Запросить данные
3. Создать транзакцию
open.onsuccess = () => {
const db = open.result
const tx = db.transaction("MyObj...
4. Запросить данные
const getJohn = store.get(12345); // по id
const getBob = index.get(["Smith", "Bob"]); // через index
...
9 . 4
5. Получить данные
9 . 4
5. Получить данные
getJohn.onsuccess = () => {
console.log(getJohn.result.name.first); // => "John"
};
getBob.onsuccess = ...
5. Получить данные
getJohn.onsuccess = () => {
console.log(getJohn.result.name.first); // => "John"
};
getBob.onsuccess = ...
5. Получить данные
getJohn.onsuccess = () => {
console.log(getJohn.result.name.first); // => "John"
};
getBob.onsuccess = ...
Boilerblate!!!1Boilerblate!!!1
9 . 5
Недостатки чистого IndexedDBНедостатки чистого IndexedDB
9 . 6
Недостатки чистого IndexedDBНедостатки чистого IndexedDB
Тяжело поддерживать
9 . 6
Недостатки чистого IndexedDBНедостатки чистого IndexedDB
Тяжело поддерживать
Не поддержки JOIN'ов
9 . 6
Недостатки чистого IndexedDBНедостатки чистого IndexedDB
Тяжело поддерживать
Не поддержки JOIN'ов
Нельзя частично обновить...
Недостатки чистого IndexedDBНедостатки чистого IndexedDB
Тяжело поддерживать
Не поддержки JOIN'ов
Нельзя частично обновить...
Недостатки чистого IndexedDBНедостатки чистого IndexedDB
Тяжело поддерживать
Не поддержки JOIN'ов
Нельзя частично обновить...
"Никто не использует IndexedDB в чистом виде"
- Все JS-разработчики
9 . 7
"Никто не использует IndexedDB в чистом виде"
- Все JS-разработчики
https://developer.mozilla.org/en-US/docs/Web/API/Index...
9 . 8
БиблиотекиБиблиотеки
ОберткиОбертки
9 . 8
БиблиотекиБиблиотеки
ОберткиОбертки
DB EngineDB Engine
9 . 8
БиблиотекиБиблиотеки
ОберткиОбертки
Idb
ZangoDb
MiniMongo
jsStore
PouchDB
Dexie
LocalForage
DB EngineDB Engine
9 . 8
БиблиотекиБиблиотеки
ОберткиОбертки
Idb
ZangoDb
MiniMongo
jsStore
PouchDB
Dexie
LocalForage
DB EngineDB Engine
YDN-DB
AlaS...
9 . 9
9 . 10
КроссбраузерностьКроссбраузерность
9 . 10
КроссбраузерностьКроссбраузерность
Chrome
Firefox
IE 11+, Edge
Safari 10+
9 . 10
SQL-подобный APISQL-подобный APIКроссбраузерностьКроссбраузерность
Chrome
Firefox
IE 11+, Edge
Safari 10+
9 . 10
SQL-подобный APISQL-подобный API
select, insert, update, delete
group by, order by, limit, skip
join
КроссбраузерностьКрос...
SQL-подобный APISQL-подобный API
select, insert, update, delete
group by, order by, limit, skip
join
КроссбраузерностьКрос...
Создание SchemaСоздание Schema
9 . 11
Создание SchemaСоздание Schema
const schemaBuilder = lf.schema.create('KEEPSOLID_SIGN_DB', 1.0);
schemaBuilder.createTable...
Создание SchemaСоздание Schema
const schemaBuilder = lf.schema.create('KEEPSOLID_SIGN_DB', 1.0);
schemaBuilder.createTable...
Типы полейТипы полей
9 . 12
Типы полейТипы полей
String
9 . 12
Типы полейТипы полей
String
Number
9 . 12
Типы полейТипы полей
String
Number
Integer (32bit)
9 . 12
Типы полейТипы полей
String
Number
Integer (32bit)
Boolean
9 . 12
Типы полейТипы полей
String
Number
Integer (32bit)
Boolean
Object
9 . 12
Типы полейТипы полей
String
Number
Integer (32bit)
Boolean
Object
Date
9 . 12
Типы полейТипы полей
String
Number
Integer (32bit)
Boolean
Object
Date
Array Buffer
9 . 12
SQL-подобный APISQL-подобный API
SQL Lovefield
9 . 13
SQL-подобный APISQL-подобный API
SQL Lovefield
SELECT *
FROM Documents
WHERE type = "TEMPLATE"
9 . 13
SQL-подобный APISQL-подобный API
SQL Lovefield
SELECT *
FROM Documents
WHERE type = "TEMPLATE"
Database
.select()
.from(do...
SQL-подобный APISQL-подобный API
SQL Lovefield
SELECT *
FROM Documents
WHERE type = "TEMPLATE"
Database
.select()
.from(do...
SQL-подобный APISQL-подобный API
SQL Lovefield
SELECT *
FROM Documents
WHERE type = "TEMPLATE"
Database
.select()
.from(do...
SQL Lovefield
9 . 14
SQL Lovefield
SELECT *
FROM Documents d, Files f
WHERE d.fileId = f.id
AND d.id = '123'
9 . 14
SQL Lovefield
SELECT *
FROM Documents d, Files f
WHERE d.fileId = f.id
AND d.id = '123'
Database
.select()
.from(document,...
SQL Lovefield
SELECT *
FROM Documents d, Files f
WHERE d.fileId = f.id
AND d.id = '123'
Database
.select()
.from(document,...
SQL Lovefield
SELECT *
FROM Documents d, Files f
WHERE d.fileId = f.id
AND d.id = '123'
Database
.select()
.from(document,...
function idb_and (index1, keyRange1, index2, keyRange2, onfound, onfinish) {
var openCursorRequest1 = index1.openCursor(ke...
function idb_and (index1, keyRange1, index2, keyRange2, onfound, onfinish) {
var openCursorRequest1 = index1.openCursor(ke...
Интересные особенностиИнтересные особенности
9 . 16
Интересные особенностиИнтересные особенности
Позволяет задавать Schema в YAML (SPAC)
9 . 16
Интересные особенностиИнтересные особенности
Позволяет задавать Schema в YAML (SPAC)
Типы Storage: IndexedDB, Memory, Fire...
Интересные особенностиИнтересные особенности
Позволяет задавать Schema в YAML (SPAC)
Типы Storage: IndexedDB, Memory, Fire...
Интересные особенностиИнтересные особенности
Позволяет задавать Schema в YAML (SPAC)
Типы Storage: IndexedDB, Memory, Fire...
Data ObservationData Observation
9 . 17
Data ObservationData Observation
var query = db.select()
.from(documents)
.where(documents.id.eq('1'));
var handler = func...
НюансыНюансы
9 . 18
Изменение SchemaИзменение Schema
9 . 19
Вывод ошибокВывод ошибок
9 . 20
Вывод ошибокВывод ошибок
Constraint error: (202) Attempted to insert NULL value to non-nullable field
Documents.signOrder.
...
Хранение файловХранение файлов
10 . 1
10 . 2
ArrayBuffer
10 . 2
Offline + Web WorkerOffline + Web Worker
11 . 1
11 . 2
FrameworkFramework
12 . 1
АрхитектураАрхитектура
12 . 2
Redux + Side Effects + Entities + Selectors
12 . 3
Side EffectsSide Effects
@Effect()
documentCreate$ = this.actions$.pipe(
ofType(DocumentActions.Types.DocumentCreate),
map(a...
EntitiesEntities
export interface Document {
id: string;
name: string;
}
export interface State extends EntityState<Docume...
EntitiesEntities
export interface Document {
id: string;
name: string;
}
export interface State extends EntityState<Docume...
EntitiesEntities
export interface Document {
id: string;
name: string;
}
export interface State extends EntityState<Docume...
Entity adapterEntity adapter
addOne
addMany
addAll
removeOne
removeMany
removeAll
updateOne
updateMany
upsertOne
upsertMan...
SelectorsSelectors
export const getDocumentState = (state: AppState) => state.documents;
export const getDocumentEntities ...
BoilerplateBoilerplate
12 . 8
SchematicsSchematics
ng generate action
store
entity
reducer
effect
container
feature
12 . 9
class GetDocumentPreviewAction implements IDocumentAction {
readonly type = DocumentActionTypes.GetDocumentPreview;
docume...
https://github.com/pelotom/unionize
import { unionize, ofType, UnionOf } from 'unionize';
const DocumentActions = unionize...
Коммуникация с Web WorkerКоммуникация с Web Worker
12 . 12
12 . 13
Effect
12 . 13
Effect
Action
12 . 13
Effect
Action
ReduxRedux
12 . 13
Effect
Action
ReduxRedux
12 . 13
Effect
Action
ReduxRedux ComutterComutter
12 . 13
Effect
Action
ReduxRedux ComutterComutter
12 . 13
Effect
Action
ReduxRedux Web WorkerComutterComutter
12 . 13
Effect
Action
ReduxRedux Web WorkerComutterComutter
12 . 13
Effect
Action
ReduxRedux Web Worker
Success/Failure
Action
ComutterComutter
12 . 13
Effect
Action
ReduxRedux Web Worker
Success/Failure
Action
ComutterComutter
12 . 13
Redux Devtools
12 . 14
ВыводыВыводы
13 . 1
13 . 2
Спасибо за вниманиеСпасибо за внимание
Тимофей Лавренюк
@geek_timofey
14
Nächste SlideShare
Wird geladen in …5
×

Перепись приложения. Нативного. На JS. Done. | OdessaFrontend Meetup #10

29 Aufrufe

Veröffentlicht am

KeepSolid Sign — приложение для подписи документов под все популярные платформы. Его ядро написано на C++ и однажды поступила задача сделать веб-версию. Тимофей Лавренюк рассказывает о том, через что получилось пройти, чтобы в итоге сделать веб-приложение не хуже нативного.

Veröffentlicht in: Technologie
  • Als Erste(r) kommentieren

  • Gehören Sie zu den Ersten, denen das gefällt!

Перепись приложения. Нативного. На JS. Done. | OdessaFrontend Meetup #10

  1. 1. Перепись приложения. Нативного.Перепись приложения. Нативного. На JS. DoneНа JS. Done Тимофей Лавренюк @geek_timofey 1
  2. 2. О себеО себе 2
  3. 3. О проектеО проекте 3 . 1
  4. 4. 3 . 2
  5. 5. Архитектура 3 . 3
  6. 6. Архитектура 3 . 3
  7. 7. ЯдроЯдро Архитектура 3 . 3
  8. 8. ЯдроЯдро RPC ServerRPC ServerАрхитектура 3 . 3
  9. 9. ЯдроЯдро А что умеет?А что умеет? 3 . 4
  10. 10. ЯдроЯдро Работа с локальной БДА что умеет?А что умеет? 3 . 4
  11. 11. ЯдроЯдро ШифрованиеРабота с локальной БДА что умеет?А что умеет? 3 . 4
  12. 12. ЯдроЯдро ШифрованиеРабота с локальной БД Общение с RPC-сервером А что умеет?А что умеет? 3 . 4
  13. 13. ЯдроЯдро ШифрованиеРабота с локальной БД Работа с PDFОбщение с RPC-сервером А что умеет?А что умеет? 3 . 4
  14. 14. Настал 2017 годНастал 2017 год 3 . 5
  15. 15. Настал 2017 годНастал 2017 год Нужна web-версия... 3 . 5
  16. 16. СРОЧНО НУЖНАСРОЧНО НУЖНА WEB-ВЕРСИЯ !!!11WEB-ВЕРСИЯ !!!11 3 . 6
  17. 17. 3 . 7
  18. 18. WEB-ВерсияWEB-Версия 1.01.0 3 . 8
  19. 19. Старая архитектура 3 . 9
  20. 20. SPASPA Старая архитектура 3 . 9
  21. 21. SPASPA REST APIREST API ЯдроЯдро Старая архитектура 3 . 9
  22. 22. SPASPA REST APIREST API ЯдроЯдро RPC ServerRPC ServerСтарая архитектура 3 . 9
  23. 23. Старая WEB-версия 3 . 10
  24. 24. Не было режима автораНе было режима автора Старая WEB-версия 3 . 10
  25. 25. Не было режима автораНе было режима автора Зависило от интернетЗависило от интернет соединениясоединения Старая WEB-версия 3 . 10
  26. 26. Не было режима автораНе было режима автора Зависило от интернетЗависило от интернет соединениясоединения Был PHPБыл PHP Старая WEB-версия 3 . 10
  27. 27. 3 . 11
  28. 28. Полноценное web-приложениеПолноценное web-приложение Задача:Задача: 3 . 12
  29. 29. Полноценное web-приложениеПолноценное web-приложение Задача:Задача: Не уступающее нативным клиентам 3 . 12
  30. 30. 3 . 13
  31. 31. Увидел PHP 3 . 14
  32. 32. Пришлось переписать веб приложение 3 . 15
  33. 33. Новая WEB-версия 3 . 16
  34. 34. Есть режим автораЕсть режим автора Новая WEB-версия 3 . 16
  35. 35. Есть режим автораЕсть режим автора Работает в OfflineРаботает в Offline Новая WEB-версия 3 . 16
  36. 36. Есть режим автораЕсть режим автора Работает в OfflineРаботает в Offline Не уступает нативнымНе уступает нативным клиентамклиентам Новая WEB-версия 3 . 16
  37. 37. Криптография Offline + Web Worker Работа с PDFОбщение с RPC-сервером Framework Progressive Web App 4
  38. 38. КриптографияКриптография 5 . 1
  39. 39. Основная проблемаОсновная проблема 5 . 2
  40. 40. Основная проблемаОсновная проблема Никто не знает как работает шифрование в проекте 5 . 2
  41. 41. 5 . 3
  42. 42. 5 . 4
  43. 43. 5 . 5
  44. 44. RSARSA AESAES Основные алгоритмы 5 . 6
  45. 45. RSARSA 5 . 7
  46. 46. AESAES cipher = encrypt(block, key) // шифруем block с помощью key block = decrypt(cipher, key) // расшифровываем cipher с помощью key 5 . 8
  47. 47. Как реализовать алгоритмыКак реализовать алгоритмы шифрования?шифрования? 5 . 9
  48. 48. 5 . 10
  49. 49. JS библиотека 5 . 10
  50. 50. JS библиотека Web Crypto API 5 . 10
  51. 51. JS библиотека Web Crypto API OpenSSL -> WebAssembly 5 . 10
  52. 52. JS библиотекаJS библиотека 5 . 11
  53. 53. JS библиотекаJS библиотека + Изоморфный код 5 . 11
  54. 54. JS библиотекаJS библиотека + Поддержка RSA + Изоморфный код 5 . 11
  55. 55. JS библиотекаJS библиотека + Поддержка RSA + Изоморфный код - Производительность 5 . 11
  56. 56. JS библиотекаJS библиотека + Поддержка RSA + Изоморфный код - Производительность - Размер 5 . 11
  57. 57. Web Crypto APIWeb Crypto API 5 . 12
  58. 58. Web Crypto APIWeb Crypto API + Размер 5 . 12
  59. 59. Web Crypto APIWeb Crypto API + Размер + Производительность 5 . 12
  60. 60. Web Crypto APIWeb Crypto API - Неполная поддержка RSA + Размер + Производительность 5 . 12
  61. 61. Web Crypto APIWeb Crypto API - Неполная поддержка RSA - Изоморфный код + Размер + Производительность 5 . 12
  62. 62. Алгоритм PBKDF2Алгоритм PBKDF2 5 . 13
  63. 63. OpenSSL -> WebAssemblyOpenSSL -> WebAssembly 5 . 14
  64. 64. OpenSSL -> WebAssemblyOpenSSL -> WebAssembly + Изоморфный код 5 . 14
  65. 65. OpenSSL -> WebAssemblyOpenSSL -> WebAssembly + Изоморфный код + Производительность 5 . 14
  66. 66. OpenSSL -> WebAssemblyOpenSSL -> WebAssembly + Изоморфный код + Производительность - Размер 5 . 14
  67. 67. JS библиотекаJS библиотека 5 . 15
  68. 68. Node-ForgeNode-Forge JS библиотекаJS библиотека 5 . 15
  69. 69. Node-ForgeNode-Forge JS библиотекаJS библиотека Про улучшение оптимизации - в следующих слайдах... 5 . 15
  70. 70. NodeForge FailsNodeForge Fails 5 . 16
  71. 71. Offline + Web WorkerOffline + Web Worker 6 . 1
  72. 72. Что хранить в Offline?Что хранить в Offline? 6 . 2
  73. 73. СтатикаСтатика Что хранить в Offline?Что хранить в Offline? 6 . 2
  74. 74. СтатикаСтатика Что хранить в Offline?Что хранить в Offline? 6 . 2
  75. 75. СтатикаСтатика Данные пользователяДанные пользователя Что хранить в Offline?Что хранить в Offline? 6 . 2
  76. 76. СтатикаСтатика Данные пользователяДанные пользователя Что хранить в Offline?Что хранить в Offline? 6 . 2
  77. 77. СтатикаСтатика Данные пользователяДанные пользователя ФайлыФайлы Что хранить в Offline?Что хранить в Offline? 6 . 2
  78. 78. СтатикаСтатика Данные пользователяДанные пользователя ФайлыФайлы Что хранить в Offline?Что хранить в Offline? 6 . 2
  79. 79. СтатикаСтатика 7 . 1
  80. 80. Service WorkerService Worker 7 . 2
  81. 81. 7 . 3
  82. 82. 7 . 3
  83. 83. { "globDirectory": "dist", "globPatterns": [ "index.html", "*.js", "assets/**/*.{png,svg}", "assets/*.{png,svg}" ], "swSrc": "src/service-workers/main.worker.js", "swDest": "dist/service-worker.js" } workbox.config.js 7 . 4
  84. 84. { "assets": [ "src/assets", "src/service-workers", "src/manifest.json", { "glob": "workbox-sw.js", "input": "node_modules/workbox-sw/build", "output": "./workbox-3.5.0" }, { "glob": "workbox-core.dev.js", "input": "node_modules/workbox-core/build/", "output": "./workbox-3.5.0" }, { "glob": "workbox-precaching.dev.js", "input": "node_modules/workbox-precaching/build/", "output": "./workbox-3.5.0" } ], } angular.json 7 . 5
  85. 85. importScripts('workbox-3.5.0/workbox-sw.js'); workbox.setConfig({ debug: true, modulePathPrefix: 'workbox-3.5.0/' }); workbox.skipWaiting(); workbox.clientsClaim(); workbox.precaching.precacheAndRoute([]); main.worker.js 7 . 6
  86. 86. workbox injectManifest 7 . 7
  87. 87. workbox injectManifest workbox.precaching.precacheAndRoute([ { "url": "index.html", "revision": "4f8109353581284e76b88e568d642376" }, { "url": "0.js", "revision": "2af14762103b4c1f18e620bd30a53d2e" }, { "url": "main.js", "revision": "ef2fb7f5913e6614c9ee3621ad299d1b" } ]) service-worker.js 7 . 7
  88. 88. СтатикаСтатика 7 . 8
  89. 89. Данные пользователяДанные пользователя 8 . 1
  90. 90. Service WorkerService Worker 8 . 2
  91. 91. Persistent StoragePersistent Storage 8 . 3
  92. 92. 8 . 4
  93. 93. LocalStorageLocalStorage 8 . 4
  94. 94. LocalStorageLocalStorage Небольшой лимит 8 . 4
  95. 95. File APIFile API LocalStorageLocalStorage Небольшой лимит 8 . 4
  96. 96. File APIFile API FileWriter deprecated LocalStorageLocalStorage Небольшой лимит 8 . 4
  97. 97. File APIFile API FileWriter deprecated CookiesCookies LocalStorageLocalStorage Небольшой лимит 8 . 4
  98. 98. File APIFile API FileWriter deprecated CookiesCookies WATTT?? LocalStorageLocalStorage Небольшой лимит 8 . 4
  99. 99.  CacheStorage CacheStorage File APIFile API FileWriter deprecated CookiesCookies WATTT?? LocalStorageLocalStorage Небольшой лимит 8 . 4
  100. 100.  CacheStorage CacheStorage Нет гибкости File APIFile API FileWriter deprecated CookiesCookies WATTT?? LocalStorageLocalStorage Небольшой лимит 8 . 4
  101. 101.  CacheStorage CacheStorage Нет гибкости File APIFile API FileWriter deprecated CookiesCookies WATTT?? WebSQLWebSQL LocalStorageLocalStorage Небольшой лимит 8 . 4
  102. 102.  CacheStorage CacheStorage Нет гибкости File APIFile API FileWriter deprecated CookiesCookies WATTT?? WebSQLWebSQL Deprecated LocalStorageLocalStorage Небольшой лимит 8 . 4
  103. 103.  CacheStorage CacheStorage Нет гибкости File APIFile API FileWriter deprecated CookiesCookies WATTT?? WebSQLWebSQL Deprecated IndexedDBIndexedDB LocalStorageLocalStorage Небольшой лимит 8 . 4
  104. 104.  CacheStorage CacheStorage Нет гибкости File APIFile API FileWriter deprecated CookiesCookies WATTT?? WebSQLWebSQL Deprecated IndexedDBIndexedDB Большой лимит Хорошая поддержка Не Deprecated LocalStorageLocalStorage Небольшой лимит 8 . 4
  105. 105. IndexedDBIndexedDB 8 . 5
  106. 106. IndexedDBIndexedDB объектно-ориентированная база данныхобъектно-ориентированная база данных 8 . 5
  107. 107. IndexedDBIndexedDB объектно-ориентированная база данныхобъектно-ориентированная база данных хранит обьекты, проиндексированные с ключомхранит обьекты, проиндексированные с ключом 8 . 5
  108. 108. IndexedDBIndexedDB объектно-ориентированная база данныхобъектно-ориентированная база данных хранит обьекты, проиндексированные с ключомхранит обьекты, проиндексированные с ключом выполнение операций происходит асинхронновыполнение операций происходит асинхронно 8 . 5
  109. 109. IndexedDBIndexedDB объектно-ориентированная база данныхобъектно-ориентированная база данных хранит обьекты, проиндексированные с ключомхранит обьекты, проиндексированные с ключом выполнение операций происходит асинхронновыполнение операций происходит асинхронно умеет хранить JS-объекты и блобыумеет хранить JS-объекты и блобы 8 . 5
  110. 110. IndexedDBIndexedDB объектно-ориентированная база данныхобъектно-ориентированная база данных хранит обьекты, проиндексированные с ключомхранит обьекты, проиндексированные с ключом выполнение операций происходит асинхронновыполнение операций происходит асинхронно имеет жутко неудобное низкоуровневое APIимеет жутко неудобное низкоуровневое API умеет хранить JS-объекты и блобыумеет хранить JS-объекты и блобы 8 . 5
  111. 111. https://caniuse.bitsofco.de/embed/index.html? feat=indexeddb&periods=future_2,future_1,current&accessible- colours=false ПоддержкаПоддержка 8 . 6
  112. 112. DebugDebug 8 . 7
  113. 113. IndexedDB в боюIndexedDB в бою 9 . 1
  114. 114. 9 . 2
  115. 115. 1. Открыть или создать базу 9 . 2
  116. 116. 1. Открыть или создать базу var open = indexedDB.open("MyDatabase", 1); 9 . 2
  117. 117. 1. Открыть или создать базу var open = indexedDB.open("MyDatabase", 1); 2. Создать Schema 9 . 2
  118. 118. 1. Открыть или создать базу var open = indexedDB.open("MyDatabase", 1); 2. Создать Schema open.onupgradeneeded = () => { const db = open.result; const store = db.createObjectStore("MyObjectStore", {keyPath: "id"}); const index = store.createIndex("NameIndex", ["name.last", "name.first"]); }; 9 . 2
  119. 119. 9 . 3
  120. 120. 3. Создать транзакцию 9 . 3
  121. 121. 3. Создать транзакцию open.onsuccess = () => { const db = open.result const tx = db.transaction("MyObjectStore", "readwrite") const store = tx.objectStore("MyObjectStore"); const index = store.index("NameIndex"); } 9 . 3
  122. 122. 4. Запросить данные 3. Создать транзакцию open.onsuccess = () => { const db = open.result const tx = db.transaction("MyObjectStore", "readwrite") const store = tx.objectStore("MyObjectStore"); const index = store.index("NameIndex"); } 9 . 3
  123. 123. 4. Запросить данные const getJohn = store.get(12345); // по id const getBob = index.get(["Smith", "Bob"]); // через index 3. Создать транзакцию open.onsuccess = () => { const db = open.result const tx = db.transaction("MyObjectStore", "readwrite") const store = tx.objectStore("MyObjectStore"); const index = store.index("NameIndex"); } 9 . 3
  124. 124. 9 . 4
  125. 125. 5. Получить данные 9 . 4
  126. 126. 5. Получить данные getJohn.onsuccess = () => { console.log(getJohn.result.name.first); // => "John" }; getBob.onsuccess = () => { console.log(getBob.result.name.first); // => "Bob" }; 9 . 4
  127. 127. 5. Получить данные getJohn.onsuccess = () => { console.log(getJohn.result.name.first); // => "John" }; getBob.onsuccess = () => { console.log(getBob.result.name.first); // => "Bob" }; 5. Закрыть транзакцию 9 . 4
  128. 128. 5. Получить данные getJohn.onsuccess = () => { console.log(getJohn.result.name.first); // => "John" }; getBob.onsuccess = () => { console.log(getBob.result.name.first); // => "Bob" }; 5. Закрыть транзакцию tx.oncomplete = function() { db.close(); }; 9 . 4
  129. 129. Boilerblate!!!1Boilerblate!!!1 9 . 5
  130. 130. Недостатки чистого IndexedDBНедостатки чистого IndexedDB 9 . 6
  131. 131. Недостатки чистого IndexedDBНедостатки чистого IndexedDB Тяжело поддерживать 9 . 6
  132. 132. Недостатки чистого IndexedDBНедостатки чистого IndexedDB Тяжело поддерживать Не поддержки JOIN'ов 9 . 6
  133. 133. Недостатки чистого IndexedDBНедостатки чистого IndexedDB Тяжело поддерживать Не поддержки JOIN'ов Нельзя частично обновить документ 9 . 6
  134. 134. Недостатки чистого IndexedDBНедостатки чистого IndexedDB Тяжело поддерживать Не поддержки JOIN'ов Нельзя частично обновить документ Скудная сортировка 9 . 6
  135. 135. Недостатки чистого IndexedDBНедостатки чистого IndexedDB Тяжело поддерживать Не поддержки JOIN'ов Нельзя частично обновить документ Скудная сортировка Auto-commit транзакций 9 . 6
  136. 136. "Никто не использует IndexedDB в чистом виде" - Все JS-разработчики 9 . 7
  137. 137. "Никто не использует IndexedDB в чистом виде" - Все JS-разработчики https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API 9 . 7
  138. 138. 9 . 8
  139. 139. БиблиотекиБиблиотеки ОберткиОбертки 9 . 8
  140. 140. БиблиотекиБиблиотеки ОберткиОбертки DB EngineDB Engine 9 . 8
  141. 141. БиблиотекиБиблиотеки ОберткиОбертки Idb ZangoDb MiniMongo jsStore PouchDB Dexie LocalForage DB EngineDB Engine 9 . 8
  142. 142. БиблиотекиБиблиотеки ОберткиОбертки Idb ZangoDb MiniMongo jsStore PouchDB Dexie LocalForage DB EngineDB Engine YDN-DB AlaSQL Lovefield 9 . 8
  143. 143. 9 . 9
  144. 144. 9 . 10
  145. 145. КроссбраузерностьКроссбраузерность 9 . 10
  146. 146. КроссбраузерностьКроссбраузерность Chrome Firefox IE 11+, Edge Safari 10+ 9 . 10
  147. 147. SQL-подобный APISQL-подобный APIКроссбраузерностьКроссбраузерность Chrome Firefox IE 11+, Edge Safari 10+ 9 . 10
  148. 148. SQL-подобный APISQL-подобный API select, insert, update, delete group by, order by, limit, skip join КроссбраузерностьКроссбраузерность Chrome Firefox IE 11+, Edge Safari 10+ 9 . 10
  149. 149. SQL-подобный APISQL-подобный API select, insert, update, delete group by, order by, limit, skip join КроссбраузерностьКроссбраузерность Chrome Firefox IE 11+, Edge Safari 10+ Отличная производительностьОтличная производительность Оптимизация и анализ запросов 9 . 10
  150. 150. Создание SchemaСоздание Schema 9 . 11
  151. 151. Создание SchemaСоздание Schema const schemaBuilder = lf.schema.create('KEEPSOLID_SIGN_DB', 1.0); schemaBuilder.createTable('Documents') .addColumn('id', lf.Type.STRING) .addColumn('parentId', lf.Type.OBJECT) .addColumn('type', lf.Type.STRING) .addColumn('signOrder', lf.Type.NUMBER) .addColumn('encryptionKey', lf.Type.STRING) .addIndex('idxSignOrder', ['signOrder'], false, lf.Order.DESC); .addPrimaryKey(['id']); schemaBuilder.connect().then((db) => { // Можно работать с базой }); 9 . 11
  152. 152. Создание SchemaСоздание Schema const schemaBuilder = lf.schema.create('KEEPSOLID_SIGN_DB', 1.0); schemaBuilder.createTable('Documents') .addColumn('id', lf.Type.STRING) .addColumn('parentId', lf.Type.OBJECT) .addColumn('type', lf.Type.STRING) .addColumn('signOrder', lf.Type.NUMBER) .addColumn('encryptionKey', lf.Type.STRING) .addIndex('idxSignOrder', ['signOrder'], false, lf.Order.DESC); .addPrimaryKey(['id']); schemaBuilder.connect().then((db) => { // Можно работать с базой }); 9 . 11
  153. 153. Типы полейТипы полей 9 . 12
  154. 154. Типы полейТипы полей String 9 . 12
  155. 155. Типы полейТипы полей String Number 9 . 12
  156. 156. Типы полейТипы полей String Number Integer (32bit) 9 . 12
  157. 157. Типы полейТипы полей String Number Integer (32bit) Boolean 9 . 12
  158. 158. Типы полейТипы полей String Number Integer (32bit) Boolean Object 9 . 12
  159. 159. Типы полейТипы полей String Number Integer (32bit) Boolean Object Date 9 . 12
  160. 160. Типы полейТипы полей String Number Integer (32bit) Boolean Object Date Array Buffer 9 . 12
  161. 161. SQL-подобный APISQL-подобный API SQL Lovefield 9 . 13
  162. 162. SQL-подобный APISQL-подобный API SQL Lovefield SELECT * FROM Documents WHERE type = "TEMPLATE" 9 . 13
  163. 163. SQL-подобный APISQL-подобный API SQL Lovefield SELECT * FROM Documents WHERE type = "TEMPLATE" Database .select() .from(document) .where(document.type.eq('TEMPLATE')) .exec() 9 . 13
  164. 164. SQL-подобный APISQL-подобный API SQL Lovefield SELECT * FROM Documents WHERE type = "TEMPLATE" Database .select() .from(document) .where(document.type.eq('TEMPLATE')) .exec() SELECT encryptionKey FROM Documents WHERE signOrder >= 1 ORDER BY signOrder DESC LIMIT 10 9 . 13
  165. 165. SQL-подобный APISQL-подобный API SQL Lovefield SELECT * FROM Documents WHERE type = "TEMPLATE" Database .select() .from(document) .where(document.type.eq('TEMPLATE')) .exec() SELECT encryptionKey FROM Documents WHERE signOrder >= 1 ORDER BY signOrder DESC LIMIT 10 Database .select(document.encryptionKey) .from(document) .where(document.signOrder.gte(1)) .orderBy(document.signOrder, lf.Order.DESC) .limit(10) .exec() 9 . 13
  166. 166. SQL Lovefield 9 . 14
  167. 167. SQL Lovefield SELECT * FROM Documents d, Files f WHERE d.fileId = f.id AND d.id = '123' 9 . 14
  168. 168. SQL Lovefield SELECT * FROM Documents d, Files f WHERE d.fileId = f.id AND d.id = '123' Database .select() .from(document, file) .where(lf.op.and( document.fileId.eq(file.id), document.id.eq('123'), )) .exec() 9 . 14
  169. 169. SQL Lovefield SELECT * FROM Documents d, Files f WHERE d.fileId = f.id AND d.id = '123' Database .select() .from(document, file) .where(lf.op.and( document.fileId.eq(file.id), document.id.eq('123'), )) .exec() SELECT * FROM document INNER JOIN file ON document.fileId = file.id WHERE document.id = '123' 9 . 14
  170. 170. SQL Lovefield SELECT * FROM Documents d, Files f WHERE d.fileId = f.id AND d.id = '123' Database .select() .from(document, file) .where(lf.op.and( document.fileId.eq(file.id), document.id.eq('123'), )) .exec() SELECT * FROM document INNER JOIN file ON document.fileId = file.id WHERE document.id = '123' Database .select() .from(document) .innerJoin( file, document.fileId.eq(file.id) ) .where(document.id.eq('123')) .exec() 9 . 14
  171. 171. function idb_and (index1, keyRange1, index2, keyRange2, onfound, onfinish) { var openCursorRequest1 = index1.openCursor(keyRange1); var openCursorRequest2 = index2.openCursor(keyRange2); assert(index1.objectStore === index2.objectStore); var primKey = index1.objectStore.keyPath; var set = {}; var resolved = 0; function complete() { if (++resolved === 2) onfinish(); } function union(item) { var key = JSON.stringify(item[primKey]); if (!set.hasOwnProperty(key)) { set[key] = true; onfound(item); } } openCursorRequest1.onsuccess = function (event) { var cursor = event.target.result; if (cursor) { union(cursor.value); } else { complete(); } } openCursorRequest2.onsuccess = function (event) { var cursor = event.target.result; if (cursor) { union(cursor.value); } else { complete(); } } } Чистый API 9 . 15
  172. 172. function idb_and (index1, keyRange1, index2, keyRange2, onfound, onfinish) { var openCursorRequest1 = index1.openCursor(keyRange1); var openCursorRequest2 = index2.openCursor(keyRange2); assert(index1.objectStore === index2.objectStore); var primKey = index1.objectStore.keyPath; var set = {}; var resolved = 0; function complete() { if (++resolved === 2) onfinish(); } function union(item) { var key = JSON.stringify(item[primKey]); if (!set.hasOwnProperty(key)) { set[key] = true; onfound(item); } } openCursorRequest1.onsuccess = function (event) { var cursor = event.target.result; if (cursor) { union(cursor.value); } else { complete(); } } openCursorRequest2.onsuccess = function (event) { var cursor = event.target.result; if (cursor) { union(cursor.value); } else { complete(); } } } Чистый API 9 . 15
  173. 173. Интересные особенностиИнтересные особенности 9 . 16
  174. 174. Интересные особенностиИнтересные особенности Позволяет задавать Schema в YAML (SPAC) 9 . 16
  175. 175. Интересные особенностиИнтересные особенности Позволяет задавать Schema в YAML (SPAC) Типы Storage: IndexedDB, Memory, Firebase 9 . 16
  176. 176. Интересные особенностиИнтересные особенности Позволяет задавать Schema в YAML (SPAC) Типы Storage: IndexedDB, Memory, Firebase Поддерживает Import/Export в Javascript-объект 9 . 16
  177. 177. Интересные особенностиИнтересные особенности Позволяет задавать Schema в YAML (SPAC) Типы Storage: IndexedDB, Memory, Firebase Поддерживает Import/Export в Javascript-объект Поддерживает Data Observation 9 . 16
  178. 178. Data ObservationData Observation 9 . 17
  179. 179. Data ObservationData Observation var query = db.select() .from(documents) .where(documents.id.eq('1')); var handler = function(changes) { // Будет вызываться всегда, когда происходит изменение данных }; db.observe(query, handler); db.update(documents) .set(documents.title, 'New Title') .where(documents.id.eq('1')) .exec(); db.unobserve(query, handler); 9 . 17
  180. 180. НюансыНюансы 9 . 18
  181. 181. Изменение SchemaИзменение Schema 9 . 19
  182. 182. Вывод ошибокВывод ошибок 9 . 20
  183. 183. Вывод ошибокВывод ошибок Constraint error: (202) Attempted to insert NULL value to non-nullable field Documents.signOrder. 9 . 20
  184. 184. Хранение файловХранение файлов 10 . 1
  185. 185. 10 . 2
  186. 186. ArrayBuffer 10 . 2
  187. 187. Offline + Web WorkerOffline + Web Worker 11 . 1
  188. 188. 11 . 2
  189. 189. FrameworkFramework 12 . 1
  190. 190. АрхитектураАрхитектура 12 . 2
  191. 191. Redux + Side Effects + Entities + Selectors 12 . 3
  192. 192. Side EffectsSide Effects @Effect() documentCreate$ = this.actions$.pipe( ofType(DocumentActions.Types.DocumentCreate), map(action => action.payload.query), exhaustMap(query => this.dbService.createDocument(query) ), flatMap(() => [ new DocumentAction.DocumentCreateSuccess(); new DocumentAction.GenerateDocumentPreview(); ]) ); 12 . 4
  193. 193. EntitiesEntities export interface Document { id: string; name: string; } export interface State extends EntityState<Document> { activeDocumentId: number | null; } 12 . 5
  194. 194. EntitiesEntities export interface Document { id: string; name: string; } export interface State extends EntityState<Document> { activeDocumentId: number | null; } export const adapter: EntityAdapter<Document> = createEntityAdapter<Document>(); 12 . 5
  195. 195. EntitiesEntities export interface Document { id: string; name: string; } export interface State extends EntityState<Document> { activeDocumentId: number | null; } export const adapter: EntityAdapter<Document> = createEntityAdapter<Document>(); 12 . 5
  196. 196. Entity adapterEntity adapter addOne addMany addAll removeOne removeMany removeAll updateOne updateMany upsertOne upsertMany map 12 . 6
  197. 197. SelectorsSelectors export const getDocumentState = (state: AppState) => state.documents; export const getDocumentEntities = (state: DocumentState) => state.entities; export const getDocumentEntities = createSelector( getDocumentState, getDocumentEntities ); export const getAllDocuments = createSelector(getDocumentEntities, entities => Object.keys(entities).map(id => entities[id]) ); 12 . 7
  198. 198. BoilerplateBoilerplate 12 . 8
  199. 199. SchematicsSchematics ng generate action store entity reducer effect container feature 12 . 9
  200. 200. class GetDocumentPreviewAction implements IDocumentAction { readonly type = DocumentActionTypes.GetDocumentPreview; documentType: DocumentType; constructor(public payload: any) { } } class GetDocumentPreviewSuccessAction implements IDocumentAction { readonly type = DocumentActionTypes.GetDocumentPreviewSuccess; documentType: DocumentType; constructor(public payload: IFileWithData) { } } class GetDocumentPreviewFailureAction implements IDocumentAction { readonly type = DocumentActionTypes.GetDocumentPreviewFailure; documentType: DocumentType; } 12 . 10
  201. 201. https://github.com/pelotom/unionize import { unionize, ofType, UnionOf } from 'unionize'; const DocumentActions = unionize({ GET_DOCUMENT_PREVIEW: ofType<{ id: string }>(), GET_DOCUMENT_PREVIEW_SUCCESS: ofType<IFileWithData>() GET_DOCUMENT_PREVIEW_FAILURE: {} }); type DocumentAction = UnionOf<typeof DocumentActions>; 12 . 11
  202. 202. Коммуникация с Web WorkerКоммуникация с Web Worker 12 . 12
  203. 203. 12 . 13
  204. 204. Effect 12 . 13
  205. 205. Effect Action 12 . 13
  206. 206. Effect Action ReduxRedux 12 . 13
  207. 207. Effect Action ReduxRedux 12 . 13
  208. 208. Effect Action ReduxRedux ComutterComutter 12 . 13
  209. 209. Effect Action ReduxRedux ComutterComutter 12 . 13
  210. 210. Effect Action ReduxRedux Web WorkerComutterComutter 12 . 13
  211. 211. Effect Action ReduxRedux Web WorkerComutterComutter 12 . 13
  212. 212. Effect Action ReduxRedux Web Worker Success/Failure Action ComutterComutter 12 . 13
  213. 213. Effect Action ReduxRedux Web Worker Success/Failure Action ComutterComutter 12 . 13
  214. 214. Redux Devtools 12 . 14
  215. 215. ВыводыВыводы 13 . 1
  216. 216. 13 . 2
  217. 217. Спасибо за вниманиеСпасибо за внимание Тимофей Лавренюк @geek_timofey 14

×