日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

js node 打包mac应用_混搭 TypeScript + GraphQL + DI + Decorator 风格写 Node.js 应用

發布時間:2025/4/16 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 js node 打包mac应用_混搭 TypeScript + GraphQL + DI + Decorator 风格写 Node.js 应用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
閱讀本文的知識前提:熟悉 TypeScript + GraphQL + Node.js + Decorator + Dependency Inject 等概念。前言
恰逢最近需要編寫一個簡單的后端 Node.js 應用,由于是全新的小應用,沒有歷史包袱,所以趁著這次機會換了一種全新的開發模式:
  • 語言使用 TypeScript,不僅僅是強類型那么簡單,它還提供很多高級語法糖,提高編程效率。

  • 兼顧 Restful + GraphQL 方式提供數據接口,前兩年 GraphQL 特別流行,最近這段時間有些平淡下來(現在比較火熱的是 Serverless);GraphQL 這種查詢語言對前端來講還是很友好的,自己寫的話能減少不少的接口開發量;

  • 使用 Decorator(裝飾器語法) + DI(依賴注入)風格寫業務邏輯。因后端 Java 開發服務的模式已經非常成熟,前端在 Node.js 的開發模式基本上是依照 Java 那套開發模子來的,尤其是 DI(依賴注入)設計模式的編程思想。這幾年隨著 ECMAScript 的標準迭代,以及 TypeScript 的成熟發展,在語言層面提供了很多現代化語法糖的支持,現在也可以利用 Decorator(裝飾器)+ DI(依賴注入)風格來寫了,個人認為這種風格也將成為書寫 Node.js 應用的常用范式之一。

  • 選用支持 TS + Decorator + DI 的 Node.js框架。在集團內使用Midway,因為 Midway 在集團內部已經是事實標準了,而且發展得很成熟了;如果選非集團內部的話,可以考慮選流行的 Next.js 框架;——?這類框架功能都很強大,而且提供完善的工具鏈和生態,就算你不熟,通讀他們的官方文檔都能收獲很多;

