使用MEVN栈的基础知识

387 阅读6分钟

在本教程中,我们将使用MEVN栈建立一个小项目。MEVN是MongoDB、Express、VueJS和Node.js作为全栈应用程序的首字母缩写。MongoDB是我们的数据存储机制,Express是处理HTTP请求和路由的中间件,VueJS是渲染数据的客户端JavaScript,而Node.js是我们的应用程序将运行的服务器。作为额外的奖励,我们将包括一个很酷的Bootswatch主题,使CSS看起来不错。让我们开始吧。


创建目录并运行npm init

为了开始,我们将添加一个新的目录来存放我们的服务器代码,并运行npm init -y ,为我们初始化一个package.json文件。

node $mkdir MEVN-Tutorial
node $cd MEVN-Tutorial
MEVN-Tutorial $npm init -y
Wrote to C:nodeMEVN-Tutorialpackage.json:
{
  "name": "MEVN-Tutorial",  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

向项目添加依赖性

现在我们可以添加所有我们将在这个项目中使用的依赖项。我们将添加Express、Morgan、Cors和body-parser来开始。Express用于创建API,Morgan用于将http请求记录到控制台,cors允许客户端与前端对话,而body-parser则用于让后端读取从前端发送的数据。

MEVN-Tutorial $npm install express morgan cors body-parser
npm notice created a lockfile as package-lock.json. You should commit this file.npm WARN MEVN-Tutorial@1.0.0 No description
npm WARN MEVN-Tutorial@1.0.0 No repository field.
+ express@4.16.3
+ body-parser@1.18.3
+ morgan@1.9.0
+ cors@2.8.4
added 63 packages from 50 contributors and audited 161 packages in 4.205s
found 0 vulnerabilities

添加一个index.js文件

让我们为我们的项目添加一个index.js文件,因为这将是应用程序的入口。

MEVN-Tutorial $touch index.js

MEVN Project Skeleton


测试我们的服务器

我们已经准备好了一切,可以测试我们的服务器。我们只需要添加一点代码来使它工作。
index.js

const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const morgan = require('morgan');

const app = express();

app.use(morgan('tiny'));
app.use(cors());
app.use(bodyParser.json());

app.get('/', (req, res) => {
    res.json({
        message: 'Behold The MEVN Stack!'
    });
});

const port = process.env.PORT || 4000;
app.listen(port, () => {
    console.log(`listening on ${port}`);
});

现在我们可以像这样运行该项目。

MEVN-Tutorial $node index.js
listening on 4000

如果我们在浏览器中加[载http://localhost:4000/,我们可以看到服务器正在工作。很好!
the server is working json response


创建客户端 客户端目录

我们将在客户端使用Vue.js,所以现在让我们继续开始设置它。首先,我们要安装Vue Cli。

MEVN-Tutorial $npm i @vue/cli

一旦完成,让我们运行vue create client 命令,选择Router和Linter / Formatter选项。
vue cli 3 manual presets
vue cli step 2
vue cli step 3
vue cli step 4
vue cli step 5

当你完成了所有的提示后,你应该看到类似下面的东西。基本上,我们现在正在使用Vue cli在客户端文件夹中为我们的后端api构建前端。

✨  Creating project in C:nodeMEVN-Tutorialclient.
🗃  Initializing git repository...
⚙  Installing CLI plugins. This might take a while...


> yorkie@2.0.0 install C:nodeMEVN-Tutorialclientnode_modulesyorkie
> node bin/install.js

setting up Git hooks
done

added 1081 packages from 734 contributors and audited 10674 packages in 101.129sfound 0 vulnerabilities

🚀  Invoking generators...
📦  Installing additional dependencies...

added 109 packages from 43 contributors and audited 12463 packages in 41.475s
found 0 vulnerabilities

⚓  Running completion hooks...

📄  Generating README.md...

🎉  Successfully created project client.
👉  Get started with the following commands:

 $ cd client
 $ npm run serve

哇!Vue cli是非常酷的!好吧,既然它说我们可以运行这两个命令,我们就这么做,然后访[问http://localhost:8080/#/,看看我们的Vue安装是否运行。
vue cli 3 npm run serve

现在,为了改变现状,我们将把Bootswatch Sketchy主题的CSS添加到index.html中,就像我们在这里看到的那样。
bootswatch sketch css include

现在,当我们看我们的前端时,看看这个很酷的效果吧
vue bootswatch

好极了。我们现在有了一个可以使用API的前端,而我们还在构建这个API。事实上,让我们转回后端代码,继续构建所需的东西。


用Monk连接到MongoDB

在另一个教程中,我们使用Mongoose连接到我们的MongoDB服务器。在这一集,我们将使用Monk,它是一个更轻的软件包,可以快速使用。所以我们现在就安装Monk,同时我们也可以安装Joi来进行一些简单的数据验证。

MEVN-Tutorial $npm i monk joi

创建一个消息模型

就这样。我们已经准备好创建一个模型来存储用户可能提交给我们api的消息。在我们的项目中,我们可以添加一个db 目录,然后把connection.jsmessages.js 文件放在那里。

MEVN-Tutorial $mkdir db
MEVN-Tutorial $cd db
db $touch connection.jsdb $touch messages.js

connection**.js**
在connection.js文件中,我们可以添加这段代码,它将允许我们连接到MongoDB。

const monk = require('monk');
const connectionString = process.env.MONGODB_URI || 'localhost/mevnStack';
const db = monk(connectionString);

module.exports = db;

messages.js
这是用于处理消息的模型。

const Joi = require('joi');
const db = require('./connection');

const schema = Joi.object().keys({
    username: Joi.string().alphanum().required(),
    subject: Joi.string().required(),
    message: Joi.string().max(500).required(),
    imageURL: Joi.string().uri({
        scheme: [
            /https?/
        ]
    })
});

const messages = db.get('messages');

function getAll() {
    return messages.find();
}

function create(message) {
    if (!message.username) message.username = 'Anonymous';

    const result = Joi.validate(message, schema);
    if (result.error == null) {
        message.created = new Date();
        return messages.insert(message);
    } else {
        return Promise.reject(result.error);
    }
}

module.exports = {
    create,
    getAll
};

添加一个路由来从Mongodb获取消息

在index.js中,我们可以添加这段代码。

const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const morgan = require('morgan');

const messages = require('./db/messages');

const app = express();

app.use(morgan('tiny'));
app.use(cors());
app.use(bodyParser.json());

app.get('/', (req, res) => {
    res.json({
        message: 'Behold The MEVN Stack!'
    });
});

app.get('/messages', (req, res) => {
    messages.getAll().then((messages) => {
        res.json(messages);
    });
});

const port = process.env.PORT || 4000;
app.listen(port, () => {
    console.log(`listening on ${port}`);
});

我们现在甚至可以测试这个。在一个命令提示符中,我们可以运行mongod ,以启动我们的Mongo服务器。然后,我们需要运行node index.js 来启动我们的项目。在这一点上,如果我们访[问http://localhost:4000/messages,我们得到的是一个空数组。这其实是好事,因为这意味着我们正在与Mongo数据库对话,只是里面还没有任何消息。


添加一个路由来创建一个消息

index.js中,让我们添加一个路由来处理对API的POST请求,这将向数据库中添加一个新的消息。

const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const morgan = require('morgan');

const messages = require('./db/messages');

const app = express();

app.use(morgan('tiny'));
app.use(cors());
app.use(bodyParser.json());

app.get('/', (req, res) => {
    res.json({
        message: 'Behold The MEVN Stack!'
    });
});

app.get('/messages', (req, res) => {
    messages.getAll().then((messages) => {
        res.json(messages);
    });
});

app.post('/messages', (req, res) => {
    console.log(req.body);
    messages.create(req.body).then((message) => {
        res.json(message);
    }).catch((error) => {
        res.status(500);
        res.json(error);
    });
});

const port = process.env.PORT || 4000;
app.listen(port, () => {
    console.log(`listening on ${port}`);
});

使用Postman向API发送请求

我们现在可以做的是使用Postman向API发送一个POST请求。
post request via postman

现在让我们用Compass来看看数据是否在数据库中。看起来不错!
new message in mongodb

此外,如果我们访问http://localhost:4000/messages,我们现在看到存储在数据库中的消息以JSON格式返回。
json response from mevn stack


消耗API并使用Vue显示消息

我们的API现在可以工作了。它能够接受并返回JSON格式的消息。现在我们想用我们的Vue客户端在屏幕上渲染一个或多个消息。我们可以先在Home.vue中添加以下Vue代码。

<template>
  <div>
    <hr>
    <div class="list-unstyled" v-for="message in messages" :key="message._id">
      <li class="media">
        <img v-if="message.imageURL" class="mr-3" :src="message.imageURL" :alt="message.subject">
        <div class="media-body">
          <h4 class="mt-0 mb-1">{{message.username}}</h4>
          <h5 class="mt-0 mb-1">{{message.subject}}</h5>
          {{message.message}}
          <br />
          <small>{{message.created}}</small>
        </div>
      </li>
      <hr>
    </div>
  </div>
</template>

<script>
const API_URL = "http://localhost:4000/messages";

export default {
  name: "home",
  data: () => ({
    error: "",
    messages: []
  }),

  mounted() {
    fetch(API_URL)
      .then(response => response.json())
      .then(result => {
        this.messages = result;
      });
  },
  methods: {}
};
</script>

<style>
img {
  max-width: 300px;
  height: auto;
}
</style>

现在要测试这个,我们实际上需要节点实例的运行。在MEVN-Tutorial目录中,我们必须运行node index.js 来启动我们的API服务器。然后,我们还必须通过打开一个*不同的*命令提示符并从客户端目录中运行npm run serve ,来启动我们的客户端。希望这是有意义的。一旦完成,我们将有两个实例在运行。

http://localhost:4000/messages 将提供后端API。
http://localhost:8080/#/ 将提供我们的客户端服务。

好了,随着这两个实例的运行,我们的Home.vue文件被填充了一些Vue标记 - 我们可以看到我们的数据正在页面上呈现。很好!
vue client rendering api data

这真是太酷了。我们的浏览器正在向Express服务器发出GET请求,然后它又以来自MongoDB数据库的JSON数据作为回应。在这一点上,我们的Vue.js代码消耗了这些JSON数据,并将其渲染到页面上。非常好!


添加一个Vue表单来提交新消息

现在我们可以把这段代码添加到Home.vue中,这样我们就有了一个添加新消息的表单。

<template>
  <div>
    <form @submit.prevent="addMessage" class="mb-3">
      <div v-if="error" class="alert alert-dismissible alert-warning">
        <button type="button" class="close" data-dismiss="alert">×</button>
        <h4 class="alert-heading">Error!</h4>
        <p class="mb-0">{{error}}</p>
      </div>
      <div class="form-group">
        <label for="username">Username</label>
        <input
          v-model="message.username"
          type="text"
          class="form-control"
          id="username" required>
      </div>
      <div class="form-group">
        <label for="subject">Subject</label>
        <input
          v-model="message.subject"
          type="text"
          class="form-control"
          id="subject"
          placeholder="Enter a subject" required>
      </div>
      <div class="form-group">
        <label for="message">Message</label>
        <textarea
          v-model="message.message"
          class="form-control"
          id="message"
          rows="3"></textarea>
      </div>
      <div class="form-group">
        <label for="imageURL">Image URL</label>
        <input
          v-model="message.imageURL"
          type="url"
          class="form-control"
          id="imageURL"
          placeholder="Enter URL to an image">
      </div>
      <button type="submit" class="btn btn-primary">Add Message</button>
    </form>
    <div class="list-unstyled" v-for="message in reversedMessages" :key="message._id">
      <li class="media">
        <img v-if="message.imageURL" class="mr-3" :src="message.imageURL" :alt="message.subject">
        <div class="media-body">
          <h4 class="mt-0 mb-1">{{message.username}}</h4>
          <h5 class="mt-0 mb-1">{{message.subject}}</h5>
          {{message.message}}
          <br />
          <small>{{message.created}}</small>
        </div>
      </li>
      <hr>
    </div>
  </div>
</template>

<script>
const API_URL = "http://localhost:4000/messages";

export default {
  name: "home",
  data: () => ({
    error: "",
    messages: [],
    message: {
      username: "Enter a screen name",
      subject: "",
      message: "",
      imageURL: ""
    }
  }),
  computed: {
    reversedMessages() {
      return this.messages.slice().reverse();
    }
  },
  mounted() {
    fetch(API_URL)
      .then(response => response.json())
      .then(result => {
        this.messages = result;
      });
  },
  methods: {
    addMessage() {
      console.log(this.message);
      fetch(API_URL, {
        method: "POST",
        body: JSON.stringify(this.message),
        headers: {
          "content-type": "application/json"
        }
      })
        .then(response => response.json())
        .then(result => {
          if (result.details) {
            // there was an error...
            const error = result.details
              .map(detail => detail.message)
              .join(". ");
            this.error = error;
          } else {
            this.error = "";
            this.showMessageForm = false;
            this.messages.push(result);
          }
        });
    }
  }
};
</script>

<style>
img {
  max-width: 300px;
  height: auto;
}
</style>

让我们继续前进,用一些新的数据来手动填写表单,测试一下。
vuejs form post request to api

一旦我们点击提交,POST请求就被发送到我们的API--连同我们在表单中输入的数据。现在我们在页面上看到了新的消息。很好!让我们再给信息添加一条信息。
new message is displayed

让我们再添加一条信息,以确保事情正常进行。
second example of vue post to API

再一次,我们在页面上看到了新的消息和我们之前添加的前两条消息。
vue for in list display


Mongo Express Vue.js Node.js教程总结

本教程探讨了使用MEVN栈的基础知识,至此结束。当然,你可能听说过MEAN栈,它使用Mongo、Express、Angular和Node。这种方法将Angular从堆栈中移除,并在它的位置上插入Vue.js。有趣的是,这是最早的教程之一,在这个教程中,客户端只做了简单的与API的交互。Express、Mongo和Node合作提供了API,而Vue.js提供了客户端应用程序,通过该API发送和接收JSON数据。优秀的!