2023年最新前端面试题汇总大全二(含答案超详细,Vue,TypeScript,React,微信小程序,Webpack 汇总篇)-- 持续更新 5

简介: 2023年最新前端面试题汇总大全二(含答案超详细,Vue,TypeScript,React,微信小程序,Webpack 汇总篇)-- 持续更新

18.react 和 vue 有什么区别?

区别:

React 的思路是 HTML in JavaScript 通过 JavaScript 来生成 HTML,所以设计了 JSX 语法,还有通过 JS 来操作 CSS

Vue 是把 HTML,CSS,JavaScript 组合到一起,用各自的处理方式,Vue 有单文件组件,可以把 HTML、CSS、JS 写到一个文件中,HTML 提供了模板引擎来处理。

React 整体是函数式的思想,在 React 中是单向数据流,推崇结合 immutable 来实现数据不可变。

Vue 的思想是响应式的,也就是基于是数据可变的,通过对每一个属性建立 Watcher 来监听,当属性变化的时候,响应式的更新对应的虚拟 DOM。

React 的性能优化需要手动去做,而Vue的性能优化是自动的,但是Vue的响应式机制也有问题,就是当 state 特别多的时候,Watcher 会很多,会导致卡顿。

共同点:

React 与 Vue 存在很多共同点,例如他们都是 JavaScript 的 UI 框架,组件化开发,单项数据流,声明式编程,虚拟dom

优势:

React

灵活性和响应性:它提供最大的灵活性和响应能力。

丰富的JavaScript库:来自世界各地的贡献者正在努力添加更多功能。

可扩展性:由于其灵活的结构和可扩展性,React已被证明对大型应用程序更好。

不断发展: React得到了Facebook专业开发人员的支持,他们不断寻找改进方法。

Web或移动平台: React提供React Native平台,可通过相同的React组件模型为iOS和Android开发本机呈现的应用程序。

Vue

易于使用: Vue.js包含基于HTML的标准模板,可以更轻松地使用和修改现有应用程序。

更顺畅的集成:无论是单页应用程序还是复杂的Web界面,Vue.js都可以更平滑地集成更小的部件,而不会对整个系统产生任何影响。

更好的性能,更小的尺寸:它占用更少的空间,并且往往比其他框架提供更好的性能。

精心编写的文档:通过详细的文档提供简单的学习曲线,无需额外的知识; HTML和JavaScript将完成工作。

适应性:整体声音设计和架构使其成为一种流行的JavaScript框架。

它提供无障碍的迁移,简单有效的结构和可重用的模板。

总结:

Vue 的响应式机制也有问题,当 state 特别多的时候,Watcher 会很多,会导致卡顿,所以大型应用(状态特别多的)一般用 React,更加可控。

对于易用性来说,VUE 是更容易上手的,对于项目来说新人更容易接手。

19.React render方法的原理,在什么时候会触发?

原理:

在类组件中render函数指的就是render方法;而在函数组件中,指的就是整个函数组件

render函数中的jsx语句会被编译成我们熟悉的js代码,在render过程中,react将新调用的render函数返回的树与旧版本的树进行比较,这一步是决定如何更新 DOM 的必要步骤,然后进行 diff 比较,更新dom树

触发机:

类组件调用 setState 修改状态

函数组件通过useState hook修改状态

一旦执行了setState就会执行render方法,useState 会判断当前值有无发生改变确定是否执行render方法,一旦父组件发生渲染,子组件也会渲染


20.React Hooks 在使用上有哪些限制?

不要在循环、条件或嵌套函数中调用 Hook;

在 React 的函数组件中调用 Hook。

21.React Hooks概述及常用的Hooks介绍?

Hooks是React 16.8版本引入的新特性,它为函数组件添加了一些类似于类组件中的状态和生命周期方法的功能


useState:用于在函数组件中添加状态管理功能。它返回一个由当前状态值和更新函数组成的数组,我们可以通过该数组来获取和更新状态的值。

useEffect:用于执行副作用操作,例如订阅事件、修改DOM等。它接受一个函数作为参数,该函数将在每次渲染完成后执行。

useContext:用于在组件间共享数据,它接受一个上下文对象作为参数,然后返回该上下文对象中提供的数据。

