【react】学习笔记

JSX

概念:JSX是 JavaScript XML(HTML)的缩写,表示在 JS 代码中书写 HTML 结构
作用:在React中创建HTML结构(页面UI结构)

优势

  • JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
  • 它是类型安全的,在编译过程中就能发现错误。
  • 使用 JSX 编写模板更加简单快速。

注意:JSX 并不是标准的 JS 语法,是 JS 的语法扩展,浏览器默认是不识别的,脚手架中内置的 @babel/plugin-transform-react-jsx 包,用来解析该语法

官方文档

JSX的注意事项

格式

1:<> xxx </>

import ReactDOM from 'react-dom'
 
const title = <> <h1>Hello React</h1><p>p标签</p> </>
 
ReactDOM.render(title, document.querySelector('#root'))

2:<React.Fragment> xxx </React.Fragment>

import ReactDOM from 'react-dom'
 
const title = <React.Fragment> <h1>Hello React</h1><p>p标签</p> </React.Fragment>>
 
ReactDOM.render(title, document.querySelector('#root'))

3.属性名不能用js中的关键字。例如class, for

4。单标签要闭合

5.换行建议使用( )包裹

JSX-嵌入表达式

1.格式:{ JS 表达式 }

import ReactDOM from 'react-dom'
 
const num = Math.random()
const title = (<div>
               		随机值:{ num }
               </div>)
 
ReactDOM.render(title, document.querySelector('#root'))

JSX-条件渲染

【if/else,三元运算符,逻辑与(&&)运算符,switch case 都可以进行渲染】

1.用三元表达式
const flag = 0
const content = (<div>{flag ? '达成' : '没有达成'}</div>)

2.用逻辑与(逻辑或一样用)
const isLogin = true
const content = (<div>{ isLogin && '您登录了'}</div>)

3.使用额外的函数
const loadData = (isLoading) => {
  if (isLoading) {
    return <div>数据加载中,请稍后...</div> 
  } else {
    return <div>数据加载完成,此处显示加载后的数据</div>
  }
}
const content = <div>{loadData(true)}</div>

4.使用map
import React from 'react' // react 提供最核心的react组件功能
import ReactDOM from 'react-dom' //  配合react 把react渲染到dom
 
// 创建元素
const list = [
  { id: 1, name: '张国荣', salary: 11000 },
  { id: 2, name: '周杰伦', salary: 15000 }
]
const app = (
  <ul>
    {list.map((item) => (
      <li key={item.id}>
        <h3>姓名:{item.name}</h3>
        <p>工资:{item.salary}</p>
      </li>
    ))}
  </ul>
)
 
// 渲染react元素
ReactDOM.render(app, document.getElementById('root'))

hooks

useState 的使用(联想vue3的ref)

useState是 react 提供的一个定义响应式变量的 hook 函数,基本语法如下:

const [count, setCount] = useState(initialCount)

  • useState它返回一个状态和一个修改状态的方法,状态需要通过这个方法来进行修改;
  • initialCount 是我们传入的一个初始状态,它是惰性的,我们可以通过传一个函数来返回一个值当作初始状态,并且这个函数只会在初始渲染时执行一次;
import { useState } from 'react'
function App() {
    const [count, setCount] = useState(0)
    const handleClick = () => {
        setCount(count + 1)
        // 传入一个函数,更新的值是基于之前的值来执行
        // setCount(count => count + 1)
    }
    return (
    	<div>
        	<h4>count: {count}</h4>
            <button onClick={ handleClick }>点击更新状态</button>
        </div>
    )
}

useEffect 类似监听

什么是副作用:当一个数据或者属性发生改变之后会引起的一些变化。

副作用是相对于主作用来说的,一个函数除了主作用,其他的作用就是副作用。对于 React 组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用(比如,手动修改 DOM)

通常在副作用中进行ajax请求,事件的绑定与解绑,设置定时器与清除等等。

useEffect(() => {
  // 副作用函数的内容
})  
 每次更新之后都要执行
--------------------
useEffect(() => {
  // 副作用函数的内容
}, []) 
初始化页面时 只执行第一次
--------------------
useEffect(() => {
  // 副作用函数的内容
}, [依赖项]) //依赖项可以有多个
1 初始执行一次 2 每次依赖项的值变化时执行  (类比vue watch)

