1. 程式人生 > 實用技巧 >vue 封裝highcharts元件

vue 封裝highcharts元件

GraphQL 簡介

一種用於 API 的查詢語言。

GraphQL 既是一種用於 API 的查詢語言也是一個滿足你資料查詢的執行時。 GraphQL 對你的 API 中的資料提供了一套易於理解的完整描述,使得客戶端能夠準確地獲得它需要的資料,而且沒有任何冗餘,也讓 API 更容易地隨著時間推移而演進,還能用於構建強大的開發者工具。

對比 restful api:

  • restful —個介面只能返回一個資源, graphql 一次可以獲取多個資源。
  • restful 用不同的 url 來區分資源, graphql 用型別區分資源。

安裝

使用 JavaScript 語言,express node.js 框架開始。

npm install express express-graphql graphql

建立 server.js 並 使用命令 node server.js 執行 demo。

const express = require('express');
const app = express();
const { graphqlHTTP } = require('express-graphql');
const { buildSchema } = require('graphql');

const schema = buildSchema(`
  type Query {
    hello: String
  }
`);

const root = { hello: () => 'Hello world!' };

app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
}));
app.listen(4000, () => console.log('Now browse to localhost:4000/graphql'));

引數型別

  • 基本型別: String, Int, Float, Boolean, ID
  • [型別]代表陣列,例如:[Int]代表整型陣列。

引數傳遞

  • 和 JavaScript 傳遞引數一樣,小括號內定義形參,但是注意 : 引數需要定義型別。
  • ! 歎號代表引數不能為空。
type Query {
    rollDice (numDice: Int!, numSide: Int): [Int]
}

自定義引數型別

GraphQL 允許使用者自定義引數型別,通常用來描述要獲取的資源的屬性。

type Account {
    name: String
    age: Int
    sex: String
    department: String
    salary: (city: String): Int
}

type Query {
    account (name: string): Account
}

server.js

const schema = buildSchema(`
  type Account {
    name: String
    age: Int
    sex: String
    department: String
    salary(city: String): Int
  }

  type Query {
    hello: String
    getClassMates(classNo: Int!): [String]
    account (username: String): Account
  }
`)

const root = {
  hello: () => 'Hello world!',

  getClassMates: ({ classNo }) => {
    const obj = {
      31: ['張三', '李四', '王五'],
      61: ['張大三', '李大四', '王大五'],
    }
    return obj[classNo]
  },

  account: ({ username }) => {
    const name = username
    const sex = 'nan'
    const age = 10
    const department = '開發部'
    const salary = ({ city }) => {
      if (city === '北京' || city === '上海' || city === '深圳' || city === '廣州') {
        return 10000
      }
      return 3000
    }
    return {
      name,
      sex,
      age,
      department,
      salary,
    }
  },
}

在客戶端訪問GraphQL介面

在 server.js 公開資料夾 供使用者訪問靜態資源。

app.use(express.static('public'))

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

    <button onclick="getData()">獲取資料</button>

    <script>

        function getData() {
            const query = `
                query Account ($username: String!) {
                    account(username: $username) {
                        name
                        age
                        sex
                        salary(city: "北京")
                    }
                }`

            const variables = { username: '李大四' }

            fetch('/graphql', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Accept': 'application/json'
                },
                body: JSON.stringify({
                    query,
                    variables
                })
            })
                .then(res => res.json)
                .then(json => {
                    console.log(json)
                })
        }
    </script>
</body>
</html>

後端的 username 對應 variales 的值中的 username, 和 query 中的 $username

使用 Mutations 修改資料

不能單獨使用 Mutation, 需結合 Query


const schema = buildSchema(`
  type Account {
    name: String
    age: Int
    sex: String
    department: String
    salary(city: String): Int
  }

  input AccountInput {
    name: String
    age: Int 
    sex: String
    department: String
    salary: Int
  }

  type Query {
    accounts: [Account]
  }

  type Mutation {
      createAccount(input: AccountInput): Account
      updateAccount(id: ID!, input: AccountInput): Account
  }
`)

// 模擬資料庫
const fakeDb = {}

const root = {
  createAccount({ input }) {
    // 模擬資料庫儲存
    fakeDb[input.name] = input
    // 返回儲存結果
    return fakeDb[input.name]
  },

  updateAccount({ id, input }) {
    // 模擬更新資料庫
    const updatedAccount = Object.assign({}, fakeDb[id], input)
    fakeDb[id] = updatedAccount
    return updatedAccount
  },

  accounts() {
    let arr = []
    for (const key in fakeDb) {
      arr.push(fakeDb[key])
    }
    return arr
  },
}

試著建立一個 account。

