常考前端【小程序】36道面试题

小程序

1.小程序组件通信

  1. 父传子通过属性传值,子组件用properties接收。
  2. 子传父通过父对子组件绑定一个事件,子组件用this.triggerEvent("事件名",值)以入参的方式传回父。
  3. 父组件用this.selectComponent获取子组件实例,可以获取子组件的数据,使用子组件的方法。
  4. 全局传值,相当于事件总线把,在app.js中定义globalData,然后在任意组件就可以读值和设置值。
  5. url传值,只能传字符串,wx.navigateTo()带上query参数,页面在onLoad(options)中options可以获取参数。
  6. 页面之间要传递复杂的数据的话,wx.navigateTo()中配置events事件以及成功的success回调,目标页面就能通过getOpenerEventChannel获取。
  7. 本地存储数据,wx.setStorageSyncwx.getStorageSync

2.小程序setData

用来修改state中的数据,修改是同步的,但是页面渲染是异步的。

3.页面下拉刷新与上拉加载

可以在全局 config 中的 window 配置中设置 enablePullDownRefresh 属性,当然建议 enablePullDownRefresh 属性是在页面级配置中设置, 在 Page 中定义 onPullDownRefresh 事件函数 到达下拉刷新条件后,该事件函数执行,发起请求方法,下拉刷新请求返回后,调用 wx.stopPullDownRefresh 停止下拉刷新。 至于上拉加载则可以利用 onReachBottom 事件函数来确认后续的功能操作,一般它与 onReachBottomDistance 进行配合处理,确认距离底部的距离,而它的单位是px

4.bindtap和catchtap的区别是什么

  • 相同点:首先他们都是作为点击事件函数,就是点击时触发。在这个作用上他们是一样的,可以不做区分。
  • 不同点:他们的不同点主要是bindtap是不会阻止冒泡事件的,catchtap是阻值冒泡的。

5.小程序有哪些导航API,它们各自的应用场景与差异区别是什么

wx.navigateTo():保留当前页面,跳转到应用内的某个页面,但是不能跳到 tabbar 页面 wx.redirectTo():关闭当前页面,跳转到应用内的某个页面,但是不允许跳转到 tabbar 页面 wx.switchTab():跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面 wx.navigateBack():关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages() 获取当前的页面栈,决定需要返回几层 wx.reLaunch():关闭所有页面,打开到应用内的某个页面

6.小程序中如何使用第三方npm模块进行功能开发

  • npm init项目的初始化操作
  • npm install lodash --save等模块安装操作
  • 微信开发者工具->详情->本地设置->使用npm模块
  • 微信开发者工具->工具->构建npm

在设置里面使用npm模块,在工具里面构建npm

7.小程序的定位在开发环境的设置

  • 使用定位操作需要授权处理,需在app.json中设置permission节点信息,将useLocation授权添加上。
  • 开发环境使用定位需要在调试器中开启,在**Sensor(传感器)**中将Geolocation的enable设置为开启状态

8.小程序的地图应用可以使用什么方式处理

wx.getLocation获取位置的经纬度等信息,再使用wx.openLocation打开微信内置地图查看位置,但是能看到的东西很少,只有经纬度。

更详细的需要用第三方的API。

9.小程序如何实现分享功能

  • 在页面中如果不设置 onShareAppMessage 分享的事件回调函数,那么小程序右上角三个点的操作中不包含分享功能
  • 通过button按钮的open-type属性设置为share则将调用页面中的 onShareAppMessage 事件,可以通过事件的 res中的 from 内容来判断是按钮button的分享处理还是右上角三个点menu的页面分享操作

10.小程序是否支持双向数据绑定

  • 但仅限于简易双向数据绑定,绑定的对象只能是一个单一字段的绑定,不支持对象等形式的数据路径设置操作

  • 在自定义组件中也可以实现传递双向绑定操作

  • 它的基本语法操作是

    html
     体验AI代码助手
     代码解读
    复制代码
    <input model:value="{{value}}" />
    

