React + TypeScript 组件的状态和构造 React Hooks

What is component state?

In React, component state refers to the internal data held by a component. It represents the mutable values that can be used within the component and can be updated over time. State allows components to keep track of information that can change, such as user input, API responses, or any other data that needs to be dynamic and responsive.State is a feature provided by React that enables components to manage and update their own data. It allows components to re-render when the state changes, ensuring that the user interface reflects the latest data.To define state in a React component, you should use the useState hook inside of component. You can then access and modify the state within the component's methods or JSX code. When the state is updated, React will automatically re-render the component and its child components to reflect the changes. Before jump to examples of using state in components, let's briefly explore what is React Hook.

在React中,组件状态指的是组件持有的内部数据。它表示可在组件中使用并随时间更新的可变值。State允许组件跟踪可能更改的信息,例如用户输入、API响应或任何其他需要动态响应的数据。State是React提供的一个特性,它使组件能够管理和更新自己的数据。它允许组件在状态改变时重新呈现,确保用户界面反映最新的数据。要在React组件中定义状态,你应该在组件内部使用useState钩子。然后,您可以在组件的方法或JSX代码中访问和修改状态。当状态更新时,React将自动重新呈现组件及其子组件以反映更改。在跳转到在组件中使用状态的示例之前,让我们简单地探索一下什么是React Hook。

React Hooks

React Hooks are a feature introduced in React 16.8 that allow you to use state and other React features in functional components. Before hooks, state management and lifecycle methods were primarily used in class components. Hooks provide a way to achieve similar functionality in functional components, making them more powerful and easier to write and understand.Hooks are functions that enable you to "hook into" React's internal features, such as state management, context, effects, and more. They are prefixed with the use keyword (e.g., useState, useEffect, useContext, etc.). React provides several built-in hooks, and you can also create custom hooks to encapsulate reusable stateful logic.The most commonly used built-in hooks are:

React Hooks是在React 16.8中引入的一个特性,它允许你在功能组件中使用状态和其他React特性。在钩子出现之前,状态管理和生命周期方法主要用于类组件。Hooks提供了一种在函数式组件中实现类似功能的方法,使它们更强大,更易于编写和理解。Hooks是允许你"hook到"React内部特性的函数,比如状态管理、上下文、效果等等。它们以use关键字作为前缀(例如,useState, useEffect, useContext等)。React提供了几个内置钩子,您还可以创建自定义钩子来封装可重用的有状态逻辑。最常用的内置钩子有:

  • useState: This hook allows you to add state to a functional component. It returns an array with two elements: the current state value and a function to update the state. 这个钩子允许你向功能组件添加状态。它返回一个包含两个元素的数组:当前状态值和更新状态的函数。
  • useEffect: This hook lets you perform side effects in your components, such as fetching data, subscribing to events, or manually manipulating the DOM. It runs after every render by default and can be used to handle component lifecycle events like when component was mount, updated or unmount. 该钩子允许您在组件中执行副作用,例如获取数据、订阅事件或手动操作DOM。默认情况下,它在每次渲染之后运行,并可用于处理组件生命周期事件,例如组件安装,更新或卸载。
  • useContext: This hook allows you to consume values from a React context. It provides a way to access context values without nesting multiple components. 这个钩子允许你从React上下文中消费值。它提供了一种无需嵌套多个组件即可访问上下文值的方法。
  • useCallback and useMemo: These hooks are used for performance optimization. useCallback memoizes a function, preventing it from being recreated on every render, while useMemo memoizes a value, recomputing it only when its dependencies change. 这些钩子用于性能优化。useCallback记忆一个函数,防止在每次渲染时重新创建它,而useMemo记忆一个值,只在它的依赖项改变时重新计算它。

We will examine all hooks in this chapter and will use them during all book. Let's continue with state and explore how we can manage it with useState hook.

我们将在本章中检查所有的钩子,并在整本书中使用它们。让我们继续讨论状态,并探索如何使用useState钩子来管理它。

Maintaining state using Hooks

The first React Hook API that we'll look at is called useState , which enables your functional React components to be stateful. In this section, you'll learn how to initialize state values and change the state of a component using Hooks.

