Склад сгущенки на этой неделеimmer.
1. Введение
Immer — проект, ставший популярным в последнее время.MobxавторMweststrateНИОКР.
Студенты, знакомые с mobx, могут обнаружить, что Immer — это Mobx более низкого уровня, который обладает функциями Mobx и может быть объединен в любую структуру потока данных, которая очень элегантна в использовании.
2 Обзор
Проблемный Неизменный
Проблема, которую хочет решить Иммер, состоит в том, чтобы использовать метапрограммирование для упрощения сложности использования Immutable. Например, давайте напишем чистую функцию:
const addProducts = products => {
const cloneProducts = products.slice()
cloneProducts.push({ text: "shoes" })
return cloneProducts
}
Хоть код и не сложный, писать его все равно больно. мы должны поставитьproducts
сделайте копию и позвоните сноваpush
функция изменения новогоcloneProducts
, и вернуть его.
Если js изначально поддерживает Immutable, его можно использовать напрямую.push
! Да, иммумер делает js теперь поддерживает:
const addProducts = produce(products => {
products.push({ text: "shoes" })
})
Это весело, эти двоеaddProducts
Функции точно такие же, и все они чистые функции.
неудобный setState
Мы все знаем, что в среде реагированияsetState
Функциональное письмо поддерживается:
this.setState(state => ({
...state,
isShow: true
}))
В сочетании с синтаксисом деструктурирования это все еще так элегантно написано. А как насчет немного более сложных данных? Будем молча терпеть "плохой Immutable":
this.setState(state => {
const cloneProducts = state.products.slice()
cloneProducts.push({ text: "shoes" })
return {
...state,
cloneProducts
}
})
Однако с Immer все иначе:
this.setState(produce(state => (state.isShow = true)))
this.setState(produce(state => state.products.push({ text: "shoes" })))
Удобное каррирование
Выше описаны преимущества поддержки Immer для каррирования. Таким образом, мы также можем напрямую использовать два параметра одновременно:
const oldObj = { value: 1 }
const newObj = produce(oldObj, draft => (draft.value = 2))
Это Immer: создайте следующее неизменное состояние, изменив текущее.
3 Интенсивное чтение
Хотя автор уже проводил некоторые исследования в этой области, например, создание библиотеки Mutable to Immutable:dob-redux, но Immer действительно потрясающий, Immer — это головоломка более низкого уровня, его можно подключить к любой структуре потока данных в качестве функционального расширения, я должен восхищаться Mweststrate, действительно дальновидным.
Поэтому автор внимательно прочитал его исходный код и взял всех на понимание Immer с принципиальной точки зрения.
Иммер - это поддержка карри,Инструменты, которые поддерживают только синхронные вычисления, поэтому он очень подходит в качестве редуктора для редукса.
Immer также поддерживает прямое возвращаемое значение.Эта функция относительно проста, поэтому в этой статье вся обработка возвращаемого значения будет пропущена. PS: mutable и return не могут одновременно возвращать разные объекты, иначе непонятно, какая модификация действительна.
Каррирование здесь не будет раскрыто, подробнее см.curry. мы видимproduce
Часть обратного вызова функции:
produce(obj, draft => {
draft.count++
})
obj
является обычным объектом, что черная магия должна появиться вdraft
На объекте, погрузиться вdraft
Все свойства объекта контролируются.
Итак, общая идея такова:draft
даobj
агент, даdraft
Изменяемые модификации будут перетекать в кастомныеsetter
функция, она не изменяет значение исходного объекта, но рекурсивный родитель продолжает неглубокое копирование и, наконец, возвращает новый объект верхнего уровня, какproduce
Возвращаемое значение функции.
Создать прокси
Первый шаг —obj
Перевести вdraft
На этом шаге, чтобы повысить эффективность Immutable, нам нужна дополнительная информация, поэтому мыobj
Инкапсулирован в прокси-объект, содержащий дополнительную информацию:
{
modified, // 是否被修改过
finalized, // 是否已经完成(所有 setter 执行完,并且已经生成了 copy)
parent, // 父级对象
base, // 原始对象(也就是 obj)
copy, // base(也就是 obj)的浅拷贝,使用 Object.assign(Object.create(null), obj) 实现
proxies, // 存储每个 propertyKey 的代理对象,采用懒初始化策略
}
На этом прокси-объекте настраиваемыйgetter
setter
, а затем бросить его прямо вproduce
воплощать в жизнь.
getter
produce
Функция обратного вызова содержитmutable
код. Итак, теперь вход становитсяgetter
а такжеsetter
.
getter
Он в основном используется для ленивой инициализации прокси-объекта, то есть при доступе к подсвойству прокси-объекта будет создан его прокси-объект.
Итак, более абстрактно, например, исходный объект выглядит следующим образом:
{
a: {},
b: {},
c: {}
}
Итак, изначально,draft
даobj
прокси, поэтому доступdraft.a
draft.b
draft.c
может вызватьgetter
setter
, введите пользовательскую логику обработки. но даdraft.a.x
Нет возможности слушать, потому что прокси может слушать только один слой.
Ленивая инициализация прокси должна решить эту проблему, когда доступ кdraft.a
, обычайgetter
Новая цель была тихо сгенерированаdraft.a
прокси объектаdraftA
,следовательноdraft.a.x
эквивалентно посещениюdraftA.x
, поэтому он может рекурсивно отслеживать все свойства объекта.
В то же время, если код обращается только кdraft.a
, то он будет генерироваться только в памятиdraftA
играет роль,b
c
Поскольку доступ к свойствам не осуществляется, нет необходимости тратить ресурсы на создание прокси.draftB
draftC
.
Конечно, Immer сделал некоторые оптимизации производительности, и когда объект изменяется (modified
), чтобы получить егоcopy
объекта, чтобы обеспечитьbase
Он неизменен и не будет здесь раскрываться.
setter
когда правильноdraft
При изменении,base
То есть исходное значение неглубоко копируется и сохраняется вcopy
свойства, при этомmodified
свойство установлено наtrue
. Это завершает самый важный неизменяемый процесс, а неглубокая копия не очень требовательна к производительности, плюс это неглубокая копия по запросу, поэтому производительность Immer в порядке.
В то же время для того, чтобы объекты всей ссылки были новыми объектами,parent
Атрибут рекурсивно относится к родителю и продолжает неглубоко копироваться до тех пор, пока весь объект ссылки от конечного узла до корневого узла не будет заменен новым.
законченныйmodified
При изменении другого свойства объекта новое значение сохраняется вcopy
на объекте.
Создание неизменяемых объектов
при исполненииproduce
После внесения всех изменений пользователем (поэтому Immer не поддерживает асинхронность), еслиmodified
собственностьfalse
, указывая на то, что пользователь вообще не изменил объект, а затем вернуться к оригиналу напрямуюbase
характеристики.
еслиmodified
собственностьtrue
, указывая на то, что объект был изменен, вернутьcopy
характеристики. ноsetter
Процесс рекурсивный,draft
дочерние объектыdraft
(включает в себяbase
copy
modified
прокси для дополнительных свойств), мы должны выполнять рекурсию слой за слоем, чтобы получить реальное значение.
Итак, на данном этапе всеdraft
изfinalized
обеfalse
,copy
Может быть многоdraft
свойства, поэтому рекурсивныеbase
а такжеcopy
Податрибуты , если они одинаковые, возвращаются напрямую; если они разные, повторяют весь процесс один раз (начиная с первой строки этого подраздела).
Последний возвращаемый объект - этоbase
некоторые свойства (немодифицированных частей) иcopy
Некоторые свойства (модифицированные части) окончательно сшиты вместе. последнее использованиеfreeze
заморозитьcopy
свойства, воляfinalized
свойство установлено наtrue
.
В этот момент генерируется возвращаемое значение, и мы сохраняем окончательное значение вcopy
свойство и заморозить его, вернув значение Immutable.
Таким образом, Immer делает невероятное: создает следующее неизменное состояние, изменяя текущее.
Прочитав исходный код, я обнаружил, что Immer действительно может поддерживать асинхронность, если он поддерживает функцию product для возврата Promise. Самая большая проблема в том, что в итоге прокси
revoke
Для очистки требуется помощь глобальных переменных, что препятствует поддержке Immer асинхронности.
4 Резюме
Прочитав это, если вы чувствуете, что этого недостаточно, вы можете взглянутьredux-boxЭта библиотека использует immer + redux для решения проблемы избыточности редуктора.return
Эта проблема.
В то же время мы также начали думать и проектировать новый фреймворк потока данных, которым автор поделится на Салоне технологий Ctrip 24 марта 2018 года."Прекрасные лекции по интерфейсной платформе потока данных mvvm", чтобы поделиться опытом исследования различных наборов технологических решений для потоков данных, появившихся в последние годы Заинтересованные студенты могут зарегистрироваться.
еще 5 обсуждений
Адрес обсуждения:Интенсивное чтение исходного кода "Immer.js" · Выпуск №68 · dt-fe/weekly
Если вы хотите принять участие в обсуждении, пожалуйста,кликните сюда, с новыми темами каждую неделю, публикуется каждую пятницу.