Next.js: Contoh apollo minimal

Dibuat pada 13 Des 2016  ·  60Komentar  ·  Sumber: vercel/next.js

Ternyata integrasi apollo jauh lebih mudah saat menggunakan apollo-client secara langsung daripada react-apollo.

Berikut kodenya: https://github.com/nmaro/apollo-next-example
Dan inilah versi yang sedang berjalan (setidaknya selama saya menjaga server graphql online): https://apollo-next-example-oslkzaynhp.now.sh

Detail yang relevan ada di sini:

apollo.js

import ApolloClient, {createNetworkInterface} from 'apollo-client'

export default new ApolloClient({
  networkInterface: createNetworkInterface({
    uri: GRAPHQL_URL
  })
})

lalu di halaman

import React from 'react'
import gql from 'graphql-tag'
import 'isomorphic-fetch'
import apollo from '../apollo'
import Link from 'next/link'

const query = gql`query {
  posts {
    _id
    title
  }
}`
export default class extends React.Component {
  static async getInitialProps({req}) {
    return await apollo.query({
      query,
    })
  }
  render() {
    ...
  }
}

Komentar yang paling membantu

Kita harus mendapatkan posting blog tentang Apollo + Next.js di blog Apollo!

Semua 60 komentar

Beberapa pengamatan: pendekatan ini tidak masuk jauh ke dalam komponen untuk memuat semua kueri graphql yang ditemuinya (sesuatu yang dapat Anda aktifkan sisi server dengan react-apollo).

Saya yakin ini agak bermasalah dengan next.js: Anda tidak benar-benar dimaksudkan untuk memuat data jauh di dalam hierarki komponen - jika Anda ingin itu terjadi baik di klien maupun di sisi server. Hanya ada satu titik untuk memuat data: di getInitialProps di komponen root. Saya tidak tahu apakah ini akan berubah di masa depan, tetapi jika tidak, maka kita harus

  1. arsitektur aplikasi kami sehingga kami memuat semua data yang relevan untuk halaman dari awal, atau
  2. sebagian data hanya ada di klien (yaitu semua yang tidak kami muat di getInitialProps), dengan strategi yang berbeda

Dalam kedua kasus, pendekatan di atas seharusnya baik untuk data yang dimuat di getInitialProps.

Dan jika beberapa pengembang inti menyukai ini, saya dapat membuat permintaan tarik dengan contoh.

Tentang getInitialProps hanya dipanggil di root, lihat https://github.com/zeit/next.js/issues/192. Akan senang untuk memiliki ide-ide Anda di sana.

@sedubois masalah apa yang Anda hadapi dengan react-apollo ?

@nmaro https://github.com/nmaro/apollo-next-example Anda kosong.

@amccloud lebih baik bertanya kepada @nmaro tentang itu (saya masih harus kembali ke kode).

Terima kasih @sedubois sekarang online (selalu lupa menjalankan push origin master alih-alih hanya push pertama kali).

Ups, saya menyebut orang yang salah. @nmaro masalah apa yang Anda miliki dengan reaksi-apollo?

Data dimuat di server, lalu segera setelah klien mulai memuat halaman itu kosong lagi. Saya kemudian melihat implementasi @sedubois ( https://github.com/RelateMind/relate ), dan berpikir itu sudah cukup rumit untuk bukti konsep yang cepat, jadi saya akhirnya mencoba dengan api tingkat rendah.

@stubailo karena Anda bertanya-tanya mengapa begitu sulit untuk mengintegrasikan apollo dengan next.js - sepertinya satu-satunya tempat Anda dapat mengambil data baik di klien maupun di server adalah di komponen root halaman di dalam fungsi async yang disebut getInitialProps. Saya pikir cara biasa untuk mengintegrasikan react-apollo hanya akan berguna di sisi klien.

Menarik - apakah ada integrasi data lain dengan Next.js? Sepertinya menggunakan Redux juga cukup sulit berdasarkan contoh yang saya lihat.

Sebagian besar sistem data modern memiliki semacam cache global (Redux, Apollo, Relay) jadi saya merasa perlu ada semacam fasilitas di Next untuk mengaktifkan ini.

Bagaimana kita bisa membuat Next.js bermain lebih baik dengan sistem data modern dengan cache global (Redux, Apollo, Relay)? Saya merasa ini harus menjadi prioritas besar untuk rilis berikutnya. @stubailo @rauchg

Sangat. Kami memiliki contoh Redux di wiki, kami perlu membuat lebih banyak lagi seperti itu :)

