React笔记

1,692 阅读14分钟
  • npm run dev和npm run build区别
    • npm run dev 用于开发过程中的快速迭代和实时预览,而 npm run build 用于生成生产就绪的静态资源,准备部署。

      1. 开发模式 (npm run dev) :
    • 这个命令通常用于启动开发服务器,提供热重载(Hot Module Replacement, HMR)功能,允许开发者在不刷新浏览器的情况下看到代码更改的效果。

    • 开发模式不进行生产环境优化,比如代码压缩、树摇(tree shaking)等,以便于开发者调试。

    • 它通常用于开发过程中,快速迭代和测试新功能。

      1. 生产构建 (npm run build) :
    • 这个命令用于执行构建过程,生成优化后的静态文件,这些文件适合部署到生产环境。

    • 构建过程可能包括代码压缩、图片优化、代码分割(code splitting)、服务端渲染(server-side rendering)等,以提高应用的性能和加载速度。

    • 构建后的文件通常放置在项目的 dist 或 build 目录下,准备部署到生产服务器。

  • useCallback: React 库中的一个钩子(Hook),它用于优化组件的性能。在函数组件中,useCallback 允许你创建一个记忆化的回调函数,这个函数在组件的渲染过程中不会被重新创建,除非其依赖项发生变化。
      1. 避免不必要的渲染:在React中,如果一个组件的props或者state发生变化,组件会重新渲染。使用 useCallback 可以确保回调函数在没有依赖项变化的情况下不会改变,从而避免因回调函数的变化导致的子组件不必要的重新渲染。
      1. 依赖项记忆useCallback 接受两个参数:第一个参数是回调函数,第二个参数是一个依赖项数组。只有当依赖项数组中的某个值发生变化时,回调函数才会重新创建。
      1. 使用场景:当你将回调函数作为prop传递给子组件,并且子组件对回调函数的引用变化导致不必要的渲染时,使用 useCallback 是非常有用的。
import React, { useCallback } from 'react';

function ParentComponent() {
  const someValue = ...; // 状态或props

  // 使用 useCallback 创建一个记忆化的回调函数
  const memoizedCallback = useCallback(() => {
    // 执行某些操作
  }, [someValue]); // 当 someValue 变化时,回调函数会重新创建

  return <ChildComponent callback={memoizedCallback} />;
}
  • useMemo:React中的一个钩子(Hook),它用于优化组件的性能。使用 useMemo 可以记忆一个值,这个值仅在其依赖项发生变化时才会重新计算。这对于避免在每次渲染时执行昂贵的计算或方法调用非常有用。
      1. 记忆值useMemo 接受两个参数:第一个参数是一个返回值的函数,第二个参数是一个依赖项数组。只有当依赖项数组中的某个值发生变化时,返回值函数才会被执行。
      1. 避免不必要的计算:通过记忆值,useMemo 可以避免在组件的每次渲染中重复执行计算,从而提高性能。
      1. 使用场景:当你有一个复杂的计算或方法调用,并且你知道这个计算的结果在依赖项不变的情况下不会改变时,使用 useMemo 是非常合适的。
import React, { useMemo } from 'react';

function MyComponent(props) {
  // 使用 useMemo 记忆一个值
  const memoizedValue = useMemo(() => {
    // 这里执行一些复杂的计算
    const result = computeExpensiveValue(props);
    return result;
  }, [props]); // 当 props 发生变化时,重新计算

  return <div>{memoizedValue}</div>;
}

// 假设这是一个昂贵的计算函数
function computeExpensiveValue(props) {
  // ...
}
  • useMemo和useCallback区别:
    • useMemo 用于记忆值,而 useCallback 用于记忆回调函数。两者都可以用来避免不必要的渲染或计算,但是 useMemo 更适用于计算值,useCallback 更适用于函数。
  • react-loadable:一个react库,用于异步加载React组件 Loadable 函数是 react-loadable 库的核心,它是一个高阶组件(HOC),用于将常规组件转换成可以异步加载的组件。使用 Loadable,你可以定义一个组件的加载行为,包括:
  1. 组件加载:指定要加载的组件或组件的异步加载函数。
  2. 加载状态:定义当组件正在加载时应该显示什么内容,通常是一个加载指示器。
  3. 超时处理:设置加载超时的时间,如果加载时间超过这个时间,可以显示一个错误消息或采取其他行动。
  4. 加载失败处理:如果组件加载失败,可以定义错误处理逻辑。
