一次TypeScript, React, Node, MongoDB的模板式前后端分离开发实践

栏目: 数据库 · 发布时间: 5年前

内容简介:在大概1年前接触了typescript之后, 日渐被它所吸引. 甚至一个简单的本地测试文件综上, 于是就有了今天这个话题:答案是: 不存在的:sweat_smile:

在大概1年前接触了typescript之后, 日渐被它所吸引. 甚至一个简单的本地测试文件 node ./test.js 有时也会切到 ts-node ./test.ts . 在同样的时间节点之前, 还是会不时地去学学node, mongodb相关的. 可是, 由于懒(需)惰(求), 在很久没碰之后, 很多知识点都忘了!:sleeping:

综上, 于是就有了今天这个话题:

论 如何在工作时间之余完成自己的个人项目并实现按时上床睡觉

答案是: 不存在的:sweat_smile:

项目简介

项目会不断维护. 无论是client端还是server端, 都只提供简单的模板式的功能.

地址

client ts-react-webpack

server showcase

线上体验

依赖

typescript是两端的基调

client

  • webpack-4.x
  • typescript-3.0.x
  • react-16.4.x
  • mobx-5.x
  • ant design
  • ...

详看

server

centos上 mongodb 的官网安装教程 , 其他系统请自行查阅.

  • nestjs
  • dotenv
  • jsonwebtoken
  • mongodb(mongoose)
  • ...

需要讲一下我为什么选了 nestjs :

nestjstypeScript 引入并基于 express 封装. 意味着, 它与绝大部分 express 插件的兼容性都很好.

nestjs 的核心概念是提供一种体系结构, 它帮助开发人员实现层的最大分离, 并在应用程序中增加抽象.

此外, 它对测试是非常友好的...

也需要声明的是, nestjs 的依赖注入特性是受到了 angular 框架的启发, 相信做 angular 开发的对整个程序体系会更容易看懂.

查看中文文档

具体实现

server

简单介绍下几个主流程模块

main.ts

我是用nest-cli工具初始化项目的, 一切从 src/main.ts 开始

import { NestFactory } from '@nestjs/core'
import * as dotenv from 'dotenv'
import { DOTENV_PATH } from 'config'

// 优先执行, 避免引用项目模块时获取环境变量失败
dotenv.config({ path: DOTENV_PATH })

import { AppModule } from './app.module'

async function bootstrap() {
    const app = await NestFactory.create(AppModule)
    // 支持跨域
    app.enableCors()
    await app.listen(9999)
}
bootstrap()
复制代码

同样地, 我们可以提供一个 express 实例到 NestFactory.create :

const server = express();
const app = await NestFactory.create(ApplicationModule, server);
复制代码

这样我们就可以完全控制 express 实例生命周期, 比如官方 FAQ中说到的创建几个同时运行的服务器

在我本地开发的时候, 根目录上还有一个 .dev.env , 这是未提交到github的, 因为里面包含了我个人的 mongodb 远程 ip 地址 其他内容与github上的 .env 一致, 因为我本地并不想再安装一遍 mongodb , 如果是刚刚把项目拉下来就跑起来的, 无论如何你都需要一个 mongodb 服务, 当然你是可以本地安装就好了.

还需要提及到一点就是调试:

以前在vscode上调试node程序都需要在调试栏新增配置, 然后利用该配置去跑起应用才能实现断点调试, 新版的vscode支持autoAttach功能, 使用 Command + Shift + P 唤起设置功能面板

一次TypeScript, React, Node, MongoDB的模板式前后端分离开发实践

启动它!

这样, 在项目的 .vscode/setting.json 里面会多了一个选项: "debug.node.autoAttach": "on" , 在我们的启动script里面加上 --inspect-brk 就可以实现vscode的断点调试了. 对应地, npm run start:debug 是我的启动项, 可参考 nodemon.debug.json

app.module.ts

import { Module } from '@nestjs/common'
import { MongooseModule } from '@nestjs/mongoose'

import { DB_CONN } from 'config/db'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import modules from 'routers'

@Module({
    imports: [
        MongooseModule.forRoot(DB_CONN, {
            useNewUrlParser: true,
        }),
        ...modules,
    ],
    controllers: [AppController],
    providers: [AppService],
})
export class AppModule {}
复制代码

每个 Nest 应用程序至少有一个模块, 即根模块. 根模块是 Nest 开始安排应用程序树的地方. 事实上, 根模块可能是应用程序中唯一的模块, 特别是当应用程序很小时, 但是对于大型程序来说这是没有意义的. 在大多数情况下, 您将拥有多个模块, 每个模块都有一组紧密相关的功能. 当然, 模块间也可以共享.

概念 解释
providers Nest 注入器实例化的提供者,并且可以至少在整个模块中共享
controllers 必须创建的一组控制器
imports 导入模块所需的导入模块列表
exports 此模块提供的提供者的子集, 并应在其他模块中使用

参考 module 的文档

AppController 在这个程序当中只是为了测试能返回 Hello World!!! , 其实它不是必须的, 我们可以把它直接干掉, 把全部接口, 全部逻辑放到各个 module 中实现, 以 modules/user 为例, 接着往下看.

modules/user

目录结构

