react面试题

1.说说你对react的理解?有哪些特性?

理解?

  1. React,用于构建用户界面的 JavaScript 库,提供了 UI 层面的解决方案

  2. 遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效

  3. 使用虚拟DOM来有效地操作DOM,遵循从高阶组件到低阶组件的单向数据流

  4. 帮助我们将界面成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,构成整体页面

  5. react 类组件使用一个名为 render() 的方法或者函数组件return,接收输入的数据并返回需要展示的内容特性

        

React特性有很多,如:

  1. 单向数据绑定

  2. JSX语法

  3. 虚拟DOM

  4. 声明式编程

  5. Component

2. 说说你对useEffect的理解,可以模拟哪些生命周期?

useEffect是React Hooks中的一个常用钩子函数,用于在函数组件中执行副作用操作(如访问 API、操作 DOM 等)。

useEffect几乎可以模拟类组件中的所有生命周期方法,主要有以下几个参数:

  • 第一个参数 effect: EffectCallback:必须是一个函数,表示需要执行的副作用操作,可以包含其他钩子和状态更新。

  • 第二个参数 deps?: DependencyList:一个可选数组,在数组中指定的变量发生变化时才会执行 effect。如果此参数未传递,则每次组件更新时都会执行 effect

使用不同的参数可以模拟不同的生命周期方法:

  • 模拟 componentDidMount 生命周期方法:将 useEffect 的第二个参数设置为空数组或者不传递参数。

  • 模拟 componentDidUpdate 生命周期方法:传递一个数组作为第二个参数,包含需要监听的状态变量,当这些状态变量更新时才会执行 effect

  • 模拟 componentWillUnmount 生命周期方法:在 effect 函数中返回一个 cleanup 函数,当组件销毁时会执行该函数。

2. 说说Real DOM和Virtual DOM的区别?优缺点?

Real DOM 是指浏览器原生的文档对象模型,通过操作 Real DOM 可以实现网页的动态变化。而 Virtual DOM 是 React 提出的一种理念,是用 JavaScript 对象模拟浏览器原生的 DOM 对象。

区别:

  • Real DOM 操作效率较低:Real DOM 在更新大量节点时会消耗大量的资源,因为在修改节点后需要重新计算节点的布局和渲染,甚至有可能导致浏览器卡顿或崩溃。而 Virtual DOM 通过对比前后的差异来实现节点的更新,能够显著提高性能。

  • Virtual DOM 操作效率较高:在 React 中,将组件状态的变化所导致的页面变化先以 Virtual DOM 的形式计算出来,然后再以最小代价和最小的操作量来更新 Real DOM。通过这样的方式,一方面可以避免不必要的 Real DOM 计算,另一方面也可以避免出现由于更新不及时而导致的页面异常。

优缺点:

  • Real DOM 优点:直观、易于理解、可以直接操作 DOM。

  • Real DOM 缺点:性能较低、操作频繁会造成浏览器卡顿、需要手动处理不同浏览器兼容性。

  • Virtual DOM 优点:性能较高、可跨平台、自动处理兼容性。

  • Virtual DOM 缺点:需要额外的处理成本、不直观。

3. 说说React生命周期有哪些不同的阶段?每个阶段对应的方法是?

React 的生命周期方法可以分为三个不同的阶段:

  • mount(挂载):当组件被插入到 DOM 树中时执行,包括以下方法:

    • constructor(): 初始化组件状态和绑定事件,不推荐使用,推荐使用Hooks中的useState和useEffect直接管理状态。

    • static getDerivedStateFromProps(props, state): 静态方法,在组件实例化时和更新时都会执行。可通过新的 props 计算 state,返回一个对象更新 state,或者返回null不更新state。

    • render(): 渲染组件结构,可以使用JSX语法或者React.createElement方法来创建虚拟DOM。

    • componentDidMount(): 组件挂载

  • update(更新):当组件被更新时执行,包括以下方法:

    • static getDerivedStateFromProps(props, state): 静态方法,在组件实例化时和更新时都会执行。可通过新的 props 计算 state,返回一个对象更新 state,或者返回null不更新state。

    • shouldComponentUpdate(nextProps, nextState): 当组件的 props 或 state 发生变化时,是否需要重新渲染,返回false即阻止重新渲染。

    • render(): 渲染组件结构

    • getSnapshotBeforeUpdate(): 必须和 componentDidUpdate 配合使用,在组件的更新快照(如滚动位置)被拍摄之前被调用,返回一个值,并将其传递给 componentDidUpdate。

    • componentDidUpdate(prevProps, prevState, snapshot): 组件更新

  • unmount(卸载):当组件被从 DOM 中移除时执行,包括以下方法:

    • componentWillUnmount(): 组件卸载