Itu bukan sesuatu yang harus kita lakukan berdasarkan rilis btw. Kami hanya dapat menulis tutorial wiki kapan saja.

Btw @nmaro contoh itu terlihat sangat rapi, terima kasih telah berkontribusi. Kita bisa menganggapnya sebagai dasar dan mengembangkannya

Oh, aneh - saya tidak menyadari masalah yang terlibat. @nmaro ada apa dengan reaksi-apollo yang membuat segalanya menjadi sulit? sepertinya Anda harus dapat mengikuti contoh redux hampir persis tetapi lakukan new ApolloClient di mana ini menggunakan createStore , dan gunakan ApolloProvider alih-alih Provider .

Saya ingin bekerja dengan seseorang untuk membuat contoh minimal. Ini adalah contoh "hello world" kami untuk React, akan sangat bagus jika memiliki port untuk Next.js: https://github.com/apollostack/frontpage-react-app

@stubailo Saya ingin bekerja dengan Anda dalam contoh minimal. Saya telah menggunakan microframework apollo universal, Saturnus, untuk beberapa proyek dan pada akhirnya ingin memindahkannya ke Next.js + Apollo :)

Bagus - ya hanya membuat sedikit modifikasi pada aplikasi halaman depan untuk membuatnya berjalan di next.js alih-alih create-react-app akan menjadi preferensi saya. maka kita bisa daftar di halaman rumah kita juga!

@stubailo

Masalah kecil adalah bahwa data dimuat dan dirender di server, hanya untuk diganti tanpa apa-apa saat memuat di klien - saya kira saya tidak tahu apollo dan selanjutnya cukup untuk memperbaikinya. Menggunakan apollo-client secara langsung, saya tidak memiliki masalah ini.

Yang lebih rumit untuk rendering server adalah jika Anda memiliki kueri yang lebih dalam di hierarki. React tidak memiliki cara untuk merender sesuatu secara asinkron, yaitu menunggu setiap komponen siap sebelum merendernya. Yang berarti kerangka kerja ssr harus

  1. menelusuri seluruh pohon komponen dua kali, sekali untuk memuat data, dan sekali untuk merendernya.
  2. berikan titik masuk async di root - ini adalah pendekatan next.js dengan getInitialProps

Sekarang pertanyaannya adalah apakah apollo memiliki cara untuk mendeteksi semua panggilan data yang akan diperlukan untuk membuat pohon komponen, dan melakukan ini semua dalam satu panggilan fungsi yang dapat diberikan ke getInitialProps.

@stubailo Apakah ada solusi untuk ini? ^

@nmaro @ads1018 pernahkah anda melihat getDataFromTree ? Seperti yang digunakan misalnya dalam contoh saya: https://github.com/RelateMind/relate/blob/master/hocs/apollo.js

BTW saya ingin tahu apakah semuanya dapat disederhanakan sekarang karena https://github.com/zeit/next.js/pull/301 digabungkan. Belum melihat ke dalam itu.

