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"}) this.setState({ text:data.weatherinfo.city }) } render() { return ( <div> {this.state.text} </div> ) } }
|
HOC创建
1.新建withxxx文件 用来容纳HOC(高阶组件明明规则 是withxxx 比如withRouter)
1 2 3 4
| 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'
import withLink from "./withLink.js"
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> ) } }
export default withLink(two)
|
3.在高阶组件中设置行参接受传递进来的组件并且返回一个组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
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
|
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'
import withLink from "./withLink.js"
class two extends Component { render() { return ( <div> {/* 组件内使用高阶组件的数据 */} {this.props.text} </div> ) } }
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';
import {HashRouter} from "react-router-dom"
ReactDOM.render( <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'
import Home from "../views/home.jsx" import Phone from "../views/phone.jsx" import User from "../views/user.jsx" import Shop from "../views/shop.jsx"
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"
import {HashRouter} from "react-router-dom"
ReactDOM.render( <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="/home" component={Home}/> <Route path="/shop" component={Shop}/> <Route path="/user" component={User}/> <Route path="/phone" component={Phone}/>
{} <Route component={No}/>
</>
|
大家会发现当我们加入了404页面之后 每个页面都会把404显示出来 因为reacrt的路由在匹配到之后 还会一直向下进行 直到最后
switch 唯一渲染
用switch包裹的内容会渲染上之后不继续向下进行 只会匹配到第一个匹配的内容
1 2 3 4 5 6 7 8 9 10 11
| {} <Switch> {} <Route path="/home" component={Home}/> <Route path="/shop" component={Shop}/> <Route path="/user" component={User}/> <Route path="/phone" component={Phone}/>
{} <Route component={No}/> </Switch>
|
重定向与精准匹配
重新定位方向
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| {} <Switch> {} <Route path="/home" component={Home}/> <Route path="/shop" component={Shop}/> <Route path="/user" component={User}/> <Route path="/phone" component={Phone}/>
{} {} <Redirect from="/" to="/home" exact/> {} <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'
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("http://localhost:8888/one") .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("http://localhost:8888/one") .then(ok=>ok.json()) .then((ok)=>{ console.log(ok) })
fetch("url/?key=val&key=val",{method:"GET"}) .then(ok=>ok.json()) .then((ok)=>{ console.log(ok) })
fetch( "url",{ method:"POST", body:"key=val&key=val" } ) .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" }
|
- 启动 npm run 你的配置名字即可