Immutable 意为「不可变的」。在编程领域,Immutable Data 是指一种一旦建立就不能更改的数据结构。它的理念是:在赋值时,产生一个与原对象彻底同样的新对象,指向不一样的内存地址,互不影响。javascript
当咱们须要对一个对象进行修改时,直接在原对象上进行变动很方便,也很节省内存。可是在 JavaScript 中,对象都是引用类型,在按引用传递数据的场景中,会存在多个变量指向同一个内存地址的状况,这样会引起不可控的反作用:java
let a = { x: 1 };
let b = a;
b.x = 6;
a.x // 6
复制代码
在一个复杂应用中,若是有多个代码块同时更改这个引用,就会产生竞态。你须要关心这个对象会在哪一个对方被修改,你对它的修改会不会影响其余代码的运行。使用 Immutable Data 就不会产生这个问题——由于每当状态更新时,都会产生一个新的对象:react
let a = { x: 1, y: 2 }; // 初始状态
a = { ...a, x: 6 }; // 建立了一个新的对象,更新了 a
复制代码
因为每次修改都会建立一个新对象,且对象不变,那么变动的记录就可以被保存下来,应用的状态变得可控、可追溯。Redux Dev Tool 和 Git 这两个可以实现「时间旅行」的工具就是秉承了 Immutable 的哲学。 git
简短答案:为了让 React 精准地重渲染 UI。github
自从 React.js 出现以来,社区中关于 Immutable 的讨论成倍增长,因此为何 React.js 须要 Immutable?编程
咱们知道,在 React 中,UI 是 state 的投影,state 的变动会引起 UI 的从新渲染。React 使用 Virtual DOM 来解决 UI 更新的问题——它会将新旧两棵 Virtual DOM 树进行比较,若是二者存在差别,则它会将这些差别来更新在真实的 DOM 上。redux
调用setState
时,React 会以 shallowMerge 的方式将咱们传入的对象与旧的 state 进行合并。shallowMerge 只会合并新旧 state 对象中第一层的内容,若是 state 中对象的引用未变,那么 React 认为这个对象先后没有发生变化。数据结构
因此若是咱们以 mutable 的方式更改了 state 中的某个对象, React 会认为该对象并无更新,那么相对应的 UI 就不会被重渲染。而以 Immutable 的方式更新 state 就不会出现这个问题。具体例子以下:工具
function App() {
const [list, setList] = useState([1, 2, 3]);
const handleFirstClick = () => {
list.push("new Item");
setList(list);
};
const handleSecondClick = () => {
setList([...list, "new item"]);
};
return (
<div className="App"> <button onClick={handleFirstClick}>Add an item in mutable way</button> <button onClick={handleSecondClick}>Add an item in immutable way</button> <ItemList list={list} /> </div> ); } function ItemList({ list }) { return ( <ul> {list.map(item => { return <li>{item}</li>; })} </ul> ); } 复制代码
咱们甚至能够看到,在屡次点击第一个按钮后,点击第二个按钮时,列表会新增以前第一个按钮产生的列表项。性能
至于如何解决屡次建立新对象以更新 state 带来的性能问题,以及如何优雅地以 Immutable 的形式更新一个复杂深层浅套的 state,这又是另一个话题了,社区中的不少 Immutable 库(如 Immutable.js)提供了很好的解决方案。
参考资料