手搭一個 React,Typescript,Koa,GraphQL 環境

本文系原創,轉載請附帶做者信息:yhlben

項目地址:https://github.com/yhlben/cdfang-spidercss

前言

在實際的開發過程當中,從零開始初始化一個項目每每很麻煩,因此各類各樣的腳手架工具應運而生。crea-react-app,vue-cli,@angular/cli 等腳手架工具,只須要執行一個命令,項目結構以及開發環境就搭建好了。html

腳手架工具確實方便了咱們使用,開發者能夠專一於業務,而不須要考慮太多的環境搭建。但做者認爲,學習腳手架工具背後的搭建過程也是很重要的,以防腳手架掛了以後,咱們還能正常搭建項目。基於這個目的,做者從零搭建了cdfang-spider項目。前端

如今讓咱們就以這個項目爲例,從零開始搭建項目吧。vue

項目選型

  1. 三大框架裏選哪一個?node

    • react 我的愛好。
    • react-router 定義路由。
    • react context 狀態管理。
    • react hooks 組件化。
  2. 引入強類型語言?react

    • typescript。爲 js 提供類型支持,編輯器友好,增長代碼可維護性,使用起來內心踏實。
    • 在使用第三方庫時,能夠寫出更加符合規範的代碼,避免 api 亂用等。
    • 項目中依賴了大量 @types/xxx 包,無形中增長了項目體積。
    • 編輯器對 ts 文件進行類型檢查,須要遍歷 node_modules 目錄下全部的 @types 文件,會形成編輯器卡頓現象。
    • 目前仍然存在不少庫沒有 @types 支持,使用起來並不方便。
  3. css 選型?webpack

    • 預編譯器 less。項目中使用了變量定義,選擇器嵌套,選擇器複用等,less 夠用了。
    • 解決命名衝突可使用 css modules,暫未考慮 css in js。
    • 使用 bem 命名規範。
    • 使用 postcss 插件 autoprefixer,增長 css 兼容性。
  4. 構建工具選哪一個?nginx

    • webpack。內置 tree shaking,scope hosting 等,打包效率高,社區活躍。
    • webpack-merge 合併不一樣環境配置文件。
    • 配置 externals。引入 cdn 代替 node_modules 中體積較大的包。
    • gulp。用來打包 node 端代碼。
  5. 代碼規範檢查?git

    • eslint。輔助編碼規範執行,有效控制代碼質量。同時也支持校驗 typescript 語法。
    • 配置 eslint-config-airbnb 規則。
    • 配置 eslint-config-prettier 關閉和 prettier 衝突的規則。
  6. 測試框架選型?es6

    • jest。大而全,包含:測試框架,斷言庫,mock 數據,覆蓋率等。
    • enzyme。測試 react 組件。
  7. 後端框架選型?

    • koa。精簡好用,中間件機制強大。
    • apollo-server。幫助搭建 graphQL 後端環境。
  8. 數據庫選型?

    • mongodb。類 json 的存錯格式,方便存儲,前端友好。
    • 配置 mongoose,方便給 mongodb 數據庫建模。
  9. 接口方式選型?

    • graphql。能夠根據須要格式獲取對應數據,減小接口冗餘數據。
    • graphql schema 定義了後端接口的參數,操做和返回類型,今後不須要提供接口文檔。
    • 前端能夠在 schema 定義後開始開發,數據格式本身掌握。
    • schema 可拼接。能夠組合和鏈接多個 graphql api,進行級聯查詢等。
    • 社區友好,有不少優秀的庫能夠直接使用: apollo,relay 等。

基本框架選型完畢,接下來就開始搭建項目環境。

搭建 TypeScript 環境

TypeScript 是 JavaScript 的超集,意味着能夠徹底兼容 JavaScript 文件,但 TypeScript 文件卻並不能直接在瀏覽器中運行,須要通過編譯生成 JavaScript 文件後才能運行。

一、 新建 tsconfig.json 文件。

  • tsc -init 生成初始化 tsconfig.json 文件。
  • vscode 會根據 tsconfig.json 文件,進行動態類型檢查,語法錯誤提示等。
  • tsc 命令會根據 tsconfig.json 文件配置的規則,將 ts 代碼轉換爲 js 代碼。
  • tslint 會讀取 tsconfig.json 文件中的規則,輔助編碼規範校驗。

    • tslint 官宣會被廢棄,後將被 eslint 代替。
    • eslint 一樣會用到 tsconfig.json 文件中的內容。

