React 学习总结

本贴最后更新于 1933 天前,其中的信息可能已经东海扬尘

第 1 章 课程导学

学习流程
相关的知识点
学习前提
要有一些 js、es6、webpack、npm 等基础知识

第 2 章 React 初探

2-1 React 简介

React
FaceBook 推出
2013 年开源
函数式编程
使用人数最多的前端框架之一
健全的文档与完善的社区

2-2 React 开发环境准备

1)、确定自己电脑已经安装了 node 环境
根据自己电脑来安装属于自己电脑版本的 node,推荐使用稳定版
node 官网下载地址
2)、安装 react 脚手架 create-react-app

npx create-react-app my-app
cd my-app
npm start
2-3 工程目录文件简介

因为通过脚手架下载的程序,有很多文件是需要我们剔除出去的。
例如没用的样式文件、测试文件、pwa 相关的一些文件等等

  • 项目结构
  • ├── node_modules # 第三方的依赖库文件
  • ├── public
  • │ ├── favicon.ico # Favicon
  • │ ├── index.html # 首页
  • ├── src
  • │ ├── app.js # 业务代码文件
  • │ ├── index.js # 应用入口
  • ├── README.md
  • └── .gitignore #git 忽略提交的文件配置
  • └── package.json #启动、编译命令设置;依赖包管理配置信息
  • └── yarn.lock #yarn 依赖包管理配置信息
2-4 React 中的组件

下面就是定义了一个 App 的组件

//App.js
import React, {Component} from 'react'

class App extends Component {
    render() {
      //JSX 语法
      return (
          <div>hello world!</div>
      )
    }
}
export default App

App 组件引用和调用

//index.js
import React from 'react'
import ReactDOM from 'react-dom'

import App from 'App.js'

ReactDOM.render(<App />,document.getElementById('root'))

JSX 语法在使用的时候,组件的定义和使用的时候要大写
JSX 语法中,我们要求一个组件 render 函数返回的内容,外层必须要有一个大的元素包裹(div 或者 Fragment)
Fragment 渲染到页面的时候是不占位的
eg:

//index.js
import App from 'App.js'  //大写

ReactDOM.render(<App />,document.getElementById('root'))

第 3 章 React 基础精讲

3-1 使用 React 编写 TodoList 功能
//TodoItem.js
import React, {Component} from 'react';
import PropTypes from 'prop-types'; //组件类型进行校验

class TodoItem extends Component {

    constructor(props) {
        super(props);
        this.handleDelete = this.handleDelete.bind(this)
    }

    render() {
        const {content, test} = this.props;

        return (
            <div onClick={this.handleDelete}>{test}-{content}</div>
        )
    }

    handleDelete() {
        const {index, deleteItem} = this.props;
        deleteItem(index)
    }
}

TodoItem.propTypes = {
    test: PropTypes.string.isRequired, //父组件没有给子组件传递test 但是又是必填项,所以可以通过defaultProps给一个默认值
    content: PropTypes.string,
    deleteItem: PropTypes.func,
    index: PropTypes.number
};

TodoItem.defaultProps = {
    test: 'hello'
};
export default TodoItem


//TodoList.js
import React, {Component, Fragment} from 'react';

import TodoItem from './TodoItem';

class TodoList extends Component {
    constructor(props) {
        super(props);
        this.state = {
            inputValue: '',
            list: []
        };
        this.handleInputChange = this.handleInputChange.bind(this);
        this.handleAdd = this.handleAdd.bind(this);
        this.handleDelete = this.handleDelete.bind(this);
    }

    render() {
        return (
            <Fragment>
                <div>
                    <label htmlFor="inputArea">输入内容:</label>
                    <input
                        id="inputArea"
                        type="text"
                        value={this.state.inputValue}
                        onChange={this.handleInputChange}
                    />
                    <button onClick={this.handleAdd}>提交</button>
                </div>
                <ul>
                    {this.getTodoItem()}
                </ul>
            </Fragment>
        )
    }

    getTodoItem() {
        return this.state.list.map((item, index) => {
            return (
                <TodoItem
                    content={item}
                    key={index}
                    index={index}
                    deleteItem={this.handleDelete}
                />
            )
        })
    }

    handleInputChange = (e) => {
        const value = e.target.value;
        this.setState(() => {
            return {
                inputValue: value
            }
        })
    };

    handleAdd = () => {
        this.setState((prevState) => {
            return {
                list: [...prevState.list, prevState.inputValue],
                inputValue: ''
            }
        });
    };

    handleDelete = (index) => {
        this.setState((prevState) => {
            const list = [...prevState.list];
            list.splice(index, 1);
            return {list}
        });
    }
}

export default TodoList;

3-2 JSX 语法细节补充

1.注释:必须要用花括号括起来
单行注释

{
//这里是单行注释
}

多行注释

{/*这是多行注释*/}

2.样式名称引入:将 class 改为 className

因为 class 这个和定义组件“类”有冲突

<input className="inputClass" />

3.表单 label for 属性的引用,要将 for 改为 htmlFor
防止 for 和 js 中循环 for 引起冲突

<label htmlFor="inputArea">用户名</label>
<input id="inputArea" />

4.将 input 标签内容直接进行转义呈现在页面 dangerouslySetInnerHTML

 <li
     key={index}
     onClick={this.handleDeleteItem.bind(this, index)}
     dangerouslySetInnerHTML={{__html: item}}
>
</li>
3-3 组件拆分与组件之间的传值

1.组件拆分
当一个页面很大的时候,那么我们就会对这个页面进行组件的拆分,拆分的结构就像一棵树一样
组件之间的结构

2.组件之间的传值
父组件向子组件传值是通过“子组件的属性”
子组件接受父组件传过来的值:this.props.属性名称
子组件向父组件传值是通过 子组件调用父组件的方法,从来改变父组件的数据,但是要对这个方法调用的时候,父组件要对这个方法进行 this 的绑定

3-4 TodoList 代码优化
//解构优化
this.props.content ,this.props.index
改为
const {content,index} = this.props
调用的时候,直接使用content,index即可

//this的绑定优化 都可以写在constructor里面
onChange={this.handleChange.bind(this)}
constructor(props){
  super(props);
  ....
  ....
  this.handleChange = this.handleChange.bind(this) //优化地方
}
调用的时候直接写:
this.handleChange

//Ui优化
当一段的UI太过于庞大的是时候,可以把这段UI放到一个函数方法里面,return 返回出去,同时在使用的地方调用一下即可

//setState键值对优化为异步函数
this.setState({
  inputValue: e.target.value
})
改为
this.setState(()=>{
   const value = e.target.value  //在异步里面必须要提出来,如果直接须赋值会报错的
   return {
     inputValue:value  //如果将value 改为e.target.value 会报错
   }
})