11.授权验证登录怎么做,用户退出后下次进入还需要再次授权吗

  • wx.login获取到一个code,拿这code去请求后台得到openId, sessionKey, unionId。
  • 调wx.getUserInfo获取用户信息内容
  • 一次性授权:每次授权都需要与后台进行权限认证操作
  • 永久授权:调取授权登录接口并把获取到的用户公开信息存入数据库

12.授权验证是怎么做的

  • 按钮触发的,open-type指定为getUserInfo类型,可以从bindgetuserinfo回调中获取到用户信息
  • 授权验证操作只执行一次,不会二次执行
  • 授权以后可以通过wx.getUserInfo获取基础的用户信息

13.微信小程序之用户授权

用户授权包括很多内容:用户信息、地理位置、后台定位、录音功能、保存到相册、摄像头等

授权操作主要分两种不同的情况

  • 弹出授权框用户点击允许,授权信息会直接记录,后续不再确认授权操作

  • 弹出授权框用户点击拒绝,授权信息会直接记录,但用户还想再次操作对应功能,需要弹窗再次授权

    1. 查看所拥有权限
    2. wx.authorize 发起请求用户授权,利用 wx.showModal 弹窗授权确认
    3. wx.showModal 确认后利用 wx.openSetting 打开授权设置
    4. 确认授权设置打开授权信息

14.使用webview直接加载要注意哪些事项?

  • 个人类型的小程序暂不支持使用
  • H5地址需要在小程序后台添加H5域名白名单,如果webview里面还有跳转到其他的H5页面,也是需要添加域名白名单的
  • 在网页内可通过window.__wxjs_environment变量判断是否在小程序环境
  • webview内可以通过桥接方式进行监听,监听事件onPageStateChange可以确认小程序是否在前台
  • 每个页面只能有一个 web-view,web-view 会自动铺满整个页面,并覆盖其他组件
  • 避免在链接中带有中文字符,在 iOS 中会有打开白屏的问题,建议加一下 encodeURIComponent
  • web-view 网页与小程序之间不支持除 JSSDK 提供的接口之外的通信

15.webview中的页面怎么跳转回小程序

wx.miniProgram.navigateTo()

16.怎么获取手机号

个人不行,要完成认证的小程序,先去微信平台认证。

  1. 利用wx.login获取登录凭证code,通过code与开发者服务器交互获取加密后的openId,并将openId与session_key进行服务器数据库信息存储
  2. 对openId进行加密传递回小程序端
  3. 利用button进行open-type的类型设置,值为getPhoneNumber,并且需要进行bindgetphonenumber事件绑定
  4. 绑定回调getPhoneNumber中可以找到手机加密数据
  5. 将加密的openId、encryptedData、iv等数据发送至服务器端
  6. 服务器端解密
  7. 可以将解密信息等内容进行返回小程序端处理

17.生命周期

应用级别的

  1. onLaunch 小程序初始化
  2. onShow 小程序启动
  3. onHide 小程序切后台

页面级别的

  1. onLoad 页面加载
  2. onShow 页面显示
  3. onReady 页面渲染完成
  4. onHide 页面隐藏
  5. onUnload 页面卸载

组件级别的

  1. created 组件实例创建
  2. attached 组件被添加到页面节点树种
  3. ready 组件初次渲染完成
  4. moved 组件被移动到新的节点
  5. detached 组件被页面节点树种移除

React

1.React中的事件机制

JSX 上写的事件并没有绑定在对应的真实 DOM 上,而是通过事件代理的方式,将所有的事件都统一绑定在了组件树的根上。这样的方式不仅减少了内存消耗,还能在组件挂载销毁时统一订阅和移除事件