4.说说React中setState执行机制?

  1.  一个组件的显示形态可以由数据状态和外部参数所决定,而数据状态就是state, 当需要修改里面的值的状态需要通过调用setState来改变,从而达到更新组件内部数据的作用

  2. setState第一个参数可以是一个对象,或者是一个函数,而第二个参数是一个回调函数,用于可以实时的获取到更新之后的数据

  3. 在使用setState更新数据的时候,setState的更新类型分成:同步更新,异步更新

  4. 在组件生命周期或React合成事件中,setState是异步

  5. 在setTimeout或者原生dom事件中,setState是同步

  6. 对同一个值进行多次 setState, setState 的批量更新策略会对其进行覆盖,取最后一次的执行结果

 

5. 说说React中setState和replaceState的区别?

React中的 setStatereplaceState 用于更新组件的状态,两者的区别如下:

  • setState(newState): setState 用于更新组件的状态,它不会完全替代原有的状态,而是会混合(即合并或覆盖)新和旧状态,因此使用 setState 更新状态时可以仅部分更新状态,而不会丢失其他状态。

  • replaceState(newState): replaceState 用于完全替代组件的状态,它会彻底替换原有的状态。

在大多数情况下,应该优先使用 setState 方法来更新组件状态,只有在必须完全替换状态时才使用 replaceState 方法。

6. 说说react中onClick绑定后的工作原理?

在 React 中,为元素绑定 onClick 事件,会将事件处理器函数注册到相应的元素上。当用户点击该元素时,事件处理器函数会按照注册顺序依次被调用。

在 React 中,调用事件处理器函数时,它被执行在 React 的合成事件对象之下。合成事件对象是虚拟 DOM 浏览器事件接口之上的一层封装,可以跨浏览器平台运行,并提供更多的优化。

React通过事件委托的机制,将所有的事件绑定在外层容器上,然后通过判断当前事件的类型以及冒泡阶段中触发事件的元素(target)是否与目标元素(绑定事件的元素)相等来判断是否需要执行事件处理函数。

React 还会自动处理事件的跨浏览器兼容性问题。在事件处理函数中可以使用 event 参数来访问事件对象,来获取事件的信息,如事件类型、目标元素、鼠标位置等。同时,React 还可以通过调用 event.preventDefault()event.stopPropagation() 等来阻止默认行为和停止事件的传递。

7. React组件之间如何通信?

在 React 中,组件之间可以通过 props 属性进行通信。一个组件可以将数据通过 props 属性传递给另一个组件,后者可以根据 props 中的数据来渲染自身。组件之间可以通过嵌套、父子关系、兄弟组件等方式进行组合嵌套,形成一个可复用的组件库。

除了 props 属性以外,还可以通过 React 的 Context 特性、事件总线等机制实现组件之间的通信。Context 特性提供了一种在组件之间共享数据的方式,而事件总线则允许组件之间通过订阅发布事件来进行通信。

8. Redux的实现原理,写出其核心实现代码?

Redux 的主要思想是维护一个全局的状态树,并通过派发 action 的方式来更新状态。Redux 的核心实现代码包括以下几个部分:

  • Store:状态管理器,用于存储应用中的状态树。同时还需要提供一些方法,如 getStatedispatchsubscribe 等,分别用于获取当前状态、派发 action、订阅状态变更等操作。

  • Action:用于描述状态的变化,它是一个普通的 JavaScript 对象,包含一个 type 属性和一些 payload 数据。通过派发 action 来触发状态的更新。

  • Reducer:用于处理 action,并返回新的状态树。Reducer 是一个纯函数,接收两个参数:当前状态和 action,返回一个新的状态。它没有副作用,而是通过拷贝当前状态数据的方式来创建新的状态。

