react hooks使用

React Suspense对Monad就像钩子对应用符号一样 (React Suspense is to a Monad as Hooks are to Applicative Notation)

Monads and Applicative Functors are extensively used in functional programming. There is a relationship between them and React Suspense for Data Fetching and React Hooks APIs. This is a quick and simple introduction to Monads and Applicatives along with a description of their similarities.

Monad和应用函子在函数式编程中被广泛使用。 它们与用于数据提取的React Suspense和React Hooks API之间存在关系。 这是对Monads和Applicatives的快速简单介绍,并介绍了它们的相似性。

The post is about future React Suspense for Data Fetching, not about recently released React Suspense for Code Splitting (React.Suspense and React.lazy) .

这篇文章是关于未来的React Suspense for Data Fetching,而不是最近发布的React Suspense for Code Splitting( React.SuspenseReact.lazy )。

单子符号 (Monad do-notation)

The React framework approach encourages developers to use functional programming techniques. At least component render functions should not have observable side effects. JavaScript has no way to ensure this, but there are programming languages which can. For example, Haskell doesn’t accept side effects at all.

React框架方法鼓励开发人员使用函数式编程技术。 至少组件渲染功能不应具有可观察到的副作用。 JavaScript无法确保这一点,但是有些编程语言可以做到。 例如,Haskell根本不接受副作用。

Pure functions make the code modular, predictable and easier to verify. But they also significantly increase verbosity. Here is a statement from Phil Walder’s Monads for functional programming(1995) tutorial:

纯函数使代码模块化,可预测且易于验证。 但是它们也大大增加了详细程度。 这是Phil Walder的Monads关于函数式编程 (1995年)教程的声明:

It is with regard to modularity that explicit data flow becomes both a blessing and a curse. On the one hand, it is the ultimate in modularity. All data in and all data out are rendered manifest and accessible, providing a maximum of flexibility. On the other hand, it is the nadir of modularity. The essence of an algorithm can become buried under the plumbing required to carry data from its point of creation to its point of use.

关于模块化,明确的数据流既是福也是祸。 一方面,它是模块化的终极目标。 所有数据输入和所有数据输出都将显示出来并可以访问,从而提供最大的灵活性。 另一方面,它是模块化的最低点。 从本质上讲,算法的本质可以掩盖在从创建点到使用点的数据传输所需的管道之下。

Monads solve this problem for Haskell. And Suspense/Hooks solve the same problem in React.

Monads为Haskell解决了这个问题。 Suspense / Hooks解决了React中的相同问题。

So what is a Monad? It is a simple abstract interface which has two functions, let’s call them of and chain.

那么什么是Monad? 这是一个简单的抽象接口,它有两个功能,让我们称他们ofchain

  • of — takes any value and returns some monadic (effectful) value

    of —取任何值并返回一些单价(有效)值

  • chain — takes an effectful value and a function from any value to an effectful one and returns another effectful value

    chain —将有效值和函数从任何值转换为有效值,然后返回另一个有效值

The effectful values there may encapsulate any concrete implementation-specific information. There are no requirements what exactly it should be, it is some opaque data. The interface’s concrete implementations should follow a set of laws, and this is it.

那里的有效值可以封装任何具体的特定于实现的信息。 没有要求确切是什么,它是一些不透明的数据。 接口的具体实现应遵循一组法律,就是这样。

There is nothing to say more about monads since they are abstract. They don’t necessarily store anything, wrap or unwrap anything or even chain anything.

由于Monad是抽象的,因此没有更多可说的了。 他们不一定存储任何东西,包装或拆开任何东西甚至连任何东西都没有。

But why do we need this if it is so abstract and defines almost nothing? The interface provides an abstract mean to compose computations with side effects.

但是,如果它是如此抽象并且几乎没有定义,我们为什么需要它呢? 该接口提供了抽象的方法来构成带有副作用的计算。