import Loadable from 'react-loadable';

const LoadableComponent = Loadable({
  loader: () => import('./MyComponent'),
  loading: () => <p>Loading...</p>,
  delay: 200, // 延迟一定时间后再显示加载提示
  timeout: 12000, // 12秒后超时
});

export default LoadableComponent;

在这个例子中:

  • loader 属性是一个返回 Promise 的函数,用于异步加载组件。
  • loading 属性是一个组件,当异步加载正在进行时显示。
  • delay 属性定义了在显示加载组件之前等待的时间(以毫秒为单位)。
  • timeout 属性定义了加载超时的时间。
  • 在项目中components基本组件,被container包裹是什么意思:
    • "container"(容器)在不同的上下文中可能有不同的含义,但通常指的是一个包含其他组件或元素的组件。容器可以提供布局、样式和/或行为给其内部的组件。例如,在React框架中,一个容器组件可能用来包裹多个子组件,提供它们共同的样式或者逻辑。

    • 当说一个组件被容器包裹时,意味着这个组件是作为子组件嵌套在另一个更大的组件(容器)内部。这样做可以带来以下好处:

      1. 组织性:通过将组件嵌套在容器中,可以更好地组织代码,使其结构清晰。
      1. 可重用性:容器可以包含多个组件,这些组件可以在不同的容器中重复使用。
      1. 样式和行为的统一:容器可以为内部的组件提供统一的样式和行为,简化开发过程。
  • Redux状态管理库的上下文中的dispatch和reducer:
      1. Dispatch:在Redux中,dispatch是一个函数,用于发送一个action到store中。Action是一个描述应用状态变化的普通对象,它包含了一些信息,比如类型(type)和可能的payload(数据)。通过调用dispatch,你可以触发应用状态的更新。
      1. Reducer:Reducer是一个纯函数,它接受当前的state(状态)和action,返回新的state。Reducer的职责是根据action的类型来决定如何更新state。Reducer必须保持纯净,即对于同样的输入,它必须总是返回相同的输出,并且不产生副作用。
    • "包装dispatch和对应的reducer"通常指的是创建一个高阶的dispatch函数或者一个高阶的reducer。这在实际开发中有几个用途:

      • 高阶dispatch:创建一个包装了原始dispatch的函数,可以在dispatch action之前执行一些额外的操作,比如日志记录、异步操作、权限检查等。
      • 高阶reducer:将多个reducer合并为一个单一的reducer,或者在更新state之前添加一些逻辑处理。Redux的combineReducers函数就是一种高阶reducer,它将多个reducer合并成一个处理多个子状态的reducer。
  • useEffect的执行时间:
    • useEffect 钩子的执行与组件是否被调用密切相关。如果组件没有被渲染到页面上,那么它内部定义的 useEffect 钩子也不会被执行。此外,useEffect 还可以接受一个依赖数组作为第二个参数,这可以用来告诉React仅在特定的依赖项变化时才重新执行副作用函数。如果没有提供依赖数组,或者数组为空,useEffect 将在每次渲染后都执行。
      1. 首次渲染:组件首次加载到DOM时,useEffect 会被执行一次。
      1. 更新渲染:当组件的props或state发生变化导致组件重新渲染时,useEffect 也会被执行。
      1. 卸载:组件卸载前,如果 useEffect 函数中返回了一个清除函数(cleanup function),该清除函数会被执行。
  • 关于异步操作async和await
const queryData = useCallback(async (skip=0) =>{
    const data = await actions.queryData({
        skip}).catch((err:any)=>console.error('异常',err))
    if(data){
        return data.length
    }else{
        return 0
        }
    },[])
  • 为什么要用异步操作?
  1. 非阻塞UI: 异步操作允许用户界面(UI)保持响应状态,而不会在等待数据查询完成时被阻塞或冻结。
  2. 网络请求: 通常,从服务器获取数据是异步的,因为网络通信所需的时间是不确定的,使用 async/await 可以更简洁地处理 Promise。
  3. 性能: 避免在组件的渲染路径中直接进行耗时操作,异步操作可以让组件渲染和数据获取并行进行,提高应用性能。
  4. 错误处理: 异步函数提供了一个自然的地点来使用 try/catch 语句来捕获和处理可能出现的错误。
  5. 代码可读性: 使用 async/await 可以使异步代码的流程看起来更像是同步代码,增加了代码的可读性和可维护性。
  6. 状态更新: 在React中,异步操作完成后通常需要更新组件状态,通过在 async 函数中使用 await 可以确保在状态更新前获取到最新的数据。
  7. 副作用处理: 在React的 useEffect 钩子中,进行数据查询等副作用操作时,使用异步操作可以在副作用执行完成后进行清理或状态更新。

