导航菜单

  • 0.api
  • 0.Async
  • 0.module
  • 1.ES2015
  • 2.Promise
  • 3.Node
  • 4.NodeInstall
  • 5.REPL
  • 6.NodeCore
  • 7.module&NPM
  • 8.Encoding
  • 9.Buffer
  • 10.fs
  • 11.Stream-1
  • 11.Stream-2
  • 11.Stream-3
  • 11.Stream-4
  • 12-Network-2
  • 12.NetWork-3
  • 12.Network-1
  • 13.tcp
  • 14.http-1
  • 14.http-2
  • 15.compress
  • 16.crypto
  • 17.process
  • 18.yargs
  • 19.cache
  • 20.action
  • 21.https
  • 22.cookie
  • 23.session
  • 24.express-1
  • 24.express-2
  • 24.express-3
  • 24.express-4
  • 25.koa-1
  • 26.webpack-1-basic
  • 26.webpack-2-optimize
  • 26.webpack-3-file
  • 26.webpack-4.tapable
  • 26.webpack-5-AST
  • 26.webpack-6-sources
  • 26.webpack-7-loader
  • 26.webpack-8-plugin
  • 26.webpack-9-hand
  • 26.webpack-10-prepare
  • 28.redux
  • 28.redux-jwt-back
  • 28.redux-jwt-front
  • 29.mongodb-1
  • 29.mongodb-2
  • 29.mongodb-3
  • 29.mongodb-4
  • 29.mongodb-5
  • 29.mongodb-6
  • 30.cms-1-mysql
  • 30.cms-2-mysql
  • 30.cms-3-mysql
  • 30.cms-4-nunjucks
  • 30.cms-5-mock
  • 30.cms-6-egg
  • 30.cms-7-api
  • 30.cms-8-roadhog
  • 30.cms-9-yaml
  • 30.cms-10-umi
  • 30.cms-12-dva
  • 30.cms-13-dva-ant
  • 30.cms-14-front
  • 30.cms-15-deploy
  • 31.dva
  • 31.cms-13-dva-antdesign
  • 33.redis
  • 34.unittest
  • 35.jwt
  • 36.websocket-1
  • 36.websocket-2
  • 38.chat-api-1
  • 38.chat-api-2
  • 38.chat-3
  • 38.chat-api-3
  • 38.chat
  • 38.chat2
  • 38.chat2
  • 39.crawl-0
  • 39.crawl-1
  • 39.crawl-2
  • 40.deploy
  • 41.safe
  • 42.test
  • 43.nginx
  • 44.enzyme
  • 45.docker
  • 46.elastic
  • 47.oauth
  • 48.wxpay
  • index
  • 52.UML
  • 53.design
  • index
  • 54.linux
  • 57.ts
  • 56.react-ssr
  • 58.ts_react
  • 59.ketang
  • 59.ketang2
  • 61.1.devops-linux
  • 61.2.devops-vi
  • 61.3.devops-user
  • 61.4.devops-auth
  • 61.5.devops-shell
  • 61.6.devops-install
  • 61.7.devops-system
  • 61.8.devops-service
  • 61.9.devops-network
  • 61.10.devops-nginx
  • 61.11.devops-docker
  • 61.12.devops-jekins
  • 61.13.devops-groovy
  • 61.14.devops-php
  • 61.15.devops-java
  • 61.16.devops-node
  • 61.17.devops-k8s
  • 62.1.react-basic
  • 62.2.react-state
  • 62.3.react-high
  • 62.4.react-optimize
  • 62.5.react-hooks
  • 62.6.react-immutable
  • 62.7.react-mobx
  • 62.8.react-source
  • 63.1.redux
  • 63.2.redux-middleware
  • 63.3.redux-hooks
  • 63.4.redux-saga
  • 63.5.redux-saga-hand
  • 64.1.router
  • 64.2.router-connected
  • 65.1.typescript
  • 65.2.typescript
  • 65.3.typescript
  • 65.4.antd
  • 65.4.definition
  • 66-1.vue-base
  • 66-2.vue-component
  • 66-3.vue-cli3.0
  • 66-4.$message组件
  • 66-5.Form组件
  • 66-6.tree
  • 66-7.vue-router-apply
  • 66-8.axios-apply
  • 66-9.vuex-apply
  • 66-10.jwt-vue
  • 66-11.vue-ssr
  • 66-12.nuxt-apply
  • 66-13.pwa
  • 66-14.vue单元测试
  • 66-15.权限校验
  • 67-1-network
  • 68-2-wireshark
  • 7.npm2
  • 69-hooks
  • 70-deploy
  • 71-hmr
  • 72.deploy
  • 73.import
  • 74.mobile
  • 75.webpack-1.文件分析
  • 75.webpack-2.loader
  • 75.webpack-3.源码流程
  • 75.webpack-4.tapable
  • 75.webpack-5.prepare
  • 75.webpack-6.resolve
  • 75.webpack-7.loader
  • 75.webpack-8.module
  • 75.webpack-9.chunk
  • 75.webpack-10.asset
  • 75.webpack-11.实现
  • 76.react_optimize
  • 77.ts_ketang_back
  • 77.ts_ketang_front
  • 78.vue-domdiff
  • 79.grammar
  • 80.tree
  • 81.axios
  • 82.1.react
  • 82.2.react-high
  • 82.3.react-router
  • 82.4.redux
  • 82.5.redux_middleware
  • 82.6.connected
  • 82.7.saga
  • 82.8.dva
  • 82.8.dva-source
  • 82.9.roadhog
  • 82.10.umi
  • 82.11.antdesign
  • 82.12.ketang-front
  • 82.12.ketang-back
  • 83.upload
  • 84.graphql
  • 85.antpro
  • 86.1.uml
  • 86.2.design
  • 87.postcss
  • 88.react16-1
  • 89.nextjs
  • 90.react-test
  • 91.react-ts
  • 92.rbac
  • 93.tsnode
  • 94.1.JavaScript
  • 94.2.JavaScript
  • 94.3.MODULE
  • 94.4.EventLoop
  • 94.5.文件上传
  • 94.6.https
  • 94.7. nginx
  • 95.1. react
  • 95.2.react
  • 96.1.react16
  • 96.2.fiber
  • 96.3.fiber
  • 97.serverless
  • 98.websocket
  • 100.1.react-basic
  • 101.1.monitor
  • 101.2.monitor
  • 102.java
  • 103.1.webpack-usage
  • 103.2.webpack-bundle
  • 103.3.webpack-ast
  • 103.4.webpack-flow
  • 103.5.webpack-loader
  • 103.6.webpack-tapable
  • 103.7.webpack-plugin
  • 103.8.webpack-optimize1
  • 103.9.webpack-optimize2
  • 103.10.webpack-hand
  • 103.11.webpack-hmr
  • 103.11.webpack5
  • 103.13.splitChunks
  • 103.14.webpack-sourcemap
  • 103.15.webpack-compiler1
  • 103.15.webpack-compiler2
  • 103.16.rollup.1
  • 103.16.rollup.2
  • 103.16.rollup.3
  • 103.16.vite.basic
  • 103.16.vite.source
  • 103.16.vite.plugin
  • 103.16.vite.1
  • 103.16.vite.2
  • 103.17.polyfill
  • 104.1.binary
  • 104.2.binary
  • 105.skeleton
  • 106.1.react
  • 106.2.react_hooks
  • 106.3.react_router
  • 106.4.redux
  • 106.5.redux_middleware
  • 106.6.connected-react-router
  • 106.6.redux-first-history
  • 106.7.redux-saga
  • 106.8.dva
  • 106.9.umi
  • 106.10.ketang
  • 106.11.antdesign
  • 106.12.antpro
  • 106.13.router-6
  • 106.14.ssr
  • 106.15.nextjs
  • 106.16.1.cms
  • 106.16.2.cms
  • 106.16.3.cms
  • 106.16.4.cms
  • 106.16.mobx
  • 106.17.fomily
  • 107.fiber
  • 108.http
  • 109.1.webpack_usage
  • 109.2.webpack_source
  • 109.3.dll
  • 110.nest.js
  • 111.xstate
  • 112.Form
  • 113.redux-saga
  • 114.react+typescript
  • 115.immer
  • 116.pro5
  • 117.css-loader
  • 118.1.umi-core
  • 119.2.module-federation
  • 119.1.module-federation
  • 120.create-react-app
  • 121.react-scripts
  • 122.react-optimize
  • 123.jsx-runtime
  • 124.next.js
  • 125.1.linux
  • 125.2.linux-vi
  • 125.3.linux-user
  • 125.4.linux-auth
  • 125.5.linux-shell
  • 125.6.linux-install
  • 125.7.linux-system
  • 125.8.linux-service
  • 125.9.linux-network
  • 125.10.nginx
  • 125.11.docker
  • 125.12.ci
  • 125.13.k8s
  • 125.14.k8s
  • 125.15.k8s
  • 125.16.k8s
  • 126.11.react-1
  • 126.12.react-2
  • 126.12.react-3
  • 126.12.react-4
  • 126.12.react-5
  • 126.12.react-6
  • 126.12.react-7
  • 126.12.react-8
  • 127.frontend
  • 128.rollup
  • 129.px2rem-loader
  • 130.health
  • 131.hooks
  • 132.keepalive
  • 133.vue-cli
  • 134.react18
  • 134.2.react18
  • 134.3.react18
  • 135.function
  • 136.toolkit
  • 137.lerna
  • 138.create-vite
  • 139.cli
  • 140.antd
  • 141.react-dnd
  • 142.1.link
  • 143.1.gulp
  • 143.2.stream
  • 143.3.gulp
  • 144.1.closure
  • 144.2.v8
  • 144.3.gc
  • 145.react-router-v6
  • 146.browser
  • 147.lighthouse
  • 148.1.basic
  • 148.2.basic
  • 148.3.basic
  • 148.4.basic
  • 148.5.basic
  • 149.1.vite
  • 149.2.vite
  • 149.3.vite
  • 149.4.vite
  • 150.react-window
  • 151.react-query
  • 152.useRequest
  • 153.transition
  • 154.emotion
  • 155.1.formily
  • 155.2.formily
  • 155.3.formily
  • 155.3.1.mobx.usage
  • 155.3.2.mobx.source
  • 156.vue-loader
  • 103.11.mf
  • 157.1.react18
  • 158.umi4
  • 159.rxjs
  • 159.rxjs2
  • 160.bff
  • 161.zustand
  • 162.vscode
  • 163.emp
  • 164.cors
  • 1.什么是GraphQL
  • 2.创建后端项目
  • 3. 实现商品分类接口
    • 3.1 server.js
    • 3.2 schema.js
    • 3.3 GraphiQL
  • 4. 实现商品接口
    • 4.1 schema.js
  • 5. 添加商品
    • 5.1 schema.js
  • 6. 使用mongodb数据库
    • 6.1 model.js
    • 6.2 schema.js
    • 6.3 操作步骤
  • 1.生成项目
  • 2.安装依赖
  • 3.连接接口
    • 3.1 src\index.tsx
  • 4.实现前台功能
    • 4.1 src\App.tsx
    • 4.2 src\App.tsx
    • 4.3 src\types.tsx
    • 4.4 src\query.tsx
    • 4.5 src\AddProduct.tsx
    • 4.6 ProductList.tsx
    • 4.7 ProductDetail.tsx