2.React 高阶组件、Render props、hooks 有什么区别,为什么要不断迭代

  • HOC

    简单来说就是一个函数接受一个组件,这个函数中又定义了一个组件,在这个新定义的组件中可以写一些属性方法等公共代码,然后把这些属性方法传给接受的组件,最后return新定义的组件。

    // hoc的定义
    function withSubscription(WrappedComponent, selectData) {
      return class extends React.Component {
        constructor(props) {
          super(props);
          this.state = {
            data: selectData(DataSource, props)
          };
        }
        // 一些通用的逻辑处理
        render() {
          // ... 并使用新数据渲染被包装的组件!
          return <WrappedComponent data={this.state.data} {...this.props} />;
        }
      };
    
    // 使用
    const BlogPostWithSubscription = withSubscription(BlogPost,
      (DataSource, props) => DataSource.getBlogPost(props.id));
    
  • Render props

    父组件向Render props组件传递了一个render函数,这个render函数定义了渲染内容;Render props组件通过this.props.render获取函数然后写入参数。

    // DataProvider组件内部的渲染逻辑如下
    class DataProvider extends React.Components {
         state = {
        name: 'Tom'
      }
    
        render() {
        return (
            <div>
              <p>共享数据组件自己内部的渲染逻辑</p>
              { this.props.render(this.state) }
          </div>
        );
      }
    }
    
    // 调用方式
    <DataProvider render={data => (
      <h1>Hello {data.name}</h1>
    )}/>
    
  • Hooks

    可以在里面用其他的hook,直接return出数据,使用的时候useXxxx就行了,非常方便。

3.对React-Fiber的理解,它解决了什么问题?

  1. 可中断的渲染

    以前react更新过程是同步的,一旦开始就会进行到底,这可能导致主线程被占据,页面卡顿做不了其他的事情。现在react把更新过程拆分成多个小任务,这样渲染的过程就能被其他优先级更高的任务插队,从而更好的响应用户交互。

  2. 优先级管理

    比如用户输入,图表更新,在浏览器原生的任务调度机制下,用户的输入可能会被图表更新所堵塞,但是在Fiber的操作下,用户的输入是优先于图表更新的。 React Fiber 如何在任务调度方面比浏览器提供更细粒度的控制。

  3. 并发渲染

    在传统架构中,React 是单线程的,无法充分利用多核 CPU 的优势。Fiber 使得 React 能够更好地利用现代多核处理器,提高渲染性能。

4.React.PureComponent

在函数组件里面就是React.memo,react会自动浅比较一下传入props,变化了就更新组件。

5.类组件中的生命周期

  • 挂载阶段

    1. constructor

      用于初始化 state 和绑定方法。

      constructor(props) {
       super(props);
       this.state = { count: 0 };
      }
      
    2. static getDerivedStateFromProps

      根据 props 更新 state。是一个静态方法。返回值会合并到state中

      static getDerivedStateFromProps(nextProps, prevState) {
       if (nextProps.someValue !== prevState.someValue) {
         return { someValue: nextProps.someValue };
       }
       return null;
      }
      
    3. render

      返回要渲染的元素。

      render() {
       return <div>{this.state.count}</div>;
      }
      
    4. componentDidMount

      组件挂载后调用,可以进行 DOM 操作或数据请求。

      componentDidMount() {
       // 进行数据请求或订阅操作
      }
      
  • 更新阶段

    1. getDerivedStateFromProps

      在每次组件更新时调用(见上)。

    2. shouldComponentUpdate

      决定组件是否应该重新渲染。返回 truefalse

      shouldComponentUpdate(nextProps, nextState) {
       return nextState.count !== this.state.count;
      }
      
    3. render

      重新渲染

    4. getSnapshotBeforeUpdate

      在更新前获取一些信息(例如,DOM 状态)。

      getSnapshotBeforeUpdate(prevProps, prevState) {
       if (prevProps.someValue !== this.props.someValue) {
         return { scrollPosition: window.scrollY };
       }
       return null;
      }
      
    5. componentDidUpdate

      组件更新后调用,可以进行 DOM 操作或数据请求。

      componentDidUpdate(prevProps, prevState, snapshot) {
       if (snapshot !== null) {
         window.scrollTo(0, snapshot.scrollPosition);
       }
      }
      
  • 卸载阶段

    componentWillUnmount

    componentWillUnmount() {
     // 进行清理工作,如取消订阅
    }
    

6.哪些方法会触发 React 重新渲染?重新渲染会做些什么?

  1. State 变化

    • 当组件的 state 发生变化时,会触发重新渲染。
    • 使用 this.setState 方法更新 state 会导致重新渲染。
  2. Props 变化

    • 当父组件传递给子组件的 props 发生变化时,子组件会重新渲染。
    • 这也是为什么组件应该是纯函数的一个原因,即它们的输出应该完全由输入(props 和 state)决定。
  3. 强制更新

    • 使用 this.forceUpdate 方法可以强制组件重新渲染。通常不推荐使用这个方法,因为它绕过了 React 的优化机制。
  4. Context 变化

    • 当使用 React Context 时,如果 Context 的值发生变化,所有使用该 Context 的组件都会重新渲染。
  5. 父组件变化

    • 父组件的渲染会带着子组件一起重新渲染一遍。

重新渲染就会用diff算法来对新旧VNode进行比对。

7.React如何判断什么时候重新渲染组件?

写了componentShouldUpdate就根据返回值来判断,true就重新渲染,false就放弃这次渲染。

8.无状态组件

就是没有自己的状态,就负责展示,可以传递props给它展示。

9.如何获取DOM元素

  1. 在类组件中使用 ref

    在类组件中,你可以通过创建一个 ref 并将其附加到一个 DOM 元素上来获取对该元素的引用。

    import React, { Component } from 'react';
    
    class MyComponent extends Component {
      constructor(props) {
        super(props);
        // 创建一个 ref
        this.myRef = React.createRef();
      }
    
      componentDidMount() {
        // 访问 DOM 元素
        console.log(this.myRef.current);
      }
    
      render() {
        return <div ref={this.myRef}>Hello, World!</div>;
      }
    }
    
    export default MyComponent;
    

10.React中可以在render访问refs吗?为什么?

不能,要在render之后。

11.对React的插槽(Portals)的理解,如何使用,有哪些使用场景

Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。

React 提供了 ReactDOM.createPortal 方法来创建一个 Portal。这个方法接收两个参数:

  1. 子组件(或元素)
  2. DOM 节点
import React from 'react';
import ReactDOM from 'react-dom';

class Modal extends React.Component {
  render() {
    return ReactDOM.createPortal(
      <div className="modal">
        {this.props.children}
      </div>,
      document.getElementById('modal-root')
    );
  }
}

// 在你的 HTML 文件中,需要有一个 id 为 'modal-root' 的元素,就是入口文件
// <div id="modal-root"></div>

class App extends React.Component {
  render() {
    return (
      <div className="app">
        <h1>My App</h1>
        <Modal>
          <p>This is a modal!</p>
        </Modal>
      </div>
    );
  }
}

export default App;

应用:弹窗类用的最多。

12.在React中如何避免不必要的render?

  1. shouldComponentUpdate
  2. PureComponent
  3. React.memo

13.对 React context 的理解

用来组件通信的

import React from 'react';

// 创建一个 Context 对象,并设置默认值
const ThemeContext = React.createContext('light');

export default ThemeContext;
 //父组件
 <ThemeContext.Provider value={this.state.theme}>
        <div>
          <button onClick={this.toggleTheme}>Toggle Theme</button>
          <ThemedComponent />
        </div>
</ThemeContext.Provider>
//子组件
  <ThemeContext.Consumer>
    {theme => (
      <div style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
        <h2>Themed Component</h2>
        <p>The current theme is {theme}</p>
      </div>
    )}
  </ThemeContext.Consumer>

14.React中什么是受控组件和非控组件

指那些表单数据由React组件状态state控制的表单元素,表单元素的值和组件的状态同步。

15.React中refs的作用是什么?有哪些应用场景?

直接获取DOM元素或者React组件实例,但是函数组件没有实例,直接获取的话会报错,所以要用到forwardRef

const A = forwardRef((props,ref)=>{
    //接收两个参数
   	//	props表示传参
    //	转发ref给其他能获取到实例的元素
    return  <input ref={ref} type="text" />
})
import React, { useRef, useEffect, forwardRef } from 'react';

// 定义一个函数组件并使用 React.forwardRef 转发 ref
const FunctionComponent = forwardRef((props, ref) => (
  <input ref={ref} type="text" />
));
//------------------------------------------------------------------
const ParentComponent = () => {
  const inputRef = useRef(null);

  useEffect(() => {
    // 组件挂载后,让输入框自动获得焦点
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }, []);

  return <FunctionComponent ref={inputRef} />;
};

export default ParentComponent;

16.React中除了在构造函数中绑定this,还有别的方式吗?

一起列举出来

  1. 在构造函数中绑定 this

    class MyComponent extends React.Component {
      constructor(props) {
        super(props);
        this.state = { count: 0 };
        this.handleClick = this.handleClick.bind(this);
      }
    
      handleClick() {
        this.setState({ count: this.state.count + 1 });
      }
    
      render() {
        return <button onClick={this.handleClick}>Click me</button>;
      }
    }
    
  2. 使用箭头函数定义类方法(类字段语法)

    class MyComponent extends React.Component {
      state = { count: 0 };
    
      handleClick = () => {
        this.setState({ count: this.state.count + 1 });
      };
    
      render() {
        return <button onClick={this.handleClick}>Click me</button>;
      }
    }
    
  3. 在jsx中使用箭头函数

    class MyComponent extends React.Component {
      state = { count: 0 };
    
      handleClick() {
        this.setState({ count: this.state.count + 1 });
      }
    
      render() {
        return <button onClick={()=>this.handleClick()}>Click me</button>;
      }
    }
    
  4. 在jsx中用bind,this.handleClick.bind(this);

17.React组件的构造函数有什么作用?它是必须的吗?

构造函数的主要作用:初始化状态和绑定事件处理器中的 this

所以如果props不涉及初始化,又不需要处理this的问题的话,那就可以不用构造函数。

class LikeButton extends React.Component {
  constructor() {
    super();
    this.state = {
      liked: false
    };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    this.setState({liked: !this.state.liked});
  }
  render() {
    const text = this.state.liked ? 'liked' : 'haven't liked';
    return (
      <div onClick={this.handleClick}>
        You {text} this. Click to toggle.
      </div>
    );
  }
}
ReactDOM.render(
  <LikeButton />,
  document.getElementById('example')
);

18.setState的用法和原理

推荐使用函数式的,可以获取到正确的状态。

this.setState((prevState, props) => ({
 ...
}),callback);
              
//第二个参数是回调函数,state完成更新后执行

useState()也是的,如果依赖旧状态,也建议写函数式的

import React, { useState } from 'react';

function Counter() {
  // 声明一个叫做 "count" 的状态变量,并赋初始值为 0
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount((prevCount )=>prevCount +1)}>
        Click me
      </button>
    </div>
  );
}
  1. 状态更新请求: 当 setState 被调用时,React 接收到一个状态更新请求,这个请求可以是一个对象或者一个函数。
  2. 合并状态: React 将新的状态与当前状态合并(浅合并),但不会立即更新 this.state。相反,React 会将状态更新放入一个队列中,稍后处理。
  3. 批量更新: 为了提高性能,React 会批量处理多个 setState 调用。在事件处理、生命周期方法或合成事件中,状态更新不会立即生效,而是会被暂存并在稍后的一个批处理中一起处理。但是如果在一些原生事件中,setState可能是同步的,因为合成事件不会处理。
  4. 重新渲染: 批处理完成后,React 会重新计算组件的状态和属性,并触发组件的重新渲染。React 会调用 render 方法生成新的虚拟 DOM,然后通过与旧的虚拟 DOM 进行对比(diffing),找到需要更新的部分,并进行实际的 DOM 更新。