在这个特定的代码片段中,queryData 函数可能是在组件加载或特定事件触发时调用的,以获取并显示数据。使用异步操作允许开发者在不干扰用户操作的情况下,从后端接口获取数据。

  • await用法
    • 于等待一个 Promise 的解决(fulfillment)或拒绝(rejection)。await 只能在异步函数(使用 async 关键字声明的函数)内部使用
  1. 等待 Promise: 使用 await 等待一个 Promise 完成。如果 Promise 被解决,await 表达式会返回 Promise 的解决值。

    javascript复制
    async function fetchData() {
      const response = await fetch('https://api.example.com/data');
      const data = await response.json();
      console.log(data);
    }
    
  2. 错误处理: 当等待的 Promise 被拒绝时,它会抛出一个错误,这个错误需要被 try...catch 语句捕获。

    javascript
    async function fetchData() {
      try {
        const response = await fetch('https://api.example.com/data');
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        const data = await response.json();
        console.log(data);
      } catch (error) {
        console.error('Fetch error:', error);
      }
    }
    
  3. 顺序执行: await 可以用来确保代码按照特定的顺序执行,即使在异步操作中也是如此。

    javascript
    async function stepByStep() {
      const result1 = await new Promise((resolve) => setTimeout(() => resolve('First'), 1000));
      const result2 = await new Promise((resolve) => setTimeout(() => resolve('Second'), 2000));
      console.log(result1, result2);
    }
    
  4. 并发执行: 使用 Promise.all 可以同时等待多个 Promise,await 可以用来等待这些 Promise 同时解决。

    javascript
    async function concurrentFetch() {
      const promise1 = fetch('https://api.example.com/data1');
      const promise2 = fetch('https://api.example.com/data2');
      const results = await Promise.all([promise1, promise2]);
      const data1 = await results[0].json();
      const data2 = await results[1].json();
      console.log(data1, data2);
    }
    
  5. 在组件中使用: 在React组件中,await 可以在 async 函数中使用,以等待数据加载后再进行状态更新或渲染。

    javascript
    async function useEffectAsync() {
      const data = await fetchData();
      setState(data);
    }
    
  6. 避免回调地狱: await 可以简化异步代码的复杂性,避免所谓的“回调地狱”(多个嵌套的回调函数)。

  • 在页面中如何通过点击去改变一个按钮的内容
//在同一个tsx文件中:要实现点击按钮就显示一个div大盒子,按钮内容为收起,再点一下按钮内容为展开,大盒子收起(后者为默认状态)
interface Props{
    expand:boolean,
    onExpand:(expand:boolean)=> void
}

export default function Component({
    expand=false,
    onExpand
    }:Props){
        const handleExpand = useCallback(()=>{
            onExpand(!expand)
    }, [expand])

    return (
        <div onClick={handleExpand}>{expand ? '收起':'展开'}</div>
    )
}
//在上一个Component的container 的index文件中
export default ComponentContainer(){
    const [expand,setExpand] = useState(false)
    const handleExpand = useCallback((expand)=>{
        setExpand(expand)
    },[])
    
    return (
        <Component 
            expand={expand},
            onExpand={handleExpand}
        />
        )
}
  • export和export default区别
    • export:一个模块中可以导出很多,导入时使用花括号 {} 明确指出要导入的名称,或者使用*来导入所有导出的命名
    • export default:一个模块只能导出一个,导入时不需要使用花括号,可以直接使用默认导出的名称,或者为它指定任何你想要的名称
// -- moduleA.js --
export const name = 'Kimi';
export function sayHello() {
  console.log('Hello!');
}

// -- moduleB.js --
import { name, sayHello } from './moduleA.js';
import * as moduleA from './moduleA.js'; // 使用命名空间的方式导入所有导出
// -- moduleC.js --
const usefulObject = { a: 1, b: 2 };
export default usefulObject;