1.什么是GraphQL #

  • graphql 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时
  • GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余
  • 请求你所要的数据不多不少
  • 只用一个请求获取多个资源

2.创建后端项目 #

mkdir server
cd server 
cnpm init -y
cnpm i express graphql express-graphql mongoose  cors --save

3. 实现商品分类接口 #

3.1 server.js #

server.js

const express = require('express');
const graphqlHTTP = require('express-graphql');
const schema = require('./schema');
const cors = require('cors');
const app = express();
app.use(cors({
    origin: 'http://localhost:3000',
    methods: "GET,PUT,POST,OPTIONS"
}));
app.use('/graphql', graphqlHTTP({
    schema,
    graphiql: true
}));
app.listen(4000, () => {
    console.log('server started on 4000');
});

3.2 schema.js #

  • 定义用户自定义类型,类型的每个字段都必须是已定义的且最终都是 GraphQL 中定义的类型。
  • 定义根类型,每种根类型中包含了准备暴露给服务调用方的用户自定义类型
  • 定义 Schema,每一个 Schema 中允许出现三种根类型:query,mutation,subscription,其中至少要有 query

schema.js

const graphql = require('graphql');
const { GraphQLObjectType,
    GraphQLString,
    GraphQLInt,
    GraphQLSchema,
    GraphQLList,
    GraphQLNonNull
} = graphql;
const categories = [
    { id: '1', name: '图书' },
    { id: '2', name: '数码' },
    { id: '3', name: '食品' }
]

