React快速入门,一文弄懂react的基本使用和高级特性(二)

简介: 在本文中,融合大量案例🌰和动图🕹️进行展示。可以把它当成是 react 的入门宝库,有不懂的语法知识点时或许在这里可以寻找到你的答案并且通过例子运用起来。叮,废话不多说,下面来开始探索 react 的奥秘吧👏

📖二、React高级特性


1、函数组件

我们先来了解 class 组件和函数组件分别是什么样的。先看 class 组件,代码如下:

// class 组件
class List extends React.Component {
    constructor(props) {
        super(props)
    }
    redner() {
        const { list } = this.props
        return <ul>{list.map((item, index) => {
            return <li key={item.id}>
                  <span>{item.title}</span>
                </li>
        })}</ul>
    }
}
复制代码

函数组件的形式如下:

// 函数组件
function List(props) {
    const { list } = props
    return <ul>{list.map((item, idnex) => {
        return <li key={item.id}>
              <span>{item.title}</span>
            </li>
    })}</ul>
}
复制代码

现在我们来梳理以下, class 组件和函数组件两者之间的区别。所谓函数组件,具有以下特点:

  • 只是一个纯函数,它输入的是 props ,输出的是 JSX
  • 函数组件没有实例没有生命周期,也没有 state
  • 函数组件不能扩展其他方法

相反地, class 组件就拥有函数组件相异的特点。


2、非受控组件

在上述表单模块,我们谈论到了受控组件,那接下来,我们就来谈论非受控组件

所谓非受控组件,就是 input 里面的值,不受到 state 的控制。下面我们先来看几种场景。


(1)input

先来看一段代码:

import React from 'react'
class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: '星期一研究室',
            flag: true,
        }
        this.nameInputRef = React.createRef() // 创建 ref
    }
    render() {
        // input defaultValue
        return <div>
            {/* 使用 defaultValue 而不是 value ,使用 ref */}
            <input defaultValue={this.state.name} ref={this.nameInputRef}/>
            {/* state 并不会随着改变 */}
            <span>state.name: {this.state.name}</span>
            <br/>
            <button onClick={this.alertName}>alert name</button>
        </div>
    }
    alertName = () => {
        const elem = this.nameInputRef.current // 通过 ref 获取 DOM 节点
        alert(elem.value) // 不是 this.state.name
    }
}
export default App
复制代码

此时浏览器的显示效果为:

1.png

大家可以看到,如果是非受控组件,那么需要使用 defaultValue 去控制组件的值。且最终 input 框里面的内容不论我们怎么改变,都不会影响到 state 的值。


(2)checkbox

对于复选框 checkbox 来说,先看以下代码:

import React from 'react'
class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: '星期一研究室',
            flag: true,
        }
    }
    render() {
        // checkbox defaultChecked
        return <div>
            <input
                type="checkbox"
                defaultChecked={this.state.flag}
            />
            <p>state.name: { this.state.flag === true ? 'true' : 'false' }</p>
        </div>
    }
}
export default App
复制代码

此时浏览器的显示效果如下:

2.png

大家可以看到,复选框如果当非受控组件来使用的使用,那么使用 defaultCkecked 来对值进行控制。同时,我们也看到了,最终不管 checked 的值如何改变, state 的值都不受影响。


(3)file

先来看一段代码:

import React from 'react'
class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: '星期一研究室',
            flag: true,
        }
        this.fileInputRef = React.createRef()
    }
    render() {
        // file
        return <div>
            <input type="file" ref={this.fileInputRef}/>
            <button onClick={this.alertFile}>alert file</button>
        </div>
    }
    alertFile = () => {
        const elem = this.fileInputRef.current // 通过 ref 获取 DOM 节点
        alert(elem.files[0].name)
    }
}
export default App
复制代码

此时浏览器的显示效果为:

3.png

在上面的代码中,我们使用通过 ref 去获取 DOM 节点,接着去获取到文件的名字。像 file 这种类型的固定,值并不会一直固定的,所以也是一个非受控组件


(4)总结梳理

setState 只能处理类似于前端的显示和渲染相关的,像文件上传这种交互类型的就处理不了。下面我们来梳理下非受控组件的几大使用场景具体如下:

  • 必须手动操作 DOM 元素, setState 并无法手动操作 DOM 元素;
  • 文件上传类型 <input type=file>
  • 某些富文本编辑器,需要传入 DOM 元素。

