likes
comments
collection
share

React快速入门

作者站长头像
站长
· 阅读数 23

React快速入门

简介

React是一个用于构建用户界面的JavaScript库,它可以让你用组件的方式来创建和组合UI元素。React的特点有:

  • 声明式:你只需要描述你想要的UI效果,而不需要关心具体的实现细节。
  • 组件化:你可以把UI分成独立的、可复用的组件,这样可以提高代码的可维护性和复用性。
  • 高效:React使用虚拟DOM(Virtual DOM)来管理UI状态,当数据发生变化时,只更新需要更新的部分,避免了不必要的渲染开销。
  • 跨平台:React不仅可以用于Web开发,还可以通过React Native和React 360来构建移动应用和虚拟现实应用。

React是由Facebook开发并于2013年开源的,目前是最流行的前端框架之一。要使用React,你需要了解一些现代JavaScript的特性,以及一种叫做JSX(JavaScript XML)的语法扩展,它可以让你在JavaScript中写类似于HTML的标签。

生命周期和钩子函数

React的生命周期是指组件从创建到销毁的过程,它包括三个阶段:挂载(mounting)、更新(updating)和卸载(unmounting)。在每个阶段,React都提供了一些钩子函数(hook function),让我们可以在组件的不同状态下执行一些操作。

  • 挂载阶段:这个阶段是组件第一次被渲染到DOM中的时候,它包括以下几个钩子函数:
    • constructor:这是组件的构造函数,用于初始化组件的状态(state)和绑定事件处理函数。这个函数只会在组件创建时调用一次。
    • static getDerivedStateFromProps:这是一个静态函数,用于根据父组件传递的属性(props)来更新组件的状态。这个函数在组件挂载和更新时都会被调用。
    • render:这是组件的核心函数,用于返回组件要渲染的内容。这个函数必须是纯函数,也就是不能修改组件的状态和产生副作用。这个函数在组件挂载和更新时都会被调用。
    • componentDidMount:这是组件挂载完成后调用的函数,用于执行一些需要DOM节点或者网络请求的操作。这个函数只会在组件挂载时调用一次。
  • 更新阶段:这个阶段是组件因为状态或者属性的改变而重新渲染的时候,它包括以下几个钩子函数:
    • static getDerivedStateFromProps:同上。
    • shouldComponentUpdate:这是一个可选的函数,用于优化组件性能,根据组件的状态或者属性来决定是否需要重新渲染。这个函数在组件更新前被调用,返回一个布尔值。
    • render:同上。
    • getSnapshotBeforeUpdate:这是一个可选的函数,用于在组件更新前获取一些信息(如滚动位置),并传递给componentDidUpdate函数。这个函数在最新的渲染输出提交给DOM前被调用,返回一个值或者null。
    • componentDidUpdate:这是一个可选的函数,用于在组件更新后执行一些操作(如DOM操作或者网络请求)。这个函数在组件更新后被调用,接收三个参数:前一个属性、前一个状态和getSnapshotBeforeUpdate返回的值。
  • 卸载阶段:这个阶段是组件从DOM中移除的时候,它只有一个钩子函数:
    • componentWillUnmount:这是一个可选的函数,用于在组件卸载前执行一些清理操作(如取消网络请求或者移除事件监听器)。这个函数在组件卸载前被调用。

状态管理和路由

React的状态管理是指如何在组件之间共享和更新数据,它可以分为内部状态管理和外部状态管理。

  • 内部状态管理:指的是使用React自带的state和props来管理组件的数据,适用于简单的场景,如计数器、表单等。state是组件内部的数据,可以通过setState方法来更新,而props是组件外部传入的数据,不能被组件修改。组件可以通过props将自己的state传递给子组件,也可以通过回调函数将子组件的state传递给父组件,实现数据的双向绑定。
  • 外部状态管理:指的是使用第三方的库或者框架来管理组件的数据,适用于复杂的场景,如多层嵌套、跨页面、异步操作等。外部状态管理可以将数据存储在一个全局的仓库(store)中,让任何组件都可以访问和修改。常见的外部状态管理库有Redux、Mobx、Recoil等¹。

