d1 React基础

React元素渲染

React 元素是不可变对象。一旦被创建,你就无法更改它的子元素或者属性。一个元素就像电影的单帧:它代表了某个特定时刻的 UI。
更新 UI 唯一的方式是创建一个全新的元素,并将其传入 ReactDOM.render()。

function tick() {
  const element = (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {new Date().toLocaleTimeString()}.</h2>
    </div>
  );
  ReactDOM.render(element, document.getElementById('root'));
}

setInterval(tick, 1000);

注意:

在实践中,大多数 React 应用只会调用一次 ReactDOM.render()。在下一个章节,我们将学习如何将这些代码封装到有状态组件中

React 只更新它需要更新的部分
按照上面的例子来说,页面上发生变化的只是h2标签里面的时间,别的dom元素是不变的(可以右键检查,进行查看)

React组件

  • 组件是React的一等公民,使用React就是在用组件
  • 组件表示页面中的部分功能
  • 组合多个组件实现完整的页面功能
  • 特点:可复用、独立、可组合

组件的创建方式

函数创建组件

  • 函数组件:使用JS的函数创建组件
  • 约定1:函数名称必须以大写字母开头
  • 约定2:函数组件必须有返回值,表示该组件的结构
  • 如果返回值为null,表示不渲染任何内容

示例demo

编写函数组件

function Hello() {
    return (
        <div>这是第一个函数组件</div>
    )
}

利用ReactDOM.render()进行渲染

ReactDOM.render(<Hello />,document.getElementById('root'))

类组件(★★★)

  • 使用ES6语法的class创建的组件
  • 约定1:类名称也必须要大写字母开头
  • 约定2:类组件应该继承React.Component父类,从而可以使用父类中提供的方法或者属性
  • 约定3:类组件必须提供 render 方法
  • 约定4:render方法中必须要有return返回值

示例demo

创建class类,继承React.Component,在里面提供render方法,在return里面返回内容

class Hello extends React.Component{
    render(){
        return (
            <div>这是第一个类组件</div>
        )
    }
}

通过ReactDOM进行渲染

ReactDOM.render(<Hello />,document.getElementById('root'))

抽离成单独的JS文件(★★★)

  • 思考:项目中组件多了之后,该如何组织这些组件?
  • 选择一:将所有的组件放在同一个JS文件中
  • 选择二:将每个组件放到单独的JS文件中
  • 组件作为一个独立的个体,一般都会放到一个单独的JS文件中

示例demo

  • 创建Hello.js
  • 在Hello.js 中导入React,创建组件,在Hello.js中导出
  import React from 'react'
    
    export default class extends React.Component {
        render(){
            return (
                <div>单独抽离出来的 Hello</div>
            )
        }
    }
  • 在index.js中导入Hello组件,渲染到页面
import Hello from './js/Hello'
ReactDOM.render(<Hello />,document.getElementById('root'))

//可以使用这样的方式传递实参const element =<Comment name="h" age="12" arr={[1,2,3,1]} props={obj}></Comment>

组合组件

组件可以在其输出中引用其他组件。这就可以让我们用同一组件来抽象出任意层次的细节。按钮,表单,对话框,甚至整个屏幕的内容:在 React 应用程序中,这些通常都会以组件的形式表示。

例如,我们可以创建一个可以多次渲染 Welcome 组件的 App 组件:

function Welcome(props) {
//组件内部通过props接收所有传递进来的参数,
//例如这个props接收的就是{name: "zs"} 这是一个对象
  return <h1>Hello, {props.name}</h1>;
}