useReducer:用于对复杂状态进行管理。它接受一个reducer函数和初始状态作为参数,并返回一个由当前状态值和dispatch函数组成的数组。

useCallback:用于缓存函数以提高性能,类似于React.memo。

useMemo:用于缓存计算结果以提高性能,类似于记忆函数。

useRef:用于引用DOM节点或保存任意可变值,它返回一个可变的ref对象。

22.说说React生命周期中有哪些坑?如何避免?

getDerivedStateFromProps 容易编写反模式代码,使受控组件和非受控组件区分模糊

componentWillMount 在 React 中已被标记弃用,不推荐使用,主要的原因是因为新的异步架构会导致它被多次调用,所以网络请求以及事件绑定应该放到 componentDidMount 中

componentWillReceiveProps 同样也被标记弃用,被 getDerivedStateFromProps 所取代,主要原因是性能问题。

shouldComponentUpdate 通过返回 true 或者 false 来确定是否需要触发新的渲染。主要用于性能优化。

componentWillUpdate 同样是由于新的异步渲染机制,而被标记废弃,不推荐使用,原先的逻辑可结合 getSnapshotBeforeUpdate 与 componentDidUpdate 改造使用。

如果在 componentWillUnmount 函数中忘记解除事件绑定,取消定时器等清理操作,容易引发 bug。

如果没有添加错误边界处理,当渲染发生异常时,用户将会看到一个无法操作的白屏,所以一定要添加。

23.说说Real diff算法是怎么运作的?

Diff算法是虚拟DOM的一个必然结果,它是通过新旧DOM的对比,将在不更新页面的情况下,将需要内容局部更新

Diff算法遵循深度优先,同层比较的原则

react中diff算法主要遵循三个层级的策略:

tree层级:DOM节点的跨层级操作不做优化,只对相同层节点进行比较,只有删除创建操作,没有移动操作

conponent 层级:如果是同一类的组件,则会继续往下进行diff运算,如果不是则直接删除组件下的所有子节点,创建新的

element 层级:对于比较同一层级的节点们,每个节点在对应的层级用唯一的key作为标识,通过key知道节点的变化,移动旧集合节点位置,更新为新集合节点位置

24.调和阶段setState干了什么?

代码中调用 setState 函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。

经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面;

在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染;

在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。

25.使用 redux 有哪些原则?

单一数据源:整个应用的全局 state 被存储在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。

State 是只读的:唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事情的普通对象。

使用纯函数来执行修改:为了描述 action 如何改变 state tree,你需要编写纯的 reducers。

26.说说redux的实现原理是什么,写出核心代码?

将应用的状态统一放到state中,由store来管理state。

reducer的作用是 返回一个新的state去更新store中对用的state。

按redux的原则,UI层每一次状态的改变都应通过action去触发,action传入对应的reducer 中,reducer返回一个新的state更新store中存放的state,这样就完成了一次状态的更新

subscribe是为store订阅监听函数,这些订阅后的监听函数是在每一次dipatch发起后依次执行

可以添加中间件对提交的dispatch进行重写

核心API

createStore 创建仓库,接受reducer作为参数

bindActionCreator 绑定store.dispatch和action 的关系

combineReducers 合并多个reducers

applyMiddleware 洋葱模型的中间件,介于dispatch和action之间,重写dispatch

compose 整合多个中间件

27.对Redux中间件的理解?原理?常用中间件有哪些?

Redux中,中间件就是放在就是在dispatch过程,在分发action进行拦截处理, 其本质上一个函数,对store.dispatch方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能


实现原理:


applyMiddlewares的源码 中我们可以看到 ,所有中间件被放进了一个数组chain,然后嵌套执行,最后执行store.dispatch。可以看到,中间件内部(middlewareAPI)可以拿到getState和dispatch这两个方法

常用中间件:


redux-thunk:用于异步操作

redux-logger:用于日志记录

28.Redux 和 Vuex 有什么区别,它们有什么共同思想吗?

相同点:

state 共享数据

流程一致:定义全局state,触发,修改state

原理相似,通过全局注入store。

不同点:

实现原理:

Redux 使用的是不可变数据,而Vuex的数据是可变的。Redux每次都是用新的state替换旧的state,而Vuex是直接修改