19.React中defaultProps

在 React 16.3 及更高版本中,推荐使用 defaultProps 静态属性来定义默认属性值:

  • 类组件

    class MyComponent extends React.Component {
      static defaultProps = {
        name: 'Default Name'
      };
    
      render() {
        return <div>{this.props.name}</div>;
      }
    }
    
    // 使用 defaultProps 的效果与 getDefaultProps 相同
    <MyComponent /> // 渲染结果:<div>Default Name</div>
    
  • 函数组件

    直接使用默认入参就行了

20.React中的setState和replaceState的区别是什么?

replaceState已经被废弃了,不了解。

21.React性能优化在哪个生命周期?它优化的原理是什么?

shouldComponentUpdate(nextProps,nextState),可以决定组件是否更新。

22.state和 props 触发更新的生命周期分别有什么区别?

getDerivedStateFromProps仅在 props 变化时调用。

23.React-Router的实现原理是什么?

客户端路由实现的思想:

  • 基于 hash 的路由:通过监听hashchange事件,感知 hash 的变化
    • 改变 hash 可以直接通过 location.hash=xxx
  • 基于 H5 history 路由:
    • 改变 url 可以通过 history.pushState 和 resplaceState 等,会将URL压入堆栈,同时能够应用 history.go() 等 API
    • 监听 url 的变化可以通过自定义事件触发实现