//setState中this.state 改为 参数prevState
handleAdd = () => {
        this.setState((prevState) => {
            return {
                list: [...prevState.list, prevState.inputValue],
                inputValue: ''
            }
        });
 };

//if else判断改变为 switch case也可以提高性能

3-5 围绕 React 衍生出的思考

声明式开发
可以与以其他框架并存
组件化
单项数据流(父组件可以向子组件传值,但是子组件一定不能直接去改变这个值)
视图层框架 (负责视图渲染,但是针对一些大数据和复杂数据的时候,需要交由 redux 等数据框架来处理)
函数式编程 (便于自动化测试)

第 4 章 React 高级内容

4-1 React developer tools 安装及使用

打开 chrome 浏览器,并且打开 chrome 应用商店,搜索 React Developer Tools 添加即可
React Developer Tools 地址
百度网盘下载地址

4-2 PropTypes 与 DefaultProps 的应用

创建好的组件,最好给组件定义属性类型(string、number、boolean 等等) 和创建默认值。就拿上面 todoList 这个例子来说吧。
PropTypes
父组件(TodoList)向子组件(TodoItem)传递了 content(字符串)、index(数字)、deleteItem(方法),那么如果我们在子组件中声明了这些类型,就可以避免一些不别要的麻烦,如果父组件传递过来的属性值不是我们想要的,那么我就可以告诉浏览器我需要什么类型的值
DefaultProps
可以当父组件没有给子组件传递任何值的时候,通过 DefaultProps 给子组件初始化一些数据(即默认值)
详细代码见 3-1 使用 React 编写 TodoList 功能
了解更多 PropTypes 知识链接地址

4-3 props,state 与 render 函数的关系

1.当组件的 state 或者 props 发生改变时,render 函数就会重新执行
2.当父组件的 render 函数被重新执行时,它的子组件的 render 函数都将被重新执行
eg:
在 render 函数的后面进行打印
打印父组件 console.log('parent-render')
打印子组件 console.log('child1-render')
更改父组件的 state 值
你会发现 console 里面 parent-render 和 child1-render 都被打印出来了

4-4 React 中的虚拟 DOM

1.state 数据
2.JSX 模板
3.数据 + 模板 结合,生成真实 DOM,来显示
4.state 发生改变
5.数据 + 模板 结合,生成真实 DOM,替换原始 DOM

缺陷:
第一次生成一个完整的 DOM 片段
第二次生成一个完整的 DOM 片段
第二次的 DOM 替换第一次的 DOM,非常消耗性能

解决??
1.state 数据
2.JSX 模板
3.数据 + 模板 结合,生成真实 DOM,来显示
4.state 发生改变
5.数据 + 模板 结合,生成真实 DOM,并不直接替换原始 DOM
6.新的 DOM(documentFragment)和原始的 DOM,做对比,找差异
7.找出 input 框发生改变的部分
8.只用新的 DOM 中的 input 元素,替换老的 DOM 中 input 的元素

缺陷:
性能的提升并不明显

接着解决:
1.state 数据
2.JSX 模板
3.生成虚拟 DOM(其实就是一个 js 对象,用它来描述真实 DOM)(损耗了性能但是极小)

['div',{id:'abc'},['span',{},'hello world']]

4.用虚拟 DOM 的结构生成真实 DOM,来显示

<div id="abc"><span>hello world</span></div>

5.state 发生改变
6.数据 + 模板 结合,生成新的虚拟 DOM (极大的提升了性能)

['div',{id:'abc'},['span',{},'bye bye']]

7.比较原始虚拟 DOM 和生成新的虚拟 DOM,找到区别是 span 的内容(极大的提升了性能)
8.直接操作 DOM,改变 span 的内容

总结如下:
虚拟 DOM 其实就是 js 对象,那为什么采用虚拟 dom 会提高性能呢?因为他们比对的是 js 对象,而不是真的 DOM,从而极大的提升了性能

4-5 深入了解虚拟 DOM
JSX(模板) => 虚拟DOM(js对象) =>真实的DOM
render() {
  return <div>item</div>
}
等价于
render() {
  return React.createElement('div',{},'item')
}

虚拟 DOM 有点:
1.性能提升了
2.它是的跨度应用得以实现 React Native
因为数据 + 模板生成的是虚拟 DOM,
1)、如果是在网页中,那么再有虚拟 DOM(JS 对象)生成真实的 DOM,浏览器可以得以识别,所以网页中可以得到应用
2)、如果在 App 中,那么再用虚拟 DOM(JS 对象)不去生成真实的 DOM,而去生成原生的组件,那么在 App 中也就可以得到应用

4-6 虚拟 DOM 中的 Diff 算法

虚拟 DOM 什么时候回发生比对呢?
答:那就是当 state 值发生改变的时候,虚拟 DOM 才会开始进行比对

为什么 setState 设计成为异步的?
答:我们知道,当 state 发生改变或者 props(即父组件中的 state 发生改变)时,也就是当我们调用 setState 方法的时候,state 值发生改变,虚拟 DOM 开始进行比较。
那么如果连续 3 次调用 setState 方法的时候,变更 3 组数据(我们此时会想 react 会进行三次虚拟 DOM 比对,三次渲染页面),其实 react 会把短时间内连续调用 setState 方法合并为一个 setState,只去做一次虚拟 DOM 的比对,然后更新一次 DOM,这样就可以省去额外两次 DOM 比对带来的性能消耗,所以把 setState 设计成为异步的

react 中虚拟 DOM 比对采用的是 diff 算法:其实同层比对,key 值比对
同层比对

key 值比对

所以列表循环中不要用 index 作为 key 值的,在 diff 算法进行比较时候,会导致 key 变更,而产生一些性能上的问题
因为 index(索引值有时候会变更)会导致 key 值不稳定,
eg:
a 0 b 1 c 2
当删除 a
b 0 c 1
使用一个稳定的值作为 key 才是我们首先要考虑的
eg:
a a b b c c
当输出 a
b b c c

4-7 React 中 ref 的使用

ref 是帮助我们 react 直接获得 dom 元素,一般情况下我们尽量不要使用 ref
eg:

<input
   id="inputArea"
   type="text"
   value={this.state.inputValue}
   onChange={this.handleInputChange}
   ref={(input) => this.input = input}  //ref 使用
/>
 handleInputChange = (e) => {
        //const value = e.target.value;
        const value = this.input.value; //通过ref 来获取input的value值
        this.setState(() => {
            return {
                inputValue: value
            }
        })
};

