精华内容
下载资源
问答
  • 本篇我们来介绍使用TS来做一些事情。什么是TSTypeScript 是微软开发一款开源的编程语言,本质上是向 JavaScript 增加静态类型系统。它是 JavaScript 的超集,所有现有的 JavaScript 都可以不加改变...

    2a3b1910ede07ffbc965935cc6b81700.png
    当我们在开发维护一些工具类项目的时候,随着功能的丰富以及维护人员的变更,会导致代码的可持续维护性下降,因此需要一些其他工具来帮我们提高代码质量,减少一些不必要的错误。本篇我们来介绍使用TS来做一些事情。

    什么是TS

    TypeScript 是微软开发一款开源的编程语言,本质上是向 JavaScript 增加静态类型系统。它是 JavaScript 的超集,所有现有的 JavaScript 都可以不加改变就在其中使用。它是为大型软件开发而设计的,它最终编译产生 JavaScript,所以可以运行在浏览器、Node.js 等等的运行时环境。

    TS能做什么

    首先TS的定位是静态类型语言,而不是类型检查器(对比flow)。

    从开发工具提供的能力看也不仅仅是类型检查,很直观的就是Intellisense over Compilation Error,当一段代码有问题(比如少写了字母)时,写完马上就会有红色波浪线提示,而不是等到编译的时候才告诉你哪一行有问题。

    因此使用TS提供的类型系统+静态分析检查+智能感知/提示,使大规模的应用代码质量更高,运行时bug更少,更方便维护。

    对比js有哪些优势

    • 开发效率

    虽然需要多写一些类型定义代码,但TS在WebStorm等IDE下可以做到智能提示,智能感知bug,同时我们项目常用的一些第三方类库框架都有TS类型声明(@types管理),我们也可以给那些没有TS类型声明的稳定模块写声明文件,这在团队协作项目中可以提升整体的开发效率。

    • 可维护性

    长期迭代维护的项目开发和维护的成员会有很多,人员的不稳定性和团队成员水平的差异的差异性,以及软件本身具有熵的特质,导致长期迭代维护的项目总会遇到可维护性逐渐降低的问题。而有了强类型约束和静态检查,以及智能IDE的帮助下,可以降低软件腐化的速度,提升可维护性。并且如果在重构代码时,强类型和静态类型检查会帮上大忙,一定程度上减少重构代价。(大家开发维护起来更安全、放心)。

    • 线上运行质量

    我们现在的工具类项目很多bug都是由于一些调用方和被调用方的数据格式不匹配引起的。TS可以在编译期进行静态检查,可以在编写调试代码时就发现这些问题,并且IDE可以智能纠错,编码时就能提前感知bug的存在,我们的线上运行时质量会更为稳定可控。

    Flow、babel、tsc

    • 类型检查

    flow用来做类型检查,比如vue就是用的flow,但是flow也有很多问题:

    1. 无用的错误信息

    比如 Incompatible instantiation for T, T 是一个类型变量,但是你并不能迅速找到这个错误在哪里。

    2.运行困难

    运行 Flow是需要一定成本的。对于Mac 用户来说非常幸运,通过 homebrew 可以安装预制的二进制包。但如果你需要自己编译它,你就先得建立一套 OCaml 开发环境。

    • 代码处理

    babel相比于tsc,首先定位是不同的,babel是一种js预处理工具,理论上说完全可以实现对ts的预处理,但是tsc对ts处理会更加精细。当然tsc 的功能没有 babel 多,扩展性也没有 babel 强。

    项目的应用

    我们的开源脚手架[builder-webpack4](https://github.com/feflow/builder-webpack4)已经实践了几个月了,为了更好的维护,我们决定迁移到ts。这中间踩了一些坑,但也积累了一些经验。

    • tsconfig配置

    ts配置文件有很多配置项,但是对于我们开发node工具来说其实用到的并不多,我们只需要关注模块化,编译路径和输出路径即可。

    关于模块化,我们希望输出的是commonjs规范的,至于最终是es5/es6或是其他,因人而异,我们需要配置的就是:

    "compilerOptions": {
        "module": "commonjs",
        "esModuleInterop": true,
        "target": "es5",
        "moduleResolution": "node"
    }

    编译路径和输出路径,这里跟webpack类似,当然这里的编译路径是指定tsc编译哪些目录下的ts文件,否则编译会因为内容太多而报错。

    "compilerOptions": {
        "outDir": "lib" //输出路径
      },
      //编译目录
      "include": [
        "src/**/*"
      ],

    当然我们还可能会指定types的路径

    "paths": {
          "*": [
            "node_modules/*",
            "src/types/*"
          ]
        }
    • npm包的types

    对于多数的npm包来说都可以通过安装@types/xxx来解决,比如node我们就可以安装@types/node,当然也有一些@types并没有做管理,那就需要我们自己来写一下。举个例子,webpack处理html相关会用到一个插件“html-webpack-plugin”,它是作为一个模块来使用,那么只需要以下声明即可

    declare module 'html-webpack-plugin';

    当然你可能会用到某些UMD的包(既可以当模块又可以作为全局变量使用):

    declare namespace UMD{
     	//可以定义一些其他东西
    }
    • interface(接口)

    比如我们有些方法需要修改一些参数,使用TypeScript 之后,把数据对应的 interface 改掉,然后重新编译一次,把编译失败的地方全部改掉就好了。

    对于builder-webpack4来说很多方法的参数都较为复杂,比如我们生成构建配置文件的时候,webpack的配置老多了,自然是需要写个interface来控制,但是问题是如果别的模块调用这个方法又得重写一次?不用的,只要吧interface当作模块一样导出即可(当然你也可以把这些写到d.ts中,然后引入到对应的文件)。

    export interface xxx{
        [propName: string]: any
    }
    • 静态类型检查

    当我们开始盲写代码的时候,总会不可避免的有些小失误,那么利用IDE配合ts提供的工具就可以帮我们提前发现一些问题;在编译调试中同样可以发现一些未触及的点。

    600e7875b055aa6138bced2a250f00e9.png
    设置图片的编译规则

    我们在调用方法的时候就知道这个方法需要哪些参数,当然如果类型写错了就立马会有红色波浪线标注出来(格外的扎眼)。

    6e4ebfe86bbff6bc0952a55bc50ce548.png
    传入错误的参数
    • 代码质量的提升

    作为一种弱类型语言,js开发一些大型/持续维护项目的时候,经常会让人体验什么是“开发一时爽,重构火葬场”(ts在Q你了)。

    • 其他注意点

    对于模块的导出

    export default builderWebpack4;

    这个玩意编译出来其实是这样子的

    exports.default = builderWebpack4;

    但是对于调用者来说并不能直接用,我们想要的是

    module.exports = builderWebpack4;

    所以源码中多加个指定就好了

    module.exports = exports.default;

    当然对于项目内部间模块调用是不需要的,tsc构建会生成相关的hack

    var __importDefault = (this && this.__importDefault) || function (mod) {
        return (mod && mod.__esModule) ? mod : { "default": mod };
    };
    Object.defineProperty(exports, "__esModule", { value: true });
    var webpack_1 = __importDefault(require("webpack"));

    什么时候需要用

    • 大型项目

    项目越大,越难以维护,因此对于多人写协作的大型项目有必要使用ts来提高代码质量。

    • 持续维护的项目

    对于一些长久运行的项目,既要保证它的稳定运行,又要保证项目交接便捷(可维护性),使用ts是目前来看最好选择。

    • 工具类项目

    使用nodejs/js写一些前端工具或者库的时候,同样是需要关注以上两点内容,而且工具类的项目影响范围较大,在开发维护中要更加谨慎,那么使用ts帮我们尽量减少一些低级错误是很有必要的。

    写在最后

    [feflow](https://www.feflowjs.org)的开源脚手架[builder-webpack4](https://github.com/feflow/builder-webpack4)已经切换为ts编写了,欢迎大家使用,如果有任何问题可以提交issue。

    展开全文
  • 近两个月把 React Native、Vue 3.0 和 Nest.js 都摸了一下,大概都摸懂了。鉴于掘金已经很多优秀的 Vue 3.0 教程了,本人自认为文笔远逊于掘金的大佬们,就没有班门弄斧也写一篇了(本来很想了,后来想想感觉炒冷饭...

    4f3e20ac2d6297ccfcdb7a212e577afe.png

    前言

    沉默了很久,一直都没发文章,有些惭愧。

    最近实习结束之后回了学校,提前开始做毕业设计了。对,就是毕业设计

    近两个月把 React NativeVue 3.0Nest.js 都摸了一下,大概都摸懂了。

    鉴于掘金已经很多优秀的 Vue 3.0 教程了,本人自认为文笔远逊于掘金的大佬们,就没有班门弄斧也写一篇了(本来很想了,后来想想感觉炒冷饭没啥意思,如果有想看我的教程风格的同学可以点赞或者在评论区里留言说一下,也不是不能写)。

    很多后端的同学都说:你们前端不就是切个图嘛,凭啥跟我们后端的同学平起平坐啊?

    这下前端的同学可以站起来了:你们后端不也就是 CRUD 嘛,凭啥瞧不起我们前端的同学啊?

    今天就写一下最近做毕业设计用到的框架:Nest.js 的基础教程吧,简单教大家做一下 CRUD(小白向,大佬轻喷)。

    挖个坑,这应该是最基础的第一章吧,如果大家觉得好就多点赞评论,过 200 点赞就加更一些,争取让大家从前端走向全栈吧

    这个教程的所有代码我都放在了我的 GitHub 仓库:Nest-CRUD-Demo,欢迎大家点个 Star

    同时也欢迎大家关注 「Hello FE」,里面有非常多其他的精品好文,不论是还在学习前端的同学还是已经工作了一段时间的朋友,都可以阅读一下(关注还有小惊喜,链接过期了可以在后台回复,我看到了会回复新的链接)。

    框架简介

    Nest 是一个用于构建高效,可扩展的 Node.js 服务器端应用程序的框架。它使用渐进式 JavaScript,内置并完全支持 TypeScript(但仍然允许开发人员使用纯 JavaScript 编写代码)并结合了 OOP(面向对象编程),FP(函数式编程)和 FRP(函数式响应编程)的元素。
    在底层,Nest 使用强大的 HTTP Server 框架,如 Express(默认)和 FastifyNest 在这些框架之上提供了一定程度的抽象,同时也将其 API 直接暴露给开发人员。这样可以轻松使用每个平台的无数第三方模块。

    我猜肯定很多同学看不懂这段话,没关系,我也暂时看不懂,但这不影响我们学会用它 CRUD

    我们只需要知道它是一款 Node.js 的后端框架,规范化开箱即用的特性使其在国外开发者社区非常流行,社区也非常活跃,GitHub Repo 拥有 31.1k Star

    相比于 ExpressKoa 的千奇百怪五花八门,Nest 确实是一股清流。

    不过我们国内也有很棒的 Node.js 框架,比如说 Midway,和 Nest 一样,采用的 IoC 的机制,想了解一下的同学可以看我的小伙伴林不渡写的文章:《走近 MidwayJS :初识 TS 装饰器与 IoC 机制》,还可以到 Midway 官网自行探索。

    包括在 Nest 当中遇到的装饰器相关的知识,大家也可以到上面林不渡同学的那篇文章中了解。

    前置知识

    • HTTP
    • TypeScript/JavaScript

    项目环境

    • git
    • mongodb
    • node.js >= 10.13.0

    安装 MongoDB

    这个章节的教程我就只写 Mac OS 上的安装了,毕竟上了大学就很少用 Windows 了,用 Windows 的同学可以到 ="https://mongodb.com/download-center/community">MongoDB 官网选择对应的系统版本去下载 msi 的安装包,或者搜索引擎里搜索一下,记得限定一下结果的时间,保证能够搜索到最新的教程。

    强烈建议使用 Homebrew 来对 Mac OS 的软件包环境进行管理,没有安装的同学可以点击这里下载。

    由于目前 MongoDB 已经不开源了,因此我们想要安装 MongoDB 就只能安装社区版本。

    brew tap mongodb/brew
    brew install mongodb-community

    安装好之后我们就可以启动 MongoDB 的服务了:

    brew services start mongodb-community

    服务启动了就不用管了,如果要关闭的话可以把 start 改成 stop,就能够停止 MongoDB 的服务了。

    构建项目

    有两种方式,可以自行选择,两者没有区别:

    使用 Nest CLI 安装:

    npm i -g @nestjs/cli
    nest new nest-crud-demo

    使用 Git 安装:

    git clone https://github.com/nestjs/typescript-starter.git nest-crud-demo

    这两条命令的效果完全一致,就是初始化一个 Nest.js 的项目到当前文件夹下,项目的文件夹名字为 nest-crud-demo,两种方式都可以。

    当然,我还是建议采用第一种方式,因为后面我们可以直接使用脚手架工具生成项目文件。

    启动服务

    cd nest-crud-demo
    npm run start:dev 或者 yarn run start:dev

    就可以以开发模式启动我们的项目了。

    这里其实有一个小小的点,就是启动的时候应该以 dev 模式启动,这样 Nest自动检测我们的文件变化,然后自动重启服务

    如果是直接 npm start 或者 yarn start 的话,虽然服务启动了,但是我们如果在开发的过程中修改了文件,就要手动停止服务然后重新启动,效率挺低的。

    安装依赖

    项目中我们会用到 Mongoose 来操作我们的数据库,Nest 官方为我们提供了一个 Mongoose 的封装,我们需要安装 mongoose@nestjs/mongoose

    npm install mongoose @nestjs/mongoose --save

    安装好之后我们就可以开始编码过程了。

    编写代码

    创建 Module

    我们这次就创建一个 User 模块,写一个用户增删改查,带大家熟悉一下这个过程。

    nest g module user server

    脚手架工具会自动在 src/server/user 文件夹下创建一个 user.module.ts,这是 Nest 的模块文件,Nest 用它来组织整个应用程序的结构。

    // user.module.ts
    import { Module } from '@nestjs/common';
    
    @Module({})
    export class UserModule {}
    

    同时还会在根模块 app.module.ts 中引入 UserModule 这个模块,相当于一个树形结构,在根模块中引入了 User 模块。

    执行上面的终端命令之后,我们会惊讶地发现,app.module.ts 中的代码已经发生了变化,在文件顶部自动引入了 UserModule,同时也在 @Module 装饰器的 imports 中引入了 UserModule

    // app.module.ts
    import { Module } from '@nestjs/common';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    import { UserModule } from './server/user/user.module'; // 自动引入
    
    @Module({
      imports: [UserModule], // 自动引入
      controllers: [AppController],
      providers: [AppService]
    })
    export class AppModule {}
    

    创建 Controller

    nest g controller user server

    Nest 中,controller 就类似前端的路由,负责处理客户端传入的请求服务端返回的响应

    举个例子,我们如果要通过 http://localhost:3000/user/users 获取所有的用户信息,那么我们可以在 UserController 中创建一个 GET 方法,路径为 users 的路由,这个路由负责返回所有的用户信息。

    // user.controller.ts
    import { Controller, Get } from '@nestjs/common';
    
    @Controller('user')
    export class UserController {
      @Get('users')
      findAll(): string {
        return "All User's Info"; // [All User's Info] 暂时代替所有用户的信息
      }
    }
    

    这就是 controller 的作用,负责分发和处理请求响应

    当然,也可以把 findAll 方法写成异步方法,像这样:

    // user.controller.ts
    import { Controller, Get } from '@nestjs/common';
    
    @Controller('user')
    export class UserController {
      @Get('users')
      async findAll(): Promise<any> {
        return await this.xxx.xxx(); // 一些异步操作
      }
    }
    

    创建 Provider

    nest g service user server

    provider 我们可以简单地从字面意思来理解,就是服务的提供者

    怎么去理解这个服务提供者呢?举个例子,我们的 controller 接收到了一个用户的查询请求,我们不能直接在 controller 中去查询数据库并返回,而是要将查询请求交给 provider 来处理,这里我们创建了一个 UserService,就是用来提供数据库操作服务的。

    // user.service.ts
    import { Injectable } from '@nestjs/common';
    
    @Injectable()
    export class UserService {}
    

    当然,provider 不一定只能用来提供数据库的操作服务,还可以用来做一些用户校验,比如使用 JWT 对用户权限进行校验的策略,就可以写成一个策略类,放到 provider 中,为模块提供相应的服务。

    挺多文档将 controllerprovider 翻译为控制器提供者,我感觉这种翻译挺生硬的,让人不知所云,所以我们姑且记忆他们的英文名吧。

    controllerprovider 都创建完后,我们又会惊奇地发现,user.module.ts 文件中多了一些代码,变成了这样:

    // user.module.ts
    import { Module } from '@nestjs/common';
    import { UserController } from './user.controller';
    import { UserService } from './user.service';
    
    @Module({
      controllers: [UserController],
      providers: [UserService]
    })
    export class UserModule {}
    

    从这里开始,我们就要开始用到数据库了~

    连接数据库

    引入 Mongoose 根模块

    连接数据之前,我们要先在根模块,也就是 app.module.ts 中引入 Mongoose 的连接模块:

    // app.module.ts
    import { Module } from '@nestjs/common';
    import { MongooseModule } from '@nestjs/mongoose';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    import { UserModule } from './server/user/user.module';
    
    @Module({
      imports: [MongooseModule.forRoot('mongodb://localhost/xxx'), UserModule],
      controllers: [AppController],
      providers: [AppService]
    })
    export class AppModule {}
    

    这段代码里面的 mongodb://localhost/xxx 其实就是本地数据库的地址,xxx 是数据库的名字。

    这时候保存文件,肯定有同学会发现控制台还是报错的,我们看一下报错信息就很容易知道问题在哪里了。

    其实就是 mongoose 模块没有类型声明文件,这就很容易解决了,安装一下就好:

    npm install @types/mongoose --dev 或者 yarn add @types/mongoose --dev

    安装完之后服务就正常重启了。

    引入 Mongoose 分模块

    这里我们先要创建一个数据表的格式,在 src/server/user 文件夹下创建一个 user.schema.ts 文件,定义一个数据表的格式:

    // user.schema.ts
    import { Schema } from 'mongoose';
    
    export const userSchema = new Schema({
      _id: { type: String, required: true }, // 覆盖 Mongoose 生成的默认 _id
      user_name: { type: String, required: true },
      password: { type: String, required: true }
    });
    

    然后将我们的 user.module.ts 文件修改成这样:

    // user.module.ts
    import { Module } from '@nestjs/common';
    import { MongooseModule } from '@nestjs/mongoose';
    import { UserController } from './user.controller';
    import { userSchema } from './user.schema';
    import { UserService } from './user.service';
    
    @Module({
      imports: [MongooseModule.forFeature([{ name: 'Users', schema: userSchema }])],
      controllers: [UserController],
      providers: [UserService]
    })
    export class UserModule {}
    

    好了,现在一切就绪,终于可以开始编写我们的 CRUD 逻辑了!冲冲冲~

    CRUD

    我们打开 user.service.ts 文件,为 UserService 类添加一个构造函数,让其在实例化的时候能够接收到数据库 Model,这样才能在类中的方法里操作数据库。

    // user.service.ts
    import { Injectable } from '@nestjs/common';
    import { InjectModel } from '@nestjs/mongoose';
    import { Model } from 'mongoose';
    import { CreateUserDTO } from './user.dto';
    import { User } from './user.interface';
    
    @Injectable()
    export class UserService {
      constructor(@InjectModel('Users') private readonly userModel: Model<User>) {}
    
      // 查找所有用户
      async findAll(): Promise<User[]> {
        const users = await this.userModel.find();
        return users;
      }
    
      // 查找单个用户
      async findOne(_id: string): Promise<User> {
        return await this.userModel.findById(_id);
      }
    
      // 添加单个用户
      async addOne(body: CreateUserDTO): Promise<void> {
        await this.userModel.create(body);
      }
    
      // 编辑单个用户
      async editOne(_id: string, body: EditUserDTO): Promise<void> {
        await this.userModel.findByIdAndUpdate(_id, body);
      }
    
      // 删除单个用户
      async deleteOne(_id: string): Promise<void> {
        await this.userModel.findByIdAndDelete(_id);
      }
    }
    

    因为 mongoose 操作数据库其实是异步的,所以这里我们使用 async 函数来处理异步的过程。

    好奇的同学会发现,这里突然出现了两个文件,一个是 user.interface.ts,另一个是 user.dto.ts,我们现在来创建一下:

    // user.interface.ts
    import { Document } from 'mongoose';
    
    export interface User extends Document {
      readonly _id: string;
      readonly user_name: string;
      readonly password: string;
    }
    // user.dto.ts
    export class CreateUserDTO {
      readonly _id: string;
      readonly user_name: string;
      readonly password: string;
    }
    
    export class EditUserDTO {
      readonly user_name: string;
      readonly password: string;
    }
    

    其实就是对数据类型做了一个定义。

    现在,我们可以到 user.controller.ts 中设置路由了,将客户端的请求进行处理,调用相应的服务实现相应的功能:

    // user.controller.ts
    import {
      Body,
      Controller,
      Delete,
      Get,
      Param,
      Post,
      Put
    } from '@nestjs/common';
    import { CreateUserDTO, EditUserDTO } from './user.dto';
    import { User } from './user.interface';
    import { UserService } from './user.service';
    
    interface UserResponse<T = unknown> {
      code: number;
      data?: T;
      message: string;
    }
    
    @Controller('user')
    export class UserController {
      constructor(private readonly userService: UserService) {}
    
      // GET /user/users
      @Get('users')
      async findAll(): Promise<UserResponse<User[]>> {
        return {
          code: 200,
          data: await this.userService.findAll(),
          message: 'Success.'
        };
      }
    
      // GET /user/:_id
      @Get(':_id')
      async findOne(@Param('_id') _id: string): Promise<UserResponse<User>> {
        return {
          code: 200,
          data: await this.userService.findOne(_id),
          message: 'Success.'
        };
      }
    
      // POST /user
      @Post()
      async addOne(@Body() body: CreateUserDTO): Promise<UserResponse> {
        await this.userService.addOne(body);
        return {
          code: 200,
          message: 'Success.'
        };
      }
    
      // PUT /user/:_id
      @Put(':_id')
      async editOne(
        @Param('_id') _id: string,
        @Body() body: EditUserDTO
      ): Promise<UserResponse> {
        await this.userService.editOne(_id, body);
        return {
          code: 200,
          message: 'Success.'
        };
      }
    
      // DELETE /user/:_id
      @Delete(':_id')
      async deleteOne(@Param('_id') _id: string): Promise<UserResponse> {
        await this.userService.deleteOne(_id);
        return {
          code: 200,
          message: 'Success.'
        };
      }
    }
    

    至此,我们就完成了一个完整的 CRUD 操作,接下来我们来测试一下~

    接口测试

    接口测试我们用的是 Postman,大家可以去下载一个,非常好用的接口自测工具。

    数据库可视化工具我们用的是 MongoDB 官方的 MongoDB Compass,也很不错。

    GET /user/users

    09ccac3dc6ad16b525eb6ad5c14237ce.png

    一开始我们的数据库中什么都没有,所以返回了一个空数组,没用用户信息。

    POST /user

    c233934716de249ad2e777c8bed02776.png

    现在我们添加一条用户信息,服务器返回添加成功。

    2547be87d8f5c8a1122ca3dc66d296f3.png

    GET /user/:_id

    1332698b40fbfc2cab69e327e39b8e6f.png

    添加完一条用户信息之后再查询,可算是能查询到我的信息了。

    PUT /user/:_id

    72859e33cc9211780b526e3c395b674f.png

    现在假如我想修改密码,发送一个 PUT 请求。

    640eacaf4ba722ab3482cba062eec5e6.png

    DELETE /user/:_id

    9b56b696abc58a7f2d59f459ff4fdeff.png

    现在我们删除一下刚才添加的用户信息。

    ec25b2342d4b47fa7e46a525afae91c5.png

    会发现数据库中的内容已经被删除了。

    完结撒花

    大功告成,CRUD 就这么简单,用这个项目去参加一些学校举行的比赛,拿个奖肯定没什么问题,开箱即用(学校老师们别打我)。

    总结

    教程还算是用了比较通俗易懂的方式为大家讲解了如何写一个带有 CRUD 功能的后端 Node.js 应用,框架采用的是 Nest.js

    相信大家在上面的教程中肯定有非常多不懂的部分,比如说 @Get()@Post()@Param()@Body() 等等的装饰器,再比如说一些 Nest.js 相关的概念。

    没关系,我的建议是:学编程先模仿,遇到不懂的地方先记住,等到自己的积累够多了,总有一天你会回过头发现自己茅塞顿开,突然懂了。这也是我个人学习的一个小技巧。

    在学习的过程中,也一定会遇到一些问题,学习编程的过程中遇到问题不能自己憋着,一定要学会请教大佬!一定要学会请教大佬!一定要学会请教大佬!重要的事情说三遍。

    不过也别很简单的问题就去请教大佬,而且最好给一点小小的报酬,毕竟谁也没有义务帮你解决问题。

    我在学习的过程中也请教了一些社区里面的大佬,同时还进入了 Nest.js 的社区答疑群,向国外友人请教学到了不少知识。

    当然,这个 Demo 中也有很多可以完善的地方,比如说错误处理

    数据库的操作肯定是有可能出现错误的,比如说我们漏传了 required: true 的参数,数据库就会报错。

    这个时候我们就要写一个 try/catch 捕获这个异常,或者干脆写一个异常的过滤器,将所有的异常统一处理(Nest.js 支持过滤器)

    除此之外,既然有可能出现异常,那么我们就需要一个日志系统去捕获这个异常,方便查错纠错。

    如果涉及到登录注册的部分,还有密码加解密的过程,同时还可能有权限校验问题需要进行处理。

    所以后端的同学肯定不止 CRUD 啦(可算圆回来了)。

    这个教程的所有代码我都放在了我的 GitHub 仓库:Nest-CRUD-Demo,欢迎大家点个 Star

    参考资料

    • NestJS - A progressive Node.js framework
    • Nest.js 中文文档
    展开全文
  • {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云文件存储NAS是一个可共享访问,弹性扩展,高可靠,高性能的分布式文件系统。...

    {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云文件存储NAS是一个可共享访问,弹性扩展,高可靠,高性能的分布式文件系统。广泛应用于容器存储、大数据分析、Web 服务和内容管理、应用程序开发和测试、媒体和娱乐工作流程、数据库备份。支持冷热数据分级存储,平均有效存储成本可低至 0.19元/月GB","link1":"https://www.aliyun.com/product/nas","link":"https://www.aliyun.com/product/nas","icon":"https://img.alicdn.com/tfs/TB1SisTRFXXXXbEXpXXXXXXXXXX-128-128.png","btn2":"折扣套餐","tip":"高性价比NAS新品上线,容量型NAS限时99元起! 立即查看","btn1":"立即开通","link2":"https://www.aliyun.com/product/nas","title":"文件存储 NAS"}],"search":[{"txt":"NAS新手入门","link":"https://m.aliyun.com/markets/aliyun/nas_edu01"},{"txt":"产品价格","link":"https://www.aliyun.com/price/product?spm=5176.149973.776685.6.ID90rH#/nas/detail"},{"txt":"最佳实践","link":"https://help.aliyun.com/document_detail/54998.html"},{"txt":"常见问题","link":"https://help.aliyun.com/knowledge_list/42175.html?spm=5176.59209.972911.4.3f6d3f62SVIECT"}],"countinfo":{"search":{"length_pc":0,"length":0},"card":{"length_pc":0,"length":0}},"simplifiedDisplay":"newEdition","newCard":[{"ifIcon":"icon","link":"https://img.alicdn.com/tfs/TB1XY8hGYr1gK0jSZFDXXb9yVXa-1740-328.png","icon":"nas","title":"文件存储 NAS","contentLink":"https://www.aliyun.com/product/nas?spm=5176.10695662.776724.1.27543d3eTvR5Bi","des":"阿里云文件存储(Network Attached Storage)是面向阿里云ECS、HPC和Docker的共享文件存储服务,支持linux和window客户端多种标准文件访问协议,无限容量及性能扩展、单一命名空间,企业级安全防护。","btn1":"立即开通","link1":"https://www.aliyun.com/product/nas?spm=5176.10695662.776724.2.27543d3eTvR5Bi","btn2":"产品文档","link2":"https://help.aliyun.com/product/27516.html?spm=5176.cnnas.0.0.42216689Kvc7ZO","link3":"https://nasnext.console.aliyun.com/overview?spm=5176.cnnas.0.0.42216689Kvc7ZO","btn3":"管理控制台","infoGroup":[{"infoName":"产品入门","infoContent":{"firstContentName":"NAS使用流程","firstContentLink":"https://help.aliyun.com/document_detail/148430.html?spm=a2c4g.11174283.6.564.b9c74da2J1uIZi","lastContentName":"挂载NAS到系统","lastContentLink":"https://help.aliyun.com/document_detail/27526.html?spm=a2c4g.11186623.6.566.1872495fTbU32y"}},{"infoName":"最新动态","infoContent":{"firstContentName":"极速型性能提升340%","firstContentLink":"https://yq.aliyun.com/articles/761833?spm=5176.cnnas.0.0.25216689DjruaG","lastContentName":"NAS低频型发布","lastContentLink":"https://yq.aliyun.com/articles/762197?spm=5176.cnnas.0.0.25216689DjruaG"}}]}]}

    {"$env":{"JSON":{}},"$page":{"env":"production"},"$context":{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云文件存储NAS是一个可共享访问,弹性扩展,高可靠,高性能的分布式文件系统。广泛应用于容器存储、大数据分析、Web 服务和内容管理、应用程序开发和测试、媒体和娱乐工作流程、数据库备份。支持冷热数据分级存储,平均有效存储成本可低至 0.19元/月GB","link1":"https://www.aliyun.com/product/nas","link":"https://www.aliyun.com/product/nas","icon":"https://img.alicdn.com/tfs/TB1SisTRFXXXXbEXpXXXXXXXXXX-128-128.png","btn2":"折扣套餐","tip":"高性价比NAS新品上线,容量型NAS限时99元起! 立即查看","btn1":"立即开通","link2":"https://www.aliyun.com/product/nas","title":"文件存储 NAS"}],"search":[{"txt":"NAS新手入门","link":"https://m.aliyun.com/markets/aliyun/nas_edu01"},{"txt":"产品价格","link":"https://www.aliyun.com/price/product?spm=5176.149973.776685.6.ID90rH#/nas/detail"},{"txt":"最佳实践","link":"https://help.aliyun.com/document_detail/54998.html"},{"txt":"常见问题","link":"https://help.aliyun.com/knowledge_list/42175.html?spm=5176.59209.972911.4.3f6d3f62SVIECT"}],"countinfo":{"search":{"length_pc":0,"length":0},"card":{"length_pc":0,"length":0}},"simplifiedDisplay":"newEdition","newCard":[{"ifIcon":"icon","link":"https://img.alicdn.com/tfs/TB1XY8hGYr1gK0jSZFDXXb9yVXa-1740-328.png","icon":"nas","title":"文件存储 NAS","contentLink":"https://www.aliyun.com/product/nas?spm=5176.10695662.776724.1.27543d3eTvR5Bi","des":"阿里云文件存储(Network Attached Storage)是面向阿里云ECS、HPC和Docker的共享文件存储服务,支持linux和window客户端多种标准文件访问协议,无限容量及性能扩展、单一命名空间,企业级安全防护。","btn1":"立即开通","link1":"https://www.aliyun.com/product/nas?spm=5176.10695662.776724.2.27543d3eTvR5Bi","btn2":"产品文档","link2":"https://help.aliyun.com/product/27516.html?spm=5176.cnnas.0.0.42216689Kvc7ZO","link3":"https://nasnext.console.aliyun.com/overview?spm=5176.cnnas.0.0.42216689Kvc7ZO","btn3":"管理控制台","infoGroup":[{"infoName":"产品入门","infoContent":{"firstContentName":"NAS使用流程","firstContentLink":"https://help.aliyun.com/document_detail/148430.html?spm=a2c4g.11174283.6.564.b9c74da2J1uIZi","lastContentName":"挂载NAS到系统","lastContentLink":"https://help.aliyun.com/document_detail/27526.html?spm=a2c4g.11186623.6.566.1872495fTbU32y"}},{"infoName":"最新动态","infoContent":{"firstContentName":"极速型性能提升340%","firstContentLink":"https://yq.aliyun.com/articles/761833?spm=5176.cnnas.0.0.25216689DjruaG","lastContentName":"NAS低频型发布","lastContentLink":"https://yq.aliyun.com/articles/762197?spm=5176.cnnas.0.0.25216689DjruaG"}}]}]}}

    展开全文
  • angular在ts引入js文件一、在全局引用js文件二、在局部引用js文件1.将引入的js文件封装成函数2.配置angular.json3.在相应的ts中声明4.调用js文件中的函数 一、在全局引用js文件 在angular项目的index.html中直接...

    一、在全局引用js文件

    在angular项目的index.html中直接引用即可,这里引用的js是整个项目全局都可以使用的,但只有在index.html中才能引用,在其他的html页面直接引用js是不行的
    在这里插入图片描述

    二、在局部引用js文件

    1.将引入的js文件封装成函数

    这是一个使用echarts构造树图的js文件,因为在ts文件中只能调用js文件中的函数,所以需要将构造树图的javascript代码封装成一个函数,在ts中直接调用就可以成功构建树图
    在这里插入图片描述

    2.配置angular.json

    在angular.json文件中的scripts中加入想要局部引用的js文件,如果不在这里配置ts中引用时将无法识别,angular.json文件更改后需要重新运行项目才会生效
    在这里插入图片描述

    3.在相应的ts中声明

    因为在angular.json中配置过了,所以只需要在ts中使用declare对js文件中的函数进行声明,后面就可以直接使用了(这两个函数分别是构造树图和地图的函数)
    在这里插入图片描述

    4.调用js文件中的函数

    在 ngOnInit()中调用我们声明的函数,就可以正常运行我们js文件中的代码了
    在这里插入图片描述

    展开全文
  • 前言本文主要讲怎么写一个typescript的描述文件(以d.ts结尾的文件名,比如xxx.d.ts)。最近开始从jsts了。但是要用到一些描述文件(d.ts),常用的比如jquery等都可以通过 npm下载到别人已经写好的npm install @types...
  • 本文链接:https://blog.csdn.net/NB_Token/article/details/78337413通常,在项目中引用js库分两种情况:1、一种是通过npm install ${name} --save 安装...2、一种就是直接将.js文件放在src目录下,通过相对路径的...
  • ts引入js时的类型检查

    2020-02-24 11:15:06
    项目由js向ts转型时,若使用import的方式引入js文件,则在脚本检查ts时,会报错找不到这些js文件,造成ts检查日志里有很多同样的信息,因此很难找到真正的ts报错信息,有如下三种解决方案
  • ts引入js报错(vue3)

    2021-04-08 11:11:24
    ts引入js模块报错(Vue3)起因&&问题解决方案 起因&&问题 今天学完Vue3 之后顺便学了ts。正好工作要我重新搭个平台,于是我顺理成章的使用了vue3+ts 我在ts文件中引用lodash工具集的时候一直报错。 ...
  • 通过Code引入外部Demo的时候是否支持ts,js结尾的文件? 尝试在config.js中添加previewLangs:['js']并不起作用。 <h3>Sample Code <p><img alt="image" src=...
  • ts项目中引入js的 npm包

    千次阅读 2019-09-24 16:46:38
    1 可以选择安装其npm包的typescript版本 npm install @...2 如果是自己写的js库 可以单独编写.d.ts文件 3 如果是npm包的话 可以引入微软的自动生成工具dts-gen // 使用方法 // 首先安装下他的包 npm install...
  • 一、现象在项目的开发中,总会用到一些公司的脚本方法,同时...2、在utils下新建文件 common.js,如:var common = {showInfo: function(name, age){// 在控制台上打印出来console.log(name);console.log(age);}}3、 ...
  • ts中引用js文件(ionic3项目)

    万次阅读 2017-11-27 18:24:28
    其实大家在引入js文件时可以先搜索下有没有开源库,直接npm 安装 ,皆大欢喜,这里介绍下没有开源库的几种解决办法 方法一:全局安装typings进行查找并用typings进行安装 1.npm install -g typings //全局安装...
  • 背景: npm 上面的包没有对应 @type类型包,只有js代码时该怎么办呢? 解决方案: 1.(推荐)那就直接 let xx:any = require('package') 当js来写;...3.没有types不能import,也就是js不能用import引入js模块; ...
  • 编写声明文件(day07)typescript引入js类库三种类库的编写方法全局库模块库umd库插件模块插件全局插件 typescript引入js类库 安装类库 npm i jquery 在src下新建 lib 文件夹 新建 index.ts 文件 安装类型声明包...
  • <script src = "a.js"/> <script> //调用a.js里的方法,不提示 somefunc() ...//把js后缀名该为ts,选择终端-->运行任务-->tsc:监视-tsconfig.json就可以自动生成代码 //现在上面的调
  • 1、在项目中定义了一个对axiox进行配置的文件,其中需要引入vuex,在response...mian.ts文件中也引入了store,确是可以打印出来东西的。2、截图文件目录打印结果import axios from 'axios'import { Message } from '...
  • ueditor.config.js和ueditor.all.js文件已经放置在assets目录下,但是项目运行时却报错无法访问到这两个文件导致加载失败,请问该如何处理?</p><p>该提问来源于开源项目:cipchk/ngx-...
  • 我们的目标是把html引入ts文件,webpack打包时就能把html打进js文件,减少文件加载啦 1 安装 text-loader npm install text-loader --save-dev 2 webpack 配置里新增 text-loader 为文本加载器 module: ...
  • 通过例举问题的方式来寻求解决方法这里记录一个奇怪的问题, 如代码图片这是一个单独的文件, 只是引入一个json文件, 使用typescript编写, 发现require关键字出错然而使用命令tsc jsonTest-1.ts却能构建出js文件,...
  • 而上面定义的这3类可导入文件js和vue是可以省略后缀的:import test from './test.vue' 等同于: import test from './test'同理:import test from './test.js' 等同于:import test from './test'json不可以省略...
  • 类型检查、直接编译到原生js引入新的语法糖为什么用ts?TypeScript的设计目的应该是解决JavaScript的“痛点”:弱类型和没有命名空间,导致很难模块化,不适合开发大型程序。另外它还提供了一些语法糖来帮助大家更...
  • js转成tsd,也就是d.ts文件

    千次阅读 2017-03-05 20:37:17
    近期在学习angularjs2的时候,遇到在typescript中引入第三方库的问题,需要tsd文件, 在TypeSearch中有的可以找到,有的则找不到,遇到找不到的又不想自己去写的,于是我就需要自动将通过js转换出tsd文件, 解决...
  • TS声明文件

    2019-10-07 16:02:09
    now我们来看一看TS怎么声明文件, 在JS里面我们经常会使用各种第三方类库,引入方式也不太相同,常见的就是在HTML中通过script标签引入,然后就可以使用全局变量$或者jQuery了 我们通常这样获取一个id是foo的元素...
  • vue nuxt 引入 ts

    2020-04-22 16:33:18
    1. 安装 ts 以及相关依赖 yarnadd-D@nuxt/typescriptts-node@types/node@nuxt/typescript-build@nuxt/types@nuxt/typescript-runtime yarnaddvue-property-...2. 在 nuxt.config.js 文件中添加 ts 的loader ...
  • 1:全局引用 一般我们会把本地的js文件放在assets目录下,然后在angular.json...2:局部调用,这种方法我们是在哪个ts文件中用,就在哪个文件中引入js文件,这种方法更适合我们自己写的js 配置tsconfig.json文件 ...
  • Angular是不支持直接引入js文件的,下面介绍项目如果引入laydate.js的方法(可同样用于其他js文件引入,可能会有一些差别) 方法如下: 一、将下载的laydate中的js和theme文件放到一个统一的文件下面,我把它放到asset...
  • angular8 加载第三方js文件

    千次阅读 2019-11-30 20:23:24
    将自己下载好的js包文件放入项目指定...然后在Angular.json中引入相关js文件 然后我们需要在typings.d.ts文件中声明一下变量 在 tsconfig.json 中设置允许使用外部 js 重启nodejs,重新调用npm start ...
  • 1.hello.ts /** * 知识点1: * 第三方库之:声明文件 ... 这样的格式表示引入了声明文件 * 知识点2: * typescript2.0推荐用@types来管理第三方声明文件。 * npm install @types/jquery --save...

空空如也

空空如也

1 2 3 4 5 ... 14
收藏数 261
精华内容 104
关键字:

ts引入js文件