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

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

内容简介:在大概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 :

nestjs 将 typeScript 引入并基于 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.ts 和 service.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 的掌握程度有限, 只是拿来练练手. 可能后续会基于这篇文章继续深入地去讲讲, 比如部署之类的, 两个项目也会不断去维护. 后续也有计划会合二为一. 看时间吧!


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

查看所有标签

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

自制编译器

自制编译器

[日] 青木峰郎 / 严圣逸、绝云 / 人民邮电出版社 / 2016-6 / 99.00元

本书将带领读者从头开始制作一门语言的编译器。笔者特意为本书设计了CЬ语言,CЬ可以说是C语言的子集,实现了包括指针运算等在内的C语言的主要部分。本书所实现的编译器就是C Ь语言的编译器, 是实实在在的编译器,而非有诸多限制的玩具。另外,除编译器之外,本书对以编译器为中心的编程语言的运行环境,即编译器、汇编器、链接器、硬件、运行时环境等都有所提及,介绍了程序运行的所有环节。一起来看看 《自制编译器》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

SHA 加密
SHA 加密

SHA 加密工具