我们要看的第一个React Hook API是useState,它使你的功能React组件是有状态的。在本节中,您将学习如何使用Hooks初始化状态值并更改组件的状态。

Initial state values

When our components are first rendered, they probably expect some state values to be set. This is called the initial state of the component, and we can use the useState Hook to set the initial state. Let's take a look at an example:

当我们的组件第一次呈现时,它们可能希望设置一些状态值。这被称为组件的初始状态,我们可以使用useState钩子来设置初始状态。让我们来看一个例子:

新增:src/MyComponent5.tsx

tsx 复制代码
import { useState } from "react";

export default function MyComponent5() {
  const [name, setName] = useState("张三");
  const [age, setAge] = useState(23);

  const onUpdateName = () => {
    setName("李四");
  };
  const onUpdateAge = () => {
    setAge(33);
  };

  return (
    <div>
      <div>
        姓名:{name}
        <button onClick={onUpdateName}>修改姓名</button>
      </div>
      <div>
        年龄:{age}
        <button onClick={onUpdateAge}>修改年龄</button>
      </div>
    </div>
  );
}

The App component is a functional React component that returns JSX markup. But it's also now a stateful component, thanks to the useState Hook. This example initializes two pieces of state, name and age. This is why there are two calls to useState , one for each state value.You can have as many pieces of state in your component as you need. The best practice is to have one call to useState per state value. You could always define an object as the state of your component using only one call to useState , but this complicates things because you have to access state values through an object instead of directly. Updating state values is also more complicated using this approach. When in doubt, use one useState Hook per state value.When we call useState , we get an array returned to us. The first value of this array is the state value itself. Since we've used arraydestructuring syntax here, we can call the value whatever we want; in this case, it is name and age. Both of these constants have values when the component is first rendered because we passed the initial state values for each of them to useState .

App组件是一个返回JSX标记的功能性React组件。但由于useState钩子,它现在也是一个有状态组件。这个例子初始化了state的两个部分:name和age。这就是对useState有两个调用的原因,每个调用一个状态值。您可以根据需要在组件中拥有任意多的状态片段。最佳实践是对每个状态值调用一次useState。您总是可以只调用一次useState就将对象定义为组件的状态,但这会使事情变得复杂,因为您必须通过对象而不是直接访问状态值。使用这种方法更新状态值也更加复杂。当有疑问时,为每个状态值使用一个useState钩子。当我们调用useState时,我们得到一个返回给我们的数组。这个数组的第一个值是状态值本身。由于我们在这里使用了数组解构语法,我们可以调用任何我们想要的值;在本例中,它是姓名和年龄。这两个常量在首次呈现组件时都有值,因为我们将它们的初始状态值传递给了useState。

Now that you've seen how to set the initial state values of your components, let's learn about updating these values.

现在您已经了解了如何设置组件的初始状态值,接下来让我们学习如何更新这些值。

Updating state values

React components use state for values that change over time. The state values used by components start off in one state, as we saw in the previous section, and then change in response to some event -- for example, the server responds to an API request with new data, or the user has clicked a button or changed a form field.To update the state, useState hook provides an individual function for every peace of states, that we can access from returned array from useState hook. The first item is the state value and the second is the function used to update the value. Let's take a look at an example:

React组件使用state来表示随时间变化的值。组件使用的状态值从一个状态开始,就像我们在上一节看到的那样,然后在响应某个事件时发生变化------例如,服务器用新数据响应API请求,或者用户单击了一个按钮或更改了一个表单字段。为了更新状态,useState钩子为每个和平状态提供了一个单独的函数,我们可以从useState钩子返回的数组中访问该函数。第一项是状态值,第二项是用于更新该值的函数。让我们来看一个例子:

tsx 复制代码
import { useState } from "react";

export default function MyComponent5() {
  const [name, setName] = useState("张三");
  const [age, setAge] = useState(23);

  const onUpdateName = () => {
    setName("李四");
  };
  const onUpdateAge = () => {
    setAge(33);
  };

  return (
    <div>
      <div>
        姓名:{name}
        <button onClick={onUpdateName}>修改姓名</button>
      </div>
      <div>
        年龄:{age}
        <button onClick={onUpdateAge}>修改年龄</button>
      </div>
    </div>
  );
}