Redux 通过使用上述三个核心模块来实现统一管理状态,并采用单向数据流的方式管理应用程序状态,从而实现了组件之间的高效通信和复用。

9. 说说你对fiber架构的理解?解决了什么问题?

Fiber 架构是 React 的一种新的协调算法,用于异步处理渲染操作,可以提高渲染效率和用户交互体验。在 Fiber 架构中,React 将更新任务拆分为多个小任务,通过优先级调度算法来动态控制任务执行顺序,让用户在交互时可以获得更快的响应反馈。

Fiber 架构解决了 React 传统的渲染方式中存在的一些问题,如在长时间渲染时由于主线程被占用会导致页面卡顿等问题。Fiber 架构还为 React 提供了更好的并发控制能力,支持更多的 React 特性。

10. 说说react diff的原理是什么?

在 React 中,当组件状态发生变化时,React 会进行一次 virtual DOM 的差异比较(diff),并将变更的内容同步到 DOM 中。React diff 的原理是通过虚拟 DOM 节点的比较和更新来判断界面该如何更新。

React diff 算法的核心思想是通过对比虚拟 DOM 树的差异,来尽量减少对真实 DOM 的操作次数。在进行虚拟 DOM 树的比较时,React 采用了叶子节点优先、层级遍历的方式,将虚拟 DOM 树分成不同的层级,依次进行比较和更新。

React diff 算法的时间复杂度是 O(n),其中 n 是虚拟 DOM 树的节点数。React 通过优化算法,使得实际的 diff 过程时间复杂度更低,从而提升渲染效率。

11. 说说你对redux中间件的理解?常用的中间件有哪些?实现原理?

Redux 中间件是用于扩展 Redux 功能,处理和响应 action 的插件。中间件可以在 action 到达 Redux store 前或后执行另一个操作,例如日志记录、崩溃报告、异步操作等。常用的 Redux 中间件有:

  • Redux Thunk:可使 Redux 支持异步操作,将普通的 action creator 转化为返回函数的形式,使我们可以在 action creator 中进行异步逻辑编写,实现异步请求的流程控制。

  • Redux Saga:使用 generator 函数来代替 Redux Thunk 中的回调函数编写方式,可以更加清晰和简洁地编写异步操作流程。

  • Redux Logger:可用于记录 action 的发生和 state 的变化,便于开发调试。

  • Redux-persist:可用于持久化存储应用程序的状态。

Redux 中间件的实现原理是在 Redux store 的 dispatch 方法中对待派发的 action 进行拦截和处理。当 action 被派发时,它会先经过一个中间件链,然后再到达 store。在中间件链中,每个中间件都有机会对 action 进行修改、延迟或者拒绝处理,从而实现对 Redux 功能的扩展。

12.垂直外边距合并

垂直外边距合并是指两个块级元素相邻排列时,它们之间的垂直外边距会出现重叠的现象。

合并后的几种情况:

  • 正负值合并:两个元素的外边距都是正数或者都是负数时,它们会合并成一个较大的外边距,大小等于两者中的较大者减去较小者的差值;

  • 正负值相加:当两个元素的外边距一个是正数一个是负数时,它们的外边距会相加,但值的大小等于两者的绝对值之和;

  • 取最大值:当两个元素的外边距相遇时,会选择它们两者中的最大值作为合并后的外边距

 

 12.如何使用css实现一个三角形,写出两种以上方案得满分?

  1. 使用border来绘制

  例如:

<div></div>

<style>

         Div{

           Width:0;

          Height:0;

         Border-top:20px solid red;

         Border-bottom:20px solid white;

        Border-left:20px solid white;

       Border-right:20px solid white;

}

</style>

  1. clip-path裁剪多边形的方式

   <div></div>

