React 大杂烩

React 简介

React是一个用于构建用户界面的JavaScript库,主要用于构建UI,因此一般被用来MVC中V层。React 可使用JavaScript 也可使用JSX,JSX会被babel编译为React.createElement(),React.createElement()将返回一个“ReactElement”的JS对象,且使用JSX语法糖降低学习成本和开发效率。

React 优势

1.声明式设计 −React采用声明范式,可以轻松描述应用。

2.高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。

3.灵活 −React可以与已知的库或框架很好地配合。

4.JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。

5.组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。

6.单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。

React 函数调用

函数调用方法:
1.react 事件触发切记别带(),直接this.函数名
2.利用箭头函数:οnclick={()=>this.函数名(参数)},该方法比较便于传参 ;
3.在Dom节点加上‘{}’,并在其中直接写函数方法。

数据绑定(单向绑定)

1.绑定数据在input,首先通过定义变量const 自定义名 = React.createRef();其次在<input ref={this,自定义名} 实现绑定;
2.若有绑定事件,DOM节点触发事件最好用箭头函数,不然得用bind指向this,避免this指向undefined。例如 οnclick={this.函数名.bind(this)}

响应式数据

react 数据非Object.defineProperty 机制,非通过setter/getter劫持数据,它是通过diff进行VDOM重新渲染。在rend函数外,用内置state:{要定义的数据名:数据},然后在方法钟通过this.setState({定义的数据名:修改后的数据})。也可通过构造函数去注册函数,constructor(){ super();//必须写继承方法 this.state{自定义名:数据}},然后在方法钟通过this.setState({自定义名:修改后的数据})

状态管理小Tip

通过拓展运算符进行数据拷贝,将拷贝数据setState改变旧数组,其原理使用深拷贝避免直接操作原数据。

组件通信

父传子

子组件封装
export default class NavBar extends Component {
//状态管理
  state={
    show:0
  }
  render() {
  //对象转数组
    const List =Object.values(this.props);
    const Tem = List.map((item,index)=>(<li onClick={()=>{this.selected(index)} } className= { this.state.show ===index ? 'active NavItem':'NavItem'} key={index}>{item}</li>));
    return (
      <div>
            <ul className='NavBox'>
              {Tem}
            </ul>
      </div>
    )
  }
 
  selected(index){
      this.setState({
        show:index
      })   
  }
}
// 默认传参
NavBar.defaultProps={
  List:['首页']
}
父组件传参
import NavBar from '../comment/NavBar/NavBar';
import React from 'react';

export default function index() {
    const List =['首页','服饰','电器','食品','宠物','其他'];
    return ( 
            <>
            //通过拓展运算符传参
            <NavBar {...List}></NavBar>
            </>
                )
            }

子传父

//父组件引用NavBar子组件,定义在组件中定义函数接收子组件传来参数
<NavBar op={(参数)=>{代码块}}></NavBar>

//NavBar子组件传参
<button onClick=>{this.props.op(参数)}>子按钮</button>
组件化开发小Tip

组件尽量不设状态,直接使用父组件进行属性传参,避免循环渲染导致状态冲突。例如子组件有自我状态,如果父组件要求改变子组件状态,会导致两种状态并存。

非父子组件传参

1、通过父组件作为媒介,将主请求交给父组件,请求数据交予子组件,子组件间的交互交予父组件去传输。
2、订阅发布模式;
3、context,

插槽

//子组件 Header.js
render(
	const {children} = this.props;
	<div>
		//预留插槽
		<span>{children[0]}</span>
		<span>{children[1]}</span>
		<span>{children[2]}</span>
	</div>
)
//父组件引用
//引入Header.js
retrun(
<>
	<Header>
		<button>返回</button>
		<span>标题</span>
		<span>设置</span>
	</Header>
</>
)

生命周期

React生命周期

旧版生命周期

初始化阶段

componentWillMount(已舍弃):render之前最后一次修改状态的机会;
render:只能访问this.props和this.state,不允许修改状态和DOM输出;
componentDidMount:成功渲染之后触发,可修改DOM。

运行阶段

componentWillReceiveProps (已舍弃):子组件可监听父组件属性修改;
shouldComponentUpdate:返回true/false,阻止render调用;
componentWillUpdate:在接收新的属性和状态的时候,可对该周期进行DOM操作,可做动画,但是无法修改属性和状态,因为会导致死循环。
render:只能访问this.props和this.state,不允许修改状态和DOM输出;
componentDidUpdate:可修改DOM。

销毁阶段

componentWillUnmount:删除组件前的所有操作的周期。
此方法用于组件卸载前,清理一些注册监听事件,或者取消订阅的网络请求等
一旦一个组件实例被卸载,其不会被再次挂载,而只可能是被重新创建

新增生命周期

getDerivedStateFromProps(props,state)
该方法会在调用 render 方法之前调用,当组件props变化时state更新。该方法是回调函数,若为空,不做任何修改;若需要DOM渲染前对状态做调整,可通过返回state进行修改(注:该函数无需this指向state)
小Tip
通过getDerivedStateFromProps(props,state),提前备份存储这一次渲染属性,然后在componentDidUpdate(props,state)里比较上次的渲染属性,若相等直接return,若不相等执行网络请求,避免死循环。例如:

state={
	type:'start';
}
static getDerivedStateFromProps(props,state){
	return type=props.父传属性值
}
componentDidUpdate(props,state){
	if(this.state.type==='start'){
		//请求模块
	}
	else if(this.state.type === props.type){
		return
	}else{
		//请求模块
	}
}

getSnapshotBeforeUpdate
该生命周期取代componentWillUpdate,触发时间是render之后DOM渲染之前,并返回一个值作为componentDidUpdate的第三个参数(属性props,状态state,componentWillUpdate返回的值)
小Tip
结合以上两个生命周期,获取DOM渲染前的数据和渲染后的数据,进行对比数据变化。例如实现位置定位:

//获取渲染前滚轮高度
getSnapshotBeforeUpdate(){
	return this.refs.ww.scrollTop;
}
//实现定位当前高度
componentDidUpdate(props,state,preHeight){
	this.refs.ww.scrollTop += this.refs.ww.scrollHeight-preHeight
}
<div style={height:'300px',overfloat:'auto' ref='ww'}>
	<ul><li></li>...</ul>
</div>

常用:
render

类组件必须实现的方法,用于渲染DOM结构,可以访问组件state与prop属性

注意:不要在 render 里面 setState, 否则会触发死循环导致内存崩溃

componentDidMount

组件挂载到真实DOM节点后执行,其在render方法之后执行此方法多用于执行一些数据获取,事件监听等操作

shouldComponentUpdate
控制组件更新,通过return true/false来控制生命周期是否进行更新。它自带两个参数(上一个props,上一个state),可通过该生命周期来控制更新频率,优化性能,例如:

shouldComponentUpdate(nextProps,nextState){
	//通过新旧状态,进行对生命周期的调用控制
	if(JSON.stringify(this.state) != JSON.stringify(nextState)){
		return true
	}else{
		return false
	}
}

避免发送请求死循环
1.避免重复加载的方法除了以上演示,还能通过引入React 内置PureComponent进行新旧props和state进行比较,避免重复刷新,由此优化性能。但是对于必然会更新的组件不建议使用该方法,避免强制校验导致性能损耗。代码如下:

import {PureComponent} from 'react'
export default class NavBar extends PureComponent{
	//代码块
}

2.通过useEffect(),实现一次仅此一次render调用该hook钟的网络请求

import {useEffect} from 'react'
useEffect(()=>{
	//axios网络请求
},[])
//空数组,代表无需依赖,若引用函数内的状态值或者传参,则在数组中传参
//当不传递第二个参数时,每次render都会执行一遍callback函数,相当于包含第一遍render的componentDidUpdate
//当传递第二个参数且是空数组时,只有第一次render才会执行callback,类似于componentDidMount
//不管是否传递第二个参数,只要在callback中return 一个函数,就相当于告诉react此组件挂掉之前执行什么操作,类似于componentWillUnMount

useEffect和useLayoutEffect如何抉择?
useEffect发生在视图更新后,而layoutEffect发生在编译成dom元素之后视图更新之前。
两者的执行顺序不同,定位也不同,由于操作DOM需要一定时间,我们最好优先使用useEffect,以保证用户页面第一时间渲染出来。
如果业务需求是先要进行DOM操作或者跟页面布局相关的,那么就可以使用useLayoutEffect

componentWillReceiveProps
该生命周期可以让子组件在不影响父组件传参的条件下,加工或者存储父组件参数到子组件。例如:

componentWillReceiveProps(nextProps){
	//给传属性后缀加工111
	this.setState({
		content:nextProps.传的参数名 + '111'
	})
}

componentDidUpdate

执行时机:组件更新结束后触发

在该方法中,可以根据前后的props和state的变化做相应的操作,如获取数据,修改DOM样式等(可通过该生命周期追溯变更后的上一次的props和state)

优化

useCallback(优化函数方法)
使用该hook函数,可避免函数不断被setter重定义,实现缓存的作用。只有第二参数(该hook数组绑定参数)发生变化,useCallback函数才重新定义一次。

import [useCallback] from 'react;
const 函数名=useCallback(()=>{
	//逻辑代码
},[绑定数据数组]) //数组为空情况下,响应式数据落绑,导致空值

useMemo(优化计算属性)
类似vue计算属性,缓存计算结果,当计算结果发生变化,重新执行/定义内部函数。

Route 路由

引入路由

npm i react-router-dom@5
Switch判断切换多个路由,必备;
Route 路由注册,必备;
Redirect 路由重定向.
import { Switch, Route, Redirect } from 'react-router-dom'
import {HashRouter,Route,Switch,Redirect } from 'react-router-dom'
import Login from '../pages/Login.js'
import Resiger from '../pages/resiger.js'
import NoFind from '../pages/NoFind.js'
import More from '../pages/More.js'
export default class IndexRouter extends Component {
  render() {
    return (
      <div>
        <HashRouter>
          {/* Switch+Redirect  路由重定向 */}
          <Switch>
            <Route  path='/Login' component={Login}/>
            <Route path='/Resiger' component={Resiger}/>
            <Route path='/More' component={More}/>
            {/* Redirect 必须先注册路径才能正常使用 */}
            <Redirect from='/' to='/Login' exact/> 
            {/* 一般重定向是模糊的,加上‘exact’变为精确,避免任何正规与非正规路径定向到默认路径 */}
            {/* <Redirect from='/' to='/Login' exact/> */}
            {/* 匹配不到以上条件路径,直接跳转指定404页面 */}
            <Route component={NoFind}/>
          </Switch>
        </HashRouter>
      </div>
    )
  }
}

子路由

如果直接在父级路由那编写子路径,子路径跟父级路径同级导致父级被顶替无法正常显示.

 <Route path='/More' component={More}/>
 {/* 不可取,会顶替父级路由 */}
 <Route path='/More/First' component={Child} />

正确嵌套是通过父级页面引入子路由

import { Switch, Route, Redirect } from 'react-router-dom'
import Child from './Childrens/Child1'
import Child2 from './Childrens/Child2'
export default class More extends Component {
    render() {
        return (
            <div>
                <h2>
                    更多
                </h2>
                <Switch>
                    <Route path='/More/First' component={Child} />
                    <Route path='/More/Second' component={Child2} />
                    <Redirect from='/More' to='/More/First' />
                </Switch>


            </div>
        )
    }
}

路由跳转

声明式导航
{/* NavLink和activeClassName都是内置,点击高亮自动匹配 */}
<NavLink to='/路径' activeClassName='自定义高亮类名'></NavLink>
编程式导航

第一种

{/* 函数页面跳转 */}
const handleGo=(id)=>{
	props.history.push(`/More/${id}/`)
}
{/* 类页面跳转(this指向) */}
const handleGo=(id)=>{
	this.props.history.push(`/More/${id}/`)
}

第二种 引入内置方法

{/* 引入 */}
import {useHistory} from 'react-router-dom'
{/* 存储*/}
const history = useHistory();
const handleGo=(id)=>{
	history.push(`/More/${id}/`)
}

路由传参

query传参
history.push({pathname:‘/路径’,query:{传参}})
** 动态路由传参 **
history.push(/路径、${参数})
** state传参**
history.push({pathname:‘/路径’,state:{传参}})

路由拦截

<Route path=‘/权限路径’ render={()=>{return isAuth()?<页面/>:}}/>

给跳转页面传参

第一种

<Route path='/权限路径' render={(props)=>{return isAuth()?<跳转页面名   传参名=‘’ {...props}/>:<Redirect to='重定向页面'>}}/>

{/* 跳转页面函数获取参数*/}
function 跳转的页面名(props){
	console.log(props)
}

第二种 引入withRouter()

{/* 跳转页面函数获取前一页面参数*/}
import {withRouter} from 'react-router-dom'
function 跳转的页面名(props){
	console.log(props)
}
{/* withRouter()将跳转页面作为前一页面的子页面*/}
export default withRouter(要跳转的页面名)

-----------------------------------------
{/*前一级路由---跳转前  props因为跳转页面withRouter(),导致跳转后有参数传出*/}
<Route path='/权限路径' render={(props)=>{return isAuth()?<跳转页面名   传参名=‘’ {...props}/>:<Redirect to='重定向页面'>}}/>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值