構築Mean stack

请留意细节。

本文是我在下面提到的参考网站上所做的事情,在Angular 6中尝试过的个人备忘录。

因此,因为所提到的方法可能不是理论,或者可能因为缺乏各种知识而造成误解,请在参考本文进行学习时注意这一点。谢谢。

另外,如果您有任何需要注意的地方,我会很高兴能够听取您的建议。

想做的事情

    • Mean stack の構築

Angular Cli と npm で構築する(yeoman とか gulp とか grunt とかは使わない。というか本来Java屋なのでこの辺はよく知らない。)
MongoDB は Docker 上に構築する

版本

    • MongoDB: v3.6.4

 

    • Express: v4.16.3

 

    • Angular: v6.0.0

 

    Node.js: v8.11.1

任务流程

    1. Node.js的开发环境配置

 

    1. Angular的开发环境配置

 

    1. Express的开发环境配置

 

    1. Angular和Express的集成

 

    1. MongoDB的开发环境配置

 

    Express和MongoDB的集成

搭建Node.js 环境

我们将按照以下流程继续。

    1. 安装Nodebrew

 

    安装Node.js

安装nodebrew


以下是在 Homebrew 已经安装的前提下。
关于安装 Homebrew,我认为最好参考官方指南。


请使用Homebrew安装nodebrew,操作如下。

$ brew install nodebrew

安装Node.js

以下是使用 nodebrew 安装 Node.js 的步骤。如果您想了解更多关于 nodebrew 的详情,请访问 nodebrew 的 GitHub 页面。

$ nodebrew install-binary v8.11.1

顺便提一下,如果使用 nodebrew install,会花费很长时间,请注意。

现在你已经准备好使用 Node.js 了。

Angular的环境搭建

    1. 安装 Angular Cli

 

    1. 创建 Angular 应用程序

 

    1. 验证 Angular 应用程序的运行情况

 

    更改构建设置并进行构建

安装 Angular Cli

使用以下方式使用 npm 安装 Angular Cli。
(这次将全局安装)

$ npm install -g @angular/cli

创建 Angular 应用程序

使用Angular Cli创建Angular应用程序的模板如下。
(在下面的示例中,同时指定生成路由配置文件的设置。)

$ ng new sample-mean --routing=true

检查 Angular 应用程序的运行情况。

使用 `ng new` 命令创建的原型可以直接运行且完全正常。
通过以下命令,可以确认其暂时的运行状态。
(使用 `–open` 参数,应用程序启动后会自动在浏览器中打开页面)

$ cd sample-mean
$ ng serve --open

如果没有任何问题,将显示如下页面。

undefined

构筑设置的更改和构筑。

构建的文件默认情况下将输出到 dist/sample-mean。
为了适应后续讨论的后端,将输出位置更改为 dist。

"outputPath": "dist/client"

在更改设定之后,您可以使用以下命令进行构建。

$ ng build --prod

建立 Express 的环境配置

    1. 表达应用的安装方法

 

    1. 后端实现

 

    编译和启动

表达 Express 的安装

在ng new生成的根目录(即sample-mean目录下),通过npm install安装express和body-parser。

顺便说一句,body-parser 是一个中间件,用于解析请求体。
另外,使用 –save 选项会自动将安装的软件包添加到 package.json 的依赖项中。
(@types/~ 是 TypeScript 的类型定义。)

$ npm install --save express @types/express body-parser @types/body-parser @types/compression

后端实现

在使用ng new创建的Angular项目中,我们将添加使用Express构建的后端部分。
通过使用npm install express-generator命令安装的express命令,我们可以像使用ng new一样创建应用程序骨架,但在这种情况下,前端部分也会同时创建。
由于我们只想将Express作为后端使用,因此我们将逐个实施相关功能。

首先,按照以下的结构创建文件夹和文件。

sample-mean/
 └ server/
    ├ bin/
    │  └ www.ts
    ├ routes/
    │  └ users.ts
    ├ app.ts
    ├ config.ts
    └ tsconfig.json

接下来,我们将在创建的每个文件中编写实现。

{
  "compileOnSave": false,
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "es2017",
      "dom"
    ],
    "declaration": false,
    "sourceMap": true,
    "outDir": "../dist/server",
    "moduleResolution": "node",
    "typeRoots": [
      "../node_modules/@types"
    ],
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true
  }
}

import * as http from 'http';
import { app } from '../app';
import { serverPort } from '../config';

const port = process.env.PORT || serverPort;
app.set('port', port);

const server = http.createServer(app);

server.listen(port);

export const serverPort = 4300;

import * as express from 'express';
import * as path from 'path';
import { json, urlencoded } from 'body-parser';
import * as compression from 'compression';

import { userRouter } from './routes/users';

const app: express.Application = express();
app.disable('x-powered-by');

app.use(json());
app.use(compression());
app.use(urlencoded({ extended: true}));

app.use(express.static(path.join(__dirname, '../client')));