受控组件 vs 非受控组件的区别如下:

  • 优先使用受控组件,符合 React 设计原则;
  • 必须操作 DOM 时,再使用非受控组件


3、Portals


(1)为什么要用 Portals ?

一般情况下,组件默认会按照既定层次嵌套渲染。类似下面这样:

<div id="root">
    <div>
        <div>
            <div class="model">Modal内容</div>
        </div>
    </div>
</div>
复制代码

大家可以看到,这样不断嵌套,但里面却只有一层区域的内容是有用的。从某种程度上来说,是非常不好的。那我们想做的事情是,如何让组件渲染到父组件以外呢?

这个时候就需要用到 Portals


(2)如何使用

先来看一段代码:

import React from 'react'
import ReactDOM from 'react-dom'
import './style.css'
class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
        }
    }
    render() {
        // 正常渲染
        // return <div className="modal">
        //     {this.props.children} {/* vue slot */}
        // </div>
        // 使用 Portals 渲染到 body 上。
        // fixed 元素要放在 body 上,有更好的浏览器兼容性。
        return ReactDOM.createPortal(
            <div className="modal">{this.props.children}</div>,
            document.body // DOM 节点
        )
    }
}
export default App
复制代码

style.css 代码如下:

.modal {
    position: fixed;
    width: 300px;
    height: 100px;
    top: 100px;
    left: 50%;
    margin-left: -150px;
    background-color: #000;
    /* opacity: .2; */
    color: #fff;
    text-align: center;
}
复制代码

此时,我们来看下浏览器节点的渲染效果。具体如下:

16.png

大家可以看到,通过使用 ReactDOM.createPortal() ,来创建 Portals 。最终 modals 节点成功脱离开父组件,并渲染到组件外部。


(3)使用场景

现在,我们来梳理一些 Portals 常见的场景。

Portals 常用于解决一些 css 兼容性问题。通常使用场景有:

  • overflow:hidden; 触发 bfc
  • 父组件 z-index 值太小;
  • position:fixed 需要放在 body 第一层级。


4、context


(1)使用场景

有时候我们经常会有一些场景出现切换的频率很频繁,比如语言切换、或者是主题切换,那如何把对应的切换信息给有效地传递给每个组件呢?

使用 props ,又有点繁琐;使用 redux ,又太小题大做了。

因此,这个需要我们可以用 react 中的 context


(2)举例阐述

先来看一段代码:

import React from 'react'
// 创建 Context 填入默认值(任何一个 js 变量)
const ThemeContext = React.createContext('light')
// 底层组件 - 函数是组件
function ThemeLink (props) {
    // const theme = this.context // 会报错。函数式组件没有实例,即没有 this
    // 函数式组件可以使用 Consumer
    return <ThemeContext.Consumer>
        { value => <p>link's theme is {value}</p> }
    </ThemeContext.Consumer>
}
// 底层组件 - class 组件
class ThemedButton extends React.Component {
    // 指定 contextType 读取当前的 theme context。
    // static contextType = ThemeContext // 也可以用 ThemedButton.contextType = ThemeContext
    render() {
        const theme = this.context // React 会往上找到最近的 theme Provider,然后使用它的值。
        return <div>
            <p>button's theme is {theme}</p>
        </div>
    }
}
ThemedButton.contextType = ThemeContext // 指定 contextType 读取当前的 theme context。
// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar(props) {
    return (
        <div>
            <ThemedButton />
            <ThemeLink />
        </div>
    )
}
class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            theme: 'light'
        }
    }
    render() {
        return <ThemeContext.Provider value={this.state.theme}>
            <Toolbar />
            <hr/>
            <button onClick={this.changeTheme}>change theme</button>
        </ThemeContext.Provider>
    }
    changeTheme = () => {
        this.setState({
            theme: this.state.theme === 'light' ? 'dark' : 'light'
        })
    }
}
export default App
复制代码

此时浏览器的显示效果为:

1.png

在上图中,我们做到了主题的切换。现在,我们来分析下上述的代码。

首先,我们创建了一个 Context ,也就是 ThemeContext ,并传入了 light 值。