tip:
当我们在试用 ref 获取 dom 元素的时候,有时候会出现数据不对,或者少一步操作现象,那么因为 setState 是异步函数,解决这个问题就是把 ref 相关操作的内容放到 setState 回调函数中进行操作

<ul ref={(ul)=>this.ul = ul}>
  <div>hello</div>
</ul>

this.setState(() => {
  return {
    inputValue: value
  }
},() => {
  //回调成功后,在这进行ref相关操作 this.ul.querySelectorAll('div').length
})

在 React v16.3 版本中引入的 React.createRef() 方法

//使用方法
import React, {Component} from 'react';

class Products extends Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef(); //这个地方
  }

  handleChangeInputValue = () => {
    console.log(this.myRef.current.value) //取值的时候借助current属性
  };

  render() {
    return (
      <div>
        <input type="text" ref={this.myRef} onChange={this.handleChangeInputValue}/>
      </div>
    )
  }
}

export default Products

Ref 的适合使用的几个场景:

  • 处理焦点、文本选择或媒体控制。
this.textInput.current.focus();
  • 触发强制动画。
  • 集成第三方 DOM 库
4-8 React 的生命周期函数

生命周期函数:
某一时刻自动调用执行的函数。

//虽然不是react的生命周期函数,但是有这个功能,初始化数据就放在constructor里面进行操作的
1.初始化
constructor(props){
  super(props);
}

2.挂载的生命周期函数(Mounting)
//当组件即将要被挂载到页面的时候,会被执行
componentWillMount(){}
//render
render(){}
//当组件被挂载到页面后,会被执行
componentDidMount(){}

3.数据发生改变更新的生命周期函数(Updating)
//当组件被更新之前,会被执行
shouldComponentUpdate(nextProps,nextState){
  const {list} = this.props;
   if(nextProps.list !== list){
        return true
   }else{
      return false
   }
}

//当组件被更新之前,会被执行,但是在shouldComponentUpdate后面
//如果shouldComponentUpdate 返回true 它才执行
//如果返回 false 它不会被执行
componentWillUpdate(){}

//当组件被更新之后,会被执行
componentDidUpdate(){}

//一个组件要从父组件接受参数
//如果这个组件第一次存在于父组件中,不会被执行
//如果这个组件之前已经存在父组件中,才会被执行
componentWillReceiveProps(nextProps){}

4.组件从页面卸载的生命周期函数
//当这个组件将要被从页面剔除出去的时候,会被执行
componentWillUnmount(){}

react 生命周期函数

4-9 React 生命周期函数的使用场景

1.生命周期使用场景

//针对组件的优化的时候,可以使用shouldComponentUpdate,当组件当中的属性值发生改变的是去做更新,没有改变则不去更新,减少组件被重复多次渲染的频率,减少性能的消耗
shouldComponentUpdate(nextProps, nextState) {
        const {content} = this.props;
        if (nextProps.content !== content) {
            return true
        } else {
            return false
        }
    }

//ajax接口请求 放在componentDidMount()里面
import axios from 'axios'
componentDidMount(){
  axios.get('api')
     .then((res)=>{
        console.log('success')
      })
    .catch(()=>{
        console.log('error')
    })
}

2.性能优化
1)、shouldComponentUpdate
2)、setState 采用的是异步回调的方式,提高了性能
3)、虚拟 DOM 中的同层比对和 key 值比对,也极大的提高了性能

4-10 使用 Charles 实现本地数据 mock

charles 下载安装

//桌面创建一个todoList.json文件
["react","vue","angular"]

//设置charles
打开 charles ---> 菜单栏tools ---> Map Local Setting

//请求接口
componentDidMount() {
        axios.get('/api/todoList')
            .then((res) => {
                this.setState(() => {
                    return {
                        list: [...res.data]
                    }
                })
            })
            .catch(() => {
                console.log('error')
            })
}

Map Local Setting 设置

4-11 React 中实现 CSS 过渡动画

css 过度动画主要还是 css3 知识点的理解和认识,比如:transition(all 1s ease-in)、animation(show-item 1s ease-in forwards)、@keyframes 等等
还就就是借助第三方库 react-transition-group 等等

//state
 constructor(props) {
        super(props);
        this.state = {
            show: true
        };

        this.handleToggle = this.handleToggle.bind(this);
}

//JSX
<h3>动画部分</h3>
<div className={this.state.show ? 'show' : 'hide'}>hello</div>
<button onClick={this.handleToggle}>toggle</button>

//绑定事件
handleToggle() {
        this.setState((prevState) => {
            return {
                show: prevState.show === true ? false : true
            }
        })
}

//style
.show {
    /*opacity: 1;*/
    /*transition: all 1s ease-in;*/

    animation: show-item 2s ease-in forwards;
}

.hide {
    /*opacity: 0;*/
    /*transition: all 1s ease-in;*/
    animation: hide-item 2s ease-in forwards;
}

@keyframes show-item {
    0% {
        opacity: 0;
        color: red;
    }
    50% {
        opacity: 0.5;
        color: green;
    }
    100% {
        opacity: 1;
        color: blue;
    }
}

@keyframes hide-item {
    0% {
        opacity: 1;
        color: red;
    }
    50% {
        opacity: 0.5;
        color: green;
    }
    100% {
        opacity: 0;
        color: blue;
    }
}
4-12 使用 react-transition-group 实现动画
import  { CSSTransition } from 'react-transition-group';
<CSSTransition
   in={this.state.show}
   timeout={1000}
   classNames="fade"
   unmountOnExit
   onEntered={(el)=>{el.style.color='blue'}}
   appear={true}
>
   <div>hello</div>
</CSSTransition>

//css

/*CSSTransition*/
.fade-enter,fade-appear{
    opacity: 0;
}
.fade-enter-active,fade-appear-active{
    opacity: 1;
    transition: opacity 1s ease-in;
}
.fade-enter-done{
    opacity: 1;
}

.fade-exit{
    opacity: 1;
}
.fade-exit-active{
    opacity: 1;
    transition: opacity 1s ease-in;
}
.fade-exit-done{
    opacity: 0;
}

当我们想一个数组都拥有动画的时候,我们会用 react-transition-group 里面的 TransitionGroup
了解更多链接地址

第 5 章 Redux 入门

5-1 Redux 概念简述

react 是一个视图层框架
那么,redux 就是一个数据层框架
Redux = Reducer + Flux
image.png

5-2 Redux 的工作流程

Redux 的工作流程

React Component:借书者
Action Creators: 我要借本三国演义这句话
Store: 图书馆管理员
Reducer:记录本

React Component:我想要借本三国演义
Action Creators:那我给你写封信(dispatch 发号了一个 action 指令),我把这个指令告诉了 Store
Store:我知道了,但是我不知道这本书放到哪了,我帮你问问 Reducers
Reducer:我给你查查,查到了,在这呢。带着三国演义这本书(newState)给了 Store,Store 记录下了借书的内容信息,并这本书最终给了 React Components