前端內部寫的后端應用基本上功能并不會太多(太專業的后端服務交給后端開發來做),絕大部分是基礎的操作,在這樣的情況下會涉及到很多重復工作量要做,基本都是一樣的套路:

  • 初始化項目腳手架

  • 數據庫的連接操作 + CRUD 操作

  • 創建數據 model 層 + service 層

  • 提供諸如 Restful 接口供多端消費

  • ...

  • 復雜的業務邏輯功能一般是直接調用后端提供的服務,前端很少介入太深的后端功能開發

    這意味著每次開發新應用都得重新來一遍 —— 這就跟前端平時切頁面一樣,重復勞動多了之后就內心還是比較煩的,甚至有抗拒心理。繁瑣的事大概涉及在工程鏈路 & 業務代碼這么兩方面,如果有良好的解決方案,將大大提升開發的幸福感:

  • 第一個方面是結構目錄的生成。這個問題比較好解決,市面上成熟的框架(Nest.js, Midway.js,Prisma.io 等)都提供了相應的腳手架工具,直接生成相應的服務端代碼結構,寫代碼既可靠又高效。同時這類成熟框架都能一鍵搞定部署發布等流程,這樣我們就可以將大部分時間用在業務代碼上、而不是折騰環境搭建細節上。

  • 第二個方面是業務代碼的書寫風格。同樣是寫業務代碼,語言風格不一樣,代碼效率也是不同的,你用 JS 寫業務代碼,跟 TypeScript + Decorator 來寫的效率大相徑庭 —— 這也就是技術發展帶來的福利。

  • 這里的第一個方面中的目錄生成,一鍵生成支持 Midway 6 ts 和 Ant Design Pro 4 ts 的版本前后端目錄,在這種強強聯合下,幾行代碼下來就能生成非常專業高效的前后端分離的架構體系 —— 包含?client?&?server?兩個文件夾,分別對應標準的 ?Ant Design Pro 4 目錄結構?和 ?Midway6 TS 目錄結構?目錄:

    然后在最外層根目錄執行即可,啟動后使用?http://localhost:6001/?打開提示即可:

    該初始化項目后就能可以跑通本地開發調試、構建、aone 部署發布流程,整個很順暢。

    不過這里需要說明的是,這套方案里使用的是 antd 3.x 的版本,建議進行升級到 antd 4.x 版本,升級成本并不算很高,antd 官方提供升級工具,基本一行代碼就能搞定:

    # 通過 npx 直接運行npx -p @ant-design/codemod-v4 antd4-codemod client/src

    建議通讀一下?官方文檔 - 從 v3 到 v4?內容,了解升級的地方在哪些。

    本文著重講解第二部分,即如何使用 TypeScript + Decorator + DI 風格編寫 Node.js 應用,讓你感受到使用這些技術框架帶來的暢快感。本文涉及的知識點比較多,代碼盡可能少放,主要是敘述邏輯思路,最后會以實現常見的?分頁功能?作為案例來詳細展示。

    數據庫 ORM


    首先我們需要解決數據庫相關的技術選項,這里說的技術選型是指 ORM 相關的技術選型(數據庫固定使用 MySQL),選型的基本原則是能力強大、用法簡單。

    ? ?ORM 選型

  • ORM 實例教程:阮一峰教程,解釋 ORM,通俗易懂

  • 使用 Typeorm 在 Midway 6 中獲得靈活一致的數據管理體驗?:一篇很受啟發的文章

  • 數據訪問庫的選擇之TypeORM:討論了如何在 Midway 中接入 TypeORM,還討論了一些高級用法

  • 除了直接拼 SQL 語句這種略微硬核的方式外,Node.js 應用開發者更多地會選擇使用開源的 ORM 庫,如 Sequelize。而在 Typescript 面前,工具庫層面目前兩種可選項,可以使用?sequelize-typescript?或者?TypeORM?來進行數據庫的管理。總結原因如下:

    • 原生類型聲明,與?Typescript?有更好的相容性

    • 支持裝飾器寫法,用法上簡單直觀;且足夠強的擴展能力,能支持復雜的數據操作;

    • 該庫足夠受歡迎,Github Star 數量高達?20.3k(截止此文撰寫 2020.08 時),且官方文檔友好

    并非說?Sequelize-typescript?不行,這兩個工具庫都很強大,都能滿足業務技術需求;Sequelize 一方面是 Model 定義方式比較 JS 化在 Typescript 天然的類型環境中顯得有些怪異;另一方面也與 Midway 6 整體的編碼風格不太統一,所以我個人更加傾向于用?TypeORM?。

    ? ?兩種操作模式

  • 架構模式中的 Active Record 和 Data Mapper

  • 什么是 ActiveRecord 模式

  • 這里簡單說明一下,ORM 架構模式中,最流行的實現模式有兩種:Active Record 和 Data Mapper。比如 Ruby 的 ORM 采取了 Active Record 的模式是這樣的:

    $user = new User;$user->username = 'philipbrown';$user->save();

    再來看使用 Data Mapper 的 ORM 是這樣的:

    $user = new User;$user->username = 'philipbrown';EntityManager::persist($user);

    現在我們察看到了它們最基本的區別:在 Active Record 中,領域對象有一個 save()?方法,領域對象通常會繼承一個 ActiveRecord 的基類來實現。而在 Data Mapper 模式中,領域對象不存在 save()?方法,持久化操作由一個中間類來實現。
    這兩種模式沒有誰比誰好之分,只有適不適合之別:

  • 簡單的 CRUD、試水型的 Demo 項目,用?Active Records?模式的 ORM 框架更好

  • 業務流程和規則較多的、成熟的項目改造用 Data Mapper 型,其允許將業務規則綁定到實體。

  • Active Records 模式最大優點是簡單 , 直觀,?一個類就包括了數據訪問和業務邏輯,恰好我現在這個小應用基本都是單表操作,所以就用 Active Records 模式了。

    TypeORM 的使用

    ? ?數據庫連接


    首先,提供數據庫初始化 service 類:

    // src/lib/database/service.tsimport { config, EggLogger, init, logger, provide, scope, ScopeEnum, Application, ApplicationContext } from '@ali/midway';import { ConnectionOptions, createConnection, createConnections, getConnection } from 'typeorm';const defaultOptions: any = { type: 'mysql', synchronize: false, logging: false, entities: [ 'src/app/entity/**/*.ts' ],};@scope(ScopeEnum.Singleton)@provide()export default class DatabaseService { static identifier = 'databaseService'; // private connection: Connection; /** 初始化數據庫服務實例 */ static async initInstance(app: Application) { const applicationContext: ApplicationContext = app.applicationContext; const logger: EggLogger = app.getLogger(); // 手動實例化一次,啟動數據庫連接 const databaseService = await applicationContext.getAsync(DatabaseService.identifier); const testResult = await databaseService.getConnection().query('SELECT 1+1'); logger.info('數據庫連接測試:SELECT 1+1 =>', testResult); } @config('typeorm') private ormconfig: ConnectionOptions | ConnectionOptions[]; @logger() logger: EggLogger; @init() async init() { const options = { ...defaultOptions, ...this.ormconfig }; try { if (Array.isArray(options)) { await createConnections(options); } else { await createConnection(options); } this.logger.info('[%s] 數據庫連接成功~', DatabaseService.name); } catch (err) { this.logger.error('[%s] 數據庫連接失敗!', DatabaseService.name); this.logger.info('數據庫鏈接信息:', options); this.logger.error(err); } } /** * 獲取數據庫鏈接 * @param connectionName 數據庫鏈接名稱 */ getConnection(connectionName?: string) { return getConnection(connectionName); }}

    說明:

  • 這里一定是單例?@scope(ScopeEnum.Singleton),因為數據庫連接服務只能有一個。但是可以初始化多個連接,比如用于多個數據庫連接或讀寫分離

  • 默認配置項?defaultOptions?中的?entities?表示數據庫實體對象存放的路徑,推薦專門創建一個?entity?目錄用來存放:

  • 其次,在 Midway 的配置文件中指定數據庫連接配置:

    // src/config/config.default.tsexport const typeorm = { type: 'mysql', host: 'xxxx', port: 3306, username: 'xxx', password: 'xxxx', database: 'xxxx', charset: 'utf8mb4', logging: ['error'], // ["query", "error"] entities: [`${appInfo.baseDir}/entity/**/!(*.d|base){.js,.ts}`], };// server/src/config/config.local.tsexport const typeorm = { type: 'mysql', host: '127.0.0.1', port: 3306, username: 'xxxx', password: 'xxxx', database: 'xxxx', charset: 'utf8mb4', synchronize: false, logging: false, entities: [`src/entity/**/!(*.d|base){.js,.ts}`],}

    說明:

  • 因為要區分 aone 環境運行和本地開發,所以需要配置兩份

  • entities的配置項本地和線上配置是不同的,本地直接用?src/entity?就行,而 aone 環境需要使用?${appInfo.baseDir}?變量

  • 最后,在應用啟動時觸發實例化:

    // src/app.tsimport { Application } from '@ali/midway';import "reflect-metadata";import DatabaseService from './lib/database/service';export default class AppBootHook { readonly app: Application; constructor(app: Application) { this.app = app; } // 所有的配置已經加載完畢 // 可以用來加載應用自定義的文件,啟動自定義的服務 async didLoad() { await DatabaseService.initInstance(this.app); }}

    說明:

  • 選擇在 app 的配置加載完畢之后來啟動自定義的數據庫服務,具體參考?啟動自定義的聲明周期參考文檔?說明

  • 為了不侵入?AppBootHook?代碼太多,我把初始化數據庫服務實例的代碼放在了?DatabaseService?類的靜態方法中。

  • ? ?數據庫操作

    • typeorm數據庫ORM框架中文文檔

    • Active Record vs Data Mapper?:官方文檔對兩者的解釋

    數據庫連接上之后,就可以直接使用 ORM 框架進行數據庫操作。不同于現有的所有其他 JavaScript ORM 框架,TypeORM 支持 Active Record 和 Data Mapper 模式(在我這次寫的項目中,使用的是 Active Record 模式),這意味著你可以根據實際情況選用合適有效的方法編寫高質量的、松耦合的、可擴展的應用程序。
    首先看一下用 Active Records 模式的寫法:

    import {Entity, PrimaryGeneratedColumn, Column, BaseEntity} from "typeorm";@Entity()export class User extends BaseEntity { @PrimaryGeneratedColumn() id: number; @Column() firstName: string; @Column() lastName: string; @Column() age: number;}

    說明:

  • 類需要用?@Entity()?裝飾

  • 需要繼承?BaseEntity?這個基類

  • 對應的業務域寫法:

    const user = new User();user.firstName = "Timber";user.lastName = "Saw";user.age = 25;await user.save();

    其次看一下 Data Mapper 型的寫法:

    // 模型定義import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";@Entity()export class User { @PrimaryGeneratedColumn() id: number; @Column() firstName: string; @Column() lastName: string; @Column() age: number;}

    說明:

  • 類同樣需要用?@Entity()?裝飾

  • 不需要繼承?BaseEntity?這個基類

  • 對應的業務域邏輯是這樣的:

    const user = new User();user.firstName = "Timber";user.lastName = "Saw";user.age = 25;await repository.save(user);

    無論是?Active Record?模式還是?Data Mapper?模式,TypeORM 在 API 上的命名使用上幾乎是保持一致,這大大降低了使用者記憶上的壓力:比如上方保存操作,都稱為 save 方法,只不過前者是放在 Entity 實例上,后者是放在 Repository 示例上而已。

    ? ?MVC架構

    整個服務器的設計模式,就是經典的 MVC 架構,主要就是通過 Controller、Service、Model 、View 共同作用,形成了一套架構體系;

    此圖來源于 《Express 教程 4:路由和控制器》https://developer.mozilla.org/zh-CN/docs/learn/Server-side/Express_Nodejs/routes

    上圖是最為基礎的 MVC 架構,實際開發過程中還會有更細分的優化,主要體現兩方面:

  • 為了方便后期擴展,還會引入?中間件(middleware)?機制,這些概念相信但凡寫過 Koa/Express 的都知道 —— 不過這里還是重述一下,因為后面 GraphQL 就是通過中間件方式引入的。

  • 一般不推薦直接讓?Controller?調用到?Model?對象,而是要中間添加一層 Service 層來進行解耦(具體的優勢詳見 Egg.js 官方文檔《服務(Service)》,里面有詳細的解釋);簡單來講,這樣的好處在于解耦 Model 和 Controller,同時保持業務邏輯的獨立性(從而帶來更好的擴展性、更方便的單元測試等),抽象出來的 Service 可以被多個 Controller 重復調用?—— 比如,GraphQL Resolver 和 Controller 就可以共用同一份 Service;


  • 現代 Node.js 框架初始化的時候都默認幫你做了這事情 —— Midway 也不例外,初始化后去看一下它的目錄結構就基本上懂了。

    更多關于該架構的實戰可參考以下文章:

  • Node Service-oriented Architecture: 介紹面向 Service 的 Node.js 架構

  • Designing a better architecture for a Node.js API:初學者教程,從實踐中感受面向 Service 架構

  • Bulletproof node.js project architecture: 如何打造一個堅固的 Node.js 服務端架構

  • ? ?RESTful API

    在 Midway 初始化項目的時候,其實已經具備完整的 RESTful API 的能力,你只要照樣去擴展就可以了,而且基于裝飾器語法和 DI 風格,編寫路由非常的方便直觀,正如官方《路由裝飾器》里所演示的代碼那樣,幾行代碼下來就輸出標準的 RESTful 風格的 API:

    import { provide, controller, inject, get } from 'midway';@provide()@controller('/user')export class UserController { @inject('userService') service: IUserService; @inject() ctx; @get('/:id') async getUser(): Promise { const id: number = this.ctx.params.id; const user: IUserResult = await this.service.getUser({id}); this.ctx.body = {success: true, message: 'OK', data: user}; }}

    GraphQL


    • 其次需要閱讀 type-graph 的教程,比如?Resolvers?章節,具體的代碼參考可以前往?recipe-resolver

    • TypeScript + GraphQL = TypeGraphQL:阿里 CCO 體驗技術部的文章,介紹地比較詳細到位,推薦閱讀(結合 egg.js 的開發實踐)

    RESTful API 方式用得比較多,不過我還是想在自己的小項目里使用 GraphQL,具體的優點我就不多說了,可以參考《GraphQL 和 Apollo 為什么能幫助你更快地完成開發需求?》等相關文章。
    GraphQL 的理解成本和接入成本還是有一些的,建議直接通讀官方文檔 《GraphQL 入門》 去了解 GraphQL 中的概念和使用。

    ? ?接入 GraphQL 服務中間件

    整體的技術選型陣容就是?apollo-server-koa?和?type-graphql?:

    • apollo-server?是一個在 Node.js 上構建 GraphQL 服務端的 Web 中間件,支持 Koa 也就天然的支持了 Midway

    • TypeGraphQL:它通過一些 TypeScript + Decorator 規范了 Schema 的定義,避免在 GraphQL 中分別寫?Schema Type DSL 和數據 Modal 的重復勞動。

    只需要將 Koa 中間件 轉 Midway 中間件就行。根據 Midway項目目錄約定,在?/src/app/middleware/?下新建文件 graphql.ts,將 apollo-server-koa 中間件簡單包裝一下:

    import * as path from 'path';import { Context, async, Middleware } from '@ali/midway';import { ApolloServer, ServerRegistration } from 'apollo-server-koa';import { buildSchemaSync } from 'type-graphql';export default (options: ServerRegistration, ctx: Context) => { const server = new ApolloServer({ schema: buildSchemaSync({ resolvers: [path.resolve(ctx.baseDir, 'resolver/*.ts')], container: ctx.applicationContext }) }); return server.getMiddleware(options);};

    說明:

    • 利用?apollo-server-koa?暴露的?getMiddleware?方法取得中間件函數,注入 TypeGraphQL 所管理的?schema?并導出該函數。

    • 我們所有的 GraphQL Resolver 都放在?'app/resolver' 目錄下

    由于 Midway 默認集成了 CSRF 的安全校驗,我們針對?/graphql 路徑的這層安全需要忽略掉:

    export const security = { csrf: { // 忽略 graphql 路由下的 csrf 報錯 ignore: '/graphql' } }

    接入的準備工作到這里就算差不多了,接下來就是編寫 GraphQL 的?Resolver?相關邏輯

    ? ?Resolvers

    對于 Resolver 的處理,TypeGraphQL 提供了一些列的 Decorator 來聲明和處理數據。通過 Resolver 類的方法來聲明 Query 和 Mutation,以及動態字段的處理 FieldResolver。幾個主要的 Decorator 說明如下:

    • @Resolver:來聲明當前類是數據處理的

    • @Query:聲明改方法是一個 Query 查詢操作

    • @Mutation:聲明改方法是一個 Mutation 修改操作

    • @FieldResovler:對?@Resolver(of => Recipe)?返回的對象添加一個字段處理

    方法參數相關的 Decorator:

    • @Root:獲取當前查詢對象

    • @Ctx:獲取當前上下文,這里可以拿到 egg 的 Context (見上面中間件集成中的處理)

    • @Arg:定義 input 參數

    這里涉及到比較多的知識點,不可能一一羅列完,還是建議先去官網?https://typegraphql.com/docs/introduction.html?閱讀一遍
    接下來我們從接入開始,然后以如何創建一個分頁(Pagination)?功能為案例來演示在如何在 Midway 框架里使用 GraphQL,以及如何應用上述這些裝飾器 。

    案例:利用 GraphQL 實現分頁功能


    ? ?分頁的數據結構

    • Apollo Server: GraphQL 數據分頁概述

    從使用者角度來,我們希望傳遞的參數只有兩個 pageNo 和 pageSize ,比如我想訪問第 2 頁、每頁返回 10 條內容,入參格式就是:

    { pageNo: 2, pageSize: 10}

    而分頁返回的數據結構如下:

    { articles { totalCount # 總數 pageNo # 當前頁號 pageSize # 每頁結果數 pages # 總頁數 list: { # 分頁結果 title, author } }}

    ? ?Schema 定義

    首先利用 TypeGraphQL 提供的 Decorator 來聲明入參類型以及返回結果類型:

    // src/entity/pagination.tsimport { ObjectType, Field, ID, InputType } from 'type-graphql';import { Article } from './article';// 查詢分頁的入參@InputType()export class PaginationInput { @Field({ nullable: true }) pageNo?: number; @Field({ nullable: true }) pageSize?: number;}// 查詢結果的類型@ObjectType()export class Pagination { // 總共有多少條 @Field() totalCount: number; // 總共有多少頁 @Field() pages: number; // 當前頁數 @Field() pageNo: number; // 每頁包含多少條數據 @Field() pageSize: number; // 列表 @Field(type => [Article]!, { nullable: "items" }) list: Article[];}export interface IPaginationInput extends PaginationInput { }

    說明:

  • 通過這里的?@ObjectType()?、@Field()?裝飾注解后,會自動幫你生成 GraphQL 所需的 Schema 文件,可以說非常方便,這樣就不用擔心自己寫的代碼跟 Schema 不一致;

  • 對?list?字段,它的類型是?Article[]?,在使用?@Field?注解時需要注意,因為我們想表示數組一定存在但有可能為空數組情況,需要使用?{nullable: "items"}(即?[Item]!),具體查閱?官方文檔 - Types and Fields?另外還有兩種配置:????

  • 基礎的?{ nullable: true | false }?只能表示整個數組是否存在(即[Item!]?或者?[Item!]!)

  • 如果想表示數組或元素都有可能為空時,需要使用?{nullable: "itemsAndList"}(即?[Item])

  • ? ?Resolver 方法

    基于上述的 Schema 定義,接下來我們要寫 Resolver,用來解析用戶實際的請求:

    // src/app/resolver/pagination.tsimport { Context, inject, provide } from '@ali/midway';import { Resolver, Query, Arg, Root, FieldResolver, Mutation } from 'type-graphql';import { Pagination, PaginationInput } from '../../entity/pagination';import { ArticleService } from '../../service/article';@Resolver(of => Articles)@provide()export class PaginationResolver { @inject('articleService') articleService: ArticleService; @Query(returns => Articles) async articles(@Arg("query") pageInput: PaginationInput) { return this.articleService.getArticleList(pageInput); }}
    • 實際解析用戶請求,調用的是 Service 層中 articleService.getArticleList 方法,只要讓返回的結果跟我們想要的 Pagination 類型一致就行。

    • 這里的?articleService?對象就是通過容器注入(inject)到當前 Resolver ,該對象的提供來自 Service 層

    ? ?Service 層

    從上可以看到,請求參數是傳到 GraphQL 服務器,而真正進行分頁操作的還是 Service 層,內部利用 ORM 提供的方法;在TypeORM 中的分頁功能實現,可以參考一下官方的 find 選項的完整示例:

    userRepository.find({ select: ["firstName", "lastName"], relations: ["profile", "photos", "videos"], where: { firstName: "Timber", lastName: "Saw" }, order: { name: "ASC", id: "DESC" }, skip: 5, take: 10, cache: true});

    其中和?分頁?相關的就是 skip 和 take 兩個參數( where 參數是跟?過濾?有關,order 參數跟排序有關)。

    • How to implement pagination in nestjs with typeorm :這里給出了使用 Repository API 實現的方式

    • Find 選項: 官方 Find API 文檔

    所以最終我們的 Service 核心層代碼如下:

    // server/src/service/article.tsimport { provide, logger, EggLogger, inject, Context } from '@ali/midway';import { plainToClass } from 'class-transformer';import { IPaginationInput, Pagination } from '../../entity/pagination';...@provide('articleService')export class ArticleService { ... /** * 獲取 list 列表,支持分頁 */ async getArticleList(query: IPaginationInput): Promise { const {pageNo = 1, pageSize = 10} = query; const [list, total] = await Article.findAndCount({ order: { create_time: "DESC" }, take: pageSize, skip: (pageNo - 1) * pageSize }); return plainToClass(Pagination, { totalCount: total, pages: Math.floor(total / pageSize) + 1, pageNo: pageNo, pageSize: pageSize, list: list, }) } ...}
    • 這里通過?@provide('articleService')?向容器提供?articleService?對象實例,這就上面 Resolver 中的?@inject('articleService')?相對應

    • 由于我們想要返回的是 Pagination 類實例,所以需要調用?plainToClass?方法進行一層轉化

    ? ?Model 層

    Service 層其實也是調用 ORM 中的實體方法 Article.findAndCount(由于我們是用Active Records模式的),這個 Article 類就是 ORM 中的實體,其定義也非常簡單:

    // src/entity/article.tsimport { Entity, PrimaryGeneratedColumn, Column, BaseEntity } from "typeorm";import { InterfaceType, ObjectType, Field, ID } from 'type-graphql';@Entity()@InterfaceType()export class Article extends BaseEntity { @PrimaryGeneratedColumn() @Field(type => ID) id: number; @Column() @Field() title: string; @Column() @Field() author: string;}

    仔細觀察,這里的 Article 類,同時接受了 TypeORM 和 TypeGraphQL 兩個庫的裝飾器,寥寥幾行代碼就支持了 GraphQL 類型聲明和 ORM 實體映射,非常清晰明了。
    到這里一個簡單的 GraphQL 分頁功能就開發完畢,從流程步驟來看,一路下來幾乎都是裝飾器語法,整個編寫過程干凈利落,很利于后期的擴展和維護。

    小結


    距離上次寫 Node.js 后臺應用有段時間了,當時的技術棧和現在的沒法比,現在尤其得益于使用 Decorator(裝飾器語法) + DI(依賴注入)風格寫業務邏輯,再搭配使用 typeorm (數據庫的連接)、 type-graphql (GraphQL的處理)工具庫來使用,整體代碼風格更加簡潔,同樣的業務功能,代碼量減少非常可觀且維護性也提升明顯。
    emm,這種感覺怎么描述合適呢?之前寫 Node.js 應用時,能用,但是總覺得哪里很憋屈 —— 就像是白天在交通擁擠的道路上堵車,那種感覺有點糟;而這次混搭了這幾種技術,會感受神清氣爽 —— 就像是在高速公路上行車,暢通無阻。
    前端的技術發展迭代相對來說迭代比較快,這是好事,能讓你用新技術做得更少、收獲地更多;當然不可否認這對前端同學也是挑戰,需要你都保持不斷學習的心態,去及時補充這些新的知識。學無止境,與君共勉。

    鏈接:

    https://pro.ant.design/docs/getting-started-cnhttps://midway.alibaba-inc.com/midwayhttps://ant.design/docs/react/migration-v4-cnhttp://www.ruanyifeng.com/blog/2019/02/orm-tutorial.htmlhttps://github.com/RobinBuschmann/sequelize-typescripthttps://github.com/typeorm/typeormhttps://blog.csdn.net/Frankltf/article/details/86626338https://blog.csdn.net/YamateDD/article/details/6826255https://eggjs.org/zh-cn/basics/app-start.htmlhttps://juejin.im/post/6844903920578330631https://typeorm.io/https://eggjs.org/zh-cn/basics/service.htmlhttps://www.codementor.io/@evanbechtol/node-service-oriented-architecture-12vjt9zs9ihttps://dev.to/pacheco/designing-a-better-architecture-for-a-node-js-api-24dhttps://softwareontheroad.com/ideal-nodejs-project-structure/https://midwayjs.org/midway/guide.htmlhttps://typegraphql.com/docs/resolvers.htmlhttps://zhuanlan.zhihu.com/p/56516614https://segmentfault.com/a/1190000018706816https://graphql.cn/learn/https://npm.alibaba-inc.com/package/apollo-server-koahttps://npm.alibaba-inc.com/package/type-graphql

    https://segmentfault.com/a/1190000009565131

    https://typegraphql.com/docs/types-and-fields.html

    https://stackoverflow.com/questions/53922503/how-to-implement-pagination-in-nestjs-with-typeorm

    https://github.com/typeorm/typeorm/blob/master/docs/zh_CN/find-options.md

    淘系技術部-拍賣前端團隊淘系拍賣前端團隊負責全球最大的在線拍賣平臺,豐富的場景、廣闊的平臺等你一起來挑戰!在這里你可以接觸到淘系全鏈路技術,主流框架( weex, rax, react )、搭建體系、源碼體系、運營中臺、工程套件物料體系、前端智能化等前沿技術,還可以與層層選拔的各路優秀同學共同戰斗,共同成長!歡迎資深前端工程師/專家加入我們,一起打造全新一代的電商運營操作系統,支撐拍賣創新業務。簡歷投遞至:muqin.lmq@alibaba-inc.com(點擊查看詳情)???拓展閱讀作者|玄農編輯|橙子君出品|阿里巴巴新零售淘系技術

    總結

    以上是生活随笔為你收集整理的js node 打包mac应用_混搭 TypeScript + GraphQL + DI + Decorator 风格写 Node.js 应用的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。