MERN 堆栈是什么?如何使用?简单的 MERN 应用

2025-06-07

MERN 堆栈是什么以及如何使用它?

简单的 MERN 应用程序

原始文章可以在这里
找到 该项目的代码可以在这里找到。

如果您已经在开发者世界中呆了一段时间,您可能已经遇到过提到“堆栈”的评论或文章,例如 MEAN、LAMP 或 XAMPP。

今天我们将深入探讨 MERN 堆栈,它与前面提到的 MEAN 堆栈类似。我将解释它是什么,以及如何在构建 Web 应用程序或网站时使用它。让我们开始吧!

什么是“堆栈”?

也许首先要讨论的是“技术栈”的概念。市面上有很多不同的技术栈,但它们只是实现同一件事的不同方式:创建一个前端(网站或应用程序),让人们能够以简单易管理的方式与后台的服务器和数据库进行交互。技术栈就是为了实现这一目标而使用的不同技术。

“堆栈只是为了实现该目标而使用的不同技术。”

虽然市面上有很多不同的堆栈可供选择,但有些堆栈比其他堆栈更常见。其中一种流行的堆栈称为 MEAN 堆栈,它由以下部分组成:

  • MongoDB
  • 邮件express.js
  • 一个angular.js
  • Node.js

今天我们将研究 MERN 技术栈,它与 MEAN 几乎相同,只是我们将用 React.js 替换了 Angular.js。这将使我们能够使用 MongoDB 作为数据库,使用 Node.js 和 Express.js 作为服务器和路由,并使用 React.js 创建用户交互的前端。

我们如何利用它进行构建?

我们如何利用它进行构建?

在深入探讨细节之前,我们先来概述一下这些部分是如何协同工作的。我花了一段时间才“理解”这部分,因为我之前是 PHP 程序员,前端和后端是混杂在一起的。

1. 后端和前端

首先要理解的是,后端和前端是独立的实体。前端可以位于同一个存储库中,也可以位于一个单独的存储库中。

2. API

如果您现在想知道我们如何让两者协同工作,答案是通过 API。我们将在服务器上创建一个 API(即应用程序接口),它将提供“端点”,供我们的前端应用程序与其交互。

为了说明起见,想象一下你的左手作为后端,右手作为前端。

现在,把你的双手十指交扣,就像你和自己牵着手一样。这就是模板语言的工作原理。它们允许你直接使用从服务器转储的数据来渲染一些 Markdown 文件,因此两者之间有很多重叠。

现在分开双手。这次,尽可能张开手指,只让左手指尖与右手指尖接触。这就是 MERN 堆栈的工作原理。后端提供端点(左手指尖),允许访问服务器;而前端(右手指尖)调用这些端点(它们接触的地方),以访问服务器(左手)。

希望这能让事情稍微明朗一些,如果没有,那就忘记我曾经提到过这件事。

后端

虽然我不会在本文中一步步讲解如何构建它(那将另文论述),但我想介绍一下这个技术栈中常用到的一些部分。我看过一些教程,它们解释了如何搭建服务器,但并不一定解释了为什么要用到这些库。

给我关于 MERN Stack 的详细信息

创建app.js文件后,我们需要安装一些软件包。以下是我之前在 Express.js 项目中使用过的一些常用软件包,希望对您有所帮助。

  • Express.js - 一个内置了许多功能的 Web 应用程序框架,包括路由。
  • Mongoose - 一个 ODM,允许我们的 express.js 应用程序和 MongoDB 之间进行交互。
  • BodyParser - 一个允许我们的 express.js 应用程序读取传入请求的正文(或内容)的库。
  • DotENV - 允许我们使用 .env 文件来存储敏感数据。
  • Passport.js - 为我们的应用程序提供身份验证,并提供几种不同的身份验证方法选项。
  • 验证器——对多种类型的数据进行简单验证。
  • bCrypt - 加密密码等敏感数据
  • Nodemon - 当情况发生变化时,为我们的节点服务器提供“热重载”,这样我们就不必在每次进行更改时停止并启动服务器

当然还有更多的软件包,但这些是我所看到的几个常用的库以及它们的使用原因。

现在我们已经了解了一些常用的包,让我们来看一些代码。首先是我们的服务器:

const express = require('express')
const mongoose = require('mongoose')
const bodyParser = require('body-parser')
const app = express()

app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())

// Import Model
const Post = require('./models/Post')

// Connect to MongoDB
mongoose.connect(
  'mongodb://localhost:27017/simple-mern',
  () => console.log('MongoDB is connected')
)

// Enable CORS
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*')
  res.header(
    'Access-Control-Allow-Headers',
    'Origin, X-Requested-With, Content-Type, Accept'
  )
  next()
})