5-3 使用 Antd 实现 TodoList 页面布局
//1.安装antd并且引入antd样式文件
npm install antd -S

//2.todoList.js
import React, {Component} from 'react';
import {Input, Button, List} from 'antd';

import 'antd/dist/antd.css';

import store from './store'

class TodoList extends Component {
    constructor(props) {
        super(props);
        this.handleInputValue = this.handleInputValue.bind(this);
        this.state = store.getState();
    }

    render() {
        return (
            <div style={{marginTop: '10px', marginLeft: '10px'}}>
                <Input
                    value={this.state.inputValue}
                    placeholder="todo info"
                    style={{width: '300px', marginRight: '10px'}}
                />
                <Button type="primary" >提交</Button>
                <List
                    style={{marginTop: '10px', width: '300px'}}
                    bordered
                    dataSource={this.state.list}
                    renderItem={item => (<List.Item>{item}</List.Item>)}
                ></List>
            </div>
        )
    }

    handleInputValue(e) {
        const action = {
            type: 'change_input_value',
            value: e.target.value
        };
        store.dispatch(action)
    }

}

export default TodoList;
5-4 创建 redux 中的 store
//在src目录下创建store目录,并在store目录下创建index.js(图书馆管理员)和reducer.js(记录本)

//index.js
import {createStore} from 'redux';
import reducer from './reducer';
const store = createStore(reducer);
export default store

//reducer.js
const defaultState = {
    inputValue: '123',
    list: [1, 2]
};

export default (state = defaultState, action) => {
    return state
}

//TodoList.js
import store from './store'
constructor(props) {
     super(props);
     this.state = store.getState();
     console.log(store.getState()) //打印出来的数据是{inputValue: "123",Array(2)}
}

创建图书馆 store 并打通 store 和 reducer 之间的桥梁,
然后 react component 通过 store.getState()方法拿到 reducer 里面的数据了

5-5 Action 和 Reducer 的编写

1.Redux DevTools 安装和配置
去 chrome 应用商店 安装 Redux DevTools 这个浏览器插件
Redux DevTools 下载链接地址
安装完了以后,发现用 chrome 浏览器打开控制台,点击 redux 标签,并没有显示任何内容,那是因为需要我们在 store 文件中写一段代码。
配置相关信息的地址,打开 github,搜索 redux-devtools-extension,就能查看相关的配置信息了

import {createStore} from 'redux';
import reducer from './reducer';

const store = createStore(
    reducer, 
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() //这段代码,为了方便浏览器能捕获到redux数据
);

export default store

2.redux 的工作流程原理演示(todoList 这个例子来说)
根据上面的 redux 工作流,咱们来说。
如果我们想要更改 React Component(input 里面的值)
那么我们需要创建一个 Action creators,并且定义一个 action 对象,通过 store.dispatch 方法传递给 store

handleInputValue(e) {
       //定义一个action
        const action = {
            type: 'change_input_value',
            value: e.target.value
        };
      // 通过store.dispatch方法,把action传递给store
        store.dispatch(action)
}

当 store 接受到 Action creators 发送过来的 action 的时候,它说我需要我的秘书 reducer 帮我查询一下,并且帮我处理一下。
于是乎 reducer 就就收了两个参数,第一个是 state(定义初始化的旧数据),第二个就是传递过来的 action(一个 type 名称,一个是传递过来新的 inputValue 值),
reducer 说:我不能直接去更改 state 里面的值,我需要把 state 值通过 JSON 的 parse 和 stringify 进行数据深层拷贝生成 newState。那么在对这个 newState 进行数据的处理,最后把处理好的数据再 return 回去
store 拿到 reducer 处理好的新数据后,
再通过自己的 store.getState()方法,去拿到 reducer 的最新数据
再通过自己的 store.subscribe()方法,去监测 store 里面的数据变化,
最后通过 this.setState()方法,把最新的数据渲染到页面上去

通过第一方法专门是获得最新数据的 store.subscribe(this.handleStore);

//reducer.js
const defaultState = {
    inputValue: '123',
    list: [1, 2]
};

export default (state = defaultState, action) => {
    console.log(state, action);
    if (action.type === 'change_input_value') {
        const newState = JSON.parse(JSON.stringify(state));
        newState.inputValue = action.value;
        return newState
    }

    if (action.type === 'add_list_item') {
        const newState = JSON.parse(JSON.stringify(state));
        newState.list.push(newState.inputValue);
        newState.inputValue = '';
        return newState
    }
    return state
}

//TodoList.js
import React, {Component} from 'react';
import {Input, Button, List} from 'antd';

import 'antd/dist/antd.css';

import store from './store'

class TodoList extends Component {
    constructor(props) {
        super(props);
        this.handleInputValue = this.handleInputValue.bind(this);
        this.handleAdd = this.handleAdd.bind(this);
        this.handleStore = this.handleStore.bind(this);
        this.state = store.getState(); 

        store.subscribe(this.handleStore);//检测handleStore方面里面数据的变化
    }

    render() {
        return (
            <div style={{marginTop: '10px', marginLeft: '10px'}}>
                <Input
                    value={this.state.inputValue}
                    placeholder="todo info"
                    style={{width: '300px', marginRight: '10px'}}
                    onChange={this.handleInputValue}
                />
                <Button type="primary" onClick={this.handleAdd}>提交</Button>
                <List
                    style={{marginTop: '10px', width: '300px'}}
                    bordered
                    dataSource={this.state.list}
                    renderItem={item => (<List.Item>{item}</List.Item>)}
                ></List>
            </div>
        )
    }

    handleInputValue(e) {
        //定义一个action 把这个type:干什么事情的名字传递过去,并且发生更改的信息传递过去
        const action = {
            type: 'change_input_value',
            value: e.target.value
        };
        store.dispatch(action)
    }

    handleAdd() {
        const action = {
            type: 'add_list_item'
        };
        store.dispatch(action)
    }

    handleStore() {
        //把变更后新的数据,重新放入到state中,然后去渲染页面
        this.setState(store.getState());
    }

}

export default TodoList;
5-6 ActionTypes 的拆分

为什么要把 action 里面的 type 拆分到一个文件里面呢?
第一当我们把 type 值拼写错误的时候,不好找错
第二我们需要调用相同的内容,写两次
所以我们在 store 文件下面创建了一个 actionType.js

5-7 使用 actionCreator 统一创建 action

为什么要把把组件中的 action 通过方法的形式拆分出去呢?
第一为了方便 action 的统一管理
第二为了减少 React Component 代码的繁琐