24.React-Router怎么设置重定向?

使用 <Navigate> 组件

25.React-Router如何获取URL的参数?

  1. 路径参数

         // 定义路由
         <Route path="/user/:id" component={UserComponent} />
    
         // 获取参数
         const { id } = useParams();
    
  2. 查询参数

         // 定义路由
         <Route path="/search" component={SearchComponent} />
    
         // 获取查询参数
         const [searchParams] = useSearchParams();
         const query = searchParams.get('query');
    
  3. state参数

    const navigate = useNavigate(); 
    navigate('/user', { state: { id: 123, name: 'John Doe' } }); // 导航并通过 state 传递参数
    
    ---------------------------
        
    const location = useLocation();
    const userState = location.state; // 获取通过 state 传递的参数
    
  4. 其他的钩子

    import React from 'react';
    import { useMatch } from 'react-router-dom';
    
    const About = () => {
      const match = useMatch('/about');
    
      return (
        <div>
          <h1>About Page</h1>
          {match && <p>This is the About page</p>}
        </div>
      );
    };
    
    export default About;
    

26.useLayoutEffect

用法和useEffect一样,普通的useEffect会再浏览器完成绘制之后执行,但是useLayoutEffect会在DOM变更后就执行(浏览器绘制之前) ,这有什么卵用呢?