//可以使用这样的方式传递实参const element =<Comment name="h" age="12" arr={[1,2,3,1]} props={obj}></Comment>
function App() {
  return (
    <div>
      <Welcome name="Sara" />
      //可以通过这样的方式进行向组件内传值
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

通常来说,每个新的 React 应用程序的顶层组件都是 App 组件。但是,如果你将 React 集成到现有的应用程序中,你可能需要使用像 Button 这样的小组件,并自下而上地将这类组件逐步应用到视图层的每一处。

提取组件
将组件拆分为更小的组件。

例如,参考如下 Comment 组件:

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <img className="Avatar"
          src={props.author.avatarUrl}
          alt={props.author.name}
        />
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

该组件用于描述一个社交媒体网站上的评论功能,它接收 author(对象),text (字符串)以及 date(日期)作为 props。

该组件由于嵌套的关系,变得难以维护,且很难复用它的各个部分。因此,让我们从中提取一些组件出来。

首先,我们将提取 Avatar 组件:

function Avatar(props) {
  return (
    <img className="Avatar"
      src={props.user.avatarUrl}
      alt={props.user.name}
    />
  );
}

Avatar 不需知道它在 Comment 组件内部是如何渲染的。因此,我们给它的 props 起了一个更通用的名字:user,而不是 author。

我们建议从组件自身的角度命名 props,而不是依赖于调用组件的上下文命名。

我们现在针对 Comment 做些微小调整:

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <Avatar user={props.author} />
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

接下来,我们将提取 UserInfo 组件,该组件在用户名旁渲染 Avatar 组件:

function UserInfo(props) {
  return (
    <div className="UserInfo">
      <Avatar user={props.user} />
      <div className="UserInfo-name">
        {props.user.name}
      </div>
    </div>
  );
}

进一步简化 Comment 组件:

function Comment(props) {
  return (
    <div className="Comment">
      <UserInfo user={props.author} />
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

最初看上去,提取组件可能是一件繁重的工作,但是,在大型应用中,构建可复用组件库是完全值得的。根据经验来看,如果 UI 中有一部分被多次使用(Button,Panel,Avatar),或者组件本身就足够复杂(App,FeedStory,Comment),那么它就是一个可提取出独立组件的候选项。

Props 的只读性
组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props。来看下这个 sum 函数:

function sum(a, b) {
  return a + b;
}

这样的函数被称为“纯函数”,因为该函数不会尝试更改入参,且多次调用下相同的入参始终返回相同的结果。

相反,下面这个函数则不是纯函数,因为它更改了自己的入参:

function withdraw(account, amount) {
  account.total -= amount;
}

React 非常灵活,但它也有一个严格的规则:

所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。
(自己试验了一下:你传进去的如果是简单数据类型的话,你更改就会报错。但是,如果你传进去的是复杂数据类型的话,你传进去就会对数据发生改变)
当然,应用程序的 UI 是动态的,并会伴随着时间的推移而变化。在下一章节中,我们将介绍一种新的概念,称之为 “state”。在不违反上述规则的情况下,state 允许 React 组件随用户操作、网络响应或者其他变化而动态更改输出内容。

props进阶

基本使用

  • 组件时封闭的,要接受外部数据应该通过props来实现
  • props的作用:接收传递给组件的数据
  • 传递数据:给组件标签添加属性
    ‘例如:
ReactDOM.render(<Hello name="zs"></Hello>,document.getElementById('root'))
  • 接收数据:函数组件通过 参数 props接收数据,类组件通过 this.props接收数据
function Hello(props){
    console.log(props);
    return (<div>接收到的数据:{props}</div>)
}
  • 函数组件获取
  class Hello extends React.Component{
    render(){
    return (<div>类接收组件的值:{this.props.name}</div>)
    }
}

特点

  • 可以给组件传递任意类型的数据
  • props是只读属性,不能对值进行修改
  • 注意:使用类组件时,如果写了构造函数,应该将props传递给super(),否则,无法在构造函数中获取到props,其他的地方是可以拿到的

组件通讯的三种方式(★★★)

父组件传递数据给子组件

  • 父组件提供要传递的state数据
  • 给子组件标签添加属性,值为state中的数据
  • 子组件中通过props接收父组件中传递的数据
// 父组件
class Father extends React.Component{
    state={
        familyName:'王',
        momey:80000
    }
    render(){
    return (<div>传递数据给子组件:<Child name={this.state}></Child></div>)
    }
}
// 子组件
class Child extends React.Component{
    render(){
        console.log(this.props);
    return (<h2>接收父组件的数据:{this.props.name.momey}</h2>)
    }
}

// 组件传值:给组件标签添加属性(函数和类的方式都是一样的)
ReactDOM.render(<Father></Father>,document.getElementById('root'))
注意:无论父组件传来的是什么样地值,子组件都以一个name的对象进行接收

子组件传递数据给父组件

  • 利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数
  • 父组件提供一个回调函数,用来接收数据
  • 将该函数作为属性的值,传递给子组件
  • 子组件通过props调用回调函数
// 1.父组件定义一个回调函数进行值的接收
// 2.父组件在嵌套的子组件内进行一个属性传值,把当前的回调函数传入子组件
// 3.子组件接收回调函数,并把要传递的数据作为参数传递给回调函数


// 父组件
class Father extends React.Component{
    getChildData=(msg)=>{
        console.log('接收子组件的数据',msg);
    }
    render(){
    return (<div>传递数据给子组件:<Child getMsg={this.getChildData}></Child></div>)
    }
}
// 类子组件
// class Child extends React.Component{
//     state={ChildData:'孩子的数据'}
//     handleClick=()=>{
//         return this.props.getMsg(this.state.ChildData)
//     }
//     render(){
//         console.log(this.props);
//     return (<button onClick={this.handleClick}>接收父组件的数据:</button>)
//     }
// }

// 函数子组件
function Child(props){
    var state={ChildData:'孩子的数据'}
    props.getMsg(state.ChildData)
    return (<h1>接收父组件的数据:</h1>)
}

// 组件传值:给组件标签添加属性(函数和类的方式都是一样的)
ReactDOM.render(<Father></Father>,document.getElementById('root'))

兄弟组件传递

  • 将共享状态(数据)提升到最近的公共父组件中,由公共父组件管理这个状态
  • 这个称为状态提升
  • 公共父组件职责:1. 提供共享状态 2.提供操作共享状态的方法
  • 要通讯的子组件只需要通过props接收状态或操作状态的方法

示例demo

  • 定义布局结构,一个Counter里面包含两个子组件,一个是计数器的提示,一个是按钮
   class Counter extends React.Component {
    // 提供共享的状态
    state = {
        count: 0
    }
     // 提供共享方法
     onIncrement = (res) => {
        // 只要第二个子组件调用了这个函数,就会执行里面代码
        this.setState({
            count: this.state.count + res
        })
    }
    render() {
        return (<div>
            {/* 把状态提供给第一个子组件 */}
            <Child1 count={this.state.count}/>
            {/* 把共享方法提供给第二个子组件 */}
            <Child2 onIncrement={this.onIncrement} />
        </div>
        )
    }
}
class Child1 extends React.Component {
    render() {
        return (
            <h1>计数器:{this.props.count}</h1>
        )
    }
}
class Child2 extends React.Component {
    handleClick = () => {
        // 这里一旦调用,就会执行父组件里面 onIncrement函数
        this.props.onIncrement(2)
    }
    render() {
        return (
            <button onClick={this.handleClick}>+</button>
        )
    }
}
ReactDOM.render(<Counter></Counter>,document.getElementById('root'))

Context(★★★)

如果出现层级比较多的情况下(例如:爷爷传递数据给孙子),我们会使用Context来进行传递

作用: 跨组件传递数据

使用步骤

  • 调用 React.createContext() 创建 Provider(提供数据) 和 Consumer(消费数据) 两个组件

  • 使用Provider 组件作为父节点

  • 设置value属性,表示要传递的数据

  • 哪一层想要接收数据,就用Consumer进行包裹,在里面回调函数中的参数就是传递过来的值

小结

  • 如果两个组件相隔层级比较多,可以使用Context实现组件通讯
  • Context提供了两个组件:Provider 和 Consumer
  • Provider组件: 用来提供数据
  • Consumer组件: 用来消费数据

props进阶

children属性

  • children属性: 表示组件标签的子节点,当组件标签有子节点时,props就会有该属性
  • children属性与普通的props一样,值可以使任意值(文本、react元素、组件、甚至是函数)

props校验(★★★)

  • 对于组件来说,props是外来的,无法保证组件使用者传入什么格式的数据,简单来说就是组件调用者可能不知道组件封装着需要什么样的数据

  • 如果传入的数据不对,可能会导致报错

  • 关键问题:组件的使用者不知道需要传递什么样的数据

  • props校验:允许在创建组件的时候,指定props的类型、格式等

  • 作用:捕获使用组件时因为props导致的错误,给出明确的错误提示,增加组件的健壮性

使用步骤

  • 安装包 prop-types (yarn add prop-types | npm i props-types)
  • 导入prop-types 包import PropTypes from 'prop-types';
  • 使用组件名.propTypes={} 来给组件的props添加校验规则
  • 校验规则通过PropTypes对象来指定
    -当校验不通过的时候,不影响渲染,但是后台会出警告
import propTypes from 'prop-types';
import React from 'react'
//propTypes大写P会报错,不知道为什么,但是换成小写就好了
function APP(Props){
    console.log(Props);
    console.log(typeof Props.color);
    return (
    <h1>hello ,{Props.color}</h1>
    )
}

APP.propTypes={
    color:propTypes.array
}
function Hello(){
    return(
        <APP color={[1,2,3,4,3]}></APP>
        // {[1,2,3,4,3]}
    )
}
ReactDOM.render(
    <Hello></Hello>,document.getElementById('root')
)

常见的约束规则

  • 创建的类型: array、bool、func、number、object、string
  • React元素类型:element
  • 必填项:isRequired
  • 特定结构的对象: shape({})
  • 更多的约束规则

props的默认值

  • 场景:分页组件 -> 每页显示条数

  • 挂载

  • 在这里插入图片描述

  • 顺序:constructor=>render=>componentDidMount(在render执行后立即执行)

当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:

constructor()//一般可以放一些初始化的操作

constructor(props)
如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。

在 React 组件挂载之前,会调用它的构造函数。在为 React.Component 子类实现构造函数时,应在其他语句之前前调用 super(props)。否则,this.props 在构造函数中可能会出现未定义的 bug。

通常,在 React 中,构造函数仅用于以下两种情况:

通过给 this.state 赋值对象来初始化内部 state。
为事件处理函数绑定实例
在 constructor() 函数中不要调用 setState() 方法。如果你的组件需要使用内部 state,请直接在构造函数中为 this.state 赋值初始 state:

constructor(props) {
  super(props);
  // 不要在这里调用 this.setState()
  this.state = { counter: 0 };
  this.handleClick = this.handleClick.bind(this);
}
只能在构造函数中直接为 this.state 赋值。如需在其他方法中赋值,你应使用 this.setState() 替代。

要避免在构造函数中引入任何副作用或订阅。如遇到此场景,请将对应的操作放置在 componentDidMount 中。

注意

避免将 props 的值复制给 state!这是一个常见的错误:

constructor(props) {
 super(props);
 // 不要这样做
 this.state = { color: props.color };
}
如此做毫无必要(你可以直接使用 this.props.color),同时还产生了 bug(更新 prop 中的 color 时,并不会影响 state)。

只有在你刻意忽略 prop 更新的情况下使用。此时,应将 prop 重命名为 initialColor 或 defaultColor。必要时,你可以修改它的 key,以强制“重置”其内部 state。

请参阅关于避免派生状态的博文,以了解出现 state 依赖 props 的情况该如何处理。
  class Clock extends React.Component {
    constructor(props) {
        super(props);
        this.state = {date:new Date()};//直接在构造函数中为 this.state 赋值初始 state:
  
      }
    render() {
      return (
        <div>
          <h1>Hello, world!</h1>
          <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
        </div>
      );
    }
  }

  ReactDOM.render(<Clock></Clock>,document.getElementById('root'))

static getDerivedStateFromProps()
render()
每次渲染就会触发,不论是开始时候渲染,还是中间渲染页面(注意:不能再调用setState()。因为只要渲染内容render就会触发,setState()既能更新状态又能更新UI,一更新UI,render就又会触发)

componentDidMount()//会在组件挂载后(插入 DOM 树中)立即调用

更新

当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
在这里插入图片描述

static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()

componentDidUpdate(prevProps, prevState, snapshot)//会在更新后会被立即调用。首次渲染不会执行此方法。(相比较于render,晚于render

在这里插入图片描述
在这里插入图片描述
卸载
当组件从 DOM 中移除时会调用如下方法:
在这里插入图片描述

componentWillUnmount()//会在组件卸载及销毁之前直接调用

在这里插入图片描述

错误处理
当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:

static getDerivedStateFromError()
componentDidCatch()

React事件处理(★★★)

事件绑定

  • React事件绑定语法与DOM事件语法相似
  • 语法:on+事件名称=事件处理函数,比如 onClick = function(){}
  • 注意:React事件采用驼峰命名法

示例demo

export default class extends React.Component {
    clickHandle(e){
        console.log('点了')
    }
    render(){
        return (
            <div><button onClick = {this.clickHandle}>点我点我点我</button></div>
        )
    }
}

小结

  • 在React中绑定事件与原生很类似
  • 需要注意点在于,在React绑定事件需要遵循驼峰命名法
  • 类组件与函数组件绑定事件是差不多的,只是在类组件中绑定事件函数的时候需要用到this,代表指向当前的类的引用,在函数中不需要调用this

事件对象

  • 可以通过事件处理函数的参数获取到事件对象
  • React中的事件对象叫做:合成事件
  • 合成事件:兼容所有浏览器,无需担心跨浏览器兼容问题
  • 除兼容所有浏览器外,它还拥有和浏览器原生事件相同的接口,包括 stopPropagation()和 preventDefault()
  • 如果你想获取到原生事件对象,可以通过 nativeEvent 属性来进行获取

示例demo

export default class extends React.Component {
    clickHandle(e){
        // 获取原生事件对象
        console.log(e.nativeEvent)
    }
    render(){
        return (
            <div><button onClick = {this.clickHandle}>点我点我点我</button></div>
        )
    }
}

支持的事件(有兴趣的课下去研究)

  • Clipboard Events 剪切板事件
    • 事件名 :onCopy onCut onPaste
    • 属性 :DOMDataTransfer clipboardData
  • compositionEvent 复合事件
    • 事件名: onCompositionEnd onCompositionStart onCompositionUpdate
    • 属性: string data
  • Keyboard Events 键盘事件
    • 事件名:onKeyDown onKeyPress onKeyUp
    • 属性: 例如 number keyCode 太多就不一一列举
  • Focus Events 焦点事件 (这些焦点事件在 React DOM 上的所有元素都有效,不只是表单元素)
    • 事件名: onFocus onBlur
    • 属性: DOMEventTarget relatedTarget
  • Form Events 表单事件
    • 事件名: onChange onInput onInvalid onSubmit
  • Mouse Events 鼠标事件
    • 事件名:
      onClick onContextMenu onDoubleClick onDrag onDragEnd onDragEnter onDragExit
      onDragLeave onDragOver onDragStart onDrop onMouseDown onMouseEnter onMouseLeave
      onMouseMove onMouseOut onMouseOver onMouseUp
  • Pointer Events 指针事件
    • 事件名:
      onPointerDown onPointerMove onPointerUp onPointerCancel onGotPointerCapture
      onLostPointerCapture onPointerEnter onPointerLeave onPointerOver onPointerOut
  • Selection Events 选择事件
    • 事件名:onSelect
  • Touch Events 触摸事件
    • 事件名:onTouchCancel onTouchEnd onTouchMove onTouchStart
  • UI Events UI 事件
    • 事件名: onScroll
  • Wheel Events 滚轮事件
    • 事件名:onWheel
    • 属性:
      number deltaMode
      number deltaX
      number deltaY
      number deltaZ
  • Media Events 媒体事件
    • 事件名:
      onAbort onCanPlay onCanPlayThrough onDurationChange onEmptied onEncrypted
      onEnded onError onLoadedData onLoadedMetadata onLoadStart onPause onPlay
      onPlaying onProgress onRateChange onSeeked onSeeking onStalled onSuspend
      onTimeUpdate onVolumeChange onWaiting
  • Image Events 图像事件
    • 事件名:onLoad onError
  • Animation Events 动画事件
    • 事件名:onAnimationStart onAnimationEnd onAnimationIteration
  • Transition Events 过渡事件
    • 事件名:onTransitionEnd
  • Other Events 其他事件
    • 事件名: onToggle

有状态组件和无状态组件

  • 函数组件又叫做 无状态组件,类组件又叫做 有状态组件
  • 状态(state) 即数据
  • 函数组件没有自己的状态,只负责数据展示
  • 类组件有自己的状态,负责更新UI,让页面动起来

State和SetState(★★★)

state基本使用

  • 状态(state)即数据,是组件内部的私有数据,只能在组件内部使用
  • state的值是对象,表示一个组件中可以有多个数据
  • 通过this.state来获取状态

示例demo

export default class extends React.Component {
    constructor(){
        super()

        // 第一种初始化方式
        this.state = {
            count : 0
        }
    }
    // 第二种初始化方式
    state = {
        count:1
    }
    render(){
        return (
            <div>计数器 :{this.state.count}</div>
        )
    }
}

setState() 修改状态

  • 状态是可变的
  • 语法:this.setState({要修改的数据})
  • 注意:不要直接修改state中的值,这是错误的
  • setState() 作用:1.修改 state 2.更新UI
  • 思想:数据驱动视图

示例demo

export default class extends React.Component {
    // 第二种初始化方式
    state = {
        count:1
    }
    render(){
        return (
            <div>
                <div>计数器 :{this.state.count}</div>
                <button onClick={() => {
                     this.setState({
            	 		count: this.state.count+1
           			  })   
                }}>+1</button>
            </div>
        )
    }
}

小结

  • 修改state里面的值我们需要通过 this.setState() 来进行修改
  • React底层会有监听,一旦我们调用了setState导致了数据的变化,就会重新调用一次render方法,重新渲染当前组件
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值