介紹
GraphQL 正紅,ApolloData 在 GraphQL 的包裝上非常用心,提供了 Cache 還有一些網路層面架構的分析包裝與語法更簡便架構更清晰的功能,讓使用GraphQL 前後端都非常的方便 官方也提供了一個 Next.js版本 提供快速入門參考
如果有興趣也可以參考 Polo 的另外一個比較詳細的介紹 ApolloData 鐵人賽文章
https://ithelp.ithome.com.tw/articles/10190943
先來看看在範例中會用到的套件除了Next.js基本的會再額外再安裝以下套件
"apollo-client"
"graphql"
"graphql-tag"
"isomorphic-fetch"
"react-apollo"
一般在流程建立上會事先設定 ApolloClient 放入 ApolloProvider 的 client 參數再包在TopLevel Wrap ,這樣底下的元件就可以直接使用這個設定檔了,在純 SPA 的設定上比較單純但在 Next.js 設定上有分 SSR 所以來看一下程式碼,因為程式碼較多所以說明就直接備注在程式碼
ApolloClient
import { ApolloClient } from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import fetch from 'isomorphic-fetch'
let apolloClient = null
// 如果是後端就打一個 fetch 功能補給他
if (!process.browser) {
global.fetch = fetch
}
function create (initialState) {
return new ApolloClient({
connectToDevTools: process.browser,
ssrMode: !process.browser, // 判斷前端還是後端再看要不要把ssrMode打開
link: new HttpLink({
uri: 'https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn', // 必須是絕對路徑
opts: {
credentials: 'same-origin' // Additional fetch() options like `credentials` or `headers`
}
}),
cache: new InMemoryCache().restore(initialState || {}),
})
}
export default function initApollo (initialState) {
//後端的話在Create專屬後端的 ApolloData設定
if (!process.browser) {
return create(initialState)
}
//前端的話在Create專屬前端的 ApolloData設定
if (!apolloClient) {
apolloClient = create(initialState)
}
return apolloClient
}
設定 ApolloData Provider
import React from 'react'
import PropTypes from 'prop-types'
import { ApolloProvider, getDataFromTree } from 'react-apollo'
import Head from 'next/head'
import initApollo from './initApollo'
// 為了方便除錯在dev顯示元件名稱用
function getComponentDisplayName (Component) {
return Component.displayName || Component.name || 'Unknown'
}
export default ComposedComponent => {
return class WithData extends React.Component {
static displayName = `WithData(${getComponentDisplayName(ComposedComponent)})`
static propTypes = {
serverState: PropTypes.object.isRequired
}
//SSR的地方 getInitialProps
static async getInitialProps (ctx) {
let serverState = {}
let composedInitialProps = {}
if (ComposedComponent.getInitialProps) {
composedInitialProps = await ComposedComponent.getInitialProps(ctx)
}
//判斷是否是後端部分
if (!process.browser) {
const apollo = initApollo()
const url = {query: ctx.query, pathname: ctx.pathname}
//要同步 ApolloData 的 cache Dom所以調用前端的 getDataFromTree 使用 promise來 得到資料
try {
await getDataFromTree(
<ApolloProvider client={apollo}>
<ComposedComponent url={url} {...composedInitialProps} />
</ApolloProvider>
)
} catch (error) {
//錯誤處理
}
//next提供的方法清除一些生命週期
Head.rewind()
// 把ApolloData的資料塞回去
serverState = {
apollo: {
data: apollo.cache.extract()
}
}
}
//在 getInitialProps 的 return 就是會傳到 constructor 當 props
return {
serverState,
...composedInitialProps
}
}
// 正常的SPA模式
constructor (props) {
super(props)
this.apollo = initApollo(this.props.serverState.apollo.data)
}
render () {
return (
<ApolloProvider client={this.apollo}>
<ComposedComponent {...this.props} />
</ApolloProvider>
)
}
}
}
總結
在 Next.js中使用 ApolloData 設定檔中,要注意的依然是 SSR 的部分,在isomorphic-fetch有些要補在 SSR 後端的 fetch 這類的設定要記得不然很容易漏掉,另外 SSR 的另外一個重點就是他使用前端的 getDataFromTree method 去調用資料 在後端建立ApolloData節點,以上是 Next.js 在ApolloData建置時候注意的地方
官方 Next.js ApolloData範例
https://github.com/zeit/next.js/tree/master/examples/with-apollo