// -- moduleD.js --
import usefulObject from './moduleC.js'; // 直接使用默认名称
import myObject from './moduleC.js'; // 为默认导出指定一个别名
  • Next.js框架
    • Next.js 是一个基于 React 的开源框架,它旨在提供更快的开发体验和更优化的生产性能。Next.js 不仅支持服务器端渲染(SSR),还支持静态站点生成(SSG),并且提供了许多其他高级功能,以帮助开发者构建高性能的 Web 应用。

以下是 Next.js 的一些核心特性:

  1. 服务器端渲染(SSR)

    • Next.js 允许你将 React 组件渲染在服务器上,然后将完整的 HTML 发送给客户端,这有助于提高首屏加载速度(从开始加载网页到浏览器首次渲染页面内容的时间)和搜索引擎优化(SEO)。
  2. 静态站点生成(SSG)

    • 静态生成每个页面,使得在构建时生成静态的 HTML 文件,适合不经常变动的内容,可以进一步提高性能和减少服务器负载。
  3. 文件系统路由

    • Next.js 通过文件系统自动创建路由,你只需将组件放在 pages 目录下,Next.js 就会自动为你创建对应的路由。
  4. TypeScript 支持

    • Next.js 内置了对 TypeScript 的支持,无需额外配置即可使用 TypeScript 开发。
  5. API 路由

    • 你可以在 Next.js 应用中轻松创建 API 路由,这些路由可以处理 API 请求并返回 JSON 数据。
  6. CSS 和 Sass 支持

    • Next.js 提供了对 CSS 和 Sass 的内置支持,无需任何配置即可使用。
  7. Image 组件

    • Next.js 提供了一个内置的 Image 组件,用于优化图片的加载和显示。
  8. 环境变量和配置

    • 你可以在 Next.js 中轻松配置环境变量和自定义配置。
  9. 自动代码拆分

    • Next.js 会自动拆分代码,使得初始加载只包含必要的代码,其他代码按需加载。
  10. 开发服务器

    • Next.js 提供了一个快速的开发服务器,支持热模块替换(HMR),使得开发体验更加流畅。
  11. 部署

    • Next.js 应用可以轻松部署到 Vercel(由 Next.js 的创建者运营的云平台),也可以部署到其他支持 Node.js 的平台。
  • react中的Suspense组件
    • React 的 Suspense 组件是一种用于异步加载组件和数据的机制,它可以在等待异步操作完成时显示一个备用内容(fallback)。这个特性在 React 16.6 版本中引入,目的是简化 React 应用中的数据加载状态管理,并提供更连贯的用户体验。

Suspense 组件通常与 React.lazy() 一起使用,用于懒加载组件。当你使用 React.lazy() 动态导入组件时,你可以将这些组件包裹在 Suspense 边界内,并提供一个 fallback 属性,这个属性定义了在懒加载的组件加载完成之前展示的内容。这样,你可以在组件加载时显示加载指示器或骨架屏,而不是空白或闪烁的屏幕。

使用 Suspense 的基本语法如下:

jsx
import { Suspense } from 'react';
const LazyComponent = React.lazy(() => import('./LazyComponent'));

function MyComponent() {
  return (
    <Suspense fallback={<LoadingIndicator />}>
      <LazyComponent />
    </Suspense>
  );
}

在上面的例子中,如果 LazyComponent 还没有加载完成,React 会展示 fallback 属性中定义的 LoadingIndicator 组件。

Suspense 组件的工作原理是:

  1. 异步边界Suspense 作为异步边界,可以包裹可能需要等待数据加载的子组件。
  2. 占位符(Fallback UI) :在等待期间,Suspense 接受一个 fallback 属性,用于显示加载指示器或其他占位内容。
  3. 数据预取(Preloading) :与 React.lazy 结合使用,可以懒加载组件,并在首次渲染时自动触发组件的加载。
  4. 数据加载协调:与 React 的 Context API 和 Hooks(如 useSuspenseResource)结合,可以实现细粒度的数据加载控制。

Suspense 组件的优点包括:

  • 高效的资源加载与渲染:通过懒加载和数据预加载,可以减少首屏加载时间,提升用户体验。
  • 优雅的错误处理:使用错误边界(Error Boundaries)和 Suspense 的错误处理机制,可以统一处理组件加载或数据获取过程中的错误。
  • 动态优先级调整Concurrent Mode 允许 React 根据当前运行环境动态调整渲染任务的优先级,确保在各种条件下都能提供最佳性能。
  • 简化状态管理:与状态库(如 Redux 或 MobX)无缝集成,帮助更平滑地管理异步状态更新。