二、 配置 eslint。

根據 typescript-eslint 引導,配置 eslint 對 typescript 的支持。

  • @typescript-eslint/parser 解析 ts 語法。
  • @typescript-eslint/eslint-plugin 爲 ts 文件應用 eslint 和 tslint 規則。

三、 選擇一個 typescript 編譯器,tsc 仍是 babel?

使用 babel。好處以下:

  • babel 社區有許多很是好的插件,babel-preset-env 能夠支持到具體兼容瀏覽器的版本號,而 tsc 編譯器沒這個功能。
  • babel 能夠同時支持編譯 js 和 ts,因此不必在引入 tsc 編譯 ts 文件,只管理一個編譯器,可維護性更高。
  • babel 編譯速度更快。tsc 編譯器須要遍歷全部類型定義文件(*.d.ts),包括 node_modules 裏的,以確保代碼中正確地使用,type 太多會形成卡頓。

babel 流程分析

babel 是一個 js 語法編譯器,在編譯時分爲 3 個階段:解析、轉換、輸出。

  • 解析階段:將 js 代碼解析爲抽象語法樹(ast)。
  • 轉換階段:對 ast 進行修改,產生一個轉換後的 ast。
  • 輸出階段:將轉換後的 ast 輸出成 js 文件。

plugin 和 preset

  • plugin: 解析,轉換,並輸出轉換後的 js 文件。例如:@babel/plugin-proposal-object-rest-spread 會輸出支持{...}解構語法的 js 文件。
  • preset: 是一組組合好的 plugin 集合。例如:@babel/preset-env 讓代碼支持最新的 es 語法,自動引入須要支持新特性的 plugin。

四、蒐集全部的 ts,tsx 頁面(前端環境使用 webpack,node 項目使用 gulp),而後經過 babel 編譯成 js 文件。

搭建 React 環境

React 是一個庫,基於組件式開發,開發時經常須要用到如下語法:

  • es6 模塊化。
  • jsx 語法。
  • typescript 語法。
  • css 預處理器。

這些語法在目前瀏覽器中並不能直接執行,須要進行打包編譯,這也是搭建 React 環境的主要工做。

具體步驟

一、新建一個 html 文件,並在 body 中建立一個根節點,用於掛載 react 最後生成的 dom。

二、新建一個 index.tsx 文件,用於將項目中的全部組件,引入進來,並調用 render 方法,將組件渲染到根節點中。

三、React 項目分層。

  • containers 目錄,存放單獨的頁面
  • components 目錄,存放的是組件,一個組件包含 jsx 和 css 兩個部分。
  • context 目錄,存放公用的 react context。
  • config 目錄,存放公共配置文件。
  • utils 目錄,公用的函數組件庫。
  • constants 目錄,存放靜態變量。

四、配置 webpack,以 index.tsx 爲入口文件,進行打包編譯。

  • 因爲不一樣環境的打包方式並不相同,這裏抽象出開發環境、上線環境、優化環境的配置文件,使用 webpack-merge 合併配置文件。
  • 配置 css 預處理器,使用 less-loader。
  • 配置 ts 編譯器,使用 babel-loader。

    • @babel/preset-env:編譯最新的 es 語法。
    • @babel/preset-react:編譯 react 語法。
    • @babel/preset-typescript:轉換 typescript 語法。
  • 配置 url-loader,打包項目中的圖片資源。
  • 配置 html-webpack-plugin 將最後生成的 js,css,注入第 1 步的 html 中。

    • 使用 ejs 模板配置開發環境和線上環境引入的 cdn。
  • 開發環境配置,使用開箱即用的 webpack-dev-server。

    • webpack-dev-server 能夠自動監聽文件修改,自動刷新頁面,以及默認 source-map 等功能。
    • 配置熱模塊替換,react-hot-loader。
webpack 打包原理