其次,核心在 <Toolbar /> 组件。 Toolbar 现有组件为 ThemedButtonThemeLink 。其中,我们先指定 ThemedButtoncontextType 去读取当前的 ThemeContext ,那么就取到了默认值 light

接着,来到了 ThemeLink 组件。 ThemeLink 是一个函数式组件,因此,我们可以直接使用 ThemeContext.Consumer 来对其进行传值。

上面两个组件的值都取到了,但那只是 ThemeContext 的初始值。取到值了之后呢,我们还要修改值, React 会往上找到最近的 ThemeContext.Provider ,通过 value={this.state.theme} 这种方式,去修改和使用 ThemeContext 最终使用的值 。


5、异步组件(懒加载)

在项目开发时,我们总是会不可避免的去加载一些大组件,这个时候就需要用到异步加载。在 vue 中,我们通常使用 import() 来加载异步组件,但在 react 就不这么使用了。

React 通常使用 React.lazyReact.Suspense 来加载大组件。

如下代码所示:

import React from 'react'
const ContextDemo = React.lazy(() => import('./ContextDemo'))
class App extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        return <div>
            <p>引入一个动态组件</p>
            <hr />
            <React.Suspense fallback={<div>Loading...</div>}>
                <ContextDemo/>
            </React.Suspense>
        </div>
        // 1. 强制刷新,可看到 loading (看不到就限制一下 chrome 的网速,Performance的network)
        // 2. 看 network 的 js 加载
    }
}
export default App
复制代码

首先我们使用 import 去导入我们要加载的组件。之后使用 React.lazy 去将这个组件给进行注册,也就是 ContextDemo 。最后使用 React.Suspense 来加载 ContextDemo 。至此,我们完成了该异步组件的加载。


6、性能优化


(1)shouldComponentUpdate(简称SCU)

先来看下面这一段代码:

shouldComponentUpdate(nextProps, nextState) {
    if (nextState.count !== this.state.count
        || nextProps.text !== this.props.length) {
        return true // 可以渲染
    }
    return false // 不重复渲染
}
复制代码

React 中,默认的是,当父组件有更新,子组件也无条件更新。那如果每回都触发更新,肯定不太好。

因此,这个时候我们需要用到 shouldComponentUpdate ,判断当属性有发生改变时,可以触发渲染。当属性不发生改变时,也就是前后两次的值相同时,就不触发渲染。

那这个时候我们 需要思考一个问题SCU 一定要每次都用吗?答案其实不是肯定的

我们会去用 SCU ,从某种层面上来讲就是为了优化。因此,我们需要依据当前的开发场景,有需要的时候再去优化。

现在,我们来总结一下 SCU 的使用方式,具体如下:

  • SCU默认返回 true ,即 React 默认重新渲染所有子组件;
  • 必须配合 “不可变值” 一起使用;
  • 可先不用 SCU ,有性能问题时再考虑使用。


(2)PureComponent和React.memo

PureComponentreact 中的使用形式如下:

class List extends React.PureComponent {
    constructor(props) {
        super(props)
    }
    render() {
        const { list } = this.props
        return <ul>{list.map((item, index) => {
            return <li key={item.id}>
                <span>{item.title}</span>
            </li>
        })}</ul>
    }
    shouldComponentUpdate() {/*浅比较*/}
}
复制代码

如果我们使用了 PureComponent ,那么 SCU 会进行浅层比较,也就是一层一层的比较下去。


下面我们来看 memomemo ,顾名思义是备忘录的意思。在 React 中的使用形式如下:

function MyComponent(props) {
    /* 使用props 渲染 */
}
function areEqual(prevProps, nextProps) {
    /*
    如果把 nextProps传入render方法的返回结果 与
     preProps传入render方法的返回结果 一致的话,则返回true,
    否则返回false
    */
}
export default React.memo(MyComponent, areEqual);
复制代码

memo ,可以说是函数组件中的 PureComponent 。同时,使用 React.memo() 的形式,将我们的函数组件areEqual 的值进行比较,最后返回一个新的函数

值得注意的是,在 React 中,浅比较已经使用于大部分情况,一般情况下,尽量不要做深度比较


(3)不可变值

React 中,用于做不可变值的有一个库: Immutable.js 。这个库有以下几大特点:

  • 彻底拥抱“不可变值”
  • 基于共享数据(不是深拷贝),速度好
  • 有一定的学习和迁移成本,按需使用