useEffect 只能是一个同步函数,不能使用 async
如果需要发送请求或者其他异步操作,需要额外加一个箭头函数包裹住



useEffect(()={
async () => {
    const res = awiat xxx()
 
   }
}, [])

useLayoutEffect

useEffect在浏览器渲染完成后执行
useLayoutEffect在浏览器渲染前执行
【官方建议优先使用useEffect】

  • 为什么建议将修改 DOM 的操作里放到 useLayoutEffect 里,而不是 useEffect?

DOM 已经被修改,但但浏览器渲染线程依旧处于被阻塞阶段,所以还没有发生回流、重绘过程。由于内存中的 DOM 已经被修改,通过useLayoutEffect 可以拿到最新的 DOM 节点,并且在此时对 DOM 进行样式上的修改,假设修改了元素的height,这些修改会 react 做出的更改一起被一次性渲染到屏幕上,依旧只有一次回流、重绘的代价。

如果放在 useEffect 里,useEffect 的函数会在组件渲染到屏幕之后执行,此时对 DOM
进行修改,会触发浏览器再次进行回流、重绘,增加了性能上的损耗。

  • useEffect 和 useLayoutEffect 的区别?

useEffect 在渲染时是异步执行,并且要等到浏览器将所有变化渲染到屏幕后才会被执行。

useLayoutEffect 在渲染时是同步执行,其执行时机与 componentDidMount,componentDidUpdate
一致

useContext 共享状态钩子

如果需要在深层次组件之间共享状态,可以使用 useContext()。context 提供了一种在组件之间共享 props 的方式,而不必显示地通过组件树的逐层传递 props; useContext 钩子比原始 class 组件中使用 context 更为方便;

假设现在有俩个组件 Navbar 和 Messages,我们希望它们之间共享状态。
使用方法如下: 第一步在它们的父组件上使用 React 的 Context API,在组件外部建立一个 Context。

import React, { useContext } from 'react';

const TestContext = React.createContext({});

const Navbar = () => {
  const { username } = useContext(TestContext);

  return (
    <div className="navbar">
      <p>{username}</p>
    </div>
  );
};

const Messages = () => {
  const { messageDetail } = useContext(TestContext);

  return (
    <div className="messages">
      <p>1 message for {messageDetail}</p>
    </div>
  );
};

const App = () => {
  return (
    <TestContext.Provider
      value={{
        username: 'superawesome',
        messageDetail: 'hello world',
      }}
    >
      <div className="test">
        <Navbar />
        <Messages />
      </div>
    </TestContext.Provider>
  );
};
export default App;

等价的 class 用例

import React from 'react';

const TestContext = React.createContext({});

const Navbar = () => (
  <TestContext.Consumer>
    {({ username }) => (
      <div className="messages">
        <p>1 message for {username}</p>
      </div>
    )}
  </TestContext.Consumer>
);

const Messages = () => {
  return (
    <TestContext.Consumer>
      {({ messageDetail }) => (
        <div className="messages">
          <p>1 message for {messageDetail}</p>
        </div>
      )}
    </TestContext.Consumer>
  );
};

class App extends React.Component {
  render() {
    return (
      <TestContext.Provider
        value={{
          username: 'superawesome',
          messageDetail: 'hello world',
        }}
      >
        <div className="test">
          <Navbar />
          <Messages />
        </div>
      </TestContext.Provider>
    );
  }
}
export default App;

useMemo和useCallback的共同点:(可联想vue compute)

接收的参数都是一样的,第一个是回调函数,第二个是依赖的数据
它们都是当依赖的数据发生变化时才会重新计算结果,起到了缓存作用

useMemo和useCallback的区别:

  • useMemo计算结果是return回来的值,通常用于缓存计算结果的值
  • useCallback计算结果是一个函数,通常用于缓存函数

useCallback:性能优化

useCallback返回的是一个 memoized(缓存)函数,在依赖不变的情况下,多次定义的时候,返回的值是相同的,他的实现原理是当使用一组参数初次调用函数时,会缓存参数和计算结果,当再次使用相同的参数调用该函数时,会直接返回相应的缓存结果。