React的路由是指如何根据不同的URL来显示不同的组件,实现单页面应用(SPA)的效果。React本身并不提供路由功能,需要使用第三方的库或者框架来实现。常见的React路由库有React Router、Reach Router等。

  • React Router:是最流行的React路由库,它提供了一系列的组件和API来实现路由功能。React Router有三个版本:web版、native版和core版。web版用于浏览器环境,native版用于移动端环境,core版是两者共享的核心模块。React Router支持两种路由模式:hash模式和history模式。hash模式是通过URL中的#来区分不同的路径,如example.com/#/home,而his… API来控制URL的变化,如example.com/home。React Router提供了以下几个主要的组件²³⁴:
    • BrowserRouter:是一个路由器组件,用于包裹整个应用,并提供history对象。它使用history模式来实现路由功能。
    • HashRouter:是另一个路由器组件,用于包裹整个应用,并提供history对象。它使用hash模式来实现路由功能。
    • Route:是一个路由匹配组件,用于根据当前URL和指定的path来决定是否渲染对应的组件。它可以接收以下几个属性:
      • path:指定要匹配的路径,可以是字符串、字符串数组或者正则表达式。
      • exact:指定是否要进行精确匹配,如果为true,则只有当URL和path完全相同时才渲染对应的组件。
      • component:指定要渲染的组件,当路由匹配时,会创建一个新的组件实例,并传入一些路由相关的属性(如match、location、history等)。
      • render:指定要渲染的函数,当路由匹配时,会调用这个函数,并传入一些路由相关的属性。这个函数必须返回一个React元素或者null。
      • children:指定要渲染的子元素,无论路由是否匹配,都会渲染这个子元素。如果是一个函数,会传入一些路由相关的属性,并返回一个React元素或者null。
    • Switch:是一个路由选择组件,用于只渲染第一个匹配的路由,而忽略其他的路由。它可以包裹多个Route或者Redirect组件,提高路由匹配的效率和优先级。
    • Redirect:是一个路由重定向组件,用于将当前URL重定向到另一个URL。它可以接收以下几个属性:
      • from:指定要重定向的源路径,只有当它被包裹在Switch组件中时才有效。
      • to:指定要重定向的目标路径,可以是字符串或者对象。
      • push:指定是否要将新的URL添加到history栈中,而不是替换当前的URL。如果为true,则可以通过浏览器的后退按钮返回上一个URL。
      • exact:指定是否要进行精确匹配,如果为true,则只有当URL和from完全相同时才进行重定向。
    • Link:是一个导航链接组件,用于在不刷新页面的情况下改变URL,并渲染对应的组件。它可以接收以下几个属性:
      • to:指定要导航到的目标路径,可以是字符串或者对象。
      • replace:指定是否要替换history栈中的当前URL,而不是添加新的URL。如果为true,则无法通过浏览器的后退按钮返回上一个URL。
      • innerRef:指定一个回调函数或者Ref对象,用于获取对应的DOM元素的引用。
    • NavLink:是一个特殊的导航链接组件,用于表示当前激活的路由。它继承了Link组件的所有属性,并添加了以下几个属性:
      • activeClassName:指定当路由匹配时要添加到DOM元素上的类名。
      • activeStyle:指定当路由匹配时要添加到DOM元素上的内联样式。
      • isActive:指定一个函数,用于判断路由是否匹配,并返回一个布尔值。这个函数会接收两个参数:match和location。
      • exact:指定是否要进行精确匹配,如果为true,则只有当URL和to完全相同时才激活对应的链接。
  • Reach Router:是另一个React路由库,它是由React Router的原作者Ryan Florence创建的,旨在提供更简单、更易用、更可访问的路由功能。Reach Router和React Router有很多相似之处,但也有一些区别⁵:
    • Reach Router使用最近嵌套原则(nearest nested principle)来匹配路由,也就是说只有最近嵌套的路由组件会被渲染,而不需要使用Switch组件来选择第一个匹配的路由。
    • Reach Router会自动为每个路由组件添加一些属性(如navigate、location、uri等),而不需要使用withRouter高阶组件来注入这些属性。
    • Reach Router会自动管理焦点,当路由切换时,会将焦点移动到对应的组件上,以提高可访问性和用户体验。
    • Reach Router提供了一些实用的组件和API,如useLocation、useParams、useNavigate、Match、Location等。

Hooks

React的Hooks是一种新的特性,它可以让你在函数组件中使用状态(state)和生命周期(lifecycle)等功能,而不需要使用类组件(class component)。Hooks的目的是让函数组件更加强大、简洁和可复用,同时避免一些类组件的问题,如this指向、代码冗余、性能损耗等。

React提供了一些内置的Hooks,如useState、useEffect、useContext、useReducer等,它们可以让你在函数组件中实现以下几种功能¹:

  • 状态管理:你可以使用useState来定义一个状态变量,并返回一个更新该变量的函数。每次调用该函数时,都会重新渲染组件,并使用最新的状态值。例如:
import React, { useState } from "react";

function Counter() {
  // 定义一个名为count的状态变量,初始值为0
  const [count, setCount] = useState(0);

  // 定义一个增加计数的函数
  function increment() {
    // 调用setCount函数,传入一个新的状态值
    setCount(count + 1);
  }

  // 返回一个JSX元素,显示当前的计数,并绑定点击事件
  return (
    <div>
      <p>当前计数:{count}</p>
      <button onClick={increment}>点击增加</button>
    </div>
  );
}
  • 副作用处理:你可以使用useEffect来在组件渲染后执行一些副作用操作,如网络请求、DOM操作、事件监听等。useEffect接收一个函数作为参数,在该函数中可以执行任何副作用操作,并返回一个清理函数(可选),用于在组件卸载或者重新渲染时执行一些清理操作。useEffect还可以接收一个依赖数组作为第二个参数,用于指定该副作用依赖于哪些状态或者属性,只有当这些状态或者属性发生变化时,才会重新执行该副作用。例如:
