1.React介绍

7 表单中的受控组件与非受控组件

非受控组件

受控组件

HOC高阶组件

在React组件的构建过程中,常常有这样的场景,有一类功能需要被不同的组件公用。(vue中使用mixins)

HOC不仅仅是一个方法,确切说应该是一个组件工厂,获取低阶组件(功能较少),生成高阶组件(添加多个组件复用的功能)

HOC–参数是组件同时返回值也是组件

高阶组件(HOC)是 React 中用于重用组件逻辑的高级技术。 HOC 本身不是 React API 的一部分。 它们是从 React 构思本质中浮现出来的一种模式。

例子:

比如在多个函数中我们都想使用一个相同的方法。这个方法就是自动发出一段异步请求 并且把请求成功的数据赋值给一个state在页面展示。

那么传统方式我们就需要把这个请求在每个组件中都是用一次 组件少了可以。但是组件变多就很麻烦 那么我们就需要想办法解决这个重复的代码 所以我们可以使用HOC来进行组件中公共内容的复用

大家发现下面的例子 这个请求可能会在多个组件都要使用 那么我们可以把他设置成一个HOC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import axios from 'axios'
import React, { Component } from 'react'

export default class two extends Component {
state={
text:""
}
// 发送请求
async componentDidMount (){
let {data}= await axios({url:"/api/data/cityinfo/101320101.html"})
// 把数据赋值给state 并且在下方展示
this.setState({
text:data.weatherinfo.city
})
}

render() {
return (
<div>
{this.state.text}
</div>
)
}
}

HOC创建

1.新建withxxx文件 用来容纳HOC(高阶组件明明规则 是withxxx 比如withRouter)

1
2
3
4
// HOC本质就是一个函数
export default ()=>{

}

2.使用HOC 在需要服用的组件中把刚才封装的HOC引用使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import axios from 'axios'
import React, { Component } from 'react'
// 1.引用高阶组件
import withLink from "./withLink.js"
// 2.修改暴漏在最下面
class two extends Component {
state={
text:""
}

async componentDidMount (){
let {data}= await axios({url:"/api/data/cityinfo/101320101.html"})

this.setState({
text:data.weatherinfo.city
})
}

render() {
return (
<div>
{this.state.text}
</div>
)
}
}
// 3.把当前组件当成函数的实参传入进去(因为高阶组件参数是一个组件)
export default withLink(two)

3.在高阶组件中设置行参接受传递进来的组件并且返回一个组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// HOC本质就是一个函数

import React from "react"

// 设置行参接受(首字母要大写)
export default (Component)=>{
// 并且返回值函数一个组件(组件可以是函数组件也可以是类组件)
return class withLinkComponent extends React.Component{

render(){
return (
<>

</>
)
}
}
}

4.移植逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// HOC本质就是一个函数

import React from "react"
import axios from "axios"

// 设置行参接受(首字母要大写)
export default (Component) => {
// 并且返回值函数一个组件(组件可以是函数组件也可以是类组件)
return class withLinkComponent extends React.Component {

// 把之前的逻辑放置进来
state = {
text: ""
}

async componentDidMount() {
let { data } = await axios({ url: "/api/data/cityinfo/101320101.html" })

this.setState({
text: data.weatherinfo.city
})
}
// 把之前的逻辑放置进来

render() {
return (
<>
{/* 把数据传递出去 */}
<Component {...this.state}></Component>
</>
)
}
}
}

组件中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import axios from 'axios'
import React, { Component } from 'react'
// 1.引用高阶组件
import withLink from "./withLink.js"
// 2.修改暴漏在最下面
class two extends Component {
render() {
return (
<div>
{/* 组件内使用高阶组件的数据 */}
{this.props.text}
</div>
)
}
}
// 3.把当前组件当成函数的实参传入进去(因为高阶组件参数是一个组件)
export default withLink(two)

高阶组件—反向继承

