Se ha denunciado esta presentación.
Utilizamos tu perfil de LinkedIn y tus datos de actividad para personalizar los anuncios y mostrarte publicidad más relevante. Puedes cambiar tus preferencias de publicidad en cualquier momento.

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

26 visualizaciones

Publicado el

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

Publicado en: Tecnología
  • Sé el primero en comentar

  • Sé el primero en recomendar esto

Перепись приложения. Нативного. На 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

×