Just like the example from the Initial state values section, the App component in this example has two pieces of state -- name and age . Unlike the previous example, this component uses two functions to update each piece of state. These are returned from the call to useState . Let's take a closer look:

就像初始状态值一节中的例子一样,本例中的App组件有两部分状态------名称和年龄。与前面的示例不同,该组件使用两个函数来更新每个状态片段。这些是从调用useState返回的。让我们仔细看看:

tsx 复制代码
const [name, setName] = useState("张三");
const [age, setAge] = useState(23);

Now, we have two functions -- setName and setAge -- that can be used to update the state of our component. Let's take a look at the text input field that updates the name state:

现在,我们有了两个函数setName和setAge可以用来更新组件的状态。让我们看一下更新name状态的文本输入字段:

tsx 复制代码
const onUpdateName = () => {
  setName("李四");
};
const onUpdateAge = () => {
  setAge(33);
};

Whenever the user changes the text in the input field, the onChange event is triggered. The handler for this event calls setName , passing it e.target.value as an argument. The argument passed to setName is the new state value of name. The succeeding paragraph shows that the text input is also updated with the new name value every time the user changes the text input.Next, let's look at the age number input field and how this value is passed to setAge :

每当用户更改输入字段中的文本时,就会触发onChange事件。此事件的处理程序调用setName,并将e.target.value作为参数传递给它。传递给setName的参数是name的新状态值。接下来的段落显示,每次用户更改文本输入时,文本输入也会使用新的name值进行更新。接下来,让我们看看年龄号输入字段,以及这个值是如何传递给setAge的:

html 复制代码
<div>
  <div>
    姓名:{name}
    <button onClick={onUpdateName}>修改姓名</button>
  </div>
  <div>
    年龄:{age}
    <button onClick={onUpdateAge}>修改年龄</button>
  </div>
</div>

The age field follows the exact same pattern as the name field. The only difference is that we've made the input a number type. Any time the number changes, setAge is called with the updated value in response to the onChange event. The following paragraph shows that the number input is also updated with every change that is made to the age state.

年龄字段遵循与姓名字段完全相同的模式。唯一的区别是我们把输入变成了数字类型。每当数字发生变化时,setAge就会调用更新后的值来响应onChange事件。下面的段落显示,每次对年龄状态进行更改时,数字输入也会更新。

In this section, you learned about the useState Hook, which is used to add state to functional React components. Each piece of state uses its own Hook and has its own value variable and its own setter function. This greatly simplifies accessing and updating state in your components. Any given state value should have an initial value so that the component can render correctly the first time. To re-render functional components that use state Hooks, you can use the setter functions that useState returns to update your state values as needed.The next Hook that you'll learn about is used to perform initialization and cleanup actions.

在本节中,您学习了useState钩子,它用于向功能性React组件添加状态。每个状态块都使用自己的Hook,有自己的value变量和setter函数。这极大地简化了访问和更新组件中的状态。任何给定的状态值都应该有一个初始值,这样组件才能在第一次正确呈现。要重新呈现使用状态钩子的功能组件,可以使用useState返回的setter函数根据需要更新状态值。您将学习的下一个Hook用于执行初始化和清理操作。

Performing initialization and cleanup actions

Often, our React components need to perform actions when the component is created. For example, a common initialization action is to fetch API data that the component needs. Another common action is to make sure that any pending API requests are canceled when the component is removed. In this section, you'll learn about the useEffect Hook and how it can help you with these two scenarios. You'll also learn how to make sure that the initialization code doesn't run too often.

通常,我们的React组件需要在创建组件时执行操作。例如,一个常见的初始化操作是获取组件所需的API数据。另一个常见的操作是确保在删除组件时取消所有挂起的API请求。在本节中,您将了解useEffect钩子以及它如何帮助您处理这两个场景。您还将了解如何确保初始化代码不会太频繁地运行。

Fetching component data

The useEffect Hook is used to run "side-effects" in your component. Another way to think about side-effect code is that functional components have only one job -- returning JSX content to render. If the component needs to do something else, such as fetching API data, this should be done in a useEffect Hook. For example, if you were to just make the API call as part of your component function, you would likely introduce race conditions and other difficult-to-fix buggy behavior.Let's take a look at an example that fetches API data using Hooks:

useEffect钩子用于在组件中运行"副作用"。考虑副作用代码的另一种方式是,功能组件只有一个任务,返回要呈现的JSX内容。如果组件需要做其他事情,比如获取API数据,就应该在useEffect钩子中完成。例如,如果您只是将API调用作为组件函数的一部分,则可能会引入竞争条件和其他难以修复的错误行为。让我们来看一个使用Hooks获取API数据的例子:

tsx 复制代码
import {useCallback, useEffect, useState} from "react";

interface User {
    id: number
    name: string
    age: number
}

export default function MyComponent6() {
    const [id, setId] = useState(1);
    const [name, setName] = useState("张三");
    const [age, setAge] = useState(23);

    const fetchUser = useCallback(() => {
        return new Promise<User>((resolve) => {
            setTimeout(() => {
                resolve({"id": 1, "name": "张三111", "age": 233} as User)
            }, 1000)
        })
    }, [])

    useEffect(() => {
        fetchUser().then((user) => {
            setId(user.id)
            setName(user.name)
            setAge(user.age)
        })
    })
    const onUpdateName = () => {
        setName("李四");
    };
    const onUpdateAge = () => {
        setAge(33);
    };

    return (
        <div>
            <div>
                ID: {id}
            </div>
            <div>
                姓名:{name}
                <button onClick={onUpdateName}>修改姓名</button>
            </div>
            <div>
                年龄:{age}
                <button onClick={onUpdateAge}>修改年龄</button>
            </div>
        </div>
    );
}

The useEffect Hook expects a function as an argument. This function is called after the component finishes rendering, in a safe way that doesn't interfere with anything else that React is doing with the component under the covers. Let's look at the pieces of this example more closely, starting with the mock API function:

useEffect钩子需要一个函数作为参数。这个函数在组件完成渲染后被调用,以一种安全的方式,不会干扰React在幕后对组件所做的任何事情。让我们更仔细地看看这个例子的各个部分,从模拟API函数开始:

tsx 复制代码
const fetchUser = useCallback(() => {
  return new Promise<User>((resolve) => {
    setTimeout(() => {
      resolve({"id": 1, "name": "张三111", "age": 233} as User)
    }, 1000)
  })
}, [])

The fetchUser function is defined using the useCallback hook. This hook is used to memoize the function, meaning that it will only be created once and will not be recreated on subsequent renders unless the dependencies change. The useCallback accepts two arguments, first is function we want to memorize and second is list of dependencies that will be use to identify when React should re-create this function instead of using memorized version. The fetchUser function is passed an empty array ( [] ) as the dependency list. This means that the function will only be created once during the initial render and won't be recreated on subsequent renders.The fetchUser function returns a promise. The promise resolves a simple object with two properties, id and name . ThesetTimeout function delays the promise resolution for 1 second, so this function is asynchronous, just as a normal fetch call would be.Next, let's look at the Hooks used by the App component:

fetchUser函数是使用useCallback钩子定义的。这个钩子用于记忆函数,这意味着它只会被创建一次,并且不会在后续渲染中重新创建,除非依赖项发生了变化。useCallback接受两个参数,第一个是我们想要记住的函数,第二个是依赖项列表,它将用于识别React何时应该重新创建这个函数,而不是使用记住的版本。fetchUser函数被传递一个空数组([])作为依赖列表。这意味着该函数将只在初始渲染期间创建一次,不会在后续渲染中重新创建。fetchUser函数返回一个承诺。promise解析一个具有两个属性的简单对象:id和name。settimeout函数将promise解析延迟1秒,所以这个函数是异步的,就像普通的fetch调用一样。接下来,让我们看看App组件使用的hook:

tsx 复制代码
const [id, setId] = useState(1);
const [name, setName] = useState("张三");
const [age, setAge] = useState(23);

useEffect(() => {
  fetchUser().then((user) => {
    setId(user.id)
    setName(user.name)
    setAge(user.age)
  })
})

