構築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
任务流程
-
- Node.js的开发环境配置
-
- Angular的开发环境配置
-
- Express的开发环境配置
-
- Angular和Express的集成
-
- MongoDB的开发环境配置
- Express和MongoDB的集成
搭建Node.js 环境
我们将按照以下流程继续。
-
- 安装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的环境搭建
-
- 安装 Angular Cli
-
- 创建 Angular 应用程序
-
- 验证 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
如果没有任何问题,将显示如下页面。
构筑设置的更改和构筑。
构建的文件默认情况下将输出到 dist/sample-mean。
为了适应后续讨论的后端,将输出位置更改为 dist。
"outputPath": "dist/client"
在更改设定之后,您可以使用以下命令进行构建。
$ ng build --prod
建立 Express 的环境配置
-
- 表达应用的安装方法
-
- 后端实现
- 编译和启动
表达 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 时,应该会返回以下响应。
Angular 和 Express 的整合。
-
- 创建服务
-
- 创建组件
-
- 设置代理
-
- 设置路由
-
- 配置HttpClient模块
-
- 进行操作确认
- 设置脚本
创建服务
服务将按以下的目录结构进行创建。(并不需要完全相同的结构。)
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 的页面。
点击屏幕上的“Users”,显示用户内容的屏幕。
脚本的设置
先前的操作确认中,我分别启动了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。
-
- 创建docker-compose.yml文件
-
- 启动容器
- 准备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 的整合
-
- 安装 Mongoose
-
- 创建模型
-
- 配置与 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中创建的虚拟数据,则表示一切正常。
请参考
Mean stack入门