//在store文件下创建actionCreators.js
import {CHANGE_INPUT_VALUE, ADD_LIST_ITEM, DELETE_LIST_ITEM} from './actionTypes';

export const getChangeInputValue = (value) => ({
    type: CHANGE_INPUT_VALUE,
    value
});

export const getAddListItem = () => ({
    type: ADD_LIST_ITEM
});

export const getDeleteListItem = (index) => ({
    type: DELETE_LIST_ITEM,
    index
});

//TodoList.js
import {getChangeInputValue, getAddListItem, getDeleteListItem} from './store/actionCreators';
handleInputValue(e) {
        const action = getChangeInputValue(e.target.value);
        store.dispatch(action)
}

handleAdd() {
        const action = getAddListItem();
        store.dispatch(action)
}

handleDeleteItem(index) {
        const action = getDeleteListItem(index);
        store.dispatch(action);
}
5-8 Redux 知识点复习补充

1.Redux 在设计和使用的三个原则
1).store 是唯一的
2).只有 store 能改变自己的内容
3).reducer 必须是个纯函数(给个固定的输入,就一定会有固定的输出,且不会有任何副作用)
所以里面不能有异步操作(ajax),不能有时间的操作 new Date()
2.redux 中核心的 API
1).createStore 帮助我们创建一个 store
2).store.dispatch() 帮助我们派发一个 action
3).store.getState() 帮助我们获得 store 当中所有的数据
1).store.subscribe() 帮助我们订阅(监测)store 当中数据的改变

第 6 章 Redux 进阶

6-1 UI 组件和容器组件

1.UI 组件
UI 组件负责页面的渲染
eg:

import React, {Component} from 'react'
import {Button, Input,List} from "antd";

class TodoListUi extends Component{
    render(){
        return (
            <div style={{marginTop: '10px', marginLeft: '10px'}}>
                <Input
                    value={this.props.inputValue}
                    placeholder="todo info"
                    style={{width: '300px', marginRight: '10px'}}
                    onChange={this.props.handleInputValue}
                />
                <Button type="primary" onClick={this.props.handleAdd}>提交</Button>
                <List
                    style={{marginTop: '10px', width: '300px'}}
                    bordered
                    dataSource={this.props.list}
                    renderItem={(item, index) => (
                        <List.Item onClick={() => this.props.handleDeleteItem(index)}>{item}</List.Item>)}
                ></List>
            </div>
        )
    }
}

export default  TodoListUi;

2.容器组件
它不管你的 UI 长成什么样子,它只负责页面的业务逻辑

import React, {Component} from 'react';
import TodoListUi from './TodoListUi';

import 'antd/dist/antd.css';

import store from './store'
import {getChangeInputValue, getAddListItem, getDeleteListItem} from './store/actionCreators';

class TodoList extends Component {
    constructor(props) {
        super(props);
        this.handleInputValue = this.handleInputValue.bind(this);
        this.handleAdd = this.handleAdd.bind(this);
        this.handleDeleteItem = this.handleDeleteItem.bind(this);
        this.handleStore = this.handleStore.bind(this);
        this.state = store.getState();

        store.subscribe(this.handleStore);
    }

    render() {
        return (
            <TodoListUi
                inputValue={this.state.inputValue}
                list={this.state.list}
                handleInputValue={this.handleInputValue}
                handleAdd={this.handleAdd}
                handleDeleteItem={this.handleDeleteItem}
            />
        )
    }

    handleInputValue(e) {
        const action = getChangeInputValue(e.target.value);
        store.dispatch(action)
    }

    handleAdd() {
        const action = getAddListItem();
        store.dispatch(action)
    }

    handleDeleteItem(index) {
        const action = getDeleteListItem(index);
        store.dispatch(action);
    }

    handleStore() {
        this.setState(store.getState());
    }

}

export default TodoList;
6-2 无状态组件

当一个组件只有 render 函数的时候,那么就可以把这个组件改写为无状态组件
优势:性能比较高
无状态组件是一个函数
而一般组件是声明的一个类,这个类里面还有一些生命周期函数,所以执行起来不仅需=要执行类还要执行 render
那么什么时候去用无状态组件呢?
当我们定义 UI 组件的时候,因为没有业务逻辑,只有一个 render,所以一般在情况下,在 UI 组件中我们使用无状态组件比较多一些

//无状态组件
import React from 'react'
import {Button, Input, List} from "antd";

const TodoListUi = (props) => {
    return (
        <div style={{marginTop: '10px', marginLeft: '10px'}}>
            <Input
                value={props.inputValue}
                placeholder="todo info"
                style={{width: '300px', marginRight: '10px'}}
                onChange={props.handleInputValue}
            />
            <Button type="primary" onClick={props.handleAdd}>提交</Button>
            <List
                style={{marginTop: '10px', width: '300px'}}
                bordered
                dataSource={props.list}
                renderItem={(item, index) => (
                    <List.Item onClick={() => props.handleDeleteItem(index)}>{item}</List.Item>)}
            ></List>
        </div>
    )
};

export default TodoListUi;
6-3 Redux 中发送异步请求获取数据
//在actionTypes.js中创建一个变量
export const INIT_LIST_DATA = 'init_list_data';

//在actionCreators.js中创建一个action
export const getTodoListData = (data) => ({
    type: INIT_LIST_DATA,
    data
});

/TodoList.js
import axios from 'axios';
import {getTodoListData} from './store/actionCreators';
componentDidMount() {
        axios.get('/api/todoList')
            .then((res) => {
                const data = res.data;
                const action = getTodoListData(data);
                store.dispatch(action)
            })
}

//reducer.js
import {INIT_LIST_DATA} from './actionTypes.js'
const defaultState = {
  inputValue:'',
  list:[]
}
export default (state = defaultState, action) =>{
     if(action.type === INIT_LIST_DATA){
      const newState = JSON.parse(JSON.stringify(state))
      newState.list = action.data
      return newState
    }
    return state
}
6-4 使用 Redux-thunk 中间件实现 ajax 数据请求

1.Redux-thunk 安装 以及 redux-Devtools 的配置

//安装
npm install redux-thunk -S

//redux-Devtools的配置 store文件下的index.js
import {createStore, applyMiddleware, compose} from 'redux';
import reducer from './reducer';
import thunk from 'redux-thunk';

const composeEnhancers =
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;

const enhancer = composeEnhancers(
    applyMiddleware(thunk),
);

const store = createStore(reducer, enhancer);

export default store

配置参考地址链接:redux-devtools-extension

2.redux-thunk 在程序中的应用
为什么使用 redux-thunk 这个 redux 中间件?
第一:可以把数据操作和数据请求操作从 React Component 中搬到 ActionCreators.js 里面,不会是的组件显得那么拥堵
第二:便于后期的单元测试

