iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 10
1
Modern Web

Next.js + 各種套件組合系列 第 10

Next.js & ApolloData(一) 基本設定

介紹
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


上一篇
Next.js & Recompose
下一篇
Next.js & ApolloData(二) 基本設定
系列文
Next.js + 各種套件組合30

尚未有邦友留言

立即登入留言