HOC的反向继承 说的简单点就是渲染劫持 在使用HOC的时候可以进行一个高阶组件中的条件渲染

8.路由

根据url的不同来切换对应的组件页面

路由可以实现spa单页面应用 一个项目只有一个完整的页面 我们通过切换页面的显示内容 已达到不刷新页面进行切换的效果

路由分类

react-router库

仅仅只包含了路由最基本的功能没有一些辅助的api 但是他轻量级

react-router-dom库

除了基本的路由功能以外 还有很多便捷性的api方便我们开发者实现路由功能

1
npm install --save react-router-dom@5

路由模式

HashRouter

带# 不会丢失

BrowerRouter

不带# 上线刷新404

实现

1.下载 npm install –save react-router-dom@5

2.设置路由模式 index.js中设置

1
2
3
4
5
6
7
8
9
10
11
12
13
import React from 'react';
import ReactDOM from 'react-dom';

// 1.引用路由模式
import {HashRouter} from "react-router-dom"

ReactDOM.render(
// 2.设置路由模式 包裹根组件
<HashRouter>
<xxxx></xxxx>
</HashRouter>,

document.getElementById("root"))

3.开始设置路由页面 写在views或者pages

4.设置路由规则与出口 新建router文件夹在新建index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import React, { Component } from 'react'
// 1.把你要用的路由页面进行引用
import Home from "../views/home.jsx"
import Phone from "../views/phone.jsx"
import User from "../views/user.jsx"
import Shop from "../views/shop.jsx"
// 2-1 引用Route
import {Route} from "react-router-dom"

export default class index extends Component {
render() {
return (
<>
{/* 路由规则与路由出口 */}
{/* <Route path="/你的路径" component={你要引用的组件}/> */}
{/* 2-2.配置出口与规则 */}
<Route path="/home" component={Home}/>
<Route path="/shop" component={Shop}/>
<Route path="/user" component={User}/>
<Route path="/phone" component={Phone}/>


</>
)
}
}

5 设置路由配置组件为根组件index.js中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React from 'react';
import ReactDOM from 'react-dom';
// 引用路由配置组件
import Index from "./router/index.js"

// 1.引用路由模式
import {HashRouter} from "react-router-dom"

ReactDOM.render(
// 2.设置路由模式 包裹根组件
<HashRouter>
{/* 注入路由组件 */}
<Index></Index>
</HashRouter>,

document.getElementById("root"))

路由导航

声明式

<关键字 to=”去哪里”></关键字>

Link 就是一个组基本的路由导航

NavLink 处理基本路由跳转的功能以外 还添加了自动选中类名的设置 active类名

但是 如果这个active的类名 已经存在了怎么办?

修改navlink的选中类名 activeClassName=”你要修改的类名”

注意

有的同学navlink可能在电脑上不加类名 如果出现这种问题 那么就不要用vscode内置的cmd打开项目 而是用外部的cmd启动项目即可解决

编程试

push方法在路由页面中跳转 this.props.history.push(“/xxxx”)

常见问题

如果编程式导航中跳转的话 那么会出现 push of undefined的错误

原因:

是因为编程式导航只能在被路由所管理的页面中进行使用(被管理的页面是指 在路由规则中配置过的页面)因为不是被路由所管理的页面就不具备路由所提供的 三个属性(location match history)

所以就不能使用this.props.history 所以就会报错

解决方式:

使用withRouter 高阶组件(HOC)来解决 因为通过withRouter 可以让不是理由所跳转的页面也具有路由的三个属性

使用:

1.引用withRouter

1
import {withRouter} from "react-router-dom"

2.修改组件的export default 到最下面

3.使用withRouter来设置当前组件

1
export default withRouter(当前组件)

更多跳转方式

replace() 替换当前路径

goBack()后退

goForward()前进

二级多级路由

1.编写二级路由页面

2.配置规则与出口 只需要在对应的一级路由页面中进行route的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React, { Component } from 'react'