import React, { useState, useCallback, memo } from 'react';
const Parent = () => {
    const [value1, setValue1] = useState(0);
    const [value2, setValue2] = useState(0);
    const handleClick1 = useCallback(()=> {
        setValue1(value1 + 1);
    }, [value1]);
    const handleClick2 = useCallback(()=> {
        setValue2(value2 + 1);
    }, [value2]);
    return (
        <>
            {(() => console.log("Parent-render"))()}
            <h3>{value1}</h3>
            <h3>{value2}</h3>
            <Child1 handleClick1={handleClick1} />
            <Child2 handleClick2={handleClick2} />
        </>
    );
}
const Child1 = memo(props => {
    return (
        <div>
            {(() => console.log("Child1-render"))()}
            <button onClick={() => props.handleClick1()}>value1 + 1</button>
        </div>
    );
});
const Child2 = memo(props => {
    return (
        <div>
            {(() => console.log("Child2-render"))()}
            <button onClick={() => props.handleClick2()}>value2 + 1</button>
        </div>
    );
});
export default Parent

useMemo:性能优化

import React, { useState, useMemo } from 'react'
const Test = ()=> {
    const [value, setValue] = useState(0);
    const [count, setCount] = useState(1);
    const getDoubleCount = useMemo(() => {
        console.log('getDoubleCount进行计算了');
        return count * 2;
    },[count]);
    return (
        <div>
            <h2>value: {value}</h2>
            <h2>doubleCount: {getDoubleCount}</h2>
            <button onClick={() => setValue(value + 1)}>value+1</button>
        </div>
    )
}
export default Test

组件

函数组件和类组件区别

1、设计思想

  • 类组件的根基是 OOP(面向对象编程),所以它会有继承,有内部状态管理等
  • 函数组件的根基是 FP(函数式编程),与数学中的函数思想类似,所以假定输入和输出存在某种关联的话,那么相同输入必定会有相同的输出

2 生命周期

  • 不能在函数组件中使用生命周期钩子,原因和不能使用state一样,所有的生命周期钩子都来自于继承的React.Component中。因此,如果你想使用生命周期钩子,那么需要使用类组件。

3、调用方式
如果SayHi是一个函数,React需要调用它:

// 你的代码 
function SayHi() { 
    return <p>Hello, React</p> 
} 
// React内部 
const result = SayHi(props) // » <p>Hello, React</p>
如果SayHi是一个类,React需要先用new操作符将其实例化,然后调用刚才生成实例的render方法:

// 你的代码 
class SayHi extends React.Component { 
    render() { 
        return <p>Hello, React</p> 
    } 
} 
// React内部 
const instance = new SayHi(props) // » SayHi {} 
const result = instance.render() // » <p>Hello, React</p>

可想而知,函数组件重新渲染将重新调用组件方法返回新的react元素,类组件重新渲染将new一个新的组件实例,然后调用render类方法返回react元素,这也说明为什么类组件中this是可变的.

函数组件

函数组件也称无状态组件,顾名思义就是以函数形态存在的 React 组件。

在 hooks 出现之前,react 中的函数组件通常只考虑负责UI的渲染,没有自身的状态,没有业务逻辑代码,是一个纯函数。下面这个函数组件就是一个纯函数,它的输出只由参数props决定,不受其他任何因素影响。

import { useState } from "react";
import User from "./User";

import classes from "./Users.module.css";

const DUMMY_USERS = [
  { id: "u1", name: "Max" },
  { id: "u2", name: "Manuel" },
  { id: "u3", name: "Julie" },
];
 const Users = () => {
   const [showUsers, setShowUsers] = useState(true);

   const toggleUsersHandler = () => {
     setShowUsers((curState) => !curState);
   };

   const usersList = (
     <ul>
       {DUMMY_USERS.map((user) => (
         <User key={user.id} name={user.name} />
       ))}
     </ul>
   );

   return (
     <div className={classes.users}>
       <button onClick={toggleUsersHandler}>
         {showUsers ? "Hide" : "Show"} Users
       </button>
       {showUsers && usersList}
     </div>
   );
 };
export default Users;

