
اسمي فيتالي ريزو ، أنا مطور أمامي كبير في Amplifer. سأشارك كيف نستخدم Logux في تطبيق ويب: ننظم تبادل البيانات في الوقت الفعلي وإشعارات الخطأ دون إعادة تحميل الصفحة والتواصل بين علامات تبويب المتصفح والتكامل مع Redux.
Amplifer هي خدمة للنشر على الشبكات الاجتماعية. كان من الضروري إخطار المستخدمين بسرعة وموثوقية بالأخطاء دون إعادة تحميل الصفحة: إذا لم يكن من الممكن فجأة معالجة الصورة ، سقطت VKontakte API ، أو قرر Facebook مرة أخرى عدم النشر. في المستقبل ، سأقول أننا خططنا أيضًا لاستخدام Logux لمناقشة المنشورات وإعادة كتابة النافذة المنبثقة للنشر المتبادل من RSS. ولكن أولاً ، لماذا لم نكن راضيين عن الحلول المتاحة.
الحلول الممكنة: WebSocket و Firebase و Swarm.js
عادة ، يتم استخدام WebSocket لتنفيذ معلومات التحديث التلقائي. مع ذلك ، لا تحتاج إلى إرسال طلبات للحصول على معلومات كل ثانية ، كما تفعل مع طلبات HTTP التقليدية ، وسيكون حجم الرأس صغيرًا. ومع ذلك ، فإن WebSocket لها عيوب:
- . , , , . , : , ;
- , . ;
- , , ;
- , (CRDT), .
Firebase, , — . Firebase CRDT, Redux . CRDT Swarm.js, Redux , .
Logux
, — Logux. , Redux, CRDT . - , API-: , .
, , « ». :
imports-api.js
update (project, id, data) {
return put(project, `settings/imports/${ id }`, convert(data))
}
imports.js
onUpdate (projectId, importId, changed) {
return dispatch({
projectId,
importId,
import: changed,
type: 'amplifr/imports/update'
})
}
:
imports.js
let dispatch = useDispatch()
let onUpdate = (projectId, importId, changed) => dispatch.sync({
projectId,
importId,
import: changed,
type: 'amplifr/imports/update'
}, { channels: ['imports/' + projectId] })
sync
dispatch
, , , . , Logux .
Logux

— . , . , .
, Logux-. .
Logux Redux. Redux createStore
. Logux, «» createLoguxCreator
:
import { createLoguxCreator } from '@logux/redux'
let createStore = createLoguxCreator({
credentials: loguxToken,
subprotocol: '0.6.5',
userId,
server: 'wss://logux.amplifr.com'
})
let store = createStore(reducers)
store.client.start()
createLoguxCreator
:
- . , . gon;
- Logux-;
- . , , .
Logux- , . , - posts/1
, . , WebSocket.
? — , , posts/1
. subscribe
— :
import useSubscription from '@logux/redux/use-subscription'
let Notices = props => {
let isSubscribing = useSubscription([`projects/${ props.id }`])
if (isSubscribing) {
return <Loader />
} else {
}
}
— , HTTP- Redux- Logux, Logux- :
def schedule_logux
LoguxNotificationWorker.perform_async(
{ type: 'amplifr/notices/add', notice: Logux::NoticePresenter.new(notice).to_json },
channels: ["projects/#{project.id}"]
)
end
, . - , , . , :
import { useDispatch } from 'react-redux'
let dispatch = useDispatch()
let onNoticeHide = noticeId => dispatch.sync({
type: 'amplifr/notices/hide',
noticeId
}, {
channels: [`projects/${ projectId }`]
})
, , Logux:

-
Logux , . , - , . , . , , , .
Logux . «» , , localStorage. , Firefox Safari localStorage! Logux , , . , .
, , Logux , , . , , .
, Logux, .
Logux

, Logux, . — , :
let PostEditor = { isApprovable, postId, … } => {
let isSubscribing = useSubscription(isApprovable ? [`posts/${ postId }`] : [])
if (isSubscribing) {
return <Loader />
} else {
}
}
notes
:
import { useDispatch, useSelector } from 'react-redux'
let dispatch = useDispatch()
let notes = useSelector(notes.filter(note => note.postId === postId))
let onNoteAdd = note => dispatch.sync({
type: 'amplifr/notes/add',
note
}, {
channels: [`posts/${ postId }`]
})
. , :
export default function notes (state = [], action) {
if (action.type === 'amplifr/notes/add') {
return state.concat([action.note])
} else if (action.type === 'amplifr/posts/close') {
return state.filter(i => i.postId !== action.postId)
} else {
return state
}
}
. Logux, .

, @subscribe
. , isSubscribing: true
. , , . .
, . , squid 3, WebSocket ( Logux WebSocket). , , . — .
, Logux AJAX-. , «» «» Firefox.
AJAX Logux RSS. RSS- . RSS-, .

. :
import { useDispatch } from 'react-redux'
let dispatch = useDispatch()
let onCreate = (projectId, importId, import) => {
return dispatch.sync({
importId,
import,
type: 'amplifr/imports/add'
}, { channels: ['imports/' + projectId] })
}
let onUpdate = (projectId, importId, changed) => {
return dispatch.sync({
importId,
changed,
type: 'amplifr/imports/update'
}, { channels: ['imports/' + projectId] })
}
, , , Logux Optimitstic UI — . , , . - , .

«» .
«», , RSS- . , . Logux (dispatch.sync(…).catch(…)
), . -, , .
: catch()
JSON, try { JSON.parse(…) } catch { … }
. .
Logux ?
Logux WebSocket, , SPA . , . , , :
import status from '@logux/client/status'
let connected = false
status(store.client, state => {
if (state === 'synchronized') connected = true
}
setTimeout(() => {
if (!connected) {
sentry.track(new Error('No Logux connection for 10 minutes'))
}
}, 60 * 1000)
100 , . , - , :

, WebSocket: -, , , , , AdBlocker Kaspersky Protection. , , , Logux, .
Logux ,
Logux , . , , . RSS-, , . - , , , , .
, . — Logux. -. , , , , :
import log from '@logux/client/log'
let store = createStore(reducers)
log(store.client)
window.store = store
:
- RSS- ;
- , ;
- RSS-, ;
- ;
- !
:
window.store.client.log.store.created.filter(i => i[0].type === 'amplifr/popups/set')
, - : meta.tab
undefined
. , . , client.id
client.tabId
@logux/redux
id
tabId
. , , Logux , , .
, - «» Logux, :
Logux : «? , , ?». , — , , Redux. « », , . Logux .

:
- Logux, Redux, CRDT ;
- C Logux , ;
- Logux , ;
- لدى Logux عيوبه: النظام ليس بسيطًا ، لا تزال هناك أخطاء ولا يتم العثور على الحلول دائمًا بسرعة ؛
- في Amplifer ، تفوقت مزايا Logux على السلبيات. نخطط لمواصلة استخدامه عند تنفيذ المشاريع المناسبة.

⌘⌘⌘
آمل أن يجد Logux الاستخدام في مشروعك. إذا كان لديك أي أسئلة ، يرجى الكتابة لي على Twitter أو عن طريق البريد .
بفضل الكسندر Marfitsin و اندريه Sitnik لمساعدتهم في إعداد هذه المادة.