导入和导出组件

组件的魔力在于它们的可重用性:你可以创建由其他组件组成的组件。但随着你嵌套的组件越来越多,通常有必要开始将它们拆分到不同的文件中。这可以让你的文件更易于浏览,并在更多地方重用组件。

🌐 The magic of components lies in their reusability: you can create components that are composed of other components. But as you nest more and more components, it often makes sense to start splitting them into different files. This lets you keep your files easy to scan and reuse components in more places.

你将学习到

  • 什么是根组件文件
  • 如何导入和导出组件
  • 何时使用默认和命名的导入和导出
  • 如何从一个文件导入和导出多个组件
  • 如何将组件拆分成多个文件

根组件文件

🌐 The root component file

你的第一个组件 中,你创建了一个 Profile 组件和一个渲染它的 Gallery 组件:

🌐 In Your First Component, you made a Profile component and a Gallery component that renders it:

function Profile() {
  return (
    <img
      src="https://i.imgur.com/MK3eW3As.jpg"
      alt="Katherine Johnson"
    />
  );
}

export default function Gallery() {
  return (
    <section>
      <h1>Amazing scientists</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  );
}

这些目前存放在一个根组件文件中,在本例中名为 App.js。不过,根据你的设置,你的根组件可能在另一个文件中。如果你使用基于文件路由的框架,例如 Next.js,那么你的根组件在每个页面都会不同。

🌐 These currently live in a root component file, named App.js in this example. Depending on your setup, your root component could be in another file, though. If you use a framework with file-based routing, such as Next.js, your root component will be different for every page.

导出和导入组件

🌐 Exporting and importing a component

如果你将来想更改启动屏幕,并在上面放一份科学书籍列表呢?或者把所有的个人资料放到其他地方呢?将 GalleryProfile 移出根组件文件是有道理的。这会使它们在其他文件中更模块化且可重用。你可以通过三个步骤来移动一个组件:

🌐 What if you want to change the landing screen in the future and put a list of science books there? Or place all the profiles somewhere else? It makes sense to move Gallery and Profile out of the root component file. This will make them more modular and reusable in other files. You can move a component in three steps:

  1. 创建一个新的 JS 文件来放置组件。
  2. 从该文件导出你的函数组件(使用默认命名导出)。
  3. **在你将使用该组件的文件中导入它(使用对应的技术导入默认命名导出)。

这里 ProfileGallery 都已从 App.js 移出,放入一个名为 Gallery.js 的新文件中。现在你可以将 App.js 改为从 Gallery.js 导入 Gallery

🌐 Here both Profile and Gallery have been moved out of App.js into a new file called Gallery.js. Now you can change App.js to import Gallery from Gallery.js:

import Gallery from './Gallery.js';

export default function App() {
  return (
    <Gallery />
  );
}

注意这个例子现在是如何分解成两个组件文件的:

🌐 Notice how this example is broken down into two component files now:

  1. Gallery.js:
    • 定义了 Profile 组件,该组件仅在同一文件中使用,并且不会导出。
    • Gallery 组件导出为默认导出。
  2. App.js:
    • Gallery.js 导入 Gallery 作为默认导入
    • 将根组件 App 导出为默认导出

注意

你可能会遇到省略了 .js 文件扩展名的文件,如下所示:

🌐 You may encounter files that leave off the .js file extension like so:

import Gallery from './Gallery';

无论是 './Gallery.js' 还是 './Gallery' 都可以与 React 一起使用,尽管前者更接近 原生 ES 模块 的工作方式。

🌐 Either './Gallery.js' or './Gallery' will work with React, though the former is closer to how native ES Modules work.

深入研究

默认与命名导出

🌐 Default vs named exports

使用 JavaScript 导出值有两种主要方式:默认导出和命名导出。到目前为止,我们的示例只使用了默认导出。但你可以在同一个文件中使用其中一种或两种。一个文件最多只能有一个 默认 导出,但可以有任意多个 命名 导出。

🌐 There are two primary ways to export values with JavaScript: default exports and named exports. So far, our examples have only used default exports. But you can use one or both of them in the same file. A file can have no more than one default export, but it can have as many named exports as you like.

Default and named exports

你如何导出组件决定了你必须如何导入它。如果你尝试以导入命名导出的方式导入默认导出,你会遇到错误!这个图表可以帮助你跟踪:

🌐 How you export your component dictates how you must import it. You will get an error if you try to import a default export the same way you would a named export! This chart can help you keep track:

语法导出语句导入语句
默认export default function Button() {}import Button from './Button.js';
命名export function Button() {}import { Button } from './Button.js';

当你编写一个 default 导入时,你可以在 import 之后放任何你想要的名字。例如,你可以写 import Banana from './Button.js',它仍然会为你提供相同的默认导出。相比之下,对于命名导入,名称必须在两边匹配。这就是它们被称为 named 导入的原因!

