我们平时写的对象赋给变量,都是共享堆内存,列入
let objA = { name: 'zfpx' };
let objB = objA;
objB.name = '9';
console.log(objA.name);
复制代码
什么是 Immutable
Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象 Immutable 实现的原理是 Persistent Data
Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变 同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗
Immutable 使用了 Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享
内部实现了一套完整的 Persistent Data Structure,还有很多易用的数据类型。像 Collection、List、Map、Set、Record、Seq
下面我们用一下这个的基本功能(记得先安装哦)
// // const obj1 = { user: { name: 'zfpx' } };
// // const obj2 = obj1;
// // obj2.user.name = 'zfpx2';
// // console.log(obj1.user.name);
let immutable = require('immutable');
let { Map, List ,is} = immutable;
// function Map(obj){
// obj.set(key,value){
// // return Object.assign(obj,{[key]: value})
// return {...obj,[key]: value}
// }
// return obj
// }
// let obj1 = Map({ name: 'zfpx', age: 9, home: { name: '北京', number: 0 } });
let obj1 = immutable.fromJS({ name: 'zfpx', age: 9, home: { name: '北京', number: 0 } });
let obj2 = obj1.set('name', 'zfpx2');
console.log(obj1, obj2);
console.log(obj1.home === obj2.home);
console.log(obj1.size);
console.log(obj1.count());
let obj3 = obj1.setIn(['home', 'name'], '山东');
console.log(obj3.get('name'), obj3.getIn(['home', 'name']));
let obj4 = obj3.update('age', val => val + 1);
console.log(obj4);
let obj5 = obj4.updateIn(['home', 'number'], val => val + 2);
console.log(obj5);
let obj6 = obj5.delete('name');
console.log(obj6);
let obj7 = obj5.clear();
console.log(obj7);
let obj8 = obj7.merge({ name: 'zfpx', age: 8 });
console.log(obj8);
console.log(obj8.toJS());
console.log(obj8.toJSON());
console.log(obj8.toObject());
console.log(...obj8.keys());
console.log(...obj8.values());
console.log(...obj8.entries());
let arr1 = List([1, 2, 3]);
let arr2 = arr1.push(4);
let arr3 = arr2.pop();
let arr4 = arr3.map(item => item * 2);
let arr5 = arr4.filter(item => item < 5);
console.log(arr1);
console.log(arr2);
console.log(arr3);
console.log(arr4);
console.log(arr5);
let arr6 = arr5.update(1, val => val + 1);
console.log(arr6);
let arr7 = arr6.delete(0);
console.log(arr7);
let arr8 = arr7.push(10);
let arr9 = arr8.last();
console.log(arr8);
console.log(arr9);
//[0,1,2,3,4,5,6,7,8,9,10]
console.log(immutable.Range(1, 6));//[1,2,3,4,5]
//4,5 -> 8 10 => 18
let ret = immutable.Range(1, 6).skip(3).map((n) => n * 2).filter((n) => n % 2 == 0).take(1).reduce((a, b) => a + b, 0);
console.log(ret);
let obj11 = Map({ name: 'zfpx', age: 9 });
let obj22 = Map({ name: 'zfpx', age: 9 });
// true immutable 的is比较的值
console.log(Object.is(obj11, obj22), is(obj11, obj22));
function handle() {
let obj = immutable.fromJS({ name: 'zfpx' });
invoke(obj);
console.log(obj.get('name'));//9
}
handle();
复制代码
imuutabel主要用于优化,如果state没有变化就不进新房刷新,那怎么做到的呢?主要是用啦PureComponent,PureComponent的实现原理其实是shouldComponentUpdate函数,因为setState内部实现也是靠shouldComponentUpdate(newState)来决定是否渲染,通常我们判断state是否更新,需要深拷贝,和深度对比,这样级占内存,也消耗CPU,严重影响性能,如下
index.js
import React, { Component} from 'react';
import PureComponent from './PureComponent';
import _ from 'lodash'; //深拷贝用
import ReactDOM from 'react-dom';
// import { Map } from 'immutable';
//组件的优化的原理是重写了shouldComponentUpdate,如果说老的状态对象和新的状态对象不是一个对象话才会刷新
/**
* 1. 每次一定都要生成新的对象 深克隆是非常消耗内存的。
*/
class Counter extends PureComponent {
state = {
counter: Map({ number: 0 })
}
handleClick = (event) => {
let state = _.cloneDeep(this.state);
// let state = {...this.state}; 不可用
let amount = this.amount.value ? Number(this.amount.value) : 0;
state.counter.number = state.counter.number + amount;
this.setState(state);
}
render() {
console.log('render');
return (
//<div>
//<p>{this.state.counter.number}</p>
//<input ref={input => this.amount = input} />
//<button onClick={this.handleClick}>+</button>
//</div>
)
}
}
// function setState(newState) {
// if (shouldComponentUpdate(newState)) {
// render();
// }
// }
//ReactDOM.render(<Counter />, document.querySelector('#root'));
复制代码
PureComponent.js
import React , {Component} from 'react';
import {is} from 'immutable';
export default class PureComponent extends Component{
shouldComponentUpdate(newProps, newState){
return !_.isEqual(nextState,this.state) //深度比较,深度克隆,占内存和cpu
return false;
}
}
复制代码
优化后 index
import React, { Component} from 'react';
import PureComponent from './PureComponent';
// import _ from 'lodash';
import ReactDOM from 'react-dom';
import {Map} from 'immutable'
// import { Map } from 'immutable';
//组件的优化的原理是重写了shouldComponentUpdate,如果说老的状态对象和新的状态对象不是一个对象话才会刷新
/**
* 1. 每次一定都要生成新的对象 深克隆是非常消耗内存的。
*/
class Counter extends PureComponent {
state = {
counter: Map({ number: 0 })
}
handleClick = (event) => {
// let state = _.cloneDeep(this.state);
// let state = {...this.state};
let amount = this.amount.value ? Number(this.amount.value) : 0;
let state = {...state , counter: this.state.counter.update('number',val =>val + amount)};
// state.counter.number = state.counter.number + amount;
// let state = { ...state, counter: this.state.counter.update('number', val => val + amount) }
this.setState(state);
}
render() {
console.log('render');
return (
//<div>
//<p>{this.state.counter.get('number')}</p>
//<input ref={input => this.amount = input} />
//<button onClick={this.handleClick}>+</button>
//</div>
)
}
}
// function setState(newState) {
// if (shouldComponentUpdate(newState)) {
// render();
// }
// }
//ReactDOM.render(<Counter />, document.querySelector('#root'));
复制代码
PureComponent
import React , {Component} from 'react';
import {is} from 'immutable';
import _ from 'lodash';
export default class PureComponent extends Component{
shouldComponentUpdate(newProps, newState){
// return !_.isEqual(nextState,this.state) //深度比较,深度克隆,占内存和cpu
let oldState = this.state || {};
newState = newState ? newState : {};
if (Object.keys(oldState).length !== Object.keys(newState).length) {
return true;
}
for (let key in newState) {
if (!is(newState[key], oldState[key])) {
return true;
}
}
return false;
}
}
复制代码
配合redux
componet/counter.js
import React,{Component} from 'react';
import { connect } from 'react-redux';
class Counter extends Component{
render(){
return(
<div>
<p>{this.props.number}</p>
<input ref={input => this.amount = input} />
<button onClick={() => this.props.add(1)}>+</button>
</div>
)
}
}
//actions
let actions = {
add(payload){
return {type : 'ADD', payload}
}
}
export default connect(
state => ({ number: state.getIn(['counter','number'])}),
//本身这里也应该是一个immutable对象,但是这里不是,不太合理
actions
)(Counter)
复制代码
store/index.js
import {createStore,applyMiddleware} from 'redux';
import createSagaMiddleware from '../redux-saga';
import {rootSaga} from '../saga'
import {Map} from 'immutable';
// import {combineReducers} from 'react-immutable'
let sagaMiddleware = createSagaMiddleware();
//创建个immutable对象
let initState = Map({ number: 0 });
//设定reducers,需要传入状态,和action,对action做处理
function counter(state = initState , action){
switch (action.type) {
case 'ADD':
return state.update('number', val => val + action.payload);
default:
return state;
}
}
//combineReducers方法的实现
function combineReducers(reducers){
return function(state= Map({}),action){
let newState = Map({});
for(let key in reducers){
newState = newState.set(key, reducers[key](state.get(key),action))
}
return newState;
}
}
//合并reducer
let reducer = combineReducers({
counter
})
//通过reducer创建仓库
let store = createStore(reducer);
export default store
复制代码
src/index.js
import React, { Component} from 'react';
import PureComponent from './PureComponent';
// import _ from 'lodash';
import ReactDOM from 'react-dom';
import {Map} from 'immutable'
// import { Map } from 'immutable';
//组件的优化的原理是重写了shouldComponentUpdate,如果说老的状态对象和新的状态对象不是一个对象话才会刷新
/**
* 1. 每次一定都要生成新的对象 深克隆是非常消耗内存的。
*/
class Counter extends PureComponent {
state = {
counter: Map({ number: 0 })
}
handleClick = (event) => {
// let state = _.cloneDeep(this.state);
// let state = {...this.state};
let amount = this.amount.value ? Number(this.amount.value) : 0;
let state = {...state , counter: this.state.counter.update('number',val =>val + amount)};
// state.counter.number = state.counter.number + amount;
// let state = { ...state, counter: this.state.counter.update('number', val => val + amount) }
this.setState(state);
}
render() {
console.log('render');
return (
<div>
<p>{this.state.counter.get('number')}</p>
<input ref={input => this.amount = input} />
<button onClick={this.handleClick}>+</button>
</div>
)
}
}
// function setState(newState) {
// if (shouldComponentUpdate(newState)) {
// render();
// }
// }
ReactDOM.render(<Counter />, document.querySelector('#root'));
复制代码
immutabel结合react-roter
目的是为了实现通过派发发动作创建路由
src/index.js
import React, { Component } from "react";
import ReactDOM from "react-dom";
import { createStore, combineReducers, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import createHistory from "history/createBrowserHistory";
import { Router, Route } from "react-router";
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
// import {
// //ConnectedRouter,
// //routerReducer,
// //routerMiddleware,
// //push
// } from "react-router-redux";
const LOCATION_CHANGE = 'LOCATION_CHANGE';
class ConnectedRouter extends Component {
static contextTypes = {
store: PropTypes.object
}
//连接仓库和路由,监听路由变化,当路径发生变化的时候派发动作
componentWillMount() {
this.store = this.context.store;
//window.addEventListener('hashchange',()=>{})
//window.addEventListener('pushState',()=>{})
//history是一个封装,让hashhistory 和browerhistory使用相同的api
this.props.history.listen(location => {
this.store.dispatch({
type: LOCATION_CHANGE,
location
});
});
}
render() {
return <Router {...this.props} />
}
}
let initRouterState = { location: {} };
function routerReducer(state = initRouterState, action) {
switch (action.type) {
case LOCATION_CHANGE:
return { ...state, location: action.location };
default:
return state;
}
return state;
}
const CHANGE_LOCATION = 'CHANGE_LOCATION';
function push(pathname) {
return {
type: CHANGE_LOCATION,
pathname
}
}
function routerMiddleware(history) {
return function ({ getState, dispatch }) {
return function (next) {
return function (action) {
if (action.type == CHANGE_LOCATION) {
history.push(action.pathname);
} else {
next(action);
}
}
}
}
}
// Create a history of your choosing (we're using a browser history in this case)
const history = createHistory();
// Build the middleware for intercepting and dispatching navigation actions
//构建一个中间件用来解释和派发发哦行的动作
const middleware = routerMiddleware(history);
// Add the reducer to your store on the `router` key
// Also apply our middleware for navigating
const store = createStore(
combineReducers({
router: routerReducer
}),
applyMiddleware(middleware)
);
let Home = () => <div>Home</div>
let About = () => <div>About</div>
let Topics = () => <div>Topics</div>
// Now you can dispatch navigation actions from anywhere!
// store.dispatch(push('/foo'))
window.store = store; //将store暴漏在全局
window.push = push;
ReactDOM.render(
<Provider store={store}>
{/* ConnectedRouter will use the store from Provider automatically */}
<ConnectedRouter history={history}>
<div>
<Link to="/about">about</Link>
<Link to="/topics">topics</Link>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/topics" component={Topics} />
</div>
</ConnectedRouter>
</Provider>,
document.getElementById("root")
);
复制代码