Immutable小记
目录
React
React.js使用了virtual dom,通过diff修改dom,实现高效的dom更新。
但是有一个问题,当state更新时,如果数据没变,也会去做virtual dom的diff,这就埋下了性能隐患。
PureRenderMixin(React.PureComponent)
上面的问题可以通过使用 PureRenderMixin(React.PureComponent)解决。
import PureRenderMixin from 'react-addons-pure-render-mixin';
class FooComponent extends React.Component {
constructor(props) {
super(props);
this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
}
render() {
return <div className={this.props.className}>foo</div>;
}
}
该mixin实现了 shouldComponentUpdate 的细节进行优化。
PureRender的问题
它只是“浅比较”对象,如果是复杂的对象结构,那就出现了问题。
比如这样的结构:
{
"state": {
“some”: {
"time": 123456,
"thing": "gg fly",
}
}
}
如果直接修改对象上的属性
const some = this.state.some;
some.time = +new Date();
this.setState({ some });
则组件不会进行重新渲染
由于直接修改some上的time的值,但some仍然指向同一个对象。
经过PureRenderMixin的优化判断,不执行重新渲染,造成“卡死”的情况。
针对上面这种情况:
- 使用forceUpdate()更新;
- 使用不可变对象。
Immutable
mutable修改:
这里就是上面问题的关键,只是更改了对象上的属性,而没有更改引用。
const x = {
name: 'old name'
};
// 这里 x, y 指向同一个对象
const y = x;
y.name = 'new name';
console.log(x.name); // 'new name'
console.log(y.name); // 'new name'
console.log(x === y); // true
如何Immutable地修改呢?
深拷贝:
const x = {
name: 'old name'
};
// x, y 不指向同一个对象
const y = JSON.parse(JSON.stringify(x));
y.name = 'new name';
console.log(x.name); // 'old name'
console.log(y.name); // 'new name'
console.log(x === y); // false
深拷贝效率特别低,强烈不推荐。
Object.assign拷贝
按照修改路径,使用 Object.assign 拷贝对象,构造新对象。
const x = {
name: 'old name'
};
// x, y 不指向同一个对象
const y = Object.assign({}, x);
y.name = 'new name';
console.log(x.name); // 'old name'
console.log(y.name); // 'new name'
console.log(x === y); // false
这样就简单解决了问题,而且效率也比较高。
数组可以使用 Array.prototype.slice 进行拷贝。
Object.assign拷贝的麻烦
如果对象再“深”一些:
{
"state": {
“some”: {
"time": {
"good": 123456,
"bad": 78910,
},
"thing": "gg fly",
}
}
}
那我们可能要一遍遍拷贝对象:
const some = this.state.some;
const neoSome = Object.assign({}, some, {
"time": Object.assign({}, some.time, {
"good": +new Date()
})
});
this.setState({ some: neoSome });
为了简化操作 或 加强相关功能,我们可以使用不可变相关的库。
Immutable库
Facebook出品的 Immutable.js,比较复杂、重,真.重型解药
Immutability Helpers,提供了方便编写的语法糖
object-path-immutable,简单、轻便。
项目中我们使用的是 object-path-immutable 配合 Array.prototype.slice & Object.assign
object-path-immutable
使用
官方简单例子:
var obj = {
a: {
b: 'c',
c: ['d', 'f']
}
};
//set a deep property
var newObj = immutable.set(obj, 'a.b', 'f');
//returns
//var obj = {
// a: {
// b: 'f',
// c: ['d', 'f']
// }
//}
//obj !== newObj
//obj.a !== newObj.a
//obj.b !== newObj.b
//However:
//obj.c === newObj.c
链式操作:
//Chaining mode. value() at the end of the chain is used to retrieve the resulting object
var newObj = immutable(obj).set('a.b', 'f').del('a.c.0').value();
var neoState = immutable(obj);
// ⚠️ 注意
// 链式操作步骤如果分开,要把操作返回的新状态“串起来”
// 否则会断链
// ❌ 这样就“断链“了
neoState.set('a.b', 'f');
neoState.del('a.c.0');
// ✅ 下面是正确的
neoState = neoState.set('a.b', 'f');
neoState = neoState.del('a.c.0');
var newObj = neoState.value();
其他API,可查看文档按情况使用。