下面来看一个使用例子:

const map1 = Immutable.Map({ a: 1, b: 2, c: 3 })
const map2 = map1.set('b', 50)
map1.get('b') // 2
map2.get('b') // 50
复制代码

基本上现在在开发中都用这个库来处理不可变值的问题。在实际使用中,可以看官方文档按需使用即可。


7、关于组件公共逻辑的抽离

React 中,对于组件公共逻辑的抽离主要有三种方式要了解。具体如下:

  • mixin ,已被 React 弃用
  • 高阶组件 HOC
  • Render Props

下面将讲解高阶组件 HOCRender Props


(1)高阶组件 HOC

先看一段代码:

// 高阶组件不是一种功能,而是一种设计模式
// 1.传入一个组件 Component
const HOCFactory = (Component) => {
    class HOC extends React.Component {
        // 在此定义多个组件的公共逻辑
        render() {
            // 2.返回拼接的结果
            return <Component {this.props} /> 
        }
    }
    return HOC
}
const EnhancedComponent1 = HOCFactory(WrappedComponent1)
const EnhancedComponent2 = HOCFactory(WrappedComponent2)
复制代码

高阶组件 HOC传入一个组件,返回一个新的组件,见上方代码的 1和2


下面来看一个例子,如下代码所示:

import React from 'react'
// 高阶组件
const withMouse = (Component) => {
    class withMouseComponent extends React.Component {
        constructor(props) {
            super(props)
            this.state = { x: 0, y: 0 }
        }
        handleMouseMove = (event) => {
            this.setState({
                x: event.clientX,
                y: event.clientY
            })
        }
        render() {
            return (
                <div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}>
                    {/* 1. 透传所有 props 2. 增加 mouse 属性 */}
                    <Component {...this.props} mouse={this.state}/>
                </div>
            )
        }
    }
    return withMouseComponent
}
const App = (props) => {
    const { x, y } = props.mouse // 接收 mouse 属性
    return (
        <div style={{ height: '500px' }}>
            <h1>The mouse position is ({x}, {y})</h1>
        </div>
    )
}
export default withMouse(App) // 返回高阶函数
复制代码

此时浏览器的显示结果为:

2.png

在上面的代码中,我们用 定义了高阶组件 withMouse ,之后它通过 <Component {...this.props} mouse={this.state}/> 这种形式,将参数 propsprops 的 mouse 属性给透传出来,供子组件 App 使用。


值得注意的是,在 react 中,还有一个比较常见的高阶组件是 redux connect用一段代码来演示:

import { connect } from 'react-redux';
// connect 是高阶组件
const VisibleTodoList = connect(
  mapStateToProps,
    mapDispatchToProps
)(TodoList)
export default VisibleTodoList
复制代码

现在,我们来看下 connect 的源码,具体如下:

export const connect =(mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {
    class Connect extends Component {
        constructor() {
            super()
            this.state = {
                allProps: {}
            }
        }
        /* 中间省略 N 行代码 */
        render () {
      return <WrappedComponent {...this.state.allProps} />
        }
    }
    return Connect
}
复制代码

大家可以看到, Connect 也是同样地,传入一个组件,并返回一个组件。


(2)Render Props

先来看一段代码:

// Render Props 的核心思想
// 1.通过一个函数,将class组件的state作为props,传递给纯函数组件
class Factory extends React.Component {
    constructor() {
        tihs.state = {
            /* state 即多个组件的公共逻辑的数据 */
        }
    }
    /* 2.修改 state */
    render() {
        return <div>{this.props.render(this.state)}</div>
    }
}
const App = () => {
    // 3.在这里使用高阶组件,同时将高阶组件中的render属性传递进来
    <Factory render={
        /* render 是一个函数组件 */
        (props) => <p>{props.a}{props.b} …</p>
    } />
}
export default App;
复制代码

在上面的高阶组件 HOC 中,最终返回的结果也是一个高阶组件。但在 Render Props 中,我们把 Factory 包裹在定义的 App 组件中,最终再把 App 返回。

值得注意的是,在 Vue 中有类似于高阶组件的用法,但没有像 Render Props 类似的用法,这一点需要稍微留意一下。


下面来看一个例子,具体代码如下:

import React from 'react'
import PropTypes from 'prop-types'
class Mouse extends React.Component {
    constructor(props) {
        super(props)
        this.state = { x: 0, y: 0 }
    }
    handleMouseMove = (event) => {
      this.setState({
        x: event.clientX,
        y: event.clientY
      })
    }
    render() {
      return (
        <div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}>
            {/* 将当前 state 作为 props ,传递给 render (render 是一个函数组件) */}
            {this.props.render(this.state)}
        </div>
      )
    }
}
Mouse.propTypes = {
    render: PropTypes.func.isRequired // 必须接收一个 render 属性,而且是函数
}
const App = (props) => (
    <div style={{ height: '500px' }}>
        <Mouse render={
            /* render 是一个函数组件 */
            ({ x, y }) => <h1>The mouse position is ({x}, {y})</h1>
        }/>
    </div>
)
/**
 * 即,定义了 Mouse 组件,只有获取 x y 的能力。
 * 至于 Mouse 组件如何渲染,App 说了算,通过 render prop 的方式告诉 Mouse 。
 */
