使用 React 和 GraphQL 做一個todo list

(點擊上方公衆號,可快速關注)

英文:Igor Ribeiro Lima  譯文:衆成翻譯/樂何 

zcfy.cc/article/graphql-overview-build-a-to-do-list-api-with-a-react-front-end-mdash-sitepoint

設想你想要參考食譜烤一個蛋糕。你將需要一些原料,並且一些合適的量。如果你能拿一個盒子裝好你烘焙所需要的各種原料 ,並且已經稱量好匹配菜譜的份量,那肯定會讓烘焙更簡單。如果你把前端 UI設想成一塊蛋糕的話,那這就是GraphQL所做的事。

在本教程中我們將寫一個小的GraphQL server 來響應Todo List app的請求。你也可以 在衆多app中來挑選,但是這些日子我開始使用React做項目,所以我將會選擇React來做前端框架。不過,你也可以挑選你用得習慣的任何js框架。

GraphQL

GraphQL 允許我們定義 一個查詢來提供一個通用的接口在客戶端和服務端之間來請求和處理數據。它用一種查詢語言來處理,允許客戶端使用一種直觀和靈活的語法和來按照客戶端程序的設計和需求來構建和組裝數據。

這使客戶端從服務端 檢索數據更加的高效。舉個栗子,設想從一個 GraphQL的實例中, 客戶端除了title和id 其它的字段一概不要,那麼這個模型應該是長這樣:

query Query {

  todos {

    id,

    title

  }

}

結果數據(JSON)是:

Which produces the resulting data (in JSON):

{

  "data": {

    "todos": [

      {

        "id": 1446412739542,

        "title": "Read emails"

      },

      {

        "id": 1446412740883,

        "title": "Buy orange"

      },

      {

        "id": 1446412741215,

        "title": "Fix garbage"

      }

    ]

  }

}

大概我們的展示demo中沒有保存數據。這背後的原因是每次我們都啓動服務,在內存中存儲的Todo(s)數組變爲空了。我們將在下面的內容中展示如何向數組中添加數據。

如你所見,返回的格式已經被替換成了客戶端已經定義和描述過了的查詢格式。就像文章「GraphQL 概述 – GraphQL 和 Node.js 入門 」中規定的。

GraphQL的查詢都像是沒有屬性的JSON對象,GraphQL 不是一種語言特性 這點被提到 很重要,它只是在客戶端和服務端中間的一種規範。如果使用通用的語言,任何的客戶端都能和任何服務端通信。

介紹 GraphQL.js

GraphQL.js 是一種基於js的GraphQL參考模型,它提供了兩個重要的功能:

  1. 創建一種類型的語法模型(schema)。

  2. 應該該類型的語法(schema)的查詢 。

需要創建一個匹配代碼基層的GraphQL 類型語法(schema)。在接下來的代碼中,我們定義一個簡單的語法(schema)。它有一種類型和一個彙總的Todo(s)列表(每個列表元素有含有三個字段),額外的,它還提供了服務於該類型語法(schema)和查詢結果。

var graphql = require ('graphql');


// Here is some dummy data to make this piece of code simpler.

// It will be changeable after introducing mutation.

var TODOs = [

  {

    "id": 1446412739542,

    "title": "Read emails",

    "completed": false

  },

  {

    "id": 1446412740883,

    "title": "Buy orange",

    "completed": true

  }

];


var TodoType = new graphql.GraphQLObjectType({

  name: 'todo',

  fields: function () {

    return {

      id: {

        type: graphql.GraphQLInt

      },

      title: {

        type: graphql.GraphQLString

      },

      completed: {

        type: graphql.GraphQLBoolean

      }

    }

  }

});


var queryType = new graphql.GraphQLObjectType({

  name: 'Query',

  fields: function () {

    return {

      todos: {

        type: new graphql.GraphQLList(TodoType),

        resolve: function () {

          return TODOs;

        }

      }

    }

  }

});


module.exports = new graphql.GraphQLSchema({

  query: queryType

});

讓我們現在看一下給我們一個JSON數據結果的js文件的代碼:

var graphql = require ('graphql').graphql

var express = require('express')

var graphQLHTTP = require('express-graphql')

var Schema = require('./schema')

var query = 'query { todos { id, title, completed } }'


graphql(Schema, query).then( function(result) {

  console.log(JSON.stringify(result));

  // Prints

  // {

  // "data":{

  // "todos":[

  // {

  // "id":1446412739542,

  // "title":"Read emails",

  // "completed":false

  // },

  // {

  // "id":1446412740883,

  // "title":"Buy orange",

  // "completed":true

  // }

  // ]

  // }

  // }

});


var app = express()

  .use('/', graphQLHTTP({ schema: Schema, pretty: true }))

  .listen(8080, function (err) {

    console.log('GraphQL Server is now running on localhost:8080');

  });

下面的代碼提供了跟上面同樣 的執行結果,cURL在本例中並非是強制執行來得到 更長遠的優勢 的。它只是一種不用在瀏覽器中擊中例子 來檢索數據的方式 。請注意萬一你是一個Windows 用戶, 你可以使用Windows的命令提示符來執行cURL例子,此外, 這裏你還可以找到好的資源來在你的系統裏安裝cURL.