If you write code in JavaScript, you may wonder now. You’ve already composed a lot of computations with side effects without seeing any Monad. But in fact, you can consider you’ve already used them there.

如果您使用JavaScript编写代码,您现在可能会感到奇怪。 您已经完成了许多带有副作用的计算,而没有看到任何Monad。 但实际上,您可以认为您已经在其中使用过它们。

In computer science, Monads first appeared for studying side effects in imperative languages. They are a tool to embed imperative worlds into a pure math world for further studying.

在计算机科学领域,Monads首次出现是为了研究命令式语言的副作用。 它们是将命令式世界嵌入纯数学世界中以供进一步研究的工具。

This way if you want to convert your imperative program into math formulas representing it, doing this with Monad expressions would be the simplest and the most straightforward way. It is so straightforward what you don’t even need to do it manually, there are tools that do it for you.

这样,如果要将命令式程序转换为表示该命令式的数学公式,使用Monad表达式执行此操作将是最简单,最直接的方法。 它非常简单,您甚至不需要手动进行操作,有一些工具可以帮助您。

Haskell has a syntax sugar called do-notation exactly for this. This makes writing imperative programs in Haskell possible. There is a special tool in its compiler. It converts such imperative programs into a Monadic pure Haskell expressions. The expressions are close to math you see in textbooks.

为此,Haskell有一个称为do-notation的语法糖。 这使得在Haskell中编写命令式程序成为可能。 它的编译器中有一个特殊的工具。 它将这样的命令式程序转换为Monadic纯的Haskell表达式。 这些表达式接近您在教科书中看到的数学。

JavaScript is an imperative language. We can consider any imperative code to be a do-notation already. But unlike the one in Haskell’s, it is not abstract. It works only for built-in side effects. There is no way to add support of any new one except extending the language.

JavaScript是命令式语言。 我们可以认为任何命令式代码已经是一个do-notation。 但是与Haskell的不同,它不是抽象的。 它仅适用于内置副作用。 除了扩展语言之外,没有其他方法可以增加对任何新语言的支持。

There are such extensions, namely generators, async and async generator functions. JavaScipt JIT compiler converts async and generator functions into concrete built-in API calls. Haskell doesn’t need such extensions. Its compiler converts do-notation into abstract Monads interface function calls.

有这样的扩展,即生成器,异步和异步生成器功能。 JavaScipt JIT编译器将异步和生成器功能转换为具体的内置API调用。 Haskell不需要此类扩展。 它的编译器将do-notation转换为抽象的Monads接口函数调用。

Here is an example of how async functions simplify sources. This shows again why we need to bother having a syntax for effects.

这是异步功能如何简化源代码的示例。 这再次说明了为什么我们需要麻烦使用效果语法。

For this post, we need only two JavaScript built-in effects. Let’s call them Mutation and Exception. They have clear meanings. Mutations allow changing values of some references. JavaScript has the Exceptions effect embedded using throw/ try-catch statements.

对于本文,我们仅需要两个JavaScript内置效果。 我们称它们为Mutation和Exception。 它们具有明确的含义。 突变允许更改某些引用的值。 JavaScript具有使用throw / try-catch语句嵌入的“异常”效果。

We can convert some effects into others. This way we can write async code using Generators.

我们可以将某些效果转换为其他效果。 这样,我们可以使用Generators编写异步代码。

This conversion trick can be applied to other effects too. And apparently, just Mutation and Exception are enough to get any other effect. This means we can turn any plain function into an abstract do-notation already. And this is exactly what Suspense does.

此转换技巧也可以应用于其他效果。 显然,仅Mutation和Exception就足以获得任何其他效果。 这意味着我们已经可以将任何普通函数转换为抽象的do-notation。 而这正是Suspense所做的。

When the code encounters some effectful operation and requires suspension it throws an exception. It contains some details (for example a Promise object). One of its callers catches the exception, waits while the promise in the argument is settled, stores the resulting value in a cache, and re-runs the effectful function from the beginning.

