准备工作
建立 react-app
1 | npx create-react-app jianshu |
ELIFECYCLE 问题
error code ELIFECYCLE
How to solve this problem?
- 清除缓存,
npm cache clean --force
- 删掉 node_modules 目录及 package-lock.json 文件,
rm -rf node_modules package-lock.json
npm install
npm start
react-app 基本框架
删除不必要的代码, 其中 ./src 目录只留下 index.js, index.css, App.js 三个文件及相关代码
1 | import React from 'react'; |
1 | /* null */ |
1 | function App() { |
方便地管理 css 样式
css 文件只要在一个 js 文件内引用, 全局均可以使用. 那么, 如何正确地引入 css 样式呢? 借助一个第三方库 styled-components,
npm install styled-components --save
- 修改 index.css 为 style.js
- styled-components 的使用
styled-components 样例
1
2
3
4
5
6
7
8
9
10
11
12
13
14import {
createGlobalStyle
}
from "styled-components";
export const GlobalStyle=createGlobalStyle` body {
margin: 0;
padding: 0;
font-family: sans-serif;
background-color: green;
color: white
}
`index.js 引入
1
2
3
4
5
6
7
8
9
10
11
12import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { GlobalStyle } from './style.js';
ReactDOM.render(
<React.StrictMode>
<App />
<GlobalStyle />
</React.StrictMode>,
document.getElementById('root')
);
让页面在所有浏览器统一
引入 Reset.css 可以使得不同浏览器对body属性等 统一起来,如行高,margin等的默认值。
分部分开发
header 组件
创建 common 文件夹, 其下创建 ./header/index.js
目前的文件结构
- src
- common
- header
- index.js
- style.js
- header
- static
- iconfont
- iconfont.js
- iconfont.eot
- iconfont.svg
- iconfont.tff
- iconfont.woff
- iconfont
- index.js
- App.js
- style.js
- common
代码与实现效果
1 | import React from 'react'; |
1 | import React, { Component } from 'react'; |
1 | import { createGlobalStyle } from 'styled-components'; |
1 | import React, { Component } from 'react'; |
1 | import styled from 'styled-components'; |
1 | import { createGlobalStyle } from 'styled-components'; |
把数据存入 redux
随着项目越来越大,数据的存取必定是一个瓶颈。一个项目如果从一开始就定位的足够大,那么数据层面采用 react-redux 是必经之路。况且,随着项目复杂度加深,组件化是显然的。所以,从一开始就使用 redux 管理数据才是明智之举。
redux-store 的使用流程
如果用redux,如果可以存入其中,就尽可能的存入其中。
- 首先安装 redux,
npm install redux --save
;为了方便在 react 中使用 redux,进一步安装npm install react-redux --save
- 要使用 redux, 需要通过 store -props-> state 流程从 store 中取得数据,所以先创建 store 实例
- 创建 store;其中还包括管理手册-reducer
1
2
3
4
5import { createStore } from 'redux';// 取得createStore方法
import reducer from './reducer.js';
const store = createStore(reducer);//创建store实例,并与reducer链接
export default store; - reducer 为纯函数,唯一输入则唯一输出【state】。管理手册【reducer】包括默认表格【defaultState】及来自 store 的 action 两部分
1
2
3
4
5const defaultState = {};
export default (state=defaultState,action)=>{
return state
} - 在 App.js 内使用 redux,需要引入 store 实例。另外,为了方便使用,使用 react-redux 的 Provider 组件 => Provider 可以使
<Header />
标签由能力使用 store 内的数据1
2
3
4import {Provider} from 'react-redux'
import store from './store'
<Provider store={store}> <Header /> </Provider>- 除此以外,
<Header />
还需要与 store 进行连接1
import {connect} from 'react-redux'
- 除此以外,
- 改写 数据 及 函数
- 改写 constructor 内 this.state 的默认数据至 reducer 的默认值
- 改写 constructor 内 bind(this) 的绑定函数
- 提高性能,Header.js 改至无状态组件
载入 redux 的调试
在 store.js 引入 redux-devtools-extension,方便调试
1 | import { createStore,compose } from 'redux';//compose 可以一次传入多个函数 |
拆分 reducer
一个文件的代码 如果超过了300行,那就说明项目的架构出了问题。因此,需要将 reducer 进行拆分 后管理 store。一个思路是,给页面的每个部分创建单独的 store-reducer,然后在总的 reducer 汇总各个部分的 reducer。大致分为两个步骤。
- 定义各个部分的 store 。以 header 举例,创建 reducer,用来存放 header 用到的 reducer
- src目录下的 store ,在该 store 的 reducer 内注册各部分的 reducer,整合成一个reducer 时需要调用 redux-combineReducer 方法。
1
2
3
4
5
6import { combineReducers } from 'redux';
import headerReducer from '../common/header/store/reducer';
export default combineReducers({
header: headerReducer
});
集中定义 action
通常 action 仅用来传递数据对象;当使用 redux-thunk 时,也可传递函数,因此定义 action 要分两种情况,其中 仅传递数据对象 又分为含参数与不含参数两种情况。
仅传递数据
- 不含参数时,定义形如
const xxx=()=>({type:···})
的函数,并导入定义 SEARCH_FOCUS 的常量文件1
2
3
4
5
6import * as constants from './constants';
//action为数据对象
export const searchFocus=()=>({//searchFocus 为action
type:'contants.SEARCH_FOCUS'
})- constants 文件如何定义常量
1
export const SEARCH_FOCUS = 'header/seach_focus'
- constants 文件如何定义常量
- 含参数时,定义形如
const xxx=(yyy)=>({type:···})
的函数1
2
3
4const changeStoreList = (data)=> ({
type: 'change_store_list',
data: fromJS(data) // 保持数据类型统一
});
- 不含参数时,定义形如
传递函数时,含参数时,定义形如
const xxx=()=>{type:···}
的函数1
2
3
4
5
6
7
8
9
10
11
12
13
14export const getList = () => {
return (dispatch) => {
axios
.get('/api/headerList.json')
.then((res) => {
const data = res.data;
const action = changeStoreList(data.data);
dispatch(action);
})
.catch(() => {
console.log('error');
});
};
};
index 出口管理
在 各部分的 store 下新建 index.js 用来管理 store 。
1 | import * as actionCreators from './actionCreators'; |
其他文件引入时
1 | // /index.js 可以省略,故./store/index.js 写作 ./store |
immutable.js 不可变更
使用 immutable.js fromJS 组件,可以使对象成为不可更改的对象。这对 state 十分重要。
- 安装 immutable,
npm install immutable --save
- 使用 immutable 库
1
2
3
4
5import {fromJS} from 'immutable'// obj js convert to immutable
const defaultState = fromJS({
focused: false
}); - 映射的数据获取需使用 get 方法
1
2
3
4
5
6
7//store -> props 规则
const mapStateToProps = (state) => {
return {
//immutable obj need to use get-method
focused: state.header.get('focused')
};
}; - 要修改 store-state 的数据则需要使用 set 方法
1
2
3
4if (action.type === 'constants.SEARCH_FOCUS') {
//immutable set func 结合以前immutable对象的数据,返回一个全新的对象
return state.set('focused', true);
} - 拓展 immutable 性质至 根store
- 借助 redux-immutable 库,
npm install redux-immutable --save
- 借助 redux-immutable 库,
ajax 数据异步请求
中间件 redux-thunk
获取 ajax 数据不会直接写在组件内,一般放在 action 内或 redux-saga内。其中,如果想在 action 内传递函数,需要借助 redux-thunk;则安装 npm install redux-thunk --save
, 这便是要使用 action-中间件-store。即 action-中间件 和 中间件-store
- action-中间件,须在创建 store 时使用 redux-thunk
1
2
3
4
5
6
7
8
9
10
11
12
13//在根目录 store 内
import { createStore,compose ,applyMiddleware} from 'redux';
//compose 可以一次传入多个函数或对象
import thunk from 'redux-thunk'
import reducer from './reducer.js';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer,composeEnhancers(
applyMiddleware(thunk)
));
export default store; - 中间件-store,须在创建 action 时使用 redux-thunk,即对应的reducer 内使用
获取 ajax 数据
ajax 异步请求,需要借助 axios,安装npm install axios --save
循环显示在页面
- 列表循环,需要用回调函数。
1
2
3
4
5list.map((item)=>{
return(
<SearchInfoItem key={item}>{item}</SearchInfoItem>
)
})
代码优化
- actionCreators 不需要暴露的 action 统一写在上面
- 解构赋值让代码更精简
1
2
3const { focused, list } = this.props;
//这样之后,focused 等效于 this.props.focused
// list 等效于 this.props.list - 多个同类型 if 语句写成 switch 语句
1
2
3
4
5
6
7switch(···){
case :
···
break;
default :
···;
}
换一换
在 header/reducer.js 的 defaultState 新增数据项 page totalPage
1 | const defaultState = fromJS({ |
ref 的使用
减少 ajax 请求
header 小结
- react 是 面向数据 编程,最难的地方 reducer 里面的数据如何被设计
- react-redux 的数据的改变具有 单向数据流 的特点,组件派发 action 给到 store, store 把 action 给到 reducer,reducer 把修改的数据返回 store,store 更新到 组件state,进而带动页面变化。
react 的路由
路由是什么?根据 url 不同,显示不同的页面就叫做路由。
安装 react-router-dom, npm install react-router-dom --save
1 | import React, { Component } from 'react'; |
路由管理
路由规则
- src
- pages
- home
- index.js
- detail
- index.js
- home
- App.js
- pages
具体如下:
1 | import React, { Component } from 'react'; |
1 | import React, { Component } from 'react'; |
1 | import React, { Component } from 'react'; |
主页组件拆分
数据与接口
定义 /api/homeData.json
{
success: true
data:{
topicList:[],
articleList:[],
recommendList:[],
authorList:[]
}
}
异步组件
使用 react-loadable 可以是 js 分开加载
1 | npm install --save react-loadable |
项目上线流程
删掉本地模拟的 api 数据,npm build
之后将 build 文件夹丢给后端即可。