import {Route,NavLink} from "react-router-dom"

import Era from "./er/era.jsx"
import Erc from "./er/erc.jsx"
export default class phone extends Component {
render() {
return (
<div>
phone
<NavLink to="/phone/era">era</NavLink>
<NavLink to="/phone/erc">erc</NavLink>

{/* 配置二级路由规则与出口 */}
<Route path="/phone/era" component={Era}/>
<Route path="/phone/erc" component={Erc}/>
</div>
)
}
}

404页面

1.创建页面

2.配置404页面规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<>
{/* 路由导航的组件 */}
<Bb></Bb>
{/* 路由规则与路由出口 */}
{/* <Route path="/你的路径" component={你要引用的组件}/> */}
{/* 2-2.配置出口与规则 */}
<Route path="/home" component={Home}/>
<Route path="/shop" component={Shop}/>
<Route path="/user" component={User}/>
<Route path="/phone" component={Phone}/>

{/* 404页面必须放在最下面 */}
<Route component={No}/>

</>

大家会发现当我们加入了404页面之后 每个页面都会把404显示出来 因为reacrt的路由在匹配到之后 还会一直向下进行 直到最后

switch 唯一渲染

用switch包裹的内容会渲染上之后不继续向下进行 只会匹配到第一个匹配的内容

1
2
3
4
5
6
7
8
9
10
11
{/* 唯一渲染 */}
<Switch>
{/* 2-2.配置出口与规则 */}
<Route path="/home" component={Home}/>
<Route path="/shop" component={Shop}/>
<Route path="/user" component={User}/>
<Route path="/phone" component={Phone}/>

{/* 404页面必须放在最下面 */}
<Route component={No}/>
</Switch>

重定向与精准匹配

重新定位方向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{/* 唯一渲染 */}
<Switch>
{/* 2-2.配置出口与规则 */}
<Route path="/home" component={Home}/>
<Route path="/shop" component={Shop}/>
<Route path="/user" component={User}/>
<Route path="/phone" component={Phone}/>

{/* 重定向 */}
{/* 精准匹配 exact */}
<Redirect from="/" to="/home" exact/>
{/* 404页面必须放在最下面 */}
<Route component={No}/>
</Switch>

路由传参

params方式

1.配置路径

1
<Route path="/home/:xxx" component={Home}>

2.发送

声明式

1
<NavLink to="/phone/数据"></NavLink>

编程式

1
this.props.history,push("/phone/数据")

3.接收

1
this.props.match.params.name

优势 : 刷新地址栏,参数依然存在

缺点 : 只能传字符串,并且,如果传的值太多的话,url会变得长而丑陋。

query方式

1.发送

1
<Link to={{ pathname : '/d' , query : { name : 'sunny' }}}>点我去d</Link>

2.接受

1
this.props.location.query.name

优势:传参优雅,传递参数可传对象;

缺点:刷新地址栏,参数丢失

state方式

1.发送

1
<Link to={{ pathname : '/d' , state: { name : 'sunny' }}}>点我去d</Link>

2.接受

1
this.props.location.state.name

优势:传参优雅地址栏不显示传递的数据,传递参数可传对象;

缺点:刷新地址栏,参数丢失

路由拦截

render调用一个函数那么我们就可以决定什么时候渲染他 同时传入props那么就可以在路由组件中使用history: {…}, location: {…}, match: {…}这几个对象

1
<Route path="/home" render={(props)=>{return <Home {...props}/>}}>

9.数据请求

扩展–数据请求在那个地方自动发送

在react16x之后componentWillMount会有一个问题 就是可能会被执行多次 所以最好不要在当前钩子中进行数据请求的发送

最好在componentDidMount中进行数据请求的发送

axios

同vue

原生jquery等等方式