import React, { useState, useEffect } from "react";

function User(props) {
  // 定义一个名为user的状态变量,初始值为null
  const [user, setUser] = useState(null);

  // 使用useEffect来在组件渲染后发送网络请求,获取用户信息
  useEffect(() => {
    // 定义一个异步函数,用于发送网络请求
    async function fetchUser() {
      // 获取props中传入的用户ID
      const userId = props.userId;
      // 发送GET请求,根据用户ID获取用户信息
      const response = await fetch(`https://example.com/api/users/${userId}`);
      // 解析响应数据为JSON格式
      const data = await response.json();
      // 调用setUser函数,更新user状态变量
      setUser(data);
    }

    // 调用异步函数
    fetchUser();
  }, [props.userId]); // 指定该副作用依赖于props.userId

  // 返回一个JSX元素,显示用户信息或者加载中提示
  return (
    <div>
      {user ? (
        <div>
          <p>用户名:{user.name}</p>
          <p>邮箱:{user.email}</p>
        </div>
      ) : (
        <p>加载中...</p>
      )}
    </div>
  );
}
  • 上下文访问:你可以使用useContext来访问React的上下文(context)对象,从而实现跨组件的数据共享。useContext接收一个上下文对象作为参数,并返回该上下文的当前值。例如:
import React, { useContext } from "react";

// 创建一个上下文对象,初始值为light
const ThemeContext = React.createContext("light");

function App() {
  // 定义一个名为theme的状态变量,初始值为dark
  const [theme, setTheme] = useState("dark");

  // 定义一个切换主题的函数
  function toggleTheme() {
    // 调用setTheme函数,根据当前主题切换为另一个主题
    setTheme(theme === "dark" ? "light" : "dark");
  }

  // 返回一个JSX元素,使用ThemeContext.Provider包裹子组件,并传入theme作为value属性
  return (
    <ThemeContext.Provider value={theme}>
      <div>
        <p>当前主题:{theme}</p>
        <button onClick={toggleTheme}>点击切换</button>
        <Child />
      </div>
    </ThemeContext.Provider>
  );
}

function Child() {
  // 使用useContext来访问ThemeContext的当前值
  const theme = useContext(ThemeContext);

  // 返回一个JSX元素,显示当前主题
  return <p>子组件的主题:{theme}</p>;
}
  • 状态逻辑复用:你可以使用useReducer来复用一些复杂的状态逻辑,从而避免在组件中编写冗长的setState函数。useReducer接收一个reducer函数和一个初始状态作为参数,并返回一个状态变量和一个派发(dispatch)函数。reducer函数接收一个当前状态和一个动作(action)作为参数,并返回一个新的状态。派发函数接收一个动作作为参数,并调用reducer函数来更新状态变量。例如:
import React, { useReducer } from "react";

// 定义一个reducer函数,根据不同的动作类型来更新状态
function reducer(state, action) {
  switch (action.type) {
    case "increment":
      // 如果动作类型是increment,返回一个新的状态,计数加一
      return { count: state.count + 1 };
    case "decrement":
      // 如果动作类型是decrement,返回一个新的状态,计数减一
      return { count: state.count - 1 };
    default:
      // 如果动作类型是其他,返回原来的状态
      return state;
  }
}

function Counter() {
  // 使用useReducer来定义一个名为state的状态变量和一个名为dispatch的派发函数
  // 传入reducer函数和初始状态{count:0}作为参数
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  // 返回一个JSX元素,显示当前的计数,并绑定点击事件
  return (
    <div>
      <p>当前计数:{state.count}</p>
      <button onClick={() => dispatch({ type: "increment" })}>点击增加</button>
      <button onClick={() => dispatch({ type: "decrement" })}>点击减少</button>
    </div>
  );
}

React还提供了一些其他的内置Hooks,如useCallback、useMemo、useRef等,它们可以让你在函数组件中实现以下几种功能¹:

  • 性能优化:你可以使用useCallback来缓存一个回调函数,避免在每次渲染时都创建一个新的函数。useCallback接收一个回调函数和一个依赖数组作为参数,并返回该回调函数的缓存版本。只有当依赖数组中的值发生变化时,才会创建一个新的回调函数。你可以使用useMemo来缓存一个计算结果,避免在每次渲染时都重新计算。useMemo接收一个计算函数和一个依赖数组作为参数,并返回该计算函数的缓存结果。只有当依赖数组中的值发生变化时,才会重新执行该计算函数。
  • 引用保持:你可以使用useRef来创建一个可变的引用对象,并在整个组件的生命周期内保持不变。useRef接