用React和Nx构建一个应用程序

1,536 阅读6分钟

构建工具是任何网络开发者的宝贵资产。目前,许多伟大的构建工具可用于所有领先的JavaScript框架。Nx是一个构建框架,它允许你在原地构建单体和脚手架完整的应用程序,就是这样一个最近越来越受欢迎的例子。

Nx将开发人员在构建应用程序时通常必须手动执行的许多步骤自动化。它包括计算缓存、依赖图和构建自动化等功能,还包括与Cypress的插件集成

在这篇文章中,我们将通过使用Nx和React来构建一个简单的待办事项列表应用程序。要跟上进度,请查看我在GitHub上的示例项目

让我们开始吧!

设置一个工作区

为了开始,你需要创建一个Nx工作区。你可以使用npx ,通过运行以下代码来完成。

npx create-nx-workspace@latest

你也可以全局安装Nx

当CLI运行时,它会创建一个工作空间,这基本上是你项目的一个容器。当我们完成后,这个容器将成为一个monorepo。

Nx Cli Runs Container Monorepo

CLI会问你一系列的问题,然后把你的项目的主要骨架搭出来。我们正在构建一个React应用程序,所以文件结构看起来像下面的代码块。

├── README.md
├── apps
│   ├── first-project
│   └── first-project-e2e
├── babel.config.json
├── jest.config.js
├── jest.preset.js
├── libs
├── nx.json
├── package-lock.json
├── package.json
├── tools
│   ├── generators
│   └── tsconfig.tools.json
├── tsconfig.base.json
└── workspace.json

文件结构包括一个apps ,该目录有两个文件夹。一个是项目本身,另一个是用Cypress运行端到端测试

现在你有了一个脚手架式的应用程序,你可以使用npx nx serve first-project 来运行它。

你将收到以下输出。

Nx First Project Homepage

创建一个API

让我们在React项目旁边创建一个API,我们可以用它来执行任何REST调用。Nx的一个很酷的功能是它能够在一个命令中为你的项目添加一个API。

我们将使用Express创建一个API。首先,你需要用Nx CLI安装express 插件。

npm install --save-dev @nrwl/express

现在,你可以用以下命令创建API。

npx nx g @nrwl/express:app api --frontendProject=first-project

文件夹结构应该看起来像下面的代码。

├── README.md
├── apps
│   ├── api
│   ├── first-project
│   └── first-project-e2e
├── babel.config.json
├── jest.config.js
├── jest.preset.js
├── libs
├── nx.json
├── package-lock.json
├── package.json
├── tools
│   ├── generators
│   └── tsconfig.tools.json
├── tsconfig.base.json
└── workspace.json

构建应用程序的脚手架

现在我们已经有了构件,让我们来看看到目前为止我们已经建立了什么如果你看一下apps 文件夹,你会看到下面的代码。

├── api
│   ├── jest.config.js
│   ├── src
│   │   ├── app
│   │   ├── assets
│   │   ├── environments
│   │   └── main.ts
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   └── tsconfig.spec.json
├── first-project
│   ├── jest.config.js
│   ├── proxy.conf.json
│   ├── src
│   │   ├── app
│   │   ├── assets
│   │   ├── environments
│   │   ├── favicon.ico
│   │   ├── index.html
│   │   ├── main.tsx
│   │   ├── polyfills.ts
│   │   └── styles.scss
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   └── tsconfig.spec.json
└── first-project-e2e
    ├── cypress.json
    ├── src
    │   ├── fixtures
    │   ├── integration
    │   ├── plugins
    │   └── support
    ├── tsconfig.e2e.json
    └── tsconfig.json

api 项目是一个完整的 Express 应用程序,它由main.ts, anentrypoint 文件构成。

/**
 * This is not a production server yet!
 * This is only a minimal backend to get started.
 */
import * as express from 'express';
const app = express();
app.get('/api', (req, res) => {
  res.send({ message: 'Welcome to api!' });
});
const port = process.env.port || 3333;
const server = app.listen(port, () => {
  console.log(`Listening at http://localhost:${port}/api`);
});
server.on('error', console.error);