当代码遇到一些有效的操作并需要暂停时,它将引发异常。 它包含一些细节(例如Promise对象)。 它的一个调用方捕获异常,等待参数中的promise结算后,将结果值存储在高速缓存中,然后从头开始重新运行有效的函数。

After the Promise is resolved the engine calls the function again. The execution goes from its start, and when it encounters the same operations it returns its value from the cache. It doesn’t throw an exception and continues execution until the next suspension request or the function’s exit. If the function doesn’t have any other side effects its execution should go the same paths and all pure expressions are recalculated producing the same values.

解决承诺后,引擎会再次调用该函数。 该执行从头开始,当遇到相同的操作时,它将从缓存中返回其值。 它不会引发异常,并继续执行,直到下一个暂停请求或函数退出为止。 如果该函数没有任何其他副作用,则其执行应走相同的路径,并重新计算所有纯表达式以产生相同的值。

Let’s re-implement Suspense. Unlike React, this one works with the abstract Monads interface. For simplicity, my implementation also hides a resource cache. Instead, the runner function counts invoked effects and uses the current counter value as a key for the internal cache. Here is the runner for the abstract interface:

让我们重新实现挂起。 与React不同,它与抽象的Monads接口一起使用。 为简单起见,我的实现还隐藏了资源缓存。 相反,运行器函数对调用的效果进行计数,并将当前计数器值用作内部缓存的键。 这是抽象接口的运行器:

/** effectful expression throws this object if it requires suspension */
const token = {};/** Pointer to mutable data used to record effectful computations */
let context;/** Runs `thunk()` as an effectful expression with `of` and `chain` as Monad's definition */
const run = (of, chain) => thunk => {/** here it caches effects requests */const trace = [];const ctx = {trace};return step();function step() {const savedContext = context;ctx.pos = 0;try {context = ctx;return of(thunk());} catch(e) {/** re-throwing other exceptions */if (e !== token)throw e;const {pos} = ctx;return chain(ctx.effect,(value) => {trace.length = pos;/* recording the resolved value */trace[pos] = value;ctx.pos = pos + 1;/** replay */return step(value);})} finally {context = savedContext;}}
}/** marks effectful expression */
const M = eff => {/* if the execution is in a replay stage the value will be cached */if (context.pos < context.trace.length)return context.trace[context.pos++];/* saving the expression to resolve in `run` */context.effect = eff;throw token;
}

Now let’s add a concrete Async effects implementation. Promises, unfortunately, aren’t exactly monads since one Monad law doesn’t hold for them, and it is a source of subtle problems, but they are still fine for our do-notation to work.

现在,让我们添加一个具体的异步效果实现。 不幸的是,由于一个Monad法律并不适用于此,因此Promises并非完全适用于Monad,这是一些细微问题的源头,但对于我们的注解工作而言,它们仍然是不错的选择。

Here is concrete Async effect implementation:

这是具体的异步效果实现:

const runPromise = run(v => Promise.resolve(v), (arg, f) => arg.then(f));

And here’s a simple example, it waits for delayed values before rendering proceeds:

这是一个简单的示例,它在渲染进行之前等待延迟的值:

The sandbox also contains Component wrapper. It turns an effectful functional component into a React component. It simply adds chain callback and updates the state accordingly. This version doesn’t have a fallback on threshold feature yet, but the last example here does have it.

沙箱还包含Component包装器。 它将有效的功能组件转换为React组件。 它仅添加chain回调并相应地更新状态。 此版本还没有回退阈值功能,但是这里的最后一个示例确实具有此功能。

The runner is abstract, so we can apply it for something else. Let’s try this for the useState hook. It is a Continuation monad, not a State monad as its name may suggest.

跑步者是抽象的,因此我们可以将其应用于其他事物。 让我们为useState挂钩尝试一下。 它是一个Continuation单子,而不是顾名思义的State monad。

Effectful value here is a function which takes a callback as an argument. This callback is called when the runner has some value to pass further. For example when the callback returned from useState is called.