比如说要写一个tooltip,当上方的空间不够了,那就应该尝试出现在下方,所以要在浏览器完成绘制之前也就是DOM一旦变更就可以去测量上方的空间够不够了,上方空间不够用立马就更改他的位置,让浏览器再次渲染一遍。

27.React 数据持久化有什么实践吗?

redux-persist

28.Redux原理

在React中,组件通过connect连接到 Redux ,如果要访问 Redux,需要dispatch一个包含 type和负载(payload) 的 Action。Action 中的 payload 是可选的,Action 将其转发给 Reducer。

当Reducer收到Action时,通过 switch…case 语法比较 Action 中type。 匹配时,更新对应的内容返回新的 state。

当Redux状态更改时,连接到Redux的组件将接收新的状态作为props。当组件接收到这些props时,它将进入更新阶段并重新渲染 UI。

import React from 'react';
import { connect } from 'react-redux';
import { increment, decrement } from './actions';

function App({ count, increment, decrement }) {
  return (
    <div>
      <button onClick={increment}> + </button>
      <p>{count}</p>
      <button onClick={decrement}> - </button>
    </div>
  )
}

//把store中的数据映射到组件的props中
const mapStateToProps = state => {
  return {
    count: state.count,
  }
}

//把actions映射到组件的props中
const mapDispatchToProps = {
  increment,
  decrement,
}

// 使用 connect 函数连接组件和 Redux store
export default connect(mapStateToProps, mapDispatchToProps)(App)

29.reducer

reducer 函数接收当前状态 state 和一个动作 action,然后根据 action.type 返回新的状态。

函数组件中使用useReducer

import React, { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>
    </div>
  );
}

export default Counter;

30.useTransition

延迟的更新:使用 useTransition 标记的状态更新会被延迟,直到所有高优先级的更新完成。

可中断的更新:过渡更新可以被高优先级的更新中断,以保持应用的响应性。