🌐 When you write a default import, you can put any name you want after import. For example, you could write import Banana from './Button.js' instead and it would still provide you with the same default export. In contrast, with named imports, the name has to match on both sides. That’s why they are called named imports!

如果文件只导出一个组件,人们通常使用默认导出;如果导出多个组件和数值,则使用命名导出。 无论你更喜欢哪种编码风格,总是要为你的组件函数及其所在的文件赋予有意义的名称。像 export default () => {} 这样的无名组件是不推荐的,因为它们会使调试更加困难。

从同一文件导出和导入多个组件

🌐 Exporting and importing multiple components from the same file

如果你只想显示一个 Profile 而不是图库怎么办?你也可以导出 Profile 组件。但是 Gallery.js 已经有一个默认导出,你不能有两个默认导出。你可以创建一个带有默认导出的新文件,或者为 Profile 添加一个命名导出。一个文件只能有一个默认导出,但可以有多个命名导出!

🌐 What if you want to show just one Profile instead of a gallery? You can export the Profile component, too. But Gallery.js already has a default export, and you can’t have two default exports. You could create a new file with a default export, or you could add a named export for Profile. A file can only have one default export, but it can have numerous named exports!

注意

为了减少默认导出和命名导出之间可能的混淆,一些团队选择只坚持使用一种风格(默认或命名),或者避免在同一个文件中混合使用它们。做最适合你的方式!

🌐 To reduce the potential confusion between default and named exports, some teams choose to only stick to one style (default or named), or avoid mixing them in a single file. Do what works best for you!

首先,使用命名导出(不使用 default 关键字)从 Gallery.js 导出 Profile

🌐 First, export Profile from Gallery.js using a named export (no default keyword):

export function Profile() {
// ...
}

然后,使用命名导入(带大括号)从 Gallery.js 导入 ProfileApp.js

🌐 Then, import Profile from Gallery.js to App.js using a named import (with the curly braces):

import { Profile } from './Gallery.js';

最后,从 App 组件 渲染 <Profile />

🌐 Finally, render <Profile /> from the App component:

export default function App() {
return <Profile />;
}

现在 Gallery.js 包含两个导出:一个默认的 Gallery 导出,以及一个命名的 Profile 导出。App.js 导入了它们两者。在这个示例中尝试将 <Profile /> 编辑为 <Gallery /> 然后再改回去:

🌐 Now Gallery.js contains two exports: a default Gallery export, and a named Profile export. App.js imports both of them. Try editing <Profile /> to <Gallery /> and back in this example:

import Gallery from './Gallery.js';
import { Profile } from './Gallery.js';

export default function App() {
  return (
    <Profile />
  );
}

现在你混合使用默认导出和命名导出:

🌐 Now you’re using a mix of default and named exports:

  • Gallery.js:
    • Profile 组件以名为 Profile具名导出方式导出。
    • Gallery 组件导出为默认导出。
  • App.js:
    • Gallery.js 导入 Profile,作为一个 命名导入,称为 Profile
    • Gallery.js 导入 Gallery 作为默认导入
    • 将根组件 App 导出为默认导出

回顾

在此页面上,你了解到:

🌐 On this page you learned:

  • 什么是根组件文件
  • 如何导入和导出组件
  • 何时以及如何使用默认和命名的导入和导出
  • 如何从同一个文件中导出多个组件

挑战 1 of 1:
进一步拆分组件

🌐 Split the components further

目前,Gallery.js 同时导出 ProfileGallery,这有点令人困惑。

🌐 Currently, Gallery.js exports both Profile and Gallery, which is a bit confusing.

Profile 组件移到它自己的 Profile.js 中,然后将 App 组件更改为依次渲染 <Profile /><Gallery />

🌐 Move the Profile component to its own Profile.js, and then change the App component to render both <Profile /> and <Gallery /> one after another.

你可以为 Profile 使用默认导出或命名导出,但请确保在 App.jsGallery.js 中使用相应的导入语法!你可以参考上面的深入解析表格:

🌐 You may use either a default or a named export for Profile, but make sure that you use the corresponding import syntax in both App.js and Gallery.js! You can refer to the table from the deep dive above:

语法导出语句导入语句
默认export default function Button() {}import Button from './Button.js';
命名export function Button() {}import { Button } from './Button.js';
// Move me to Profile.js!
export function Profile() {
  return (
    <img
      src="https://i.imgur.com/QIrZWGIs.jpg"
      alt="Alan L. Hart"
    />
  );
}

export default function Gallery() {
  return (
    <section>
      <h1>Amazing scientists</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  );
}

在你让它与一种导出一起工作之后,让它与另一种导出一起工作。

🌐 After you get it working with one kind of exports, make it work with the other kind.