//actionCreators.js中的修改 
import axios from 'axios';
export const getTodoListDataAction = (data) => ({
    type: INIT_LIST_DATA,
    data
});

export const getListData = () => {
    //redux-thunk 返回action是一个函数的时候,且放到了action里面进行操作的
    return (dispatch) => {
        axios.get('/api/todoList')
            .then((res) => {
                const data = res.data;
                const action = getTodoListDataAction(data);
                dispatch(action)
            })
    }
};

//TodoList.js中的修改
import { getListData } from './store/actionCreators';
componentDidMount() {
        const action = getListData();
        store.dispatch(action);
}
6-5 什么是 Redux 的中间件

redux 数据工作流

redux-thunk 其实是对 store.dispatch(action)方法的一个封装和升级,是把异步请求的操作放到了 action 当中进行操作。
在没有使用 redux-thunk 的时候,定义的 action 是一个对象
使用 redux-thunk 之后,定义的 action 不仅可以是对象,而且还可以可以是一个函数
######其他 redux 中间件:
redux-logger:可以记录 action 每次派发的日志
redux-saga:也是解决 react 中异步的一个中间件,单独的把异步的操作放到一个文件中进行操作

6-8 Redux-saga 中间件入门

1.redux-sage 的安装和配置

//安装
npm install redux-saga -S

//配置是在 store文件下面的index.js中
import {createStore, applyMiddleware, compose} from 'redux';
import reducer from './reducer';
// import thunk from 'redux-thunk';
import createSagaMiddleware from 'redux-saga';
import todoSaga from './sagas'

const sagaMiddleware = createSagaMiddleware();



const composeEnhancers =
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;

const enhancer = composeEnhancers(
    applyMiddleware(sagaMiddleware),
);

const store = createStore(reducer, enhancer);
sagaMiddleware.run(todoSaga);

export default store

//sagas.js

配置参考地址链接:redux-saga

2.redux-saga 在程序中的应用

//TodoList.js
componentDidMount() {
        const action = getTodoListData();
        store.dispatch(action);
}

//actionTypes.js
export const INIT_LIST_DATA = 'init_list_data';

//actionCreators.js
import {INIT_LIST_DATA} from './actionTypes';
export const getTodoListData = (data) => ({
    type: INIT_LIST_DATA,
    data
});

//reducer.js
import {INIT_LIST_DATA} from './actionTypes';
const defaultState = {
    inputValue: '',
    list: []
};
export default (state = defaultState, action) => {
    // console.log(state, action);
    if (action.type === INIT_LIST_DATA) {
        const newState = JSON.parse(JSON.stringify(state));
        newState.list = action.data;
        return newState
    }
    return state
}

//sagas.js
import {takeEvery, put} from 'redux-saga/effects';
import {INIT_LIST_DATA} from './actionTypes';
import {getTodoListData} from './actionCreators';
import axios from 'axios';

function* getListData() {
    try {
        const res = yield axios.get('/api/todoList');
        const action = getTodoListData(res.data);
        yield put(action)
    } catch(e) {
        console.log('todoList 网络异常')
    }
}

function* todoSaga() {
    yield takeEvery(INIT_LIST_DATA, getListData);
}

export default todoSaga;

总结:
1).ajax 请求
不采用 Promise 那种形式了(.then),而是通过 yield 来等待返回的结果
2).接受或者监听 Action 的
通过的 takeEvery,检测到变量名称,触发一个 generator 函数
3).派发请求
不采用 store.dispatch(), 而是通过的是 put()
4).出了 takeEvery、put 方法还有(takeLatest、call 等等多种 API)

6-9 如何使用 React-redux

react-redux 核心 API 有哪些?
1).Provider:就是一个连接器的组件,因为 Provider 和 store 做了关联,所以 Provider 这些内部的组件都可以获取到 store 里面的数据内容了

//安装react-redux
npm install react-redux -S

//使用 在src文件下面的index.js文件进行编写
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider }  from 'react-redux'
import TodoList from './TodoList'
import store from './store'

const App = (
      <Provider store={store}>
        <TodoList />
      </Provider>
)

ReactDOM.render(App,document.getElementById('root'))

2).connect: 是 React Component 调用 react-redux 的 connect 方法,使得组件和 store 关联起来,并且能对 state 进行设置和修改

import React,{ Component } from 'react'
import {connect} from 'react-redux;

class TodoList extends Component {
    render() {
      return (
          <div>
                 <div>
                    <input 
                        value={this.props.inputValue} 
                        onChange={this.props.handleInputChange}
                    />
                    <button>提交</button>
                </div>
                <div>
                    <ul><li>hello</li></ul>
                </div>
          </div>
      )
    }
}
const mapStateToProps = (state) => {
    return {
        inputValue: state.inputValue
    }
}

//store.dispatch,props
mapDispatchToProps = (dispatch) => {
      return {
          handleInputChange(e) {
              const action = {
                  type:'change_input_value',
                  value: e.target.value
               }
              dispatch(action)
          }
      }
}

export default connect(mapStateToProps,mapDispatchToProps)(TodoList)
6-12 最终 TodoList 功能

通过 react 官网提供的脚手架工具(creat-react-app)来搭建项目
1).采用了 react 全家桶:
react
react-dom
react-redux
redux
redux-thunk
2).ajax 请求
axios
3).项目目录
项目目录
4).代码展示

//src文件下的 index.js
import React from 'react'
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import TodoList from './TodoList'
import store from './store'

const App = (
    <Provider store={store}>
        <TodoList/>
    </Provider>
)

ReactDOM.render(App, document.getElementById('root'));

//TodoList.js
import React, {Component} from 'react'
import {connect} from "react-redux";
import {getInputValueAction, getHandleClickAction, getDeleteItemAction, getListDataApi} from './store/actionCreators'

class TodoList extends Component {
    render() {
        const {inputValue, list, handleInputChange, handleClick, handleDelete} = this.props;
        return (
            <div>
                <div>
                    <input
                        type="text"
                        value={inputValue}
                        onChange={handleInputChange}
                    />
                    <button onClick={handleClick}>提交</button>
                </div>
                <div>
                    <ul>
                        {
                            list.map((item, index) => (
                                <li key={index} onClick={() => handleDelete(index)}>{item}</li>
                            ))
                        }
                    </ul>
                </div>
            </div>
        )
    }

    componentDidMount() {
        this.props.getListData()
    }
}

const mapStateToProps = (state) => {
    return {
        inputValue: state.inputValue,
        list: state.list
    }
};