Redux 在检测数据变化的时候,是通过 diff 的方式比较差异的,而Vuex其实和Vue的原理一样,是通过 getter/setter来比较的

表现层:

vuex定义了state、getter、mutation、action四个对象;redux定义了state、reducer、action。

vuex中state统一存放,方便理解;reduxstate依赖所有reducer的初始值

vuex有getter,目的是快捷得到state;redux没有这层,react-redux mapStateToProps参数做了这个工作。

vuex中mutation只是单纯赋值(很浅的一层);redux中reducer只是单纯设置新state(很浅的一层)。他俩作用类似,但书写方式不同

vuex中action有较为复杂的异步ajax请求;redux中action中可简单可复杂,简单就直接发送数据对象({type:xxx, your-data}),复杂需要调用异步ajax(依赖redux-thunk插件)。

vuex触发方式有两种commit同步和dispatch异步;redux同步和异步都使用dispatch

共同思想:

单一的数据源

变化可以预测

29.props和state相同点和不同点?

不同点:

props是只读的,只能由父组件传递给子组件,而不能在子组件中修改。而state是可变的,在组件内部可以通过setState方法来修改其值

相同点:

1.props和state都会触发渲染更新


2.props和state都是纯JS对象(用typeof来判断,结果都是object)


30.shouldComponentUpdate有什么作用?

shouldComponentUpdate () 可以理解为是否触发渲染的阀门,当状态发生改变时会走到该生命周期,shouldComponentUpdate接收两个参数props,state分别是更新前和更新后的状态,可以判断前后是否发生改变返回true和false,来决定是否往下执行


31.React的props.children使用map函数来遍历会收到异常显示,为什么?应该 如何遍历

原因:在react.js中props.children不一定是数组


当前组件没有子节点 undefined

有一个子节点 object

多个子节点 array

react资深提供了一个react.children.map()方法,可以安全遍历子节点对象。


32.谈谈你对immutable.js的理解?

Immutable.js采用了 持久化数据结构 ,保证每一个对象都是不可变的,任何添加、修改、删除等操作都会生成一个新的对象,且通过 结构共享 等方式大幅提高性能

33.redux原理、工作流程及其应用,三大原则

redux: redux是专门用于集中式管理状态的javascript库,他并不是react的插件库。


redux三大核心:


actions

actions英文直译过来就是行动、动作的意思,那么我们就可以猜到他表示的是“怎么做”,简单来说actions就是一个对象,actions里面有两个属性分别为type和data:

type:标识属性,值为字符串且唯一,必要属性(你想要做什么事情

data:数据属性,值为任意类型,可选属性(你要做事情的数据


那我们浅浅举个栗子:比如计算器你可以进行加1减2等操作,那么加减乘除这个操作就是你的type,数字就是你的数据


store

store有且只能有一个,他相当于一个最高指挥家,他负责把action动作交给对应的reducer进行执行,也就是说将state、action和reducer联系在一起的对象。


reducer

reducer用于将store发过来的action完成并将结果返回给store,他接收两个参数preState(旧状态)和action(动作)并返回一个newState(新状态)。


工作流程:当组件使用store中的数据需要发生变化时,告诉action生成动作对象,通过dispatch分发对象到store,store对需要使用的reducer进行绑定,然后将action分发到对应reducer上执行相应逻辑进行数据覆盖,再将store数据渲染


1c6d8c8a24d74c85806490f06fd719ca.png


三大原则


唯一数据源(state)


数据源(state)只读


通过纯函数(pure function)改变数据源(state)


34.react-redux原理,工作流程

react-redux实现过程


react-redux执行流程详解:


初始化阶段:


创建 Redux store 对象,并将 Reducer 传入 createStore 函数中。

创建一个 Provider 组件,并将 Redux store 对象作为 Provider 组件的 props 传入其中。

将应用根组件包装在 Provider 组件中,并渲染整个应用。

运行阶段:


使用 connect 函数将组件与 Redux 中的 state 和 action creators 相连接,并将它们转化为组件的 props 属性

import { connect } from 'react-redux';
import { addToCart } from '../actions';
const Product = ({ product, addToCart }) => (
 <div>
   <h3>{product.name}</h3>
   <button onClick={() => addToCart(product)}>Add to cart</button>
 </div>
);
const mapDispatchToProps = {
 addToCart,
};
export default connect(null, mapDispatchToProps)(Product);

更新阶段:


在 Store 的 dispatch 方法中,执行 action 并更新 Store 中的 state。

React-Redux 根据 Store 中的新状态,检查哪些组件的 props 发生了变化。

对于发生变化的组件,React-Redux 将触发相关的生命周期方法和 render 方法进行重新渲染。

工作流程:


4c284d62ee5342179516ed658fdb92d6.png


35.react组件通讯的context

使用步骤:


在顶层组件中创建一个 context 对象,并将需要共享的数据挂载到该对象上

const MyContext = React.createContext(defaultValue);

在顶层组件的 render 方法中,使用 MyContext.Provider 组件来包裹整个应用程序,并将共享的数据传递给 value 属性

<MyContext.Provider value={sharedData}>
    <App />
</MyContext.Provider>

在需要使用共享数据的组件中,使用 MyContext.Consumer 组件来接收 context 的 value 属性,并在 Consumer 的子元素中使用这些数据

<MyContext.Consumer>
    {sharedData => (
        // 此处可以使用 sharedData 来操作共享数据
    )}
</MyContext.Consumer>

36.react-redux中 Provider 组件实现原理

React-Redux 中的 Provider 组件是一个 React 组件,它使用了 React 的 Context API 来实现数据的传递。Provider 组件提供一个 context 对象,它可以让嵌套在它内部的子组件都可以访问到这个 context 对象,并且可以通过它来获取到 Redux store 中的数据。


37.react-redux中Connect原理

connect 函数是将 React 组件与 Redux Store 进行连接的重要方法。它接收两个函数作为参数,并返回一个高阶组件,通过这个高阶组件可以将 Redux Store 和 React 组件关联起来。 (实现容器组件包裹ui组件)


在原应用组件上包裹一层,使原来整个应用成为Provider的子组件,接收Redux的store作为props,通过context对象传递给子孙组件上的connect,它真正连接 Redux 和 React,它包在我们的容器组件的外一层,它接收上面 Provider 提供的 store 里面的 state 和 dispatch,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。

//其中 mapStateToProps 和 mapDispatchToProps 是将 Store 和 action creator 映射到组件的 props 上的函数。
connect(mapStateToProps, mapDispatchToProps)(Product);

38.react-toolkit

react-toolkit是一个官方维护的包含多个有用工具的 Redux 库,旨在使 React 和 Redux 的开发变得更加简单、高效和直观


简化 Redux 工作流

redux-toolkit 提供了一种新的方式来编写 Redux 应用程序,该方式包含了常见的 Redux 模式,并通过封装样板代码来简化它们。这使得开发者可以更加专注于实现业务逻辑,而不必关心较低级别的细节。


内置常用中间件

redux-toolkit 包含了 Redux 应用程序中常用的几个中间件,如 redux-thunk 中间件、redux-saga 中间件和 redux-logger 中间件,使开发者可以轻松地使用和配置这些中间件。


1.强制执行不可变性

redux-toolkit 的 createSlice 函数在创建 reducer 时会自动使用不可变性(immutability)来更新 state,这避免了因直接修改 state 而产生的潜在错误。


1.自动生成 Redux action 类型

redux-toolkit 提供了 createSlice 函数来创建 reducer,该函数还会自动为每个 action type 创建一个字符串常量,避免手动编写这些常量带来的冗余代码。


1.管理副作用

redux-toolkit 提供了 createAsyncThunk 函数来创建具有异步副作用的 action,该函数自动处理异步流程,并可以在状态中跟踪每个异步操作的进度和结果。


39.React.memo() 和 useMemo() 的用法是什么,有哪些区别?

React.memo 是一个高阶组件,它可以将一个纯函数组件,当组件的 props 没有变化时,会直接复用组件的渲染结果,从而避免不必要的渲染。

//当 MyComponent 的 text 属性没有变化时,MemoizedComponent 就会复用之前的渲染结果,而不会重新渲染。
function MyComponent(props) {
  return <div>{props.text}</div>;
}
const MemoizedComponent = React.memo(MyComponent);

useMemo 是一个 Hook,它可以用于缓存计算结果,以避免重复计算。当传入的依赖项没有变化时,会直接返回缓存的结果。

//如果 calculate 方法比较耗时,为了避免不必要的计算,我们可以使用 useMemo 来缓存计算结果:
function MyComponent({ a, b }) {
  const result = useMemo(() => calculate(a, b), [a, b]);
  return <div>{result}</div>;
}

区别:


React.memo 和 useMemo 都可以用于优化 React 应用的性能,但是它们的优化对象和优化手段不同。React.memo 通过避免组件的不必要渲染来提高性能,而 useMemo 通过避免重复计算来提高性能。在实际开发中,需要根据具体场景和需求来选择适合的优化方法。


40.usecallback,usememo区别

useCallback 用于缓存函数,以避免不必要的函数创建和渲染。当依赖项发生变化时,会返回一个新的函数引用,否则直接返回之前缓存的函数引用。

const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

useMemo 用于缓存计算结果,以避免重复计算和渲染。当依赖项发生变化时,会重新计算并返回新的计算结果

const memoizedValue = useMemo(() => {
  return heavyComputation(a, b);
}, [a, b]);

useCallback 通过避免函数创建和渲染来提高性能,而 useMemo 通过避免重复计算和渲染来提高性能


41.react使用ref

ref 是用来访问 DOM 元素或组件实例的引用的一种方式。


字符串回调

class MyComponent extends React.Component {
  componentDidMount() {
    console.log(this.refs.myInput);
    // 输出:<input type="text" />
  }
  render() {
    return <input type="text" ref="myInput" />;
  }
}

函数回调

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = null;
  }
  componentDidMount() {
    console.log(this.myRef);
    // 输出:<input type="text" />
  }
  render() {
    return <input type="text" ref={node => this.myRef = node} />;
  }
}

