- npm run dev和npm run build区别
-
npm run dev用于开发过程中的快速迭代和实时预览,而npm run build用于生成生产就绪的静态资源,准备部署。 -
- 开发模式 (
npm run dev) :
- 开发模式 (
-
这个命令通常用于启动开发服务器,提供热重载(Hot Module Replacement, HMR)功能,允许开发者在不刷新浏览器的情况下看到代码更改的效果。
-
开发模式不进行生产环境优化,比如代码压缩、树摇(tree shaking)等,以便于开发者调试。
-
它通常用于开发过程中,快速迭代和测试新功能。
-
- 生产构建 (
npm run build) :
- 生产构建 (
-
这个命令用于执行构建过程,生成优化后的静态文件,这些文件适合部署到生产环境。
-
构建过程可能包括代码压缩、图片优化、代码分割(code splitting)、服务端渲染(server-side rendering)等,以提高应用的性能和加载速度。
-
构建后的文件通常放置在项目的
dist或build目录下,准备部署到生产服务器。
-
- useCallback:
React 库中的一个钩子(Hook),它用于优化组件的性能。在函数组件中,
useCallback允许你创建一个记忆化的回调函数,这个函数在组件的渲染过程中不会被重新创建,除非其依赖项发生变化。-
- 避免不必要的渲染:在React中,如果一个组件的props或者state发生变化,组件会重新渲染。使用
useCallback可以确保回调函数在没有依赖项变化的情况下不会改变,从而避免因回调函数的变化导致的子组件不必要的重新渲染。
- 避免不必要的渲染:在React中,如果一个组件的props或者state发生变化,组件会重新渲染。使用
-
- 依赖项记忆:
useCallback接受两个参数:第一个参数是回调函数,第二个参数是一个依赖项数组。只有当依赖项数组中的某个值发生变化时,回调函数才会重新创建。
- 依赖项记忆:
-
- 使用场景:当你将回调函数作为prop传递给子组件,并且子组件对回调函数的引用变化导致不必要的渲染时,使用
useCallback是非常有用的。
- 使用场景:当你将回调函数作为prop传递给子组件,并且子组件对回调函数的引用变化导致不必要的渲染时,使用
-
import React, { useCallback } from 'react';
function ParentComponent() {
const someValue = ...; // 状态或props
// 使用 useCallback 创建一个记忆化的回调函数
const memoizedCallback = useCallback(() => {
// 执行某些操作
}, [someValue]); // 当 someValue 变化时,回调函数会重新创建
return <ChildComponent callback={memoizedCallback} />;
}
- useMemo:React中的一个钩子(Hook),它用于优化组件的性能。使用
useMemo可以记忆一个值,这个值仅在其依赖项发生变化时才会重新计算。这对于避免在每次渲染时执行昂贵的计算或方法调用非常有用。-
- 记忆值:
useMemo接受两个参数:第一个参数是一个返回值的函数,第二个参数是一个依赖项数组。只有当依赖项数组中的某个值发生变化时,返回值函数才会被执行。
- 记忆值:
-
- 避免不必要的计算:通过记忆值,
useMemo可以避免在组件的每次渲染中重复执行计算,从而提高性能。
- 避免不必要的计算:通过记忆值,
-
- 使用场景:当你有一个复杂的计算或方法调用,并且你知道这个计算的结果在依赖项不变的情况下不会改变时,使用
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,你可以定义一个组件的加载行为,包括:
- 组件加载:指定要加载的组件或组件的异步加载函数。
- 加载状态:定义当组件正在加载时应该显示什么内容,通常是一个加载指示器。
- 超时处理:设置加载超时的时间,如果加载时间超过这个时间,可以显示一个错误消息或采取其他行动。
- 加载失败处理:如果组件加载失败,可以定义错误处理逻辑。
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框架中,一个容器组件可能用来包裹多个子组件,提供它们共同的样式或者逻辑。
-
当说一个组件被容器包裹时,意味着这个组件是作为子组件嵌套在另一个更大的组件(容器)内部。这样做可以带来以下好处:
-
- 组织性:通过将组件嵌套在容器中,可以更好地组织代码,使其结构清晰。
-
- 可重用性:容器可以包含多个组件,这些组件可以在不同的容器中重复使用。
-
- 样式和行为的统一:容器可以为内部的组件提供统一的样式和行为,简化开发过程。
-
- Redux状态管理库的上下文中的dispatch和reducer:
-
- Dispatch:在Redux中,dispatch是一个函数,用于发送一个action到store中。Action是一个描述应用状态变化的普通对象,它包含了一些信息,比如类型(type)和可能的payload(数据)。通过调用dispatch,你可以触发应用状态的更新。
-
- 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将在每次渲染后都执行。-
- 首次渲染:组件首次加载到DOM时,
useEffect会被执行一次。
- 首次渲染:组件首次加载到DOM时,
-
- 更新渲染:当组件的props或state发生变化导致组件重新渲染时,
useEffect也会被执行。
- 更新渲染:当组件的props或state发生变化导致组件重新渲染时,
-
- 卸载:组件卸载前,如果
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
}
},[])
- 为什么要用异步操作?
- 非阻塞UI: 异步操作允许用户界面(UI)保持响应状态,而不会在等待数据查询完成时被阻塞或冻结。
- 网络请求: 通常,从服务器获取数据是异步的,因为网络通信所需的时间是不确定的,使用
async/await可以更简洁地处理 Promise。 - 性能: 避免在组件的渲染路径中直接进行耗时操作,异步操作可以让组件渲染和数据获取并行进行,提高应用性能。
- 错误处理: 异步函数提供了一个自然的地点来使用
try/catch语句来捕获和处理可能出现的错误。 - 代码可读性: 使用
async/await可以使异步代码的流程看起来更像是同步代码,增加了代码的可读性和可维护性。 - 状态更新: 在React中,异步操作完成后通常需要更新组件状态,通过在
async函数中使用await可以确保在状态更新前获取到最新的数据。 - 副作用处理: 在React的
useEffect钩子中,进行数据查询等副作用操作时,使用异步操作可以在副作用执行完成后进行清理或状态更新。
在这个特定的代码片段中,queryData 函数可能是在组件加载或特定事件触发时调用的,以获取并显示数据。使用异步操作允许开发者在不干扰用户操作的情况下,从后端接口获取数据。
- await用法
- 于等待一个 Promise 的解决(fulfillment)或拒绝(rejection)。
await只能在异步函数(使用async关键字声明的函数)内部使用
- 于等待一个 Promise 的解决(fulfillment)或拒绝(rejection)。
-
等待 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); } -
错误处理: 当等待的 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); } } -
顺序执行:
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); } -
并发执行: 使用
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); } -
在组件中使用: 在React组件中,
await可以在async函数中使用,以等待数据加载后再进行状态更新或渲染。javascript async function useEffectAsync() { const data = await fetchData(); setState(data); } -
避免回调地狱:
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:一个模块只能导出一个,导入时不需要使用花括号,可以直接使用默认导出的名称,或者为它指定任何你想要的名称
- export:一个模块中可以导出很多,导入时使用花括号
// -- 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 的一些核心特性:
-
服务器端渲染(SSR) :
- Next.js 允许你将 React 组件渲染在服务器上,然后将完整的 HTML 发送给客户端,这有助于提高首屏加载速度(从开始加载网页到浏览器首次渲染页面内容的时间)和搜索引擎优化(SEO)。
-
静态站点生成(SSG) :
- 静态生成每个页面,使得在构建时生成静态的 HTML 文件,适合不经常变动的内容,可以进一步提高性能和减少服务器负载。
-
文件系统路由:
- Next.js 通过文件系统自动创建路由,你只需将组件放在
pages目录下,Next.js 就会自动为你创建对应的路由。
- Next.js 通过文件系统自动创建路由,你只需将组件放在
-
TypeScript 支持:
- Next.js 内置了对 TypeScript 的支持,无需额外配置即可使用 TypeScript 开发。
-
API 路由:
- 你可以在 Next.js 应用中轻松创建 API 路由,这些路由可以处理 API 请求并返回 JSON 数据。
-
CSS 和 Sass 支持:
- Next.js 提供了对 CSS 和 Sass 的内置支持,无需任何配置即可使用。
-
Image 组件:
- Next.js 提供了一个内置的
Image组件,用于优化图片的加载和显示。
- Next.js 提供了一个内置的
-
环境变量和配置:
- 你可以在 Next.js 中轻松配置环境变量和自定义配置。
-
自动代码拆分:
- Next.js 会自动拆分代码,使得初始加载只包含必要的代码,其他代码按需加载。
-
开发服务器:
- Next.js 提供了一个快速的开发服务器,支持热模块替换(HMR),使得开发体验更加流畅。
-
部署:
- Next.js 应用可以轻松部署到 Vercel(由 Next.js 的创建者运营的云平台),也可以部署到其他支持 Node.js 的平台。
- react中的Suspense组件
- React 的
Suspense组件是一种用于异步加载组件和数据的机制,它可以在等待异步操作完成时显示一个备用内容(fallback)。这个特性在 React 16.6 版本中引入,目的是简化 React 应用中的数据加载状态管理,并提供更连贯的用户体验。
- 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 组件的工作原理是:
- 异步边界:
Suspense作为异步边界,可以包裹可能需要等待数据加载的子组件。 - 占位符(Fallback UI) :在等待期间,
Suspense接受一个fallback属性,用于显示加载指示器或其他占位内容。 - 数据预取(Preloading) :与
React.lazy结合使用,可以懒加载组件,并在首次渲染时自动触发组件的加载。 - 数据加载协调:与 React 的
ContextAPI 和 Hooks(如useSuspenseResource)结合,可以实现细粒度的数据加载控制。
Suspense 组件的优点包括:
- 高效的资源加载与渲染:通过懒加载和数据预加载,可以减少首屏加载时间,提升用户体验。
- 优雅的错误处理:使用错误边界(Error Boundaries)和
Suspense的错误处理机制,可以统一处理组件加载或数据获取过程中的错误。 - 动态优先级调整:
Concurrent Mode允许 React 根据当前运行环境动态调整渲染任务的优先级,确保在各种条件下都能提供最佳性能。 - 简化状态管理:与状态库(如 Redux 或 MobX)无缝集成,帮助更平滑地管理异步状态更新。