export default App
复制代码

此时,浏览器的显示效果如下:

3.png

在上面的代码中,通过 this.props.render(this.state) 这种形式,将 Mouse 组件中的属性传递给 App ,并让 App 成功使用到 Mouse 的属性值。


(3)HOC vs Render Props

现在,我们来梳理下 HOCRender Props 的区别,具体如下:

  • HOC:模式简单,但会增加组件层级
  • Render Props:代码简洁,学习成本较高
  • 各有各的优缺点,根据实际场景按需使用即可


📚三、Redux和React-router


1、Redux


(1)Redux概念简述

对于 react  来说,它是一个非视图层的轻量级框架,如果要用它来传递数据的话,则要先父传子,然后再慢慢地一层一层往上传递。

但如果用 redux 的话,假设我们想要某个组件的数据,那这个组件的数据则会通过 redux 来存放到 store 中进行管理。之后呢,通过 store ,再来将数据一步步地往下面的组件进行传递。

值得注意的是,我们可以视 ReduxReducerFlux 的结合。


(2)Redux的工作流程

Redux ,实际上就是一个数据层的框架,它把所有的数据都放在了 store 之中。我们先来看一张图:

17.png

大家可以看到中间的 store ,它里面就存放着所有的数据。继续看 store 向下的箭头,然后呢,每个组件都要向 store 里面去拿数据。

我们用一个例子来梳理整张图,具体如下:

  • ①整张图上有一个 store ,它存放着所有的数据,也就是存储数据的公共区域
  • ②每个组件,都要从 store 里面拿数据;
  • ③假设现在有一个场景,模拟我们要在图书馆里面借书。那么我们可以把 react Component 理解为借书人,之后呢,借书人要去找图书馆管理员才能借到这本书。而借书这个过程中数据的传递,就可以把它视为是 Action Creators ,可以理解为 “你想要借什么书” 这句话。
  • Action Creatures 去到 store 。这个时候我们把 store 当做是图书馆管理员,但是,图书馆管理员是没有办法记住所有图书的数据情况的。一般来说,它都需要一个记录本,你想要借什么样的书,那么她就先查一下;又或者你想要还什么书,她也要查一下,需要放回什么位置上。
  • ⑤这个时候就需要跟 reducers 去通信,我们可以把 reducers 视为是一个记录本,图书馆管理员用这个记录本来记录需要的数据。管理员 store 通过 reducer 知道了应该给借书人 Components 什么样的数据。


(3)react-redux

React-redux 中要了解的几个点是 ProviderConnectmapStateToPropsmapDisptchToProps

来看以下代码:

import React from 'react'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'
let store = createStore(todoApp)
export default function() {
    return <Provider store={store}>
          <App />
        </Provider>
}
复制代码

react-redux 提供了 Provider 的能力,大家可以看到最后部分的代码, Provider<App /> 包裹起来,其实也就是说为它包裹的所有组件提供 store 能力,这也是 Provider 发挥的作用。

再来看一段代码:

import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'
// 不同类型的 todo 列表
const getVisibleTodos = (todos, filter) => {
  switch (filter) {
    case 'SHOW_ALL':
      return todos
    case 'SHOW_COMPLETED':
      return todos.filter(t => t.completed)
    case 'SHOW_ACTIVE':
      return todos.filter(t => !t.completed)
  }
}
const mapStateToProps = state => {
  // state 即 vuex 的总状态,在 reducer/index.js 中定义
  return {
    // 根据完成状态,筛选数据
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}
const mapDispatchToProps = dispatch => {
  return {
    // 切换完成状态
    onTodoClick: id => {
      dispatch(toggleTodo(id))
    }
  }
}
// connect 高阶组件,将 state 和 dispatch 注入到组件 props 中
const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)
export default VisibleTodoList
复制代码

在上面的代码中, connectstatedispatch 给注入到组件的 props 中,将属性传递给到 TodoList 组件。


(4)异步action

redux 中的同步action 如下代码所示:

// 同步 action
export const addTodo = text => {
    // 返回 action 对象
    return {
        type: 'ADD_TODO',
        id: nextTodoId++,
        text
    }
}
复制代码

redux 中的异步action 如下代码所示:

// 异步 action
export const addTodoAsync = text => {
    // 返回函数,其中有 dispatch 参数
    return (dispatch) => {
        // ajax 异步获取数据
        fetch(url).thne(res => {
            // 执行异步 action
            dispatch(addTodo(res.text))
        })
    }
}
复制代码


(5)Redux数据流图

Redux 的单项数据流图如下所示:

18.png

关于 Redux 更详细内容,可查看这篇文章:Redux从入门到进阶,看这一篇就够了!


2、React-router


(1)路由模式

React-routervue-router 一样,都是两种模式具体如下:

hash模式的路由配置如下代码所示:

import React from 'react'
import {
    HashRouter as Router,
    Switch,
    Route
} from 'react-router-dom'
function RouterComponent() {
    return(
      <Router>
          <Switch>
            <Route exact path="/">
              <Home />
            </Route>
            <Route exact path="/project/:id">
              <Project />
            </Route>
            <Route path="*">
              <NotFound />
            </Route>
          </Switch>
        </Router>
    )
}
复制代码

History模式的路由配置如下:

import React from 'react'
import {
    BrowserRouter as Router,
    Switch,
    Route
} from 'react-router-dom'
function RouterComponent() {
    return(
      <Router>
          <Switch>
            <Route exact path="/">
              <Home />
            </Route>
            <Route exact path="/project/:id">
              <Project />
            </Route>
            <Route path="*">
              <NotFound />
            </Route>
          </Switch>
        </Router>
    )
}
复制代码

注意,hashhistory 的区别在于 import 中的 HashRouterBrowserRouter

关于 hashhistory 相关的内容,进一步了解可查看这篇文章:浅谈前端路由原理hash和history


(2)路由配置


Ⅰ. 动态路由

假设现在有父组件 RouterComponent具体代码如下:

function RouterComponent() {
    return(
      <Router>
          <Switch>
            <Route exact path="/">
              <Home />
            </Route>
            <Route exact path="/project/:id">
              <Project />
            </Route>
            <Route path="*">
              <NotFound />
            </Route>
          </Switch>
        </Router>
    )
}
复制代码

其中,在这个组件中还有一个 Project 组件,需要进行动态传参。


继续,我们来看下子组件 Project 组件时如何进行动态传参的。具体代码如下:

import React from 'react'
import { Link, useParams } from 'react-router-dom'
function Project() {
    // 获取 url 参数,如 '/project/100'
    const { id } = useParams()
    console.log('url param id', id)
    return (
      <div>
          <Link to="/">首页</Link>
        </div>
    )
}
复制代码

大家可以看到,在 React 中,通过 const { id } = useParams() 这样的形式,来进行动态传参。


还有另外一种情况是跳转路由请看以下代码:

import React from 'react'
import { useHistory } from 'react-router-dom'
function Trash() {
    let history = useHistory()
    function handleClick() {
        history.push('/')
    }
    return (
      <div>
          <Button type="primary" onClick={handleClick}>回到首页</Button>
        </div>
    )
}
复制代码

大家可以看到,通过使用 useHistory ,让点击事件跳转到首页中。


Ⅱ. 懒加载

import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import React, { Suspence, lazy } from 'react';
const Home = lazy(() => import('./routes/Home'));
const About lazy(() => import('./routes/About'));
const App = () => {
    <Router>
      <Suspense fallback={<div>Loading……</div>}>
          <Switch>
            <Route exact path="/" component={Home}/>
                 <Route path="/about" component={About}/>
          </Switch>
        </Suspense>    
    </Router>
}
复制代码

