Immutable kann React-Anwendungen um ein Vielfaches verbessern. Einige Leute sagen, dass die Einführung von Immutable in jüngster Zeit eine großartige Erfindung in JavaScript war, weil React gleichzeitig so beliebt war, dass sein Licht in den Hintergrund geriet. In diesem Artikel wird hauptsächlich die Praxis von Immutable und React vorgestellt. Freunde, die es benötigen, können darauf verweisen.
Einige Leute sagen, dass Immutable Dutzende Male Verbesserungen für React-Anwendungen bringen kann, und andere sagen, dass die Einführung von Immutable dies ist Der neueste Schritt in JavaScript. Eine großartige Erfindung, da React gleichzeitig so beliebt war, dass sein Licht im Dunkeln lag. Diese zeigen zumindest, dass Immutable sehr wertvoll ist, das wollen wir weiter unten herausfinden.
Objekte in JavaScript sind im Allgemeinen veränderbar (veränderbar), da das neue Objekt einfach auf das ursprüngliche Objekt verweist. Beispielsweise foo={a: 1}; bar=foo; bar.a=2
werden Sie feststellen, dass foo.a zu diesem Zeitpunkt ebenfalls in 2 geändert wurde. Dies kann zwar Speicher sparen, birgt jedoch bei komplexeren Anwendungen sehr große versteckte Gefahren, und die Vorteile von Mutable sind den Gewinn mehr als wert. Um dieses Problem zu lösen, besteht der allgemeine Ansatz darin, flache Kopien (flache Kopie) oder tiefe Kopien (tiefe Kopie) zu verwenden, um Änderungen zu vermeiden. Dies führt jedoch zu einer Verschwendung von CPU und Speicher.
Immutable kann diese Probleme sehr gut lösen.
Was sind unveränderliche Daten?
Unveränderliche Daten sind Daten, die nach ihrer Erstellung nicht mehr geändert werden können. Jede Änderung, Hinzufügung oder Löschung eines unveränderlichen Objekts gibt ein neues unveränderliches Objekt zurück. Das Prinzip der unveränderlichen Implementierung ist die persistente Datenstruktur (persistente Datenstruktur). Das heißt, wenn alte Daten zum Erstellen neuer Daten verwendet werden, muss sichergestellt werden, dass die alten Daten gleichzeitig verfügbar und unverändert sind. Um gleichzeitig den Leistungsverlust zu vermeiden, der durch das Kopieren aller Knoten durch DeepCopy verursacht wird, verwendet Immutable Structural Sharing (strukturelle gemeinsame Nutzung). Wenn sich also ein Knoten im Objektbaum ändert, sind nur dieser Knoten und der übergeordnete Knoten davon betroffen werden geändert, und andere Knoten werden dann geändert. Schauen Sie sich bitte die Animation unten an:
Sehr geehrte Damen und Herren, ich habe mir viel Mühe gegeben, eine GIF-Animation zu erstellen, aber sie musste zwangsweise in ein statisches Bild umgewandelt werden. Bitte gehen Sie zu http://img. alicdn.com/tps/i2/ TB1zzi_KXXXXXctXFXXbrb8OVXX-613-575.gif Ansehen
Es gibt derzeit zwei beliebte unveränderliche Bibliotheken:
1.immutable.js
Lee Byron, Facebook-Ingenieur Die Erstellung dauerte drei Jahre und erschien gleichzeitig mit React, war jedoch nicht standardmäßig im React-Toolset enthalten (React bietet einen vereinfachten Helfer). Es implementiert intern einen vollständigen Satz persistenter Datenstrukturen sowie viele benutzerfreundliche Datentypen. Wie Sammlung, Liste, Karte, Satz, Datensatz, Seq. Es gibt sehr umfassende Karten-, Filter-, GroupBy-, Reduzier- und Funktionsoperationsmethoden. Gleichzeitig ist die API dem Objekt oder Array möglichst ähnlich.
Es gibt drei wichtigste Datenstrukturen: (Java-Programmierer sollten am vertrautesten sein)
Map: Eine Sammlung von Schlüssel-Wert-Paaren, entsprechend Object. ES6 verfügt auch über ein spezielles Map-Objekt
Liste: eine geordnete und wiederholbare Liste, entsprechend Array
Menge: eine ungeordnete und nicht wiederholbare Liste
2.seamless-immutable
Anders Ausgehend vom akademischen Stil von Immutable.js implementiert seamless-immutable
keine vollständige persistente Datenstruktur, sondern verwendet Object.defineProperty (kann daher nur in IE9 und höher verwendet werden), um die Array- und Objektobjekte von JavaScript zu erweitern. Unterstützt zwei Datentypen : Array und Object, und die API basiert auf denselben Operationen wie Array und Object. Die Codebasis ist sehr klein, nur 2 KB, wenn sie komprimiert heruntergeladen wird. Immutable.js ist 16 KB komprimiert.
Geben Sie den Code unten ein, um den Unterschied zwischen den beiden zu spüren:
// 原来的写法 let foo = {a: {b: 1}}; let bar = foo; bar.a.b = 2; console.log(foo.a.b); // 打印 2 console.log(foo === bar); // 打印 true // 使用 immutable.js 后 import Immutable from ‘immutable'; foo = Immutable.fromJS({a: {b: 1}}); bar = foo.setIn([‘a', ‘b'], 2); // 使用 setIn 赋值 console.log(foo.getIn([‘a', ‘b'])); // 使用 getIn 取值,打印 1 console.log(foo === bar); // 打印 false // 使用 seamless-immutable.js 后 import SImmutable from ‘seamless-immutable'; foo = SImmutable({a: {b: 1}}) bar = foo.merge({a: { b: 2}}) // 使用 merge 赋值 console.log(foo.a.b); // 像原生 Object 一样取值,打印 1 console.log(foo === bar); // 打印 false
Immutable Advantages
1. Immutable reduziert die Komplexität, die Mutable mit sich bringt.
Variable (veränderliche) Daten koppeln die Konzepte von Zeit und Wert, was es schwierig macht, die Daten zurückzuverfolgen.
Zum Beispiel der folgende Code:
function touchAndLog(touchFn) { let data = { key: ‘value' }; touchFn(data); console.log(data.key); // 猜猜会打印什么? }
Ohne auf den Code von touchFn zu schauen, weil ich nicht sicher bin, was er tut zu Daten, Es ist unmöglich zu wissen, was gedruckt wird (ist das nicht Blödsinn)? Wenn die Daten jedoch unveränderlich sind, können Sie sicher sein, dass der Wert gedruckt wird.
2. Speicher sparen
Immutable.js verwendet Structure Sharing und wird versuchen, Speicher wiederzuverwenden. Objekte, auf die nicht verwiesen wird, werden durch Garbage Collection erfasst.
import { Map} from ‘immutable'; let a = Map({ select: ‘users', filter: Map({ name: ‘Cam' }) }) let b = a.set(‘select', ‘people'); a === b; // false a.get(‘filter') === b.get(‘filter'); // true
A und b oben teilen sich den unveränderten „Filter“-Knoten.
3. Rückgängig machen/Wiederholen, Kopieren/Einfügen und sogar Zeitreisen sind ein Kinderspiel
Denn jedes Mal, wenn die Daten Der Unterschied besteht darin, dass Sie die Daten nur in einem Array speichern und die entsprechenden Daten überall dort herausnehmen müssen, wo Sie zurückkehren möchten. Die Funktion zum Rückgängigmachen und Wiederherstellen ist einfach zu entwickeln.
Ich werde später ein Beispiel für die Verwendung von Flux zum Rückgängigmachen geben.
4. Parallelitätssicherheit
传统的并发非常难做,因为要处理各种数据不一致问题,因此『聪明人』发明了各种锁来解决。但使用了 Immutable 之后,数据天生是不可变的,并发锁就不需要了。
然而现在并没什么卵用,因为 JavaScript 还是单线程运行的啊。但未来可能会加入,提前解决未来的问题不也挺好吗?
拥抱函数式编程
Immutable 本身就是函数式编程中的概念,纯函数式编程比面向对象更适用于前端开发。因为只要输入一致,输出必然一致,这样开发的组件更易于调试和组装。
像 ClojureScript,Elm 等函数式编程语言中的数据类型天生都是 Immutable 的,这也是为什么 ClojureScript 基于 React 的框架 — Om 性能比 React 还要好的原因。
使用 Immutable 的缺点
需要学习新的 API
No Comments
增加了资源文件大小
No Comments
容易与原生对象混淆
这点是我们使用 Immutable.js 过程中遇到最大的问题。写代码要做思维上的转变。
虽然 Immutable.js 尽量尝试把 API 设计的原生对象类似,有的时候还是很难区别到底是 Immutable 对象还是原生对象,容易混淆操作。
Immutable 中的 Map 和 List 虽对应原生 Object 和 Array,但操作非常不同,比如你要用 map.get(‘key') 而不是 map.key,array.get(0) 而不是 array[0]。另外 Immutable 每次修改都会返回新对象,也很容易忘记赋值。
当使用外部库的时候,一般需要使用原生对象,也很容易忘记转换。
下面给出一些办法来避免类似问题发生:
使用 Flow 或 TypeScript 这类有静态类型检查的工具
约定变量命名规则:如所有 Immutable 类型对象以 $$ 开头。
使用 Immutable.fromJS 而不是 Immutable.Map 或 Immutable.List 来创建对象,这样可以避免 Immutable 和原生对象间的混用。
更多认识
1 . Immutable.is
两个 immutable 对象可以使用 === 来比较,这样是直接比较内存地址,性能最好。但即使两个对象的值是一样的,也会返回 false:
let map1 = Immutable.Map({a:1, b:1, c:1}); let map2 = Immutable.Map({a:1, b:1, c:1}); map1 === map2; // false
为了直接比较对象的值,immutable.js 提供了 Immutable.is 来做『值比较』,结果如下:
Immutable.is(map1, map2); // true
Immutable.is 比较的是两个对象的 hashCode 或 valueOf(对于 JavaScript 对象)。由于 immutable 内部使用了 Trie 数据结构来存储,只要两个对象的 hashCode 相等,值就是一样的。这样的算法避免了深度遍历比较,性能非常好。
后面会使用 Immutable.is 来减少 React 重复渲染,提高性能。
另外,还有 mori、cortex 等,因为类似就不再介绍。
2 . 与 Object.freeze、const 区别
Object.freeze 和 ES6 中新加入的 const 都可以达到防止对象被篡改的功能,但它们是 shallowCopy 的。对象层级一深就要特殊处理了。
3 . Cursor 的概念
这个 Cursor 和数据库中的游标是完全不同的概念。
由于 Immutable 数据一般嵌套非常深,为了便于访问深层数据,Cursor 提供了可以直接访问这个深层数据的引用。
import Immutable from ‘immutable'; import Cursor from ‘immutable/contrib/cursor'; let data = Immutable.fromJS({ a: { b: { c: 1 } } }); // 让 cursor 指向 { c: 1 } let cursor = Cursor.from(data, [‘a', ‘b'], newData => { // 当 cursor 或其子 cursor 执行 update 时调用 console.log(newData); }); cursor.get(‘c'); // 1 cursor = cursor.update(‘c', x => x + 1); cursor.get(‘c'); // 2
实践
与 React 搭配使用,Pure Render
熟悉 React 的都知道,React 做性能优化时有一个避免重复渲染的大招,就是使用 shouldComponentUpdate(),但它默认返回 true,即始终会执行 render() 方法,然后做 Virtual DOM 比较,并得出是否需要做真实 DOM 更新,这里往往会带来很多无必要的渲染并成为性能瓶颈。
当然我们也可以在 shouldComponentUpdate()
中使用使用 deepCopy 和 deepCompare 来避免无必要的 render(),但 deepCopy 和 deepCompare 一般都是非常耗性能的。
Immutable 则提供了简洁高效的判断数据是否变化的方法,只需 === 和 is 比较就能知道是否需要执行 render(),而这个操作几乎 0 成本,所以可以极大提高性能。修改后的 shouldComponentUpdate
是这样的:
import { is } from ‘immutable'; shouldComponentUpdate: (nextProps = {}, nextState = {}) => { const thisProps = this.props || {}, thisState = this.state || {}; if (Object.keys(thisProps).length !== Object.keys(nextProps).length || Object.keys(thisState).length !== Object.keys(nextState).length) { return true; } for (const key in nextProps) { if (thisProps[key] !== nextProps[key] || !is(thisProps[key], nextProps[key])) { return true; } } for (const key in nextState) { if (thisState[key] !== nextState[key] || !is(thisState[key], nextState[key])) { return true; } } return false; }
使用 Immutable 后,如下图,当红色节点的 state 变化后,不会再渲染树中的所有节点,而是只渲染图中绿色的部分:
你也可以借助 React.addons.PureRenderMixin
或支持 class 语法的pure-render-decorator
来实现。
setState 的一个技巧
React 建议把 this.state 当作 Immutable 的,因此修改前需要做一个 deepCopy,显得麻烦:
import ‘_' from ‘lodash'; const Component = React.createClass({ getInitialState() { return { data: { times: 0 } } }, handleAdd() { let data = _.cloneDeep(this.state.data); data.times = data.times + 1; this.setState({ data: data }); // 如果上面不做 cloneDeep,下面打印的结果会是已经加 1 后的值。 console.log(this.state.data.times); } }
使用 Immutable 后:
getInitialState() { return { data: Map({ times: 0 }) } }, handleAdd() { this.setState({ data: this.state.data.update(‘times', v => v + 1) }); // 这时的 times 并不会改变 console.log(this.state.data.get(‘times')); }
上面的 `handleAdd` 可以简写成:
handleAdd() { this.setState(({data}) => ({ data: data.update(‘times', v => v + 1) }) }); }
2 . 与 Flux 搭配使用
由于 Flux 并没有限定 Store 中数据的类型,使用 Immutable 非常简单。
现在是实现一个类似带有添加和撤销功能的 Store:
import { Map, OrderedMap } from ‘immutable'; let todos = OrderedMap(); let history = []; // 普通数组,存放每次操作后产生的数据 let TodoStore = createStore({ getAll() { return todos; } }); Dispatcher.register(action => { if (action.actionType === ‘create') { let id = createGUID(); history.push(todos); // 记录当前操作前的数据,便于撤销 todos = todos.set(id, Map({ id: id, complete: false, text: action.text.trim() })); TodoStore.emitChange(); } else if (action.actionType === ‘undo') { // 这里是撤销功能实现, // 只需从 history 数组中取前一次 todos 即可 if (history.length > 0) { todos = history.pop(); } TodoStore.emitChange(); } });
3 . 与 Redux 搭配使用
Redux 是目前流行的 Flux 衍生库。它简化了 Flux 中多个 Store 的概念,只有一个 Store,数据操作通过 Reducer 中实现;同时它提供更简洁和清晰的单向数据流(View -> Action -> Middleware -> Reducer),也更易于开发同构应用。目前已经在我们项目中大规模使用。
由于 Redux 中内置的 combineReducers 和 reducer 中的 initialState 都为原生的 Object 对象,所以不能和 Immutable 原生搭配使用。
幸运的是,Redux 并不排斥使用 Immutable,可以自己重写 combineReducers 或使用 redux-immutablejs 来提供支持。
上面我们提到 Cursor 可以方便检索和 update 层级比较深的数据,但因为 Redux 中已经有了 select 来做检索,Action 来更新数据,因此 Cursor 在这里就没有用武之地了。
总结
Immutable 可以给应用带来极大的性能提升,但是否使用还要看项目情况。由于侵入性较强,新项目引入比较容易,老项目迁移需要评估迁移。对于一些提供给外部使用的公共组件,最好不要把 Immutable 对象直接暴露在对外接口中。
如果 JS 原生 Immutable 类型会不会太美,被称为 React API 终结者的 Sebastian Markbåge 有一个这样的提案,能否通过现在还不确定。不过可以肯定的是 Immutable 会被越来越多的项目使用。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
Javascript中prototype与__proto__的关系详解
js中document.write和document.writeln的区别
Das obige ist der detaillierte Inhalt vonErklären Sie ausführlich die praktischen Fähigkeiten in Immutable und React. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!