immutable
一、immutabl
1.1、Immutable.js介绍
更多详情请参考 immutable官方文档
还可以参考 这一篇博客
总的来说,immutable就是用来解决内存浪费的问题。
Immutable
( 不可改变的 ) Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。
Immutable
实现的原理是 Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变
。同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable 使用了Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享
。
1.2、Immutable优缺点
-
优点
减少内存的使用(深拷贝消耗内存)
并发安全
降低项目的复杂度
-
缺点
库的大小(建议使用seamless-immutable)
对现有项目入侵严重
容易与原生的对象进行混淆
1.3、Immutable 中常用类型(Map,List)
1.3.1、Map
作用:复制一个对象(键值对)
参数:json对象
返回值:Map对象(经过immutable包装了的Map对象) ,该Map对象也可以使用set,get方法。但是set方法调用后会产生一个新的Map对象。
Immutable.Map(json对象);
示例:
npm i --save immutable
function f(){
//定义一个对象obj1
var obj1 = {
id:"007",
name:"张三疯",
address:{
province:"陕西省",
city:"西安市"
}
};
//浅复制:let obj2 = obj1; 两个对象会共享同一块内存区域
//深拷贝:两个对象的内存是独立的,
//使用Immutable.Map()进行复制,相同的数据会共享。
let obj2 = Immutable.map(obj1).toJS();
//修改数据时,只把改动数据及其父级数据部分进行复制,其它部分不做复制,依然共享,节约了内存。
obj1.address.province="北京";
console.log(obj1);//obj1.address.province是北京
console.log(obj2);//obj2.address.province是陕西
}
Map对象的set函数会产生一个新的对象
示例:
function f(){
var obj1 = {
id:"007",
name:"张三疯"
};
let obj2 = Immutable.Map(obj1);
let obj3 = obj2.set("name","李思峰");
let obj4 = obj2.set("sex","男");
console.log(obj2.toJS()); //{id: "007", name: "张三疯"}
console.log(obj3.toJS()); //{id: "007", name: "李思峰"}
console.log(obj4.toJS()); //{id: "007", name: "张三疯", sex: "男"}
}
1.3.2、List
可重复的有序列表,对应Array
作用:复制一个数组
参数:数组
返回值:List。
是有序的索引密集集合,类似于JavaScript数组,针对List类型有set和get方法,分别表示设置和获取
function f(){
const persons = ['芙蓉姐姐','春哥','犀利哥']
let ipersons = Immutable.List(persons);
let ipersons2 = ipersons.set( 1, '干露露');
console.log("ipersons",ipersons);//List对象
console.log("ipersons.toJS()",ipersons.toJS())
console.log("ipersons2",ipersons2);//List对象
console.log("ipersons2.toJS()",ipersons2.toJS())
}
1.4、Immutable 中常用方法
(1) Immutable.fromJs(),
//把一个js对象(数组)转化为Immutable对象。
let obj = Immutable.fromJS({ a: 1,b: 1,c: 1 });
console.log(obj);
let arr = Immutable.fromJS(['芙蓉姐姐','春哥','犀利哥']);
console.log(arr);
(2) immutable对象.toJS(),
//把一个Immutable对象转化为js对象(数组)。
let o = obj.toJS();
console.log(o);
let arr2 = arr.toJS();
console.log(arr2);
(3) Immutable.is():
//比较两个Map(或List)的值是否相等
var map1 = Immutable.Map({ a: 1,b: 1,c: 1 });
var map2 = Immutable.Map({ a: 1,b: 1,c: 1 });
console.log(Immutable.is(map1, map2));
1.5、Immutable + Redux 的开发方式
redux中的state也是用immutable的方式,就需要使用redux-immutable提供的combineReducers
来代替。
//创建仓库
import {combineReducers} from 'redux-immutable';
import { createStore } from "redux";
import reducer from "../store/reducer";
let rootReducers = combineReducers({reducer});
let store = createStore(rootReducers);
export default store;
reducers中的state也需要使用immutable
//reducer
// 初始化reducer中的initialState时建议用List方法而不是fromJS方法,效率更高
import Immutable from "immutable";
let initState=Immutable.List([
{
text:"吃饭",
isComplete:false
},
{
text:"睡觉",
isComplete:true
}
])
export default (state=initState,action)=>{
//let state1 = state.toJS();
let {type,payload} = action;
switch(type){
case "ADDTODO":{
let obj = {
text:payload,
isComplete:false
}
return Immutable.set(state.size,obj]);
}
default:return state;
}
}
组件里面,获取到数据后,就需要使用toJS()进行转换。或者使用immutable的函数进行操作。
组件:
class App extends React.Component {
constructor(props){
super(props);
console.log("store.getState()",store.getState().toJS().reducer);
this.state ={
todos:store.getState().toJS().reducer,
text:""
}
}
componentDidMount(){
store.subscribe(()=>{
this.setState({
todos:store.getState().toJS().reducer
})
});
}
…………………………
二、mobx
2.1、介绍
类似于redux的一套数据流方案。
Flux思想单向数据流方案,以 Redux 为代表;
Reactive响应式数据流方案,以 Mobx 为代表;
-
单向数据流实现:redux + react-redux + react-thunk
-
响应式数据流实现:mobx + mobx-react
MobX 是通过观察者模式对数据进行追踪处理,在对可观察属性作出变更或者引用的时候,触发其依赖的监听函数,整体的store注入机制采用react提供的context来进行传递
2.2、装饰器Decorator
是个函数,用来装饰类或者类成员 ,是Object.defineProperty的语法糖
//给对象添加或修改属性
Object.defineProperty(target, prop, desc)
//target 需要定义属性的当前对象
//prop 当前需要定义的属性名 类型:字符
desc | 默认值 | 说明 |
---|---|---|
configurable | false | 描述属性是否可以被删除,默认为 false |
enumerable | false | 描述属性是否可以被for…in或Object.keys枚举,默认为 false |
writable | false | 描述属性是否可以修改,默认为 false |
get | undefined | 当访问属性时触发该方法,默认为undefined |
set | undefined | 当属性被修改时触发该方法,默认为undefined |
value | undefined | 属性值,默认为undefined |
//定义装饰器(以下代码会被转成Object.defineProperty的写法)
function 装饰器名 (target,prop,descriptor){
descriptor.writable=false;//writable属性是否可以写入
}
//使用装饰器
@装饰器名 类
@装饰器名 实例属性|静态属性(类的属性)
@装饰器名 实例方法|静态方法(类的方式)
//使用场景
mobx / angluarTs / vueTs / reactTs / java ...
配置
create-react-app脚手架 不支持装饰器语法,需要配置
yarn add @babel/plugin-proposal-decorators --save
package.json
babel: {
"presets":...
+
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
]
....
}
vscode编辑配置
vscode->文件->首选项-->设置->搜索:experimentalDecorators->勾上
//webstrom 无需设置
示例代码:
定义一个只读装饰器(其实就是个函数),设定属性是只读的。
function readonly(target,prop,desc) {
desc.writable = false;
}
class Dog{
//使用装饰器,修饰dark属性为只读的
@readonly dark="wangwang";
}
let d = new Dog();
d.dark="mie"; //这个是不能修改成功的
console.log(d.dark);
2.3、mobx成员
observable
装饰类和其成员
@observable 装饰store类的成员,让store中的成员成为被观察者,即:在组件里可以订阅该数据的变化。
Mbox的思想是:把state和reducer都放在一个类中。state就是数据,reducer就是方法。
import { observable } from 'mobx';
//1)、定义状态并使其可观察
//state:(在对象外面套上observable()函数,就可以,让对象里的数据可以被订阅)
let appStore = observable({
count:0
});
appStore.increment = function () {
console.log("increment");
appStore.count += 1;
}
export default appStore;
2.4、mobx-react成员
Provider
inject
observer
Provider,顶层提供appStore的服务
<Provider appStore={appStore}>
</Provider>
inject:在组件里使用仓库对象(appStore)。 Provider和inject结合起来,就可以让顶层提供的仓库对象(appStore)在组件里使用
@inject('appStore')
class Index extend React.Component{
render(){
<div>
{this.props.appStore.属性名}
</div>
}
}
observer:能够监听到数据的变化,并响应到组件里。即:在组件里订阅了store中的数据,当store中的数据发生变化时,就会发布给组件。
@observer
class Index extend React.Component{
render(){
<div>
{this.props.appStore.属性名}
</div>
}
}
2.5、Mobx 的使用
1、安装:
npm install mobx --save 或者 yarn add mobx
2、创建仓库
文件名: ./mobxstore/index.js
import { observable } from 'mobx';
//1)、定义状态并使其可观察
//state:(在对象外面套上observable()函数,就可以,让对象里的数据可以被订阅)
const appStore = observable({
count: 0
});
appStore.increment = function () {
console.log("increment");
appStore.count += 1;
}
export default appStore;
3、入口文件:
在顶层提供 仓库对象(appStore)
//index.js
import appStore from './mobxstore';
import { Provider } from "mobx-react";
ReactDOM.render(
<BrowserRouter>
<Provider appStore = {appStore}>
<Route path="/" component={App} />
</Provider>
</BrowserRouter>,
document.getElementById('root')
);
4、使用数据的组件
需要使用 observer,inject
import { Component } from "react";
import {observer,inject} from "mobx-react";
@inject('appStore') // 使用appStore对象,对应着顶层组件的Provider提供的appStore
@observer //监听appStore中数据的变化,即:只要appStore中的数据发生变化,就会重新渲染该组件
class Index extends Component {
//这个不能用箭头函数
render(){
return(
<div>
count:{this.props.appStore.count}
</div>
)
}
}
export default Index;
5、修改数据的组件:
需要使用 inject
import {observer,inject} from "mobx-react";
@inject('appStore') // 使用appStore对象,对应着顶层组件的Provider提供的appStore
export default class Layout extends Component {
incCount(){
this.props.appStore.increment();
}
render = () => (
<div className="box">
<input type="button" value=" 加 " onClick={this.incCount.bind(this)} />
</div>
)
}
注意:如果提示以下内容:
Parsing error: Using the export keyword between a decorator and a class is not allowed. Please use `export @dec class` instead.
请安装:
npm install --save-dev babel-plugin-transform-decorators-legacy