@sedubois saya memeriksanya terima kasih telah berbagi! Ya, saya membayangkan contoh Anda menggunakan react-apollo dapat disederhanakan dengan API terprogram baru (#301) yang baru saja digabungkan ke Master sehingga Anda tidak perlu membungkus semua komponen halaman dengan HOC Anda sendiri. Jika Anda membuat kemajuan dalam hal itu, beri tahu saya! Akan keren untuk mendapatkan contoh next.js di beranda apollo :)

NB @ads1018 https://github.com/zeit/next.js/pull/301 adalah tentang mengekstrak kode umum dengan CommonsChunkPlugin, bukan Programmatic API. Tapi ya API terprogram pasti akan membantu juga, menantikan untuk merilisnya.

Adakah yang beruntung mendapatkan react-apollo bekerja dengan rilis 2.0.0-beta.2 yang baru?

@sedubois @stubailo Saya mendorong upaya saya berikutnya + reaksi-apollo jika Anda ingin melihatnya. Anda dapat menemukannya di sini: https://github.com/ads1018/frontpage-next-app

Satu masalah yang saya hadapi saat ini adalah bahwa komponen hanya ditampilkan di sisi klien dan bukan di sisi server. Mungkin kita bisa menggunakan metode getDataFromTree react-apollo di dalam server.js? Atau mungkin di dalam kebiasaan kita sendiri <document> ? Setiap saran / permintaan tarik dipersilakan!

Akan senang untuk akhirnya memasukkan contoh halo dunia ini di dalam folder Contoh berikutnya dan halaman beranda Apollo.

Satu-satunya prasyarat untuk server-rendering data adalah bahwa data tersebut dikembalikan sebagai objek di getInitialProps , tidak perlu diganti.

Kena kau. Saya pikir ini agak sulit dengan react-apollo karena seperti yang ditunjukkan @nmaro :

pertanyaannya adalah apakah apollo memiliki cara untuk mendeteksi semua panggilan data yang akan diperlukan untuk membuat pohon komponen, dan melakukan ini semua dalam satu panggilan fungsi yang dapat diberikan ke getInitialProps.

Kena kau

@ads1018 Dari sedikit mengaduk-aduk, jika komponen tingkat atas diekspos di getInitialProps, komponen itu kemudian dapat dirender menjadi string menggunakan Apollo helper .

_document kemudian akan menjadi seperti:

export default class MyDocument extends Document {
  static async getInitialProps ({ app }) {
    const wrapped = React.createElement(ApolloProvider, { client }, app)
    const rendered = await renderToStringWithData(wrapped)
    return { html: rendered, initialState: client.store.getState() }
  }

  render () {

    return (
      <html>
        <Head>
          <title>My page</title>
        </Head>
        <body>
          <ApolloProvider client={client}>
            <Main />
          </ApolloProvider>
          <NextScript />
        </body>
      </html>
    )
  }
}

@rauchg Tampaknya perubahan sederhana untuk mengekspos app selain renderPage , tetapi apakah ada sesuatu yang saya abaikan?

@bs1180 ah brilian. Itulah yang saya cari. Mudah-mudahan, ini adalah perubahan sederhana untuk mengekspos app . Itu akan langsung membuat Next menjadi kerangka kerja yang ramah klien graphql.

@bs1180 Saya telah mengekspos app di dalam objek kembali renderPage . Apakah ini sesuai dengan apa yang Anda pikirkan?

@ads1018 Tidak cukup - dalam versi Anda render masih dipanggil, yang akan menjadi duplikasi yang tidak perlu jika renderToStringWithData akan dipanggil secara manual.

Saya melakukan lebih banyak pekerjaan pada ini dan hasil akhir saya hampir tidak secantik yang saya bayangkan, terutama karena aplikasi utama dirender sebagai anak dari komponen <Main /> (ke dalam div __next), yang berhembus jauhkan konteks apa pun agar tidak diturunkan ke aplikasi Anda dari atas. Jadi masih membutuhkan HOC untuk menambahkan konteks Apollo lagi.

@bs1180 saya mengerti. Apakah mungkin untuk merender <Main /> sebagai anak dari ApolloProvider sehingga kami dapat mewariskan konteksnya?

Saya tidak yakin apa yang Anda maksud, tapi saya pikir itu arah yang salah. SSR yang sempurna dapat dicapai hanya dengan HOC- inilah versi saya yang dibuat bersama sebagai titik awal:

export default (options = {}) => Component => class ApolloHOC extends React.Component {
  static async getInitialProps (ctx) {
    const user = process.browser ? getUserFromLocalStorage() : getUserFromCookie(ctx.req)
    const jwt = process.browser ? null : getJwtFromCookie(ctx.req)

    if (options.secure && !user) {
      return null // skip graphql queries completely if auth will fail
    }

    const client = initClient(jwt)
    const store = initStore(client)

   // This inserts the context so our queries will work properly during the getDataFromTree call,
   //  as well as ensuring that any components which are expecting the url work properly 
    const app = React.createElement(ApolloProvider, { client, store },
      React.createElement(Component, { url: { query: ctx.query }}))

 // this is the most important bit :)
    await getDataFromTree(app)

    const initialState = {[client.reduxRootKey]: {
      data: client.store.getState()[client.reduxRootKey].data
    }}

    return { initialState, user }
  }

  constructor (props) {
    super(props)
    this.client = initClient()
    this.store = initStore(this.client, this.props.initialState)
  }

  render () {
    return (
      <ApolloProvider client={this.client} store={this.store}>
          <Component url={this.props.url} />
      </ApolloProvider>
    ) 
  }
}

initClient dan initStore dimodelkan pada contoh redux. Setiap halaman kemudian terlihat seperti ini:

import ApolloHOC from '../hoc'
import { graphql } from 'react-apollo'

export default ApolloHOC({ secure: false })(() => <b>Hello world</b>)

Semoga bermanfaat - Saya ingin tahu apakah ada cara lain untuk diselidiki, atau sesuatu yang saya abaikan.

@bs1180 Keren, ini sangat bermanfaat terima kasih telah berbagi.

Apakah ada hal lain yang dapat kami lakukan untuk merender halaman dengan data graphql di dalam _document.js ? Akan lebih baik jika kita bisa melewati HOC itu bersama-sama seperti yang Anda usulkan pada awalnya.

Saya rasa tidak - dari apa yang saya lihat, render sisi klien akan menghapus apa pun yang diteruskan pada konteks (baik itu klien Apollo, toko Redux standar, tema, dll.) dari _document.js kustom. Meskipun beberapa logika Apollo SSR dapat dipindahkan ke sana, semacam komponen HOC/wrapper masih diperlukan untuk menambahkan objek yang diperlukan kembali ke konteksnya.
Seseorang dengan pengetahuan yang lebih baik tentang next.js internal mungkin memiliki ide yang lebih baik.

Nah jika Anda berhasil mendapatkan contoh kerja, akan senang untuk memeriksanya. Saya masih berjuang agar ini berhasil.

Saya punya contoh kerja React Apollo dan Next Saya harap banyak dari Anda yang merasa berguna. Anda dapat memeriksanya di sini: https://github.com/ads1018/next-apollo-example (Saya juga telah menerapkan demo menggunakan Now.)

Saya akhirnya menggunakan HOC di dalam halaman saya yang disebut withData() yang membungkus halaman dengan ApolloProvider . Saya awalnya dimatikan dengan menggunakan penyedia pada basis per halaman sebagai lawan sekali di dalam satu file, tetapi saya diyakinkan oleh beberapa orang yang sangat pintar bahwa itu lebih baik untuk keterbacaan dan skalabilitas. Saya benar-benar berpikir withData(MyComponent) terlihat cukup bagus dan memberikan konteks yang baik kepada pembaca (tidak ada permainan kata-kata) bahwa halaman tertentu mengambil data.

Terima kasih @bs1180 dan @rauchg telah mengarahkan saya ke arah yang benar. Jika Anda ingin menambahkan contoh with-apollo ke repo, beri tahu saya dan saya dapat membuat permintaan tarik.

Terima kasih @ads1018 😊 Dibandingkan dengan contoh saya https://Relate.now.sh , apakah contoh ini menyelesaikan masalah penggunaan Apollo dalam komponen yang sangat bersarang (menghindari kaskade getInitialProps)? Mungkin contoh harus menunjukkan itu sebagai titik sakit utama. Dan saya yakin menambahkan ini ke folder contoh akan sangat dihargai.

@sedubois Saya tidak dapat mereproduksi kesalahan yang Anda rujuk di # 192. Saya menggunakan Apollo di dalam komponen bersarang tanpa masalah. Jika Anda menarik contoh saya dan dapat mereproduksinya, apakah Anda akan memberi tahu saya?

Terima kasih @ads1018 , semuanya bekerja dengan baik dengan perbaikan di https://github.com/ads1018/next-apollo-example/issues/2 . Saya memperbarui contoh saya juga: https://github.com/RelateNow/relate

Kerja bagus, @ads1018 @sedubois! Saya telah mengikuti ini dan # 192, saya juga telah menyelidiki tampilan prefetching/async menggunakan Apollo dan vanilla React.

Pernahkah Anda memperhatikan atau mengantisipasi masalah kinerja apa pun dengan menjalankan getDataFromTree sebelum setiap halaman ditampilkan? Karena secara teknis, metode itu merender seluruh pohon secara rekursif , dan kemudian ketika getInitialProps kembali, React merender pohon lagi (walaupun dengan data dari cache).

Solusi yang sangat bagus 👍 Saya pikir rendering dua kali adalah satu-satunya pilihan untuk memastikan semua data anak di-cache, hanya ingin tahu apa pendapat kalian tentang kinerja.

Hai @estrattonbailey - Saya belum melihat masalah kinerja apa pun dan saya tidak mengantisipasi apa pun. Bahkan, itu sangat tajam! Adapun menjalankan getDataFromTree , saya telah membungkus panggilan metode itu di dalam kondisional yang memeriksa apakah kita berada di server sehingga hanya dipanggil ketika pengguna pertama kali memuat aplikasi dan dilewati pada semua perubahan rute berikutnya . Anda dapat bermain-main dengan demo jika Anda ingin melihat kinerjanya. Tolong beri tahu saya jika Anda memiliki umpan balik!

@ads1018 beberapa ide untuk contoh Anda:

  • sederhanakan initialState seperti ini
  • pisahkan middleware, simpan dan peredam dalam file seperti ini
  • sederhanakan isServer menjadi typeof window !== 'undefined' , jatuhkan !!ctx.req
  • ekstrak const IS_SERVER itu ke lib, tidak perlu menyebarkannya sebagai param

@ads1018 Senang mendengarnya! Demo kecil yang bagus.

Yang ingin saya tanyakan adalah: seberapa baik skala ini? Meskipun saya belum pernah menggunakan Next, seperti yang saya pahami, Next memanggil getInitialProps pada setiap transisi rute, jika tersedia pada komponen halaman yaitu pages/page.js . Pada aplikasi/situs web skala penuh dengan ratusan node dan banyak data masuk, saya membayangkan bahwa rendering dua kali pada setiap rute dapat berkontribusi pada beberapa latensi.

Proyek yang sedang saya kerjakan adalah situs editorial berskala besar, jadi saya berharap dapat melakukan benchmarking dari pendekatan yang berbeda, termasuk milik Anda. Ingin berdiskusi lebih lanjut di twitter jika Anda mau. Terima kasih atas pekerjaan Anda!

@estrattonbailey Gotcha. Saya membayangkan itu akan berskala sangat baik. Untuk pemuatan halaman awal, getInitialProps hanya akan dijalankan di server. Anda benar bahwa getInitialProps akan dieksekusi lagi pada klien tetapi tidak ada data yang diminta dua kali karena getDataFromTree dibungkus di dalam kondisi yang memeriksa apakah kita berada di server atau tidak.

Catatan tambahan - jika Anda khawatir tentang waktu pemuatan halaman awal karena banyak komponen dan data yang diminta pada halaman, Anda selalu dapat memberi tahu apollo untuk secara sengaja melewati kueri tertentu selama SSR dan menurunkannya ke klien dengan meneruskan ssr: false dalam opsi kueri apollo.

Saya akan terhubung dengan Anda di twitter jika Anda ingin berdiskusi lebih lanjut :)

