@dxtmisha/wiki 0.24.0 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/media/functional/en/about.mdx +55 -0
- package/src/media/functional/en/dataStorage.mdx +61 -83
- package/src/media/functional/en/useApiRef.mdx +517 -0
- package/src/media/functional/en/useBroadcastValueRef.mdx +344 -0
- package/src/media/functional/en/useCookieRef.mdx +348 -0
- package/src/media/functional/en/useGeoIntlRef.mdx +288 -0
- package/src/media/functional/en/useHashRef.mdx +302 -0
- package/src/media/functional/en/useLazyRef.mdx +329 -0
- package/src/media/functional/en/useLoadingRef.mdx +159 -0
- package/src/media/functional/en/useSessionRef.mdx +248 -0
- package/src/media/functional/en/useStorageRef.mdx +242 -0
- package/src/media/functional/en/useTranslateRef.mdx +312 -0
- package/src/media/functional/ru/about.mdx +55 -0
- package/src/media/functional/ru/dataStorage.mdx +59 -81
- package/src/media/functional/ru/useApiRef.mdx +517 -0
- package/src/media/functional/ru/useBroadcastValueRef.mdx +344 -0
- package/src/media/functional/ru/useCookieRef.mdx +348 -0
- package/src/media/functional/ru/useGeoIntlRef.mdx +288 -0
- package/src/media/functional/ru/useHashRef.mdx +302 -0
- package/src/media/functional/ru/useLazyRef.mdx +329 -0
- package/src/media/functional/ru/useLoadingRef.mdx +159 -0
- package/src/media/functional/ru/useSessionRef.mdx +248 -0
- package/src/media/functional/ru/useStorageRef.mdx +242 -0
- package/src/media/functional/ru/useTranslateRef.mdx +312 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import {Meta} from '@storybook/addon-docs/blocks'
|
|
2
|
+
|
|
3
|
+
<Meta title='@dxtmisha/functional/ru/Composables/useBroadcastValueRef'/>
|
|
4
|
+
|
|
5
|
+
# Композабл useBroadcastValueRef
|
|
6
|
+
|
|
7
|
+
Композабл для создания реактивной переменной, синхронизированной между вкладками браузера через Broadcast Channel API. Автоматически управляет передачей данных между открытыми вкладками одного домена в реальном времени без использования localStorage. Идеально подходит для синхронизации состояния приложения, уведомлений и обмена данными между вкладками.
|
|
8
|
+
|
|
9
|
+
## Основные возможности
|
|
10
|
+
|
|
11
|
+
- **Реальная синхронизация** — мгновенная передача данных между вкладками браузера
|
|
12
|
+
- **Broadcast Channel API** — использует нативный браузерный API для эффективной коммуникации
|
|
13
|
+
- **Автоматическая синхронизация** — изменения в одной вкладке автоматически отражаются во всех остальных
|
|
14
|
+
- **Singleton паттерн** — переиспользование канала для одинаковых имён
|
|
15
|
+
- **Уникальная идентификация** — каждая сессия браузера получает уникальный ID
|
|
16
|
+
- **Типобезопасность** — полная поддержка TypeScript с дженериками
|
|
17
|
+
- **Значения по умолчанию** — поддержка начальных значений и функций-фабрик
|
|
18
|
+
- **Изоляция сессий** — данные не передаются между разными сессиями браузера
|
|
19
|
+
|
|
20
|
+
## Функция
|
|
21
|
+
|
|
22
|
+
### `useBroadcastValueRef`
|
|
23
|
+
|
|
24
|
+
Создаёт реактивную переменную, синхронизированную между вкладками через Broadcast Channel.
|
|
25
|
+
|
|
26
|
+
**Параметры:**
|
|
27
|
+
- `name: string` — имя канала для связи между вкладками
|
|
28
|
+
- `defaultValue?: T | string | (() => (T | string))` — значение по умолчанию или функция для его генерации (опционально)
|
|
29
|
+
|
|
30
|
+
**Возвращает:** `Ref<T | string | undefined>` — реактивная переменная Vue, синхронизированная между вкладками
|
|
31
|
+
|
|
32
|
+
```javascript
|
|
33
|
+
import { useBroadcastValueRef } from '@dxtmisha/functional'
|
|
34
|
+
|
|
35
|
+
// Простое использование
|
|
36
|
+
const sharedCounter = useBroadcastValueRef('counter')
|
|
37
|
+
|
|
38
|
+
// С значением по умолчанию
|
|
39
|
+
const activeTab = useBroadcastValueRef('active-tab', 'home')
|
|
40
|
+
|
|
41
|
+
// С функцией по умолчанию
|
|
42
|
+
const sessionId = useBroadcastValueRef('session', () => Math.random().toString(36))
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Основное использование
|
|
46
|
+
|
|
47
|
+
### Базовая синхронизация
|
|
48
|
+
|
|
49
|
+
```javascript
|
|
50
|
+
import { useBroadcastValueRef } from '@dxtmisha/functional'
|
|
51
|
+
|
|
52
|
+
// Создание синхронизированной переменной
|
|
53
|
+
const counter = useBroadcastValueRef('counter', 0)
|
|
54
|
+
|
|
55
|
+
// В первой вкладке
|
|
56
|
+
counter.value = 5
|
|
57
|
+
|
|
58
|
+
// Во второй вкладке автоматически обновляется
|
|
59
|
+
console.log(counter.value) // 5
|
|
60
|
+
|
|
61
|
+
// Изменение в любой вкладке отражается во всех остальных
|
|
62
|
+
counter.value = 10
|
|
63
|
+
// Все вкладки получат counter.value === 10 мгновенно
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Singleton паттерн
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
// При повторном вызове с тем же именем возвращается существующий канал
|
|
70
|
+
const channel1 = useBroadcastValueRef('notifications', null)
|
|
71
|
+
const channel2 = useBroadcastValueRef('notifications', null)
|
|
72
|
+
|
|
73
|
+
console.log(channel1 === channel2) // true - тот же ref
|
|
74
|
+
|
|
75
|
+
channel1.value = { type: 'info', message: 'Привет!' }
|
|
76
|
+
console.log(channel2.value) // { type: 'info', message: 'Привет!' }
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Использование в компонентах
|
|
80
|
+
|
|
81
|
+
### Синхронизация уведомлений
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
import { useBroadcastValueRef } from '@dxtmisha/functional'
|
|
85
|
+
|
|
86
|
+
export default {
|
|
87
|
+
setup() {
|
|
88
|
+
const notification = useBroadcastValueRef('notification', null)
|
|
89
|
+
|
|
90
|
+
const showNotification = (message, type = 'info') => {
|
|
91
|
+
notification.value = {
|
|
92
|
+
id: Date.now(),
|
|
93
|
+
type,
|
|
94
|
+
message,
|
|
95
|
+
timestamp: new Date()
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
notification,
|
|
101
|
+
showNotification
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Template:
|
|
107
|
+
// <div v-if="notification" class="notification">
|
|
108
|
+
// <span>{{ notification.message }}</span>
|
|
109
|
+
// <button @click="notification = null">×</button>
|
|
110
|
+
// </div>
|
|
111
|
+
//
|
|
112
|
+
// Уведомление показывается во всех открытых вкладках
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Общий счётчик
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
import { useBroadcastValueRef } from '@dxtmisha/functional'
|
|
119
|
+
|
|
120
|
+
export default {
|
|
121
|
+
setup() {
|
|
122
|
+
const cartCount = useBroadcastValueRef('cart-count', 0)
|
|
123
|
+
|
|
124
|
+
const addToCart = () => {
|
|
125
|
+
cartCount.value = (cartCount.value || 0) + 1
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const removeFromCart = () => {
|
|
129
|
+
if (cartCount.value > 0) {
|
|
130
|
+
cartCount.value--
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
cartCount,
|
|
136
|
+
addToCart,
|
|
137
|
+
removeFromCart
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Template:
|
|
143
|
+
// <div>
|
|
144
|
+
// <span class="badge">{{ cartCount }}</span>
|
|
145
|
+
// <button @click="addToCart">Добавить</button>
|
|
146
|
+
// <button @click="removeFromCart">Удалить</button>
|
|
147
|
+
// </div>
|
|
148
|
+
//
|
|
149
|
+
// Счётчик синхронизируется между всеми вкладками
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Синхронизация состояния
|
|
153
|
+
|
|
154
|
+
### Активная вкладка
|
|
155
|
+
|
|
156
|
+
```javascript
|
|
157
|
+
import { useBroadcastValueRef } from '@dxtmisha/functional'
|
|
158
|
+
import { watch } from 'vue'
|
|
159
|
+
|
|
160
|
+
export default {
|
|
161
|
+
setup() {
|
|
162
|
+
const activeTab = useBroadcastValueRef('active-nav-tab', 'home')
|
|
163
|
+
|
|
164
|
+
const setActiveTab = (tab) => {
|
|
165
|
+
activeTab.value = tab
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Отслеживание изменений из других вкладок
|
|
169
|
+
watch(activeTab, (newTab) => {
|
|
170
|
+
console.log(`Активная вкладка изменена на: ${newTab}`)
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
activeTab,
|
|
175
|
+
setActiveTab
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// При переключении вкладки в одном окне,
|
|
181
|
+
// все остальные окна синхронизируются
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Статус авторизации
|
|
185
|
+
|
|
186
|
+
```javascript
|
|
187
|
+
import { useBroadcastValueRef } from '@dxtmisha/functional'
|
|
188
|
+
|
|
189
|
+
export default {
|
|
190
|
+
setup() {
|
|
191
|
+
const isAuthenticated = useBroadcastValueRef('auth-status', false)
|
|
192
|
+
const currentUser = useBroadcastValueRef('current-user', null)
|
|
193
|
+
|
|
194
|
+
const login = (userData) => {
|
|
195
|
+
currentUser.value = userData
|
|
196
|
+
isAuthenticated.value = true
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const logout = () => {
|
|
200
|
+
currentUser.value = null
|
|
201
|
+
isAuthenticated.value = false
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
isAuthenticated,
|
|
206
|
+
currentUser,
|
|
207
|
+
login,
|
|
208
|
+
logout
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// При входе/выходе в одной вкладке,
|
|
214
|
+
// все остальные вкладки обновляются мгновенно
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Отличия от useStorageRef
|
|
218
|
+
|
|
219
|
+
```javascript
|
|
220
|
+
// useStorageRef - через localStorage (постоянное хранение)
|
|
221
|
+
const persistentData = useStorageRef('theme', 'light')
|
|
222
|
+
// - Данные сохраняются в localStorage
|
|
223
|
+
// - Синхронизация через storage event
|
|
224
|
+
// - Более медленная передача данных
|
|
225
|
+
// - Данные остаются после закрытия браузера
|
|
226
|
+
|
|
227
|
+
// useBroadcastValueRef - через Broadcast Channel (в памяти)
|
|
228
|
+
const realtimeData = useBroadcastValueRef('active-users', [])
|
|
229
|
+
// - Данные в памяти (не сохраняются)
|
|
230
|
+
// - Мгновенная синхронизация
|
|
231
|
+
// - Быстрая передача данных
|
|
232
|
+
// - Данные теряются при закрытии всех вкладок
|
|
233
|
+
// - Изоляция между сессиями браузера
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Работа с типами данных
|
|
237
|
+
|
|
238
|
+
```javascript
|
|
239
|
+
// Числа
|
|
240
|
+
const counter = useBroadcastValueRef<number>('counter', 0)
|
|
241
|
+
counter.value = 42
|
|
242
|
+
|
|
243
|
+
// Строки
|
|
244
|
+
const message = useBroadcastValueRef<string>('message', '')
|
|
245
|
+
message.value = 'Привет из другой вкладки!'
|
|
246
|
+
|
|
247
|
+
// Объекты
|
|
248
|
+
const userState = useBroadcastValueRef('user-state', {
|
|
249
|
+
id: null,
|
|
250
|
+
name: '',
|
|
251
|
+
online: false
|
|
252
|
+
})
|
|
253
|
+
userState.value = { id: 1, name: 'Иван', online: true }
|
|
254
|
+
|
|
255
|
+
// Массивы
|
|
256
|
+
const activeUsers = useBroadcastValueRef<number[]>('active-users', [])
|
|
257
|
+
activeUsers.value = [1, 2, 3, 4, 5]
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Примеры использования
|
|
261
|
+
|
|
262
|
+
### Чат между вкладками
|
|
263
|
+
|
|
264
|
+
```javascript
|
|
265
|
+
const chatMessages = useBroadcastValueRef('chat-messages', [])
|
|
266
|
+
|
|
267
|
+
const sendMessage = (text) => {
|
|
268
|
+
const message = {
|
|
269
|
+
id: Date.now(),
|
|
270
|
+
text,
|
|
271
|
+
timestamp: new Date()
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
chatMessages.value = [...(chatMessages.value || []), message]
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Сообщения появляются во всех открытых вкладках мгновенно
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Синхронизация воспроизведения
|
|
281
|
+
|
|
282
|
+
```javascript
|
|
283
|
+
const playerState = useBroadcastValueRef('player', {
|
|
284
|
+
isPlaying: false,
|
|
285
|
+
currentTime: 0,
|
|
286
|
+
track: null
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
const play = () => {
|
|
290
|
+
playerState.value = { ...playerState.value, isPlaying: true }
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const pause = () => {
|
|
294
|
+
playerState.value = { ...playerState.value, isPlaying: false }
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Управление плеером в одной вкладке
|
|
298
|
+
// влияет на плееры во всех остальных
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Общие фильтры
|
|
302
|
+
|
|
303
|
+
```javascript
|
|
304
|
+
const filters = useBroadcastValueRef('search-filters', {
|
|
305
|
+
category: 'all',
|
|
306
|
+
priceMin: 0,
|
|
307
|
+
priceMax: 10000
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
const updateFilter = (key, value) => {
|
|
311
|
+
filters.value = { ...filters.value, [key]: value }
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Изменение фильтров синхронизируется
|
|
315
|
+
// между всеми открытыми страницами поиска
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Уникальная идентификация сессии
|
|
319
|
+
|
|
320
|
+
Композабл автоматически создаёт уникальный ID для каждой сессии браузера и сохраняет его в localStorage:
|
|
321
|
+
|
|
322
|
+
```javascript
|
|
323
|
+
// При первом запуске генерируется случайный ID
|
|
324
|
+
// broadcast__name_1234567__counter
|
|
325
|
+
// ^^^^^^^^^ - уникальный ID сессии
|
|
326
|
+
|
|
327
|
+
// Этот ID переиспользуется для всех каналов в рамках одной сессии
|
|
328
|
+
// Разные сессии браузера имеют разные ID и не пересекаются
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Особенности Broadcast Channel API
|
|
332
|
+
|
|
333
|
+
```javascript
|
|
334
|
+
// Broadcast Channel работает только:
|
|
335
|
+
// - В рамках одного origin (протокол + домен + порт)
|
|
336
|
+
// - Между вкладками одного браузера
|
|
337
|
+
// - В той же сессии браузера
|
|
338
|
+
|
|
339
|
+
// НЕ работает:
|
|
340
|
+
// - Между разными браузерами
|
|
341
|
+
// - Между разными доменами
|
|
342
|
+
// - В режиме инкогнито между обычными вкладками
|
|
343
|
+
```
|
|
344
|
+
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import {Meta} from '@storybook/addon-docs/blocks'
|
|
2
|
+
|
|
3
|
+
<Meta title='@dxtmisha/functional/ru/Composables/useCookieRef'/>
|
|
4
|
+
|
|
5
|
+
# Композабл useCookieRef
|
|
6
|
+
|
|
7
|
+
Композабл для создания реактивной переменной, синхронизированной с cookies браузера. Автоматически управляет чтением и записью значений в cookies с поддержкой синхронизации между вкладками через Broadcast Channel API, настройками срока действия и singleton паттерна для эффективного переиспользования.
|
|
8
|
+
|
|
9
|
+
## Основные возможности
|
|
10
|
+
|
|
11
|
+
- **Двусторонняя синхронизация** — автоматическая синхронизация между ref и cookies
|
|
12
|
+
- **Синхронизация между вкладками** — изменения в одной вкладке мгновенно отражаются во всех остальных через Broadcast Channel
|
|
13
|
+
- **Автоматическое сохранение** — изменения ref автоматически сохраняются в cookies
|
|
14
|
+
- **Настройка срока действия** — поддержка expires, max-age и других опций cookies
|
|
15
|
+
- **Singleton паттерн** — переиспользование ref для одинаковых имён cookies
|
|
16
|
+
- **Типобезопасность** — полная поддержка TypeScript с дженериками
|
|
17
|
+
- **Значения по умолчанию** — поддержка начальных значений и функций-фабрик
|
|
18
|
+
- **Интеграция с Cookie** — использует класс Cookie для управления cookies
|
|
19
|
+
|
|
20
|
+
## Функция
|
|
21
|
+
|
|
22
|
+
### `useCookieRef`
|
|
23
|
+
|
|
24
|
+
Создаёт реактивную переменную, синхронизированную с cookies браузера.
|
|
25
|
+
|
|
26
|
+
**Параметры:**
|
|
27
|
+
- `name: string` — имя cookie
|
|
28
|
+
- `defaultValue?: T | string | (() => (T | string))` — значение по умолчанию или функция для его генерации (опционально)
|
|
29
|
+
- `options?: CookieOptions` — дополнительные параметры cookie (опционально)
|
|
30
|
+
|
|
31
|
+
**Возвращает:** `Ref<T | string | undefined>` — реактивная переменная Vue, связанная с cookie
|
|
32
|
+
|
|
33
|
+
**CookieOptions:**
|
|
34
|
+
- `expires?: number | Date` — дата истечения срока действия
|
|
35
|
+
- `path?: string` — путь для cookie (по умолчанию '/')
|
|
36
|
+
- `domain?: string` — домен для cookie
|
|
37
|
+
- `secure?: boolean` — использовать только HTTPS
|
|
38
|
+
- `sameSite?: 'Strict' | 'Lax' | 'None'` — политика SameSite
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
import { useCookieRef } from '@dxtmisha/functional'
|
|
42
|
+
|
|
43
|
+
// Простое использование
|
|
44
|
+
const userToken = useCookieRef('token')
|
|
45
|
+
|
|
46
|
+
// С значением по умолчанию
|
|
47
|
+
const theme = useCookieRef('theme', 'light')
|
|
48
|
+
|
|
49
|
+
// С опциями (срок действия 7 дней)
|
|
50
|
+
const rememberMe = useCookieRef('remember', true, {
|
|
51
|
+
expires: 7,
|
|
52
|
+
path: '/',
|
|
53
|
+
sameSite: 'Lax'
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Основное использование
|
|
58
|
+
|
|
59
|
+
### Базовая синхронизация
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
import { useCookieRef } from '@dxtmisha/functional'
|
|
63
|
+
|
|
64
|
+
// Создание ref, синхронизированного с cookie
|
|
65
|
+
const userTheme = useCookieRef('theme', 'light')
|
|
66
|
+
|
|
67
|
+
// При изменении ref - cookie автоматически обновляется
|
|
68
|
+
userTheme.value = 'dark'
|
|
69
|
+
// document.cookie содержит: 'theme=dark'
|
|
70
|
+
|
|
71
|
+
// При перезагрузке страницы значение восстанавливается
|
|
72
|
+
console.log(userTheme.value) // 'dark'
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Singleton паттерн
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
// При повторном вызове с тем же именем возвращается существующий ref
|
|
79
|
+
const token1 = useCookieRef('auth-token', '')
|
|
80
|
+
const token2 = useCookieRef('auth-token', '')
|
|
81
|
+
|
|
82
|
+
console.log(token1 === token2) // true - тот же ref
|
|
83
|
+
|
|
84
|
+
token1.value = 'abc123'
|
|
85
|
+
console.log(token2.value) // 'abc123' - оба указывают на один ref
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Синхронизация между вкладками
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
import { useCookieRef } from '@dxtmisha/functional'
|
|
92
|
+
|
|
93
|
+
// В первой вкладке
|
|
94
|
+
const userState = useCookieRef('user-online', 'active')
|
|
95
|
+
userState.value = 'away'
|
|
96
|
+
|
|
97
|
+
// Во второй вкладке автоматически обновляется мгновенно
|
|
98
|
+
// userState.value === 'away' (благодаря Broadcast Channel)
|
|
99
|
+
|
|
100
|
+
// В третьей вкладке
|
|
101
|
+
const sameState = useCookieRef('user-online', 'active')
|
|
102
|
+
console.log(sameState.value) // 'away'
|
|
103
|
+
|
|
104
|
+
// Изменение в любой вкладке синхронизируется со всеми остальными
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Использование в компонентах
|
|
108
|
+
|
|
109
|
+
### Настройки пользователя
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
import { useCookieRef } from '@dxtmisha/functional'
|
|
113
|
+
|
|
114
|
+
export default {
|
|
115
|
+
setup() {
|
|
116
|
+
const theme = useCookieRef('user-theme', 'light', {
|
|
117
|
+
expires: 365 // 1 год
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
const language = useCookieRef('user-language', 'ru', {
|
|
121
|
+
expires: 365
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
const fontSize = useCookieRef('font-size', 16, {
|
|
125
|
+
expires: 30 // 30 дней
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
theme,
|
|
130
|
+
language,
|
|
131
|
+
fontSize
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Template:
|
|
137
|
+
// <div>
|
|
138
|
+
// <select v-model="theme">
|
|
139
|
+
// <option value="light">Светлая</option>
|
|
140
|
+
// <option value="dark">Тёмная</option>
|
|
141
|
+
// </select>
|
|
142
|
+
// <select v-model="language">
|
|
143
|
+
// <option value="ru">Русский</option>
|
|
144
|
+
// <option value="en">English</option>
|
|
145
|
+
// </select>
|
|
146
|
+
// </div>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Авторизация с "Запомнить меня"
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
import { useCookieRef } from '@dxtmisha/functional'
|
|
153
|
+
|
|
154
|
+
export default {
|
|
155
|
+
setup() {
|
|
156
|
+
const authToken = useCookieRef('auth-token', '', {
|
|
157
|
+
expires: 7, // 7 дней
|
|
158
|
+
secure: true,
|
|
159
|
+
sameSite: 'Strict'
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
const rememberMe = useCookieRef('remember-me', false)
|
|
163
|
+
|
|
164
|
+
const login = (token) => {
|
|
165
|
+
const expires = rememberMe.value ? 30 : 1 // 30 дней или 1 день
|
|
166
|
+
authToken.value = token
|
|
167
|
+
|
|
168
|
+
// Обновить опции cookie
|
|
169
|
+
useCookieRef('auth-token', token, {
|
|
170
|
+
expires,
|
|
171
|
+
secure: true,
|
|
172
|
+
sameSite: 'Strict'
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const logout = () => {
|
|
177
|
+
authToken.value = ''
|
|
178
|
+
rememberMe.value = false
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
authToken,
|
|
183
|
+
rememberMe,
|
|
184
|
+
login,
|
|
185
|
+
logout
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Настройки cookie
|
|
192
|
+
|
|
193
|
+
### Срок действия
|
|
194
|
+
|
|
195
|
+
```javascript
|
|
196
|
+
// Expires в днях
|
|
197
|
+
const token = useCookieRef('token', '', {
|
|
198
|
+
expires: 7 // удалится через 7 дней
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
// Expires как Date
|
|
202
|
+
const session = useCookieRef('session', '', {
|
|
203
|
+
expires: new Date('2025-12-31')
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
// Без expires (сессионная cookie, удаляется при закрытии браузера)
|
|
207
|
+
const tempData = useCookieRef('temp', '')
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Безопасность
|
|
211
|
+
|
|
212
|
+
```javascript
|
|
213
|
+
// HTTPS only
|
|
214
|
+
const secureToken = useCookieRef('secure-token', '', {
|
|
215
|
+
secure: true,
|
|
216
|
+
sameSite: 'Strict'
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
// Cross-site запросы
|
|
220
|
+
const apiToken = useCookieRef('api-token', '', {
|
|
221
|
+
secure: true,
|
|
222
|
+
sameSite: 'None' // требуется secure: true
|
|
223
|
+
})
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Путь и домен
|
|
227
|
+
|
|
228
|
+
```javascript
|
|
229
|
+
// Для конкретного пути
|
|
230
|
+
const adminPrefs = useCookieRef('admin-prefs', {}, {
|
|
231
|
+
path: '/admin'
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
// Для поддомена
|
|
235
|
+
const sharedData = useCookieRef('shared', '', {
|
|
236
|
+
domain: '.example.com' // доступно для всех поддоменов
|
|
237
|
+
})
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Работа с типами данных
|
|
241
|
+
|
|
242
|
+
```javascript
|
|
243
|
+
// Строки
|
|
244
|
+
const username = useCookieRef<string>('username', '')
|
|
245
|
+
username.value = 'Иван'
|
|
246
|
+
|
|
247
|
+
// Числа (автоматическая сериализация)
|
|
248
|
+
const count = useCookieRef<number>('count', 0)
|
|
249
|
+
count.value = 42
|
|
250
|
+
|
|
251
|
+
// Булевы значения
|
|
252
|
+
const accepted = useCookieRef<boolean>('accepted', false)
|
|
253
|
+
accepted.value = true
|
|
254
|
+
|
|
255
|
+
// Объекты (JSON сериализация)
|
|
256
|
+
const userPrefs = useCookieRef('prefs', {
|
|
257
|
+
theme: 'dark',
|
|
258
|
+
notifications: true
|
|
259
|
+
})
|
|
260
|
+
userPrefs.value = { theme: 'light', notifications: false }
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Интеграция с классом Cookie
|
|
264
|
+
|
|
265
|
+
Композабл использует класс `Cookie` для управления cookies:
|
|
266
|
+
|
|
267
|
+
```javascript
|
|
268
|
+
import { Cookie } from '@dxtmisha/functional'
|
|
269
|
+
|
|
270
|
+
// Прямое использование класса
|
|
271
|
+
const cookie = new Cookie('theme')
|
|
272
|
+
cookie.set('dark', { expires: 30 })
|
|
273
|
+
console.log(cookie.get()) // 'dark'
|
|
274
|
+
|
|
275
|
+
// useCookieRef автоматически синхронизируется
|
|
276
|
+
const themeRef = useCookieRef('theme')
|
|
277
|
+
console.log(themeRef.value) // 'dark'
|
|
278
|
+
|
|
279
|
+
// Изменения через Cookie отражаются в ref (через Broadcast Channel)
|
|
280
|
+
cookie.set('light')
|
|
281
|
+
// themeRef.value автоматически обновится во всех вкладках
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Примеры использования
|
|
285
|
+
|
|
286
|
+
### Согласие на cookies (GDPR)
|
|
287
|
+
|
|
288
|
+
```javascript
|
|
289
|
+
const cookieConsent = useCookieRef('cookie-consent', false, {
|
|
290
|
+
expires: 365,
|
|
291
|
+
sameSite: 'Lax'
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
const acceptCookies = () => {
|
|
295
|
+
cookieConsent.value = true
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// В шаблоне:
|
|
299
|
+
// <div v-if="!cookieConsent" class="cookie-banner">
|
|
300
|
+
// <p>Мы используем cookies...</p>
|
|
301
|
+
// <button @click="acceptCookies">Принять</button>
|
|
302
|
+
// </div>
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Корзина покупок
|
|
306
|
+
|
|
307
|
+
```javascript
|
|
308
|
+
const cart = useCookieRef<CartItem[]>('shopping-cart', [], {
|
|
309
|
+
expires: 7
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
const addToCart = (item) => {
|
|
313
|
+
cart.value = [...cart.value, item]
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const removeFromCart = (itemId) => {
|
|
317
|
+
cart.value = cart.value.filter(item => item.id !== itemId)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Корзина синхронизируется между вкладками автоматически
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Отслеживание последних просмотров
|
|
324
|
+
|
|
325
|
+
```javascript
|
|
326
|
+
const recentlyViewed = useCookieRef<number[]>('recently-viewed', [], {
|
|
327
|
+
expires: 30
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
const addToRecent = (productId) => {
|
|
331
|
+
const recent = [productId, ...recentlyViewed.value.filter(id => id !== productId)]
|
|
332
|
+
recentlyViewed.value = recent.slice(0, 10) // Храним последние 10
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## Преимущества синхронизации через Broadcast Channel
|
|
337
|
+
|
|
338
|
+
**В отличие от обычного ref с cookie:**
|
|
339
|
+
- ✅ Мгновенная синхронизация между вкладками (не требуется перезагрузка)
|
|
340
|
+
- ✅ Реальное время обновления (без задержек)
|
|
341
|
+
- ✅ Эффективная передача данных (не через localStorage)
|
|
342
|
+
- ✅ Изоляция сессий браузера
|
|
343
|
+
|
|
344
|
+
**Механизм работы:**
|
|
345
|
+
1. Изменение в одной вкладке → сохраняется в cookie
|
|
346
|
+
2. Через Broadcast Channel (`__cookie:${name}`) отправляется сообщение
|
|
347
|
+
3. Все остальные вкладки получают обновление мгновенно
|
|
348
|
+
4. ref во всех вкладках обновляется автоматически
|