const Category = new GraphQLObjectType({
    name: 'Category',
    fields: () => (
        {
            id: { type: GraphQLString },
            name: { type: GraphQLString },
        }
    )
});

const RootQuery = new GraphQLObjectType({
    name: 'RootQuery',
    fields: {
        getCategory: {
            type: Category,
            args: {
                id: {
                    type: GraphQLString
                }
            },
            resolve(parent, args) {
                return categories.find(item => item.id === args.id);
            }
        }
    }
});
module.exports = new GraphQLSchema({
    query: RootQuery
})

3.3 GraphiQL #

  • GraphiQLis an in-browser tool for writing, validating, and testing GraphQL queries.
  • 浏览器访问

  • 每次调用 GraphQL 服务,需要明确指定调用 Schema 中的哪个根类型(默认是 query)

  • 然后指定这个根类型下的哪几个字段(每个字段对应一个用户自定义类型),然后指定这些字段中的那些子字段的哪几个。一直到所有的字段都没有子字段为止
  • Schema 明确了服务端有哪些字段(用户自定义类型)可以用,每个字段的类型和子字段
  • 每次查询时,服务器就会根据 Schema 验证并执行查询
     {
       field(arg: "value") {
         subField
       }
     }
query{
  getCategory(id: "1") {
    id
    name
  }
}