app.use('/api/users', userRouter);

app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, '../client/index.html'));
});

export { app };

import { Request, Response, Router } from 'express';

const userRouter: Router = Router();

const users = {
  users: [
    {
      id: 0,
      name: 'user0'
    },
    {
      id: 1,
      name: 'user1'
    }
  ]
};

userRouter.get('/', (req: Request, res: Response) => {
  res.json(users);
});

export { userRouter };

转译和启动

可以使用以下命令进行转译。
顺便说一下,您可以使用 npx 命令来运行本地包。

$ npx tsc -p ./server

当使用node命令来指定编译的端点时,服务器会启动。

$ node dist/server/bin/www.js

当访问 http://localhost:4300/api/users 时,应该会返回以下响应。

undefined

Angular 和 Express 的整合。

    1. 创建服务

 

    1. 创建组件

 

    1. 设置代理

 

    1. 设置路由

 

    1. 配置HttpClient模块

 

    1. 进行操作确认

 

    设置脚本

创建服务

服务将按以下的目录结构进行创建。(并不需要完全相同的结构。)

sample-mean/
 └ src/
    └ app/
       └ services/
          └ apis/
             ├ users-api.service.ts
             └ users-api.service.spec.ts

首先我们将创建目录。
以下预设当前目录为app。

$ mkdir -p services/apis

下一步,使用Angular CLI创建服务的框架。

$ ng g service usersApi

因为本次的目的是构建Mean stack,所以我们只需创建一个简单的方法来获取用户。

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class UsersApiService {

  constructor(
    private http: HttpClient
  ) { }

  getUsers() {
    return this.http.get('api/users');
  }
}

创建组件

不需要Angular和Express实际上的协作,但我们需要创建一个组件来确认协作结果作为视图。

组件将按照以下的目录结构进行创建。(不必要求完全相同的结构。)

sample-mean/
 └ src/
    └ app/
       └ component/
          └ users/
             ├ users.component.ts
             ├ users.component.spec.ts
             ├ users.component.css
             └ users.component.html

首先我们将创建一个目录。
以下假设当前目录是 “app”。

$ mkdir components

接下来,我们将使用Angular CLI来创建组件的框架。

$ ng g component users

我们将使用先前创建的服务来获取和显示用户的实现。

import { Component, OnInit } from '@angular/core';
import { UsersApiService } from '../../services/apis/users-api.service';

@Component({
  selector: 'app-users',
  templateUrl: './users.component.html',
  styleUrls: ['./users.component.css'],
  providers: [ UsersApiService ]
})

export class UsersComponent implements OnInit {
  users: any;

  constructor(
    private usersApiService: UsersApiService
  ) { }

  ngOnInit() {
    this.usersApiService.getUsers().subscribe(res => {
      this.users = res['users'];
    });
  }
}
<table border="1px">
  <tr>
    <th>id</th>
    <th>name</th>
  </tr>
  <tr *ngFor="let user of users">
    <td>{{user.id}}</td>
    <td>{{user.name}}</td>
  </tr>
</table>
<header>
  <h1 class="logo"><a href="/">MEAN</a></h1>
  <nav>
    <ul>
      <li><a routerLink="/users">Users</a></li>
    </ul>
  </nav>
</header>
<router-outlet></router-outlet>

代理服务器的设置

为了从Angular端访问Express端,需要进行代理设置。
在项目的根目录中创建proxy.conf.json文件,并按以下方式进行配置。
要使用此代理设置,在执行ng serve命令时,需要通过–proxy-config选项将proxy.conf.json传递给它。

{
  "/api": {
    "target": "http://localhost:4300",
    "secure": false,
    "changeOrigin": true
  }
}
$ ng serve --proxy-config proxy.conf.json

路由设置

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { UsersComponent } from './components/users/users.component';