进入first-project 文件夹。你会看到用传统的文件夹结构构建的React应用,常见的有assets,styles,index,main, 和app 文件。

import { StrictMode } from 'react';
import * as ReactDOM from 'react-dom';
import App from './app/app';
ReactDOM.render(
  <StrictMode>
    <App />
  </StrictMode>,
  document.getElementById('root')
);

最后,如果你查看first-project-e2e 文件夹,你会看到Cypress项目,具有任何Cypress测试套件的传统结构。

├── cypress.json
├── src
│   ├── fixtures
│   ├── integration
│   ├── plugins
│   └── support
├── tsconfig.e2e.json
└── tsconfig.json

调用API

到目前为止,我们已经建立了一个脚手架式的应用程序。现在,让我们添加一个功能,展示CLI的作用我将用React和Nx建立一个简单的待办事项应用程序

首先,将以下代码复制到apps/first-project/src/app/app.tsx 文件夹中。

import React, { useState } from 'react';
interface Todo {
  title: string;
}
export const App = () => {
  const [todos, setTodos] = useState<Todo[]>([
    { title: 'Todo 1' },
    { title: 'Todo 2' },
  ]);
  function addTodo() {
    setTodos([
      ...todos,
      {
        title: `New todo ${Math.floor(Math.random() * 1000)}`,
      },
    ]);
  }
  return (
    <>
      <h1>Todos</h1>
      <ul>
        {todos.map((t) => (
          <li className={'todo'}>{t.title}</li>
        ))}
      </ul>
      <button id={'add-todo'} onClick={addTodo}>
        Add Todo
      </button>
    </>
  );
};
export default App;

如果你从项目根部运行npm run start ,你会看到以下内容。

Npm Run Start Project Root

到目前为止,一切都很好!我们可以通过整合对API项目的调用来改进我们的代码。首先,创建一个名为apps/api/src/app/todos.ts 的文件,并在其中添加以下代码。

import { Express } from 'express';

interface Todo {
  title: string;
}

const todos: Todo[] = [{ title: 'Todo 1' }, { title: 'Todo 2' }];

export function addTodoRoutes(app: Express) {
  app.get('/api/todos', (req, resp) => resp.send(todos));
  app.post('/api/addTodo', (req, resp) => {
    const newTodo = {
      title: `New todo ${Math.floor(Math.random() * 1000)}`,
    };
    todos.push(newTodo);
    resp.send(newTodo);
  });
}

上面的代码创建了许多我们的API需要的路由。现在,我们将通过修改apps/api/src/main.ts 文件使其看起来像下面的代码块来向我们的API注册这些路由。

import * as express from 'express';
import { addTodoRoutes } from './app/todos';
const app = express();
app.get('/api', (req, res) => {
  res.send({ message: 'Welcome to api!' });
});
addTodoRoutes(app);
const port = process.env.port || 3333;
const server = app.listen(port, () => {
  console.log(`Listening at http://localhost:${port}/api`);
});
server.on('error', console.error);