getCategory

getCategories

4. 实现商品接口 #

4.1 schema.js #

schema.js

const graphql = require('graphql');
const { GraphQLObjectType,
    GraphQLString,
    GraphQLSchema,
    GraphQLList,
} = graphql;
const categories = [
    { id: '1', name: '图书' },
    { id: '2', name: '数码' },
    { id: '3', name: '食品' }
]
+const products = [
+    { id: '1', name: '红楼梦', category: '1' },
+    { id: '2', name: '西游记', category: '1' },
+    { id: '3', name: '水浒传', category: '1' },
+    { id: '4', name: '三国演义', category: '1' },
+    { id: '2', name: 'iPhone', category: '2' },
+    { id: '3', name: '', category: '3' }
+]
//定义用户自定义类型
//类型的每个字段都必须是已定义的且最终都是 GraphQL 中定义的类型。
const Category = new GraphQLObjectType({
    name: 'Category',
    fields: () => (
        {
            id: { type: GraphQLString },
            name: { type: GraphQLString },
+            products: {
+                type: new GraphQLList(Product),
+                resolve(parent) {
+                    return products.filter(item => item.category === parent.id);
+                }
+            }
        }
    )
});
+const Product = new GraphQLObjectType({
+    name: 'Product',
+    fields: () => (
+        {
+            id: { type: GraphQLString },
+            name: { type: GraphQLString },
+            category: {
+                type: Category,
+                resolve(parent) {
+                    return categories.find(item => item.id === parent.category);
+                }
+            }
+        }
+    )
+});

const RootQuery = new GraphQLObjectType({
    name: 'RootQuery',
    fields: {
        getCategory: {
            type: Category,
            args: {
                id: {
                    type: GraphQLString
                }
            },
            resolve(parent, args) {
                return categories.find(item => item.id === args.id);
            }
        },
        getCategories: {
            type: new GraphQLList(Category),
            args: {

            },
            resolve(parent, args) {
                return categories;
            }
        },
+        getProduct: {
+            type: Product,
+            args: {
+                id: {
+                    type: GraphQLString
+                }
+            },
+            resolve(parent, args) {
+                return products.find(item => item.id === args.id);
+            }
+        },
+        getProducts: {
+            type: new GraphQLList(Product),
+            args: {},
+            resolve(parent, args) {
+                return categories;
+            }
+        }
    }
});
//定义 Schema,每一个 Schema 中允许出现三种根类型:query,mutation,subscription,其中至少要有 query
module.exports = new GraphQLSchema({
    query: RootQuery
})

5. 添加商品 #

5.1 schema.js #

schema.js

