手把手搭建GraphQL & Relay (二)

1,443 阅读3分钟

经过前一章节的基础介绍,大家对graphql已经有了基本的认识,也可以搭建一个简易的graphql server。那么在react前端项目中,怎么去连接graphql server和请求到数据呢?

Relay是什么?

在react应用中,relay提供了一套请求和管理graphql数据的方案。它是一套基于GraphQL和React的框架,将这两者结合,在原来React组件的基础上,进一步将请求封装进组件。

下面将一步步介绍在react项目中怎么使用relay。

基础搭建

使用create-react-app快速搭建一个react项目

npx create-react-app relay-test
yarn add --dev customize-cra react-app-rewired

搭建Relay

安装react-relay

yarn add react-relay

安装babel解析relay的插件babel-plugin-relay,同时配置.babelrc

relay插件需要在其他babel插件或者babel preset之前运行,保证graphql语法能够正确的转译

yarn add --dev babel-plugin-relay graphql

// .babelrc
{
  "plugins": [
    ["relay", {
      "schema": "schema.graphql",
      "artifactDirectory": "./src/__generated__"
    }]
  ]
}

安装relay解析 relay-compiler,同时在package.json中添加relay脚本

yarn relay之后,会在src目录下自动生成__generated__目录

yarn add --dev relay-compiler

// package.json
"scripts": {
    "relay": "relay-compiler --src ./src --schema ./schema.graphql --artifactDirectory ./src/__generated__",
    "start": "yarn relay && react-app-rewired start"
}

在根目录创建文件config-overrides.js, 覆盖webpack相关配置。

注意:devServer的配置中,我们将/graphql请求代理到http://localhost:4000/,也就是我们在第一章节搭建的graphql server,之后可以通过/graphql请求拿到数据。

const {
  override,
  addBabelPlugins,
  addWebpackAlias,
  addDecoratorsLegacy,
  fixBabelImports,
  disableEsLint
} = require('customize-cra');
const path = require('path');

module.exports = {
  webpack: override(
    // 添加babel插件relay
    ...addBabelPlugins(['relay', { artifactDirectory: './src/__generated__' }]),
    // 添加alias
    addWebpackAlias({
      'src': path.resolve(__dirname, 'src')
    }),
    // 项目中使用antd,需要先安装antd和babel-plugin-import
    fixBabelImports('import', {
      libraryName: 'antd',
      libraryDirectory: 'es',
      style: 'css'
    })
  ),
  devServer (configFunction) {
    return (proxy, allowedHost) => {
      const config = configFunction({
        '/graphql': {
          target: 'http://localhost:4000/',
          changeOrigin: true
        }
      }, allowedHost)
      return config
    }
  }
}

业务场景

基于graqhql server的用户列表数据,我们先尝试用graphql请求用户列表users,在前端显示出一个用户的表格。 目录结构如下

QQ20210627-181110.png

schema

在根目录下创建schema.graphql,定义客户端可用的query和mutation。

注意:与server端的schema保持一致

type Query {
  users: [User]!
  user (id: ID!): User!
}

type Mutation {
  addUser (id: ID!, name: String!, email: String!, age: Int): User
  updateUser (id: ID!, name: String, email: String, age: Int): User
  deleteUser(id: ID!): User!
}

type User {
  id: ID!
  key: Int!
  name: String!
  email: String!
  age: Int!
}

QueryRenderer

Relay框架提供了QueryRenderer这样一个高阶组件(HOC)来封装React组件和GraphQL请求。这个组件接受四个Props:environment、query、variables以及render。environment需要配置网络请求和Store;query接受的便是GraphQL请求;variables接受GraphQL请求中需要的变量,最后render用来定义组件的渲染。引用出处>>

注意: 在relay中,组件定义的Operation name必须是以文件名开头,以Query或Mutation结尾。比如src/AppRoute.js中定义的query的Operation name是AppRouteQuery

// src/AppRoute.js
import React from 'react';
import { graphql, QueryRenderer } from 'react-relay';
import Home from './pages/home';
import environment from './lib/environment';

const AuthRoute = () => {
  return <QueryRenderer 
    environment={environment}
    query={graphql`
      query AppRouteQuery {
        users {
          key
          name
          age
          email
        }
      }
    `}
    render={({error, props}) => {
      if (props) {
        return <Home data={props.users}/>
      }
    }}
  />
}

export default AuthRoute;

environment用来配置网络请求和Store。这里的Store是RecordStore。Relay会为每一个请求节点记录一个id,在store中存为一个record。

// src/lib/environment.js
import {
  Environment,
  Network,
  RecordSource,
  Store
} from 'relay-runtime';

const modernEnvironment = new Environment({
  network: Network.create(async (operation, variables) => {
    const response = await fetch('/graphql', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        query: operation.text,
        variables,
      })
    })
    return response.json();
  }),
  store: new Store(new RecordSource()) 
})

export default modernEnvironment;

Home表格渲染界面,用antd Table组件渲染。

// src/pages/home/index
import {Table} from 'antd';

export default function Home (props) {
  const columns = [{
    title: 'name',
    dataIndex: 'name',
    key: 'name'
  }, {
    title: 'age',
    dataIndex: 'age',
    key: 'age'
  }, {
    title: 'email',
    dataIndex: 'email',
    key: 'email'
  }]
  return <Table dataSource={props.data} columns={columns} />;
}

项目启动

yarn start 执行后,先会对src中的relay进行解析,生成src/__generated__目录,里面存放解析好的.graphql.js文件,然后启动客户端服务,打开localhost:3000,可以看到用户列表已经请求到并渲染出来。

QQ20210627-184507.png

QQ20210627-184400.png

至此,大功告成!

最后

FragmentMutation的relay用法,暂未做一一演示。有兴趣的童靴可以继续研究下。

relay文档