我们平时写的对象赋给变量,都是共享堆内存,列入
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 => 18let 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 ( ////) }}// function setState(newState) { // if (shouldComponentUpdate(newState)) { // render();// }// }//ReactDOM.render({this.state.counter.number}
// this.amount = input} /> // //, 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 ( ////) }}// function setState(newState) { // if (shouldComponentUpdate(newState)) { // render();// }// }//ReactDOM.render({this.state.counter.get('number')}
// this.amount = input} /> // //, 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() }}//actionslet actions = { add(payload){ return {type : 'ADD', payload} }}export default connect( state => ({ number: state.getIn(['counter','number'])}), //本身这里也应该是一个immutable对象,但是这里不是,不太合理 actions)(Counter)复制代码{this.props.number}
this.amount = input} />
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; }}//合并reducerlet 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 () }}// function setState(newState) {// if (shouldComponentUpdate(newState)) {// render();// }// }ReactDOM.render({this.state.counter.get('number')}
this.amount = input} />, 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}}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 navigatingconst store = createStore( combineReducers({ router: routerReducer }), applyMiddleware(middleware));let Home = () => Homelet About = () =>Aboutlet Topics = () =>Topics// Now you can dispatch navigation actions from anywhere!// store.dispatch(push('/foo'))window.store = store; //将store暴漏在全局window.push = push;ReactDOM.render({/* ConnectedRouter will use the store from Provider automatically */} , document.getElementById("root"));复制代码 about topics