// Get all of our posts
app.get('/api/posts', (req, res) => {
  Post.find({}).then(posts => {
    res.json(posts)
  })
})

// Get One of Our posts
app.get('/api/posts/:id', (req, res) => {
  Post.findOne({ _id: req.params.id }).then(post => {
    res.json(post)
  })
})

// Create and Update post
app.post('/api/posts', (req, res) => {
  const data = {
    title: req.body.title,
    content: req.body.content,
  }
  Post.findOne({ _id: req.body.id }, (err, post) => {
    if (post) {
      Post.findByIdAndUpdate(req.body.id, data, { upsert: false }).then(
        updated => {
          res.json(updated)
        }
      )
    } else {
      Post.create(data).then(created => {
        res.json(created)
      })
    }
  })
})

// Delete selected post
app.post('/api/posts/:id', (req, res) => {
  Post.findByIdAndDelete(req.params.id).then(post => {
    res.json({ message: 'Your post was deleted!' })
  })
})

app.listen(3333, () => console.log('Server is running on port 3333'))
Enter fullscreen mode Exit fullscreen mode

这就是我们简单的 API 服务器。如你所见,它有一些基本的 CRUD(创建-读取-更新-删除)功能,但并不复杂。如果仔细观察,我们会发现我们使用它来res.json()将输出数据发送到特定的 URL,而不是输出 HTML 或其他模板。这就是我们构建 API 以便将数据提供给 React 的方式。

你可能注意到了,我刚刚把 mongoose 指向了我机器上的 mongodb 服务器。为了正常工作,你的电脑上需要安装并运行 MongoDB。如果没有运行,只需打开一个终端窗口并输入以下命令:

mongod
Enter fullscreen mode Exit fullscreen mode

这将在您的本地计算机上启动 MongoDB 服务器。由于这只是在本地执行,如果您在仓库中运行代码,您将无法看到我的帖子。您必须创建新内容。如果您正在寻找一些虚拟内容,我目前最喜欢的生成器是Fillerama.io,它可以从我最喜欢的一些电影和节目中生成文本。

如果我们有兴趣单独测试服务器,我们可以运行以下命令来启动服务器:

npm run server

or

yarn server
Enter fullscreen mode Exit fullscreen mode

服务器启动后,显示它在 3333 端口运行,并且已连接 MongoDB,我们就可以打开Postman测试路由了。对于 GET 路由,我们只需输入路由信息并点击“发送”即可。对于 POST 路由,我们需要选择“正文”,并创建/输入标题和内容字段。

前端

现在我们的服务器已经启动并运行,我们可以开始开发用户交互的客户端(或前端)。这将使用 React 构建,并且可以通过几种不同的方式实现。

第一种方法是将必要的前端库(react、react-dom、react-router 等)package.json与后端库添加到同一个文件中。虽然我在这个项目中确实这样做了,但需要注意的是,我认为这并非最佳实践。我觉得如果使用这种方法,随着项目的增长,代码库会变得越来越混乱,难以维护。我决定在这个特定的应用程序中采用这种方法,因为我知道它不会增长或发生真正的变化。我在这里引用的应用程序仅用于演示目的。

第二种也是更优的方法(在我看来)是为后端创建一个仓库,为前端创建一个单独的仓库。我们仍然可以将前端仓库克隆到项目目录中,只要确保将前端代码包含在文件中即可.gitignore。例如,我们这个应用的文件结构包含一个名为 的目录,client用于存放所有前端代码。我们可以将其放在一个单独的仓库中,然后将以下内容放入.gitignore后端仓库的文件中:

client
Enter fullscreen mode Exit fullscreen mode

将文件夹添加client.gitignore文件将确保它不会被视为项目中的第二个仓库。此外,这样做可以方便地重新设计和替换前端,而无需触及后端。

您的应用程序如何设计完全取决于您,我只是觉得通过为前端和后端维护单独的存储库可以使事情变得更有条理。
组织

反应

既然我们已经了解了项目组织,现在来谈谈实际的前端代码。下面是我的app.jsReact 应用文件,我不会在这篇文章里贴出每个组件的代码,而是直接在这里放一个代码库的链接,并解释每个组件的功能。

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter as Router, Route } from 'react-router-dom'
import Header from './components/header'
import Index from './components/index'
import Single from './components/single'
import New from './components/new'
import Edit from './components/edit'

const App = () => (
  <Router>
    <div>
      <Header />
      <Route path="/" exact component={Index} />
      <Route path="/new" exact component={New} />
      <Route path="/post/:id" exact component={Single} />
      <Route path="/edit/:id" exact component={Edit} />
    </div>
  </Router>
)

