面试总结(持续更新)

字节、滴滴面试

  • 如何触发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);

  1. 写一个任务队列,可以添加任务,没三秒执行一个任务
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'));

滴滴打车

  1. bfc全称(block Formatting context)
  2. 盒模型

天志数融

  1. 前端如何优化
前端优化是提高网站性能和用户体验的关键步骤。优化前端可以显著减少页面加载时间,提高响应速度,并改善整体用户体验。以下是一些常见的前端优化策略:

### 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等工具分析和监控网站性能。
- **持续优化**:根据分析结果持续优化,确保网站性能保持在最佳状态。

通过实施这些优化策略,你可以显著提高网站的加载速度和用户体验,从而提高用户满意度和网站的整体表现。
  1. 如何做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是一个持续的过程,需要不断的监控和优化。通过结合以上策略,你可以提高网站在搜索引擎中的可见性,吸引更多的自然流量。
  1. 如何加快首屏加载速度

优酷

  • 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 包括 useStateuseEffectuseContext 等。
  • 简化代码: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。新的组件在挂载时会输出日志信息。

高阶组件的用途

  1. 逻辑复用:HOC 可以在多个组件之间共享相同的逻辑,例如权限控制、数据获取、性能优化等。
  2. 代码分离:通过 HOC,可以将组件的逻辑与视图分离,使代码更加清晰和易于维护。
  3. 增强组件: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 属性。

注意事项

  1. 不要在 HOC 内部修改原始组件:HOC 应该通过组合来增强组件,而不是直接修改原始组件。
  2. 传递所有 props:确保 HOC 将所有 props 传递给被包装的组件,以保持组件的可复用性。
  3. 静态方法的丢失: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;
}

常见的高阶组件

  1. Redux 的 connect:用于将 Redux 状态和操作绑定到组件上。
  2. React Router 的 withRouter:用于将路由相关的 props 传递给组件。
  3. Apollo 的 graphql:用于将 GraphQL 查询结果绑定到组件上。

总结

高阶组件是 React 中的一种高级技术,用于复用组件逻辑。通过 HOC,可以在多个组件之间共享相同的逻辑,增强组件的功能和行为。理解和掌握高阶组件的使用,可以帮助你构建更加高效和可维护的 React 应用。

react Hooks原理

React Hooks 是 React 16.8 引入的一项特性,允许在函数组件中使用状态和其他 React 特性。Hooks 的引入极大地简化了组件逻辑的组织方式,使得函数组件可以实现与类组件相同的功能。理解 Hooks 的实现原理有助于更好地使用和调试它们。

Hooks 的基本概念

  • useState:用于在函数组件中添加状态。
  • useEffect:用于在函数组件中执行副作用(如数据获取、订阅等)。
  • useContext:用于在函数组件中访问上下文。
  • useReducer:用于在函数组件中使用 Redux 风格的状态管理。
  • useMemouseCallback:用于优化性能,缓存计算结果和函数。

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 强制执行以下规则:

  1. 只能在函数组件或自定义 Hook 中调用 Hooks:不能在普通的 JavaScript 函数中调用 Hooks。
  2. 只能在顶层调用 Hooks:不能在循环、条件语句或嵌套函数中调用 Hooks。Hooks 必须在组件的顶层调用,以确保每次渲染时调用顺序一致。

总结

React Hooks 通过内部的 Hook 调用栈和状态数组,实现了在函数组件中使用状态和副作用的功能。Hooks 的引入极大地简化了组件逻辑的组织方式,使得函数组件可以实现与类组件相同的功能。理解 Hooks 的实现原理有助于更好地使用和调试它们。

react diff算法原理

React 的 diff 算法是 React 高效更新用户界面的核心。它通过比较新旧虚拟 DOM 树,计算出最小的更新操作,然后将这些操作应用到实际 DOM 中。理解 React 的 diff 算法有助于优化性能和编写高效的 React 应用。

Diff 算法的基本概念

React 的 diff 算法基于以下几个基本假设:

  1. 同级比较:只比较同一层级的节点,不会跨层级比较。
  2. 唯一标识:通过 key 属性唯一标识列表中的每个元素,以便高效地比较和更新列表。
  3. 最小化操作:尽量减少对实际 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 算法通过以下优化策略提高性能:

  1. 最小化 DOM 操作:尽量减少对实际 DOM 的操作,只更新需要改变的部分。
  2. 批量更新:将多次状态更新合并为一次更新,减少重新渲染的次数。
  3. 使用 key 属性:通过 key 属性唯一标识列表中的每个元素,提高列表比较的效率。

Diff 算法的局限性

虽然 React 的 diff 算法在大多数情况下表现良好,但在某些极端情况下可能会有性能问题。例如,当列表中的元素频繁变化且没有唯一 key 属性时,React 可能会重新创建所有元素,导致性能下降。

总结

React 的 diff 算法通过分层比较、节点类型比较和 key 属性优化,实现了高效的虚拟 DOM 更新。理解 diff 算法的原理有助于编写高效的 React 应用,并在需要时进行性能优化。通过合理使用 key 属性和避免不必要的重新渲染,可以进一步提高 React 应用的性能。

react状态管理

在 React 应用中,状态管理是一个关键概念,涉及到如何在组件之间共享和管理状态。随着应用规模的增长,合理的状态管理策略可以显著提高代码的可维护性和可扩展性。以下是一些常见的 React 状态管理方案及其特点。

1. 本地状态(Local State)

定义

  • 本地状态是指组件内部的状态,使用 useStateuseReducer 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 的核心概念

  1. Store:存储应用的状态,是整个应用的唯一数据源。
  2. Action:描述应用中发生的事情,是改变状态的唯一途径。
  3. Reducer:纯函数,接收当前状态和 action,返回新的状态。
  4. Dispatch:分发 action,触发状态更新。
  5. 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 的数据流是单向的,遵循以下步骤:

  1. Action:用户在应用中触发一个 action。
  2. Dispatch:通过 store.dispatch 方法分发 action。
  3. Reducer:Redux 调用 reducer,传入当前状态和 action,计算出新的状态。
  4. Store:Store 保存新的状态。
  5. Subscriber:所有订阅了状态变化的监听器(如 React 组件)都会被通知,更新 UI。

总结

Redux 的工作流程包括定义 action、定义 reducer、创建 store、分发 action 和连接 React 组件。通过理解 Redux 的核心概念和数据流,可以更好地使用 Redux 来管理应用状态,提高代码的可维护性和可扩展性。

js的this指向

this 指向总结

  • 全局上下文

    • this 指向全局对象(在浏览器中是 window)。
  • 函数调用

    • this 指向全局对象(在严格模式下是 undefined)。
  • 方法调用

    • this 指向调用该方法的对象。
  • 构造函数调用

    • this 指向新创建的实例对象。
  • callapply 调用

    • 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 是一个表示异步操作最终完成或失败的对象。它有三种状态:

  1. Pending(进行中):初始状态,操作尚未完成。
  2. Fulfilled(已成功):操作成功完成。
  3. Rejected(已失败):操作失败。

Promise 的基本用法

一个 Promise 对象可以通过 new Promise 构造函数创建。构造函数接收一个执行器函数,该函数包含两个参数:resolvereject。在异步操作成功时调用 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 提供了一种更清晰和更易于管理的方式来处理异步操作。通过 thencatchfinally 方法,可以处理异步操作的成功和失败结果。Promise 的链式调用和静态方法(如 Promise.allPromise.race)使得处理多个异步操作变得更加简单和高效。理解和掌握 Promise 是编写现代 JavaScript 应用程序的重要技能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值