但是这种函数组件一旦我们需要给组件加状态,那就只能将组件重写为类组件,因为函数组件没有实例,没有生命周期。所以我们说在 hook 之前的函数组件和类组件最大的区别又是状态的有无。

类组件

mport { Component } from "react";
import User from "./User";

import classes from "./Users.module.css";

const DUMMY_USERS = [
  { id: "u1", name: "Max" },
  { id: "u2", name: "Manuel" },
  { id: "u3", name: "Julie" },
];

class Users extends Component {
  constructor() {
    super();
    // 对于类组件,this.state 必须永远是一个对象
    // useState 相比之下灵活得多
    this.state = {
      showUsers: true,
    };
  }
  toggleUsersHandler() {
	// setState 将旧的state和新的state进行合并, 
	// useState 不同。
	// 对于类组件,如果新状态依赖于前一个状态,
	// 也必须使用箭头函数:
    this.setState((curState) => {
      // // 这里返回的对象会被 React 与旧的 state 合并
      return { showUsers: !curState.showUsers };
    });
  }

  render() {
    const usersList = (
      <ul>
        {DUMMY_USERS.map((user) => (
          <User key={user.id} name={user.name} />
        ))}
      </ul>
    );

    return (
      <div className={classes.users}>
        <button onClick={this.toggleUsersHandler.bind(this)}>
          {this.state.showUsers ? "Hide" : "Show"} Users
        </button>
        {this.state.showUsers && usersList}
      </div>
    );
  }
}
export default Users;

传值

父子组件传值

父组件通过props传递给子组件;

//父组件
class CommentList extends Component{
    render(){
        return(
            <div>
               <Comment comment={information}/>
            </div>
        )
    }
}
//子组件
class Comment extends Component{
    render(){
        return(
            <div>
                <span>{this.props.comment}:</span>
            </div>
        )
    }
}

子组件向父组件传值

//父组件
import Child from './Child.js'export default class Parent extend compenent{
  getData=(data)=>{
    console.log(data);
  }
  render(){
    return (
      <div>
        父组件
        <Child getData={this.getData}/>
      </div>
    )
  }
}

//子组件
export default class Child extend compenent{
  state={
    data:[1,2,3]
  }
  render(){
    const {data}=this.state;
    return (
      <div>
        子组件
        <button onClick={()=>{this.props.getData(data)}}><button>
      </div>
    )
  }
}

使用context进行传递(上面代码已经有了)

不相关组件传值 (联想 angular RXJS)

1、通过发布/订阅进行传递
发布订阅又被称之为观察者模式(听起来挺高端哈)
其实很简单,就是一个组件A发布一个需求,然后组件B去监听并实现实现这个需求

工具库: PubSubJS

下载: npm install pubsub-js --save

使用:

import PubSub from ‘pubsub-js’ //引入

PubSub.publish(‘delete’, data) //发布消息
PubSub.subscribe(‘delete’, function(data){ }); //订阅
发布消息