有效值是将回调作为参数的函数。 当跑步者具有进一步传递的价值时,将调用此回调。 例如,当调用useState返回的回调时。

Here, for simplicity, I use single callback continuations. Promises have one more continuation for failure propagation.

在这里,为简单起见,我使用单个回调继续。 对于故障传播,承诺还有一个延续。

const runCont = run(value => cont => cont(value),(arg, next) => cont => arg(value => next(value)(cont)));const useState = initial =>M(cont => cont([initial, function next(value) { cont([value,next]); }]));

And here is a working usage example, with most of “kit.js” copy-pasted, except the monad’s definition.

这是一个有效的用法示例,除了monad的定义外,大多数“ kit.js”都已粘贴。

Unfortunately, this is not exactly the useState hook from React yet, and the next section shows why.

不幸的是,这还不是React的useState钩子,下一节将说明原因。

适用的注解 (Applicative do-notation)

There is another extension for do-notation in Haskell. It targets not only Monad abstract interface calls but also calls of Applicative Functors abstract interface.

Haskell中的do-notation有另一个扩展。 它不仅针对Monad抽象接口调用,而且还针对Applicative Functors抽象接口调用。

Applicative interfaces shares the of function with Monads and there is another function, let’s call it join. It takes an array of effectful values and returns a single effectful value resolving to an array. The resulting array contains all the values to which each element of the argument array was resolved.

应用型接口共享of功能与单子还有另外一个功能,让我们把它join 。 它需要一个有效值数组,并返回一个有效值解析为一个数组。 结果数组包含参数数组的每个元素都解析为的所有值。

I use a different one from Haskell’s interface. Both are equal though — it is simple to convert Haskell’s interface into the one used here and back. I do this because this basis is much simpler to use in JavaScript, it doesn’t need any higher-order functions, and there is already its instance in the standard runtime.

我使用与Haskell界面不同的界面。 两者是平等的-将Haskell的界面转换成此处使用的界面很简单。 我这样做是因为此基础在JavaScript中使用起来要简单得多,它不需要任何高阶函数,并且在标准运行时中已经有其实例。

In Haskell and in JavaScript any Monad is immediately an Applicative Functor. This means we don’t need to write a concrete implementation of Applicative interface, we can generate it automatically.

在Haskell和JavaScript中,任何Monad都立即成为应用函子。 这意味着我们不需要编写Applicative接口的具体实现,我们可以自动生成它。

If there is a default implementation, why do we need Applicative Functors? There are two reasons. The first one is not all Applicative Functors are Monads, so there is no chain method from which we can generate join. Another reason is, even if there is chain, custom join implementation can do the same thing in a different way, probably more efficiently. For example, fetching resources in parallel rather than sequentially.

如果有默认实现,为什么我们需要应用函子? 有两个原因。 第一个不是所有的应用函子都是Monad,因此没有chain方法可用来生成join 。 另一个原因是,即使有chain ,自定义join实现也可以以不同的方式来做同样的事情,可能更有效。 例如,以并行方式而不是顺序方式获取资源。

There is an instance of this interface for Promises in the standard runtime. It is Promise.all(ignoring some details here for simplicity again).

在标准运行时中,该接口有一个Promises接口的实例。 它就是Promise.all (为简单起见,这里不再赘述)。

Let’s now return to the state example. What if we add another counter in the component?

现在让我们回到状态示例。 如果我们在组件中添加另一个计数器怎么办?

The second counter now resets its value when the first one is incremented. It is not how Hooks are supposed to work. Both counters should keep their values and work in parallel.

现在,当第一个计数器递增时,第二个计数器将重置其值。 这不是胡克斯应该如何工作的。 两个计数器应保持其值并并行工作。

This happens because each continuation invocation erases everything after it in the code. When the first counter changes its value the whole next continuation is re-started from the beginning. And there, the second counter value is 0 again.