<style>

          Div{

         Width:100px;

         Height:100px;

        Background:red;

Clip-path:polygon(0,0,0 100%,100% 100%);

}

</style>

 

 

13.什么是强缓存和协商缓存

强缓存策略和协商缓存策略在缓存命中时都会直接使用本地的缓存副本,区别在于协商缓存会向服务器发送一次请求。它们缓存不命中时,都会向服务器发送请求来获取资源。在实际的缓存机制中,强缓存和协商缓存是一起合作使用的,浏览器 会根据请求的信息判断,强缓存是否命中,如果命中则直接使用资源。如果不命中则根据头信息向服务器发起请求,使用协商缓存,如果协商缓存命中的话,则服务器不返回资源,浏览器直接使用本地资源的副本,如果协商缓存不命中,则浏览器返回最新的资源给浏览器。

 

14.useEffect的依赖为引用类型如何处理?

当useEffect的依赖为引用类型时,每次该引用类型的值改变时都会触发useEffect的回调函数,这样会造成性能问题。为了防止这种情况,我们可以将依赖项改为一个稳定不变的值,比如使用useMemo和useCallback进行优化,也可以使用第三方库比如use-deep-compare-effect等。

 

15.说说你对@reduxjs/toolkit的理解?和react-redux有什么区别

React-redux是官方react UI绑定层,允许React组件从redux存储中读取数据,并操作分派到存储以更新的状态。提供了connect,Provider等API,帮助我们连接react和redux,实现的逻辑会更加严谨高效。

@reduxjs/tookit是对redux的二次封装,开箱即用的一个高效的redux开发工具,使得创建store,更新store

区别:

  1. reduxjs/tookit相对于react-redux来说比较方便,集成了redux-devtools-extension,不需要额外的配置,非常方便
  2. Reduxjs/tookit集成immutable-js的功能,不需要安装配置使用,提高了开发效率
  3. Reduxjs/tookit集成了redux-thunk的功能
  4. Reduxjs/tookit讲types,actions,reducers放在一起组成了全新的slices,简化了我们的使用。

16.说说React jsx转换成真实DOM的过程

使用react.createElement或者jsx编写react组件,实际上所有的jsx代码最后都会转换成react.createElement(),label帮助我们完成了这个转换的过程。

CreateElement函数对key和ref等特殊的props进行处理,并获取defaultProps对默认props进行赋值,并且对传入的孩子节点进行处理,最终构造一个虚拟DOM对象。

ReactDOM.render将生成好的虚拟DOM 渲染到指定容器上,其中采用了批处理、事务等机制并对特定浏览器进行了性能优化,最终转换为真实DOM

 

17.createPortal

createPortal是react的一个API,可以将子组件渲染到父组件的任意位置,不受组件层级关系的限制。主要的使用场景包括:

  • 在父组件的dom结构外部创建一个新的容器,然后将子组件渲染到这个容器中,通常被用来实现模态框等弹出式组件;

  • 将子组件渲染到某个特定的dom节点中,比如在已有的站点中引入React应用的时候。

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

render函数里面可以编写jsx,转化成createElement这种形式,用于生成虚拟DOM,最终转化成真实DOM。

在react中,类组件只要执行了setState方法就一定会触发render函数执行,函数组件使用useState更改状态不一定导致重新render,组件的props改变了,不一定触发render函数的执行,但是如果props的值来自于父组件或者祖先组件的state,在这种情况下父组件或者祖先组件的state发送了改变,就会导致子组件的重新渲染,所以一旦执行了setState就会执行render()方法,useState会判断当前值有无发生改变确定是否执行render方法,一旦父组件发送渲染子组件也会发送渲染。