user
├── dto -------------- 数据传输对象
├── index.ts --------- UserModule, 概念同AppModule
├── controller.ts ---- 传统意义的控制器, `Nest`会将控制器映射到相应的路由
├── interface.ts ----- 类型声明
├── schema.ts -------- mongoose schema
├── service.ts ------- 处理逻辑
复制代码

有必要讲讲 controller.tsservice.ts , 这是nestjs的概念中很重要的部分

controller.ts

import { Get, Post, Body, Controller } from '@nestjs/common'

import UserService from './service'
import CreateDto from './dto/create.dto'

@Controller('user')
export default class UserController {
    constructor(private readonly userService: UserService) {}

    @Get()
    findAll() {
        return this.userService.findAll()
    }

    @Post('create')
    create(@Body() req: CreateDto) {
        return this.userService.create(req)
    }
}
复制代码

装饰器路由为每个路由(user)声明了前缀,所以 Nest 会在这里映射每个 /user 的请求

@Get() 装饰器告诉 Nest 创建此路由路径的端点

同样地, @Post() 也是如此, 并且这类Method装饰器接收一个 path 参数, 如 @Post('create') , 那么我们就可以实现post到路径 /user/create

到此, 往后的逻辑交给 service 实现

service.ts

import { Injectable } from '@nestjs/common'
import { InjectModel } from '@nestjs/mongoose'
import { Model } from 'mongoose'

import logger from 'utils/logger'
import { cryptData } from 'utils/common'
import ServiceExt from 'utils/serviceExt'
import { IUser } from './interface'
import CreateDto from './dto/create.dto'

@Injectable()
export default class UserService extends ServiceExt {
    constructor(@InjectModel('User') private readonly userModel: Model<IUser>) {
        super()
    }

    async create(createDto: CreateDto) {
        if (!createDto || !createDto.account || !createDto.password) {
            logger.error(createDto)
            return this.createResData(null, '参数错误!', 1)
        }
        const isUserExist = await this.isDocumentExist(this.userModel, {
            account: createDto.account,
        })
        if (isUserExist) {
            return this.createResData(null, '用户已存在!', 1)
        }
        const createdUser = new this.userModel({
            ...createDto,
            password: cryptData(createDto.password),
        })
        const user = await createdUser.save()
        return this.createResData(user)
    }

    async findUserByAccount(account: string) {
        const user = await this.userModel.findOne({ account })
        return user
    }

    async findAll() {
        const users = await this.userModel.find({})
        return this.createResData(users)
    }
}

复制代码

至此, 我们运行 npm run start:dev 启动一下服务:

直接在浏览器端访问 http://localhost:9999/#/

一次TypeScript, React, Node, MongoDB的模板式前后端分离开发实践

没错, 的确失败了!!! 因为我们使用了 jsonwebtoken , 在 modules/auth 可以看到它的实现.

现在我们在 postman 中登录了再试试吧!

一次TypeScript, React, Node, MongoDB的模板式前后端分离开发实践
一次TypeScript, React, Node, MongoDB的模板式前后端分离开发实践

bingo!!!

(如果是想拉下来跑的话, 也可以照着 schema 的格式用 postman 先伪造条用户数据, 把系统打通!!!)

client

关于client端的实现我不会细讲, 可以看 项目github 我之前的文章( typescript-react-webpack4 起手与踩坑 ), 项目结构会有改动.

讲一下接入了真实服务器之后http请求对于token的一些处理, 查看 http.ts

首先是创建 axios 实例时需要在 header 处把token带上

const axiosConfig: AxiosRequestConfig = {
    method: v,
    url,
    baseURL: baseUrl || DEFAULTCONFIG.baseURL,
    headers: { Authorization: `Bearer ${getCookie(COOKIE_KEYS.TOKEN)}` }
}
const instance = axios.create(DEFAULTCONFIG)
复制代码

cookie也可以存放在localStorage

另外一点是, 对应服务端返回的token错误处理

const TOKENERROR = [401, 402, 403]
let authTimer: number = null
...

if (TOKENERROR.includes(error.response.status)) {
    message.destroy()
    message.error('用户认证失败! 请登录重试...')
    window.clearTimeout(authTimer)
    authTimer = window.setTimeout(() => {
        location.replace('/#/login')
    }, 300)
    return
}
复制代码

总结

两端项目都是简单的模板项目, 不存在什么繁杂的业务, 属于比较初级的学习实践. 对 nestjs 的掌握程度有限, 只是拿来练练手. 可能后续会基于这篇文章继续深入地去讲讲, 比如部署之类的, 两个项目也会不断去维护. 后续也有计划会合二为一. 看时间吧!


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

打火机与公主裙·荒草园

打火机与公主裙·荒草园

Twentine / 青岛出版社 / 2017-3 / 36.00元

“如果人临死前真有走马灯这个环节,她大概会是我这辈子见的最后一人。” 从青涩的校园时代里一抹明亮的金,到厮杀的职场中那化不开的黑,李峋就像荒芜之地的一株野草,受到再大的挫折依然固执地生长。 如果说朱韵从前的生活一直维持着表面的顺风顺水,平静安和,那李峋的出现则打破了这一切。他是她生命中第一次,也是唯一一次的冒险。 在外人眼里李峋嚣张而轻蔑,只有朱韵懂得他心中那片自留地,自愿成为孤......一起来看看 《打火机与公主裙·荒草园》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试