发生这种情况是因为每次继续调用都会删除代码中的所有内容。 当第一个计数器的值改变时,整个下一个继续从头开始重新开始。 在那里,第二个计数器值再次为0。

In the run function implementation, the invalidation happens at line 26 — trace.length = pos — this removes all the memorized values after the current one (at pos). Instead, we could try to diff/patch the trace instead. It would be an instance of Adaptive Monad used for incremental computations. MobX and similar libraries are very similar to this.

在运行功能实现中 ,该失效发生在第26行— trace.length = pos —这将删除当前值(在pos )中的所有存储值。 相反,我们可以尝试差异化/修补跟踪。 这将是用于增量计算的Adaptive Monad的一个实例。 MobX和类似的库与此非常相似。

If we invoke effectful operations only from a function’s top level, there are no branches or loops. Everything will be merged well overwriting the values on the corresponding positions, and this is exactly what Hooks do. Try to remove the line in the code sandbox for two counters above.

如果我们仅从函数的顶层调用有效的操作,则没有分支或循环。 一切都会很好地合并,覆盖相应位置上的值,而这正是Hooks所做的。 尝试删除上面两个计数器的代码沙箱中的行。

Transpiler替代品 (Transpiler alternative)

Using Hooks already makes programs more succinct, reusable and readable. Imagine what you could do if there were no limitations (Rules of Hooks). The limitations are due to runtime-only embedding. We can remove these limitations by means of a transpiler.

使用Hooks已经使程序更加简洁,可重用和可读。 想象一下,如果没有限制,该怎么办(《勾子规则》)。 限制是由于仅运行时嵌入。 我们可以通过转译器消除这些限制。

Effectful.JS is a transpiler for embedding effectful into JavaScipt. It supports both Monadic and Applicative targets. It greatly simplifies programs in the designing, implementing, testing, and maintaining stages.

Effectful.JS是用于将有效嵌入到JavaScipt中的编译器。 它同时支持Monadic和Applicative目标。 它极大地简化了设计,实施,测试和维护阶段的程序。

Unlike React Hooks and Suspense, the transpiler doesn’t need to follow any rules. It works for any JavaScript statement (branches, loops, exceptions etc). It never re-plays functions from the beginning. This is faster. Plus the functions can use any JavaScript built-in side effect.

与React Hooks和Suspense不同,编译器不需要遵循任何规则。 它适用于任何JavaScript语句(分支,循环,异常等)。 它从不从头开始重播功能。 这样更快。 加上这些功能可以使用任何JavaScript内置的副作用。

Effectful.JS is not exactly a transpiler but rather a tool to create transpilers. There are also a few predefined ones and a lot of options for tuning. It supports double-level syntax, with special markers for effectful values (like awaitexpressions in async functions, or Haskell’s do). And it also supports a single level syntax where this information is implicit (like Suspense, Hooks or languages with Algebraic Effects).

Effectful.JS并非完全是编译器,而是创建编译器的工具。 还有一些预定义的选项和许多调整选项。 它支持双层语法,并带有用于有效值的特殊标记(例如异步函数中的await表达式或Haskell的do)。 它还支持单级语法,其中该信息是隐式的(例如Suspense,Hooks或具有代数效果的语言)。

I’ve quickly built a Hooks-like transpiler for demo-purposes — @effectful/react-do. Calling a function with names starting with “use” is considered effectful. Functions are transpiled only if their name starts with “use” or they have “component” or “effectful” block directive (a string at the beginning of the function).

我已经为演示目的快速构建了一个类似于Hooks的编译器- @ effectful / react-do 。 以“ use”开头的名称调用函数被认为是有效的。 仅当函数的名称以“ use”开头或具有“ component”或“ effectful”块指令(函数开头的字符串)时,才对函数进行编译。

There are also “par” and “seq” block-level directives to switch between applicative and monadic targets. With “par” mode enabled the compiler analyzes variable dependencies and injects join instead of chain if possible.

