如何用React创建一个真实项目

假设我们要创建一个应用,如何用react的思维进行创建呢?本例是react官网的例子,主要是进行更详细的叙述和细节补充。

注:本文默认您已经清楚react的基本用法了,不清楚请点击React官网学习哦。

我们要创建一个什么样的应用?

假设我们要创建一个商品列表的应用。
界面如下(一般界面我们可以从设计师处拿到)

 

而我们从后端获取的数据如下:

 

[
  {category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
  {category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
  {category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
  {category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
  {category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
  {category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
];

Step 1: Break The UI Into A Component Hierarchy

(把UI分成层级组件)

首先你可以根据UI图形为每个component划分区域(一个矩形之类),如果设计稿做得好,很可能每个图层的名称就是我们的组件名称。

 

如何进行分层呢?原则和我们创建一个函数和对象是一致的,就是“单一职责原则”。一个组件理论上应该只做一件事。如果内能还能在划分,那么它应该继续细化。

由于您经常向用户显示JSON数据模型,您会发现,如果您的模型构建正确,您的UI(以及您的组件结构)将很好地映射。 这是因为UI和数据模型倾向于遵循相同的信息架构,这意味着将UI分成组件的工作往往是微不足道的。 只需将其分解成代表您的数据模型的一部分。

基于以上原则,我们把应用分成以下组件。

  • FilterableProductTable (橘色): contains the entirety of the example
  • SearchBar (蓝色): receives all user input
  • ProductTable (绿色): displays and filters the data collection based on user input
  • ProductCategoryRow (蓝绿色): displays a heading for each category
  • ProductRow (红色): displays a row for each product

所以我们的组件层级结果如下

  • FilterableProductTable
  • SearchBar
  • ProductTable
    • -- ProductCategoryRow
    • -- ProductRow

根据我们的结构创建初级代码

代码如下

 

/**
 * Created by zhengzehao on 2017/10/18.
 */
import React, {Component} from 'react'
import  './myCss.css'
class FilterableProductTable extends Component {

    render() {
        return (<div>
            <SearchBar/>
            <ProductTable/>
        </div>)
    }
}

class SearchBar extends Component {

    render() {
        return (<h2>SearchBar</h2>)
    }
}

class ProductTable extends Component {


    render() {
        return (<div>
            <h2>
            ProductTable
            </h2>
            <div>
                <ProductCategoryRow/>
                <ProductRow/>
            </div>
        </div>)
    }
}

class ProductCategoryRow extends Component {


    render() {
        return (<div>ProductCategoryRow</div>)
    }
}

class ProductRow extends Component {


    render() {
        return (<div>ProductRow</div>)
    }
}

const PRODUCTS = [
    {category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
    {category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
    {category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
    {category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
    {category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
    {category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];
export default FilterableProductTable

我们预览看看结果怎么样。

 

 

Step 2: Build A Static Version in React(用创建一个静态版本)

虽然上面的基础版本看起有点丑,但是确定了我们的组件和层级关系,接下来我们不考虑其交互关系,先创建一个静态版本。
因为创建这样的版本可以可以根据各个模块分别创建,而不需要考虑那么多的数据传递方式。

首先需要确定的是,既然是静态版本,我们还不需要state,因为使用state意味着我们有数据的交互,而我们只是创建静态版本。
而我们为什么要用props呢?因为props是父组件传递给子组件的方式,既然我们确定了层级关系,在静态版本我们也需要确定子组件的数据来源。

至于是自上而下(top-down),还是自下而上(bottom-up),取决于我们的结构,一般小应用我们用自上而下是比较快的,而层级比较多而复杂的组件,我们采取自下而上是比较理想的。

FilterableProductTable的修改

对于这个最大的上层组件,我们确定的是关系如下:

 

<div>
    <SearchBar/>
    <ProductTable/>
</div>

但既然我们<ProductTable/>需要展示数据,我们的来源就需要从顶层这里获取。
因此我们的组件修改如下:
(只是添加了products的引用,简单吧...)

 

class FilterableProductTable extends Component {
    render() {
        return (<div>
            <SearchBar/>
            <ProductTable products={PRODUCTS}/>
        </div>)
    }
}

SearchBar的修改

想想我们的SearchBar长什么样?

 

一个input框和一个checkbox,然后是一段文字

所以很容易就创建以下结构。

ps:我们先不考虑数据交互,先做静态版本,所以这里先不与
products交互。

 

class SearchBar extends Component {
    render() {
        return (
            <div>
                <input type="text" placeholder="Search"/>
                <p>
                    <input type="checkbox"/>{''}Only show products in stock
                </p>
            </div>
        )
    }
}

这是的p标签只是用以区分input和checkbox,或者说换行用的,可以自行替换.

ProductTable的修改

 

同样,我们根据UI来看html应该怎么写。

首先是一个table,里面包着两个heade(Name & Price),
Sporting GoodsElectronics呢?
它们也是每一个类别的表头,但是横跨两行。所以我们可以轻松写出这样的结构。

 

<table>
    <thead>
    <tr>
        <th>Name</th>
        <th>Price</th>
    </tr>
    </thead>
    <tbody>
        <tr>
            <th cols={2}>{category}</th>
        </tr>
        <tr>
            <td>{name}</td>
            <td>{price}</td>
        </tr>
    </tbody>
</table>

这里,Name和Price是固定的,而后面的内容是根据后台数据变化的,所以可以把这部分设置为一个变量,如下:

 

<table>
    <thead>
    <tr>
        <th>Name</th>
        <th>Price</th>
    </tr>
    </thead>
    <tbody>{rows}</tbody>
</table>

这里我们的rows要怎么写呢?
考虑一下,我们首先需要一个数组,包含所有的table内容,每个类别有一个表头和内容,
所以应该是这样的

 

rows=[<Category1/>,<Row1/>,<Row2/>,
<Category2/>,<Row3/>,</Row4/>]

而我们的后台数据是这样的

 

const PRODUCTS = [
    {category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
    {category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
    {category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
    {category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
    {category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
    {category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];

这么形成rows形式的结构呢?就是对一个数组判断,如果有一个category(比如Sporting Goods),就push这个表头,然后push后面的内容.
如果下一个对象的category还是一样的,我们就忽略这个category,但是继续push其他内容。直到我们遇到下一个category(比如Electronics),我们才push这个表头。

 

const rows = [];
let lastCategory = null;//作为是否是新category的判断

//首先我们需要从父元素获取这个products的对象列表,然后对每个元素进行遍历(category有新的就push表头,否则就push内容)
this.props.products.forEach((product) => {
    //要清楚我们需要的结构rows = [<Category1/>,<Row1/>,<Row2/>,<Category2/>,<Row3/>,</Row4/>]
    if (product.category !== lastCategory) {
        rows.push(
            <ProductCategoryRow
                category={product.category}
                key={product.category}/>
        );
        //这里添加cataegory时因为子元素需要获取每个表头的名字,key就不多说了,不清楚可以看官网了解key的作用
    }
    rows.push(
        <ProductRow
            product={product}
            key={product.name}/>
    );
    //这里添加product是因为子元素需要获取每个product的内容,包括价格,名称等等
    lastCategory = product.category;
});

所以我们创建了一个单项数据流的结构,自上而下,只有一个render()方法,当我们需要确认UI变化的原因,可以很容易地追溯到。

综上,我们的ProductTable代码如下:

 

class ProductTable extends React.Component {
    render() {
        const rows = [];
        let lastCategory = null;
        this.props.products.forEach((product) => {
            //要清楚我们需要的结构rows = [<Category1/>,<Row1/>,<Row2/>,<Category2/>,<Row3/>,</Row4/>]
            if (product.category !== lastCategory) {
                rows.push(
                    <ProductCategoryRow
                        category={product.category}
                        key={product.category}/>
                );
            }
            rows.push(
                <ProductRow
                    product={product}
                    key={product.name}/>
            );
            lastCategory = product.category;
        });
        return (
            <table>
                <thead>
                <tr>
                    <th>Name</th>
                    <th>Price</th>
                </tr>
                </thead>
                <tbody>{rows}</tbody>
            </table>
        );
    }
}

ProductCategoryRow的修改

这里的html结构我们上面已经讨论过了。
由于需要category的字段,我们直接从父元素获取(this.props.category)

 

class ProductCategoryRow extends Component {
    render() {
        var category = this.props.category;
        return (
        <tr>
            <th cols={2}>{category}</th>
        </tr>
        )
    }
}

ProductRow的修改

结构之前已经讨论过,而我们需要对是否stocked进行标记,所以采取
三元表示符。(stocked为false的颜色为红)

 

 const name = product.stocked ?
            product.name :
            <span style={{color: 'red'}}>
                {product.name}
            </span>;

 

class ProductRow extends React.Component {
    render() {
        const product = this.props.product;
        const name = product.stocked ?
            product.name :
            <span style={{color: 'red'}}>
                {product.name}
            </span>;
        // 如果是stocked就直接输出,否则用span包裹并设置style的color为red
        return (
            <tr>
                <td>{name}</td>
                <td>{product.price}</td>
            </tr>
        );
    }
}

完整的代码

 

/**
 * Created by zhengzehao on 2017/10/18.
 */
import React, {Component} from 'react'
class FilterableProductTable extends Component {
    render() {
        return (<div>
            <SearchBar/>
            <ProductTable products={PRODUCTS}/>
        </div>)
    }
}

class SearchBar extends Component {
    render() {
        return (
            <div>
                <input type="text" placeholder="Search"/>
                <p>
                    <input type="checkbox"/>{''}Only show products in stock
                </p>
            </div>
        )
    }
}

class ProductTable extends React.Component {
    render() {
        const rows = [];
        let lastCategory = null;
        this.props.products.forEach((product) => {
            //要清楚我们需要的结构rows = [<Category1/>,<Row1/>,<Row2/>,<Category2/>,<Row3/>,</Row4/>]
            if (product.category !== lastCategory) {
                rows.push(
                    <ProductCategoryRow
                        category={product.category}
                        key={product.category}/>
                );
            }
            rows.push(
                <ProductRow
                    product={product}
                    key={product.name}/>
            );
            lastCategory = product.category;
        });
        return (
            <table>
                <thead>
                <tr>
                    <th>Name</th>
                    <th>Price</th>
                </tr>
                </thead>
                <tbody>{rows}</tbody>
            </table>
        );
    }
}

class ProductCategoryRow extends Component {

    render() {
        var category = this.props.category;
        return (<tr>
            <th cols={2}>{category}</th>
        </tr>)
    }
}

class ProductRow extends React.Component {
    render() {
        const product = this.props.product;
        const name = product.stocked ?
            product.name :
            <span style={{color: 'red'}}>
                {product.name}
            </span>;
        // 如果是stocked就直接输出,否则用span包裹并设置style的color为red
        return (
            <tr>
                <td>{name}</td>
                <td>{product.price}</td>
            </tr>
        );
    }
}

const PRODUCTS = [
    {category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
    {category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
    {category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
    {category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
    {category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
    {category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];
export default FilterableProductTable

预览一下:

Step 3: Identify The Minimal (but complete) Representation Of UI State

要使UI产生交互效果,在REACT中我们是通过state来进行的。

考虑一下我们的的各种数据有:

  • 后端提供的products列表。
  • input标签的输入框产生的内容
  • checkbox的结果
  • ProductTable过滤后的结果

 

考虑是不是state数据有以下原则

  • 是否可以从父组件通过props传递过来?
    • If so, it probably isn’t state.
  • 是否是一个常量?
    • If so, it probably isn’t state.
  • 是否可以根据其他state或者props计算出来?
    • If so, it isn’t state.

接下来逐项分析:

  • products列表的内容可以从父元素的props获取,所以不是state。
  • 搜索框和checkbox的内容似乎是state,因为它们无法从其他元素获取或者计算得出。
  • 最后,过滤的列表也不是state,因为它可以通过搜索框和checkbox的值进行筛选得出。

最终,我们只有两个state需要设置。

  1. 搜索框里用户输入的值
  2. checkbox的值

Step 4:Identify Where Your State Should Live

好的,所以我们确定了最小的应用状态是什么。 接下来,我们需要确定哪个组件应该改变或拥有这种状态。

记住:React是一个单向数据流的层级结构,而我们或许一开始并不清楚需要把state设置在哪里。
这通常是对入门者最困扰的地方,但是我们可以根据下面的步骤进行分析。

对每个state进行分析:

  • Identify every component that renders something based on that state.
  • 找出每个需要根据state进行渲染的组件
  • Find a common owner component (a single component above all the components that need the state in the hierarchy).
  • 找出一个共通组件(一般是层级组件的上层)
  • Either the common owner or another component higher up in the hierarchy should own the state.
  • 一个共通组件或者另外的更高阶层的组件应该拥有这些state
  • If you can’t find a component where it makes sense to own the state, create a new component simply for holding the state and add it somewhere in the hierarchy above the common owner component.
  • 如果没有找到一个组件适合去存放这些state,我们可以创建一个。

我们根据上面的原则进行分析

  • ProductTable需要根据与用户在搜索框的输入和checkbox的勾选进行过滤数据,把过滤后的数据展示在上面。

  • 共通的组件便是FilterableProductTable.(ProductTable和SearchBar的共通父级)

Cool, so we’ve decided that our state lives in FilterableProductTable

① 在FilterableProductTable 建立初始statethis.state = {filterText: '', inStockOnly: false}
② 在<ProductTable/> 和 <SearchBar/>传递props filterText 和 inStockOnly=state对应的值,使子组件获取到该值。

 

<div>
<SearchBar filterText={this.state.filterText}inStockOnly={this.state.inStockOnly}/>

<ProductTable products={PRODUCTS}
filterText={this.state.filterText} inStockOnly={this.state.inStockOnly}/>
</div>

③ 用这些props去过滤ProductTable的内容,并且把SeachBar对应的props设为这些state。

 

class SearchBar extends Component {
    render() {
        const filterText = this.props.filterText;
        const inStockOnly = this.props.inStockOnly;
        return (
            <div>
                <input type="text" placeholder="Search" value={filterText}/>
                <p>
                    <input type="checkbox" checked={inStockOnly}/>{''}Only show products in stock
                </p>
            </div>
        )
    }
}

 

class ProductTable extends React.Component {
    render() {
        const filterText = this.props.filterText;
        const inStockOnly = this.props.inStockOnly;

        const rows = [];
        let lastCategory = null;
        this.props.products.forEach((product) => {

            // new
            if (product.name.indexOf(filterText) === -1) {
                return;
            }
            if (inStockOnly && !product.stocked) {
                return;
            }

            if (product.category !== lastCategory) {
                rows.push(
                    <ProductCategoryRow
                        category={product.category}
                        key={product.category}/>
                );
            }
            rows.push(
                <ProductRow
                    product={product}
                    key={product.name}/>
            );
            lastCategory = product.category;
        });
        return (
            <table>
                <thead>
                <tr>
                    <th>Name</th>
                    <th>Price</th>
                </tr>
                </thead>
                <tbody>{rows}</tbody>
            </table>
        );
    }
}

完整的代码

 

/**
 * Created by zhengzehao on 2017/10/18.
 */
import React, {Component} from 'react'
class FilterableProductTable extends Component {
    constructor(props) {
        super(props)
        this.state = {
            filterText: '',
            inStockOnly: false
        };
    }
    render() {
        return (<div>
            <SearchBar filterText={this.state.filterText} inStockOnly={this.state.inStockOnly}/>
            <ProductTable products={PRODUCTS}
                          filterText={this.state.filterText}
                          inStockOnly={this.state.inStockOnly}/>
        </div>)
    }
}

class SearchBar extends Component {
    render() {
        const filterText = this.props.filterText;
        const inStockOnly = this.props.inStockOnly;
        return (
            <div>
                <input type="text" placeholder="Search" value={filterText}/>
                <p>
                    <input type="checkbox" checked={inStockOnly}/>{''}Only show products in stock
                </p>
            </div>
        )
    }
}


class ProductTable extends React.Component {
    render() {
        const filterText = this.props.filterText;
        const inStockOnly = this.props.inStockOnly;

        const rows = [];
        let lastCategory = null;
        this.props.products.forEach((product) => {

            // new
            if (product.name.indexOf(filterText) === -1) {
                return;
            }
            if (inStockOnly && !product.stocked) {
                return;
            }

            if (product.category !== lastCategory) {
                rows.push(
                    <ProductCategoryRow
                        category={product.category}
                        key={product.category}/>
                );
            }
            rows.push(
                <ProductRow
                    product={product}
                    key={product.name}/>
            );
            lastCategory = product.category;
        });
        return (
            <table>
                <thead>
                <tr>
                    <th>Name</th>
                    <th>Price</th>
                </tr>
                </thead>
                <tbody>{rows}</tbody>
            </table>
        );
    }
}


class ProductCategoryRow extends Component {
    render() {
        var category = this.props.category;
        return (<tr>
            <th cols={2}>{category}</th>
        </tr>)
    }
}

class ProductRow extends React.Component {
    render() {
        const product = this.props.product;
        const name = product.stocked ?
            product.name :
            <span style={{color: 'red'}}>
                {product.name}
            </span>;
        // 如果是stocked就直接输出,否则用span包裹并设置style的color为red
        return (
            <tr>
                <td>{name}</td>
                <td>{product.price}</td>
            </tr>
        );
    }
}

const PRODUCTS = [
    {category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
    {category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
    {category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
    {category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
    {category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
    {category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];
export default FilterableProductTable

Step 5: Add Inverse Data Flow

到目前为止,我们已经构建了一个应用程序,可以在层次结构中用function的props或者state进行正确地渲染。 现在是时候来进行数据交互了。

React这样的数据结构使我们能明确地知道各个组件之间的数据流动,但相对一般的双向数据流,我们需要多一步(通过回调函数)来改变state的值。

如果你尝试在上一步建立的app中进行输入或者改变checkbox的状态,你会发现是无效的。原因是我们设置了input的值永远是由FilterableProductTable的state进行传递的。

让我们确认一下发生了什么。我们想要确定无论何时,用户改变表单,我们就根据输入的内容(包括input和checkbox)改变我们的state。
FilterableProductTable 会传递回调函数,只要我们的state需要变更,它便会触发。

我们可以通过onChange作为这个回调函数,一旦用户的表单发生改变,我们就用setState()来改变state。

完整代码

相对上一步,我们就多了两个函数,onChange的时候调用这两个函数进行改变state,注意要绑定this哦😯...

 

/**
 * Created by zhengzehao on 2017/10/18.
 */
import React, {Component} from 'react'
class FilterableProductTable extends Component {
    constructor(props) {
        super(props)
        this.state = {
            filterText: '',
            inStockOnly: false
        };
        this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
        this.handleInStockChange = this.handleInStockChange.bind(this);
    }
    handleFilterTextChange(filterText){
        this.setState({
            filterText: filterText
        });
    }

    handleInStockChange(inStockOnly){
        this.setState({
            inStockOnly: inStockOnly
        })
    }
    render() {
        return (<div>
            <SearchBar filterText={this.state.filterText} inStockOnly={this.state.inStockOnly}
                       onFilterTextChange={this.handleFilterTextChange}
                       onInStockChange={this.handleInStockChange}/>
            <ProductTable products={PRODUCTS}
                          filterText={this.state.filterText}
                          inStockOnly={this.state.inStockOnly}/>
        </div>)
    }
}

class SearchBar extends Component {
    constructor(props) {
        super(props);
        this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
        this.handleInStockChange = this.handleInStockChange.bind(this);
    }

    handleFilterTextChange(e) {
        this.props.onFilterTextChange(e.target.value);
    }

    handleInStockChange(e) {
        this.props.onInStockChange(e.target.checked);
    }

    render() {
        const filterText = this.props.filterText;
        const inStockOnly = this.props.inStockOnly;
        return (
            <div>
                <input type="text" placeholder="Search" value={filterText}  onChange={this.handleFilterTextChange}/>
                <p>
                    <input type="checkbox" checked={inStockOnly} onChange={this.handleInStockChange}/>{''}Only show products in stock
                </p>
            </div>
        )
    }
}


class ProductTable extends React.Component {
    render() {
        const filterText = this.props.filterText;
        const inStockOnly = this.props.inStockOnly;

        const rows = [];
        let lastCategory = null;
        this.props.products.forEach((product) => {

            // new
            if (product.name.indexOf(filterText) === -1) {
                return;
            }
            if (inStockOnly && !product.stocked) {
                return;
            }

            if (product.category !== lastCategory) {
                rows.push(
                    <ProductCategoryRow
                        category={product.category}
                        key={product.category}/>
                );
            }
            rows.push(
                <ProductRow
                    product={product}
                    key={product.name}/>
            );
            lastCategory = product.category;
        });
        return (
            <table>
                <thead>
                <tr>
                    <th>Name</th>
                    <th>Price</th>
                </tr>
                </thead>
                <tbody>{rows}</tbody>
            </table>
        );
    }
}


class ProductCategoryRow extends Component {
    render() {
        var category = this.props.category;
        return (
        <tr>
            <th cols={2}>{category}</th>
        </tr>)
    }
}

class ProductRow extends React.Component {
    render() {
        const product = this.props.product;
        const name = product.stocked ?
            product.name :
            <span style={{color: 'red'}}>
                {product.name}
            </span>;
        // 如果是stocked就直接输出,否则用span包裹并设置style的color为red
        return (
            <tr>
                <td>{name}</td>
                <td>{product.price}</td>
            </tr>
        );
    }
}

const PRODUCTS = [
    {category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
    {category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
    {category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
    {category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
    {category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
    {category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];
export default FilterableProductTable

此时就可以进行筛选和搜索了。

 

 



作者:Aleph_Zheng
链接:https://www.jianshu.com/p/9ff4eb06b443
来源:简书
著作权归作者所有。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值