react进阶用法完全指南

React调用回调函数,正确设置this指向的三种方法

  1. 通过bind
this.increment = this.increment.bind(this);
  1. 通过箭头函数
 

multi = () => {
    this.setState({
        count: this.state.count * 10
    })
}
  1. 箭头函数包裹
  

绑定事件传递参数

通过箭头函数传递事件参数。
  • {this.movie(item,index,e)}}>{item}
  • 条件渲染

    1. 通过if进行条件判断
    const {isLogin} = this.state;
    let welcome = null;
    if (isLogin) {
        welcome = 

    欢迎回来

    } else { welcome =

    请先登录!

    }
    1. 使用三目运算符
    {isLogin ? 

    欢迎回来

    :

    请先登录!

    }
    1. 使用逻辑与
    下面这种写法可以省略null。
    {isLogin && 

    你哈欧亚

    }

    列表渲染

    1. 使用map高阶函数
    {
        this.state.movies.map((item,index) => {
            return (
                
  • {this.movie(item,index,e)}}> {item}
  • ) }) }
    1. 使用filter进行过滤
      { this.state.scores.filter(item => { return item >= 60 }) }
    1. 使用slice进行截取
    区间是左闭右开。
    {
        this.state.scores.slice(0,3).map(item => {
            return 
  • {item}
  • }) }

    脚手架的基本使用

    使用脚手架创建项目
    • 项目名称不能包含大写字母。
    create-react-app demo

    组件通信

    1. 父组件向子组件传递数据通过props

    • 父组件
    export default class App extends Component {
      render() {
        return (
          
    ) } }
    • 子组件
    class Child extends Component {
        constructor(props) {
            super()
            this.props = props;
        }
        render() {
            const {name,age} = this.props;
            return (
                
    子组件获取到的name是:{name},age是:{age}
    ) } }

    参考React实战视频讲解:进入学习

    2. 子组件向父组件传递数据通过回调函数

    import React, { Component } from 'react';
    
    class Btn extends Component {
        render() {
            const {increment} = this.props;
            return (
                
            )
        }
    }
    
    
    class App extends Component {
        constructor() {
            super();
            this.state = {
                count: 0
            }
        }
        render() {
            const {count} = this.state;
            return (
                

    当前求和为:{count}

    this.increment()} />
    ); } increment() { console.log(666); this.setState({ count: this.state.count + 1 }) } } export default App;

    3. 跨组件层级通信(Context)(类组件)

    import React, { Component } from 'react'
    
    const UserContext = React.createContext({
        name: '张三',
        age: 20
    })
    
    class Sub extends Component {
        render() {
            return (
                

    name是:{this.context.name }

    age是:{this.context.age}

    ) } } Sub.contextType = UserContext function Profile() { return (
    • 设置1
    • 设置2
    • 设置3
    • 设置4
    ) } export default class App extends Component { constructor(){ super(); this.state = { name: '李四', age: 18 } } render() { return (
    ) } }
    下面是函数式组件的写法
    function Sub(props) {
        return (
            
                {                value => {                    return (                        

    name是: {value.name}

    age是: {value.age}

    ) } }
    ) }

    4. 任意组件通信(事件总线event bus)

    1. 安装events库
    npm install events
    1. 创建eventBus对象
    const eventBus = new EventEmitter()
    1. 通过emit发送消息
    1. 通过addListener来监听消息
    eventBus.addListener('sayHello',(args) => {
        this.setState({
            message: args
        })
    })
    • 在线CodeSandBox

    参数验证

    使用PropTypes进行参数验证。
    import React from 'react'
    import PropTypes from 'prop-types'
    export default function App() {
        const names = [1,2,3]
        return (
            
    ) } function Cpn(props) { const { name, age,names } = props; return (

    {name} + {age} +

    { names.map(item => item) }
    ) } Cpn.propTypes = { names: PropTypes.array, age: PropTypes.number.isRequired }

    React实现slot

    通过props进行传递jsx。
    • 父组件
    export default class App extends Component {
      render() {
        return (
          
    111} centerSlot={222} rightSlot={666} />
    ) } }
    • 子组件
    export default class NavBar extends Component {
        render() {
            const {leftSlot,centerSlot,rightSlot} = this.props;
            return (
                
    {leftSlot}
    {centerSlot}
    {rightSlot}
    ) } }

    性能优化

    1. 函数组件:使用memo
    2. 类组件:使用pureComponent

    使用ref操作DOM

    在React的开发模式中,通常情况下不需要直接操作DOM,但是某些特殊情况,确实需要直接对DOM进行操作,此时就需要用到Ref。

    注意:下面的几种方法都是在类组件中的

    1. 字符串形式的ref
      
    Hello,React
    1. 通过createRef
    class App extends Component {
      constructor(props) {
        super(props);
        this.titleRef = createRef();
      }
      render() {
        return (
          
    Hello,React
    ); } }
    1. 回调函数形式的Ref
    class App extends Component {
      constructor(props) {
        super(props);
        this.titleRef = null;
      }
      render() {
        return (
          
    this.titleRef = arg}>Hello,React
    ); } }
    在函数组件中使用ref,可以通过useRef钩子函数
    function App() {
      const titleRef = useRef();
      return (
        
    Hello,React
    ); }

    受控组件和非受控组件

    受控组件

    将可变状态保存在组件的state属性中,并且只能通过使用setState来更新,这种组件叫做受控组件。

    下面是一个受控组件的例子:

    function App() {
      const [msg,setMsg] = useState('');
      useEffect(() => {
       console.log(msg); 
      })
      return (
        
    handleSubmit(e)}>
    ); }

    非受控组件

    如果要使用非受控组件中的数据,需要使用ref来从DOM节点中获取表单数据。

    高阶组件

    高阶组件是一个接收参数为组件,返回值为新组件的函数。注意:高阶组件是一个函数。

    下面是一个高阶组件的实例:

    class App extends PureComponent {
      render() {
        return (
          
    App {this.props.name}
    ) } } function enhanceComponent(WrappedComponent) { return class newComponent extends PureComponent { render() { return } } } const EnhanceComponent = enhanceComponent(App) export default EnhanceComponent

    高阶组件的应用一:增强props

    function Home(props) {
      return (
        

    昵称:{props.nickname} 等级: {props.level} 区域:{props.region}

    ) } function enhanceProps(Cpn) { return props => { return } } const EnhanceHome = enhanceProps(Home) class App extends PureComponent { render() { return (
    ) } } export default App

    高阶组件的其他应用

    高阶组件还可以用于登录鉴权、生命周期劫持(这里的生命周期劫持,我们可以理解为计算某个组件的渲染时间)、通过forwardRef高阶函数给函数式组件传递Ref,具体不再赘述。

    portals的使用

    portals存在的意义在于,有时候我们想要一个组件独立于父组件进行渲染,例如这样的一个场景:父组件的显示区域比较小,但是我们想要一个组件显示在屏幕的中间,此时就可以使用portals。

    下面这个例子是将Modal组件渲染到屏幕的中间。

    function Modal(props) {
      return (
        ReactDOM.createPortal(props.children,document.querySelector('#modal'))
      )
    }
    
    function Home(props) {
      return (
        

    Home

    Title

    ) } export default class App extends PureComponent { render() { return (
    ) } }

    fragment

    所谓的fragment就是使用空标签来代替div标签,防止出现不必要的标签。
    <>
      

    当前求和为:{count}

    下面这种写法,可以添加属性,上面的写法则不行。
    
      

    当前求和为:{count}

    React中的CSS

    内联样式

    优点:
    1. 不会有冲突。
    2. 可以动态获取当前state中的动态。
    export default function App() {
      const pStyle = {
        color: 'pink'
      }
      return (
        

    这是标题

    这是一段文字

    ) }
    缺点:
    1. 写法上需要使用驼峰标识。
    2. 某些样式没有提示。
    3. 大量的样式,代码混乱。
    4. 某些样式无法编写,例如伪类、伪元素。

    组件文件夹下单独引入css

    这种方式容易出现样式覆盖的问题。

    CSS modules

    CSS modules可以有效的解决样式覆盖的问题。
    1. 在组件文件夹下编写CSS文件,注意后缀是.module.css
    2. 组件中引入样式
    import style from './style.module.css'
    1. 通过类名的方式使用css
    export default function App() {
      return (
        

    这是APP

    ) }
    从这种方式我们可以看出明显要好于单独写CSS。但是这种方案也有其缺点,就是引用的类名中不能包含短横线,这样无法识别,不方便动态修改某些样式。

    CSS IN JS

    CSS-in-JS是一种模式,其中CSS由JS生成而不是在外部文件中定义,此功能不是React的一部分,而是由第三方库提供。

    目前比较流行的CSS-in-JS库有:

    • styled-components(使用最多的)
    • emotion
    • glamorous
    在使用CSS-in-JS之前,我们需要掌握标签模板字符串的用法,下面是一个经典的例子:
    function test(...arg) {
      console.log(arg);  // [['123 is ', ''], '张三']
    }
    
    const name = '张三'
    
    test`123 is ${name}`
    下面介绍下,如何使用styled-components。
    1. 安装
    npm install styled-components
    1. 引入styled-components
    import styled from 'styled-components'
    1. 创建带样式的组件(注意:样式没有加引号)
    const Wrapper = styled.h1`  color: red;`
    1. 使用带样式的组件替换原生组件
    这是APP组件
    styled-components也是支持less等写法的,例如下面的例子:
    const Wrapper = styled.div`  color: red;  .banner {    background-color: blue;  }  // 注意:这里也可以使用props的写法来获取动态属性`
    给css-in-js传递动态属性。
    import React,{useState} from 'react'
    import styled from 'styled-components'
    
    const Wrapper = styled.div.attrs({
      bColor: "red"
    })`  background-color: lightblue;  border: 2px solid;  border-color: ${props => props.bColor};  color: ${props => props.color};`
    
    export default function App() {
      const [color] = useState('yellow')
      return (
        
    这是APP组件

    这是H2

    ) }

    使用classnames库给React动态添加className

    1. 安装库
    npm install classnames
    1. 引入库
    import classNames from 'classnames';
    1. 以对象的形式动态添加className
    function App() {
      const [isActive] = useState(true);
      return (
        

    这是APP

    ); }
    如果想要赋值常量,直接传入普通的字符串即可,以逗号分割。

    Antd的基本使用

    脚手架场景下

    1. 安装antd
    npm install antd
    1. 引入antd和对应的css样式
    import { Button, Space } from 'antd';
    import { PoweroffOutlined } from '@ant-design/icons';
    import './App.css'
    1. 根据官网组件的实例代码进行修改。

    通过craco对antd主题进行配置

    1. 安装@craco
    npm install @craco
    1. 自定义主题实际上需要安装下面三个包
    "@craco/craco": "^6.4.3",
    "babel-plugin-import": "^1.13.5",
    "craco-less": "^2.0.0",
    "less-loader": "^10.2.0"
    1. craco.config.js
    const CracoLessPlugin = require('craco-less');
    module.exports = {
      babel: {
          plugins: [
             [
                 "import", 
                 {
                     "libraryName": "antd",
                     "libraryDirectory": "es",
                      "style": true //设置为true即是less
                  }
              ]
          ]
      },
      plugins: [
          {
              plugin: CracoLessPlugin,
              options: {
                  lessLoaderOptions: {
                      lessOptions: {
                          modifyVars: { '@primary-color': '#7d2b21' },
                          javascriptEnabled: true,
                      },
                  },
              },
          },
      ],
    };
    强烈建议使用yarn,不要使用antd。

    给文件夹路径起别名

    首先,之所以要给文件夹起别名,就是因为有时候文件的嵌套层级比较深,不好找到文件,但是通过给根文件夹起别名则可以很快的找到它们。

    在配置文件中进行如下配置:

    const path = require('path')
    // 将参数的路径和当前的路径进行一个拼接
    const resolve = dir => path.resolve(__dirname, dir);
    module.exports = {
        webpack: {
            alias: {
                "@": resolve("src"),
                "components": resolve("src/components")
          }
        }
    };
    引入文件路径的时候则可以这样引入:”
    import Test from 'components/Test'

    评论组件案例

    • codesandbox在线代码

    axios的使用和封装

    1. 安装axios
    yarn add axios
    1. 引入axios
    import axios from 'axios'
    1. 发送get请求
    axios({
      url: "https://httpbin.org/get",
      params: {
        name: '张三',
        age: 20
      }
    }).then(res => {
      console.log(res);
    }).catch(err => {
      console.log(err);
    })
    还可以通过下面的方式:
    axios.get("https://httpbin.org/get", {
      params: {
        name: '张三',
        age: 20
      }
    }
    ).then(res => console.log(res))
    1. 发送post请求
    axios({
      url: "https://httpbin.org/post",
      data: {
        name: 'edge',
        age: 0
      },
      method: "post"
    }).then(res => {
      console.log(res);
    }).catch(err => {
      console.error(err)
    })
    也可以通过下面这种方式:
    axios.post("https://httpbin.org/post", {
      data: {
        name: 'edge',
        age: 0
      }
    }).then(console.log)
    1. axios结合async和await
      useEffect(() => {
        async function fetchData() {
          const result = await axios.post("https://httpbin.org/post", {
            data: {
              name: 'edge',
              age: 0
            }
          })
          console.log('111',result);
        }
        fetchData()
      }, [])
    1. axios.all的使用
    const request1 = axios({
      url: "https://httpbin.org/get",
      params: {name: "test",age: 20}
    })
    
    const request2 = axios({
      url: "https://httpbin.org/post",
      data: {name: 'kobe',age: 66},
      method: 'post'
    })
    
    axios.all([request1,request2]).then(([res1,res2]) => {
      console.log('axios.all:',res1,res2);
    }).catch(err => {
      console.log(err);
    })
    1. 配置多个请求的共同信息
    在引入axios的地方进行如下配置:
    axios.defaults.baseURL = "https://httpbin.org";
    axios.defaults.timeout = 5000;
    axios.defaults.headers.common["token"] = "dfasdfkajndsfkjndsf";
    axios.defaults.headers.post["Content-type"] = "application/text"
    配置后的请求则可以这样写:
    const request1 = axios({
      url: "/get",
      params: {name: "test",age: 20}
    })
    
    const request2 = axios({
      url: "/post",
      data: {name: 'kobe',age: 66},
      method: 'post'
    })
    1. 创建axios实例来实现个性化请求不同的服务器
    上面我们提到了创建公共请求的配置信息,但是有时候我们想要请求的URL可能是不同的地址,此时就需要个性化的配置了。
    const instance2 = axios.create({
      baseURL: "http://baidu.xyz",
      timeout: 1000
    })
    instance2.get('/get',{
      params: {data: "test"}
    }).then(res => console.log(res)).catch(err => console.log(err))
    1. axios拦截器
    axios.interceptors.request.use(config => {
      // 1. 可以在这个位置设置显示loading组件
      // 2. 给请求添加token
      // 3. 对params进行序列化的操作
      console.log('拦截成功');
      config.headers.token = JSON.stringify({ name: 'ty' });
      return config
    }, err => {
    
    })
    
    // // 响应拦截器
    axios.interceptors.response.use(res => {
      // res.data = 666;
      console.log('响应拦截器拦截成功');
      return res
    }, err => {
    
    })
    
    axios.get('https://httpbin.org/get', {
      params: { name: 'justin' }
    }).then(console.log).catch(console.log)
    
    axios.post('https://httpbin.org/post', {
      data: { name: 'justin6366666666' }
    }).then(res => console.log('响应:',res)).catch(console.log)

    二次封装axios

    之所以要对axios进行二次封装,主要就是一旦请求不能使用了,只需要修改一个文件即可,同时封装可以减少很多重复代码的编写。
    1. 创建一个service文件夹
    2. service文件夹下创建一个request.js
    3. service文件夹下创建一个config.js(用于书写axios的公共配置信息)
    config.js中可以写下面的配置信息:
    const devBaseURL = "https://httpbin.org";
    const proBaseURL = "https://production.org";
    
    export const BASE_URL = process.env.NODE_ENV === 'development' ? devBaseURL : proBaseURL;
    
    export const TIMEOUT = 5000;
    request.js中可以写下面的请求方法:
    import axios from "axios";
    
    import {BASE_URL,TIMEOUT} from './config'
    
    const instance = axios.create({
      baseURL: BASE_URL,
      timeout: TIMEOUT
    })
    
    export default instance

    React Hooks

    为什么需要Hooks?

    Hook是React16.8中新增的特性,它可以让我们在不编写class的情况下使用state以及其他的React特性。

    在Hook出现之前,函数式组件相对于class组件有如下劣势:

    • class组件可以定义自己的状态,函数式组件不可以。
    • class组件有自己的生命周期,函数式组件则会每次重新渲染都重新发送一次网络请求。
    • 函数式组件在重新渲染时整个函数都会被执行。

    class组件存在的问题

    1. 随着业务的增加,逻辑的复杂,class组件可能会变得越来越复杂,比如componetDidMount中可能包含大量的逻辑代码,这样的class实际上难以拆分,逻辑混在一起,代码的复杂度比较高。
    2. class组件中的this指向比较复杂,难以理解。
    3. 组件复用状态难。例如我们使用Provider、Consumer来共享状态,但是多次使用Consumer时,我们的代码就会存在很多嵌套。

    为什么叫做Hook?

    Hook直接翻译可能是钩子的意思,意味着这类函数可以帮助我们钩入React的state以及生命周期等特性。

    使用Hooks的两个规则

    1. 只能在函数最外层调用Hook,不要在循环、条件判断、或者子函数中调用。
    2. 只能在React的函数式组件中调用Hook,不能在JS函数中调用。

    useState的核心用法

    useState可以接收一个函数,也可以接收一个值,如果是函数,其可以拿到前一个状态,但是返回的要是最新的状态,如果是值的话,就应该是返回的最新状态。
    
    

    useEffect的核心用法

    useEffect主要是用来模拟生命周期。

    • useEffect在一个函数组件中可以定义多个,并按照顺序执行。
      useEffect(() => {
        console.log('修改DOM');
      })
    
      useEffect(() => {
        console.log('订阅事件');
      },[])
    • 检测某个状态发生变化的时候才执行回调函数。
      useEffect(() => {
        console.log('订阅事件');
      },[count])

    useContext的核心用法

    // 1. 创建一个xxxContext
    const countContext = createContext();
    
    // 2. 通过xxxContext.Provider 包裹传递value给目标组件
    function App() {
        return (
            
                
            
        )
    }
    // 3. 目标组件通过useContext(xxxContext)获取value传递的值
    function Foo() {
        const count = useContext(countContext)
        return (
            
    {count}
    ) }

    useReducer的核心用法

    useReducer是useState的一种替代方案。
    const reducer = (state,action) => {
      switch (action.type) {
        case "increment":
          return {...state,count: state.count + 1}
        default:
          return state;
      }
    }
    
    export default function Home() {
      const [count,dispatch] = useReducer(reducer,{count: 0});
      return (
        

    当前求和为:{count.count}

    ) }

    useCallback的核心用法

    useCallback会返回一个函数的memorized值,在依赖不变的情况下,多次定义的时候,返回的值是相同的。

    useCallback想要解决的问题是这样的,假如一个函数组件中有一个函数,只要状态发生改变,这个函数都会被重新定义,十分浪费性能,并且可能带来不好的影响。

    useCallback结合memo可以进行性能优化,确保传入的是相同的函数实例。useCallback如果依赖项是一个空数组,则只会执行一次,返回的都是相同的函数实例,如果有依赖项的话,则是依赖项发生变化才返回新的实例。

    常见的使用场景是:将一个函数传递给组件进行回调时,可以进行性能优化。

    export default function CallbackDemo() {
      console.log('CallbackDemo被重新渲染');
      const [count, setCount] = useState(0);
      const [show,setShow] = useState(true);
      const increment1 = useCallback(() => {
        console.log('increment1函数执行了~');
        setCount(count + 1);
      })
    
      const increment2 = useCallback(() => {
        console.log('increment2函数执行了~');
        setCount(count + 1);
      }, [count])
      return (
        

    当前求和为:{count}

    ) }

    useMemo的核心用法

    useMemo的核心也是为了性能优化。
    • useMemo返回的也是一个缓存的值。
    • 依赖不变的情况下,多次定义的时候,返回的值是相同的。
    下面的这个例子可以很好的说明useMemo的核心用法,可以有效的避免calc函数不必要的重新计算。
    const calc = (num) => {
      console.log('重新计算');
      let temp = 0;
      for (let i = 1; i <= num; i++) {
        temp += i;
      }
      return temp;
    }
    
    export default function MemoHookDemo() {
      const [count, setCount] = useState(10);
      const [show,setShow] = useState(true);
      // 避免calc函数出现不必要的重新计算
      const total = useMemo(() => {
        return calc(count);
      },[count])
      return (
        

    当前求和为:{total}

    ) }
    useMemo还可以避免子组件不必要的重新渲染。(结合了memo)
    const MyButton = memo(props => {
      console.log('子组件重新渲染');
      return (
        

    子组件收到的props:{props.info.name}

    ) }) export default function MemoHookDemo02() { console.log('父组件重新渲染'); const [show,setShow] = useState(false); const info = useMemo(() => { return {name: "漫威"} },[]) return (
    ) }

    useRef的核心用法

    1. 使用ref引用DOM。
    export default function RefHook() {
      const titleRef = useRef();
    
      const changeDOM = () => {
        titleRef.current.innerHTML = "引用DOM"
      }
      return (
        

    这是useRef的核心用法

    ) }
    1. 函数式组件是不能直接给ref的。
    函数组件可以通过React.forwardRef进行包裹来使用ref。
    const Test = React.forwardRef((props,ref) => {
      return (
        

    这是Test组件

    ) })
    1. 使用useRef跨足剑周期保存数据
    export default function RefHook() {
      const [count,setCount] = useState(0);
      const countRef = useRef(count);
    
      return (
        

    useRef中保存的值:{countRef.current}

    这是count的值:{count}

    ) }

    useImperativeHandle的核心用法

    之所以要有useImperativeHandle这个钩子函数,是为了防止父组件通过ref获取到子组件的所有权限,通过useImperativeHandle可以让子组件指定对外暴露的功能。
    const Son = forwardRef((props,ref) => {
      const inputRef = useRef();
    
      useImperativeHandle(ref,() => ({
        focus: () => {
          inputRef.current.focus();
        }
      }))
    
      return (
        
      )
    })
    
    export default function RefHook() {
      const sonRef = useRef()
      return (
        
    ) }

    useLayoutEffect的核心用法

    useLayoutEffect和useEffect的区别主要有以下两点:
    • useEffect是在DOM更新完成之后执行,不会阻塞DOM的更新。
    • useLayoutEffect会在更新DOM之前执行,会阻塞DOM的更新。
    如果希望在某些操作发生之后再去更新DOM,那么这个操作应该放在useLayoutEffect中执行。主要是解决闪烁问题。
    export default function LayoutDemo() {
      const [count,setCount] = useState(10);
    
      // useEffect会在渲染之后执行
      // useEffect(() => {
      //   if (count === 0) {
      //     setCount(Math.random());
      //   }
      // },[count])
    
      // useLayoutEffect会在渲染之前执行
      useLayoutEffect(() => {
        if (count === 0) {
          setCount(Math.random());
        }
      },[count])
    
      return (
        

    当前随机数为:{count}

    ) }

    自定义Hook的核心用法(主要还是用于逻辑复用)

    自定义Hook的本质是一种函数代码逻辑的抽取。自定义组件必须以use开头,否则会报错。

    下面的这个自定义Hook就是对组件的挂载和卸载中重复的逻辑进行复用。

    export default function CustomHook() {
      useInfo('CustomHook');
      return (
        

    这是测试自定义Hook

    ) } function useInfo(name) { useEffect(() => { console.log(`${name}组件被挂载了~`); return () => { console.log(`${name}组件被卸载了~`); }; }, []); }
    自定义Hook和普通的函数封装的区别在于,自定义Hook可以使用默认的Hooks,类似于useState等,但是普通的函数不能使用,这也就是为什么自定义Hook在命名时需要以use开头。

    react-router的核心用法

    安装react-router-dom
    yarn add react-router-dom

    react-router中最核心的API

    BrowserRouter和HashRouter

    • Router中包含了对路径改变的监听,并且会将相应的路径传递给子组件。
    • BrowserRouter使用History模式。
    • HashRouter使用Hash模式。

    Link和NavLink

    • 一般路径的跳转使用Link组件,其最终会被渲染成a元素。
    • NavLink是在Link基础上增加一些样式属性。
    • to属性,指定跳转到的路径。

    Route

    • Route用于路径的匹配
    • path属性:用于设置匹配到的路径。
    • component属性:设置匹配到的路径后,渲染的组件。
    • exact:精准匹配,只有精准匹配到完全一致的路径,才会渲染对应的组件。

    基本使用

    下面使用的是一些新特性:
    export default function RouteTest() {
      return (
        
    首页 关于 {/* */} {/* 下面是React18的新语法 */} } /> } />
    ) }

    注意:Link都会显示成a标签,但是并不是所有的Route都会显示,Route所在的区域就是命中路由的组件要显示的区域。我们可以把Route理解为占位符。

    react-router的不同版本的特点都是不一样的,因此,有些特定功能的用法一定要根据版本去官网查用法,例如下面的这个给选中的link改变颜色,就是通过这个版本对应的官网查到的。

    • react-router-V6
    export default function RouteTest() {
      let activeStyle = {
        color: "red",
      };
      return (
        
    {/* */} {/* 下面是React18的新语法 */} } /> } /> isActive ? activeStyle : undefined }> 首页 isActive ? activeStyle : undefined }> 关于
    ) }
    需要注意的是在react-router(V6)版本中Switch已经被Routes取代了。

    路由重定向

    重定向和Link的区别在于,Link是需要用户点击的,重定向可以是JS执行的。

    在V6版本的react-router-dom中重定向Redirect已经被Navicat这个API取代了、

    import {Navigate} from 'react-router-dom'
    
    const User = () => {
      const [isLogin] = useState(false);
      return isLogin ? (
        

    这是User组件

    ) : ; }

    动态路由

    需要注意的是,设置动态路由的时候最好在某个路径下使用,而不是直接就是一个动态路由,那样容易出现拦截到意外路由的情况。
    } />
    • 使用useParams获取动态路由的值。
    import {Navigate,useParams} from 'react-router-dom'
    
    const User = () => {
      const [isLogin] = useState(true);
      const params = useParams();
      console.log(params);
      return isLogin ? (
        

    这是User组件

    ) : ; }
    • 使用useSearchParams获取查询字符串(通过原型对象上的get方法来获取值)
    import {Navigate,useSearchParams} from 'react-router-dom'
    
    const User = () => {
      const [isLogin] = useState(true);
      const [searchParams,setSearchParams] = useSearchParams();
      console.log(searchParams);
      console.log(searchParams.get('name'));
      return isLogin ? (
        

    这是User组件

    ) : ; }
    • 使用遍历的方式获取到所有的查询字符串
    import {Navigate,useSearchParams} from 'react-router-dom'
    
    const User = () => {
      const [isLogin] = useState(true);
      const [searchParams,setSearchParams] = useSearchParams();
      searchParams.forEach((item,key) => {
        console.log(item,key);
      })
      console.log(searchParams.get('name'));
      return isLogin ? (
        

    这是User组件

    ) : ; }

    使用react-router-config简化路由的编写

    具体可以通过查看官网使用。
    • react-router-config

    嵌套路由

    嵌套路由我们可以理解为路由中的路由。(需要使用Outlet进行占位,具体看下面的链接中的文章。)
      
        
          }>        } />        } />      
      
    • react-router v6 使用(这篇文章讲的特别好)

    手动路由跳转

    在react-router-dom 6版本中history这个API被useNavigate取代了。
    const About = () => {
      const navigate = useNavigate()
      return (
        
    企业文化 联系我们
    ); }

    你可能感兴趣的:(react.js)