下面是一个视频和一个GIF动画, 感觉一下基于Websocket, 经过GraphQL实现的即时聊天应用是个什么鬼.html
视频链接: https://v.qq.com/x/page/x0508...react
GIF动画webpack
graphql()
函数是一个给组件增长数据逻辑(查询, 修改, 删除)的一个高阶函数, 存在于react-apollo
模块中, 若是要使用它, 须要把它import
进来. git该函数接受一个React组件, 同时返回一个通过修改(
增长数据逻辑
)的React组件. 属于设计模式中的装饰器模式
, 在不修改原组件的状况下, 对组件增长额外的功能, 实现了「对修改关闭, 对扩展开放」
的软件工程原则.github
graphql 容器的基本形态以下:web
# 导入 graphql 函数 import { graphql } from 'react-apollo'; # 函数签名, 参数分别为GraphQL查询(经过gql 模板标签进行构造, 一个可选的配置对象, 以及一个被包装的React组件) graphql(query, [config])(component)
graphql()
函数有两个参数数据库第一个参数为经过
gql
包裹的查询字符串, 如:编程const TODO_QUERY = gql`query Todo { todos: { id text } `}第二个参数为一个配置对象, 方括号表示其是可选的, 可省略
第三个参数为被包装的React组件.设计模式
graphql()
函数是 react-apollo
提供的最重要的一个函数. 用这个函数能够建立执行查询何更新的高阶组件. api
graphql()
函数能够这样用:
function TodoApp({ data: { todos } }) { return ( <ul> {todos.map(({ id, text }) => ( <li key={id}>{text}</li> ))} </ul> ); } export default graphql(gql` query TodoAppQuery { todos { id text } } `)(TodoApp);
也能够定义中间函数
# 中间函数 const withTodoAppQuery = graphql(gql`query { ... }`); # 传入React组件给这个中间函数 const TodoAppWithData = withTodoAppQuery(TodoApp); # 导出这个组件 export default TodoAppWithData;
graphql()
函数也能够做为装饰器使用:
@graphql(gql` query TodoAppQuery { todos { id text } } `) export default class TodoApp extends Component { render() { const { data: { todos } } = this.props; return ( <ul> {todos.map(({ id, text }) => ( <li key={id}>{text}</li> ))} </ul> ); } }
graphql()
函数的使用依赖于在React组件树的根外层再包装一个 <ApolloProvider/>
组件. <ApolloProvider />
在其属性上提供了一个 ApolloClient
实例用于访问数据.
经过 graphql()
函数加强的组件依据GraphQL的查询类型(Query, Mutation, Subscription)有不一样的行为.
options
该对象能够是一个纯对象
, 或者是一个函数
. 用于定制GraphQL查询的行为, 好比, 给一个Mutation传递输入对象参数:
options: ({ params }) => ({ variables: { text: '我是一个粉刷匠, 粉刷本领强4' }, }),
纯对象很简单, 形式为:
const config = { name: 'getTodos' }
options 函数, 接收一个组件属性做为参数, 形式为:
export default graphql(TODO_QUERY, { options: (props) => ({ }), })(MyComponent);
该属性用于定义一个映射函数. 传入组件自身的属性, 和经过 graphql()
函数添加的属性(Query为, props.data
, Mutation 为 props.mutate
), 这让咱们可以构造一个新的属性对象, 并把这个新的属性对象
传递给被graphql()
包装的组件.
config.props 的基本用途
config.props
可让咱们把复杂的函数调用抽离成单独的模块, 而且做为简单的属性传递给组件.
从UI组件解耦 GraphQL 逻辑, 让UI组件更简单, 大致上讲就是UI组件只负责UI的渲染, config.props
选项用于封装复杂的数据处理逻辑. UI组件和数据交互逻辑实现分离, 而且经过 config.props
关联.
这选项的做用是避免一个组件中有多个GraphQL查询, 或者Mutation名称上的冲突.
咱们方位GraphQL查询结果的数据一般是经过this.props.data
返回数据的, data
属性是经过graphql()
高阶函数注入到咱们的组件属性中的, 这个名字有时候会和咱们组件自己
的属性名称冲突, 为了解决冲突问题, 能够在graphql()
函数第二个参数配置对象中设置一个 name
, 而后经过 this.props.${name}
来访问这个属性.
export default compose( graphql(gql`mutation (...) { ... }`, { name: 'createTodo' }), graphql(gql`mutation (...) { ... }`, { name: 'updateTodo' }), graphql(gql`mutation (...) { ... }`, { name: 'deleteTodo' }), )(MyComponent); function MyComponent(props) { console.log(props.createTodo); console.log(props.updateTodo); console.log(props.deleteTodo); return null;
这样, 咱们就能够经过 this.props.createTodo
, this.props.updateTodo
,this.props.deleteTodo
来访问咱们须要的数据了.
设置
config.withRef
为 true 时, 能够经过graphql()
返回的高阶组件上调用getWrappedInstance
方法获取被包装组件的实例. 一般咱们须要访问被包装组件
的属性和方法是须要把这个选项设置为true
, 默认为false
# 建立一个UI组件 class HelloWorld extends Component { saySomething() { console.log('Hello, world!'); } render() { } } # 添加数据逻辑, 编程一个支持GraphQL的组件, 咱们简称GraphQL组件 const HelloWorldWithData = graphql( gql`{ ... }`, { withRef: true }, )(HelloWorld); # 使用 GraphQL 组件 # 经过该组件的ref属性, 咱们可以访问到原始的 HelloWorld 组件实例 class Container extends Component { render() { return ( <HelloWorldWithData ref={component => { assert(component.getWrappedInstance() instanceof HelloWorld); component.saySomething(); }} /> ); } }
组件别名, 主要是给 React Devtools 使用, 用于区分多个不一样的高阶组件
export default compose( graphql(gql`{ ... }`, { alias: 'withCurrentUser' }), graphql(gql`{ ... }`, { alias: 'withList' }), )(MyComponent);
看一下别名的效果, 咱们实际的组件名称为FeedbackList
, 经过graphql()
高阶组件包装后的组件名称为Apollo(FeedbackList)
, 若是组件很少的状况下, 咱们很好分辨不一样的组件, 若是一个单页应用中使用了大量的graphql()
高阶组件, 这样的名字容易引其混乱, 所以咱们经过alias
可以避免名称上的混乱, 让组件更容易识别. 下面咱们看一下代码:
import React, { Component } from 'react' import PropTypes from 'prop-types' import { graphql, gql, withApollo, compose } from 'react-apollo' // 查询文本 import QUERY_FEEDBACKS from './graphql/ListFeedback.graphql' import SUBSCRIPTION_NEW_FEEDBACKS from './graphql/SubscribeAddFeedback.graphql' // 反馈列表组件 class FeedbackList extends Component { // constructor(props) { // super(props) // } componentWillMount() { this.props.subscribeToNewFeedback(); } render() { if(this.props.data.loading == true) { return <div>Loading...</div> } else { return ( <ul>{ this.props.data.feedbacks.map((item, index) => { console.log(item.id) return ( <div key={item.id}>{item.text}</div> ) }) }</ul> ) } } } // 属性验证 FeedbackList.propTypes = { subscribeToNewFeedback: PropTypes.func.isRequired } // 高阶组件 export default graphql(QUERY_FEEDBACKS, { // name: 'FeedbackList', options: ({ params }) => ({ variables: { key: 'value' }, }), // 无别名时的效果 // alias: 'FeedbackListWithData' })(FeedbackList);
无别名
增长别名后
本文所描述的代码放在Github上, 能够Clone下来进行学习和测试, 代码中的数据是经过一个内存数组存储的, 服务器重启后数据丢失. 若是须要持久化, 能够改成使用数据库.
示例代码实现了GraphQL的订阅模式, 客户端经过 Websocket 创建到服务器的长链接. 能够一次做为「使用GraphQL实现即时聊天应用」的基础, 示例代码包含完整的服务器和客户端代码, 可经过下面两行命令启动服务器和客户端.
# 启动GraphQL服务器 # GraphQL服务器, 提供GraphQL查询接口: http://localhost:7001/api # 订阅服务器, 订阅功能: http://localhost:7003/feedback yarn server # 启动 webpack dev server , 提供Web界面: http://localhost:7001 yarn client
GraphiQL 查询工具, 能够经过 http://localhost:7001/graphiql
访问. 启动服务器和客户端后, 能够经过在 GraphiQL 工具中执行以下的查询看到效果:
查询
mutation AddFeedback($data: FeedbackInput!) { addFeedback(data: $data) { id text } }
变量
{ "data": { "text": "我是一个粉刷匠, 粉刷本领强" } }
这个链接是订阅
http://localhost:7001/graphiq...
这个链接是添加一条反馈(Feedback)记录, 以及对应的变量. 执行此Mutation, 会在客户端和订阅窗口看到数据的实时更新.
http://localhost:7001/graphiq...
http://dev.apollodata.com/rea...
https://github.com/apollograp...
https://github.com/apollograp...
http://dev.apollodata.com/rea...
https://webpack.js.org/config...