如何使用Next.js与TypeScript协同工作(附实例)

1,819 阅读7分钟

Using Next.js with TypeScript

Next.js是一个开源框架,旨在与React一起工作。它被用来构建登陆页面、SEO友好型网站、电子商务商店,以及需要快速、高性能加载时间的各种网络应用。另一方面,TypeScript是一种建立在JavaScript上的编程语言,Next.js支持这种语言。这两者结合起来,为用户和开发者提供了更好的体验

Next.js和TypeScript主要分别被归类为全栈框架和模板语言及扩展工具,但让我们来看看两者应用内容和方式,以及它们如何协同工作,包括其应用实例。


目录

什么是Next.js
Next.js用来做什么
使用Next.js进行客户端数据
什么是TypeScript?
使用TypeScript做什么?
如何在NextJS中安装TypeScript


什么是 Next.js?

Next.js 是由 Vercel 创建的一个开源框架。它声称自己是 Web 的软件开发工具包,拥有 "使 Web 更快"(原文如此)所需的所有工具。更快"(原文如此)。在这里了解Next.js与React的功能及其应用。

Next.js有什么用处?

Next.js使搜索引擎能够轻松地优化React应用程序,而无需配置。传统的React应用是在客户端渲染的,浏览器从一个缺乏任何渲染内容的HTML页面的外壳开始。从那里,浏览器获取包含React代码的JavaScript文件,将内容渲染到页面上,并使其具有互动性。然而,客户端渲染有两个主要缺点。

1.内容不能被所有搜索引擎可靠地索引,也不能被社交媒体链接机器人读取;
2. 当用户第一次登陆网页时,可能需要更长的时间才能到达第一个内容丰富的油漆。

Next.js是一个框架,它允许你建立一个React应用程序,但在服务器上提前渲染内容,所以用户或搜索机器人首先看到的是完全渲染的HTML。在收到这个初始页面后,客户端渲染就会接管,它就像一个传统的React应用一样工作。**这是两个世界中最好的:**为机器人提供完全渲染的内容,为用户提供高度互动的内容。

使用Next.js进行客户端数据获取

当我们谈论数据获取时,真正的魔力开始发挥作用,因为Next.js可以从一个项目中执行多种服务器渲染策略。当你的页面不需要SEO索引时,当你不需要预渲染数据时,或者当你的页面内容需要经常更新时,客户端数据获取就很有用。静态生成或预渲染允许你在构建时渲染你的页面。请看下面的例子:

export default function IndexPage() {
  const [loading, setLoading] = useState(true);
  const [postList, setPostList] = useState([]);
  useEffect(() => {
    const fetchData = async () => {
      const res = await fetch(API_URL);
      const posts: IPost[] = await res.json();
      setPostList(posts);
      setLoading(false);
    };
    fetchData();
  }, []);
  //...

在前面的代码片段中可以看到一个使用React的useEffect 钩子进行客户端数据获取的例子。

首先,我们初始化了一些常量来检查获取是否仍在进行,并保存获取过程中产生的数据。然后,我们用两个不同的参数调用useEffect 钩子。

  • 回调--包含副作用登录的函数,在变化被推送到DOM后立即运行。在我们的案例中,其逻辑是从一个端点获取数据并将其保存到我们的常量中。
  • Dependencies- 一个依赖数组,允许指定回调何时运行。通过传递一个空数组,这意味着我们希望回调只运行一次。

什么是TypeScript?

TypeScript是一种由微软开发和维护的编程语言,它是JavaScript的严格超集。它支持静态和动态类型,并进一步提供了继承特性、类、可见性范围、命名空间、接口、联合体和其他现代特性。TS是为处理大型项目而设计的,因为它更容易重构代码。通过与JavaScript的深入比较,了解更多关于它的功能。

您使用 TypeScript 的目的是什么?

JavaScrip 开发人员考虑使用 TypeScript 的原因有很多。

  • 使用ECMAScript的新功能- TypeScript支持ECMAScript标准,并将其转译到您选择的ECMAScript目标,因此您可以使用模块、lambda函数、类、重组等功能。

  • 静态类型--JavaScript是一种动态类型,在运行时真正实例化之前,它不知道变量是什么类型;在这里,TypeScript给JavaScript增加了类型支持。

  • 类型推理--TypeScript通过使用类型推理,使打字变得更简单,更不明确。即使你不明确地输入类型,它们仍然存在,以拯救你的行为,否则会导致运行时错误。

  • 更好的IDE支持--与JavaScript相比,TypeScript的开发经验有很大的改进。有各种各样的IDE对TypeScript有很好的支持,比如Visual Studio和VS Code,IntelliJ和Sublime,或者WebStorm。

  • 严格的空值检查- 像 "你不能读取未定义的属性'x'"这样的错误在JavaScript编程中很常见。你可以通过严格检查来避免大多数这类错误,因为人们不能使用TypeScript编译器不知道的变量。

  • 互操作性- TypeScript 与 JavaScript 密切相关,因此它有很好的互操作能力,但要在 TypeScript 中与 JavaScript 库一起工作,需要一些额外的工作。

如何在 NextJS 中安装 TypeScript?

以下是在 Next.js 应用程序中安装 TypeScript 的分步指南。

1.创建基础项目--命令。npx create-next-app next-app-example.
用基础模板创建项目。

2.2.在项目的根部添加tsconfig.json ,以激活TypeScript。

3.在表格中构造项目。

src
├── components
|  ├── FormTodo.tsx
|  └── Todo.tsx
├── pages
   ├── Todo
        ├── index.tsx
|  ├── index.tsx
|  └── _app.tsx
├── styles
|  └── index.css
├── tsconfig.json
├── types
|  └── index.ts
├── next-env.d.ts

4.在Next.js中创建TypeScript类型

你可以为你的应用程序中的任何东西创建类型,包括道具类型、API响应、函数的参数等等。

我们首先为我们的todos 创建一个类型:

--code 

export interface ITodo {
  id: number;
  description: string;
  isDone: boolean;
}

--end code

5.在Next.js中创建组件

现在我们有了我们的ITODO 类型,我们可以创建Todo.tsx 组件:

--code

import React from "react";
import { Button } from "react-bootstrap";
import { ITodo } from "../types";

type Props = {
  todo: ITodo;
  markTodo: (id: number) => void;
  removeTodo: (id: number) => void;
};

const Todo: React.FC<Props> = ({ todo, markTodo, removeTodo }) => {
  return (
    <div className="todo">
      <span style={{ textDecoration: todo.isDone ? "line-through" : "" }}>
        {todo.description}
      </span>
      <div>
        {!todo.isDone && (
          <Button variant="outline-success" onClick={() => markTodo(todo.id)}>
            ✓
          </Button>
        )}
        <Button variant="outline-danger" onClick={() => removeTodo(todo.id)}>
          ✕
        </Button>
      </div>
    </div>
  );
};

export default Todo;

--end code

正如你所看到的,我们首先导入了之前创建的类型,同时还创建了另一个名为Props 的类型,它将反映组件所接收到的作为参数的道具。

这个组件负责显示ITodo 对象。这个组件接收ITodo 对象、一个markTodo 函数和一个removeTodo 函数作为道具。注意,这个参数必须与props类型相匹配,以使Typescript满意。

现在让我们来创建我们的FormTodo.tsx 组件,负责在我们的应用程序中添加Todos :

--code
import * as React from "react";
import { Button, Form } from "react-bootstrap";

type Props = {
  addTodo: (text: string) => void;
};

const FormTodo: React.FC<Props> = ({ addTodo }) => {
  const [value, setValue] = React.useState<string>("");

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    if (!value) return;
    addTodo(value);
    setValue("");
  };

  return (
    <Form onSubmit={handleSubmit}>
      <Form.Group>
        <Form.Label>
          <b>Add Todo</b>
        </Form.Label>
        <Form.Control
          type="text"
          className="input"
          value={value}
          onChange={(e) => setValue(e.target.value)}
          placeholder="Add new todo"
        />
      </Form.Group>
      <Button variant="primary mb-3" type="submit">
        Submit
      </Button>
    </Form>
  );
};