运行npx nx serve api ,然后点击 [http://localhost:3333/api/todos](http://localhost:3333/api/todos).你会看到以下内容。

[{"title":"Todo 1"},{"title":"Todo 2"}]

现在,让我们从我们的应用程序调用API。我们将设置一个代理,以便从我们的React应用程序的API调用直接调用API。

请看位于项目根部的workspace.json 文件,在那里你可以找到整个应用程序的配置。

接下来,我们需要在我们的first-project React应用程序的serve 目标中找到proxyConfig ,它应该看起来像下面的代码块。

{
  "serve": {
    "builder": "@nrwl/web:dev-server",
    "options": {
      "buildTarget": "first-project:build",
      "proxyConfig": "apps/first-project/proxy.conf.json"
    },
    "configurations": {
      "production": {
        "buildTarget": "first-projects:build:production"
      }
    }
  }
}

打开proxyConifg 设置的文件,在apps/first-project/proxy.conf.json ,并添加以下代码。它将代理所有的API调用来调用你的API项目。

{
  "/api": {
    "target": "http://localhost:3333",
    "secure": false
  }
}

现在,如果我们修改first-rproject 文件夹中的main.ts 文件,我们就可以调用API,而不是使用我们最初设置的本地状态。修改apps/first-project/src/app/app.tsx ,看起来像下面的代码块。

import React, { useEffect, useState } from 'react';

interface Todo {
  title: string;
}

const App = () => {
  const [todos, setTodos] = useState<Todo[]>([]);

  useEffect(() => {
    fetch('/api/todos')
      .then((_) => _.json())
      .then(setTodos);
  }, []);

  function addTodo() {
    fetch('/api/addTodo', {
      method: 'POST',
      body: '',
    })
      .then((_) => _.json())
      .then((newTodo) => {
        setTodos([...todos, newTodo]);
      });
  }

  return (
    <>
      <h1>Todos</h1>
      <ul>
        {todos.map((t) => (
          <li className={'todo'}>{t.title}</li>
        ))}
      </ul>
      <button id={'add-todo'} onClick={addTodo}>
        Add Todo
      </button>
    </>
  );
};

export default App;

现在,我们的React应用程序正在调用API,而不是依赖本地状态。

让我们运行API和frontendProject!你可以使用npx nx serve first-projectnpx nx serve api 。你也可以使用一个npm包,比如Concurrently[start-server-and-test](https://www.npmjs.com/package/start-server-and-test),使用脚本来自动运行API。

Nx Npm Run Api Frontendproject Page

用Cypress测试

如前所述,Nx在应用被架设时内置了Cypress测试。

为了看到测试的实际效果,修改apps/first-project-e2e/src/support/app.po.ts 的测试文件,以包括以下内容。

export const getTodos = () => cy.get('li.todo');
export const getAddTodoButton = () => cy.get('button#add-todo');

现在,修改位于apps/first-project-e2e/src/integration/app.spec.ts 的文件,使其看起来像以下代码。

import { getAddTodoButton, getTodos } from '../support/app.po';

describe('TodoApps', () => {
  beforeEach(() => cy.visit('/'));

  it('should display todos', () => {
    getTodos().should((t) => expect(t.length).equal(2));
    getAddTodoButton().click();
    getTodos().should((t) => expect(t.length).equal(3));
  });
});

运行npx nx e2e first-project-e2e --watch ,看看你的测试在运行中

额外的Nx功能

Nx CLI有许多功能,超出了我们到目前为止所涉及的范围。其中最有用的是为你的项目生成一个依赖关系图的能力,它通过数据可视化简化了复杂的项目。我们简单的待办事项应用程序不需要这个,但看到它也是很不错的

从运行下面的代码开始。

npx nx dep-graph

你会收到一个看起来像下面图片的输出。

Generate Dependency Graph Nx

另一个非常酷的功能是Nx有能力检测变化,并且只重建受项目最新修改影响的部分。下面是一些利用这个功能的命令样本,来自Nx的文档

nx affected:apps # prints affected apps
nx affected:libs # prints affected libs
nx affected:build # builds affected apps and libs
nx affected:lint # lints affected apps and libs
nx affected:test # tests affected apps and libs
nx affected:e2e # e2e tests affected apps

最后,Nx允许你创建库计算缓存以提高性能。我强烈建议阅读更多关于这些和其他功能的资料

接下来的步骤

在本教程中,我们通过搭建一个简单的待办事项应用程序的脚手架,介绍了使用React和Nx的基础知识。我们项目的下一步将是构建资产并部署它们。Nx可以为小项目自动构建,也可以扩展到企业应用的大项目

我希望你喜欢读这篇文章!我希望它能激励你去看看Nx,并将它与React一起使用。我绝对推荐你查看Nx的文档,并阅读他们的一些指南,以了解更多信息。

andrewevans.dev上关注我,在Twitter上与我联系:@AndrewEvans0102

The postBuilding an application with React and Nxappeared first onLogRocket Blog.