本系列教程的第一部分将介绍如何为实际的 Angular 应用程序设置云托管的 MongoDB 数据库、Node 服务器和前端。
- 介绍:我们打算做什么?
- 配置 Angular 应用
- 配置托管的 MongoDB
- Auth0 的配置
- Node.js 服务器设置
- Angular: 创建 HomeComponent
- Angular: 布局和全局组件
介绍:我们打算做什么?
本系列教程将教你如何构建一个真实的基于MEAN技术栈的应用程序,涵盖从构思和数据建模到生产部署的所有内容。
为了了解生产级的 JavaScript web 应用程序开发的来龙去脉,我们将会构建一个围绕事件的应用程序。通过 RSVP 应用程序,管理员能够发布、更新和删除事件信息;其他用户能够回复事件。这个 RSVP 应用程序的功能将包括以下:
- 身份验证和角色授权(客户端和服务端)
- 使用 API 进行 CRUD 操作
- 搜索和过滤
- 模版驱动的表单
- 具有自定义验证的响应式表单
- 简单的动画
- 延迟加载
- 在 VPS 上使用 nginx 和 SSL 进行生产部署
废话不多说,开始吧!
配置 Angular 应用
整个系列我们将使用Angular CLI 进行构建开发,所以保证你已经全局安装了 CLI:
1 | $ npm install -g @angular/cli |
Angular CLI: 6.1.5 Node: 10.7.0 Angular: 6.1.10
创建 Angular 项目
安装好 CLI 后,打开终端并进入你想创建项目的路径,执行下面的命令:
1 | ng new mean-rsvp --routing --style scss |
CLI 会生成一个带有路由模块和 SCSS 支持的 Angular 项目。一旦项目依赖安装完成,我们就可以着手开发了。
添加 Title 服务
为了能够在路由时动态地改变页面标题,我们需要使用 Angular 内置的Title服务。这是因为我们创建的是一个单页应用,<title>标签并不在我们的 Angular 应用程序内,所以我们无法对它进行操作。
1 | // src/app/app.module.ts |
添加 Bootstrap
打开src/index.html并添加Bootstrap样式 CDN:
1 | <!-- src/index.html --> |
或者通过angular.json配置styles引入 bootstrap 样式(此处略)。
全局 SCSS
现在我们将添加一些 SCSS 来管理应用程序的全局样式,包含基本的布局和媒体查询。
首先在src/assets下创建新的scss文件夹,然后将src/styles.scss移动到这个新建的路径。接着修改 angular.json 中对应项目的styles:
1 | ... |
做完这些,项目的样式将管理在在 assets 文件夹中。
BASE STYLES
1 | /* src/assets/scss/_base.scss */ |
Bootstrap 提供了大量样式,_base.scss则提供了一些基本的帮助和改进样式。
VARIABLES AND PARTIALS
创建新的文件夹src/assets/scss/partials,添加_layout_vars.scss文件:
1 | /* src/assets/scss/partials/_layout.vars.scss */ |
再添加_responsive.partial.scss文件:
1 | /* src/assets/scss/partials/_responsive.partial.scss */ |
文件包含了一个$large变量,其中包含一个用于大屏幕大小的媒体查询,以及一个mq() 混合,用于在 SCSS 中轻松定位媒体查询。如果有必要,我们可以随着应用程序的增长向该文件添加更多变量。
IMPORT GLOBAL SCSS
最后,我们整合这些创建的样式,以导入项目中。还记得前面项目样式放在assets/scss/styles.scss吗, 所以只需:
1 | /* src/assets/scss/styles.scss */ |
配置托管的 MongoDB
MongoDB是一个开源的文档数据库。为了提高速度和易用性,我们将在应用程序的数据库中使用mLab的免费云托管 MongoDB 部署。我们还将通过MongoBooster连接管理 MongoDB。
具体的账号注册,数据库创建连接管理这里就不再赘述,相信电脑前聪明的你很快就能搞定!
Auth0 的配置
到了这里说明你已经配置好数据库啦,真棒!接下来,我们的 Angular 应用程序和 Node API 将使用 IDaaS(身份即服务)平台Auth0进行身份验证和路由授权。
注册免费账号
我们需要一个 Auth0 帐户来管理身份验证。你可以在这里注册一个免费帐户。接下来,设置一个 Auth0 应用程序和 API,这样 Auth0 就可以与 Angular 应用程序和 Node API 进行交互。
配置应用程序
- 进入 Dashboard,创建新的应用程序,如
RSVP MEAN App,并选择SPA. - 切换到
Settings页面,Allowed Callback URLs:添加http://localhost:8083/callback和http://localhost:4200/callback. - Allowed Web Origins,添加
http://localhost:8083和http://localhost:4200. - Allowed Logout URLs, 添加
http://localhost:4200. - 高级设置的 OAuth 下的 JsonWebToken Signature Algorithm,确保设置为
RS256.
我们在回调 url 中添加了两个端口,并允许 web 源,因为我们将在开发期间从这两个端口运行和测试应用程序。端口 4200 是 Angular CLI 服务于 Angular 应用的端口。端口 8083 是我们的 Node API 和服务器使用的端口:为了测试产品构建,这是必要的。项目部署后,我们将替换这些设置为生产环境。
配置 API
- 切换到 API,创建 API 配置。输入 API 的名称(例如:RSVP 表示 API)。
- 将标识符设置为 API 端点 URL。此标识符是授权调用的
audience参数。在我们的应用程序中,是http://localhost:8083/api/。 - 签名算法配置为
RS256。
Node.js 服务器设置
紧接着就是我们的 Node 服务器和 API 了。
安装依赖项
在我们的 Angular 项目根目录下,运行下面命令:
1 | $ npm install express body-parser express-jwt jwks-rsa method-override mongoose cors --save |
服务端文件结构
在项目根目录下创建server文件夹并在其中添加两个文件:server/api.js和server/config.js,同时在根目录下创建server.js文件。至此文件结构大概如下:
1 | ... |
配置项
打开server/config.js,添加如下配置:
1 | module.exports = { |
记住将上述的相应值替换成你的配置,这些配置可以在 Auth0 和 mLab 账号里找到。
Node Server
打开server.js,添加如下配置:
1 | // server.js |
请注意,有几个部分是与环境相关的。对于开发,我们希望能够利用 Angular CLI 提供和监视文件的功能,而不需要每次检查工作时都构建一个完整的项目。为了便于实现这一点,我们将从开发中分离 Node.js 服务器和 Angular 前端开始。
这样,我们就可以在localhost:8083上运行 Node API,而 Angular 应用程序在localhost:4200上运行。对于生产环境,我们希望 Node 服务器运行 API 并使用静态路径来提供前端服务。
API 路由
打开api.js文件并编辑:
1 | // server/api.js |
Auth0 API 结合express-jwt和jwks-rsa一起使用,我们可以在必要时实现对特定 API 路由进行保护。实现这一点,我们可以通过向希望保护的路由添加jwtCheck中间件函数。
启动项目
为了方便开发,全局安装nodemon来监视 Node 服务器的变化,而不需要在更新之后重新启动:npm install nodemon -g
开发阶段,我们会经常性的修改项目,所以打算使用分开的终端窗口来启动Angular App和Node API。
1 | # Angular App => http://localhost:4200 |
Angular: 创建 HomeComponent
运行下面的命令,添加一个主页面组件:
1 | $ ng g component pages/home |
将新创建的 Home 组件添加到路由:
1 | // src/app/app-routing.module.ts |
使用 Title 服务
前面我们已经在Angular App Setup全局注入了Title服务,现在我们可以直接使用它:
1 | // src/app/pages/home/home.component.ts |
上述代码导入了 Title 服务,然后添加一个名为 pageTitle 的属性,默认值为Events。然后我们将 Title 服务传递给构造函数,在ngOnInit()生命周期方法中,我们将使用Title. settitle()方法将文档标题更改为本地pageTitle的值。通过将这个标题存储在属性中,我们还可以在组件的模板中使用它来设置标题:
1 | <!-- src/app/pages/home/home.component.html --> |
文档标题和标题现在应该显示在浏览器中。我们已经有了路由和 home 组件,接下来我们可以开始 Angular 应用的全局布局了。
Angular: 布局和全局组件
接下来我们将会设置 Angular 应用的布局和全局元素,比如页眉、导航和页脚。我们希望应用程序可以在任何大小的浏览器中工作,因此我们将实现非画布导航。为此,我们需要向根应用程序组件AppComponent添加一些标记和功能,以及创建一个页眉和页脚。
1 | $ ng g component header |
整个项目将会忽略相应的测试代码.spec.ts。
Header Component
打开生成的HeaderComponent:
1 | // src/app/header/header.component.ts |
HeaderComponent包含了一个导航链接和折叠开关,我们通过@Output声明一个EventEmitter用于和父组件进行交互,通知折叠按钮的闭合。
navOpen属性默认是闭合的,所以我们在组件的ngOnInit()钩子中通过观察路由事件,在路由开始时,闭合菜单面板。
当用户点击折叠按钮,会调用toggleNav()方法,它改变折叠状态,并向父组件传递新的状态,通知折叠按钮的变化。
header.component.html相关模板:
1 | <!-- src/app/header/header.component.html --> |
1 | /* src/app/header/header.component.scss */ |
详细代码请查阅源码,这个文件提供了nav和header的样式,以及将折叠图标动画成X和back的 CSS 样式。值得注意的是,当访问当前组件外部的类时,可以使用特殊的选择器:host-context(.ancestor-class)来访问组件的封装之外的类并向上访问树。
Footer Component
我们的底部非常简单,打开footer.component.html和footer.component.scss:
1 | <!-- src/app/footer/footer.component.html --> |
1 | /* src/app/footer/footer.component.scss */ |
上面把底部的 margin/padding(边距/填充) 移到宿主元素,这样段落边距就不会影响下一步窗口高度的计算。
App Component
现在我们可以在根组件里使用 Header 和 Footer 了。打开app.component.ts:
1 | // src/app/app.component.ts |
上面创建了一个navOpen属性来存储HeaderComponent导航面板的状态。navToggledHandler将处理子组件 Header 发出的navToggled事件,并同步更新navOpen的值。同时,观察订阅窗口大小调整事件,调用_resizeFn()处理程序,以确保布局画布的高度与浏览器视图的高度匹配。
我们也可以通过 layout canvas 元素上设置height: 100vh样式来达到同样的效果,但是由于在移动浏览器中与 vh 不一致,所以采用了 JS 代码的方式。
打开app.component.html编辑模板:
1 | <!-- src/app/app.component.html --> |
上面使用了几个布局容器来管理导航面板,同时通过navOpen属性来动态添加/移除样式。
还记得之前 Header 组件里提到的:host-context()吗,Header 组件的样式里就利用了上面的nav-open等这些类。
利用[style.min-height]可以动态改变元素的高度。
注意这是一个 DOM 属性,而不是 HTML 属性。注意到其中的差别是很重要的。请务必通读绑定语法:HTML 属性 vs. DOM 属性
最后是app.component.scss,具体请查阅源码。
至此,我们已经完成了项目的基础结构和全局组件,可以进一步开发了。
总结
这一部分介绍了 MEAN 技术栈应用程序所需的软件和工具的设置以及依赖关系。还建立了 Angular 前端的基本布局和架构。在 Angular 系列的下一部分中,我们将讨论身份验证和授权、功能模块规划和数据建模。
系列索引