webpack 打包過程就像是一條流水線,從入口文件開始,蒐集項目中全部文件的依賴關係,若是遇到不可以識別的模塊,就使用對應的 loader 轉換成可以識別的模塊。webpack 還能使用 plugin 在流水線生命週期中掛載自定義事件,來控制 webpack 輸出結果。

五、編寫 npm script,一鍵開啓開發模式。

// cross-env 用來跨環境設置環境變量
"scripts": {
  "dev:client": "cross-env NODE_ENV=development webpack-dev-server --open"
}

六、如今運行 npm run dev:client 就能夠愉快地編寫客戶端代碼了。

搭建 NodeJs 環境

因爲 node 端使用了 typescript 和最新的 es 語法,因此須要進行打包編譯。

  • 配置 gulp,遍歷每個 ts 文件,調用 gulp-babel,將 ts 代碼轉換成 js 代碼。
  • 配置 supervisor 自動重啓 node 服務(nodemon 對於不存在的目錄不能進行監控)。
  • 編寫 npm script 一鍵啓動 node 端開發環境。
"scripts": {
  "dev:server": "cross-env NODE_ENV=development gulp & cross-env NODE_ENV=development supervisor -i ./dist/client/ -w ./dist/ ./dist/app.js",
}

配置好 gulp 後,就能夠運行 npm run dev:server 一鍵啓動服務器端開發環境。

層次結構劃分

項目採用傳統的 mvc 模式進行層次劃分。

Model 層

Model 層的主要工做:鏈接數據庫,封裝數據庫操做,例如:新增數據、刪除數據、查詢數據、更新數據等。

  • 新建 model 文件夾,目錄下的每個文件對應數據庫的一個表。
  • model 文件中包含對一個數據表的增刪改查操做。

    • 使用 mongoose 更方便地對 mongodb 數據庫進行讀寫操做。
  • model 文件返回封裝好的對象,提供給 controller 層使用。

Controller 層

Controller 層的主要工做:接收和發送 http 請求。根據前端請求,調用 model 層獲取數據,再返回給前端。

傳統的後端通常還包含 service 層,專門用來處理業務邏輯。
  • 根據前端請求,找到對應的 model 層獲取數據,通過加工處理後,返回給前端。
  • 編寫中間件,記錄系統日誌,錯誤處理,404 頁面等。
  • 支持前端 react-router 中的 BrowserRouter。根據前端路由,後端配置對應的路由,匹配結果爲 index.html 文件。
  • 項目中使用的 graphql 比較基礎,也直接放在了 controller 層進行處理。

View 層

View 層的主要工做:提供前端頁面模板。若是是服務器端渲染,是將 model 層的數據注入到 view 層中,最後經過 controller 層返回給客戶端。因爲本項目前端使用 react 渲染,因此 view 層直接是通過 webpack 打包後的頁面。

  • 使用 koa-static 提供一個靜態文件服務器,用來訪問前端打包後生成的 html 文件。

搭建 GraphQL 環境

GraphQL 是一種用於 api 的查詢語言,須要服務器端配置 graphql 支持,同時也須要客戶端使用 graphql 語法的格式進行請求。

使用 apollo 更快的搭建 graphql 環境。

  • 服務器端配置 apollo-server。

    • 使用 schema,定義請求的類型,返回的格式。
    • 使用 resolvers 來處理對應的 schema。
  • 客戶端配置 apollo-client。

    • 按照 apollo-server 定義的 schema,來請求數據。

搭建 MongoDB 環境

MongoDB 是一個面向文檔存儲的數據庫,操做起來十分簡單。

Mongoose 爲 mongodb 提供了一種直接的,基於 scheme 結構去定義你的數據模型。它內置數據驗證,查詢構建,業務邏輯鉤子等,開箱即用。

  • 使用 mongoose 創建和本地 mongodb 的鏈接。
  • 建立 model 模型,一個模型對應 mongodb 裏的一張表。
  • 根據 model 封裝增刪改查功能,並返回給 controller 層使用。

接下來的步驟就是安裝 mongodb,啓動服務,就能夠了。

搭建測試環境

本項目使用 jest 做爲測試框架,jest 包含了斷言庫、測試框架、mock 數據等功能,是一個大而全的測試庫。因爲前端使用了 react 項目,這裏引入了專門用來測試 react 的 enzyme 庫。