React 中,我们可以直接用 lazy() 包裹,对页面的内容进行懒加载。当然,还有另外一种情况是,加载类似于首页初次加载页面 Loading 的那种效果,在 react 中可以使用 <Suspense> 来解决。


🗞️四、结束语



在上面的文章中,我们讲解了 react 的基本使用以及高级特性。同时,还讲解了 react 的周边插件, ReduxReact-router

前端在做 react 的项目时,总是脱离不开以上文章所涉及到的知识点,唯一的区别在于基本使用的内容用的较多,而高级特性的使用场景相对会少一些。

希望通过上文的讲解,小伙伴们有所收获🥂



相关文章
|
6月前
|
前端开发 API UED
React 18有哪些新特性值得关注
【4月更文挑战第18天】React 18推出了新渲染引擎React Reconciler,提升性能和可扩展性;优化SSR,加快首屏加载;新事件处理API增强控制与可读性;自动批量处理减少渲染次数;引入过渡和并发模式,精细控制更新优先级;改变根节点挂载方式,提升响应速度。不支持IE,新增API和服务端渲染优化。React 18在性能、体验和开发效率上迈出重要一步。
480 2
|
1月前
|
前端开发 开发者
React 18 的新特性
【10月更文挑战第12天】 React 18 引入了并发渲染、自动批处理、新的 Suspense 特性、新的 Hooks 和其他多项改进。并发渲染使渲染过程可中断和恢复,提高了应用响应性;自动批处理优化了事件处理,减少不必要的重新渲染;新的 Suspense 支持数据获取和更好的错误处理;新增的 `useId` 和 `useTransition` Hooks 提供了更多功能;服务器组件和改进的错误边界处理进一步提升了性能和稳定性。这些新特性为开发者提供了强大的工具,帮助构建更高效、更稳定的应用。
|
2月前
|
存储 移动开发 前端开发
探秘react,一文弄懂react的基本使用和高级特性
该文章全面介绍了React的基本使用方法与高级特性,包括JSX语法、组件化设计、状态管理、生命周期方法、Hooks使用、性能优化策略等内容,并探讨了Redux和React Router在项目中的集成与应用。
探秘react,一文弄懂react的基本使用和高级特性
|
3月前
|
前端开发 JavaScript Android开发
React Native 快速入门简直太棒啦!构建跨平台移动应用的捷径,带你开启高效开发之旅!
【8月更文挑战第31天】React Native凭借其跨平台特性、丰富的生态系统及优异性能,成为移动应用开发的热门选择。它允许使用JavaScript和React语法编写一次代码即可在iOS和Android上运行,显著提升开发效率。此外,基于React框架的组件化开发模式使得代码更加易于维护与复用,加之活跃的社区支持与第三方库资源,加速了应用开发流程。尽管作为跨平台框架,React Native在性能上却不输原生应用,支持原生代码优化以实现高效渲染与功能定制。对于开发者而言,React Native简化了移动应用开发流程,是快速构建高质量应用的理想之选。
79 0
|
4月前
|
存储 前端开发 安全
|
5月前
|
前端开发 JavaScript API
React小记(四)_路由的基本使用
React小记(四)_路由的基本使用
43 1
|
5月前
|
移动开发 前端开发 Java
技术笔记:ReactNative学习笔记(一)————(RN)快速入门
技术笔记:ReactNative学习笔记(一)————(RN)快速入门
66 0
|
6月前
|
前端开发 API 开发者
React这些新特性在开发效率上有哪些改进
【4月更文挑战第18天】React 18 提升开发效率,引入新Root API `createRoot`优化挂载,支持渐进式升级,减少迁移成本。新增性能工具如Profiler API和Concurrent Mode,自动化批处理提高性能,减少重渲染。服务器组件优化加载速度,减轻客户端负担。开发者可更高效构建和优化React应用。
114 6
|
6月前
|
前端开发 JavaScript 开发者
React 是什么?有什么特性?有哪些优势?
React 是什么?有什么特性?有哪些优势?
177 1
|
6月前
|
前端开发 JavaScript
React 16.8 新特性:让你的应用更出色(下)
React 16.8 新特性:让你的应用更出色(下)
React 16.8 新特性:让你的应用更出色(下)