5_Разетдинов.pptx
- Количество слайдов: 99
Mob. X: простое управление состоянием веб-приложения Азат Разетдинов, руководитель отдела разработки интерфейсов персональных сервисов
CV 3
Состояние приложения Текущий урл Отображение Состояние приложения Состояние на сервере Локальное хранилище 4
Состояние — это дерево 5
Состояние — это живое дерево 6
Как отслеживать изменения состояния?
Ручная подписка на изменения 1. Используем текущее состояние 2. Подписываемся на изменения 3. Реагируем на изменения 8
Ручная подписка на изменения render(state); state. on(‘change’, () => { render(state); }); 9
Проблемы ручной подписки на изменения
│ Переподписка: │ отслеживание │ изменения всего │ дерева избыточно
│ Недоподписка: │ отслеживание │ отдельных полей │ в конце концов ведёт │ к неконсистентности
13
Mob. X: автоматическая подписка
@observable
@observable class Person { @observable first. Name = 'Vasya'; @observable last. Name = 'Pupkin'; @observable nick. Name; . . . } 16
@observable A B 1 First Name: Vasya 2 Last Name: Pupkin 3 Nickname: 4 5 17
│ @observable содержит │ исходные данные
@computed
@computed class Person {. . . @computed get full. Name() { return this. first. Name + ' ' + this. last. Name; }. . . } 20
@computed A B 1 first. Name: Vasya 2 last. Name: Pupkin 3 nick. Name: 4 full. Name: = B 1&" "&B 2 5 21
│ @computed зависит │ от исходных данных │ и возвращает │ производные данные
@action
@action class Person {. . . @action set. Nick. Name(nick. Name) { this. nick. Name = nick. Name; } } 24
│ @action изменяет │ исходные данные
http: //www. reactiongifs. com/magic-3/ 26
Реакции
│ Реакции срабатывают │ при каждом изменении │ исходных данных, │ от которых они зависят
autorun
autorun(() => { console. log(person. nick. Name || person. full. Name); }); 30
autorun A B 1 first. Name: Vasya 2 last. Name: Pupkin 3 nick. Name: 4 full. Name: 5 autorun: = B 1&" "&B 2 =IF(B 3="", B 4, B 3) 31
autorun(() => { console. log(person. nick. Name || person. full. Name); }); 32
│ @observable │ и @computed │ используют геттеры для │ подписки на изменения
autorun(() => { console. log(person. nick. Name || person. full. Name); }); 34
@computed class Person {. . . @computed get full. Name() { return this. first. Name + ‘ ‘ + this. last. Name; }. . . } 35
Наблюдаемые поля › › person. nick. Name person. full. Name person. first. Name person. last. Name 36
Дерево зависимостей first. Name full. Name last. Name autorun nick. Name 37
Уведомление об изменении данных
@action person. set. Nick. Name('Bacek'); 39
@action class Person {. . . @action set. Nick. Name(nick. Name) { this. nick. Name = nick. Name; } } 40
│ @observable │ использует сеттер │ для уведомления │ об изменении данных
autorun(() => { console. log(person. nick. Name || person. full. Name); }); 42
autorun(() => { console. log(person. nick. Name || person. full. Name); }); 43
Наблюдаемые поля › person. nick. Name Ненаблюдаемые поля › › › person. full. Name person. first. Name person. last. Name 44
Дерево зависимостей first. Name full. Name last. Name autorun nick. Name 45
│ autorun при каждом │ выполнении заново │ собирает список │ зависимостей
│ Минимальный набор │ подписок может быть │ получен только │ в рантайме
@observer
@observer class Profile. View extends React. Component { render() { const person = { this. props }; if (person. nick. Name) { return <div>{person. nick. Name}</div>; } return <div>{person. full. Name}</div>}; } } 49
│ @observer │ перерисовывывает │ React-компонент │ при изменении данных
@observer class Some. Component extends React. Component { render() { if (!this. props. is. Enabled) { return null; }. . . много кода. . . } } 51
│ Динамическая подписка │ кардинально снижает │ число перерисовок │ React-компонента
Кэширование @computed
Кэширование @computed class Person {. . . @computed get full. Name() { return this. first. Name + ' ' + this. last. Name; }. . . } 54
Кэширование @computed first. Name full. Name last. Name autorun nick. Name 55
│ @computed кэширует │ своё значение, пока │ есть хотя бы один │ подписчик
Кэширование @computed first. Name full. Name last. Name autorun nick. Name 57
│ При отсутствии │ подписчиков │ @computed работает │ как обычный геттер
Асинхронные данные
Асинхронные данные class Person { bio: from. Promise(load. Bio()) } person. bio. state // pending|fulfilled|rejected person. bio. value 60
Итого
Плюсы Mob. X › › › Минимум бойлерплейта Автоматическая подписка в рантайме Производительность искаропке 62
Секундочку…
› › › А как же immutability? А как же time travel? А как же devtools? 64
Redux
Redux › › ООП → функциональное программирование › Очень много бойлерплейта : ( Модели → immutable-структуры Методы → экшны + редьюсеры Связи → нормализация + селекторы 66
© Metro-Goldwyn-Mayer 67
© Paramount Pictures 68
69
https: //i. ytimg. com/vi/ab. LSL 1 e. AE 3 U/maxresdefault. jpg 70
mobx-state-tree
│ mobx-state-tree │ совмещает все плюсы │ mobx и redux
types. model const Todo = types. model({ }); 73
types. string const Todo = types. model({ title: types. string }); 74
types. optional const Todo = types. model({ title: types. string, is. Completed: types. optional(types. boolean, false) }); 75
types. optional const Todo = types. model({ title: types. string, is. Completed: false }); 76
types. reference const Message = types. model({ title: types. string, is. Completed: false, folder: types. reference(Folder) }); 77
types. identifier() const Folder = types. model({ id: types. identifier(), title: types. string }); 78
types. array const Todo. Store = types. model({ todos: types. array(Todo), folders: types. array(Folder) }); 79
views const Todo. Store = types. model({ todos: types. array(Todo), folders: types. array(Folder) }). views(self => ({ get completed. Todos() { return self. todos. filter(todo => { return todo. is. Completed; }); } })); 80
actions const Folder = types. model({ id: types. identifier(), title: types. string }). actions(self => ({ set. Title(title) { self. title = title; } })); 81
Model. create const data = { todos: [{title: 'Выспаться', folder: '1'}], folders: [{id: '1', title: 'Срочные дела'}] }; const todo. Store = Todo. Store. create(data); 82
@observer class Todo. View extends React. Component { render() { const todo = { this. props }; . . . <div class="title">{todo. title}</div> <div class="folder">{todo. folder. title}</div>. . . } } 83
https: //i. ytimg. com/vi/ab. LSL 1 e. AE 3 U/maxresdefault. jpg 84
© Cake Entertainment 85
get. Snapshot const snapshot = get. Snapshot(todo. Store); 86
│ get. Snapshot │ сериализует модель │ в простой объект
on. Snapshot(todo. Store, (snapshot) => { console. dir(snapshot); }); 88
│ on. Snapshot │ генерирует снэпшоты │ при каждом │ изменении модели
│ Неизменившиеся │ части дерева │ реиспользуются │ (structural sharing)
apply. Snapshot(todo. Store, snapshot); 91
│ apply. Snapshot │ реиспользует модели │ с совпадающими │ идентификаторами │ (reconciliation)
Живая модель и снэпшоты https: //youtu. be/i. Dbf. WT-h. SG 8 93
Redux Store и Redux Devtools const redux. Store = as. Redux. Store(todo. Store); connect. Redux. Devtools(require("remotedev"), todo. Store); 94
Итого
Плюсы mobx-state-tree › › › ООП-модели вместо immutable-структур Живые ссылки вместо селекторов Дешёвое получение снэпшотов всего дерева Применение снэпшотов с реконсайлингом Адаптеры для Redux Store и Redux Devtools 96
│ Mob. X — │ Like React, but for Data Daniel Earwicker Chief Software Architect, FISCAL Technologies Ltd
Ссылки › › › Mobx https: //mobx. js. org › › Mobx vs. Redux Performance: https: //clck. ru/B 4 DW 5 Mobx-state-tree https: //github. com/mobxjs/mobx-state-tree Becoming fully reactive: an in-depth explanation of Mob. X https: //clck. ru/B 4 DL 9 An artificial example where Mob. X really shines and Redux is not really suited for it https: //clck. ru/B 4 Dj. N 98
Спасибо! Азат Разетдинов руководитель отдела разработки интерфейсов azat@yandex-team. ru @razetdinov
5_Разетдинов.pptx