字节、滴滴面试
-
如何触发bfc
- display:inline-block
- overflow:hidden
- float: left
- position: absolute或fixed
- display: flow-root;
- BFC(Block Formatting Context,块级格式化上下文)是 CSS 中 的一个概念,它是页面上一个独立的渲染区域,具有特定的布局规则。触发 BFC 可以帮助解决一些常见的布局问题,如清除浮动、避免外边距重叠等。
-
对闭包的理解以及手写闭包
- 词法作用域:javascript使用词法作用域,这意味着函数的作用域在函数定义时就已经确定,而不是在函数调用的时候
- 函数嵌套:当一个函数在另一个函数内部定义时,内部函数可以访问外部函数的变量
- 闭包的形成:当内部函数被外部函数返回并在外部调用时,内部函数依然能够访问外部函数的变量
- 闭包的特性:持久化变量-闭包可以记住并访问其词法作用域的变量,即使外部函数已经执行完毕;
封装:闭包可以用于创建私有变量和方法,从而实现数据封装
-
闭包示例
function createCounter() {
let count = 0; // 外部函数的局部变量
return function() {
// 内部函数形成闭包
count += 1;
return count;
};
}
const counter = createCounter(); // 调用外部函数,返回内部函数
console.log(counter()); // 输出: 1
console.log(counter()); // 输出: 2
console.log(counter()); // 输出: 3
createCounter 函数:定义了一个局部变量 count,并返回一个匿名函数。
闭包的形成:返回的匿名函数形成了一个闭包,它捕获了 createCounter 的词法环境,因此可以访问和修改 count。
持久化状态:每次调用 counter() 时,count 的值都会被保留和更新。
-
闭包的应用
- 数据隐藏和封装:通过闭包可以创建私有变量和方法,防止外部直接访问
- 回调函数:在异步编程中,闭包常用于回调函数,保持对某些变量的访问
- 函数工厂:使用闭包创建具有特定行为的函数
- 模块模式:利用闭包实现模块化代码,封装私有数据和方法
-
注意事项
- 内存泄漏:不当的使用闭包可能导致内存泄漏,因为闭包会保留其词法作用域的引用
-主要是声明了变量而没有使用也没有清楚
-性能影响:过多的闭包可能影响性能,尤其是在频繁创建和销毁闭包的情况下
- 内存泄漏:不当的使用闭包可能导致内存泄漏,因为闭包会保留其词法作用域的引用
-
react副作用
-
flex布局
-
5个接口请求如何知道成功几个失败几个,promise.allsettled
美团面试
1. 传入时间戳,返回一个倒计时分秒
function startCountdown(targetTimestamp) {
function updateCountdown() {
const now = Date.now();
const remainingTime = targetTimestamp - now;
if (remainingTime <= 0) {
console.log("00:00");
return;
}
const totalSeconds = Math.floor(remainingTime / 1000);
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
const formattedMinutes = String(minutes).padStart(2, '0');
const formattedSeconds = String(seconds).padStart(2, '0');
console.log(`${formattedMinutes}:${formattedSeconds}`);
// 使用 setTimeout 代替 setInterval,确保每次调用间隔为一秒
setTimeout(updateCountdown, 1000 - (Date.now() % 1000));
}
// 立即调用一次以显示初始倒计时
updateCountdown();
}
// 示例用法
const futureTimestamp = Date.now() + 5 * 60 * 1000; // 5分钟后的时间戳
startCountdown(futureTimestamp);
- 写一个任务队列,可以添加任务,没三秒执行一个任务
class TaskQueue {
constructor() {
this.queue = []; // 存储任务的队列
this.isRunning = false; // 标记任务是否正在运行
}
// 添加任务到队列
addTask(task) {
this.queue.push(task);
if (!this.isRunning) {
this.runNextTask();
}
}
// 运行下一个任务
runNextTask() {
if (this.queue.length === 0) {
this.isRunning = false;
return;
}
this.isRunning = true;
const task = this.queue.shift(); // 取出队列中的第一个任务
// 执行任务
task();
// 设置定时器,三秒后运行下一个任务
setTimeout(() => {
this.runNextTask();
}, 3000);
}
}
// 示例用法
const taskQueue = new TaskQueue();
// 添加任务
taskQueue.addTask(() => console.log('Task 1 executed'));
taskQueue.addTask(() => console.log('Task 2 executed'));
taskQueue.addTask(() => console.log('Task 3 executed'));
滴滴打车
- bfc全称(block Formatting context)
- 盒模型
天志数融
- 前端如何优化
前端优化是提高网站性能和用户体验的关键步骤。优化前端可以显著减少页面加载时间,提高响应速度,并改善整体用户体验。以下是一些常见的前端优化策略:
### 1. 减少HTTP请求
- **合并文件**:将多个CSS和JavaScript文件合并为一个文件,以减少HTTP请求的数量。
- **使用CSS Sprites**:将多个图像合并为一个图像,并使用CSS定位来显示所需部分。
- **内联小型资源**:对于小型CSS和JavaScript,可以直接内联到HTML中,减少请求。
### 2. 压缩和最小化资源
- **压缩文件**:使用工具(如Gzip)压缩HTML、CSS和JavaScript文件。
- **最小化代码**:使用工具(如UglifyJS、CSSNano)去除代码中的空格、注释和不必要的字符。
### 3. 优化图像
- **选择合适的格式**:使用WebP、JPEG 2000等现代图像格式。
- **压缩图像**:使用工具(如ImageOptim、TinyPNG)压缩图像文件。
- **延迟加载**:使用懒加载技术,只有在图像进入视口时才加载。
### 4. 使用内容分发网络(CDN)
- **CDN加速**:将静态资源托管在CDN上,利用其全球分布的服务器加速资源加载。
### 5. 优化CSS和JavaScript
- **异步加载脚本**:使用`async`或`defer`属性异步加载JavaScript,以防止阻塞页面渲染。
- **CSS放在头部,JavaScript放在底部**:确保CSS在页面加载时优先加载,而JavaScript在页面内容加载后执行。
### 6. 浏览器缓存
- **设置缓存头**:通过设置HTTP缓存头(如`Cache-Control`、`Expires`)来缓存静态资源。
- **版本化文件**:使用文件名版本化(如`style.v1.css`)来控制缓存。
### 7. 优化字体加载
- **选择合适的字体格式**:使用WOFF2等现代字体格式。
- **字体显示策略**:使用`font-display`属性控制字体加载行为。
### 8. 提高页面渲染速度
- **减少DOM元素数量**:简化HTML结构,减少DOM节点。
- **使用虚拟DOM**:在使用框架(如React、Vue)时,利用虚拟DOM提高渲染效率。
- **CSS动画代替JavaScript动画**:使用CSS动画和过渡效果,减少JavaScript的计算。
### 9. 性能监控和分析
- **使用性能分析工具**:使用Google Lighthouse、WebPageTest等工具分析和监控网站性能。
- **持续优化**:根据分析结果持续优化,确保网站性能保持在最佳状态。
通过实施这些优化策略,你可以显著提高网站的加载速度和用户体验,从而提高用户满意度和网站的整体表现。
- 如何做seo
搜索引擎优化(SEO)是一组策略和技术,用于提高网站在搜索引擎结果页面(SERP)中的排名。良好的SEO实践可以增加网站的可见性,从而吸引更多的自然流量。以下是一些关键的SEO策略和技术:
### 1. 关键词研究
- **识别关键词**:使用工具(如Google Keyword Planner、Ahrefs、SEMrush)来识别与你的业务相关的高搜索量和低竞争的关键词。
- **长尾关键词**:关注长尾关键词,这些关键词通常更具体,竞争较少,转化率更高。
### 2. 页面优化(On-Page SEO)
- **标题标签**:确保每个页面都有一个独特且描述性的标题标签,包含主要关键词。
- **元描述**:撰写吸引人的元描述,虽然它不直接影响排名,但可以提高点击率。
- **标题和副标题**:使用H1、H2、H3等标签来组织内容,确保主要关键词出现在H1标签中。
- **内容质量**:提供有价值、原创和相关的内容,确保内容长度适中并涵盖主题的各个方面。
- **图像优化**:使用描述性文件名和ALT标签来优化图像。
- **URL结构**:保持URL简洁且包含关键词。
### 3. 技术优化(Technical SEO)
- **网站速度**:优化网站加载速度,使用工具如Google PageSpeed Insights来检测和改进。
- **移动友好性**:确保网站在移动设备上的表现良好,使用响应式设计。
- **安全性**:使用HTTPS来提高网站的安全性。
- **结构化数据**:使用Schema.org标记来帮助搜索引擎理解网站内容。
- **XML站点地图**:创建并提交XML站点地图,以帮助搜索引擎抓取网站。
### 4. 外部优化(Off-Page SEO)
- **反向链接**:获取高质量的反向链接,这些链接来自权威网站,可以提高你的网站信誉。
- **社交媒体**:利用社交媒体平台来推广内容并吸引访问者。
- **在线目录和黄页**:确保你的业务信息在在线目录中准确无误。
### 5. 用户体验(UX)
- **导航**:确保网站导航简单直观。
- **内部链接**:使用内部链接来帮助用户和搜索引擎发现更多内容。
- **跳出率和停留时间**:通过提供相关内容和良好的用户体验来降低跳出率和提高停留时间。
### 6. 分析和调整
- **使用分析工具**:使用Google Analytics和Google Search Console来监控网站流量和SEO表现。
- **定期审查和调整**:根据分析数据定期调整SEO策略,以适应搜索引擎算法的变化和用户行为的变化。
SEO是一个持续的过程,需要不断的监控和优化。通过结合以上策略,你可以提高网站在搜索引擎中的可见性,吸引更多的自然流量。
- 如何加快首屏加载速度
优酷
- react的特点
- React 是一个用于构建用户界面的 JavaScript 库,由 Facebook 开发和维护。它具有许多独特的特点,使其在前端开发中非常受欢迎。以下是 React 的一些主要特点:
1. 声明式编程
- 声明式 UI:React 采用声明式编程范式,开发者只需描述 UI 应该是什么样子,React 会根据状态自动更新和渲染 UI。
- 简化开发:声明式编程使代码更易于理解和调试,减少了手动 DOM 操作的复杂性。
2. 组件化
- 组件:React 应用由独立、可复用的组件组成。每个组件封装了自己的逻辑和 UI,可以像拼积木一样组合成复杂的用户界面。
- 组件复用:组件化设计提高了代码的复用性和可维护性,开发者可以在不同的项目中复用相同的组件。
3. 虚拟 DOM
- 虚拟 DOM:React 使用虚拟 DOM 来提高性能。虚拟 DOM 是实际 DOM 的轻量级副本,React 会在内存中构建虚拟 DOM 树,并在状态变化时进行高效的差异计算(diffing),然后将最小的更新应用到实际 DOM。
- 性能优化:虚拟 DOM 减少了直接操作实际 DOM 的次数,提高了渲染性能。
4. 单向数据流
- 单向数据流:React 采用单向数据流(单向绑定),数据从父组件流向子组件。子组件不能直接修改父组件的数据,只能通过回调函数通知父组件更新数据。
- 易于调试:单向数据流使数据流动更加清晰和可预测,便于调试和维护。
5. JSX 语法
- JSX:JSX 是 JavaScript 的语法扩展,允许在 JavaScript 中编写类似 HTML 的代码。JSX 最终会被编译为
React.createElement
调用。 - 直观易用:JSX 使得编写组件更加直观和简洁,开发者可以在一个文件中同时编写逻辑和 UI。
6. 高效的更新机制
- Reconciliation:React 通过 Reconciliation 过程高效地更新虚拟 DOM 和实际 DOM。它使用一种高效的 diffing 算法来比较新旧虚拟 DOM 树,并计算出最小的更新操作。
- 最小更新:React 只会更新实际 DOM 中需要改变的部分,避免了不必要的重绘和重排。
7. 强大的社区和生态系统
- 社区支持:React 拥有一个庞大且活跃的社区,提供了丰富的资源、教程和第三方库。
- 生态系统:React 的生态系统非常丰富,包括状态管理库(如 Redux、MobX)、路由库(如 React Router)、UI 组件库(如 Material-UI、Ant Design)等。
8. 跨平台开发
- React Native:React 不仅可以用于构建 Web 应用,还可以通过 React Native 构建移动应用。React Native 允许使用相同的 React 组件模型构建原生移动应用。
- 跨平台一致性:通过 React 和 React Native,开发者可以在 Web 和移动平台上实现一致的开发体验和代码复用。
9. Hooks
- Hooks:React 16.8 引入了 Hooks,允许在函数组件中使用状态和其他 React 特性。常见的 Hooks 包括
useState
、useEffect
、useContext
等。 - 简化代码:Hooks 简化了组件逻辑的组织方式,避免了类组件中的复杂生命周期方法,使代码更加简洁和易于理解。
10. 灵活性
- 灵活的架构:React 只是一个视图层库,可以与其他库和框架(如 Redux、MobX、GraphQL)无缝集成,构建复杂的应用。
- 渐进式采用:React 可以逐步引入到现有项目中,不需要一次性重写整个应用。
通过这些特点,React 提供了一个高效、灵活且易于维护的开发方式,使其成为现代前端开发中的重要工具。
受控组件和非受控组件
受控组件(Controlled Components)
定义:
- 受控组件是指其值由 React 组件的状态(state)控制的表单元素。每次用户输入时,都会触发一个事件处理程序来更新组件的状态,从而重新渲染组件并更新表单元素的值。
特点:
- 表单元素的值由 React 组件的状态控制。
- 通过事件处理程序(如
onChange
)更新状态。 - 组件的状态是单一数据源(Single Source of Truth)。
优点:
- 更容易控制和验证输入。
- 状态和 UI 保持同步,便于调试和维护。
- 可以轻松实现复杂的表单逻辑和验证。
缺点:
- 对于大型表单,可能会导致更多的状态管理代码。
非受控组件(Uncontrolled Components)
定义:
- 非受控组件是指其值由 DOM 自身维护的表单元素。React 使用
ref
来直接访问 DOM 元素,从而获取其值。
特点:
- 表单元素的值由 DOM 自身维护。
- 使用
ref
访问 DOM 元素的值。 - 适用于简单的表单或不需要频繁更新的场景。
优点:
- 代码更简洁,适用于简单的表单。
- 不需要频繁更新状态,性能较好。
缺点:
- 难以控制和验证输入。
- 状态和 UI 可能不同步,调试和维护较为困难。
选择使用受控组件还是非受控组件
- 受控组件:适用于需要精细控制和验证输入的场景,如复杂的表单、动态表单验证等。受控组件使得状态和 UI 保持同步,更容易调试和维护。
- 非受控组件:适用于简单的表单或不需要频繁更新的场景,如文件上传、简单的搜索框等。非受控组件代码更简洁,性能较好。
总结
- 受控组件:通过 React 组件的状态控制表单元素的值,适用于需要精细控制和验证输入的场景。
- 非受控组件:通过 DOM 自身维护表单元素的值,适用于简单的表单或不需要频繁更新的场景。
通过理解受控组件和非受控组件的区别和使用场景,可以帮助你在构建表单时做出更合适的选择,从而提高代码的可维护性和性能。
什么是高阶组件
高阶组件(Higher-Order Component,HOC)是 React 中的一种高级技术,用于复用组件逻辑。它是一个函数,接受一个组件作为参数,并返回一个新的组件。通过高阶组件,可以在多个组件之间共享逻辑,而不需要重复代码。
高阶组件的定义
定义:
- 高阶组件是一个函数,接受一个组件作为参数,并返回一个新的组件。
示例:
import React from 'react';
// 一个简单的高阶组件示例
function withLogging(WrappedComponent) {
return class extends React.Component {
componentDidMount() {
console.log('Component mounted');
}
render() {
return <WrappedComponent {...this.props} />;
}
};
}
// 使用高阶组件
class MyComponent extends React.Component {
render() {
return <div>Hello, {this.props.name}!</div>;
}
}
const MyComponentWithLogging = withLogging(MyComponent);
export default MyComponentWithLogging;
在这个示例中,withLogging
是一个高阶组件,它接受 MyComponent
作为参数,并返回一个新的组件 MyComponentWithLogging
。新的组件在挂载时会输出日志信息。
高阶组件的用途
- 逻辑复用:HOC 可以在多个组件之间共享相同的逻辑,例如权限控制、数据获取、性能优化等。
- 代码分离:通过 HOC,可以将组件的逻辑与视图分离,使代码更加清晰和易于维护。
- 增强组件:HOC 可以为组件添加额外的功能或行为,例如日志记录、错误处理等。
高阶组件的实现
基本实现:
- 高阶组件通常通过类组件或函数组件实现,并在
render
方法中返回传入的组件。
示例:
import React from 'react';
// 一个简单的高阶组件示例
function withExtraProps(WrappedComponent) {
return function EnhancedComponent(props) {
const newProps = { extraProp: 'I am an extra prop' };
return <WrappedComponent {...props} {...newProps} />;
};
}
// 使用高阶组件
function MyComponent(props) {
return <div>{props.extraProp}</div>;
}
const MyComponentWithExtraProps = withExtraProps(MyComponent);
export default MyComponentWithExtraProps;
在这个示例中,withExtraProps
是一个高阶组件,它为传入的组件添加了一个额外的 extraProp
属性。
注意事项
- 不要在 HOC 内部修改原始组件:HOC 应该通过组合来增强组件,而不是直接修改原始组件。
- 传递所有
props
:确保 HOC 将所有props
传递给被包装的组件,以保持组件的可复用性。 - 静态方法的丢失:HOC 返回的新组件不会继承原始组件的静态方法,可以使用
hoist-non-react-statics
库来解决这个问题。
示例:
import React from 'react';
import hoistNonReactStatics from 'hoist-non-react-statics';
function withLogging(WrappedComponent) {
class WithLogging extends React.Component {
componentDidMount() {
console.log('Component mounted');
}
render() {
return <WrappedComponent {...this.props} />;
}
}
hoistNonReactStatics(WithLogging, WrappedComponent);
return WithLogging;
}
常见的高阶组件
- Redux 的
connect
:用于将 Redux 状态和操作绑定到组件上。 - React Router 的
withRouter
:用于将路由相关的props
传递给组件。 - Apollo 的
graphql
:用于将 GraphQL 查询结果绑定到组件上。
总结
高阶组件是 React 中的一种高级技术,用于复用组件逻辑。通过 HOC,可以在多个组件之间共享相同的逻辑,增强组件的功能和行为。理解和掌握高阶组件的使用,可以帮助你构建更加高效和可维护的 React 应用。
react Hooks原理
React Hooks 是 React 16.8 引入的一项特性,允许在函数组件中使用状态和其他 React 特性。Hooks 的引入极大地简化了组件逻辑的组织方式,使得函数组件可以实现与类组件相同的功能。理解 Hooks 的实现原理有助于更好地使用和调试它们。
Hooks 的基本概念
useState
:用于在函数组件中添加状态。useEffect
:用于在函数组件中执行副作用(如数据获取、订阅等)。useContext
:用于在函数组件中访问上下文。useReducer
:用于在函数组件中使用 Redux 风格的状态管理。useMemo
和useCallback
:用于优化性能,缓存计算结果和函数。
Hooks 的实现原理
1. Hook 调用顺序
React 通过一个内部的 Hook 调用栈来跟踪每个 Hook 的调用顺序。每次组件渲染时,Hooks 必须按照相同的顺序调用,以确保状态和副作用能够正确地关联到对应的组件实例。
2. useState
的实现
useState
是最基本的 Hook,用于在函数组件中添加状态。它返回一个状态值和一个更新状态的函数。
示例:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
实现原理:
- React 内部维护一个状态数组,每个组件实例对应一个状态数组。
- 每次调用
useState
时,React 会从状态数组中取出当前 Hook 对应的状态值,并返回给组件。 - 当调用更新函数时,React 会更新状态数组中的对应值,并触发组件重新渲染。
3. useEffect
的实现
useEffect
用于在函数组件中执行副作用。它接受一个函数和一个依赖数组,当依赖数组中的值发生变化时,副作用函数会重新执行。
示例:
import React, { useEffect, useState } from 'react';
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return <p>Count: {count}</p>;
}
实现原理:
- React 内部维护一个副作用数组,每个组件实例对应一个副作用数组。
- 每次调用
useEffect
时,React 会将副作用函数和依赖数组存储在副作用数组中。 - 在组件渲染完成后,React 会遍历副作用数组,执行副作用函数。
- 如果依赖数组发生变化,React 会清除上一次的副作用,并执行新的副作用函数。
4. useContext
的实现
useContext
用于在函数组件中访问上下文。它接受一个上下文对象,并返回当前上下文的值。
示例:
import React, { useContext } from 'react';
const ThemeContext = React.createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button style={{ background: theme === 'light' ? '#fff' : '#333' }}>Themed Button</button>;
}
实现原理:
- React 内部维护一个上下文对象和当前上下文值。
- 每次调用
useContext
时,React 会返回当前上下文的值。 - 当上下文值发生变化时,所有使用该上下文的组件都会重新渲染。
Hooks 的规则
为了确保 Hooks 能够正确工作,React 强制执行以下规则:
- 只能在函数组件或自定义 Hook 中调用 Hooks:不能在普通的 JavaScript 函数中调用 Hooks。
- 只能在顶层调用 Hooks:不能在循环、条件语句或嵌套函数中调用 Hooks。Hooks 必须在组件的顶层调用,以确保每次渲染时调用顺序一致。
总结
React Hooks 通过内部的 Hook 调用栈和状态数组,实现了在函数组件中使用状态和副作用的功能。Hooks 的引入极大地简化了组件逻辑的组织方式,使得函数组件可以实现与类组件相同的功能。理解 Hooks 的实现原理有助于更好地使用和调试它们。
react diff算法原理
React 的 diff 算法是 React 高效更新用户界面的核心。它通过比较新旧虚拟 DOM 树,计算出最小的更新操作,然后将这些操作应用到实际 DOM 中。理解 React 的 diff 算法有助于优化性能和编写高效的 React 应用。
Diff 算法的基本概念
React 的 diff 算法基于以下几个基本假设:
- 同级比较:只比较同一层级的节点,不会跨层级比较。
- 唯一标识:通过
key
属性唯一标识列表中的每个元素,以便高效地比较和更新列表。 - 最小化操作:尽量减少对实际 DOM 的操作,以提高性能。
Diff 算法的工作原理
React 的 diff 算法主要包括以下几个步骤:
1. 树的分层比较
React 通过分层比较新旧虚拟 DOM 树,只比较同一层级的节点,不会跨层级比较。这种方式大大简化了比较过程,提高了性能。
示例:
// 旧的虚拟 DOM
const oldVNode = (
<div>
<h1>Title</h1>
<p>Content</p>
</div>
);
// 新的虚拟 DOM
const newVNode = (
<div>
<h1>New Title</h1>
<p>Content</p>
</div>
);
在这个示例中,React 只会比较 <h1>
和 <p>
节点,而不会跨层级比较。
2. 节点类型比较
React 通过比较节点类型来决定如何更新节点:
- 相同类型的节点:保留节点,只更新属性。
- 不同类型的节点:删除旧节点,创建新节点。
示例:
// 旧的虚拟 DOM
const oldVNode = <div className="old">Old Content</div>;
// 新的虚拟 DOM
const newVNode = <div className="new">New Content</div>;
在这个示例中,React 会保留 <div>
节点,只更新 className
属性和内容。
3. 列表比较
React 通过 key
属性唯一标识列表中的每个元素,以便高效地比较和更新列表。key
属性帮助 React 确定哪些元素被修改、添加或删除。
示例:
// 旧的虚拟 DOM 列表
const oldList = [
<li key="1">Item 1</li>,
<li key="2">Item 2</li>,
<li key="3">Item 3</li>
];
// 新的虚拟 DOM 列表
const newList = [
<li key="1">Item 1</li>,
<li key="3">Item 3</li>,
<li key="2">Item 2</li>
];
在这个示例中,React 会通过 key
属性确定元素的位置变化,只移动元素而不重新创建。
Diff 算法的优化
React 的 diff 算法通过以下优化策略提高性能:
- 最小化 DOM 操作:尽量减少对实际 DOM 的操作,只更新需要改变的部分。
- 批量更新:将多次状态更新合并为一次更新,减少重新渲染的次数。
- 使用
key
属性:通过key
属性唯一标识列表中的每个元素,提高列表比较的效率。
Diff 算法的局限性
虽然 React 的 diff 算法在大多数情况下表现良好,但在某些极端情况下可能会有性能问题。例如,当列表中的元素频繁变化且没有唯一 key
属性时,React 可能会重新创建所有元素,导致性能下降。
总结
React 的 diff 算法通过分层比较、节点类型比较和 key
属性优化,实现了高效的虚拟 DOM 更新。理解 diff 算法的原理有助于编写高效的 React 应用,并在需要时进行性能优化。通过合理使用 key
属性和避免不必要的重新渲染,可以进一步提高 React 应用的性能。
react状态管理
在 React 应用中,状态管理是一个关键概念,涉及到如何在组件之间共享和管理状态。随着应用规模的增长,合理的状态管理策略可以显著提高代码的可维护性和可扩展性。以下是一些常见的 React 状态管理方案及其特点。
1. 本地状态(Local State)
定义:
- 本地状态是指组件内部的状态,使用
useState
或useReducer
Hook 管理。
特点:
- 简单易用,适用于小型应用或单个组件的状态管理。
- 状态只在组件内部可见,不适用于跨组件共享状态。
示例:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
2. 上下文(Context API)
定义:
- Context API 允许在组件树中传递数据,而不需要通过每一级组件手动传递
props
。
特点:
- 适用于中小型应用,解决“props drilling”问题。
- 可以与
useContext
Hook 结合使用,简化状态访问。
示例:
import React, { createContext, useContext, useState } from 'react';
const CountContext = createContext();
function CounterProvider({ children }) {
const [count, setCount] = useState(0);
return (
<CountContext.Provider value={{ count, setCount }}>
{children}
</CountContext.Provider>
);
}
function Counter() {
const { count, setCount } = useContext(CountContext);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
function App() {
return (
<CounterProvider>
<Counter />
</CounterProvider>
);
}
3. Redux
定义:
- Redux 是一个集中式状态管理库,适用于大型应用。它通过单一的状态树(store)管理应用的所有状态。
特点:
- 提供集中式的状态管理和时间旅行调试。
- 需要更多的样板代码(actions、reducers、store)。
- 适用于复杂的状态管理和跨组件共享状态。
示例:
// actions.js
export const increment = () => ({ type: 'INCREMENT' });
// reducer.js
const initialState = { count: 0 };
function counterReducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
default:
return state;
}
}
// store.js
import { createStore } from 'redux';
import counterReducer from './reducer';
const store = createStore(counterReducer);
// Counter.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment } from './actions';
function Counter() {
const count = useSelector(state => state.count);
const dispatch = useDispatch();
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch(increment())}>Increment</button>
</div>
);
}
// App.js
import React from 'react';
import { Provider } from 'react-redux';
import store from './store';
import Counter from './Counter';
function App() {
return (
<Provider store={store}>
<Counter />
</Provider>
);
}
4. MobX
定义:
- MobX 是一个基于响应式编程的状态管理库,简化了状态管理。
特点:
- 使用装饰器和观察者模式,简化状态管理。
- 状态变化自动触发视图更新。
- 适用于需要响应式状态管理的应用。
示例:
import React from 'react';
import { observer } from 'mobx-react';
import { observable, action } from 'mobx';
class CounterStore {
@observable count = 0;
@action increment = () => {
this.count += 1;
};
}
const store = new CounterStore();
const Counter = observer(() => (
<div>
<p>Count: {store.count}</p>
<button onClick={store.increment}>Increment</button>
</div>
));
function App() {
return <Counter />;
}
5. Recoil
定义:
- Recoil 是 Facebook 开发的状态管理库,旨在解决 React 应用中的状态管理问题。
特点:
- 提供原子化状态管理,允许状态的细粒度控制。
- 与 React 紧密集成,支持并发模式。
示例:
import React from 'react';
import { RecoilRoot, atom, useRecoilState } from 'recoil';
const countState = atom({
key: 'countState',
default: 0,
});
function Counter() {
const [count, setCount] = useRecoilState(countState);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
function App() {
return (
<RecoilRoot>
<Counter />
</RecoilRoot>
);
}
总结
- 本地状态:适用于小型应用或单个组件的状态管理。
- Context API:适用于中小型应用,解决“props drilling”问题。
- Redux:适用于大型应用,提供集中式的状态管理和时间旅行调试。
- MobX:适用于需要响应式状态管理的应用,简化状态管理。
- Recoil:提供原子化状态管理,适用于需要细粒度控制的应用。
选择合适的状态管理方案取决于应用的复杂性和具体需求。理解这些状态管理方案及其特点,可以帮助你在开发中做出更合适的选择。
redux工作流程
Redux 是一个用于 JavaScript 应用的状态管理库,特别适用于 React 应用。它提供了一个集中式的状态存储,使得应用的状态管理更加可预测和可维护。理解 Redux 的工作流程有助于更好地使用它来管理应用状态。
Redux 的核心概念
- Store:存储应用的状态,是整个应用的唯一数据源。
- Action:描述应用中发生的事情,是改变状态的唯一途径。
- Reducer:纯函数,接收当前状态和 action,返回新的状态。
- Dispatch:分发 action,触发状态更新。
- Subscriber:监听状态变化,通常用于更新 UI。
Redux 的工作流程
Redux 的工作流程可以分为以下几个步骤:
1. 定义 Action
Action 是一个描述发生了什么的普通 JavaScript 对象。每个 action 必须有一个 type
属性,表示 action 的类型,通常还会有一些附加数据。
示例:
// actions.js
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export const increment = () => ({
type: INCREMENT
});
export const decrement = () => ({
type: DECREMENT
});
2. 定义 Reducer
Reducer 是一个纯函数,接收当前状态和 action,返回新的状态。Reducer 根据 action 的类型决定如何更新状态。
示例:
// reducer.js
import { INCREMENT, DECREMENT } from './actions';
const initialState = {
count: 0
};
function counterReducer(state = initialState, action) {
switch (action.type) {
case INCREMENT:
return { ...state, count: state.count + 1 };
case DECREMENT:
return { ...state, count: state.count - 1 };
default:
return state;
}
}
export default counterReducer;
3. 创建 Store
Store 是应用的唯一数据源,使用 createStore
函数创建。Store 需要一个 reducer 作为参数。
示例:
// store.js
import { createStore } from 'redux';
import counterReducer from './reducer';
const store = createStore(counterReducer);
export default store;
4. 分发 Action
通过 store.dispatch
方法分发 action,触发状态更新。
示例:
// index.js
import store from './store';
import { increment, decrement } from './actions';
// 订阅状态变化
store.subscribe(() => {
console.log(store.getState());
});
// 分发 action
store.dispatch(increment());
store.dispatch(decrement());
5. 连接 React 组件
使用 react-redux
库将 Redux 与 React 连接。通过 Provider
组件将 Redux store 提供给 React 组件树,通过 connect
函数将 Redux 状态和操作绑定到组件上。
示例:
// Counter.js
import React from 'react';
import { connect } from 'react-redux';
import { increment, decrement } from './actions';
function Counter({ count, increment, decrement }) {
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
const mapStateToProps = state => ({
count: state.count
});
const mapDispatchToProps = {
increment,
decrement
};
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
// App.js
import React from 'react';
import { Provider } from 'react-redux';
import store from './store';
import Counter from './Counter';
function App() {
return (
<Provider store={store}>
<Counter />
</Provider>
);
}
export default App;
Redux 的数据流
Redux 的数据流是单向的,遵循以下步骤:
- Action:用户在应用中触发一个 action。
- Dispatch:通过
store.dispatch
方法分发 action。 - Reducer:Redux 调用 reducer,传入当前状态和 action,计算出新的状态。
- Store:Store 保存新的状态。
- Subscriber:所有订阅了状态变化的监听器(如 React 组件)都会被通知,更新 UI。
总结
Redux 的工作流程包括定义 action、定义 reducer、创建 store、分发 action 和连接 React 组件。通过理解 Redux 的核心概念和数据流,可以更好地使用 Redux 来管理应用状态,提高代码的可维护性和可扩展性。
js的this指向
this
指向总结
-
全局上下文:
this
指向全局对象(在浏览器中是window
)。
-
函数调用:
this
指向全局对象(在严格模式下是undefined
)。
-
方法调用:
this
指向调用该方法的对象。
-
构造函数调用:
this
指向新创建的实例对象。
-
call
和apply
调用:this
指向第一个参数。
-
箭头函数:
this
捕获其所在上下文的this
值(即词法作用域中的this
)。
-
事件处理函数:
this
指向触发事件的 DOM 元素。
-
bind
方法:this
绑定到指定的对象。
-
类方法:
this
指向类的实例。
理解这些规则有助于正确使用 this
,从而编写更清晰和可维护的 JavaScript 代码。
防抖截流
防抖(Debouncing)和截流(Throttling)是两种常用的优化技术,用于控制函数的执行频率,特别是在处理频繁触发的事件(如窗口调整、滚动、输入框输入等)时。这两种技术可以提高性能,减少不必要的计算和资源消耗。
防抖(Debouncing)
防抖的基本思想是:在事件被触发后,等待一段时间再执行函数。如果在等待期间事件再次被触发,则重新开始计时。防抖适用于需要在事件停止触发后执行一次操作的场景。
实现原理:
- 使用一个定时器来延迟函数的执行。
- 每次事件触发时,清除之前的定时器并重新设置一个新的定时器。
示例:
function debounce(func, wait) {
let timeout;
return function(...args) {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), wait);
};
}
// 使用示例:在用户停止输入后执行搜索
const handleInput = debounce(() => {
console.log('Search executed');
}, 300);
document.getElementById('searchInput').addEventListener('input', handleInput);
截流(Throttling)
截流的基本思想是:在一段时间内,只允许函数执行一次。截流适用于需要限制函数执行频率的场景。
实现原理:
- 使用一个标志来记录函数是否可以执行。
- 在函数执行后,设置一个定时器来重置标志。
示例:
function throttle(func, limit) {
let inThrottle;
return function(...args) {
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// 使用示例:限制窗口调整事件的处理频率
const handleResize = throttle(() => {
console.log('Resize event handled');
}, 1000);
window.addEventListener('resize', handleResize);
防抖与截流的区别
- 防抖:在事件停止触发后执行一次操作。适用于需要在用户停止输入后执行搜索、自动保存等场景。
- 截流:在一段时间内只允许函数执行一次。适用于限制滚动、窗口调整等事件的处理频率。
选择使用防抖还是截流
- 防抖:适用于需要在事件停止触发后执行一次操作的场景,如搜索框输入、表单验证等。
- 截流:适用于需要限制事件处理频率的场景,如滚动事件、窗口调整事件等。
通过合理使用防抖和截流,可以有效提高应用的性能,减少不必要的计算和资源消耗。
异步编程promise
异步编程是现代 JavaScript 开发中的一个重要概念,尤其是在处理网络请求、文件读取等耗时操作时。Promise
是 JavaScript 中用于处理异步操作的一种方式,它提供了一种更清晰和更易于管理的异步代码编写方式。
Promise 的基本概念
Promise
是一个表示异步操作最终完成或失败的对象。它有三种状态:
- Pending(进行中):初始状态,操作尚未完成。
- Fulfilled(已成功):操作成功完成。
- Rejected(已失败):操作失败。
Promise 的基本用法
一个 Promise
对象可以通过 new Promise
构造函数创建。构造函数接收一个执行器函数,该函数包含两个参数:resolve
和 reject
。在异步操作成功时调用 resolve
,在失败时调用 reject
。
示例:
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true; // 模拟异步操作结果
if (success) {
resolve('Operation succeeded');
} else {
reject('Operation failed');
}
}, 1000);
});
myPromise
.then(result => {
console.log(result); // 输出: Operation succeeded
})
.catch(error => {
console.error(error); // 如果失败,输出错误信息
});
Promise 的方法
then
:用于处理Promise
成功的结果。它接收两个可选参数:成功时的回调函数和失败时的回调函数。catch
:用于处理Promise
失败的结果。它相当于then
的第二个参数,但更具可读性。finally
:无论Promise
成功还是失败,都会执行的回调函数。
示例:
myPromise
.then(result => {
console.log(result);
})
.catch(error => {
console.error(error);
})
.finally(() => {
console.log('Operation completed');
});
Promise 的链式调用
Promise
支持链式调用,可以通过返回新的 Promise
来实现多个异步操作的顺序执行。
示例:
function asyncOperation1() {
return new Promise((resolve) => {
setTimeout(() => resolve('Result 1'), 1000);
});
}
function asyncOperation2() {
return new Promise((resolve) => {
setTimeout(() => resolve('Result 2'), 1000);
});
}
asyncOperation1()
.then(result1 => {
console.log(result1);
return asyncOperation2();
})
.then(result2 => {
console.log(result2);
});
Promise 的静态方法
Promise.all
:接收一个Promise
数组,返回一个新的Promise
,当所有Promise
都成功时,返回成功结果数组;如果有一个失败,则返回失败结果。Promise.race
:接收一个Promise
数组,返回第一个完成的Promise
的结果,无论成功还是失败。Promise.allSettled
:接收一个Promise
数组,返回一个新的Promise
,当所有Promise
都已完成时,返回每个Promise
的结果对象数组。Promise.any
:接收一个Promise
数组,返回第一个成功的Promise
的结果;如果所有Promise
都失败,则返回一个失败结果。
示例:
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'foo'));
const promise3 = Promise.reject('Error');
Promise.all([promise1, promise2])
.then(values => console.log(values)) // 输出: [3, 'foo']
.catch(error => console.error(error));
Promise.race([promise1, promise2, promise3])
.then(value => console.log(value)) // 输出: 3
.catch(error => console.error(error));
总结
Promise
提供了一种更清晰和更易于管理的方式来处理异步操作。通过 then
、catch
和 finally
方法,可以处理异步操作的成功和失败结果。Promise
的链式调用和静态方法(如 Promise.all
和 Promise.race
)使得处理多个异步操作变得更加简单和高效。理解和掌握 Promise
是编写现代 JavaScript 应用程序的重要技能。