export default FormTodo;

--end code

我们的组件接受addTodo 作为一个参数。它处理提交一个新的Todo 。如果该值不是空的,那么我们就对该todo文本调用addTodo 函数,然后再次将表单的值设置为空。这个组件返回一个接受todos的表单,并有一个提交按钮,通过点击该按钮,我们将todo添加到todo列表中。

6.创建我们的页面,以便使用我们的反应组件。
我们将从导入我们先前创建的组件和类型开始:

-- code
import React from "react";
import { Card } from "react-bootstrap";
import { InferGetStaticPropsType } from "next";
import { ITodo } from "../../types";
import Todo from "../../components/Todo";
import FormTodo from "../../components/FormTodo";

export default function indexPage({
  todos,
}: InferGetStaticPropsType<typeof getStaticProps>) {
  const [todosList, setTodosList] = React.useState(todos);

  const addTodo = (text: string) => {
    const newTodo: ITodo = {
      id: Math.random(),
      description: text,
      isDone: false,
    };
    const newTodos = [...todosList, newTodo];
    setTodosList(newTodos);
  };

  const markTodo = (id: number) => {
    const newTodos = todosList.map((todo) => {
      if (todo.id === id) {
        return {
          ...todo,
          isDone: true,
        };
      }
      return todo;
    });
    setTodosList(newTodos);
  };

  const removeTodo = (id: number) => {
    const newTodos = todosList.filter((todo) => {
      return todo.id !== id;
    });
    setTodosList(newTodos);
  };

  return (
    <div className="app">
      <div className="container">
        <h1 className="text-center mb-4">Todo List</h1>
        <FormTodo addTodo={addTodo} />
        <div>
          {todosList.map((todo, index) => (
            <Card key={index}>
              <Card.Body>
                <Todo todo={todo} markTodo={markTodo} removeTodo={removeTodo} />
              </Card.Body>
            </Card>
          ))}
        </div>
      </div>
    </div>
  );
}

export async function getStaticProps() {
  const todos: ITodo[] = [
    {
      id: 2,
      description: "Test next.js app",
      isDone: true,
    },
    {
      id: 1,
      description: "Build next.js app",
      isDone: true,
    },
  ];

  return {
    props: {
      todos,
    },
  };
}
--end code

在导入组件和类型后,我们导入了InferGetStaticPropsType ,这是由Next.js提供的,允许我们在getStaticProps方法上设置类型。

之后,我们使用useState 钩子初始化了我们的todoList ,将getStaticProps 提供的初始todos作为一个参数。

最后,我们声明了实现我们的逻辑的主要执行部分。

  • addTodo - 允许在我们的列表中添加一个todo
  • removeTodo - 允许在我们的列表中删除一个todo
  • markTodo - 允许在我们的列表中设置一个已完成的todo

最后,我们使用我们的组件返回一个todos的列表。