react开发简书用到技术有路由、react-redux、immutable库,样式采用styled-components,在App中的代码如下:
import React from ‘react‘;
import { GlobalStyle } from ‘./styled‘;
import { Font } from ‘./static/iconfont/iconfont‘
import Header from ‘./common/header‘
import { Provider } from ‘react-redux‘
import { BrowserRouter,Route } from ‘react-router-dom‘
import Home from ‘./pages/home‘
import Detail from ‘./pages/detail/loadable‘
import Login from ‘./pages/login‘
import Write from ‘./pages/write‘
import store from ‘./store‘
function App() {
return (
<div>
<Provider store = {store}>
<BrowserRouter>
<div>
<Header ></Header>
<Route path = ‘/‘ exact component={Home}></Route>
<Route path = ‘/login‘ exact component={Login}></Route>
<Route path = ‘/write‘ exact component={Write}></Route>
<Route path = ‘/detail/:id‘ exact component={Detail}></Route>
</div>
</BrowserRouter>
</Provider>
<Font></Font>
<GlobalStyle></GlobalStyle>
</div>
);
}
export default App;
Provider将页面全部包裹起来,引入redux建立的store,在store文件夹中有index.js文件和reducer文件;如下图所示结构:
在index.js文件中代码如下:
import { createStore,applyMiddleware } from ‘redux‘
import reducer from ‘./reducer‘
import thunk from ‘redux-thunk‘
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
const enhancer = composeEnhancers(
applyMiddleware(thunk),
);
const store = createStore(reducer, enhancer);
export default store;
redux提供了createStore,applyMiddleware两个方法,createStore负责创建store这个store管理唯一数据源,至于我们担心数据过于庞大的问题,
combineReducers正是用来解决这个问题的,reducer.js文件中代码如下:
//import { combineReducers } from ‘redux‘
import { combineReducers } from ‘redux-immutable‘
//import headerReducer from ‘../common/header/store/reducer‘
import { reducer as headerReducer } from ‘../common/header/store‘
import { reducer as homeReducer } from ‘../pages/home/store‘
import { reducer as detailReducer} from ‘../pages/detail/store‘
import { reducer as loginReducer } from ‘../pages/login/store‘
const reducer = combineReducers({
header:headerReducer,
home:homeReducer,
detail:detailReducer,
login:loginReducer
})
export default reducer
在开发的简书页面上有头部、home、详情部分、登录部分,每个部分都有自己reducer,没部分的reducer由combineReducers结合起来,各部分的reducer处理
各自部分的代码逻辑。
以head头部为例结构如下图:
在head部分的store文件夹中有actionType.js文件action.Creaters.js文件、reducer.js文件、index.js文件,actonType相当于事件类型,如下代码:
export const SEARCH_FOCUS = ‘header/search_focus‘;
export const SARCH_BLUR = ‘header/search_blur‘;
export const CHANGE_LIST = ‘header/change_list‘;
export const MOUSE_ENTER = ‘header/mouse_enter‘
export const MOUSE_LEAVE = ‘header/mouse_leave‘;
export const change_Page = ‘header/chang_page‘
这里以前是字符串,这里用变量方便调错,如果报错能找到是哪个事件出问题了。
action.Creaters.js代码如下:
import * as ationType from ‘./actionType‘
import axios from ‘axios‘
import {fromJS} from "immutable";
const getChangeList = (data) =>({
type:ationType.CHANGE_LIST,
data:fromJS(data),
totalPage: Math.ceil(data.length/10)
});
export const SEARCH_FOCUS = () =>({
type:ationType.SEARCH_FOCUS
});
export const SEARCH_BLUR = () =>({
type:ationType.SARCH_BLUR
});
export const mouseEnter = () =>({
type:ationType.MOUSE_ENTER
});
export const mouseLeave = () =>({
type:ationType.MOUSE_LEAVE
});
export const changePageList = (page) =>({
type:ationType.change_Page,
page
})
export const getList = () =>{
return (dispatch) =>{
axios.get(‘/api/headerList.json‘).then((res) =>{
const data = res.data.data;
dispatch(getChangeList(data))
}).catch(() =>{
console.log(‘err‘)
})
}
}
redux-thunk引入redux-thunk最重要的思想,就是可以接受一个返回函数的action creator。如果这个action creator 返回的是一个函数,就执行它,如果不是
,就按照原来的next(action)执行。例如:在简书开发项目中ajax请求数据就用了这个用处代码如下:
const getChangeList = (data) =>({
type:ationType.CHANGE_LIST,
data:fromJS(data),
totalPage: Math.ceil(data.length/10)
});
export const getList = () =>{
return (dispatch) =>{
axios.get(‘/api/headerList.json‘).then((res) =>{
const data = res.data.data;
dispatch(getChangeList(data))
}).catch(() =>{
console.log(‘err‘)
})
}
}
定义的getList方法返回一个ation给reducer,同时dispatch一个ation是getChangeList()函数将ajax获取的数据接收过来给reducer,代码如下:
import * as ationType from ‘./actionType‘
import { fromJS } from ‘immutable‘
const defaultState = fromJS(
{
focused:false,
mouseIn:false,
list:[],
page:1,
totalPage:1
}
)
export default (state = defaultState,action) =>{
switch (action.type)
{ case ationType.CHANGE_LIST:
return state.merge({
‘list‘:action.data,
‘totalPage‘:action.totalPage
});
case ationType.SEARCH_FOCUS:
return state.set (‘focused‘, true);
case ationType.SARCH_BLUR:
return state.set (‘focused‘, false);
//可以使用merge方法
//state.set (‘list‘, action.data).set(‘totalPage‘,action.totalPage);
case ationType.MOUSE_ENTER:
return state.set(‘mouseIn‘,true);
case ationType.MOUSE_LEAVE:
return state.set(‘mouseIn‘,false);
case ationType.change_Page:
return state.set(‘page‘,action.page);
default:
return state;
}
import * as ationType from ‘./actionType‘在这里需要引入相应的事件集合,在这里创建action函数把相应的action发送到reducer里,
在reducer里进行变更store里面的状态,reducer.js代码如下:
import * as ationType from ‘./actionType‘
import { fromJS } from ‘immutable‘
const defaultState = fromJS(
{
focused:false,
mouseIn:false,
list:[],
page:1,
totalPage:1
}
)
export default (state = defaultState,action) =>{
switch (action.type)
{
case ationType.SEARCH_FOCUS:
return state.set (‘focused‘, true);
case ationType.SARCH_BLUR:
return state.set (‘focused‘, false);
case ationType.CHANGE_LIST:
return state.merge({
‘list‘:action.data,
‘totalPage‘:action.totalPage
})
//可以使用merge方法
//state.set (‘list‘, action.data).set(‘totalPage‘,action.totalPage);
case ationType.MOUSE_ENTER:
return state.set(‘mouseIn‘,true);
case ationType.MOUSE_LEAVE:
return state.set(‘mouseIn‘,false);
case ationType.change_Page:
return state.set(‘page‘,action.page);
default:
return state;
}
}
在组件中发送getList()action代码如下:
const mapDispatchToProps = (dispatch) =>{
return {
handleInputFocus (list) {
// (list.size === 0) && dispatch(ationCreaters.getList());
if(list.size<=0){
dispatch(ationCreaters.getList());
}
}
这样就可以经数据请求到并渲染到页面中。
在reducer代码里引入immutable.js库。
Immutable Data
就是一旦创建,就不能再被更改的数据。对 Immutable
对象的任何修改或添加删除操作都会返回一个新的 Immutable
对象Immutable
实现的原理是 Persistent Data Structure
(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变deepCopy
把所有节点都复制一遍带来的性能损耗, Immutable
使用了 Structural Sharing····
(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。// 原生对象
let a1 = {
b: 1,
c: {
c1: 123
}
};
let b1 = a1;
b1.b = 2;
console.log(a1.b, b1.b); // 2, 2
console.log(a1 === b1); // true
console.log(a1.c === b1.c); // true
// immutable.js 的Map
let a2 = Immutable.fromJS({
b: 1,
c: {
c1: 123
}
});
let b2 = a2.set(‘b‘, 2);
// 对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象
console.log(a2.get(‘b‘), b2.get(‘b‘)); // 1, 2 对象 a2 的 b 值并没有变成2。
console.log(a2 === b2); // false
//如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。
console.log(a2.get(‘c‘) === b2.get(‘c‘)); //true
Javascript
没有不可变数据结构的问题React
开发中,频繁操作state对象或是 store
,配合 immutableJS
快、安全、方便React.js
的都应该知道, React.js
是一个 UI = f(states)
的框架,为了解决更新的问题, React.js
使用了 virtual dom
, virtual dom
通过 diff
修改 dom
,来实现高效的 dom
更新。state
更新时,如果数据没变,你也会去做 virtual dom
的 diff
,这就产生了浪费。这种情况其实很常见PureRenderMixin
来解决呀, PureRenderMixin
是个好东西,我们可以用它来解决一部分的上述问题PureRenderMixin
只是简单的浅比较,不使用于多层比较。那怎么办?自己去做复杂比较的话,性能又会非常差immutable.js
可以解决这个问题。因为每一次 state
更新只要有数据改变,那么 PureRenderMixin
可以立刻判断出数据改变,可以大大提升性能可变( Mutable
)数据耦合了 Time
和 Value
的概念,造成了数据很难被回溯
Immutable.js
使用了 Structure Sharing
会尽量复用内存,甚至以前使用的对象也可以再次被复用。没有被引用的对象会被垃圾回收
import { Map} from ‘immutable‘;
let a = Map({
select: ‘users‘,
filter: Map({ name: ‘Cam‘ })
})
let b = a.set(‘select‘, ‘people‘);
a === b; // false
a.get(‘filter‘) === b.get(‘filter‘); // true
因为每次数据都是不一样的,只要把这些数据放到一个数组里储存起来,想回退到哪里就拿出对应数据即可,很容易开发出撤销重做这种功能。
传统的并发非常难做,因为要处理各种数据不一致问题,因此『聪明人』发明了各种锁来解决。但使用了 Immutable
之后,数据天生是不可变的,并发锁就不需要了。
Immutable
本身就是函数式编程中的概念,纯函数式编程比面向对象更适用于前端开发。因为只要输入一致,输出必然一致,这样开发的组件更易于调试和组装。
List
: 有序索引集,类似 JavaScript
中的 Array
。Map
: 无序索引集,类似 JavaScript
中的 Object
。OrderedMap
: 有序的 Map
,根据数据的 set()
进行排序。Set
: 没有重复值的集合。OrderedSet
: 有序的 Set
,根据数据的 add
进行排序。Stack
: 有序集合,支持使用 unshift()
和 shift()
添加和删除。Range()
: 返回一个 Seq.Indexed
类型的集合,这个方法有三个参数, start
表示开始值,默认值为 0
, end
表示结束值,默认为无穷大, step
代表每次增大的数值,默认为 1
.如果 start = end
,则返回空集合。Repeat()
: 返回一个 vSeq.Indexe
类型的集合,这个方法有两个参数, value
代表需要重复的值, times
代表要重复的次数,默认为无穷大。Record
: 一个用于生成 Record
实例的类。类似于 JavaScript
的 Object
,但是只接收特定字符串为 key
,具有默认值。Seq
: 序列,但是可能不能由具体的数据结构支持。Collection
: 是构建所有数据结构的基类,不可以直接构建上面那么多常用的也就是 List
和 Map
fromJS()
是最最最常用的将原生 JS
数据转换为 ImmutableJS
数据的转换方法。使用方式类似于 JSON.parse()
,接收两个参数: json
数据和 reviver
函数reviver
函数的情况下,默认将原生 JS
的 Array
转为 List
, Object
转为 Map
// 常见
const t1 = Immutable.fromJS({a: {b: [10, 20, 30]}, c: 40});
console.log(t1);
// 不常用
const t2 = Immutable.fromJS({a: {b: [10, 20, 30]}, c: 40}, function(key, value) {
// 定制转换方式,下这种就是将Array转换为List,Object转换为Map
const isIndexed = Immutable.Iterable.isIndexed(value);
return isIndexed ? value.toList() : value.toOrderedMap();
// true, "b", {b: [10, 20, 30]}
// false, "a", {a: {b: [10, 20, 30]}, c: 40}
// false, "", {"": {a: {b: [10, 20, 30]}, c: 40}}
});
console.log(t2);
先来看官网的一段话: immutable
数据应该被当作值而不是对象,值是表示该事件在特定时刻的状态。这个原则对理解不可变数据的适当使用是最重要的。为了将 Immutable.js
数据视为值,就必须使用 Immutable.is()
函数或 .equals()
方法来确定值相等,而不是确定对象引用标识的 ===
操作符
toJS()
就是用来对两个 immutable
对象进行值比较的。使用方式类似于 Object.is(obj1, obj2)
,接收两个参数const map1 = Immutable.Map({a:1, b:1, c:1});
const map2 = Immutable.Map({a:1, b:1, c:1});
// 两个不同的对象
console.log(map1 === map2); // false
// 进行值比较
console.log(Immutable.is(map1, map2)); // true
// 不仅仅只能比较ImmutableJS的类型的数据
console.log(Immutable.is(undefined, undefined)); // true
console.log(Immutable.is(null, undefined)); // false
console.log(Immutable.is(null, null)); // true
console.log(Immutable.is(NaN, NaN)); // true
// 区别于 Object.is
console.log(Object.is(0, -0) ,Immutable.is(-0, 0)); // false , true
Map
数据类型,对应原生 Object
数组。最最常用的 数据结构之一,循环时无序( orderedMap
有序),对象的 key
可以是任意值。具体看下面的例子
console.log(Map().set(List.of(1), ‘list-of-one‘).get(List.of(1)));
console.log(Map().set(NaN, ‘NaN‘).get(NaN));
console.log(Map().set(undefined, ‘undefined‘).get(undefined));
console.log(Map().set(null, ‘null‘).get(null));
OrderedMap
是 Map
的变体,它除了具有 Map
的特性外,还具有顺序性,当开发者遍历 OrderedMap
的实例时,遍历顺序为该实例中元素的声明、添加顺序。 OrderedMap
比非有序 Map
更昂贵,并且可能消耗更多的内存。如果真要求遍历有序,请使用 List
List
数据类型,对应原生 Array
数组。和原生数组,最大区别不存在’空位’。 [, , , , ]
console.log(List([,,,,]).toJS());// [undefined, undefined, undefined, undefined]
原文:https://www.cnblogs.com/zhx119/p/10868927.html