还有“ par”和“ seq”块级指令可在应用目标和单子目标之间切换。 启用“ par”模式后,编译器将分析变量依赖关系,并在可能的情况下注入join而不是chain

Here is the example with two counters, but now adapted with the transpiler:

这是带有两个计数器的示例,但现在已与转译器一起使用:

For demo purposes, it also implements Suspense for Code Splitting. The whole function is six lines long. Check it out in the runtime implementation @effectful/react-do/main.js. In the next example, I’ve added another counter which rendering is artificially delayed for demo purposes.

出于演示目的,它还实现了暂挂代码拆分。 整个功能为六行。 在运行时实现@ effectful / react-do / main.js中进行检查 。 在下一个示例中,我添加了另一个计数器,出于演示目的,该计数器会人工延迟渲染。

代数效应 (Algebraic Effects)

Algebraic Effects are often mentioned along with Suspense and Hooks. These may be internals details or a modeling tool, but React doesn’t ship Algebraic Effects to its userland anyway.

经常提到“代数效应”以及“悬疑”和“挂钩”。 这些可能是内部细节或建模工具,但React始终不会将代数效果传递到其用户领域。

With access to Algebraic Effects, users could override operations behavior by using own Effect Handler. This works like exceptions with an ability to resume a computation after throw. Say, some library function throws an exception if some file doesn’t exist. Any caller function can override how it can handle it, either ignore or exit process, etc.

使用代数效果,用户可以使用自己的效果处理程序来覆盖操作行为。 这与异常类似,可以在throw之后恢复计算。 说,如果某些文件不存在,则某些库函数将引发异常。 任何调用方函数都可以覆盖其处理方式,可以忽略或退出进程等。

EffectfulJS doesn’t have built-in Algebraic Effects. But their implementation is a tiny runtime library on top of continuations or free monads.

EffectfulJS没有内置的代数效果。 但是它们的实现是在延续或免费monad之上的一个很小的运行时库。

Invoking a continuation also erases everything after the corresponding throw. There is also special syntax and typing rules to get Applicative (and Arrows) API — Algebraic Effects and Effect Handlers for Idioms and Arrows. Unline Applicative-do this prohibits using any anything which requires Monad operations.

调用延续会在相应的throw之后擦除所有内容。 还有一些特殊的语法和键入规则来获取Applicative(和Arrows)API — 习语和箭头的代数效果和效果处理程序 。 Unline Applicative-这样做禁止使用任何需要Monad操作的东西。

结语 (Wrapping up)

The transpiler is a burden, and it has its own usage cost. Like for any other tool, use it only if this cost is smaller than the value you get.

编译器是一个负担,它有自己的使用成本。 与其他任何工具一样,仅在此成本小于获得的价值时才使用它。

And you can achieve a lot with EffectfulJS. It is a new way to write JavaScript programs. It is useful for projects with complex business logic. Any complex workflow can be a simple maintainable script.

您可以使用EffectfulJS取得很多成就。 这是编写JavaScript程序的新方法。 这对于具有复杂业务逻辑的项目很有用。 任何复杂的工作流程都可以是简单的可维护脚本。

As an example, Effectful.JS can replace Suspense, Hooks, Context, and Components State with tiny functions. Error Boundaries are the usual try-catch statements. Async rendering is an async scheduler. But we can use it for any computations, not only for rendering.

例如,Effectful.JS可以用微小的功能代替Suspense,Hooks,Context和Components State。 错误边界是通常的try-catch语句。 异步渲染是一个异步调度程序。 但是我们可以将其用于任何计算,而不仅仅是渲染。

There are a lot of other awesome application-specific uses, and I’m going to write more about them soon. Stay tuned!

还有许多其他很棒的特定于应用程序的用途,我将在不久后写更多关于它们的内容。 敬请关注!

翻译自: https://www.freecodecamp.org/news/when-to-use-react-suspense-vs-react-hooks-f66ef94cb54f/

react hooks使用

