react基础

JSX语法(JSX JavaScript 语法的扩展):

  • 如果再jsx要写行内样式需要使用style={{coler:red}}形式(样式接受⼀个样式对象,但是前提是需要花括号提供js环境
  • Babel ES6 代码转为 ES5 代码、内嵌了对 JSX 的⽀持
Babel 会把 JSX 转译成⼀个名为 React.createElement() 函数调⽤。
以下两种示例代码完全等效:
 const element = (
     <h1 className="greeting">
         Hello, world!
     </h1>
 );

 const element = React.createElement(
 'h1',
 {className: 'greeting'},
 'Hello, world!'
 );
React.createElement() 会预先执⾏⼀些检查,以帮助你编写⽆错代码,但实际上它创建了⼀个这样的对象:
const element = {
     type: 'h1',
     props: {
         className: 'greeting',
         children: 'Hello, world!'
     }
 };

class绑定:

样式的类名指定不能写class,要写className;

JSX 允许在大括号中嵌入任何表达式

const myId="22"
<h2 className="bg" id={myId}>111111</h2>
<div style={{color:'red'}}>yuwqiuyi</div>

列表渲染:

ReactDOM.render(...) 是渲染⽅法,所有的 js,html 都可通过它进⾏渲染绘制,他有两个参数,内容和渲染⽬标 js 对象。
内容就是要在渲染⽬标中显示的东⻄,可以是⼀个 React 组件 。渲染⽬标 JS 对象,就是⼀个 DIV
需要注意的:渲染的元素只能有⼀个根元素
function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li key={number.toString()}>
      {number}
    </li>
  );
  return (
    <ul>{listItems}</ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

组件定义:

注意: 组件名称必须以⼤写字母开头。
React 会将以⼩写字母开头的组件视为原⽣ DOM 标签
为什么在函数式组件不加 this ,⽽在类组件需要加 this?
如果去打印函数式组件中的 this ,会发现是 undefined ,按照我们理解最起码应该是 window
因为这是经过 Babel 翻译的结果。
Babel 是严格模式 ‘use strict’ 下进⾏的,不允许函数⾥的 this 指向 window ,所以这⾥指向 undefined
⽽在类组件中,类其实类似于对象,要想访问对象中的属性肯定需要加 this
// 函数式组件 默认开启严格模式
function Demo(){
  return <h2 onClick={()=>{cosole.log(222}}>我是函数式组件</h2>
}

//类组件
class Weather extends React.Component{
  state={is:false,wind:"微风"}
  render(){
    const {is,wind,user,password}=this.state
    return (
        <h2 onClick={this.changeWeather}> 12{is?2:8} 风速{wind}  我叫{name},年龄{age}</h2>)
  }
  changeWeather=(event)=>{ 
      const is=!this.state.is
      this.setState({is});   //修改state
  }
}

事件处理以及传参问题

//如果采⽤ JSX 的语法你需要传⼊⼀个函数作为事件处理函数,⽽不是⼀个字符串(DOM 元素的写法)
<button onclick="activateLasers()">
激活按钮
</button>
//React 中写法为:
<button onClick={activateLasers}>
激活按钮
</button>

class Child extends React.Component {
    fn() {
        console.log(this)
        //this的结果是undefined
    }
    fn2=(m)=> {
        console.log(m)
    }
    render() {
        return (
            <div>
                <h1 onClick={this.fn}>我是⼦组件</h1>
                //在react中,这样写加括号表示函数⽴即执⾏
                <h1 onClick={this.fn2(3)}>我是⼦组件</h1>
                //在react中,可以用以下两种方法进行传参
                <h1 onClick={this.fn.bind(this,3)}>我是⼦组件</h1>
                <h1 onClick={()=>this.fn(3)}>我是⼦组件</h1>
            </div>
        )
    }
}
ReactDOM.render(
<Child />,
document.getElementById('app')
);

this指向问题

这⾥有⼀个 this 指向的问题,如果直接像上⾯代码这样使⽤,会提示 this undefined
onClick={this.fn()} 如果 onClick ⾥⾯的内容换成这样,结果就正确了
因为直接调⽤是可以直接获取到 this 的,但是经历过⼀次赋值, this 就丢失了
//为什么直接赋值给函数this指向会丢失?
"use strict"
let obj = {
    display: function() {
        console.log(this)
    }
};
function handleClick(callback) {
    callback()
}
handleClick(obj.display) // undefined 正常会指向window,严格模式下就是undefined

解决方案

1、在构造函数中绑定this
constructor(){
    super()
    this.fn=this.fn.bind(this)
}
2、调⽤的时候绑定this
<h1 onClick={this.fn.bind(this)}>我是⼦组件</h1>
3、使⽤箭头函数
fn=()=> {
    console.log(this) 
}
render() {
    return (
        <div>
        <h1 onClick={this.fn}>我是⼦组件</h1>
        </div>
    )
}

特殊情况(vue中的slot插槽)

因为我们封装好的组件可以写成双标签也可以写成单标签
例如 <Welcome/> 或者 <Welcome></Welcome> 都是正确的写法
那如果如下这种情况,我们会发现 h1 标签根本⽆法正常渲染
 <Welcome>
     <h1>hello</h1>
 </Welcome>

因为welcome本身是⼀个组件,代表了⼀⼤段html结构,这时候加⼀个h1,并不知道应该把这个h1加到组件的哪个位置上,所以需要指定this.props.children类似于vue中的slot

 function Welcome(props) {
     return <div>
         <p>1111</p>
         <div>{props.children}</div>
     </div>;
 }

 <Welcome>
 <h1>hello</h1>
 </Welcome>
 //或者⽤下⾯的⽅式,把children看成是⼀个属性
 <Welcome children={<h1>hello</h1>}/>

如果传⼊单个内容,返回的就是⼀个对象,如果传⼊多个内容的话,返回的就是数组
所以也不需要像是vue具名插槽那样,如果需要放置在不同位置,直接通过数组的⽅式访问即可

import React, { Component } from 'react'

export default class Context extends Component {
    render() {
        return (
            <div>
                <div>生产者</div>
                <Children render={(name) => <Son name={name} />} />
            </div>
        )
    }
}

class Children extends Component {
    state = { name: 'tom' }
    render() {
        const {name} = this.state
        return (
            <div>
                子组件
                {this.props.render(name)}
            </div>
        )
    }
}

class Son extends Component {
    render() {
        return (
            <div>
                孙组件{this.props.name}
            </div>
        )
    }
}

类组件

 ⾸先super代表⽗类的构造函数,作⽤是⽤来新建⽗类的this对象,结果返回⼦类实例对象

⼦类必须在constructor⽅法中调⽤super⽅法,否则新建实例时会报错。

这是因为⼦类⾃⼰的this对象,必须先通过⽗类的构造函数完成塑造,得到 与⽗类同样的实例属性和⽅法,然后再对其进⾏加⼯,加上⼦类⾃⼰的实例 属性和⽅法。如果不调⽤super⽅法,⼦类就得不到this对象。

ES5 的继承,实质是先创造⼦类的实例对象this,然后再将⽗类的⽅法添加到this上⾯(Parent.apply(this))。

ES6 的继承机制完全不同,实质是先将⽗类实例对象的属性和⽅法,加到this上⾯(所以必须先调⽤super⽅法) 然后再⽤⼦类的构造函数修改this。 

state  状态管理器,必须是对象的形式

1.不能直接修改 State
 // 错误写法
 this.state.comment = 'Hello';
 // 正确写法
 this.setState({comment: 'Hello'});
2.构造函数是唯⼀可以给 this.state 赋值的地⽅,也就是初始值另外特殊的,还可以简化,state可以不写在构造函数中
class Clock extends React.Component{
    constructor(props){
        super(props);
        //this.state={date:new Date()} 可以不写在这里
    }
    state={date:bew Date()} //可以加在这里 ES6的新写法
}
state 的更新可能是异步的
出于性能考虑, React 可能会把多个 setState() 调⽤合并成⼀个调⽤。
class Demo extends React.Component{
    constructor(){
        super()
        this.state={
            info:1
        }
    }
    fn=()=>{
       for(let i=0;i<10;i++){
            console.log(this.state.info); //10个0 ⻚⾯显示1
            this.setState({
                info:this.state.info+1
            })
        }
    }
    render(){
        return(
            <div onClick={this.fn}>{this.state.info}</div>
        )
    }
}
ReactDOM.render(
<Demo/>,
document.getElementById('app')
);
但是有些情况下我们确实需要⽴即拿到结果,该怎么解决呢?
setState接收的参数还可以是⼀个函数,在这个函数中可以拿先前的状态,并通过这个函数的返回值得到
下⼀个状态。
this.setState((prev)=>{
    console.log(prev.info);
    return{
    info:prev.info+1
    }
})
setState接受第⼆个参数,即状态更新后的回调函数
this.setState({
    info:this.state.info+1
    },()=>{
        alert(this.state.info)
    })

props不容更改

组件⽆论是使⽤函数声明还是通过 class 声明,都决不能修改⾃身的 props

父子组件传值:

const p={name:'jerry',age:12}
//类组件
<Weather {...p}/>

class Weather extends React.Component{
    render(){
        const {name,age}=this.props
         return (<h2> 我叫{name},年龄{age}</h2>)
    }
}

//函数组件
<Demo {...p}/>

function Demo(props){
  return <h2>我是函数式组件 {props.name}</h2>
}

子父组件传值:

refs属性( 获取dom节点)

什么是 ref ref React 提供的⽤来操纵 React 组件实例或者 DOM 元素的接⼝。
简单来说,就是提供了⼀种⽅式能让你直接获取到 dom 元素或者组件,但是你肯定 想问,原⽣的 document.getElementById 不也能拿
dom 元素吗,为什么还需要 ref
ref 性能更⾼,会减少获取 dom 节点的消耗,并且写法与其余 react 代码更加⼀致
基本跟 vue 中的 ref ⽤法⼀样 ref 拿到的是真实 dom
ref 可以挂到任何组件上,可以挂到组件上也可以是 dom 元素上
挂到组件(这⾥组件指的是有状态组件)上的 ref 表示对组件实例的引⽤,⽽挂载到 dom 元素上时表示具体的 dom 元素节点。
//第一种
<h2 ref={this.myRef} }>rrr</h2>
myRef=React.createRef()
console.log(this.myRef.current) //获取dom节点

//第二种
this.refs.submit
<input ref='submit' type='button'>

//第三种
<input type="text" ref={input => this.textInput = input} />
console.log(this.textInput);

受控组件与非受控组件

<form onSubmit={this.handleSubmit}>
    {/* 受控组件 */}
    <select value={this.state.value} onChange={this.handleChange}>
        <option value="grapefruit">葡萄柚</option>
        <option value="lime">酸橙</option>
        <option value="coconut">椰子</option>
        <option value="mango">芒果</option>
    </select>
    {/* 非受控组件 */}
    <input type="text" ref={this.input} />
    <input type="submit" value="提交" />
</form>

state = { value: 'coconut' };
input = React.createRef();
handleChange = (event) => {
    this.setState({ value: event.target.value });
}
handleSubmit = (event) => {
    alert('受控组件:' + this.state.value + '非受控组件:' + this.input.current.value);
    event.preventDefault();
}

生命周期

旧生命周期:

新生命周期(删3个 加2个):

componentWillMount 在渲染前调⽤ ,在客户端也在服务端。 (新版本不⽀持)
componentWillReceiveProps 在组件接收到⼀个新的 prop ( 更新后 ) 时被调⽤。这个⽅法在初始化 render 时不会被调⽤。也就是传⼊的默认属性是不会调⽤的 (新版本不⽀持)
componentWillUpdate 在组件接收到新的 props 或者 state 但还没有 render 时被调⽤。在初始化时不会被调⽤。 ( 新版本不⽀持 )
getDerivedStateFromProps() : 在调⽤ render ⽅法之前调⽤,并且在初始挂载及后续更新时都会被调⽤。 (版本新增)
//必须前⾯添加static 否则报错
static getDerivedStateFromProps(props, state) {
    //必须要返回⼀个状态对象,不然也会报错
    return {state1: props.abc }
}
我们的state值msg只取决于传进来的属性,这也就是该⽣命周期名字的由来(从属性中获取衍⽣的状态)
getDerivedStateFromProps 的存在只有⼀个⽬的:让组件在 props 变化时更新 state state 的值在任何时候都取决于 props 所以如果将来你的状态只取决于你的属性的话,就可以⽤它。
getSnapshotBeforeUpdate (prevProps, prevState (版本新增)在更新之前获取快照
getSnapshotBeforeUpdate() 在最近⼀次渲染输出(提交到 DOM 节点)之前调⽤。
它使得组件能在发⽣更改之前从 DOM 中捕获⼀些信息(例如,滚动位置)。
此⽣命周期⽅法的任何返回值将作为参数传递给 componentDidUpdate()的第三个参数
注意这个⽅法必须和 componentDidUpdate ⼀起使⽤,不然会报错
class News extends React.Component{
    state={newsArr:[]}
    myList=React.createRef()
    componentDidMount(){
        setInterval(()=>{
            const {newsArr} =this.state
            const news =`星期${newsArr.length+1}`
            this.setState({newsArr:[news,...newsArr]})
        },2000)
    }
    getSnapshotBeforeUpdate(){
        return this.myList.current.scrollHeight;
    }
    componentDidUpdate(preProps,preState,height){
        this.myList.current.scrollTop+=this.myList.current.scrollHeight-height
    }
    render(){
        return (
            <ul className="news" ref={this.myList}>
                {this.state.newsArr.map((item)=>{
                    return <li>{item}</li>
                })}
            </ul>
        )
    }
}

componentWillUnmount   (相当于vue中的beforeDestroy   关闭定时器)

render() : ⽅法是 class 组件中唯⼀必须实现的⽅法。
当前生命周期用来进行数据与模板的结合
render 函数第一次执行的时候会将渲染的数据在内存中保存一份,当第二次数据发生了改变后,render 会将这次的虚拟DOM与缓存中的虚拟DOM进行对比(diff 算法)
只要 this.state 、 this.props 发生了改变那么 render 函数就会执行
componentDidMount : 在第⼀次渲染后调⽤,只在客户端。之后组件已经⽣成了对应的 DOM 结构 可以在这个⽅法中调⽤ setTimeout, setInterval 或者发送 AJAX 请求等操作 ( 防⽌异步操作阻塞UI)
componentDidUpdate(prevProps, prevState) {
    if (prevState.count !== this.state.count) {
        document.title = `You clicked ${this.state.count} times`;
    }
}

shouldComponentUpdate (nextProps, nextState)返回⼀个布尔值。在组件接收到新的props或者state时被调⽤。在初始化时或者使⽤forceUpdate时不被调⽤。

判断如果某个值变了就让组件重新渲染,否则不渲染,提⾼性能
可以在你确认不需要更新组件时使⽤。
正常情况下只有更新了 state 或者 props ⻚⾯都会重新渲染,因为 shouldComponentUpdate 不写的话默认返回 true, 如果返回 false 那么将不会往下再⾛⽣命周期,所以 render 函数将不会执⾏
可以将 this.props nextProps 以及 this.state nextState 进⾏⽐较,并返回 false 以告知 React 可以跳过更新。
请注意,返回 false 并不会阻⽌⼦组件在 state 更改时重新渲染。
shouldComponentUpdate(nextProps,nextState) {
    if (nextProps.m1 === this.props.m1 && nextState.m2 === this.state.m2) {
        return false;
    } else {
        return true;
    }
}
componentDidUpdate 在组件完成更新后⽴即调⽤。在初始化时不会被调⽤。
当组件更新后,可以在此处对 DOM 进行操作。如果你对更新前后的 props 进行了比较,也可以选择在此处进行网络请求。(例如,当 props 未发生变化时,则不会执行网络请求)。
componentDidUpdate(prevProps, prevState, snapshot) {
  // 典型用法(不要忘记比较 props):
  if (this.props.userID !== prevProps.userID) {
    this.fetchData(this.props.userID);
  }
}

你也可以在 componentDidUpdate() 中直接调用 setState(),但请注意它必须被包裹在一个条件语句里,正如上述的例子那样进行处理,否则会导致死循环。

componentWillUnmount 在组件从 DOM 中移除之前⽴刻被调⽤

pureComponent

1.setState存在两个不合理之处

  • 1.setState⽆论是否更新了staterender函数都会重现调⽤,这是不合理的
  • 2.如果⽗组件更新了,⽆论⼦组件变⽤没⽤到⽗组件的数据也都会重新渲染⼦组件,这是不合理的
2.传统的解决⽅案
//判断两次不⼀致再更新,否则不更新
shouldComponentUpdate(nextProps,nextState){
    if(this.props.someprops===nextProps.someprops){
        return false
    }else{
        return true
    }
}
但是这仅仅是⼀个属性,如果有多个属性的话,⼀个⼀个对⽐会⽐较麻烦
所以使⽤ PureComponent
class Mouse extends React.PureComponent {}

react强制更新页面

  • this.setState({})
  • this.forceUpdate()

react脚⼿架

脚手架安装:

        创建项目:yarn create react-app 项目名

        启动项目:npm start

        

脚⼿架⽂件介绍:

        public:静态资源⽂件
        index.html:主⻚⾯ 。
         %PUBLIC_URL% 代表public⽂件夹
        src:存放所有我们开发的源代码
        index.js:是我们项⽬的⼊⼝⽂件
        App.js:项⽬的根组件
        App.css:根组件的样式
        index.css 全局样式⽂件

样式模块化:

css:中间的名字必须是module不能改

CSS Modules 允许通过⾃动创建 [filename]\_[classname]\_\_[hash] 格式的唯⼀ classname 来确定 CSS 的作⽤域。

import style from "./style.module.css"
< h1 className = { style . title } > 你好啊 </ h1 >
通过审查元素就可以看⻅确实是加了⽂件名 +class +hash
less:可以将当前元素的样式写在⼀个⼤的⽗级中

react请求(跨域):

src ⽬录下新建⽂件 src/setupProxy.js ⽂件
注意: 你⽆需在任何位置导⼊此⽂件。 它在启动开发服务器时会⾃动注册。⽂件名不能更改
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
    app.use(
        createProxyMiddleware("/api", {
            target: "https://api.binstd.com",//跨域地址
            changeOrigin: true,
            pathRewrite:{
            "^/api":""
        }
    })
);

react-router路由组件:

react-router 是浏览器和原⽣应⽤的通⽤部分。
react-router-dom 是⽤于浏览器的。
react-router-native 是⽤于原⽣应⽤的
安装:npm i react-router-dom
npm install react - router - dom@ ^ 5.3.0 -- save( ⽬前最新版本的路由是 6 版本,如果要按准过 5 版本的话运⾏下⾯指令安装指定版本即可
BrowserRouter, 这是对 Router 接⼝的实现。使得⻚⾯和浏览器的 history保持⼀致。⽐如: window.location
HashRouter ,和上⾯的⼀样,只是使⽤的是 url hash 部分,⽐如: window.location.hash

import React,{Component} from "react"
import './App.css';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
function Login() {
    return <div>Register</div>;
}

function Register() {
    return <div>Login</div>;
}

class App extends Component{
    render(){
        return(
            <Router>
                <div className="App">
                    <Link to="/login">Login</Link>
                    <Link to="/register">Register</Link>
                    <Route path="/login" component={Login}></Route>
                    <Route path="/register"  render={(props) => {
                        console.log(props)
                        <div>Home</div>}}>
                    </Route>
                </div>
            </Router> 
        )
    }
}
export default App;
//children :⽤法和render基本⼀致
//但是重点观察match字段
//对于没有匹配上的路由,children⾥的match字段会是null,⽽render函数根本就不会运⾏
//这就允许你动态调整你的 UI 界⾯,基于路线是否匹配,如果路线匹配我们则添加⼀个 active 类
路由组件:接收到三个固定的属性 

  • activeClassName(string):设置选中样式,默认值为active
  • activeStyle(object):当元素被选中时,为此元素添加样式
  • exact(bool):为true时,只有当导致和完全匹配classstyle才会应⽤
  • strict(bool):为true时,在确定为位置是否与当前URL匹配时,将考虑位置pathname后的斜线
  • isActive(func)判断链接是否激活的额外逻辑的功能
NavLink 组件进⾏⼆次封装
class MyNavLink extends Component{
    render(){
        return <NavLink activeClassName="haha" to={this.props.to}>{this.props.children}                </NavLink>
    }
}
//调用:
<MyNavLink>login</MyNavLink>
replace 模式:
<NavLink replace={true} activeClassName="haha" to="/register/b">Register</NavLink>

路由可以开启replace模式,这样路由跳转的时候不会留下痕迹,⽆法通过浏览器的返回键返回,⽽是会直接替换当前路由

实际使⽤场景:⽐如⽀付成功⻚⾯,成功之后不允许⽤户点返回重复⽀付

Switch的使用:

  • 匹配到第⼀个就不会继续向下匹配,这种⽅式还可以提⾼性能
<Switch>
    <Route path="/login" component={Login}></Route>
    <Route path="/register" component={Register}></Route>
    <Route path="/register" component={Welcome}></Route>
</Switch>

路径的严格匹配与模糊匹配:

  • 默认使用模糊匹配,输入的路径必须包含匹配的路径,顺序一致
  • 尽量不开启严格匹配(如果出现了⽗⼦路由的情况,严格匹配会导致匹配不上⼦路由)

路由的重定向(Redirect):

  • 默认路由,当所有路由都无法匹配时,进入该路由  
<Switch>
    <Route path="/login" component={Login}></Route>
    <Route path="/register" component={Welcome}></Route>
    <Redirect to="/login"></Redirect>
</Switch>

嵌套路由:

  • 注册子路由写上父路由的path值
  • 路由的匹配是按照注册路由的顺序执行的
//第一种
<Route path={`${this.props.match.url}/home`} component={Home}></Route>
//第二种
<Route path="/home" render={({match})=>
    <Detail>
        <Route path={`${match.url}/index`} component={Index}></Route>
        <Route path={`${match.url}/about`} component={About}></Route>
    </Detail>
}></Route>

路由传参:

params: 

        <Link to="/about/${obj.id}">about</Link>   

        <Route path="/about/:id  component={test}/>

        const id =this.props.match.params

search:

        <Link to="/about?name=tom&age=18">about</Link>   

        <Route path="/about  component={test}/>

        const id =this.props.location.search

        querystring插件可以解析

state(类似于post请求的参数):    

        缺点: 如果⼿动刷新当前路由时,数据参数有可能会丢失!!!’
        在react 中,最外层包裹了 BrowserRouter 时,不会丢失 , 但如果使⽤的时 HashRouter ,刷新当前⻚⾯时,会丢失 state 中的数据

         <Link to={{path:'/about',state:{name:'tom',age:18}}}>about</Link>   

         <Route path="/about  component={test}/>

         const id =this.props.location.state

路由跳转的两种模式:

        默认是push  

        修改replace:直接在<Link replace to='XXX'>标签上添加一个replace属性即可

路由跳转⽅式总结:

路径字符串的形式
< Link to = "/courses" replace />
对象的形式
<Link to={{
    pathname: '/courses',
    search: '?sort=name',
    hash: '#the-hash',
    state: { fromDashboard: true }
}}/>

编程式导航:

  • this.props.history.push()
  • this.props.history.replace()
  • this.props.history.goBack()
  • this.props.history.goForward()
  • this.props.history.go()

BrowserRouter和HashRouter的区别:

  • BrowserRouter刷新无任何影响,因为state保存在history对象中
  • HashRouter刷新后会导致路由丢失state参数

withRouter:

//withRouter实现原理: 
//将组件包裹进 Route, 然后返回
const withRouter = () => {
    return () => {
        return <Route component={Nav} />
    }
}
// 这⾥是简化版
const withRouter = ( Component ) => () => <Route component={ Component }/>

1、先引入import { withRouter} from "react-router-dom"
2、定义一般组件class XX extends ...
3、export default withRouter( XX )

HOOK(useHistory):

useHistory 在您拥有 Routes 的组件中不起作⽤,因为尚未设置 useHistory 所需的上下⽂。 useHistory 将适⽤于您在您中声明 的任何⼦组件或组件,但它不适⽤于组件本身
import React from 'react';
import { useHistory } from 'react-router-dom';
export default function Demo() {
const history=useHistory();
function jump(){
    history.push("/login")
}
return <div onClick={jump}>helloA </div>;
}

解决多级路径刷新样式丢失问题:

public/index.html中引入样式不写./写/或者%PUBLIC_URL%

路由懒加载:

import  {lazy, Suspense } from 'react'
const Home = lazy(() => import('./pages/Home'))

<Suspense fallback={<h1>loading....</h1>}>
    <Switch>
        <Route path="/home" component={Home} />
        <Route path="/About" component={About} />
    </Switch>
</Suspense>
react 路由写的⽐较杂乱,不如像 vue那样集中管理如果想集中管理,可以借助⼀个插件
npm install react - router - config
编写基本的路由路线,path为路径,component为对应渲染的组件,exact属性决定是否精确匹配

 现在如果直接去访问嵌套路由的路径,会发现渲染不出来

 

代理:

条件渲染:

if

if (isLoggedIn) {
   return <UserGreeting />;
}
   return <GuestGreeting />;

<Greeting isLoggedIn={false} />,

与运算符 &&

如果条件是 true&& 右侧的元素就会被渲染,如果是 false,React 会忽略并跳过它。

<div>
   {unreadMessages.length > 0 &&
     <h2>
        You have {unreadMessages.length} unread messages.
     </h2>
    }
</div>

<Mailbox unreadMessages={messages} />,

三目运算符

  <div>
      {isLoggedIn
        ? <LogoutButton onClick={this.handleLogoutClick} />
        : <LoginButton onClick={this.handleLoginClick} />
      }
  </div>

隐藏组件:

function son (props){
	const {show} = props;
	if (!show){
	  return null;  // 提前返回null,不渲染子组件的内容
	}
	return (<p> 这是子组件的内容 </p> )
}

function father () {
  const [show, setShow] = useState(true)
  const notShow = () => {
    setShow(false); // 阻止子组件的渲染
  }
  
  return (
  	<div>
        <button onClick={notShow}>点击按钮,阻止渲染子组件</button>
        <son show={show} />
	</div>
  )
}

Redux:

npm install -- save redux

在Redux中,所有的数据被保存在一个被称为store的容器中 ,在一个应用程序中只能有一个store对象。当一个store接收到一个action,它将把这个action代理给相关的reducer。reducer是一个纯函数,它可以查看之前的状态,执行一个action并且返回一个新的状态。

action:

        action是把数据从应用传到 store 的有效载荷,

        它是 store 数据的唯一来源;

        要通过本地或远程组件更改状态,需要分发一个action;

const action = {
    type: 'ADD_TODO',
    payload: 'Learn Redux'
};

reducer(必须是一个纯函数):

        Reducer 是⼀个纯函数,它接受 Action 和当前 State 作为参数,返回⼀个新的 State

const reducer = function (prevState, action) {
    // ...
    return new_state;
};
⼀个函数的返回结果只依赖于它的参数,并且在执⾏过程⾥⾯没有副作⽤,我们就把这个函数叫做纯函数
纯函数中,下⾯的操作都是不允许的
1. 调⽤⾮纯函数,如 Date.now() Math.random();
2. 执⾏有副作⽤的操作,如 API 请求和路由跳转;
3. 不得改写参数数据

store:

        store就是把action和reducer联系到一起的对象,

        store本质上是一个状态树,保存了所有对象的状态。

        任何UI组件都可以直接从store访问特定对象的状态。

        当前时刻的 State ,可以通过 store.getState() 拿到
//createStore函数接受另⼀个函数作为参数,返回新⽣成的 Store 对象。
import { createStore } from 'redux';
const store = createStore(fn);
const state = store.getState();
Action Creator
View 要发送多少种消息,就会有多少种 Action 。如果都⼿写,会很麻烦。可以定义⼀个函数来⽣成 Action ,这个函数就叫 ActionCreator
//我们建议把type类型都设置为常量,因为写成字符串很容易出现错误
//字符串写错了,系统不会报错, 但是如果常量写错了,系统会报错
const ADD_TODO = '添加 TODO';
function addTodo(text) {
    return {
        type: ADD_TODO,
        text
    }
}
const action = addTodo('Learn Redux');
异步action:
所谓的同步 action ,其实就是个对象
异步 action ,本质上是⼀个函数,但是 redux 是处理不了函数形式的 action
// 使⽤action creator创建⼀个异步action
export function incrementAsync(params) {
    // 异步action中第1个参数叫dispatch,第2个参数是getState
    return function(dispatch,getState) {
        // 在异步action中就可以写异步代码
        setTimeout(()=>{
            // 在异步代码,还需要派发⼀个同步的action
            dispatch(increment())
        },3000)
    }
}
如果要处理异步问题,需要引⼊中间件
中间件就是⼀个函数,对 store.dispatch ⽅法进⾏了改造,
在发出 Action 和执⾏ Reducer 这两步之间,添加了其他功能。
npm i redux - thunk
import { createStore,applyMiddleware } from 'redux';
//applyMiddleware⽤来调⽤中间件
import myReducer from "./reducer"
import thunk from "redux-thunk";
const store = createStore(myReducer,applyMiddleware(thunk));
export default store

redux我们需要⽤store.subscribe主动监测

provider:
组件之间想使⽤ redux 中的数据那也需要再创建容器组件,那么 store 就需要重复引⼊,可以使用provider
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import store from './redux/store';

ReactDOM.render(
    <React.StrictMode>
        //在此处添加Provider即可
        <Provider store={store}>
            <App />
        </Provider>
    </React.StrictMode>,
document.getElementById('root')

react-redux:

react-redux 可以⾃动检测 redux 中数据变化
React-Redux 将所有组件分成两⼤类: UI 组件 和容器组件
UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。

容器组件由 connect 函数⽣成
npm install react - redux

redux-thunk

 actions/login.js

const ADD_TODO = 'add';
// 使⽤action creator创建⼀个异步action
export function loginAction(payload) {
  // 异步action中第1个参数叫dispatch,第2个参数是getState
  return function(dispatch,getState) {
      // 在异步action中就可以写异步代码
      setTimeout(()=>{
          // 在异步代码,还需要派发⼀个同步的action
          dispatch({
            type:ADD_TODO,
            payload
          })
      },3000)
  }
}

reducers/login.js

const initState = {
  role: '孙子',
  name: '我'
}
export function loginReducer (prevState = initState, action) {
  const { type, payload } = action
  switch (type) {
    case 'add':
      return payload
    default:
      return prevState
  }
}
store.js
import {legacy_createStore as createStore,combineReducers,applyMiddleware} from "redux"
import {loginReducer} from "./reducers/login"
import thunk from "redux-thunk";
const rootReducer = combineReducers({
  loginReducer
})

export default createStore(rootReducer,applyMiddleware(thunk))

App.js

import React from 'react'
import { connect} from 'react-redux'
import {loginAction} from './redux/actions/login'


function Son (props) {
  console.log(props)
  const {loginActionFun}=props
  const {role,name}=props.loginReducer
  const change=()=>{
    loginActionFun(
      {
        role:'我',
        name:'孙子'
      }
    )
  }
  return (
    <div>
      <p>我是孙子</p>
      <p>孙子是我</p>
      <p>猜猜谁是孙子?</p>
      <p>{role+'是'+name}</p>
      <button onClick={change}>改变</button>
    </div>
  )
}
export default connect(
  (state)=>{
    return state
  },{
    loginActionFun:loginAction
  }
)(Son)

index.js

import { Provider } from 'react-redux'

<Provider store={store}>
    <App />
</Provider>

redux-saga

 actions/login.js

const ADD_TODO = 'add2';
// 使⽤action creator创建⼀个异步action
export function loginAction(payload) {
return{
    type:ADD_TODO,
    payload
  }
}

reducers/login.js

const initState = {
  role: '孙子',
  name: '我'
}
export function loginReducer (prevState = initState, action) {
  const { type, payload } = action
  switch (type) {
    case 'addTop':
      return payload
    default:
      return prevState
  }
}
sagas/login.js
import { put,call, takeEvery,delay} from 'redux-saga/effects'
const ADD_TODO = 'addTop';
const ADD_ee = 'add2';
export default function* watchlogin(){
  yield takeEvery(ADD_ee,worklogin)
}
function* worklogin(){
  const res=yield call(fetch,"https://cnodejs.org/api/v1/topics?limit=10&page=7")
  yield delay(3000)
  const data=yield res.json()
  yield put({
    type: ADD_TODO,
    payload:{
      name:data.data[0].content
    }
  })
}

sagas/rootSaga.js

import { all } from 'redux-saga/effects'
import watchlogin from './login'
export function*rootSaga(){
  yield all([watchlogin()])
}

store.js

import {legacy_createStore as createStore,combineReducers,applyMiddleware} from "redux"
import {loginReducer} from "./reducers/login"
import createSagaMiddleware from 'redux-saga'
import {rootSaga} from './sagas/rootSaga'

const sagaMiddleware = createSagaMiddleware()
const rootReducer = combineReducers({
  loginReducer
})
const store=createStore(rootReducer,applyMiddleware(sagaMiddleware))
sagaMiddleware.run(rootSaga)
export default store

App.js

import React from 'react'
import { connect} from 'react-redux'
import {loginAction} from './redux/actions/login'


function Son (props) {
  console.log(props)
  const {loginActionFun}=props
  const {role,name}=props.loginReducer
  const change=()=>{
    loginActionFun()
  }
  return (
    <div>
      <p>我是孙子</p>
      <p>孙子是我</p>
      <p>猜猜谁是孙子?</p>
      <p>{role+'是'+name}</p>
      <button onClick={change}>改变</button>
    </div>
  )
}
export default connect(
  (state)=>{
    return state
  },{
    loginActionFun:loginAction
  }
)(Son)

index.js

import { Provider } from 'react-redux'

<Provider store={store}>
    <App />
</Provider>

高阶函数:

1、一类特别的函数

        参数是函数

        返回是函数

2、常见的高阶函数

        定时器设置函数

        数组的forEach()/map()

        promise

        react-redux中的connect函数

Hook :

import React, {useState, useEffect,useRef} from 'react'

useState 会返回⼀对值:当前状态和⼀个让你更新它的函数

 const [count, setCount] = useState(0) = [0, ƒ]
 setCount(count + 1)
 <h1>{count}</h1>
每次更新的数据我们并没有记录也没有存储,并没有像 setState 那样每次更改把数据存起来那么react 是怎么记住我们之前的状态呢?其实是按照顺序
if(true){
    const [age,setAge]=useState(18);
}

React Hook "useState" 被有条件地调⽤。 在每个组件渲染 react-hooks/rules-of-hooks 中,必须以完全相同的顺序调⽤ React  Hooks

useEffect(生命周期):

// componentDidMount
useEffect(()=>{
  console.log('componentDidMount')
},[])

// componentDidUpdate
useEffect(()=>{
  console.log('componentDidUpdate')
})

//在某些情况下,每次渲染后都执⾏清理或者执⾏ effect 可能会导致性能问题。
useEffect(()=>{
  console.log('count被修改了')
},[count])

// componentWillUnmount
useEffect(()=>{
  //ajax请求
  console.log('componentDidUpdate')
  return ()=>{
    //清除定时器
    console.log('componentWillUnMount')
  }
})

useContext:

⽤来解决跨组件之间传值的问题
我们知道,如果⽗⼦组件之间传递数据,通过 props 即可
如果与孙⼦组件之间的传值,那就⽐较麻烦,需要通过 props 层层传递
使⽤步骤:
第⼀步我们需要创建⼀个单独的context.js⽂件,创建我们的上下⽂对象并导出
import {createContext}from 'react'
const Context=createContext()
export default Context;
在⽗组件中引⼊上下⽂对象,并将要传递的数据通过 Provider 提供出去
Parent.jsx
import React,{useState}from 'react'
import Child from "./Child"
import Context from './context';
export default function Parent() {
    const [count, setCount] = useState("⽗数据");
    return (
        <div>
            <p onClick={()=>setCount("aaaaaaaaaa")}>我是⽗组件</p> 
            <Context.Provider value={count}>
            {/*需要把接收数据的组件放在provider⾥⾯就可以将数据传递给该组件及其⼦组件,这⾥也可以解构赋值⼀下 */
                <Child msg="我是⽗组件的数据"></Child>
            </Context.Provider>
        </div>
    )
}
Grandson.jsx
import React ,{useContext}from 'react'
import Context from './context';
export default function Childs2(props) {
    //孙⼦组件需要主动调⽤useContext⽅法拿到上下⽂的数据,如果要传递多个数据,使⽤对象即可
    const a = useContext(Context)
    return (
        <div>
            <p>我是孙⼦组件,{props.mymsg}</p>
            <h1>{a}</h1>
        </div>
    )
}

useMemo:

const double = useMemo(() => {
    return count * 2
  }, [count === 3])
<h2>{double}</h2>

useCalback:

import React, { PureComponent, useCallback, useRef } from 'react'
class Counter extends PureComponent {
  speak () {
    console.log('154')
  }
  render () {
    const { props } = this
    return (
      <h1 onClick={props.onClick}>23424</h1>
    )
  }
}

function App () {
  const counterRef = useRef()
  const onClick = useCallback(() => {
    counterRef.current.speak()
  }, [counterRef])
  return (
    <div>
      <Counter ref={counterRef} onClick={onClick} />
    </div>
  )
}
export default App;

ref:

const myRef = useRef()
<input type="text" ref={myRef}/>
console.log(myRef.current.value)

项目实践代码片段:

菜单权限过滤:
export function filterMenu(data,role){
  return data.filter(item=>{
    return item.meta.role.indexOf(role)!=1
  }).map(item=>{
    if(item.children){
      item.children=filterMenu(item.children,role)
    }
  })
}
//异步路由
renderRoute=(menu)=>{
    let routerList=[]
    const asyncRoute=(data)=>{
        data.forEach((item)=>{
            if(item.children){
                asyncRoute(item.children)
            }else{
                routerList.push(
                  <Route path={item.path} component={lazy(()=>import('./views'))} />
                )
            }
        })
    }
}
useImperativeHandle的作用是将子组件的指定元素暴漏给父组件使用

moment日期插件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值