const graphql = require('graphql');
const {
    GraphQLObjectType,
    GraphQLString,
    GraphQLSchema,
    GraphQLList,
    GraphQLNonNull
} = graphql;
const categories = [
    { id: '1', name: '图书' },
    { id: '2', name: '数码' },
    { id: '3', name: '食品' }
]
+const products = [
+    { id: '1', name: '红楼梦', category: '1' },
+    { id: '2', name: '西游记', category: '1' },
+    { id: '3', name: '水浒传', category: '1' },
+    { id: '4', name: '三国演义', category: '1' },
+    { id: '2', name: 'iPhone', category: '2' },
+    { id: '3', name: '', category: '3' }
+]
//定义用户自定义类型
//类型的每个字段都必须是已定义的且最终都是 GraphQL 中定义的类型。
const Category = new GraphQLObjectType({
    name: 'Category',
    fields: () => (+
        {
            id: { type: GraphQLString },
            name: { type: GraphQLString },
            products: {
                type: new GraphQLList(Product),
                resolve(parent) {
                    return products.filter(item => item.category === parent.id);
                }
            }
        }
    )
});
+const Product = new GraphQLObjectType({
+    name: 'Product',
+    fields: () => (
+        {
+            id: { type: GraphQLString },
+            name: { type: GraphQLString },
+            category: {
+                type: Category,
+                resolve(parent) {
+                    return categories.find(item => item.id === parent.category);
+                }
+            }
+        }
+    )
+});

const RootQuery = new GraphQLObjectType({
    name: 'RootQuery',
    fields: {
        getCategory: {
            type: Category,
            args: {
                id: {
                    type: GraphQLString
                }
            },
            resolve(parent, args) {
                return categories.find(item => item.id === args.id);
            }
        },
        getCategories: {
            type: new GraphQLList(Category),
            args: {

            },
            resolve(parent, args) {
                return categories;
            }
        },
        getProduct: {
            type: Product,
            args: {
                id: {
                    type: GraphQLString
                }
            },
            resolve(parent, args) {
                return products.find(item => item.id === args.id);
            }
        },
        getProducts: {
            type: new GraphQLList(Product),
            args: {

            },
            resolve(parent, args) {
                return categories;
            }
        }
    }
});
+const RootMutation = new GraphQLObjectType({
+    name: 'RootMutation',
+    fields: {
+        addCategory: {
+            type: Category,
+            args: {
+                name: { type: new GraphQLNonNull(GraphQLString) }
+            },
+            resolve(parent, args) {
+                args.id = categories.length + 1 + '';
+                categories.push(args);
+                return args;
+            }
+        },
+        addProduct: {
+            type: Product,
+            args: {
+                name: { type: new GraphQLNonNull(GraphQLString) },
+                category: { type: new GraphQLNonNull(GraphQLString) }
+            },
+            resolve(parent, args) {
+                args.id = products.length + 1 + '';
+                products.push(args);
+                return args;
+            }
+        }
    }
});
//定义 Schema,每一个 Schema 中允许出现三种根类型:query,mutation,subscription,其中至少要有 query
module.exports = new GraphQLSchema({
    query: RootQuery,
+    mutation: RootMutation
})

addProduct

6. 使用mongodb数据库 #

6.1 model.js #

const mongoose = require('mongoose');
const ObjectId = mongoose.Schema.Types.ObjectId;
const Schema = mongoose.Schema;
const conn = mongoose.createConnection(`mongodb://localhost/graphql`, {
    useNewUrlParser: true, useUnifiedTopology: true
});
conn.on('open', () => console.log('数据库连接成功'));
conn.on('error', (error) => console.log('数据库连接失败', error));

const CategorySchema = new Schema({
    name: String
});
const CategoryModel = conn.model('Category', CategorySchema);
const ProductSchema = new Schema({
    name: String,
    category: {
        type: ObjectId,
        ref: 'Category'
    }
});
const ProductModel = conn.model('Product', ProductSchema);
module.exports = {
    CategoryModel,
    ProductModel
}

6.2 schema.js #

schema.js

