Swarm.js: реактивная синхронизация данных — Виктор Грищенко — MoscowJS 13
"Immutable данные в JS приложениях", Дмитрий Кунин, MoscowJS 20
Transcript of "Immutable данные в JS приложениях", Дмитрий Кунин, MoscowJS 20
Immutable DataНеизменяемые данные в приложении:
что, зачем и каклогитип moscow js
Дмитрий Кунин, AT-Consulting
Что это такое?
2 / 45
Immutable + Persistent
3 / 45
Принцип работы
var list = Immutable.List.of(1,2,3);var list2 = list.push(4);
console.log(list.toJS()) // [1,2,3] console.log(Immutable.is(list,list2)) // false
4 / 45
Принцип работы
List.prototype.push = function(value){ // Делаем клон var clone = deepCopy(this); // Меняем значение в клоне clone[clone.length] = value; // Вовзращаем клон return clone; }
5 / 45
var list = [];for(var i=0;i < 1000000;i++) { list.push(i);}
vanilla: 83 ms
var list = mori.vector();for (var i=0; i < 1000000; i++) { mori.conj(list, i);}
mori: 288 ms
Скорость работы
6 / 45
Направленный ациклический граф
7 / 45
8 / 45
9 / 45
10 / 45
11 / 45
12 / 45
13 / 45
14 / 45
Готовые библиотеки
immutable.jsmoriseamless-immutableimmutatopimsetitexoancient-oak
15 / 45
Зачем использовать?
16 / 45
Зачем использовать?
Решать проблемы!
17 / 45
Мутирующие объекты могут мутировать:)))
var identity = "Федор";...identity = "Федор Петрович";...identity = "Косой";
18 / 45
Интерфейс долженследить за изменениями
19 / 45
Слушатели событийvar userData = { name:"Федор", online:true, profilePic: "/url/user1.png" };
Object.observe(userData, function(changes){ rerenderProfile();});
userData.name = "Федор Петрович";userData.online = false;userData.profilePic = "/newurl/user2.png";
20 / 45
var userData = { ... };Object.observe(userData, ... );userData.name.nickname = "Косой"; // Изменения не замечены
21 / 45
"Грязная" проверка Грязные танцыvar userData = { dirty: false, _raw: { name: "Федор Петрович", online: true, profilePic: "/url/user1.png"}, get: function(key){ return this._raw[key] }, set: function(key, newValue){ this._raw[key] = newValue; this.dirty = true; }}
22 / 45
funtion renderProfile(data) { if(!data.dirty) return; data.dirty = false; ...}
userData.set("online", true);
renderProfile(userData); // ok
23 / 45
funtion renderProfile(data) { if(!data.dirty) return; data.dirty = false;}
funtion renderContactItem(data) { if(!data.dirty) return; data.dirty = false;}
data.set("online", true);
renderProfile(userData); // okrenderContactItem(userData); // dirty = false; no render
24 / 45
Сравнение Immutable объектов / списковvar user1 = Immutable.Map({ name: "Федор Петрович", online: true, profilePic: "/url/user1.png"});
25 / 45
Сравнение Immutable объектов / списковvar user1 = Immutable.Map({ name: "Федор Петрович", online: true, profilePic: "/url/user1.png"});
var user2 = user1.set("name", "Косой");console.log(Immutable.is(user1, user2)); // false
26 / 45
Сравнение Immutable объектов / списковvar user1 = Immutable.Map({ name: "Федор Петрович", online: true, profilePic: "/url/user1.png"});
var user2 = user1.set("name", "Косой");console.log(Immutable.is(user1, user2)); // false
var user3 = user2.set("name", "Федор Петрович");console.log(Immutable.is(user1, user3)); // true
27 / 45
Глубокое сравнениеvar user1 = Immutable.Map({ skills: Immutable.Map({str: 23, int: 18, luck: 99}), category: Immutable.Map({gentelaman: true})});
28 / 45
Глубокое сравнениеvar user1 = Immutable.Map({ skills: Immutable.Map({str: 23, int: 18, luck: 99}), category: Immutable.Map({gentelaman: true})});var user2 = user1.setIn(["skills", "int"], 0);
Immutable.is(user2 , user1)
29 / 45
Глубокое сравнениеvar user1 = Immutable.Map({ skills: Immutable.Map({str: 23, int: 18, luck: 99}), category: Immutable.Map({gentelaman: true})});var user2 = user1.setIn(["skills", "int"], 0);
Immutable.is(user2 , user1) Immutable.is(second.get("skills") , first.get("skills")) //false
30 / 45
Глубокое сравнениеvar user1 = Immutable.Map({ skills: Immutable.Map({str: 23, int: 18, luck: 99}), category: Immutable.Map({gentelaman: true})});var user2 = user1.setIn(["skills", "int"], 0);
Immutable.is(user2 , user1) Immutable.is(second.get("skills") , first.get("skills")) //falseImmutable.is(second.get("category"), first.get("category")) //true
31 / 45
Плюшки
32 / 45
Отсутствие побочных эффектов
function updateValueAndLog(updateFunction) { var data = {name: "Федор"}; updateFunction(data); console.log(data); // ?}
33 / 45
Отсутствие побочных эффектов
function updateValueAndLog(updateFunction) { var data = {name: "Федор"}; updateFunction(data); console.log(data); // ?}
var user = Immutable.Map({ name: "Федор Петрович", online: true, profilePic: "/url/user1.png"});
var userClone = user; 34 / 45
Практически бесплатныйundo/redo
35 / 45
0:00
36 / 45
Как пользоваться?
37 / 45
Массивыvar list1 = Immutable.List.of(1, 2);assert(list1.size === 2);
var list2 = list1.push(3, 4, 5);assert(list2.size === 5);
var list3 = list2.unshift(0);assert(list3.size === 6);
var list4 = list1.concat(list2, list3); assert(list4.size === 13);
assert(list4.get(0) === 1);
38 / 45
Объектыvar user1 = Immutable.Map({ name: "Федор Петрович", online: true, profilePic: "/url/user1.png"});
var user2 = user1.set("name", "Косой");
user1.get("name"); // "Федор Петрович"user2.get("name"); // "Косой"
39 / 45
Принимает и возвращает обычные JSобъектыvar user1 = Immutable.Map({ name: "Федор Петрович", online: true, profilePic: "/url/user1.png"});
var stat = { name:"Косой", age: 37 };var user2 = user1.merge(stat);
console.log(user2.toJS()) // { name: "Косой", // online: true, // profilePic: "/url/user1.png",// age: 37 } 40 / 45
Многое другое
StackOrderedMapSetOrderedSetRecord
41 / 45
Групповые операцииvar traits1 = Immutable.List.of("communication","luck","skill"var traits2 = traits1.withMutations(function (traits) { traits.push("dexterity").push("power").push("speed");});assert(traits1.size === 3);assert(traits2.size === 6);
42 / 45
Стоит пощупать вследующем проекте?
43 / 45
Стоит пощупать вследующем проекте?
Да!
44 / 45
Вопросы?Дмитрий Кунин
vcard : dkun.indemo + libs : bit.ly/imm-jstwitter : DKuninSkype : dkunin1985