Anda benar bahwa getInitialProps akan dieksekusi lagi pada klien tetapi tidak ada data yang akan diminta dua kali karena getDataFromTree dibungkus di dalam kondisi yang memeriksa apakah kita berada di server atau tidak.

Penting untuk diingat getInitialProps dieksekusi di sisi klien hanya saat transisi dengan <Link> , bukan setelah pemuatan awal

@ads1018 @estrattonbailey AFAIK memang masih ada 2 render sisi server pada pemuatan halaman pertama: getDataFromTree dieksekusi dan merender seluruh pohon secara internal, lalu render dipanggil lagi untuk membuat respons HTML. Jangan berpikir jika ada cara untuk menghindarinya, tapi saya rasa masih cukup baik berkat jaringan bolak-balik yang dihindari oleh SSR.

Saya kira kinerjanya maksimal ketika server GraphQL di-host di mesin yang sama dengan server Next.js, jadi Anda selalu dapat mencobanya jika Anda peduli dengan kinerja (pada titik ini, saya membuat prototipe aplikasi saya dengan Graphcool untuk backend, sedangkan Next.js digunakan dengan Now/Zeit World).

@sedubois @estrattonbailey Koreksi saya jika saya salah, tetapi kami masih hanya merender sekali . getDataFromTree tidak merender pohon, itu hanya mengembalikan janji yang diselesaikan ketika data siap di toko Klien Apollo kami. Pada saat janji diselesaikan, penyimpanan Klien Apollo kami akan sepenuhnya diinisialisasi dan kami dapat merender pohon secara opsional jika kami ingin meneruskan hasil yang dirangkai dalam respons permintaan HTTP tetapi kami tidak melakukannya dalam contoh saya.