const graphql = require('graphql');
+const { CategoryModel, ProductModel } = require('./model');
const {
    GraphQLObjectType,
    GraphQLString,
    GraphQLSchema,
    GraphQLList,
    GraphQLNonNull
} = graphql;
const categories = [
    { id: '1', name: '图书' },
    { id: '2', name: '数码' },
    { id: '3', name: '食品' }
]
const products = [
    { id: '1', name: '红楼梦', category: '1' },
    { id: '2', name: '西游记', category: '1' },
    { id: '3', name: '水浒传', category: '1' },
    { id: '4', name: '三国演义', category: '1' },
    { id: '2', name: 'iPhone', category: '2' },
    { id: '3', name: '', category: '3' }
]
//定义用户自定义类型
//类型的每个字段都必须是已定义的且最终都是 GraphQL 中定义的类型。
const Category = new GraphQLObjectType({
    name: 'Category',
    fields: () => (
        {
            id: { type: GraphQLString },
            name: { type: GraphQLString },
            products: {
                type: new GraphQLList(Product),
                resolve(parent) {
                    //return products.filter(item => item.category === parent.id);
+                    return ProductModel.find({ category: parent.id });
                }
            }
        }
    )
});
const Product = new GraphQLObjectType({
    name: 'Product',
    fields: () => (
        {
            id: { type: GraphQLString },
            name: { type: GraphQLString },
            category: {
                type: Category,
                resolve(parent) {
                    //return categories.find(item => item.id === parent.category);
+                    return CategoryModel.findById(parent.category);
                }
            }
        }
    )
});

const RootQuery = new GraphQLObjectType({
    name: 'RootQuery',
    fields: {
        getCategory: {
            type: Category,
            args: {
                id: {
                    type: GraphQLString
                }
            },
            resolve(parent, args) {
                //return categories.find(item => item.id === args.id);
+                return CategoryModel.findById(args.id);
            }
        },
        getCategories: {
            type: new GraphQLList(Category),
            args: {},
            resolve(parent, args) {
                //return categories;
+                return CategoryModel.find();
            }
        },
        getProduct: {
            type: Product,
            args: {
                id: { type: GraphQLString }
            },
            resolve(parent, args) {
                //return products.find(item => item.id === args.id);
+                return ProductModel.findById(args.id);
            }
        },
        getProducts: {
            type: new GraphQLList(Product),
            args: {

            },
            resolve(parent, args) {
                //return categories;
+                return ProductModel.find();
            }
        }
    }
});
const RootMutation = new GraphQLObjectType({
    name: 'RootMutation',
    fields: {
        addCategory: {
            type: Category,
            args: {
                name: { type: new GraphQLNonNull(GraphQLString) }
            },
            resolve(parent, args) {
                /*
                  args.id = categories.length + 1 + '';
                  categories.push(args);
                  return args;
                */
+                return CategoryModel.create(args);
            }
        },
        addProduct: {
            type: Product,
            args: {
                name: { type: new GraphQLNonNull(GraphQLString) },
                category: { type: new GraphQLNonNull(GraphQLString) }
            },
            resolve(parent, args) {
                /* args.id = products.length + 1 + '';
                products.push(args);
                return args; */
+                return ProductModel.create(args);
            }
        }
    }
});
//定义 Schema,每一个 Schema 中允许出现三种根类型:query,mutation,subscription,其中至少要有 query
module.exports = new GraphQLSchema({
    query: RootQuery,
    mutation: RootMutation
})

6.3 操作步骤 #

mutation{
  addCategory(name:"书籍"){
    id,
    name
  }
}
{
  "data": {
    "addCategory": {
      "id": "5dcfb188fe2d74a3543392ab",
      "name": "书籍"
    }
  }
}
mutation{
  addCategory(name:"数码产品"){
    id,
    name
  }
}
{
  "data": {
    "addCategory": {
      "id": "5dcfb1bdfe2d74a3543392ad",
      "name": "数码产品"
    }
  }
}
mutation{
  addCategory(name:"食品"){
    id,
    name
  }
}
{
  "data": {
    "addCategory": {
      "id": "5dcfb1c5fe2d74a3543392ae",
      "name": "食品"
    }
  }
}


{
  getCategories {
    id
    name
  }
}

{
  "data": {
    "getCategories": [
      {
        "id": "5dcfb188fe2d74a3543392ab",
        "name": "书籍"
      },
      {
        "id": "5dcfb1bdfe2d74a3543392ad",
        "name": "数码产品"
      },
      {
        "id": "5dcfb1c5fe2d74a3543392ae",
        "name": "食品"
      }
    ]
  }
}


mutation {
  addProduct(name: "西游记", category: "5dcfb188fe2d74a3543392ab") {
    id
    name
  }
}

{
  "data": {
    "addProduct": {
      "id": "5dcfb341b2f03ea4906dd913",
      "name": "西游记"
    }
  }
}

mutation {
  addProduct(name: "红楼梦", category: "5dcfb188fe2d74a3543392ab") {
    id
    name
  }
}

{
  "data": {
    "addProduct": {
      "id": "5dcfb354b2f03ea4906dd914",
      "name": "红楼梦"
    }
  }
}