const routes: Routes = [
  {
    path: 'users', component: UsersComponent
  },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

HttpClient 模块的配置

由于在创建的 service 中使用了 HttpClientModule,因此需要将其添加到 app.module.ts 中。

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { UsersComponent } from './components/users/users.component';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [
    AppComponent,
    UsersComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

确认行动

首先,启动Express端。

$ node dist/server/bin/www.js

然后加载代理设置,启动Angular端。

$ ng serve --proxy-config proxy.conf.json

如果一切正常,应该显示如下画面。

访问了 http://localhost:4200 的页面。

undefined

点击屏幕上的“Users”,显示用户内容的屏幕。

undefined

脚本的设置

先前的操作确认中,我分别启动了Angular和Express端。
此外,Angular端需要指定代理设置。
为了提高效率,在继续开发时不再重复执行这些步骤,
我将修改package.json中的scripts并一直使用它们。

首先,安装nodemon和npm-run-all两个软件包。

$ npm install --save-dev npm-run-all nodemon

接下来,将 package.json 的内容修改为以下方式。

  "scripts": {
    "ng": "ng",
    "tsc": "tsc -p ./server",
    "watch:ng": "ng serve --open --proxy-config proxy.conf.json",
    "watch:tsc": "tsc -w -p ./server",
    "watch:nodemon": "nodemon dist/server/bin/www.js",
    "start:p": "run-p watch:*",
    "start": "run-s tsc start:p",
    "build:ng": "ng build --prod",
    "build": "run-p tsc build:ng",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },

如果按照上述的方法修改脚本,那么使用以下命令可以同时启动Angular和Express,并且如果有任何代码更改,它们都会被自动更新到相应的源代码中。

$ npm start

MongoDB的环境配置

这次不会在本地环境上安装MongoDB,而是准备在Docker上安装。并且想要同时准备MongoDB的Web GUI环境Express,所以会使用docker-compose。

    1. 创建docker-compose.yml文件

 

    1. 启动容器

 

    准备mongodb

编写一个docker-compose.yml文件。

我会创建一个类似以下的 docker-compose.yml 文件。
MongoDB 部分会绑定到 27017 端口,
而 Express 部分会绑定到 8081 端口。
此外,Express 还会添加 Basic 认证的设置。

version: '3.6'

services:
  mongo:
    image: mongo
    restart: always
    volumes:
      - mongodb-data:/data/db
    environment:
      MONGO_INITDB_ROOT_USERNAME: username
      MONGO_INITDB_ROOT_PASSWORD: password
    ports:
      - 27017:27017

  mongo-express:
    image: mongo-express
    restart: always
    ports:
      - 8081:8081
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: username
      ME_CONFIG_MONGODB_ADMINPASSWORD: password
      ME_CONFIG_BASICAUTH_USERNAME: username
      ME_CONFIG_BASICAUTH_PASSWORD: password

volumes:
  mongodb-data:

启动容器

切换到包含docker-compose.yml文件的目录,并执行以下命令。
-d用于以后台模式运行的选项。

$ docker-compose up -d

MongoDB的准备工作

当容器启动后,使用以下命令进入MongoDB容器内部。

$ docker exec -it mongo_mongo_1 bash

进入容器后,启动 MongoDB 的 shell。

$ mongo admin -u username -p password

添加一个用于创建数据库的用户。

> use sample_db

> db.createUser({
  ... user: "sample",
  ... pwd: "sample",
  ... roles: [{
  ... role: "readWrite",
  ... db: "sample_db"
  ... }]
  ... })

创建对应于关系型数据库(RDB)中的表的集合(Collection)。

> db.createCollection("users")

最后,我会创建一些虚拟数据。

> for(let i = 0; i < 20; i++) {
  ... db.users.save({id: i, name: "name" + i})
  ...} 

Express 和 MongoDB 的整合

    1. 安装 Mongoose

 

    1. 创建模型

 

    1. 配置与 MongoDB 的连接

 

    进行操作验证

安装 mongoose

$ npm install --save mongoose @types/mongoose

创建模型

创建目录并添加文件,使其具有以下类似的目录结构。

sample-mean/
 └ server/
    └ models/
       └ users.model.ts
import * as mongoose from 'mongoose';

const usersModel: mongoose.Model<mongoose.Document> = mongoose.model(
  'users',
  new mongoose.Schema({
      id: {
        type: Number
      },
      name: {
        type: String
      }
    })
);

export { usersModel };

与 MongoDB 的连接设置

export const serverPort = 4300;
export const mongoUrl = 'mongodb://sample:sample@localhost/sample_db';
import * as http from 'http';
import { app } from '../app';
import { serverPort, mongoUrl } from '../config';
import * as mongoose from 'mongoose';

const port = process.env.PORT || serverPort;
app.set('port', port);

const server = http.createServer(app);

server.listen(port, () => {
  mongoose.connect(mongoUrl);
});
import { Request, Response, Router } from 'express';
import { usersModel } from '../models/users.model';

const userRouter: Router = Router();

userRouter.get('/', (req: Request, res: Response) => {
  usersModel.find({}, function(err, users) {
    if (err) { throw err; }
    res.json(users);
  });
});

export { userRouter };

确认行动

请在确认操作之前,将users.component.ts文件进行以下修改。

import { Component, OnInit } from '@angular/core';
import { UsersApiService } from '../../services/apis/users-api.service';

@Component({
  selector: 'app-users',
  templateUrl: './users.component.html',
  styleUrls: ['./users.component.css'],
  providers: [ UsersApiService ]
})

export class UsersComponent implements OnInit {
  users: any;

  constructor(
    private usersApiService: UsersApiService 
  ) { }

  ngOnInit() {
    this.usersApiService.getUsers().subscribe(res => {
      this.users = res;
    });
  }
}

当访问http://localhost:4200/users时,如果显示了以下在MongoDB中创建的虚拟数据,则表示一切正常。

undefined

请参考

Mean stack入门

广告
将在 10 秒后关闭
bannerAds