React.createRef

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  componentDidMount() {
    console.log(this.myRef.current);
    // 输出:<input type="text" />
  }
  render() {
    return <input type="text" ref={this.myRef} />;
  }
}

42.为什么循环和判断不能使用hooks

原因:


循环、条件语句等块级作用域会影响 Hooks 调用的次数和顺序,从而破坏 React 内部的依赖关系和渲染逻辑,导致组件出现无法预期的错误。因此,为了保证组件能够正常渲染和更新,我们需要遵循 React Hooks 的使用规范,在顶层作用域中调用 Hooks。


43.React实现过度动画

使用第三方库: React Transition Group 详细学习过度动画


注意: CSSTransition 中类命名方式classNames


44. react懒加载实现原理

React 的懒加载实现原理主要是基于 ES6 的 import() 函数和 React 的 lazy 函数。

import React, { lazy, Suspense } from "react";
const LazyComponent = lazy(() => import("./LazyComponent"));
function App() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <LazyComponent />
      </Suspense>
    </div>
  );
}

当我们使用 lazy 函数时,React 会在运行时动态创建一个新的组件,这个新的组件继承了原始组件的所有属性和方法,并且它的 render 方法被重写成一个异步函数。当这个新的组件需要被渲染时,React 就会自动触发它的 render 方法,这个方法会异步加载原始组件的代码,并将其渲染到页面上。


这里涉及到了 ES6 中的 import() 函数,它是一个异步函数,用来动态加载 JavaScript 模块。import() 函数会返回一个 Promise 对象,当模块加载完成后,Promise 对象就会被 resolve,我们可以通过 then 方法获取模块的默认导出对象。


45.immutable

Immutable 是一个 JavaScript 库,它提供了一些数据结构和 API,使得创建不可变数据成为可能。React 中使用 Immutable 可以带来以下好处:


性能优化: React 中使用的 Virtual DOM 技术会频繁地进行比较和更新操作,而不可变数据可以减少需要更新的节点数量,从而提高应用的性能和响应速度。

便于管理状态: React 应用中的状态通常非常复杂,使用不可变数据可以更加方便地管理状态,并且避免了因为状态被修改而引发的副作用。

并发安全: 在高并发环境下,使用不可变数据可以减少数据竞争和锁竞争,提高并发安全性能。

immutable创建不可变数据类型,想要修改只能通过数据覆盖创建新的变量,对react进行性能优化


46.useState为什么返回数组而不是对象

原因:React 团队认为,使用数组可以带来更好的灵活性和易用性


优点:


无需命名: 当使用数组时,我们可以使用数组解构语法来命名状态变量和修改函数,并且不需要考虑命名冲突等问题。