react hooks使用_何时使用React Suspense和React Hooks相关推荐

  1. react 布局容器_如何使用容器模式开发React超能力

    react 布局容器 Hello everyone! ? 大家好! ? This time I'm going to tell you about this very useful pattern i ...

  2. react转跳_您跳过的这些React基础知识可能会杀死您

    react转跳 Often times, the inability to debug a certain error stems from not understanding some founda ...

  3. react在线文件_编程界“滥竽充数者”?React是否名不副实?

    全文共 3672字,预计学习时长 11分钟 图源:Aphinya Dechalert提供 年初,笔者试着真正使用了一回React库.由于对Angular有一定的了解,笔者对库中提出的概念保持开放包容的 ...

  4. react生命周期函数_如何优雅的消灭掉react生命周期函数

    开源不易,感谢你的支持,❤ star concent^_^ 序言 在react应用里,存在一个顶层组件,该组件的生命周期很长,除了人为的调用unmountComponentAtNode接口来卸载掉它和 ...

  5. react 流程图框架_【赠书】Preact(React)核心原理详解Preact(React) 核心原理解析...

    豆皮粉儿们,又见面了,今天这一期,由字节跳动数据平台的"winge(宝丁)",带大家见识见识前端"轮子"之一Preact框架. 提到Preact,你肯定会先想到 ...

  6. native react 折线图_【详解】纯 React Native 代码自定义折线图组件(译)

    本文为 Marno 翻译,转载必须保留出处! 公众号[ Marno ],关注后回复 RN 加入交流群 React Native 优秀开源项目大全:http://www.marno.cn 一.前言 在移 ...

  7. react 渲染道具_如何使用渲染道具模式开发React超能力

    react 渲染道具 Hey everybody! This time I'm going to tell you about this great superpower called "r ...

  8. react hooks使用_为什么要使用React Hooks?

    react hooks使用 The first thing you should do whenever you're about to learn something new is ask your ...

  9. react hooks使用_如何使用React和Hooks检测外部点击

    react hooks使用 by Andrei Cacio 通过安德烈·卡西奥(Andrei Cacio) 如何使用React和Hooks检测外部点击 (How to detect an outsid ...

最新文章

  1. 【转载】[BetterExplained]为什么你应该(从现在开始就)写博客
  2. 【sprinb-boot】配置文件分离打包
  3. spring学习(26):更优雅的依赖注入 在@bean注入参数
  4. python经典100例(21-40)
  5. leetcode —— 209. 长度最小的子数组
  6. $(obj).each 和 $.each() 区别
  7. java如何使用移位运算符_JAVA移位运算符使用教程
  8. Java 正则表达式的用法及常用方法
  9. [开源] PLC梯形图转指令表的算法源代码
  10. spring-webflux理解
  11. Android studio修改标题菜单栏增加功能图标(navigation bar toolbar)
  12. Windows 11系统映像恢复到新硬盘的3种方式
  13. 【Vue3.0 + Element-plus】el-tree树状结构节点前箭头样式修改
  14. 全栈Linux运维-Linux云计算运维与高级架构班课程 全新自动化运维必学课程
  15. 24张神GIF动图:数学概念这么酷!
  16. 源中瑞能源在线监测系统帮助企业降低能源损耗
  17. VMWare虚拟机设置固定IP
  18. 分布式存储系统(一) - 概念
  19. 《MetaSploit渗透测试魔鬼训练营》之环境搭建
  20. jdk1.8新特性 Lambda表达式和Stream集合操作(详细)

热门文章

  1. 线程放弃 java 1615477619
  2. 熟悉HTML基本标签的分类测试分析 1218
  3. 小货车DataAdapter对象 1129
  4. 连接查询 左连接 右连接 内连接 1112 sqlserver
  5. 阿里云Freeswtich部署
  6. CF666B. World Tour
  7. TensorFlow相关
  8. Python3 数据库连接
  9. 为什么拙劣的软件也会成功?
  10. [转]踏实从小事做起, 才能有大发展