mutation {
  addProduct(name: "水浒传", category: "5dcfb188fe2d74a3543392ab") {
    id
    name
  }
}

{
  "data": {
    "addProduct": {
      "id": "5dcfb36cb2f03ea4906dd915",
      "name": "水浒传"
    }
  }
}

mutation {
  addProduct(name: "三国演义", category: "5dcfb188fe2d74a3543392ab") {
    id
    name
  }
}


{
  "data": {
    "addProduct": {
      "id": "5dcfb37bb2f03ea4906dd916",
      "name": "三国演义"
    }
  }
}

mutation {
  addProduct(name: "iPhone", category: "5dcfb1bdfe2d74a3543392ad") {
    id
    name
  }
}

{
  "data": {
    "addProduct": {
      "id": "5dcfb393b2f03ea4906dd917",
      "name": "iPhone"
    }
  }
}


mutation {
  addProduct(name: "面包", category: "5dcfb1c5fe2d74a3543392ae") {
    id
    name
  }
}

{
  "data": {
    "addProduct": {
      "id": "5dcfb3a7b2f03ea4906dd918",
      "name": "面包"
    }
  }
}


{
  getProducts {
    id
    name
  }
}

{
  "data": {
    "getProducts": [
      {
        "id": "5dcfb341b2f03ea4906dd913",
        "name": "西游记"
      },
      {
        "id": "5dcfb354b2f03ea4906dd914",
        "name": "红楼梦"
      },
      {
        "id": "5dcfb36cb2f03ea4906dd915",
        "name": "水浒传"
      },
      {
        "id": "5dcfb37bb2f03ea4906dd916",
        "name": "三国演义"
      },
      {
        "id": "5dcfb393b2f03ea4906dd917",
        "name": "iPhone"
      },
      {
        "id": "5dcfb3a7b2f03ea4906dd918",
        "name": "面包"
      }
    ]
  }
}

1.生成项目 #

graphqlfront

create-react-app client --typescript
cd client
cnpm start

2.安装依赖 #

  • get-started
cnpm install apollo-boost @apollo/react-hooks graphql --save
cnpm i bootstrap@3 --save
模块名 含义
apollo-boost Package containing everything you need to set up Apollo Client
@apollo/react-hooks React hooks based view layer integration
graphql Also parses your GraphQL queries

3.连接接口 #

3.1 src\index.tsx #

src\index.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import ApolloClient from 'apollo-boost';
import { gql } from "apollo-boost";
const client = new ApolloClient({
    uri: 'http://localhost:4000/graphql',
});

client.query({
    query: gql`
      query{
        getCategories {
          id
          name
        }
      }
    `
}).then(result => console.log(result));

4.实现前台功能 #

4.1 src\App.tsx #

import React from 'react';
import ReactDOM from 'react-dom';
import ApolloClient from 'apollo-boost';
import { ApolloProvider } from '@apollo/react-hooks';
import 'bootstrap/dist/css/bootstrap.css';
import App from './App';
const client = new ApolloClient({ uri: 'http://localhost:4000/graphql' });
ReactDOM.render(<ApolloProvider client={client}>
    <App />
</ApolloProvider>, document.getElementById('root'));

4.2 src\App.tsx #

src\App.tsx

import React, { useState } from 'react';
import { CATEGORIES_PRODUCTS } from './query';
import { useQuery } from '@apollo/react-hooks';
import AddProduct from './AddProduct';
import ProductList from './ProductList';
import ProductDetail from './ProductDetail';
import { Product } from './types';
function App() {
    const [product, setProduct] = useState<Product>();
    const { loading, error, data } = useQuery(CATEGORIES_PRODUCTS);
    if (loading) {
        return <p>加载中...</p>;
    }
    if (error) {
        return <p>加载错误</p>;
    }
    let { getCategories, getProducts } = data;
    return (
        <div className="container">
            <div className="row" >
                <div className="col-md-6" >
                    <div className="panel panel-default" style={{ padding: 20 }}>
                        <div className="panel-header">
                            <AddProduct getCategories={getCategories} />
                        </div>
                        <div className="text-center" style={{ height: '400px', overflow: 'scroll' }}>
                            <ProductList getProducts={getProducts} setProduct={setProduct} />
                        </div>
                    </div>
                </div>
                <div className="col-md-6" >
                    <div className="panel panel-default" style={{ padding: 20 }}>
                        <div className="text-center">
                            <ProductDetail product={product} />
                        </div>
                    </div>
                </div>
            </div>
        </div>
    )
}
export default App;