通過 accounts,查詢到剛剛建立的 account。

試著修改資料,我們將 age 18 改為 20,並將該 account 更改後的資訊返回。

這裡將 name 作為記錄的主鍵了。

Constructing Types

上文通過字串的形式構建 schema,還可以通過建構函式來構建。

以前:

const schema = buildSchema(`
  type Account {
    name: String
    age: Int
    sex: String
    department: String
    salary(city: String): Int
  }

  type Query {
    account (username: String): Account
  }
`)

現在

const AccountType = new graphql.GraphQLObjectType({
  name: 'Account',
  fields: {
    name: {type: graphql.GraphQLString},
    age: {type: graphiql.GraphQLInt},
    sex: {type: raphql.GraphQLString},
    department: {type: graphql.GraphQLString},
  }
})

const queryType = new graphiql.GraphQLObjectType({
  name: 'Query',
  fields: {
    account: {
      type: AccountType,
      args: {
        username: {type: graphiql.GraphQLString}
      },
      resolve(_, {username}){
        const name = username
        const sex = 'nan' 
        const age = 18
        const department = '開發部'
        return {
          name,
          age,
          sex,
          department
        }
      }
    }
  }
})

const schema = new graphiql.GraphQLSchema({query: queryType})

程式碼量提升,編輯器提示,可維護性提升,報錯資訊更精準。

結合 MySql CURD

接下來需要稍作更改,拼接幾個 SQL 語句, 操作資料庫。

建立資料庫 test , 表 account,並新增幾個欄位如下:

npm i mysql -S
const mysql = require('mysql')

const pool = mysql.createPool({
  connectionLimit: 10,
  host: 'localhost',
  user: 'root',
  password: '123456',
  database: 'test',
})

const schema = buildSchema(`
  type Account {
    name: String
    age: Int
    sex: String
    department: String
    salary(city: String): Int
  }

  input AccountInput {
    name: String
    age: Int 
    sex: String
    department: String
    salary: Int
  }

  type Query {
    accounts: [Account]
  }

  type Mutation {
      createAccount(input: AccountInput): Account
      updateAccount(id: ID!, input: AccountInput): Account
      deleteAccount(id: ID!): Boolean
  }
`)

const rootValue = {
  createAccount({ input }) {
    const data = {
      name: input.name,
      age: input.age,
      sex: input.sex,
      department: input.department,
    }

    // query 是非同步操作, 使用  promise
    return new Promise((resolve, reject) => {
      pool.query('insert into account set ?', data, err => {
        if (err) {
          console.log('出錯了' + err.message)
          return
        }
        resolve(data)
      })
    })
  },

  deleteAccount({ id }) {
    return new Promise((resolve, reject) => {
      pool.query('delete from account where id = ?', id, err => {
        if (err) {
          console.log('出錯了' + err)
          reject(false)
          return
        }
        resolve(true)
      })
    })
  },

  updateAccount({ id, input }) {
    const data = input
    return new Promise((resolve, reject) => {
      pool.query('update account set ? where id = ?', [data, id], err => {
        if (err) {
          console.log('出錯了' + err.message)
          return
        }
        resolve(data)
      })
    })
  },

  accounts() {
    return new Promise((resolve, reject) => {
      pool.query('select name, age, sex, department from account', (err, res) => {
        if (err) {
          console.log('出錯了' + err)
          return
        }
        let arr = []
        for (let i = 0; i < res.length; i++) {
          arr.push({
            name: res[i].name,
            sex: res[i].sex,
            age: res[i].age,
            department: res[i].department,
          })
        }
        resolve(arr)
      })
    })
  },
}

程式碼片段

為方便操作,我將完整程式碼片段放在最後,供你試一試。

fakeDb
const express = require('express')
const app = express()
const { graphqlHTTP } = require('express-graphql')
const { buildSchema } = require('graphql')

const schema = buildSchema(`
  type Account {
    name: String
    age: Int
    sex: String
    department: String
    salary(city: String): Int
  }

  input AccountInput {
    name: String
    age: Int 
    sex: String
    department: String
    salary: Int
  }

  type Query {
    hello: String
    getClassMates(classNo: Int!): [String]
    account (username: String): Account
    accounts: [Account]
  }

  type Mutation {
      createAccount(input: AccountInput): Account
      updateAccount(id: ID!, input: AccountInput): Account
  }
`)

const fakeDb = {}