1.下载jquery npm install –save jquery

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React, { Component } from 'react'
// 1.引用
import $ from "jquery"
export default class democ extends Component {
componentDidMount(){
$.ajax({
url:"http://localhost:8888/one",
type:"GET",
success(ok){
console.log(ok)
}
})
}
render() {
return (
<div>democ</div>
)
}
}

fetch Es提供的最新方式

fetch 最新的前后台异步数据交互的方式 传统的方式 axios或者是原生 jqueryajax 都是基于XMLHttpRequest方法来进行实现的 但是fetch不是因为Es最新规范中给我们提供了fetchAPI通过这个fetch

Api来进行前后台的数据交互

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React, { Component } from 'react'

export default class demob extends Component {
componentWillMount(){
console.log("will")
}
componentDidMount() {
console.log("did")
// fetch使用
fetch("http://localhost:8888/one")
// 把数据转换成json
.then(ok=>ok.json())
.then((ok)=>{
console.log(ok)
})

}
render() {
return (
<div>demob</div>
)
}
}

其他的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import React, { Component } from 'react'

export default class demob extends Component {
componentWillMount(){
console.log("will")
}
componentDidMount() {
console.log("did")
// fetch使用
fetch("http://localhost:8888/one")
// 把数据转换成json
.then(ok=>ok.json())
.then((ok)=>{
console.log(ok)
})

// 发送数据get
fetch("url/?key=val&key=val",{method:"GET"})
// 把数据转换成json
.then(ok=>ok.json())
.then((ok)=>{
console.log(ok)
})

// 发送post
fetch(
"url",{
method:"POST",
body:"key=val&key=val"
}
)
// 把数据转换成json
.then(ok=>ok.json())
.then((ok)=>{
console.log(ok)
})

}
render() {
return (
<div>demob</div>
)
}
}

fetch VS axios VS ajax区别

1.传统的ajax 就是值使用XMLHttpRequest方法实现的数据请求 他隶属于原生的js 核心就是XMLHttpRequest对象 如果多个请求有先后顺序的话 那么容易造成回调地狱问题

jqueryajax 就是对原生XMLHttpRequest封装

2.axios 是基于promise封装的 本质上还是XMLHttpRequest的封装 只不过他是基于最新的语法进行封装的

3.fetch 就是原生js最新标准 和XMLHttpRequest没有半点关系

跨域

方式1

同vue一样也是对devServer进行代理跨域 但是唯一的不同的写跨域的文件不一样

需要到node_modules/react-scripts/config/webpackDevServer.config.js中进行配置

找到proxy 进行设置

1
2
3
4
5
6
7
8
9
proxy:{
"/api(可以随便写)":{
target:"请求地址",
changeOrigin:true,
"pathRewrite":{
"^/api(和上面一样)":"/"
}
}
},

但是不要忘了修改请求的地址为/api 与重启项目

方式2:http-proxy-middleware

1.下载:npm install http-proxy-middleware –save

2.在项目的src路径下创建setupProxy.js

3.写入如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
app.use(
'/api',
createProxyMiddleware({
target: 'http://www.weather.com.cn/',
changeOrigin: true,
pathRewrite:{
"^/api":"/"
}
})
);
};

json-server模拟数据

在项目中用来进行模拟数据的

1.下载 npm install -g json-server

2.查看版本 json-server –version

3.创建模拟数据文件夹mock 在其中写入数据的json文件

1
2
3
4
5
{
"one":[
{"name":"xiaoyang","age":18}
]
}

4.启动

(4-1)cd到mock文件夹下

(4-2)json-server –watch 你的json文件名字 –port 你的端口

疑问 上面这个名字太难记了 我怎么修改他?

1.cd到mock文件夹下 npm init -y 初始化一个package.json 文件

2。到package.json文件中 在scripts节点中设置你的启动命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"name": "mock",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
我是配置启动命令的
"xiaoming":"json-server --watch data.json --port 8877",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

  1. 启动 npm run 你的配置名字即可