React学习总结

第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-1 Redux 入门

-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的

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容