As you can see, in addition to useCallback we're using two Hooks in this component -- useState and useEffect . Combining Hook functionality like this is powerful and encouraged. First, we set up the id and name states of the component. Then, useEffect is used to set up a function that calls fetchUser and sets the state of our component when the promise resolves.

正如你所看到的,除了useCallback,我们还在这个组件中使用了两个钩子,useState和useEffect。像这样结合Hook功能是非常强大且值得鼓励的。首先,我们设置组件的id和名称状态。然后,使用useEffect来设置一个函数,该函数调用fetchUser,并在承诺解析时设置组件的状态。

After 1 second, the promise returned from fetchUser is resolved with data from the API, which is then used to update the ID and name states.

1秒后,fetchUser返回的promise被解析为来自API的数据,然后用于更新ID和名称状态。

There is a good chance that your users will navigate around your application while an API request is still pending. The useEffect Hook can be used to deal with canceling these requests.

当API请求仍未处理时,您的用户很有可能在应用程序中导航。useEffect钩子可以用来处理取消这些请求。

Canceling actions and resetting state

There's a good chance that, at some point, your users will navigate your app and cause components to unmount before responses to their API requests arrive. Sometimes your component can listen some events and we should delete all listeners before unmount component to avoid memory leaks. In general its important to stop performing any background actions when related component was deleted from the screen.Thankfully, the useEffect Hook has a mechanism to clean up effects such as pending setInterval when the component is removed. Let's take a look at an example of this in action:

很有可能,在某个时候,你的用户会浏览你的应用程序,并导致组件在对他们的API请求的响应到达之前卸载。有时候你的组件可以监听一些事件,我们应该在卸载组件之前删除所有的监听器,以避免内存泄漏。一般来说,当相关组件从屏幕上删除时,停止执行任何后台操作是很重要的。值得庆幸的是,当组件被移除时,useEffect钩子有一个机制来清理挂起的setInterval等效果。让我们来看一个实际的例子:

新增: src/Timer.tsx

tsx 复制代码
import {useEffect, useState} from "react";

export default function Timer() {
    const [timer, setTimer] = useState(100);
    useEffect(() => {
        const interval = setInterval(() => {
            setTimer(prevTimer => prevTimer === 0 ? 0 : prevTimer - 1)
        }, 1000)

        // 返回的函数会在组件卸载之前自动执行
        return ()=>{
            clearInterval(interval);
        }
    }, [])

    return <p>Timer:{timer}</p>
}

This a simple Timer component. It has the state timer , and it setup interval callback to update timer inside useEffect() , and it renders the output with the current timer value. Let's take a closer look at the useEffect() Hook:

这是一个简单的Timer组件。它有状态计时器,并且在useEffect()中设置了间隔回调来更新计时器,并且用当前计时器值呈现输出。让我们仔细看看useEffect()钩子:

tsx 复制代码
useEffect(() => {
  const interval = setInterval(() => {
    setTimer(prevTimer => prevTimer === 0 ? 0 : prevTimer - 1)
  }, 1000)

  // 返回的函数会在组件卸载之前自动执行
  return ()=>{
    clearInterval(interval);
  }
}, [])

This effect creates a interval timer by calling the setInterval function with callback which updates our timer state. Interesting thing you can notice here, to setTimer function we are passing callback instead of number. It's a valid React API, when we need previous state value to use to calculate new one, we can pass callback where first argument is current or "previous" state value and we should return the new state value from this callback to update our state.Inside useEffect we are also returns a function, which React runs when the component is removed. In this example, the interval that is created by calling setInterval is cleared by calling function that we returned from useEffect where we call clearInterval . Functions that you return from useEffect will be triggered when component is going to unmount.Now, let's look at the App component, which renders and removes the Timer component:

这个效果通过调用setInterval函数来创建一个间隔计时器,该函数带有更新计时器状态的回调函数。有趣的是,在setTimer函数中我们传递的是callback而不是number。这是一个有效的React API,当我们需要以前的状态值来计算新的状态时,我们可以通过回调,其中第一个参数是当前或"以前"的状态值,我们应该从这个回调中返回新的状态值来更新我们的状态。在useEffect中,我们也返回一个函数,当组件被移除时React会运行这个函数。在这个例子中,通过调用setInterval创建的间隔通过调用从useEffect返回的函数来清除,我们调用clearInterval。当组件要卸载时,从useEffect返回的函数将被触发。现在,让我们看看App组件,它渲染并移除Timer组件:

新增:MyComponent8.tsx

tsx 复制代码
import Timer from "./Timer.tsx";
import {useState} from "react";

export default function MyComponent8() {
    const [show, setShow] = useState(false);

    return (
        <div>
            <div>
                {show ? <Timer/> : ''}
            </div>
            <button onClick={() => setShow(!show)}>切换</button>
        </div>
    );
}

Optimizing side-effect actions

By default, React assumes that every effect that is run needs to be cleaned up and it should be run on every render. This typically isn't the case. For example, you might have specific property or state values that require cleanup and run one more time when they change. You can pass an array of values to watch as the second argument to useEffect -- for example, if you have a resolved state that requires cleanup when it changes, you would write your effect code like this:

默认情况下,React假定运行的每个效果都需要清理,并且应该在每次渲染时运行。但通常情况并非如此。例如,您可能具有需要清理的特定属性或状态值,并在它们更改时再次运行。你可以传递一个值数组作为useEffect的第二个参数------例如,如果你有一个已解决的状态,当它改变时需要清理,你可以这样写你的效果代码:

tsx 复制代码
const [resolved, setResolved] = useState(false);
useEffect(() => {
  // ...the effect code...
  return () => {
  	// ...the cleanup code
  };
}, [resolved]);

In this code, effect will be triggered only ever run if the resolved state value changes. If the effect runs and the resolved state hasn't changed, then the cleanup code will not run and original effect code will not run second time as well. Another common case is to never run the cleanup code, except for when the component is removed. In fact, this is what we want to happen in the example from the section of fetching user data. Right now, the effect runs after every render. This means that we're repeatedly fetching the user API data when all we really want is to fetch it once when the component is first mounted.Let's make some modifications to the App component from the fetching component data requests example:

在这段代码中,只有在解析状态值发生变化时才会触发效果。如果效果运行并且解析状态没有改变,那么清理代码将不会运行,原始效果代码也不会第二次运行。另一种常见的情况是永远不运行清理代码,除非组件被删除。事实上,这就是我们想要在获取用户数据部分的示例中发生的事情。现在,效果在每次渲染后运行。这意味着,当我们真正想要的是在组件第一次挂载时获取一次用户API数据时,我们却在重复地获取它。让我们从抓取组件数据请求的例子中对App组件做一些修改:

tsx 复制代码
React.useEffect(() => {
  fetchUser().then((user) => {
    setId(user.id);
    setName(user.name);
  });
}, []);

We've added a second argument to useEffect , an empty array. This tells React that there are no values to watch and that we only want to run the effect once it rendered and cleanup code when the component is removed. We've also addedconsole.count('fetching user') to the fetchUser function. This makes it easier to look at the browser dev tools console and make sure that our component data is only fetched once. If you remove the [] argument that is passed to useEffect , you'll notice that fetchUser is called several times.In this section, you learned about side effects in React components. Effects are an important concept, as they are the bridge between your React components and the outside world. One of the most common use cases for effects is to fetch data that the component needs, when it is first created, and then clean up after the component when it is removed.Now, we're going to look at another way to share data with React components -- context.

我们给useEffect添加了第二个参数,一个空数组。这告诉React,没有任何值需要观察,我们只想在渲染后运行效果,并在删除组件时清理代码。我们还添加了控制台。count('取回用户')到fetchUser函数。这样可以更容易地查看浏览器开发工具控制台,并确保组件数据只被获取一次。如果您删除传递给useEffect的[]参数,您会注意到fetchUser被调用了几次。在本节中,您了解了React组件中的副作用。效果是一个重要的概念,因为它们是React组件与外部世界之间的桥梁。效果最常见的用例之一是在组件第一次创建时获取组件所需的数据,然后在组件被删除后进行清理。现在,我们来看看与React组件共享数据的另一种方式------上下文。

相关推荐
正小安26 分钟前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch2 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光2 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   2 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   2 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web2 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常2 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇3 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr3 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho4 小时前
【TypeScript】知识点梳理(三)
前端·typescript