钩子的丑陋一面

In this post, I will share my own point of view about React Hooks, and as the title of this post implies, I am not a big fan.

在这篇文章中,我将分享我对React Hooks的观点,正如这篇文章的标题所暗示的那样,我不是一个忠实的粉丝。

Let’s break down the motivation for ditching classes in favor of hooks, as described in the official React’s docs.

让我们按照React的官方文档中所述,放弃使用钩子放弃类的动机。

动机1:课程令人困惑 (Motivation #1: Classes are confusing)

we’ve found that classes can be a large barrier to learning React. You have to understand how this works in JavaScript, which is very different from how it works in most languages. You have to remember to bind the event handlers. Without unstable syntax proposals, the code is very verbose […] The distinction between function and class components in React and when to use each one leads to disagreements even between experienced React developers.

我们发现类可能是学习React的一大障碍。 您必须了解this在JavaScript中的工作方式,这与大多数语言中的工作方式截然不同。 您必须记住绑定事件处理程序。 没有不稳定的语法建议 ,代码就非常冗长[…] React中函数和类组件之间的区别以及何时使用每个组件都会导致甚至经验丰富的React开发人员之间的分歧。

Ok, I can agree that this could be a bit confusing when you are just starting your way in Javascript, but arrow functions solve the confusion, and calling a stage 3 feature that is already being supported out of the box by Typescript, an “unstable syntax proposal”, is just pure demagoguery. React team is referring to the class field syntax, a syntax that is already being vastly used and will probably soon be officially supported:

好的,我可以同意,当您刚开始使用Javascript时, this可能会有些混乱,但是箭头函数解决了这种困惑,并调用了Typescript已经支持的现阶段3功能,“ 不稳定“语法建议 ”,仅是纯粹的煽动。 React团队指的是类字段语法,该语法已经被广泛使用并且可能很快会得到正式支持:

As you can see, by using a class field arrow function, you don’t need to bind anything in the constructor, and this will always point to the correct context.

如您所见,通过使用类字段箭头函数,您无需在构造函数中绑定任何内容,并且this始终指向正确的上下文。

And if classes are confusing, what can we say about the new hooked functions? A hooked function is not a regular function, because it has state, it has a weird looking this (akauseRef), and it can have multiple instances. But it is definitely not a class, it is something in between, and from now on I will refer to it as a Funclass. So, are those Funclasses going to be easier for human and machines? I am not sure about machines, but I really don’t think that Funclasses are conceptually easier to understand than classes. Classes are a well known and thought concept, and every developer is familiar with the concept of this, even if in javascript it’s a bit different. Funclasses on the other hand, are a new concept, and a pretty weird one. They feel much more magical, and they rely too much on conventions instead of a strict syntax. You have to follow some strict and weird rules, you need to be careful of where you put your code, and there are many pitfalls. Be also prepared for some awful naming like useRef(a fancy name for this), useEffect ,useMemo, useImperativeHandle (say whatt??) and more.

如果类令人困惑,那么对于新的钩子函数我们能说些什么呢? 一个带钩的功能不是一般的功能,因为它有状态,它有一个奇怪的看着this (又名useRef)它可以有多个实例。 但这绝对不是类,介于两者之间,从现在开始,我将其称为Funclass 。 那么,对于人类和机器而言,那些Funclass会更容易吗? 我不确定机器,但是我真的不认为Funclass从概念上比类更容易理解。 类是一个众所周知的思想概念,每个开发人员都熟悉this的概念,即使在javascript中也有所不同。 另一方面,Funclass是一个新概念,一个很奇怪的概念。 他们感到更加神奇,他们过多地依赖约定而不是严格的语法。 您必须遵循一些严格而又怪异的规则 ,需要注意将代码放置在何处,并且有很多陷阱。 也要做好一些糟糕的命名,如useRef ( this花哨的名称), useEffectuseMemouseImperativeHandle (比如whatt ??)等等。

The syntax of classes was specifically invented in order to deal with the concept of multiple instances and the concept of an instance scope (the exact purpose of this ). Funclasses are just a weird way of achieving the same goal, using the wrong puzzle pieces.

类的语法,以处理具有多个实例的概念和实例范围的概念(的确切目的是专门发明this )。 趣味类只是使用错误的拼图碎片而达到相同目标的一种怪异方法。

Oh, and about the last note:

哦,关于最后一个音符:

The distinction between function and class components in React and when to use each one leads to disagreements even between experienced React developers

React中功能和类组件之间的区别以及何时使用每个组件之间的区别甚至导致经验丰富的React开发人员之间也存在分歧

Until now, the distinction was pretty clear- if you needed a state or lifecycle methods, you used a class, otherwise it doesn’t really matter if you used a function or class. Personally, I liked the idea that when I stumbled upon a function component, I could immediately know that this is a “dumb component” without a state. Sadly, with the introduction of Funclasses, this is not the situation anymore.

直到现在,两者之间的区别还是很明显的-如果需要状态或生命周期方法,则使用类,否则使用函数或类并不重要。 就个人而言,我喜欢这样的想法:当我偶然发现一个功能组件时,我可以立即知道这是一个没有状态的“哑巴组件”。 可悲的是,随着Funclasss的引入,这种情况不再存在。

动机2:很难在组件之间重用有状态逻辑 (Motivation #2: It’s hard to reuse stateful logic between components)

React doesn’t offer a way to “attach” reusable behavior to a component (for example, connecting it to a store)…React needs a better primitive for sharing stateful logic.

React没有提供一种将可重用行为“附加”到组件的方法(例如,将其连接到商店)…React需要更好的原语来共享状态逻辑。

Isn’t it ironic? The biggest problem of React, in my opinion at least, is that it doesn’t provide an out-of-the-box state management solution, leaving us with a long lasted debate on how we should fill this gap, and opening a door for some very bad design patterns like Redux. So after years of frustration, React team has finally came to the conclusion that it is hard to share stateful logic between components…who could have guessed. Anyways, are hooks going to make the situation any better? The answer is not really. Hooks can’t work with classes, so if your codebase is already written with classes, you still need another way for sharing stateful logic. Also, hooks only solve the problem of sharing per-instance logic, but if you want to share state between multiple instances, you still need to use stores and 3rd party state management solutions, and as I said, if you already use them, you don’t really need hooks. So instead of just fighting the symptoms, maybe it’s time for React to take an action and implement a proper state management tool for managing both global state (stores) and local state (per instance), and thus kill this loophole once and for all.

具有讽刺意味吗? 至少在我看来,React的最大问题是它没有提供开箱即用的状态管理解决方案,这给我们留下了关于如何填补这一空白的长期辩论,并打开了一扇门一些非常糟糕的设计模式,例如Redux。 因此,经过多年的挫败,React团队终于得出结论,很难在组件之间共享有状态的逻辑……谁可能会猜到。 无论如何,勾子会使情况变得更好吗? 答案不是真的。 挂钩不能与类一起使用,因此,如果您的代码库已经用类编写,则仍然需要另一种共享状态逻辑的方法。 同样,钩子只能解决按实例逻辑共享的问题,但是如果要在多个实例之间共享状态,则仍然需要使用商店和第三方状态管理解决方案,正如我所说,如果已经使用它们,则真的不需要钩子。 因此,不仅仅是抗争症状,也许是时候让React采取行动并实施适当的状态管理工具来管理全局状态(存储)和局部状态(每个实例),从而彻底消除此漏洞。

动机3:复杂的组件变得难以理解 (Motivation #3: Complex components become hard to understand)

We’ve often had to maintain components that started out simple but grew into an unmanageable mess of stateful logic and side effects. Each lifecycle method often contains a mix of unrelated logic. […] Mutually related code that changes together gets split apart, but completely unrelated code ends up combined in a single method. […] Hooks let you split one component into smaller functions based on what pieces are related (such as setting up a subscription or fetching data), rather than forcing a split based on lifecycle methods.

我们经常不得不维护一些组件,这些组件起初很简单,但是发展成为状态逻辑和副作用难以控制的混乱状态。 每个生命周期方法通常包含不相关逻辑的混合。 […]一起变化的相互关联的代码被分开,但是完全不相关的代码最终以单个方法组合在一起。 […]挂钩使您可以根据相关的部分(例如设置订阅或获取数据)将一个组件拆分为较小的功能,而不必根据生命周期方法进行拆分。

If you are already using stores, this argument is almost not relevant. Let’s see why:

如果您已经在使用商店,则此参数几乎不相关。 让我们看看为什么:

class Foo extends React.Component {
componentDidMount() {
doA();
doB();
doC();
}
}

As you can see in this example, we are possibly mixing unrelated logic in componentDidMount, but Is it bloating our component? Not exactly. The whole implementation sits outside of the class, and the state sits in the store. without stores, all the stateful logic must be implemented inside the class, and the class would have been bloated indeed. But again, it looks like React is solving a problem that mostly exists in a world without state-management tools. In reality, most of the big apps are already using a state management tool, and this problem is already mitigated. Also, in most cases we could probably break this class to smaller components and put each doSomething() in the componentDidMount of the sub-components.

如本例所示,我们可能在componentDidMount混合了不相关的逻辑,但这会使我们的组件膨胀吗? 不完全是。 整个实现位于类之外,而状态位于存储中。 没有存储,所有状态逻辑都必须在类内部实现,而该类确实会肿。 但是同样,React似乎正在解决一个大多数情况下都没有状态管理工具的问题。 实际上,大多数大型应用程序已经在使用状态管理工具,并且该问题已得到缓解。 此外,在大多数情况下,我们也许可以打破这个类更小的组件,并把每个doSomething()componentDidMount子组件。

With Funclasses, we could write something like this:

使用Funclass,我们可以编写如下代码:

function Foo() {
useA();
useB();
useC();
}

It looks a bit cleaner, but is it? We still need to write 3 different useEffect hooks somewhere, so at the end we are going to write more code, and look what we did here- with the class component, you can tell at first glance what the component is doing on mount. In the Funclass example, you need to follow the hooks, and try to search for a useEffect with an empty dependencies array, in order to understand what the component is doing on mount. The declarative nature of the life-cycle methods is mostly a good thing, and I found it much harder to investigate the flow of Funclasses. I have seen many cases were Funclasses made it easier for developers to write bad code, we’ll see an example later on.

看起来有点干净,但是吗? 我们仍然需要在某个地方编写3个不同的useEffect挂钩,因此最后我们将编写更多代码,并看看我们在这里所做的工作-使用类组件,您可以乍一看就知道该组件在安装时正在做什么。 在Funclass示例中,您需要遵循这些钩子,并尝试使用空的依赖项数组搜索useEffect ,以便了解组件在挂载时正在做什么。 生命周期方法的声明性本质上是一件好事,我发现研究Funclasss的流程要困难得多。 我已经看到很多情况下Funclasss使开发人员更容易编写错误的代码,我们将在后面看到一个示例。

But first, I must admit that there is something nice about useEffect, take a look at the following example:

但首先,我必须承认useEffect有一些useEffect ,请看以下示例:

useEffect(() => {
subscribeToA();
return () => {
unsubscribeFromA();
};
}, []);

The useEffect hook lets us pair together subscribe and unsubscribe logic. This is actually a pretty neat pattern. Same goes for pairing together componentDidMount and componentDidUpdate. In my experience, those cases are not so common, but they are still valid use-cases, and useEffect is really helpful here. The question is- why do we have to use Funclasses in order to get useEffect? why can’t we have something similar with classes? The answer is we can:

useEffect挂钩使我们可以将订阅和退订逻辑配对在一起。 这实际上是一个非常简洁的模式。 将componentDidMountcomponentDidUpdate配对在一起也是如此。 以我的经验,这些案例并不常见,但它们仍然是有效的用例,在这里useEffect确实很有帮助。 问题是-为什么我们必须使用Funclass才能获得useEffect? 我们为什么不能在类上有类似的东西? 答案是我们可以:

class Foo extends React.Component {
someEffect = effect((value1, value2) => {
return () => {
unsubscribeFromA();
};
}) render(){
this.someEffect(this.props.value1, this.state.value2);
return <Text>Hello world</Text>
}
}

The effect function will memoize the given function, and will call it again only if one of its params has changed. By triggering the effect from within our render function, we make sure that it’s being called on every render/update, but the given function will run again only if one of its params has been changed, so we achieve the exact same result of useEffect.

effect函数将记住给定的函数,并且仅当其参数之一已更改时才会再次调用它。 通过从render函数中触发效果,可以确保在每次渲染/更新时都调用该效果,但是给定的函数只有在其参数之一已更改的情况下才会再次运行,因此我们可以达到useEffect的完全相同的结果。

You can check out the implementation of the effect function here, and if you want to see it in action, check out this working example.

您可以在此处查看 effect功能的实现,如果想要查看实际效果,请查看此工作示例

The bottom line is that useEffect should not be considered as valid motivation for moving into Funclasses. It is a valid motive on its own, and could be implemented for Classes too. The suggested implementation is a bit ugly, because calling the effect from within the render is weird, but React can add native support for this pattern and improve it.

最重要的是, useEffect不应被视为进入Funclasss的有效动机。 它本身就是一个有效的动机,也可以为Classes实现。 建议的实现有些丑陋,因为从渲染器内部调用效果很奇怪,但是React可以为该模式添加本机支持并加以改进。

动机4:绩效 (Motivation #4: Performance)

We found that class components can encourage unintentional patterns that make these optimizations fall back to a slower path. Classes present issues for today’s tools, too. For example, classes don’t minify very well, and they make hot reloading flaky and unreliable

我们发现类组件可以鼓励无意识的模式,这些模式会使这些优化退回到较慢的路径。 类也提出了当今工具的问题。 例如,类无法很好地缩小,并且它们使热重装变得片状且不可靠

The React team is saying that classes are harder to optimize and minimize, and that Funclasses should somehow improve things. Well, I have only one thing to say about this- show me the numbers.

React团队说类很难更优化和最小化,而Funclass应该以某种方式改进。 好吧,关于这一点,我只有一件事要说- 给我看看数字。

I couldn’t find any paper what so ever, or any benchmark demo app that I could clone and run, comparing the performance of Funclasses VS classes. The fact that we haven’t seen such a demo is not surprising- Funclasses need to implement this (or useRef if you prefer) somehow, so I pretty much expect that the same problems that makes classes hard to optimize, will effect Funclasses too.

我至今找不到任何论文,也找不到能克隆并运行以比较Funclasses VS类的性能的基准测试演示应用程序。 我们还没有看到这样的演示,这一事实不足为奇-Funclasss需要以某种方式实现this (或者,如果愿意,可以使用useRef ),因此我非常希望使类难以优化的那些问题也会影响Funclasss。

Anyways, all the debate about performance is really worth nothing without showing the numbers, so we can’t really use it as an argument.

无论如何,如果不显示数字,关于性能的所有辩论实际上一文不值,因此我们不能真正将其用作论据。

动机5:Funclass不太冗长 (Motivation #5: Funclasses are less verbose)

You can find many examples for code reduction by converting a Class to a Funclass, but most if not all of the examples take advantage of the useEffect hook in order to combine componentDidMount and componentWillUnmount, thus achieving great impact. But as I said earlier, useEffect should not be considered as a Funclass’ advantage, and if you ignore the code reduction achieved by it, you are left with a much minor impact. And if you’ll try to optimize your Funclasses using useMemo, useCallback and so on, you could even end up with a more verbose code than an equivalent class. When comparing small and trivial components, Funclasses win without a doubt, because classes have some inherent boilerplate that you need to pay no matter how small your class is. But when comparing big components, you can barely see the differences, and sometimes as I said, classes can even be cleaner.

您可以找到许多通过将Class转换为Funclass来减少代码的示例,但是大多数(如果不是全部)示例都利用useEffect钩子来组合componentDidMountcomponentWillUnmount ,从而产生巨大的影响。 但是正如我之前所说, useEffect不应被视为Funclass的优势,并且如果您忽略它所实现的代码缩减,那么您所受的影响就很小。 而且,如果您尝试使用useMemouseCallback等来优化Funclass,您甚至可能得到比等效类更冗长的代码。 在比较小型组件和琐碎组件时,Funclass无疑会获胜,因为类有一些固有的样板,无论您的类有多小,您都需要付出这些。 但是,在比较大型组件时,您几乎看不到它们之间的差异,有时就像我说的那样,类甚至可以变得更干净。

So after raising some questions about the motivations, let’s take a look at some other stuff that I don’t like about Funclasses.

因此,在提出有关动机的问题之后,让我们看一下我不喜欢的有关Funclass的其他内容。

隐藏的副作用 (The hidden side effect)

One of the things that bothers me the most in the implementation of useEffect for Funclasses, is the lack of clarity about what are the side effects of a given component. With classes, if you wanted to find out what a components is doing on mount, you could easily check out the code in componentDidMount or check the constructor. If you see a repeating call, you should probably check out componentDidUpdate. With the new useEffect hook, side effects could be hidden deeply nested within the code.

在FunClasss的useEffect的实现中,最令我困扰的事情之一是,对于给定组件的副作用有哪些不清楚。 使用类,如果您想了解安装时组件的功能,则可以轻松地检出componentDidMount的代码或检查构造函数。 如果看到重复的呼叫,则可能应该签出componentDidUpdate 。 使用新的useEffect钩子,副作用可以深深地嵌套在代码中。

Let’s say we detect some unwanted calls to the server. We look at the code of the suspected component and see this:

假设我们检测到一些不必要的服务器调用。 我们查看可疑组件的代码,然后看到以下内容:

const renderContacts = (props) => {
  const [contacts, loadMoreContacts] = useContacts(props.contactsIds);
  return (
    <SmartContactList contacts={contacts}/>
  )
}

Nothing special here. Should we investigate SmartContactList or maybe we should dive into useContacts? Let’s dive into useContacts:

这里没什么特别的。 我们应该调查SmartContactList还是应该深入使用useContacts ? 让我们深入useContacts

export const useContacts = (contactsIds) => {
  const {loadedContacts, loadingStatus}  = useContactsLoader();
  const {isRefreshing, handleSwipe} = useSwipeToReresh(loadingStatus);
  // ... many other useX() functions
  useEffect(() => {
    //** lots of code, all related to some animations that are relevant for loading contacts*//
  
  }, [loadingStatus]);
  
  //..rest of code
}

Ok, it’s starting to get tricky. where is the hidden side effect? If we’ll dive into useSwipeToRefresh we will see:

好的,开始变得棘手。 隐藏的副作用在哪里? 如果我们深入研究useSwipeToRefresh我们将看到:

export const useSwipeToRefresh = (loadingStatus) => {
  // ..lot's of code
  // ...
  
  useEffect(() => {
    if(loadingStatus === 'refresing') {
       refreshContacts(); // bingo! our hidden sideEffect!
    }  
  }); //<== we forgot the dependencies array!
}

We found our hidden effect. refreshContacts will accidentally call fetch contacts on every component render. In a big codebase, and some badly structured components, nested useEffect could cause nasty trouble.

我们发现了隐藏的效果。 refreshContacts会在每个组件渲染上意外调用获取联系人。 在大型代码库和某些结构不良的组件中,嵌套的useEffect可能会造成麻烦。

I am not saying that you can’t write bad code with classes too, but Funclasses are much more error prone, and without the strictly defined structure of the life cycle methods, it’s much easier to do bad things.

我并不是说您也不能使用类编写错误的代码,但是Funclasss更容易出错,并且如果没有生命周期方法的严格定义的结构,做坏事情就容易得多。

膨胀的API (Bloated API)

By adding hooks API alongside classes, React’s API is practically doubled. Everyone needs to learn two completely different methodologies now. And I must say that the new API is much more obscure than the old one. Simple things like getting the previous props and state, are becoming good interview material. Can you write a hook for getting the prev props, without the help of google?

通过在类旁边添加钩子API,React的API实际上增加了一倍。 现在每个人都需要学习两种完全不同的方法。 我必须说,新API比旧API晦涩得多。 获得以前的道具和状态等简单的事情正成为很好的面试材料。 您能否在不借助Google的情况下写钩子来获取上一个道具?

A big library like React must be very careful with adding such huge changes in the API, and the motivation here was not even close to being justified.

像React这样的大型库必须非常小心地在API中添加如此巨大的更改,并且这样做的动机甚至还没有道理。

缺乏说明性 (Lack of declarativity)

In my opinion, Funclasses tend to get much messier than classes. For example, It’s harder to find the entry point of the component- with classes you just search for the render function, but with Funclasses it can be hard to spot the main return statement. Also, It’s harder to follow the different useEffect statements and understand the flow of the component, as opposed to the regular life-cycle methods that give you some good hints for where you need to look for your code. If I am searching for some kind of initialization logic, I will jump (cmd + shift + o in VSCode) to componentDidMount. If I am looking for some kind of updating mechanism, I will probably jump to componentDidUpdate and so on. With Funclasses I find it much harder to orient inside big components.

在我看来,Funclass比类更混乱。 例如,使用仅搜索render函数的类很难找到组件的入口点,但是对于Funclasss来说,很难找到主要的return语句。 另外,与常规的生命周期方法相反,它很难遵循不同的useEffect语句并理解组件的流程,而常规生命周期方法为您提供了在哪里寻找代码的一些良好提示。 如果我正在寻找某种初始化逻辑,我将跳转(VSCode中的cmd + shift + o )到componentDidMount 。 如果我正在寻找某种更新机制,则可能会跳到componentDidUpdate等。 使用Funclass,我发现很难在大型组件内部定位。

只是感觉不对 (It just feels wrong)

You know that feeling that something isn’t right? This is how I feel about hooks. Sometimes I can put my finger on the exact problem, but sometimes it’s just a general feeling that we are on the wrong track. When you discover a good concept, you can see how things are working together nicely. But when you are struggling with the wrong concept, it turns out that you need to add more and more specific stuff and rules in order to make thing work. With hooks, there are more and more weird things that pop out, more “useful” hooks that help you do some trivial stuff, and more things to learn. If we need so many utils for our day to day work, just for hiding away some weird complications, this is a huge sign that we are on the wrong track.

您知道那种感觉不对吗? 这就是我对钩子的感觉。 有时我会把手指放在确切的问题上,但有时只是一般的感觉,我们走错了路。 当您发现一个好的概念时,您会发现事情如何很好地协同工作。 但是,当您为错误的概念而苦苦挣扎时,事实证明您需要添加越来越多的特定内容和规则才能使事情正常运行。 有了钩子,就会出现越来越多的怪异事物,更多“有用”的钩子可以帮助您做一些琐碎的事情,还有更多的东西要学习。 如果我们在日常工作中需要大量的工具,只是为了隐藏一些奇怪的并发症,这是一个巨大的信号,表明我们走错了路。

Few years ago, when I switched from Angular 1.5 to React, I was amazed by how simple the API of React was, and how thin the docs are. Angular used to have huge docs. It would have taken you days to cover everything- the digest mechanism, the different compilation phases, transclude, binding, templates and more. This alone was a huge indication for me that something is just wrong. React on the other hand, immediately felt right. It was clean and concise, you could go over the whole docs in a matter of hours and you were good to go. While trying hooks for the first time, and for the second time, and for all the following times, I’ve found myself obligated to go back to docs again and again.

几年前,当我从Angular 1.5切换到React时,我惊讶于React的API如此简单,文档如此之薄。 Angular曾经有大量的文档。 这将花费您几天的时间来涵盖所有内容,包括摘要机制,不同的编译阶段,transclude,bind,模板等。 仅此一点就对我来说是一个很大的指示,说明出事了。 另一方面,立即做出React。 简洁明了,您可以在几个小时内浏览整个文档,并且一切顺利。 在第一次,第二次以及之后的所有时间里尝试钩子时,我发现自己有义务一次又一次地回到文档上。

结论 (Conclusion)

I hate being the party pooper, but I really think that Hooks could be the 2nd worst thing that happened to the React community (first place is still taken by Redux). It added another useless debate to an already fragile eco-system, it’s not clear now if hooks are the recommended way to go or is it just another feature and a matter of personal taste. Anyways, for now me and my teammates decided to stick with classes and a Mobx-based solution as a state management tool.

我讨厌成为聚会上的警察,但是我真的认为Hooks可能是React社区发生的第二大最糟糕的事情(Redux仍然排名第一)。 它给已经脆弱的生态系统增加了另一场毫无用处的争论,现在尚不清楚钩子是否是推荐的使用方式,还是只是另一个功能和个人品味的问题。 无论如何,现在我和我的队友决定坚持使用课程和基于Mobx的解决方案作为状态管理工具。

Personally I really wish React could just ctrl + z hooks all together.

我个人真的希望React可以将ctrl + z挂钩在一起。

翻译自: https://medium.com/@niryo/the-ugly-side-of-hooks-584f0f8136b6

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值