直接修改: 使用数组解构后,我们可以直接对数组元素进行修改操作,而无需再次调用对象中的某个方法或属性。

易于扩展: 在未来的版本中,React 还可能会增加更多的状态相关的 Hook,使用数组可以使得新增的 Hook 更加容易添加到现有的语法中

47.类组件为什么不能使用hooks

Hooks 是基于函数式编程思想设计的,不支持类组件的一些特性。但是,React 提供了一些方法,可以让开发者在类组件中使用 Hooks 的部分功能,或者将类组件转换为函数组件来使用 Hooks。


48.redux-thunk和redux-saga区别

redux-thunk 的设计思路是将异步逻辑封装到“thunk”函数中,这个函数接收 dispatch 方法作为参数,并返回一个带有回调函数的函数。这个回调函数在异步操作完成后被调用,然后再通过 dispatch 方法触发一个 action,更新 Redux store 中的数据。redux-thunk 适合用于处理简单的异步逻辑,比如发送 AJAX 请求或者获取本地存储数据等。 没有拓展api


redux-saga 则采用了另外一种设计思路,它使用了 ES6 的 generator 函数来实现异步逻辑的管理。在 redux-saga 中,使用 generator 函数定义一个 saga,它可以监听一个或多个 action,并在相应的 action 被触发后执行一些副作用,比如发送 AJAX 请求、触发其他 action 等。redux-saga 还提供了一些辅助函数和特性(api),比如 takeLatest、put、call 等,使得开发者可以更加方便地管理异步流程,处理错误和取消请求等。


49.react合成事件使用原因,原理

探索合成事件


React 合成事件(SyntheticEvent)是 React 模拟原生 DOM 事件所有能力的一个事件对象


使用原因:


进行浏览器兼容,实现更好的跨平台


React 采用的是顶层事件代理机制,能够保证冒泡一致性,可以跨浏览器执行。React 提供的合成事件用来抹平不同浏览器事件对象之间的差异,将不同平台事件模拟合成事件。


避免垃圾回收


事件对象可能会被频繁创建和回收,因此 React 引入事件池,在事件池中获取或释放事件对象。即 React 事件对象不会被释放掉,而是存放进一个数组中,当事件触发,就从这个数组中弹出,避免频繁地去创建和销毁(垃圾回收)。


方便事件统一管理和事务机制


原理:


事件绑定

在React17之前,React是把事件委托在document上的,React17及以后版本不再把事件委托在document上,而是委托在挂载的容器上

原生事件和合成事件两者其实是通过一个叫事件插件(EventPlugin)的模块产生关联的,每个插件只处理对应的合成事件,比如onClick事件对应SimpleEventPlugin插件,这样React在一开始会把这些插件加载进来,通过插件初始化一些全局对象,比如其中有一个对象是registrationNameDependencies,它定义了合成事件与原生事件的对应关系

事件触发

事件触发都会执行dispatchEvent函数,当触发事件时会对当前元素的所有父元素处理构造成合成对象,将合成事件一次存放入eventQueue中,遍历 eventQueue 模拟一遍捕获和冒泡阶段,然后通过runEventsInBatch方法依次触发调用每一项的监听事件

50.React事件代理机制

React 并不会把所有的处理函数直接绑定在真实的节点上。而是把所有的事件绑定到结构的最外层,使用一个统一的事件监听器,这个事件监听器上维持了一个映射来保存所有组件内部的事件监听和处理函数。


所有事件都是委托在id = root的DOM元素中(网上很多说是在document中,17版本不是了);

在应用中所有节点的事件监听其实都是在id = root的DOM元素中触发;

React自身实现了一套事件冒泡捕获机制;

React实现了合成事件SyntheticEvent;

React在17版本不再使用事件池了(网上很多说使用了对象池来管理合成事件对象的创建销毁,那是16版本及之前);

事件一旦在id = root的DOM元素中委托,其实是一直在触发的,只是没有绑定对应的回调函数;

51.React事件和原生事件的执行顺序