一、新建 jest.config.js 文件。

  • 配置初始化 setup.ts 文件。

    • 根據 react 版本配置對應的 enzyme-adapter。
    • mock 全局變量,如 fech,canvas 等。
  • 配置須要測試的文件。
  • 配置 mock 數據文件。
  • 配置測試文件的編譯方式。

    • ts 代碼使用 ts-jest 編譯。
  • 配置代碼覆蓋率文件。

二、編寫測試文件。

  • 新建__mocks__,__tests__目錄,存放測試文件和 mock 數據文件。
  • 按照 src 中的目錄,創建相應的測試文件目錄。

三、編寫測試腳本和上傳覆蓋率腳本。

"scripts": {
  "test": "jest --no-cache --colors --coverage --forceExit --detectOpenHandles",
  "coverage": "codecov"
}

配置上線環境

安裝好各類環境以後,接下來就要考慮項目上線了。

配置服務器環境

  • 安裝 nodejs 環境。nvm 安裝 node
  • 安裝 pm2 進程守護。npm i pm2 -g
  • 安裝 mongodb。mongodb 官方文檔
  • 安裝免費 https 證書。letsencrypt 官網

    • 域名須要先進行備案(使用阿里雲備案,資料準備齊全的話 10 天左右就能夠批下來)。

代碼發佈

本項目發佈很是簡單,只須要一步操做就搞定了,這些都是通過持續集成配置後的結果。

# clone with Git Bash
git clone https://github.com/yhlben/cdfang-spider.git

# change directory
cd cdfang-spider

# install dependencies
npm i

# build for production with minification
npm run build

全部的事情都在 build 命令下完成了,咱們分析一下 npm run build 命令作的事情。

  • eslint 語法錯誤檢查。
  • 單元測試。

    • 上傳測試覆蓋率。
  • 打包客戶端代碼。

    • 打包後生成 html 文件做爲 node 端的 view 層,和後端綁定在一塊兒。
    • 其餘靜態資源,在 webpack 打包後自動上傳到七牛 cdn,使用 qiniu-upload-plugin 來進行一鍵上傳。
  • 打包服務器端代碼。

上述事情經過建立 npm script 就能夠了完成需求了,但這些命令也不該該每次都由手工敲一遍,經過配置 travisCI,每一次 master 分支提交代碼時,自動運行上述命令就好了。

travisCI 配置

travisCI 是一個持續集成平臺,每當 github 提交代碼時,travisCI 就會獲得通知,而後根據 travisCI 中的配置信息執行相應的操做,並及時把運行結果反饋給用戶。travisCI 配置文件能夠參考項目根目錄下的 .travis.yml 文件。配置文件核心在於 script 的配置。

script:
  - npm run build
  - npm run test
after_success: npm run coverage

能夠看到,每一次 github 提交後,travisCI 就會執行 名稱爲 build 的任務,任務分爲 2 個步驟,首先執行 build 命令,而後執行 test 命令,當命令都執行完成後,執行 coverage 命令。若是執行命令期間出現任何錯誤,travisCI 會經過郵件及時通知咱們。真正要上線時,先查看 ci 狀態,若是已經過全部的步驟,那就不用擔憂發佈的代碼有問題了。

總結

至此,整個項目選型與搭建流程已經介紹完畢了,固然還有一些很細節的地方沒有寫進去,若是有不太明白的地方,能夠提 issue,或者加我微信 yhl2016226。

接下來對如下 4 個方面寫個小總結。

  • 開發方面:項目將前端、後端、數據庫端連通起來,組合成了一個小全棧的項目,加深了我對整個開發環節的理解。
  • 測試方面:經過編寫單元測試,ui 測試,api 測試,積累了自動化測試方面的經驗。
  • 運維方面:經過配置持續集成,守護進程,nginx,https 等,讓我有能力實現小型項目的部署。
  • 技術方面:項目中使用了一些比較新的技術,如:hooks api,graphql 等,但用的都很基礎,主要是爲了練手,後續還得深刻學習。

對於項目後期更新,主要是基於如下幾個方面:graphql,docker,k8s,微服務,serverless 等,東西太多,還得加油學習啊,😂

參考連接

相關文章
相關標籤/搜索