指示过渡状态:可以获取过渡是否进行中的状态,以便在 UI 中展示加载指示器等。

import React, { useState, useTransition } from 'react';

function App() {
  const [isPending, startTransition] = useTransition();
  const [count, setCount] = useState(0);

  const handleClick = () => {
    startTransition(() => {
      setCount(count + 1);
    });
  };

  return (
    <div>
      <button onClick={handleClick}>Increment</button>
      {isPending ? <p>Loading...</p> : <p>Count: {count}</p>}
    </div>
  );
}

export default App;

31.useDeferredValue

创造一个延迟更新的值,第一次返回旧值,当其他优先级高的任务执行完了再创造一个新值。

32.React.lazy()和Suspense

用于路由懒加载

import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { Suspense, lazy } from 'react';
const Home = lazy(() => import('./routes/Home'))
const count = lazy(() => import('./routes/count'))
 

export default class App extends Component {
  render() {
    return (
      <div>
          <Router>
            <Suspense fallback={<h1>Loading...</h1>}>
              <Switch>
                <Route exact path="/" component={Home}/>
                <Route path="/count" component={count}/>
                ...
              </Switch>
            </Suspense>
          </Router>
      </div>
    )
  }
}

33.JSX是怎么变成真实的DOM的

JSX 解析和编译:JSX 通过 Babel 编译成 React.createElement 调用。

创建虚拟 DOMReact.createElement 创建虚拟 DOM 对象。

构建虚拟 DOM 树:React 通过虚拟 DOM 对象构建虚拟 DOM 树。

比较和更新虚拟 DOM 树:React 比较新旧虚拟 DOM 树,找出差异。

更新真实 DOM:React 将差异转换为实际的 DOM 操作,更新真实 DOM。

渲染真实 DOM:React 将虚拟 DOM 树中的元素渲染成真实的 DOM 元素,并插入页面。

34.为什么 Hooks 不能在条件判断中使用?

React 依赖 Hook 调用顺序来正确关联状态和副作用

35.如何通过 DOM 元素找到对应的 Fiber 对象

具体来说,React 在内部创建 DOM 元素时,会在 DOM 元素上附加一个 __reactFiber$ 前缀的属性,该属性的值是 fiber 对象。

36.React源码

36-1.JSX转换成ReactElement的过程

调用了createElement方法

/**
* 创建React Element
* type  元素类型
* config  配置属性
* children  子元素
* type  元素类型
*	1.分离props属性和特殊属性
*	2.将子元素挂在到props.children中
*	3.为props属性赋默认值
*	4.创建并返回ReactElement
*/
function createElement(type,config,children){
    ...
}

如何判断一个参数是ReactElement?

看对象的$$typeof属性

object.$$typeof === REACT_ELEMENT_TYPE
36-2.React架构
  • Scheduler(调度层):调度任务的优先级,高优先级的先进入协调器。
  • Reconciler(协调层):构建Fiber数据结构,对比Fiber对象找出差异,记录Fiber对象要进行的DOM操作。
  • Renderer(渲染层):将变化的部分渲染到页面上。

调度层和协调层的工作是在内存当中进行的,渲染层设定的就是不可以打断的,所以不会出现DOM渲染不完整的问题。