19.React性能优化的手段有哪些

  1. 使用纯组件
  2. 使用React.memo进行组件记忆(React.memo是一个高阶组件),对于相同的输入,不重复执行。
  3. 如果是类组件,使用shouldConponentUpdate生命周期事件,可以利用此事件来决定何时需要重新渲染组件。
  4. 路由懒加载
  5. 使用React Fragments避免额定标记
  6. 不要使用内联函数定义
  7. 避免在willxxx系列的生命周期中进行异步请求,操作DOM
  8. 如果是类组件,事件函数在constructor中绑定bind改变this指向
  9. 避免使用内联样式属性
  10. 优化React中的条件渲染。
  11. 不要再render方法中导出数据
  12. 列表渲染时加key
  13. 在函数组件中使用useCallback和useMemo来进行组件优化,依赖没有变化的话,不重复执行
  14. 类组件中使用immutable对象。

 

20.Provider和connect的底层原理实现,写出其核心代码?

Provider:

Provider 是 React-Redux 中提供的一个组件,它通过向后代组件中注入 store,使得组件树可以访问到 store 中的数据。 其核心代码如下所示:

class Provider extends Component {
  static propTypes = {
    store: PropTypes.shape({
      subscribe: PropTypes.func.isRequired,
      dispatch: PropTypes.func.isRequired,
      getState: PropTypes.func.isRequired
    }),
    context: PropTypes.object,
    children: PropTypes.any
  }
​
  static childContextTypes = {
    store: PropTypes.shape({
      subscribe: PropTypes.func.isRequired,
      dispatch: PropTypes.func.isRequired,
      getState: PropTypes.func.isRequired
    }).isRequired
  }
​
  getChildContext() {
    return { [storeKey]: this.store }
  }
​
  constructor(props, context) {
    super(props, context)
    this.store = props.store
  }
​
  render() {
    return Children.only(this.props.children)
  }
}

connect:

Connect 是 React-Redux 提供的一个高阶组件,用于将 React 组件与 Redux 进行连接,并将 store 中的数据作为 props 传递给组件。其核心代码如下所示:

function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {
  return function wrapWithConnect(WrappedComponent) {
    const { pure = true, areStatesEqual, areOwnPropsEqual, areStatePropsEqual, areMergedPropsEqual, ...extraOptions } = options
​
    class Connect extends Component {
      
      // 其他代码省略...
      
      render() {
        const { store } = this.context
        const state = store.getState()
        const stateProps = this.computeStateProps(store, this.props)
        const dispatchProps = this.computeDispatchProps(store, this.props)
        const mergedProps = this.computeMergedProps(stateProps, dispatchProps, this.props)
​
        return <WrappedComponent {...mergedProps} />
      }
    }
​
    return Connect
  }
}

在 Connect 组件中,会在 constructor() 方法中进行如下操作:

  • 获取 Redux store,通过 this.context 访问。

  • 注册当前组件订阅 store 更新,以确保每当 state 发生变化时都会更新当前组件。

  • 计算 stateProps 和 dispatchProps。

  • 计算 mergedProps 并以 props 的形式传递给 WrappedComponent。

最后,返回一个 Connect 组件,并将 WrappedComponent 包裹在其内部。这样,所有通过 connect() 连接的组件都会被 Connect 包裹以便于访问 Redux store 中的数据。

21. 说说 webpack 中常见的 loader?解决了什么问题?

  • babel-loader

    用于将 ES6/ES7/JSX 代码转换为浏览器兼容的 ES5 代码,以及在代码中使用特定的语言特性(如 TypeScript)。可以解决浏览器对新语言特性的兼容性问题。

  • url-loader/file-loader

    用于处理图片、字体等文件类型的加载,可以将文件转换为 base64 码嵌入到代码中,或者将文件复制到输出目录并使用文件名获取路径。可以减少网络请求,优化页面加载速度。

  • style-loader/css-loader/less-loader/sass-loader

    用于加载样式表(CSS/LESS/Sass),将样式表编译为浏览器能理解的 CSS 代码,并将其注入到 DOM 中。可以提高页面加载性能,并使样式表调试更加方便。

  • eslint-loader

    用于在编译阶段对代码进行语法检查、风格检查等操作,可以帮助开发人员规范代码风格,提高代码的可读性和可维护性。

  • postcss-loader/autoprefixer

    用于对 CSS 样式进行预处理、优化和自动添加浏览器前缀。可以大大减少在编写 CSS 时的重复工作,提高样式表的可维护性和可处理性。

 22.说说如何借助webpack来优化前端性能

  1. 压缩代码
  2. 利用CDN加速
  3. Tree shaking讲代码中永远不会走到的片段删除掉。
  4. Code Splitting奖代码按路由维度或者组件分块,这样做到按需加载,同时充分可以利用浏览器缓存。
  5. 提取公共第三方库。