$ curl -XPOST -"Content-Type:application/graphql" -'query { todos { title } }' http://localhost:8080

{

  "data": {

    "todos": [

      {

        "title": "Read emails"

      },

      {

        "title": "Buy orange"

      }

    ]

  }

}

關於語法(schema)的一個重要的事情 ,自從它描述了用戶可以使用的API,它就假定數據已經存儲 了。數據存儲和描述的方式是一種實現細節。

React

React 是由Facebook和Instagram來開發 的一種用來創建用戶界面 JavaScript 庫。很多人會認爲React是MVC模型中的V, 官方文檔中是這樣規定的:

我們做出React是爲了解決一個問題:創建大型應用時,加載數據超時。根源在於構建可重用的組件。實際上,本質就是構建組件庫。

如果你需要一個React指南,你可以閱讀下面的資料:

  • 視頻: React入門

  • React JS庫概述

  • 視頻: 單向數據流概述

一個簡單的React 組件

React 組件通過render()方法來獲取輸入數據並將返回結果渲染展示。這裏是使用JSX(跟XML語法相似)的例子. JSX是一個非必須項。JSX是一種更像是XML的JavaScript 語法擴展,你可以使用React將簡單的JSX語法轉化。

輸入數據可以通過this.props來向render()渲染的組件傳值。下面是關於如何創建一個React 組件的簡單的例子 並且在 CodePen中可用.

var Application = React.createClass({

  render: function() {

    return

      { this.props.text }

      { this.props.id }

    ;

  }

});

和適當的這些預編譯的代碼,這些未編譯過的JavaScript代碼由JSX編譯器生成。

"use strict";

var Application = React.createClass({

  displayName: "Application",

  render: function render() {

    return React.createElement(

      "div",

      null,

      this.props.text,

      this.props.id

    );

  }

});

如果你想探究更多關於React 組件,可以花一分鐘 看一下這個視頻關於組件狀態的介紹.

一次關於本例子的彩排

首先,我的們需要 一個服務端 (運行正常的)來接收我們從Todo List應用發出的GraphQL請求。這個服務端已經在上面寫好了。

開啓我們的服務,在命令行中執行:

$ git clone https://github.com/sitepoint-editors/todo-graphql-server.git

$ cd todo-graphql-server

$ npm install

$ npm start

你必須已經安裝Node v4.0.0以其更高的版本,因爲 服務端的代碼 使用了在老版本中並不支持的ES2015 特性

任何以/graphql結尾的POST請求 將會與我們的GraphQL語法(schema)發生執行衝突。測試一下是否正常運行,輸入以下 代碼 :

$ curl -XPOST -"Content-Type:application/graphql" -'query { todos { title } }' http://localhost:8080

{

  "data": {

    "todos": []

  }

}

還是沒有數據保存。所以我們每次重啓服務,在內存中的存儲了todo(s) 數組數據都會被清空。當然 ,我們不單單想只讀空數組,我們還需要添加和更新數據。這種接收異常類型的操作,在GraphQL中被 稱作修改mutations),定義一個修改(mutations)跟定義一個查詢一樣,也會返回一個類型的的值。這個想法是無論什麼變量發生了變化 ,就返回什麼。

var MutationAdd = {

  type: new GraphQLList(TodoType),

  description: 'Add a Todo',

  args: {

    title: {

      name: 'Todo title',

      type: new GraphQLNonNull(GraphQLString)

    }

  },

  resolve: (root, {title}) => {

    TODOs.push({

      id: (new Date()).getTime(),

      title: title,

      completed: false

    });

    return TODOs;

  }

};


var MutationType = new GraphQLObjectType({

  name: 'Mutation',

  fields: {

    add: MutationAdd

  }

});


export var Schema = new GraphQLSchema({

  query: QueryType,

  mutation: MutationType

});

上面的箭頭符號 (=>) 參考定義函數的新語法, ES2015中最有趣的部分之一。

正如Clay Allsopp所寫的這篇題爲「Your First GraphQL Server」 文章中所寫,

修改(mutation)與查詢之間的一個有意義的區別是轉換是串行的,但是查詢沒有這樣的規定(實際上,GraphQL鼓勵服務端爲獨立查詢開發固有的並行模型)。GraphQL說明書提供 了這個關於修改(mutation)查詢的一個集合例子必須按照下面的順序在服務端執行:

{

  first: changeTheNumber(newNumber: 1) {

    theNumber

  },

  second: changeTheNumber(newNumber: 3) {

    theNumber

  },

  third: changeTheNumber(newNumber: 2) {

    theNumber

  }

}

因此,在請求結束, theNumber 字段值一定是2。在這個快速的修改(mutation)的介紹之後,我們可以最終在服務端添加一個todo

$ curl -XPOST -"Content-Type:application/graphql" -'mutation { add (title: "Clean garage") { id, title } }' http://localhost:8080

{

  "data": {

      "data": {

    "add": [

      {

        "id": 1446443172937,

        "title": "Clean garage"

      }

    ]

  }

}

是不是相當的酷?我們除了這個添加(add)修改還有更多的修改(mutation):toggle, toggleAll, destroy,

相關文章
相關標籤/搜索