getDataFromTree tidak merender pohon

@ads1018 AFAIK dan melihat kode Apollo , itu _melakukan_ membuat pohon secara rekursif (hanya untuk memicu pengambilan data Apollo). Jadi itulah 2 render sisi server pada pemuatan halaman pertama.

Tapi bagaimanapun, berkat demo Anda, sekarang kami memiliki integrasi yang dapat digunakan antara Apollo dan Next, dan pertanyaan yang tersisa tentang kinerja Apollo SSR tidak memiliki sesuatu yang spesifik untuk Next.js lagi saya pikir. Saya akan menyarankan untuk mengajukan pertanyaan tentang itu di sana.

@sedubois apa itu render? Saya akan menyebutnya berjalan dan menggoyang-goyangkan pohon. Tampaknya dioptimalkan dengan cukup baik – setState ditekan dan tidak ada rekonsiliasi ke DOM.

@ads1018 contoh yang bagus! Sepertinya sudah ditambahkan ke Wiki di sini juga, jadi masalah ini mungkin bisa ditutup?

cc @rauchg

Kita harus mendapatkan posting blog tentang Apollo + Next.js di blog Apollo!

@stubailo @ads1018 contohnya sangat bagus Untuk sesuatu yang lebih besar menggunakan prinsip Apollo yang sama, dapat memeriksa aplikasi saya: https://github.com/relatenow/relate