const mapDispatchToProps = (dispatch) => {
    return {
        handleInputChange(e) {
            const action = getInputValueAction(e.target.value);
            dispatch(action)
        },
        handleClick() {
            const action = getHandleClickAction();
            dispatch(action)
        },
        handleDelete(index) {
            const action = getDeleteItemAction(index);
            dispatch(action)
        },
        getListData() {
            const action = getListDataApi();
            dispatch(action);
        }
    }
};

export default connect(mapStateToProps, mapDispatchToProps)(TodoList);

//store 文件下的index.js
import {createStore, applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducer'

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;

const enhancer = composeEnhancers(applyMiddleware(thunk));
const store = createStore(reducer, enhancer);

export default store;

//store 文件下的reducer.js
import {CHANGE_INPUT_VALUE, ADD_ITEM, DELETE_ITEM, GET_LIST_DATA} from './actionTypes';

const defaultState = {
    inputValue: '',
    list: []
};
export default (state = defaultState, action) => {
    if (action.type === CHANGE_INPUT_VALUE) {
        const newState = JSON.parse(JSON.stringify(state));
        newState.inputValue = action.value;
        return newState
    }
    if (action.type === ADD_ITEM) {
        const newState = JSON.parse(JSON.stringify(state));
        newState.list.push(newState.inputValue);
        newState.inputValue = '';
        return newState
    }
    if (action.type === DELETE_ITEM) {
        const newState = JSON.parse(JSON.stringify(state));
        newState.list.splice(action.index, 1);
        return newState
    }
    if (action.type === GET_LIST_DATA) {
        const newState = JSON.parse(JSON.stringify(state));
        newState.list = action.data;
        return newState
    }
    return state
}

//store 文件下的actionTypes.js
export const CHANGE_INPUT_VALUE = 'change_input_value';
export const ADD_ITEM = 'add_item';
export const DELETE_ITEM = 'delete_item';
export const GET_LIST_DATA = 'get_list_data';

//store 文件下的actionCreators.js
import axios from 'axios';
import {CHANGE_INPUT_VALUE, ADD_ITEM, DELETE_ITEM, GET_LIST_DATA} from './actionTypes';

export const getInputValueAction = (value) => ({
    type: CHANGE_INPUT_VALUE,
    value
});

export const getHandleClickAction = () => ({
    type: ADD_ITEM
});

export const getDeleteItemAction = (index) => ({
    type: DELETE_ITEM,
    index
});

export const getListDataAction = (data) => ({
    type: GET_LIST_DATA,
    data
});

export const getListDataApi = () => {
    return (dispatch) => {
        axios.get('/api/todoList')
            .then(res => {
                const data = res.data;
                const action = getListDataAction(data);
                dispatch(action)
            })
            .catch((e) => {
                console.log('/api/todoList 网络异常')
            })
    }
};

第 7 章 项目实战中的一些技巧

7-1 styled-components 的应用

在写 react 组件的时候,为了防止样式被污染到,我们可以通过 styled-components 自定义标签以及样式。

//1.安装 styled-components
npm install styled-components -S

//2.初步使用方法,创建一个style.js文件
import styled from 'styled-components';

export const Nav = styled.div`
  width:1000px;
  margin: 0 auto;
  height: 50px;
  line-height: 50px;
  &.txtColor{
    color:red
  }
`
组件中引用
import {Nav} from './style.js'
<Nav className="txtColor">

//3.attrs属性
export const NavItem = styled.a.attrs({
  href: '/'
})`
  //样式
`
export const NavItem = styled.input.attrs({
  placeholder: '搜索'
})`
  //样式
`

//4.嵌套的应用
import { Nav,NavItem} from './style.js'
<Nav>
  <NavItem className="bg">首页</NavItem>
</Nav>
export const Nav = styled.div`
  width:1000px;
  margin: 0 auto;
  height: 50px;
  line-height: 50px;
  &.txtColor{
    color:red
  }
  //嵌套写法
  .bg{
      background: red
  }
`

//5.全局样式的使用(createGlobalStyle),比如reset.css、iconfont.css等等
export const GlobalStyle = createGlobalStyle`
//reset.css内容 或者 iconfont.css 内容等等
`;
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import Main from './Main';
import store from './store';
import {GlobalStyle} from './style';
import {GlobalStyle2} from './statics/font/iconfont'

const App = (
    <Provider store={store}>
        <GlobalStyle/> //这个地方就可以设置为全局样式了
        <GlobalStyle2/>//这个地方就可以设置为全局样式了
        <Main/>
    </Provider>
);

ReactDOM.render(App, document.getElementById('root'));

//6.参数传递和获取
应用场景,当我们都在循环一个列表的数据的时候,需要传递这个img作为它的背景图片
<Toppic imgUrl = '......png'></Topic>
<Toppic imgUrl = '......png'></Topic>

import styled from 'styled-components'
export const Topic = styled.div`
  background:url(${(props)=>props.imgUrl})
`;

上面是 styled-components 的一些常用的使用方法,要是想学习和了解更多。
styled-components 更多学习和了解地址

7-2 redux 中 combinReducers 的使用

在开发过程中,我们不可能把所有的 reducer 放到一个文件里面,那么肯定需要对 reducer 进行拆分的,但是拆分后的 reducer 最后我们肯定需要在合并到一起呢,因为在 redux 在创建 store 的时候,需要 reducer 的集合作为入参的。所有合并 reducer 就诞生了 combinReducers

import { combinReducers } from 'reducer'
import {reducer as headerReducer} from '../common/header/store'
import {reducer as footerReducer} from '../common/footer/store'
...

const reducer = combinReducers({
  header: headerReducer,
  footer: footerReducer
  ...
})
export default reducer

ps: 调用的时候注意了

const mapState = (state) => {
    return {
        focused: state.header.focused //调用时候,你加上你header或者footer
    }
};
7-3 store 的拆分

store 的拆分

上面我们写 TodoList 的 demo 的是,因为只涉及两个页面,所以不会考虑到 store 的拆分,但是在我们制作项目的时候,我们就的考虑 store 的拆分了

1.最外层的 store 文件(store 总中心):(index.js 和 reducer.js)
index.js:创建 store,并且把 store 和 reducer 链接起来,而且配置了 redux-devtools 可以让我们在 chrome 里面看到 redux 的变化
reducer.js: 把项目中各个地方的 reducer 通过 combinReducers 方便合并起来,把合并的最终结果 reducer,暴露出去

//index.js
import {createStore, applyMiddleware, compose} from "redux";
import reducer from './reducer';
import thunk from 'redux-thunk';

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;

const enhancer = composeEnhancers(
    applyMiddleware(thunk),
);
const store = createStore(reducer, enhancer);

export default store;

//reducer.js
import {combineReducers} from "redux";
import {reducer as headerReducer} from '../common/header/store'

const reducer = combineReducers({
    header: headerReducer
});

export default reducer;

2.组件中的 store 文件(碎片 store,那公共 header 为例)的拆分:
index.js: 把下面三个文件整合到一起,并且暴露出去,为了更方便的引用
constants.js: 定义了一些大写的常量,且暴露出去这些常量
actionCreators.js: header 组件中 action 的一些方法都放在这个里面,且暴露出去
reducer.js: header 组件中的 reducer 操作放在这个里面,且暴露出去

//index.js:
import reducer from './reducer';
import * as constants from './constants';
import * as actionCreators from './actionCreators';
export {
    reducer, //为了总store中reducer更方便的引入
    constants,//为了actionCreator更方便的引入
    actionCreators//为了组件中更方便的引入
}
//总store中reducer引入的时候:import {reducer as headerReducer} from '../common/header/store'
//actionCreator引入的时候:import {constants} from './index';
//header组件引入的时候:import {actionCreators} from './store';

//constants.js
export const INPUT_FOCUSED = 'header/input_focused';
export const INPUT_BLUR = 'header/input_blur';

//actionCreators.js
import {constants} from './index';
export const getInputFocusedAction = () => ({
    type: constants.INPUT_FOCUSED
});
export const getInputBlurAction = () => ({
    type: constants.INPUT_BLUR
});
7-4 使用 Immutable.js 来管理 store 中的数据

为了确保原始 state 不会被修改,导致的一些问题。所以我们引入了 Immutable.js 来更好维护我们的原始 state 数据

//1.安装 immutable 
npm install immutable -S

//2.使用 immutable中的fromJS 可以把 js对象转变为 immutable对象
import {constants} from './index';
import { fromJS } from 'immutable';
const defaultState = fromJS({
  focused:false
})

//3.设置  更改state里面的 immutable数据那么就需要.set()方法
//immutable对象的set方法,会结合之前immutable对象的值和设置的值,返回一个全新的对象
export default (state = defaultState,action) =>{
    if(action.type === constants.INPUT_FOCUSED)  {
        return state.set('focused',true)
    }
   if(action.type === constants.GET_HEADER_LIST)  {
        //return state.set('list', ).set('totalPage', );
        //改为 state.merge()方法的
         return state.merge({
                list: action.data,
                totalPage:action.totalPage
         });
    }
    return state
}

//4.获取   要使用immutable数据 要通过.get方法
const mapState = (state) =>{
  return {
    focused: state.header.get('focused')
  }
}

//5.获取  当需要把immutable对象转化为 普通的js对象时候
const {list} = this.props
const newList = list.toJS() //toJS方法的使用
7-5 使用 redux-immutable 统一数据格式

上一小节说到,我们将 state 初始化的数据通过 immutable 这个库变成了 immutable 对象,确保了 state 里面数据的稳定性,但是呢,在我们组件去获得 immutable 的时候:
focused: state.header.get('focused')中
state.header 是“js 对象”
而后面的.get('focused')则是“immutable 对象”
这样看的有些不统一,那么如何把 state.header 也变成 immutable 对象呢?那么我们就去看那个地方设置 state.header

//安装redux-immutable
npm install redux-immutable -S

//在最外层的reducer.js 文件对跟reducer进行设置
将
import {combineReducers} from "redux";
改为
import {combineReducers} from "redux-immutable";//redux 改为 redux-immutable
import {reducer as headerReducer} from '../common/header/store'

const reducer = combineReducers({
    header: headerReducer
});
export default reducer;


//优化代码
const mapState = (state) => {
    return {
        focused: state.get(header).get('focused')
    }
};
改为 连续调用两次get方法通过getIn方法一次实现
const mapState = (state) => {
    return {
        focused: state.getIn(['header','focused'])
    }
};

ps:学习了解更多 immutable 的知识链接

7-6 避免无意义的请求发送,提升组件性能

有些数据并不是我们每次点击就去请求接口,需要我们初次点击的时候,请求一次接口,随后点击就不请求了,那么就要加一些判断限制一下

const {list} = this.props

//当去请求一个列表的时候,如果初始的数据为0,那么我去请求一次接口
(list.length === 0) && dispatch(action)

ps:这种只是项目中的一种情况,我们在开发过程中呢,要结合项目开发功能,来写不同的判断来减少或者没必要的接口请求
7-7 什么是路由,如何在 React 中使用路由功能

我们使用的是 react-router-dom 来做路由的

//安装 react-router-dom
npm install react-router-dom -S

//使用
import React, {Component} from 'react';
import {Provider} from 'react-redux';
import {BrowserRouter, Route} from 'react-router-dom';
import store from './store';
import {GlobalStyle} from './style';
import {GlobalStyle2} from './statics/font/iconfont'
import Header from './common/header';
import Home from './pages/Home';
import Detail from './pages/Detail';

class App extends Component {
    render() {
        return (
            <Provider store={store}>
                <GlobalStyle/>
                <GlobalStyle2/>
                <Header/>
                <BrowserRouter>
                    <div>
                        <Route path='/' exact component={Home}></Route>
                        <Route path='/detail' exact component={Detail}></Route>
                    </div>
                </BrowserRouter>
            </Provider>
        )
    }
}
export default App;

2.单页路由的跳转,通过 Link 的 to 属性进行页面的跳转
单页跳转的好处,减少 http 请求,请求速度会很快

import {Link} from 'react-router-dom';
<Link to='/'>
  <Logo />
</Link>

3.页面路由参数的传递
1)、动态路由获取参数
http://localhost:3000/detail/1

//配置路由
<Route path='/detail/:id' exact component={Detail}></Route>

//获取Url上面的1这个参数值
this.props.match.params.id

2)、非动态路由获取参数
http://localhost:3000/detail?id=1

//配置路由
<Route path='/detail' exact component={Detail}></Route>

//获取Url上面的1这个参数值
const id = this.props.location.search;   //search: ?id=2
再对id进行数据的处理才能拿到最终我们想要的值
7-8 PureComponent 的应用
import React,{Component} from 'react';
改为 
import React,{PureComponent} from 'react';

为了考虑 react 的性能优化,需要我们对变更的数据的进行监测,采用 shouldComponentUpdate 对组件进行优化,如果这样的话,就需要写很多 shouldComponentUpdate 的代码,那么 react 中这个 PureComponent 组件 就是自动帮助我们做这部分优化功能的。
注释:如果采用 PureComponent 这个组件那么,必须保证你的数据是 immutable 的

  • React

    React 是 Facebook 开源的一个用于构建 UI 的 JavaScript 库。

    192 引用 • 291 回帖 • 370 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...