ReactDOM.render(<App />, document.getElementById('app'))
Enter fullscreen mode Exit fullscreen mode

如你所见,这app.js并不复杂。它有一个<Router>,允许我们在 React 中设置路由,根据 URL 渲染不同的组件。以下是我们的 React 应用程序中使用的其他组件:

  • 标题- 屏幕顶部的导航栏
  • 索引- 列出所有可用的博客文章
  • 新建- 允许用户创建新博客文章的表单
  • 单篇- 根据 ID 显示一篇博客文章
  • 编辑- 允许用户根据 ID 更新博客文章的表单

我们使用 Axios 对 API 端点进行 http 调用,然后使用 React 按照我们想要的方式显示数据。我会将 Index.js 代码放在这篇文章中,以便我们查看它们是如何协同工作的。

import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import axios from 'axios'

class Index extends Component {
  constructor(props) {
    super(props)
    this.state = {
      posts: [],
    }
  }

  componentDidMount() {
    axios.get('http://localhost:3333/api/posts').then(posts => {
      this.setState({
        posts: posts.data,
      })
    })
  }

  render() {
    return (
      <div className="m-8">
        <ul className="index">
          {this.state.posts.map(post => (
            <li key={post.title}>
              <h2>
                <Link to={`/post/${post._id}`}>{post.title}</Link>
              </h2>
            </li>
          ))}
        </ul>
      </div>
    )
  }
}

export default Index
Enter fullscreen mode Exit fullscreen mode

在上面的代码中,我们使用了一个类组件,它允许我们使用状态和生命周期方法。这是必要的,因为 Axios 调用应该在componentDidMount()生命周期方法中进行。需要注意的是,当我尝试调用本地 API 时,我遇到了 CORS 错误。为了解决这个问题,我server.js在 Express 服务器的文件中添加了一些标头,使其正常工作。该server.js文件中的注释中注明了这些代码。

渲染

在结束之前,我想稍微谈谈渲染。如果我们运行应用程序并直接访问某篇博客文章,可能会出现内容无法显示的问题。这会导致用户浏览体验不佳,并且搜索引擎爬虫难以索引该网站。为了解决这个问题,我建议使用Gatsby.jsNext.js之类的工具。这两个解决方案各不相同,但都可以根据您的需求提供相应的解决方案。

Gatsby.js是一个静态网站生成器,它允许你使用 React 构建网站,然后 Gatsby 在构建时将其转换为静态文件,从而提高网站速度。Gatsby 有很多实用的插件,使它变得非常灵活。事实上,我的网站就是一个 Gatsby.js 网站!由于静态文件是在构建时创建的,因此每次更改源内容时都需要重建网站。

另一方面,Next.js是 React 网站的服务器端渲染工具。它内置了许多功能,例如路由、代码拆分、样式化组件等等。服务器端渲染意味着数据会像在服务器上一样自动更新,但会在浏览器尝试显示之前完成渲染。这意味着用户不会遇到任何数据显示问题,搜索引擎爬虫也能顺利完成工作。

还有很多其他的解决方案,但这两个是我最常听到的,也是我用过的两个来解决这个问题的。这两个方案都有很棒的文档,而且很容易上手。

结论

希望本文能帮助您理清 MERN 技术栈工作原理的一些疑惑。它只是使用 MongoDB、Express.js 和 Node.js 来创建一个服务器,该服务器提供 API 端点,供我们的 React 应用程序调用以获取数据。既然您已经对 MERN 有了更深入的理解,那就去构建一些精彩的作品,并在下方分享您的链接,让我们一起欣赏吧!

GitHub 徽标 iamtimsmith / simple-mern-app

这是与博客文章配套的 MERN 应用程序的代码库

简单的 MERN 应用程序

这是 MERN 应用程序的代码库,与此处的博客文章配套使用。它并非旨在成为一个复杂或可立即投入生产的应用程序,而是以通俗易懂的术语解释 MERN 堆栈的各个部分如何组合在一起。

Tim Smith 的简单 MERN 应用程序

下载:

git clone https://github.com/iamtimsmith/simple-mern-app.git

然后:

npm install

or

yarn

仅运行服务器:

npm run server

or

yarn server

仅运行客户端:

npm run client

or

yarn client

同时运行两者:

npm run dev

or

yarn dev
文章来源:https://dev.to/iam_timsmith/what-is-the-mern-stack-and-how-do-i-use-it-55b8
PREV
网站开发人员/自由职业者如何及时建立网站?
NEXT
5 门适合初学者的最佳机器学习课程