36-3. Fiber数据结构
type Fiber= {
    /************DOM实例相关************/
    
    //表示Fiber节点的类型 用于协调和渲染的时候做判断的
	tag:WorkTag,
    
    //React元素的类型 DOM元素就是字符串("div","span") React元素就是组件的构造函数或者类
    type:any,
    
    //与Fiber节点关联的实例对象,对于DOM元素来说是实际的DOM节点,对于类组件来说是组件实例
    stateNode: any,
    
    /************构建Fiber树相关的************/
    
    //父Fiber节点,表示树结构中的父节点
  	return: Fiber | null,

  	//子Fiber节点,表示树结构中的第一个子节点。
  	child: Fiber | null,

  	//兄弟Fiber节点,表示树结构中当前节点的下一个兄弟节点。
  	sibling: Fiber | null,
    
    //有了这些数据,无论出于Fiber树种哪个位置,都能快速的找到父级 子级 兄弟级节点了
    
    //指的是 workInProgress Fiber节点
    alternate: Fiber | null,
    
    /************状态数据相关的************/
    
    //将用于渲染的新属性
    pendingProps: any,

  	//上一次渲染时使用的属性
  	memoizedProps: any,

  	//上一次渲染时使用的状态
 	 memoizedState: any,
    
     /************副作用相关************/
    
    //该Fiber对应的组件所产生的状态更新都会放在这个队列里
    updateQueue:updateQueue<any> | null,
    
    //用来记录当前Fiber要执行的DOM操作,比如说当前对应的DOM节点要做插入 删除等操作
    effectTag:sudeEffectTag,
    
    //指向当前Fiber节点的第一个副作用Fiber节点。
    firstEffect:Fiber | null,
    
    //指向当前Fiber节点的下一个副作用Fiber节点。
    nextEffect:Fiber | null,
    
    //指向当前Fiber节点的最后一个副作用Fiber节点。
    lastEffect:Fiber | null,
    
    //通过设置不同的expirationTime,React可以控制各个更新任务的执行顺序,确保用户交互等高优先级任务能及时响应。
    expirationTime:ExpirationTime,
    
    //表示当前Fiber节点的渲染模式  并发渲染,同步渲染,不使用特定的渲染模式等等
    mode:TypeOfMode,  
}
36-4.双缓存技术

React最多同时存在两颗Fiber树,屏幕中看到的讲座current Fiber树,当发生更新的时候,React会在内存里面重新构建一颗workInProgress Fiber树。当workInProgress Fiber树构建好了直接替换current Fiber树,更新就更流畅了。

36-5.FiberRoot和RootFiber
  • FiberRoot

    是React应用的顶层结构,包含了整个应用的渲染状态和调度信息。它主要负责管理整个应用的渲染和更新过程。

  • RootFiber

    是整个React应用的顶层Fiber节点,是FiberRoot的一个属性。rootFiber表示当前渲染的Fiber树的根节点,是从这个节点开始递归遍历整个Fiber树。

36-6.整个过程
  1. render方法的调用其实是returnlegacyRenderSubtreeIntoContainer的结果。
  2. legacyRenderSubtreeIntoContainer是为container<div id = 'app'>)创建或者获取FiberRoot,然后开始下一步调用updateContainer
  3. 更新过程就是调用了updateContainerupdateContainer会创建任务(初始化渲染或者更新渲染)放在任务队列里面,等待浏览器空闲执行。
  4. 任务执行调用scheduleUpdateOnFiber,这会进入渲染阶段,渲染阶段包括了协调和提交两个子阶段。
  5. 在协调阶段会对比新旧Fiber树的差异,React会建立一个Effect List,用于在提交阶段执行所有的副作用。每个Fiber节点都会有一个effectTag,用于标记要执行的操作,例如更新、插入、删除等等。
  6. 提交阶段又分了三个小阶段,Before Mutation、Mutation和Layout。
  7. Before Mutation会处理DOM更新前要处理的副作用,例如getSnapshotBeforeUpdate
  8. Mutation就处理DOM节点的添加更新等。
  9. Layout会执行所有需要再DOM变更之后处理的副作用例如 componentDidMountcomponentDidUpdate
  10. DOM的更新。
  11. 在所有的更新完成之后,React会执行需要清理的任务,例如 useEffect 中的回调函数。

在这里插入图片描述

《Java 后端开发面试指南》题来自 2025 阿里、字节真题,问的是 MySQL 9.0 JSON 索引、Redis 8.0 多线程性能、网关动态路由、分布式事务选型。这些要的是原理理解、场景适配、量化思维 ——2025 年不缺 “会写接口的人”,缺 “写优、做稳、解决透” 的人,别用 “能跑通” 定义自己。

总结

专注收集整理Java面试题,不定期给大伙分享面试中的高频题与大厂难题。
如果你觉得文章对你有用,可以点赞和关注哦!❤
欢迎大家在评论区留下你宝贵的建议📢

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值