4.3 src\types.tsx #

src\types.tsx

export interface Category {
    id?: string;
    name?: string;
}
export interface Product {
    id?: string;
    name?: string;
    categoryId?: string;
    category?: Category;
}

4.4 src\query.tsx #

src\query.tsx

import { gql } from 'apollo-boost';
export const CATEGORIES_PRODUCTS = gql`
query{
    getCategories {
      id,
      name,
      products{
        id,
        name,
      }
    }
    getProducts {
      id
      name,
      category{
        id,
        name,
        products{
          id,
          name,
        }
      }
    }
}
`;
export const CATEGORIES = gql`
query{
    getCategories {
      id
      name
    }
}
`;
export const PRODUCTS = gql`
query{
    getProducts {
      id
      name,
      category{
        id,
        name
      }
    }
}
`;
export const ADD_PRODUCT = gql`
mutation($name:String!,$categoryId: String!){
  addProduct(name: $name,category:$categoryId) {
    id,
    name,
    category{
      id,
      name
    }
  }
}
`;

4.5 src\AddProduct.tsx #

src\AddProduct.tsx

import React, { useState } from 'react';
import { Category, Product } from './types';
import { PRODUCTS, ADD_PRODUCT } from './query';
import { useMutation } from '@apollo/react-hooks';
function AddProduct(props: any) {
    const [product, setProduct] = useState<Product>({ name: '', categoryId: props.getCategories[0].id });
    const [addProduct] = useMutation(ADD_PRODUCT);
    function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
        event.preventDefault();
        addProduct({ variables: product, refetchQueries: [{ query: PRODUCTS }] });
    }
    return (
        <form onSubmit={handleSubmit}>
            <div className="form-group">
                <label htmlFor="product_name">商品名称</label>
                <input onChange={(event: React.ChangeEvent<HTMLInputElement>) => setProduct({ ...product, name: event.target.value })}
                    className="form-control" id="product_name" placeholder="商品名称" />
            </div>
            <div className="form-group">
                <label htmlFor="categoryId">商品分类</label>
                <select onChange={(event: React.ChangeEvent<HTMLSelectElement>) => setProduct({ ...product, categoryId: event.target.value })}
                    className="form-control" id="categoryId">
                    <option>请选择分类</option>
                    {
                        props.getCategories.map((category: Category) => {
                            return (
                                <option key={category.id} value={category.id}>{category.name}</option>
                            )
                        })
                    }
                </select>
            </div>
            <div className="form-group">
                <input className="btn btn-primary" type="submit" />
            </div>
        </form>
    )
}
export default AddProduct;

4.6 ProductList.tsx #

src\ProductList.tsx

import React from 'react';
import { Product } from './types';
function ProductList(props: any) {
    return (
        <table className="table table-striped">
            <caption className="text-center">产品列表</caption>
            <thead>
                <tr className="active">
                    <td>名称</td><td>分类</td>
                </tr>
            </thead>
            <tbody>
                {
                    props.getProducts.map((product: Product, index: number) => (
                        <tr key={product.id} onClick={() => props.setProduct(product)}>
                            <td>{product.name}</td><td>{product.category!.name}</td>
                        </tr>
                    ))
                }
            </tbody>
        </table>
    )
}
export default ProductList;

4.7 ProductDetail.tsx #

src\ProductDetail.tsx

import React from 'react';
import { Product } from './types';
function ProductDetail(props: any) {
    console.log(props.product);

    if (!props.product)
        return null;
    return (
        <ul className="list-group">
            <li className="list-group-item">
                ID:{props.product.id}
            </li>
            <li className="list-group-item">
                名称:{props.product.name}
            </li>
            <li className="list-group-item">
                分类:{props.product.category.name}
            </li>
            <li className="list-group-item">
                此分类下所有产品:
                <ul className="list-group">
                    {
                        props.product.category.products.map((product: Product, index: number) => (
                            <li key={product.id} className="list-group-item">{product.name}</li>
                        ))
                    }
                </ul>
            </li>
        </ul>
    )
}
export default ProductDetail;

访问验证

请输入访问令牌

Token不正确,请重新输入