import React, { useRef, useEffect } from "react";
import "./styles.css";
const logFunc = (target, isSynthesizer, isCapture = false) => {
    const info = `${isSynthesizer ? "合成" : "原生"}事件,${
        isCapture ? "捕获" : "冒泡"}阶段,${target}元素执行了`;
    console.log(info);
};
const batchManageEvent = (targets, funcs, isRemove = false) => {
    targets.forEach((target, targetIndex) => {
        funcs[targetIndex].forEach((func, funcIndex) => {
            target[isRemove ? "removeEventListener" : "addEventListener"](
                "click",
                func,
                !funcIndex
            );
        });
    });
};
export default function App() {
    const divDom = useRef();
    const h1Dom = useRef();
    useEffect(() => {
        const docClickCapFunc = () => logFunc("document", false, true);
        const divClickCapFunc = () => logFunc("div", false, true);
        const h1ClickCapFunc = () => logFunc("h1", false, true);
        const docClickFunc = () => logFunc("document", false);
        const divClickFunc = () => logFunc("div", false);
        const h1ClickFunc = () => logFunc("h1", false);
        batchManageEvent(
            [document, divDom.current, h1Dom.current],
            [
                [docClickCapFunc, docClickFunc],
                [divClickCapFunc, divClickFunc],
                [h1ClickCapFunc, h1ClickFunc]
            ]
        );
        return () => {
            batchManageEvent(
                   [document, divDom.current, h1Dom.current],
                [
                    [docClickCapFunc, docClickFunc],
                    [divClickCapFunc, divClickFunc],
                    [h1ClickCapFunc, h1ClickFunc]
                ],
                true
            );
        };
    }, []);
    return (
        <div
          ref={divDom}
          className="App1"
          onClickCapture={() => logFunc("div", true, true)}
          onClick={() => logFunc("div", true)}
        >
          <h1
            ref={h1Dom}
            onClickCapture={() => logFunc("h1", true, true)}
            onClick={() => logFunc("h1", true)}
          >
            Hello CodeSandbox
          </h1>
        </div>
    );
}

React16

a405a22de5bd4a2981a0876d5ab88e8d.png


React17



8981982865ed4c12a10afbf626d0d0e4.png

总结


16版本先执行原生事件,当冒泡到document时,统一执行合成事件,

17版本在原生事件执行前先执行合成事件捕获阶段,原生事件执行完毕执行冒泡阶段的合成事件,通过根节点来管理所有的事件

52.jsx转换真实dom流程

使用React.createElement()或JSX编写React组件,实际上所有的JSX都会转换成React.createElement(),Babel完成转换过程

createElement函数对key和ref等特殊的props进行处理,生成vDom

ReactDOM.render将生成好的dom渲染到容器上,进行处理转化成真实dom

53.什么是JSX?

JSX即JavaScript XML。一种在React组件内部构建标签的类XML语法。JSX为react.js开发的一套语法糖,也是react.js的使用基础。React在不使用JSX的情况下一样可以工作,然而使用JSX可以提高组件的可读性,因此推荐使用JSX。


优点:


允许使用熟悉的语法来定义 HTML 元素树;

提供更加语义化且移动的标签;

程序结构更容易被直观化;

抽象了 React Element 的创建过程;

可以随时掌控 HTML 标签以及生成这些标签的代码;

是原生的 JavaScript。

目录
相关文章
|
12天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的校园水电费管理微信小程序的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的校园水电费管理微信小程序的详细设计和实现
35 0
|
26天前
|
前端开发 JavaScript 网络协议
前端最常见的JS面试题大全
【4月更文挑战第3天】前端最常见的JS面试题大全
47 5
|
12天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的优购电商小程序的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的优购电商小程序的详细设计和实现
34 0
|
5天前
|
前端开发 JavaScript 开发者
深入了解Webpack:前端模块打包工具
深入了解Webpack:前端模块打包工具
8 1
|
12天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的微信课堂助手小程序的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的微信课堂助手小程序的详细设计和实现
46 3
|
12天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的电子商城购物平台的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的电子商城购物平台的详细设计和实现
41 3
|
12天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的英语学习交流平台的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的英语学习交流平台的详细设计和实现
27 2
|
12天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的微信阅读网站小程序的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的微信阅读网站小程序的详细设计和实现
39 2
|
12天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的移动学习平台的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的移动学习平台的详细设计和实现
33 1
|
12天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的教师管理系统的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的教师管理系统的详细设计和实现
37 2