23.说说 JavaScript 内存泄漏的几种情况?

  • 全局变量引起的内存泄漏

    因为全局变量本身不会被回收,如果大量使用全局变量,可能会导致内存泄漏。可以使用模块化开发的方式来避免。

  • 闭包引起的内存泄漏

    当闭包函数中引用到了外部变量,在函数执行完之后,由于闭包的存在,这些变量的内存不会被回收,可能导致内存泄漏。可以尽量避免使用不必要的闭包,或者在使用中及时释放外部变量。

  • 定时器引起的内存泄漏

    当使用 setTimeout 或 setInterval 时,如果不及时清除定时器,在定时器函数中访问的对象将无法被回收,可能导致内存泄漏。可以在设置定时器时及时清除。

  • 循环引用引起的内存泄漏

    当多个对象之间相互引用,形成一个循环结构时,由于引用计数导致这些对象的内存无法被回收,可能导致内存泄漏。可以使用弱引用或者手动解除循环引用来解决。

 

  24.如何通过原生js实现一个节流函数和防抖函数,写出核心代码,不是简单的思路?

防抖:当事件被触发N秒之后再执行回调,如果在N秒内被触发,则会重新计时。

Const debounce=(func,wait=50)=>{

Let timer=null

Return (...args)=>{

     If(timer) clearTimeout(timer)

     Timer=setTimeOut(()=>{

         Func.apply(this,args)

},wait )

}

}

节流:规定在一个时间单位内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效

Const throttle=(func,wait=50)=>{

  Let flag=true

  Return (...args)=>{

      If(flag){

        setTimeout(()=>{

         Func.apply(this,args);

        Flag=true

},wait)

}

Flag=false

}

}

25.说说webpack中代码分割如何实现

Webpack实现代码分割的主要方法是动态导入语法或者配置optimization.splitChunks

动态导入语法:webpack 2提供了import()函数来实现动态导入模块。这个函数会返回一个promise对象,可以用于异步加载模块。在webpack中,可以将动态导入语法用于实现代码分割。

Optimization.splitChunks配置

Webpack4及以上版本可以使用optimization.splitChunks配置项,自动地将公共代码块提取到单独的文件中。这样可以避免重复大打包相同的代码块,从而减小文件大小和加载时间。

说说你对webSocket的理解

WebSocket是什么?

WebSocket是HTML5下一种新的协议(websocket协议本质上是一个基于tcp的协议) 它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的 Websocket是一个持久化的协议

websocket的原理

websocket约定了一个通信的规范,通过一个握手的机制,客户端和服务器之间能建立一个类似tcp的连接,从而方便它们之间的通信 ​ 在websocket出现之前,web交互一般是基于http协议的短连接或者长连接 ​ websocket是一种全新的协议,不属于http无状态协议,协议名为"ws"

说说你对Object.defineProperty()的理解

参考icon-default.png?t=N3I4https://blog.csdn.net/weixin_66557048/article/details/128242342

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象

为什么能实现响应式 通过defineProperty 两个属性,get及set

get 属性的 getter 函数,当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值

set 属性的 setter 函数,当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。默认为 undefined

 

说说你对vue中mixin的理解

参考链接

ixin是面向对象程序设计语言中的类,提供了方法的实现。其他类可以访问mixin类的方法而不必成为其子类; mixin类通常作为功能模块使用,在需要该功能时“混入”,有利于代码复用又避免了多继承的复杂。 本质就是一个JS对象,可以包含组件中的任意功能选项,如data、components、methods、creaded、computed以及生命周期函数等等。 只需要将共用的功能以对象的方式传入mixins选项中,当组件使用mixins对象时所有mixins对象的对象都将被混入该组件本身的选项中来。

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值