const rootValue = {
  hello: () => 'Hello world!',

  getClassMates: ({ classNo }) => {
    const obj = {
      31: ['張三', '李四', '王五'],
      61: ['張大三', '李大四', '王大五'],
    }
    return obj[classNo]
  },

  account: ({ username }) => {
    const name = username
    const sex = 'nan'
    const age = 10
    const department = '開發部'
    const salary = ({ city }) => {
      if (city === '北京' || city === '上海' || city === '深圳' || city === '廣州') {
        return 10000
      }
      return 3000
    }
    return {
      name,
      sex,
      age,
      department,
      salary,
    }
  },

  createAccount({ input }) {
    // 模擬資料庫儲存
    fakeDb[input.name] = input
    // 返回儲存結果
    return fakeDb[input.name]
  },

  updateAccount({ id, input }) {
    // 模擬更新資料庫
    const updatedAccount = Object.assign({}, fakeDb[id], input)
    fakeDb[id] = updatedAccount
    return updatedAccount
  },

  accounts() {
    let arr = []
    for (const key in fakeDb) {
      arr.push(fakeDb[key])
    }
    return arr
  },
}

// 公開資料夾  供使用者訪問靜態資源
app.use(express.static('public'))

// const middleware = (req, res, next) => {
//   // console.log(req.headers.cookie)
//   if (req.url.indexOf('/graphql') !== -1) {
//     res.send(
//       JSON.stringify({
//         error: '您沒有權訪問這個介面',
//       })
//     )
//     return
//   }
//   next()
// }

// app.use(middleware)

app.use(
  '/graphql',
  graphqlHTTP({
    schema,
    rootValue,
    graphiql: true,
  })
)

app.listen(4000, () => console.log('Now browse to localhost:4000/graphql'))

MySQL
const express = require('express')
const app = express()
const { graphqlHTTP } = require('express-graphql')
const { buildSchema } = require('graphql')
const mysql = require('mysql')
const { resolve } = require('path')

const pool = mysql.createPool({
  connectionLimit: 10,
  host: 'localhost',
  user: 'root',
  password: '123456',
  database: 'test',
})

const schema = buildSchema(`
  type Account {
    name: String
    age: Int
    sex: String
    department: String
    salary(city: String): Int
  }

  input AccountInput {
    name: String
    age: Int 
    sex: String
    department: String
    salary: Int
  }

  type Query {
    hello: String
    getClassMates(classNo: Int!): [String]
    account (username: String): Account
    accounts: [Account]
  }

  type Mutation {
      createAccount(input: AccountInput): Account
      updateAccount(id: ID!, input: AccountInput): Account
      deleteAccount(id: ID!): Boolean
  }
`)

const rootValue = {
  hello: () => 'Hello world!',

  getClassMates: ({ classNo }) => {
    const obj = {
      31: ['張三', '李四', '王五'],
      61: ['張大三', '李大四', '王大五'],
    }
    return obj[classNo]
  },

  account: ({ username }) => {
    const name = username
    const sex = 'nan'
    const age = 10
    const department = '開發部'
    const salary = ({ city }) => {
      if (city === '北京' || city === '上海' || city === '深圳' || city === '廣州') {
        return 10000
      }
      return 3000
    }
    return {
      name,
      sex,
      age,
      department,
      salary,
    }
  },

  accounts() {
    return new Promise((resolve, reject) => {
      pool.query('select name, age, sex, department from account', (err, res) => {
        if (err) {
          console.log('出錯了' + err)
          return
        }
        let arr = []
        for (let i = 0; i < res.length; i++) {
          arr.push({
            name: res[i].name,
            sex: res[i].sex,
            age: res[i].age,
            department: res[i].department,
          })
        }
        resolve(arr)
      })
    })
  },

  createAccount({ input }) {
    const data = {
      name: input.name,
      age: input.age,
      sex: input.sex,
      department: input.department,
    }
    return new Promise((resolve, reject) => {
      pool.query('insert into account set ?', data, err => {
        if (err) {
          console.log('出錯了' + err.message)
          return
        }
        resolve(data)
      })
    })
  },

  updateAccount({ id, input }) {
    const data = input
    return new Promise((resolve, reject) => {
      pool.query('update account set ? where id = ?', [data, id], err => {
        if (err) {
          console.log('出錯了' + err.message)
          return
        }
        resolve(data)
      })
    })
  },

  deleteAccount({ id }) {
    return new Promise((resolve, reject) => {
      pool.query('delete from account where id = ?', id, err => {
        if (err) {
          console.log('出錯了' + err)
          reject(false)
          return
        }
        resolve(true)
      })
    })
  },
}

// 公開資料夾  供使用者訪問靜態資源
app.use(express.static('public'))

app.use(
  '/graphql',
  graphqlHTTP({
    schema,
    rootValue,
    graphiql: true,
  })
)

app.listen(4000, () => console.log('Now browse to localhost:4000/graphql'))