//定义方法,并且发布消息
search = () =>{
	..........发布消息所需参数
	pubSub.publish(‘search',参数)
}

订阅并实现

订阅消息并且实现,需要写在componentDidMount方法里面
componentDidMount(){
pubsub.subscribe('search',(参数)=>{
	..........实现过程
})
}

redux / react-redux (联想 VUEX)

官方定义:redux 是 js 应用的可预测状态的容器。 可以理解为全局数据状态管理工具(状态管理机),用来做组件通信等。
为什么使用redux:如果没有redux,不相关组件或者好几层组件之间传值很麻烦,redux定义全局单一的数据

  • action :redux 将每一个更改动作描述为一个action,要更改state中的内容,你需要发送action。一个action是一个简单的对象,用来描述state发生了什么变更。
  • reducer:数据state,指示action都有了那么就是实现了。reducer就是根据action来对state进行操作。
  • store :就是整个项目保存数据的地方,并且只能有一个。
  • dispatch : store.dispatch()是组件发出action的唯一方法。
  • connect:connect用于连接React组件与 Redux store
  • Provider:Provider组件主要有以下两个作用:
    1、在原应用组件上包裹一层,使原来整个应用成为Provider的子组件
    2、接收Redux的store作为props,通过context对象传递给子孙组件

react-几步搞定redux-persist-持久化存储

store

store/channel.Store.js

import { makeAutoObservable } from 'mobx'
import { http } from '@/utils'
class ChannelStore {
  channelList = []
  constructor() {
    makeAutoObservable(this)
  }
  // article publish 哪里调用这个函数呢?
  loadChannelList = async () => {
    const res = await http.get('/channels')
    this.channelList = res.data.channels
  }
}

export default ChannelStore

store/index.js

// 把所有的模块做统一处理
// 导出一个统一的方法 useStore
import React from "react"
import LoginStore from "./login.Store"
import UserStore from "./user.Store"
import ChannelStore from "./channel.Store"

import { configure } from "mobx"
configure({
  enforceActions: "never",
})


class RootStore {
  constructor() {
    this.loginStore = new LoginStore()
    this.userStore = new UserStore()
    this.channelStore = new ChannelStore()
    // ...
  }
}

// 实例化根
// 导出useStore context
const rootStore = new RootStore()
const context = React.createContext(rootStore)

const useStore = () => React.useContext(context)

export { useStore }

路由

react路由的基本使用

  • 安装react-router-dom,在入口文件index.js中引入HasRouter组件,并将要使用的到路由的组件进行包裹
  • 在子组件中引入Link,Route
  • Link用来配置路由的跳转路径
  • to属性设置跳转的路由地址

Route用来设置展示的组件
component用于设置匹配的路径展示的组件
path设置匹配的路径
exact 属性用于让Router中的path与Link中的to完全匹配
Switch标签的作用是将包裹在其中的Router中path重复的进行剔除,只留下唯一的Router
包裹的必须直接是route标签的元素

// app.js

import Nav from './components/Nav'
import Home from './components/Home'
import {Link, Route} from 'react-router-dom'

function App() {
  return (
    <div>
      根组件
      <div>
        <p><Link to='/home'>home</Link></p>
        <p><Link to='/nav'>nav</Link></p>
      </div>
      <div>
        <p><Route exact component={Home} path='/home'></Route></p>
        <p><Route exact component={Nav} path='/nav'></Route></p>
      </div>
      <hr />
      <div>
        <Switch>
          <Route exact component={Home} path='/home'></Route>
          <Route exact component={Home} path='/'></Route>
          <Route exact component={Nav} path='/nav'></Route>
          <Route exact component={Nav} path='/nav'></Route>
          <Route exact component={Nav} path='/nav'></Route>
        </Switch>
      </div>
    </div>
  );
}

export default App;
// index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {HashRouter} from 'react-router-dom'

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

路由参数传递

动态路由传参

  • 需要路由参数占位符,:id
  • 触发操作的时候传递具体的参数
  • 在具体的组件中使用传递的参数
  • 首先要在路由内配置匹配规则Route
  • 相关路径的组件需要内部配置Link规则
  • 通过props.match.params获取传入的数据对象
nav 内部配置文件
import React, {Component} from 'react'
import {Link} from 'react-router-dom'

class Nav extends Component {
    render () {
        return (
            <>
                <p><Link excat to='/detail/1'>文章1</Link></p>
                <p><Link excat to='/detail/2'>文章2</Link></p>
                <p><Link excat to='/detail/3'>文章3</Link></p>
            </>
        )
    }
}

export default Nav
// detail  获取传入的数据

import React, {Component} from 'react'

class Detail extends Component {
    render () {
        return (
            <>
                {this.props.match.params.id}
            </>
        )
    }
}

export default Detail

// app 根组件

...
        <Switch>
          ...
          <Route exact component={Detail} path='/detail/:id'></Route>
        </Switch>
...

查询参数传参

  • 在路径后通过xxx?属性名=yyy&…&…进行传参
  • 通过props.location.search获取整个查询字段
  • 安装qs,通过qs.parse进行转译,ignoreQueryPrefix省略问号
// detail.js

import React, {Component} from 'react'
import qs from 'qs'

class Detail extends Component {
    render () {
        console.log(this.props);
        console.log(qs.parse(this.props.location.search, {ignoreQueryPrefix:true}));
        return (
            <>
                {this.props.match.params.id}
                {qs.parse(this.props.location.search, {ignoreQueryPrefix:true}).title}
            </>
        )
    }
}

export default Detail

路由的嵌套

  • 在路由组件内部书写新的路由规则和渲染的组件
  • 通过props.match.url和props.match.path可以分别动态的获取上一级的路由Link的to路径和Route的path路径,避免了修改父路由而引发的路径错误
  • 父组件一定要去除excat属性
// Nav.js

import React, {Component} from 'react'
import {Link, Route} from 'react-router-dom'
import Inner from './inner'
import Outer from './outer'

class Nav extends Component {
    render () {
        return (
            <>
                <p><Link to='/nav/1'>文章1</Link></p>
                <p><Link to='/nav/2'>文章2</Link></p>
                <p><Link to='/nav/3?title="张三"'>文章3</Link></p>
                <div>
                    <div>
                        {console.log(this.props)}
                        <Link to={`${this.props.match.url}/inner`}>inner</Link>
                        <Link to={`${this.props.match.url}/outer`}>outer</Link>
                    </div>  
                    <div>
                        <Route path={`${this.props.match.path}/inner`} component={Inner} />
                        <Route path={`${this.props.match.path}/outer`} component={Outer} />
                    </div>
                </div>
            </>
        )
    }
}

export default Nav

路由跳转及重定向

从react-router-dom中引入Redirect组件,属性to为跳转到的路由路径
可以通过判断来进行路由的跳转

import React from 'react'
import { Redirect } from 'react-router-dom'

class Inner extends React.Component {
    render() {
        var isShow = true
        if (isShow) {
            return (
                <Redirect to='/home' />
            )
        } else {
            <p>123</p>
        }
    }
}

export default Inner

路由守卫

在Route标签中书写path,并且内部可以进行render操作
render中方法传入的参数是所在路由的所有信息,是子组件的thisprops

import Nav from './components/Nav'
import Home from './components/Home'
import Detail from './components/Detail'
import { Link, Redirect, Route, Switch } from 'react-router-dom'

const auth = {
  isAuthor: false,
  show: function() {
    this.isAuthor = true
  }
}

function App() {
  return (
    <div>
      根组件
      <div>
        <p><Link to='/home'>home</Link>---<Link to='/nav'>nav</Link></p>
        <button onClick={() => {auth.show()}}>转换</button>
      </div>
      <div>
        <Switch>
          <Route exact component={Detail} path='/nav/:id'></Route>
          <Route path='/nav' render={props => {
            console.log(props);
            if (auth.isAuthor) {
              return <Nav {...props} />
            } else {
              return <Redirect to='/home' />
            }
          }} />
        </Switch>
      </div>
    </div>
  );
}

export default App;

路由守卫组件

react用高阶组件实现路由守卫

目的在于简化路由守卫的操作,单独操作没有什么复杂的,单数数量多就会影响开发

// Guard.js

import  React from 'react'
import { Redirect, Route } from 'react-router';

export default class AuthRouterGuard extends React.Component {
    render () {
        
        const {component: Component, ...rest} = this.props
        return (
            <Route {...rest} render={props => {
                if (this.props.auth.isAuthor) {
                    return <Component {...props} />
                } else {
                    return <Redirect to='/home' />
                }
            }} />
        )
    }
}
// app.js

import Nav from './components/Nav'
import Home from './components/Home'
import { Link,  Route, Switch } from 'react-router-dom'
import AuthRouterGuard from './guard'

const auth = {
  isAuthor: false,
  show: function() {
    this.isAuthor = true
  }
}

function App() {
  return (
    <div>
      根组件
      <div>
        <Link to='/nav'>nav</Link></p>
      </div>
      <div>
        <Switch>
          <AuthRouterGuard path='/nav' auth={auth} component={Nav} /
        </Switch>
      </div>
    </div>
  );
}

export default App;

路由懒加载

懒加载的目的是降低首次页面加载时的性能损耗
体现在于一次拆开所有薯片再吃和拆开一包吃一包
通过@loadable/component第三方包插件,引入loadComponent方法
内部参数为方法,返回值为import引入的组件


import loadComponent from '@loadable/component'

const Home = loadComponent(() => import('./components/Home'))
...
<Route exact component={Home} path='/home'></Route>
...

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值