Terima kasih @helfer. Saya senang dengan bagaimana itu keluar. Saya merasa seperti menemukan cawan suci pengembangan aplikasi dengan Next.js + Apollo. Saya bermaksud menindaklanjuti dengan posting blog dalam upaya menyebarkan Injil tetapi belum sempat melakukannya. @stubailo Saya akan senang untuk berkolaborasi dalam sebuah karya di media publikasi Apollo :)

Teriakan besar untuk @sedubois karena membantu dengan contoh dan aplikasi manisnya . 😄.

@ads1018 akan senang untuk membuat Anda di blog. Saat Anda siap untuk mengobrol tentang hal itu, ping saya (thea) di Apollo Slack. :)

@helper Anda benar sekali. Saya harus melakukan pass masalah baru untuk melihat apakah masalah dapat ditutup

@stubailo @theadactyl ide yang luar biasa ❤️

Adakah yang tahu tentang masalah/PR yang harus diperhatikan - mengenai permintaan data dua kali sisi server? Hanya penasaran

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

renatorib picture renatorib  ·  3Komentar

kenji4569 picture kenji4569  ·  3Komentar

knipferrc picture knipferrc  ·  3Komentar

jesselee34 picture jesselee34  ·  3Komentar

flybayer picture flybayer  ·  3Komentar