传送门
eg:代表代码对照
若文章有误,欢迎读者指出
问题:在项目中某些情况下,子元素样式会受父元素样式的影响
传送门作用:让当前元素逃离父元素,去想去的地方【一般是body】
使用场景:
- overflow: hidden
- 父组件z - index值太小,逃离父组件
- fixed需要放在body第一层级
- loading 弹框 全局组件 也应该放在最外面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class PortalDemo extends Component { render() { return ( <div> <div> <div> {/* 当前div逃离当前父元素,然后去body下面,参数1 将要逃离的子元素,参数2 逃离到哪里去 */} {/* 注意:样式和功能不变,只是最终的渲染位置跑到指定的位置。 */} { ReactDOM.createPortal(<div className='portal-class'> <h3>我是标题</h3> <div>我是内容</div> </div>, document.body) } </div> </div> </div> ) } }
|
React中的Axios
Axios发请求
react中通过npm来安装axios插件
npm i -S axios
- 引入`axios包
挂载后,发送请求【组件渲染完成就开始发送请求,将请求到的数据渲染】
get方法请求[第一参数为请求接口地址,第二参数为对象,param形参名随意设定,但是里面params固定,简写{ params: id: 11, name: ‘aa’ }]
1 2 3
|
axios.get('https://api.i-lynn.cn/ip').then(res => this.setState({obj: res.data}))
|
post方法请求[第一参数为请求接口地址,第二参数为对象,简写{ firstName: ‘black’ }]
1 2 3
|
axios.post('https://api.i-lynn.cn/ip').then(res => this.setState({ obj: res.data }))
|
对象写法【常规写法】
1 2 3 4 5 6 7 8 9
| axios({ method: 'get', url: 'https://api.i-lynn.cn/ip', params: {}, headers: {} }).then(res => { this.setState({obj: res.data}) })
axios({ method: 'post', url: 'https://api.i-lynn.cn/ip', data: {}, headers: {} }).then(res => { this.setState({obj: res.data}) })
|
post方式也能使用params,拼接到url后面,具体原因分析:http://www.qianduanheidong.com/blog/article/319066/b5ef11c3754262a378ec/
eg1:
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
| import React, { Component } from 'react'
import axios from 'axios'
class AxiosDemo extends Component { state = { obj: {} } componentDidMount() { axios({ method: 'post', url: 'https://api.i-lynn.cn/ip', data: {}, headers: {} }).then(res => { this.setState({obj: res.data}) }) } render() { return ( <div> <h3>Axios发请求</h3> <p>{this.state.obj.area}</p> <p>{this.state.obj.country}</p> <p>{this.state.obj.ip}</p> </div> ) } }
export default AxiosDemo
|
post方式也能使用params,拼接到url后面,具体原因分析:http://www.qianduanheidong.com/blog/article/319066/b5ef11c3754262a378ec/
反向代理
反向代理用途:解决浏览器跨域问题
同源策略 http://localhost:8080 请求 https://api.i-lynn.cn/ip 协议域名端口号有一个不同就报跨域错误。
如何解决跨域:
- 前端: 反向代理
- 后端:
cors 反向代理
- 服务器:
linux 正向 反向代理
React的反向代理
- 安装代理插件
npm i -S http-proxy-middleware
- 在
src目录下,创建一个setupProxy.js文件【该文件不需要引入到哪里,配置完就可以使用了】
- 开始配置反向代理
setupProxy.js文件配置代码如下[可以配置多个代理,例如/bpi]:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const { createProxyMiddleware: proxy } = require('http-proxy-middleware')
module.exports = app => { app.use('/api', proxy({ target: 'https://api.i-lynn.cn', changeOrigin: true, pathRewrite: { '^/api': '' } })) }
|
如何使用[以get为例]:
1 2 3 4 5 6 7 8
| componentDidMount(){ axios.get('/api/ip').then(res=>{ this.setState({ obj:res.data }) }) }
|
没做反向代理前

做了反向代理后

异步组件
- 异步引入组件[执行时不加载,需要用到的时候加载]
1
| const Child = React.lazy(() => import('./Child'))
|
- 对比一下vue异步组件
1
| components: { Child: () => import('./Child') }
|
- 异步组件使用[使用React.Suspense标签包裹异步组件标签占位符]
fallback属性可选,fallback是异步引入的loading销毁,fallback可以放一些loading好看的效果,刷新几下可以看到效果
1 2 3
| <React.Suspense fallback={<h2>------loading--------</h2>}> <Child /> </React.Suspense>
|
- 对比同步组件[直接使用]
SCU性能优化
shouldComponentUpdate(简称SCU)
react里面父组件刷新,他所有子组件都会自动刷新,SCU可以解决某个子组件依赖数据不发生变化,而对其不刷新
SCU 一定要每次都用吗?—— 需要的时候才优化,没有特殊要求,可以先完成功能为主,后期再优化
num的变化,当前组件要刷新,但是Child组件依赖数据没有变化,所以Child组件可以不用刷新,性能提升的点。
模拟场景:父组件里放一个num、arr,当我们更改num,父组件更新子组件无条件更新,而子组件依赖于arr,我们对子组件做性能优化(让其不更新):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| state = { num: 0, arr: [1,2,3,4], }
addHandle = () => { this.setState({ num: this.state.num+1 }) } render() { return ( <div> <h3>SCU单性能优化</h3> <p>{this.state.num}</p> <button onClick={this.addHandle}>点击+1</button> <Child1 arr={this.state.arr}/> </div> ) }
|
我们要对Child1子组件做SCU,性能优化,this.props.arr数组没有改变,Child1组件中的render就不用执行。
- 需要用到
shouldComponentUpdate(nextProps, nextState)
SCU是render的开关,默认返回是true,默认情况下render是要被渲染的。
nextProps是最新的props,并不等于this.props,所以我们可以判断nextProps和this.props是否相等就可以判断出数据有没有变化。
nextState就是最新state.并不等于this.state这两个也可以进行SCU的比较。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| export default class Child extends Component { shouldComponentUpdate(nextProps, nextState) { if (nextProps.arr === this.props.arr) { return false } return true } render() { console.log('Child1子组件渲染了') return ( <div>Child1 <ul>{ this.props.arr.map((item, index) => { return <li key={index}>{item}</li> }) }</ul> </div> ) } }
|
上面只是浅比较,深比较【递归,借助第三方模块】
- 安装插件:
npm i -S lodash
- 子组件需要做
SCU引入import _ from 'lodash'
lodash工具类 _.isEqual(obj1,obj2)递归比较对象的值是否相等 相等返回true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| state = { num: 0, person: { name: '小明', jobs: ['前端', '后端'] } }
addHandle = () => { this.setState({ num: this.state.num+1 }) } render() { return ( <div> <h3>SCU单性能优化</h3> <p>{this.state.num}</p> <button onClick={this.addHandle}>点击+1</button> <Child3 person={this.state.person}/> </div> ) }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import _ from 'lodash' export default class Child2 extends Component { shouldComponentUpdate(nextProps, nextState) { var b = _.isEqual(nextProps.person, this.props.person) if (b) return false return true } render() { console.log('child2更新了') return ( <div>Child2 <p>{this.props.person.jobs}</p> </div> ) } }
|
类组件还自带一个浅比较的SCU,继承PureComponent来实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| state = { num: 0, arr: [1,2,3,4] } addHandle = () => { this.setState({ num: this.state.num+1 }) } render() { return ( <div> <h3>SCU单性能优化</h3> <p>{this.state.num}</p> <button onClick={this.addHandle}>点击+1</button> <Child3 arr={this.state.arr}/> </div> ) }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import React, { PureComponent } from 'react'
export default class Child3 extends PureComponent { render() { console.log('Child3子组件渲染了') return ( <div>Child3 <ul>{ this.props.arr.map((item, index) => { return <li key={index}>{item}</li> }) }</ul> </div> ) } }
|
函数组件可以使用memo高阶组件来实现浅比较的SCU
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| state = { num: 0, arr: [1,2,3,4] } addHandle = () => { this.setState({ num: this.state.num+1 }) } render() { return ( <div> <h3>SCU单性能优化</h3> <p>{this.state.num}</p> <button onClick={this.addHandle}>点击+1</button> <Child4 arr={this.state.arr}/> </div> ) }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import React, { memo } from 'react'
function Child5(props) { console.log('Child4 更新了') return <div>Child4 <ul>{ props.arr.map((item, index) => { return <li key={index}>{item}</li> }) }</ul> </div> } export default memo(Child4)
|
函数组件的深比较
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| state = { num: 0, jobs: ['前端', '后端'] } addHandle = () => { this.setState({ num: this.state.num+1 }) } render() { return ( <div> <h3>SCU单性能优化</h3> <p>{this.state.num}</p> <button onClick={this.addHandle}>点击+1</button> <Child5 person={this.state.person}/> </div> ) }
|
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, { memo } from 'react' import _ from 'lodash'
const Child6 = (props) => { console.log('Child5更新了') return ( <div>Child5 <p>{props.person.jobs}</p> </div> ) }
function dispatchObj(nextPros, props) { var b = _.isEqual(nextPros.person, props.person) if (b) { return true } else { return false } }
export default memo(Child5, dispatchObj)
|
SCU小结:
- 类组件自带一个浅比较继承自
PureComponent,不需要我们去做判断,同时如果对象只有一层我们也可以使用shouldComponentUpdate直接使用===比较,深比较依旧是可以借助递归或工具类lodash
- 函数组件需要通过
memo高阶组件来进行浅比较,不需要我们去做判断,深比较需要借助memo高阶组件和lodash
HOC高阶组件【函数组件】
- 高阶组件一般是函数组件,参数是组件,返回值也还是一个组件。
- 高阶组件是为了提取公共功能用的,公共逻辑。高阶组件写越得越多说明能力越强,质量越高。
- 封装高阶组件,创建函数组件,传入一个组件作为参数,return一个类组件,这个类组件相当于穿插到了父子组件之间
- 使用:引入高阶组件,把高阶组件当函数用就行了【哪个组件需要这个高阶组件功能就往哪里引入,调用】
父组件
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 Child from './Child'
class HOCDemo extends Component { state = { arr: [1, 2, 3, 4, 5] } render() { return ( <div> <h3>父组件</h3> <Child arr={this.state.arr} /> </div> ) } }
export default HOCDemo
|
开始封装高阶组件
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'
const HOC = (COM) => { return class newCom extends Component { render() { return ( <div> {/* {...this.props} 承上启下 高阶组件必须携带,否则出现父子通信中断的bug */} <COM {...this.props}/> </div> ) } } }
export default 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 React, { Component } from 'react'
import HOC from './HOC'
class Child extends Component { render() { return ( <div> <h3>子组件</h3> <ul> { this.props.arr.map((item, index) => { return <li key={index}>{item}</li> }) } </ul> </div> ) } }
export default HOC(Child)
|
开启装饰器模式,使用高阶组件语法糖@
装饰器模式的配置步骤:
配置装饰器支持
在当前项目根目录下面创建一个名称为config-overrides.js文件,对webpack进行配置
1 2 3 4 5 6 7 8 9 10 11
| const path = require("path") const {override,addDecoratorsLegacy,disableEsLint,addWebpackAlias} = require("customize-cra")
module.exports = override( disableEsLint(), addDecoratorsLegacy(), addWebpackAlias({ ["@"]: path.resolve(__dirname, "./src"), }) )
|
npm i -D customize-cra react-app-rewired
到package.json中的script命令中修改”start”: “react-app-rewired start”
会出现的bug:
对装饰器的实验支持功能在将来的版本中可能更改。在 “tsconfig” 或 “jsconfig” 中设置 “experimentalDecorators” 选项以删除此警告。ts(1219)(https://www.cnblogs.com/Annely/p/14613567.html)
- npm i @babel/plugin-proposal-decorators -D
- 项目根目录下创建babel.config.js 或者 .babelrc.js,复制粘贴如下代码:
1 2 3 4 5 6 7 8
| module.exports = { presets: [ ["@babel/preset-env"], ], plugins: [ ['@babel/plugin-proposal-decorators', { 'legacy': true }] ] };
|
- vscode的设置里搜索types打勾如下图

开启装饰器之后,使用语法糖:
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 HOC from './HOC'
@HOC class Child extends Component { render() { return ( <div> <h3>子组件</h3> <ul> { this.props.arr.map((item, index) => { return <li key={index}>{item}</li> }) } </ul> </div> ) } }
export default HOC(Child)
|