Skip to content

5. Exemplo [nuxt-02]: páginas do servidor e do cliente

Neste projeto, mostramos:

  • que a página construída pelo cliente pode ser visualmente diferente daquela recebida do servidor. Temos, assim, uma mudança rápida de página, percetível pelo utilizador, o que prejudica a ergonomia da aplicação. Trata-se, portanto, de uma opção a evitar;
  • uma solução para que a página do cliente recrie a mesma página que a enviada pelo servidor;

O projeto [nuxt-02] é obtido inicialmente através da cópia do projeto [nuxt-01].

Image

É adicionada ao projeto uma pasta [store], bem como duas novas páginas. Voltaremos a este assunto.

5.1. A página [index]

5.1.1. O código da página

O código da página [index] passa a ser o seguinte:


<!-- página principal -->
<template>
  <Layout :left="true" :right="true">
    <!-- navegação -->
    <Navigation slot="left" />
    <!-- mensagem-->
    <b-alert slot="right" show variant="warning"> Home - value= {{ value }} </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-undef */
/* eslint-disable no-console */
/* eslint-disable nuxt/no-env-in-hooks */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'Home',
  // componentes utilizados
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // ciclo de vida
  beforeCreate() {
    // cliente e servidor
    console.log('[home beforeCreate]')
  },
  created() {
    // cliente e servidor
    console.log('[home created]')
    // apenas servidor
    if (process.server) {
      this.value = 10
    }
    // cliente e servidor
    console.log('value=', this.value)
  },
  beforeMount() {
    // apenas cliente
    console.log('[home beforeMount]')
  },
  mounted() {
    // apenas cliente
    console.log('[home mounted]')
  }
}
</script>

Comentários

  • linha 7: a página [index] irá apresentar o valor da sua propriedade [value] (linha 28);
  • linhas 36-45: é importante lembrar aqui que a função [created] é executada tanto no lado do servidor como no lado do cliente. Nas linhas 40-42, o servidor definirá o valor da propriedade [value] para 10. O cliente, por sua vez, não altera este valor. Queremos simplesmente saber se este valor é mantido pelo cliente. Vamos descobrir que não;

5.1.2. Execução

Alteramos o ficheiro [/nuxt.config.js] para executar o projeto [nuxt-02]:


...
  // diretório do código-fonte
  srcDir: 'nuxt-02',
  // router
  router: {
    // raiz dos URL da aplicação
    base: '/nuxt-02/'
  },
  // servidor
  server: {
    // porta de serviço, 3000 por predefinição
    port: 81,
    // endereços de rede em escuta, por predefinição localhost: 127.0.0.1
    // 0.0.0.0 = todos os endereços de rede do computador
    host: 'localhost'
  }
...

Executamos o projeto [1]:

Image

A página [index] é então apresentada como [2-3]. Apresenta o valor [10] durante alguns instantes e, em seguida, apresenta o valor [0]. O que aconteceu?

Passo 1

É o servidor que é executado primeiro. Este executa o código da página [index]:


export default {
  name: 'Home',
  // componentes utilizados
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // ciclo de vida
  beforeCreate() {
    // cliente e servidor
    console.log('[home beforeCreate]')
  },
  created() {
    // cliente e servidor
    console.log('[home created]')
    // apenas servidor
    if (process.server) {
      this.value = 10
    }
    // cliente e servidor
    console.log('value=', this.value)
  },
  beforeMount() {
    // apenas cliente
    console.log('[home beforeMount]')
  },
  mounted() {
    // apenas cliente
    console.log('[home mounted]')
  }
}
  • devido à linha 23, a propriedade [value] da linha 10 assume o valor 10;

É possível verificar isso consultando o código-fonte da página recebida pelo navegador (opção [code source] no navegador):


<!doctype html>
<html data-n-head-ssr>
<head>
  <title>Introduction à [nuxt.js]</title>
  <meta data-n-head="ssr" charset="utf-8">
  <meta data-n-head="ssr" name="viewport" content="width=device-width, initial-scale=1">
  <meta data-n-head="ssr" data-hid="description" name="description" content="ssr routing loading asyncdata middleware plugins store">
  <link data-n-head="ssr" rel="icon" type="image/x-icon" href="/favicon.ico">
  <base href="/nuxt-02/">
  <link rel="preload" href="/nuxt-02/_nuxt/runtime.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/commons.app.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/vendors.app.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/app.js" as="script">
  ....
</head>
<body>
  <div data-server-rendered="true" id="__nuxt">
    <div id="__layout">
      <div class="container">
        <div class="card">
          <div class="card-body">
            <div role="alert" aria-live="polite" aria-atomic="true" align="center" class="alert alert-success">
              <h4>[nuxt-02] : page serveur, page client</h4>
            </div> <div>
              <div class="row">
                <div class="col-2">
                  <ul class="nav flex-column">
                    <li class="nav-item">
                      <a href="/nuxt-02/" target="_self" class="nav-link active nuxt-link-active">
                        Home
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-02/page1" target="_self" class="nav-link">
                        Page 1
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-02/page2" target="_self" class="nav-link">
                        Page 2
                      </a>
                    </li>
                  </ul>
                </div> <div class="col-10">
                  <div role="alert" aria-live="polite" aria-atomic="true" class="alert alert-warning">
                    Home - value= 10
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
  <script>window.__NUXT__ = ....;</script>
  <script src="/nuxt-02/_nuxt/runtime.js" defer></script>
  <script src="/nuxt-02/_nuxt/commons.app.js" defer></script>
  <script src="/nuxt-02/_nuxt/vendors.app.js" defer></script>
  <script src="/nuxt-02/_nuxt/app.js" defer></script>
</body>
</html>
  • linha 46: na página recebida, [value] tinha o valor 10;

passo 2

Sabemos que, após a receção da página, os scripts das linhas 57-60 assumem o controlo e alteram o comportamento da página recebida, ou mesmo as informações apresentadas, como neste caso. Estes scripts constituem o cliente, que também executa o código da página [index], o mesmo código que o servidor:


export default {
  name: 'Home',
  // componentes utilizados
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // ciclo de vida
  beforeCreate() {
    // cliente e servidor
    console.log('[home beforeCreate]')
  },
  created() {
    // cliente e servidor
    console.log('[home created]')
    // apenas servidor
    if (process.server) {
      this.value = 10
    }
    // cliente e servidor
    console.log('value=', this.value)
  },
  beforeMount() {
    // apenas cliente
    console.log('[home beforeMount]')
  },
  mounted() {
    // apenas cliente
    console.log('[home mounted]')
  }
}
  • para compreender o que se passa, é necessário perceber que o cliente [nuxt] não executará as linhas 22 a 24 (process.server=false);
  • numa aplicação [vue] clássica, a propriedade [value] da linha 10 permanece em 0. É por isso que, assim que o cliente acede à página recebida, o valor apresentado passa a ser [0];

O valor gerado pelo servidor [nuxt] para a propriedade [value] não serviu para nada.

5.2. A página [page1]

5.2.1. A loja [Vuex]

Adicionámos uma pasta [store] ao projeto [nuxt-02]:

Image

A existência deste ficheiro faz com que o [nuxt] implemente automaticamente um armazenamento [Vuex]. É o ficheiro [index.js] que implementa este armazenamento. Aqui, o ficheiro [index.js] é o seguinte:


export const state = () => ({
  counter: 0
})

export const mutations = {
  increment(state, inc) {
    state.counter += inc
  }
}

O [nuxt] implementa um store [Vuex] a partir do conteúdo do [index.js]:

  • linhas 1-3: definição do estado [state] do store. Este estado é devolvido por uma função. Aqui, o estado tem apenas uma propriedade, o contador da linha 2. A função exportada deve chamar-se [state];
  • linhas 5-9: as operações possíveis no estado do store. Chamam-se [mutations]. Aqui, a modificação [increment] permite incrementar a propriedade [counter] numa quantidade [inc]. O objeto exportado deve chamar-se [mutations];

O [store] Vuex implementado por [nuxt] está disponível em vários locais. Nas vistas, está disponível na propriedade [this.$store].

5.2.2. O código da página

Tal como a página [index], a página [page1] irá apresentar um valor, o do contador do Vuex:


<!-- página 1 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navegação -->
    <Navigation slot="left" />
    <!-- mensagem-->
    <b-alert slot="right" show variant="primary"> Page 1 - value = {{ value }} </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-console */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'Page1',
  // componentes utilizados
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // ciclo de vida
  beforeCreate() {
    // cliente e servidor
    console.log('[home beforeCreate]')
  },
  created() {
    // cliente e servidor
    console.log('[home created]')
    // apenas servidor
    if (process.server) {
      this.$store.commit('increment', 25)
    }
    // cliente e servidor
    this.value = this.$store.state.counter
    console.log('value=', this.value)
  },
  beforeMount() {
    // apenas cliente
    console.log('[home beforeMount]')
  },
  mounted() {
    // apenas cliente
    console.log('[home mounted]')
  }
}
</script>

Comentários

  • linhas 38-40: o servidor irá incrementar o contador em 25;
  • linha 42: tanto o servidor como o cliente irão apresentar o valor do contador;
  • linha 7: o valor do contador é apresentado;

Ao ler este código, é preciso compreender duas coisas:

  • o código executado é o mesmo tanto para o servidor como para o cliente;
  • o objeto [this], por sua vez, não é o mesmo: existe uma versão [this] no lado do servidor e outra no lado do cliente, [client];

Queremos saber se o [this.$store] do servidor é o mesmo que o [this.$store] do cliente. Como é o servidor que é executado primeiro (ao iniciar a aplicação), isto equivale a perguntar: será que o [store] inicializado pelo servidor é transmitido ao cliente?

5.2.3. Execução

Executamos o projeto [nuxt-02] e digitamos manualmente [localhost:81/nuxt-02/page1] para que o servidor seja solicitado. Tal como no arranque da página [index]:

  • o servidor executa a página [page1.vue];
  • envia a página gerada para o navegador. Esta é apresentada;
  • os scripts do cliente incorporados na página enviada assumem o controlo e executam novamente a página [page1.vue];
  • a página apresentada é então alterada;

O resultado final é o seguinte:

Image

Desta vez, o valor apresentado é efetivamente aquele definido pelo servidor e, visualmente, não se observa a página a «oscilar» devido a uma alteração, por parte do cliente, do valor apresentado pelo servidor. O que aconteceu desta vez?

O servidor executou a página [page1] seguinte:


...

<script>
/* eslint-disable no-console */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'Page1',
  // componentes utilizados
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // ciclo de vida
  beforeCreate() {
    // cliente e servidor
    console.log('[page1 beforeCreate]')
  },
  created() {
    // cliente e servidor
    console.log('[page1 created]')
    // apenas servidor
    if (process.server) {
      this.$store.commit('increment', 25)
    }
    // cliente e servidor
    this.value = this.$store.state.counter
    console.log('value=', this.value)
  },
  beforeMount() {
    // apenas cliente
    console.log('[page1 beforeMount]')
  },
  mounted() {
    // apenas cliente
    console.log('[page1 mounted]')
  }
}
</script>
  • as linhas 30-32 foram executadas sem erros. O que significa que, também do lado do servidor, [this.$store] designa o registo [Vuex]. A linha 31 aumentou o contador do registo para 25;
  • feito isto, a página foi enviada ao cliente;

Se analisarmos a página recebida pelo cliente, encontramos os seguintes elementos:


<!doctype html>
<html data-n-head-ssr>
<head>
  <title>Introduction à [nuxt.js]</title>
  <meta data-n-head="ssr" charset="utf-8">
  <meta data-n-head="ssr" name="viewport" content="width=device-width, initial-scale=1">
  <meta data-n-head="ssr" data-hid="description" name="description" content="ssr routing loading asyncdata middleware plugins store">
  <link data-n-head="ssr" rel="icon" type="image/x-icon" href="/favicon.ico">
  <base href="/nuxt-02/">
  <link rel="preload" href="/nuxt-02/_nuxt/runtime.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/commons.app.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/vendors.app.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/app.js" as="script">
  ...
</head>
<body>
  <div data-server-rendered="true" id="__nuxt">
    <div id="__layout">
      <div class="container">
        <div class="card">
          <div class="card-body">
            <div role="alert" aria-live="polite" aria-atomic="true" align="center" class="alert alert-success">
              <h4>[nuxt-02] : page serveur, page client</h4>
            </div>
            <div>
              <div class="row">
                <div class="col-2">
                  <ul class="nav flex-column">
                    <li class="nav-item">
                      <a href="/nuxt-02/" target="_self" class="nav-link">
                        Home
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-02/page1" target="_self" class="nav-link active nuxt-link-active">
                        Page 1
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-02/page2" target="_self" class="nav-link">
                        Page 2
                      </a>
                    </li>
                  </ul>
                </div> <div class="col-10">
                  <div role="alert" aria-live="polite" aria-atomic="true" class="alert alert-primary">
                    Page 1 - value = 25
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
  <script>
  window.__NUXT__ = (function (a, b, c) {
      return {
        layout: "default", data: [{}], error: null, state: { counter: 25 }, serverRendered: true,
        logs: [
          { date: new Date(1574085336802), args: ["[home beforeCreate]"], type: a, level: b, tag: c },
          { date: new Date(1574085336839), args: ["[home created]"], type: a, level: b, tag: c },
          { date: new Date(1574085336869), args: ["value=", "25"], type: a, level: b, tag: c }
        ]
      }
    }("log", 2, ""));</script>
  <script src="/nuxt-02/_nuxt/runtime.js" defer></script>
  <script src="/nuxt-02/_nuxt/commons.app.js" defer></script>
  <script src="/nuxt-02/_nuxt/vendors.app.js" defer></script>
  <script src="/nuxt-02/_nuxt/app.js" defer></script>
</body>
</html>
  • linha 47: o valor enviado pelo servidor;
  • linha 60: verifica-se que o estado do armazenamento [Vuex] foi incorporado na página. Isto permitirá ao cliente, que será executado após a receção da página, reconstituir um novo armazenamento [Vuex] com 25 como valor inicial do contador;

Após a receção e a visualização da página recebida do servidor, o cliente assume o controlo e, por sua vez, executa a página [page1]:


...

<script>
/* eslint-disable no-console */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'Page1',
  // componentes utilizados
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // ciclo de vida
  beforeCreate() {
    // cliente e servidor
    console.log('[page1 beforeCreate]')
  },
  created() {
    // cliente e servidor
    console.log('[page1 created]')
    // apenas servidor
    if (process.server) {
      this.$store.commit('increment', 25)
    }
    // cliente e servidor
    this.value = this.$store.state.counter
    console.log('value=', this.value)
  },
  beforeMount() {
    // apenas cliente
    console.log('[page1 beforeMount]')
  },
  mounted() {
    // apenas cliente
    console.log('[page1 mounted]')
  }
}
</script>
  • linha 34: a propriedade [value] da linha 18 recebe, por sua vez, o valor 25 do contador;

O «store» de [nuxt] permite, assim, que o servidor transmita informações ao cliente durante o carregamento inicial da página, quando esta é solicitada ao servidor. Recorde-se que, uma vez obtida esta página, o servidor já não é solicitado e a aplicação funciona como uma aplicação [vue] clássica, no modo SAP.

5.3. A página [page2]

Na página [page2], mostramos outra forma de fazer com que:

  • o servidor inclua informações calculadas na página;
  • o cliente não as altere;

5.3.1. O código da página

O código da página [page2] evolui da seguinte forma:


<!-- página 2 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navegação -->
    <Navigation slot="left" />
    <!-- mensagem -->
    <b-alert slot="right" show variant="secondary"> Page 2 - value = {{ value }} </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-console */
/* eslint-disable nuxt/no-timing-in-fetch-data */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'Page2',
  // componentes utilizados
  components: {
    Layout,
    Navigation
  },
  asyncData(context) {
    // quem executa este código?
    console.log('asyncData, client=', process.client, 'serveur=', process.server)
    // apenas para o servidor
    if (process.server) {
      // retorna-se uma promessa
      return new Promise(function(resolve, reject) {
        // normalmente, aqui temos uma função assíncrona
        // simulamo-la com uma espera de um segundo
        setTimeout(() => {
          // este resultado será incluído nas propriedades de [data]
          resolve({ value: 87 })
          // registo
          console.log('asynData terminée')
        }, 1000)
      })
    }
  },
  // ciclo de vida
  beforeCreate() {
    // cliente e servidor
    console.log('[page2 beforeCreate]')
  },
  created() {
    // cliente e servidor
    console.log('[page2 created]')
  },
  beforeMount() {
    // apenas cliente
    console.log('[page2 beforeMount]')
  },
  mounted() {
    // apenas cliente
    console.log('[page2 mounted]')
  }
}
</script>
  • linha 7: a página apresenta o valor de uma propriedade denominada [value];
  • a propriedade [value] não existe como elemento de um objeto devolvido pela função [data]. Neste caso, essa função não existe. A propriedade [value] é criada dinamicamente pela linha 36;
  • linha 25: a função [asyncData] é uma função [nuxt]. Como o próprio nome indica, trata-se normalmente de uma função assíncrona. A sua função habitual é ir buscar dados externos. A função [nuxt] garante que a página não é enviada para o navegador do cliente antes de a função [asyncData] ter devolvido os seus dados assíncronos;
  • a função [asyncData] recebe como parâmetro o contexto [nuxt]. Este objeto é muito abrangente e dá acesso a muita informação sobre a aplicação [nuxt]. Iremos descobrir isso nas secções seguintes;
  • linha 31: implementamos a função [asyncData] com um [Promise] (ver documento |Introdução à linguagem ECMASCRIPT 6 através de exemplos|). O construtor desta classe aceita como parâmetro uma função assíncrona que:
    • indica sucesso ao devolver dados com a função [resolve]. O objeto devolvido por esta função é automaticamente incluído nas propriedades [data] da página;
    • indica um erro ao devolver um erro com a função [reject];
  • linha 34: simula-se uma função assíncrona com a função [setTimeout]. Esta função devolve o objeto [{ value: 87 }] (linha 36) ao fim de um segundo (linha 31) graças à função [resolve], que indica o sucesso da função [Promise]. O objeto devolvido pela função assíncrona é incluído automaticamente nas propriedades [data] da página. E é, portanto, esta propriedade que a linha 7 apresenta;
  • linha 27: vamos descobrir que a função [asyncData] é executada pelo servidor, mas não pelo cliente;
  • linha 29: a inicialização da propriedade [value] é feita pelo servidor;

Nota: o objeto [this] não é reconhecido na função [asyncData], uma vez que o objeto que encapsula o componente [vue] ainda não foi criado;

5.3.2. Execução

Executa-se o projeto [nuxt-02] e introduz-se manualmente [localhost:81/nuxt-02/page2] para que o servidor seja solicitado. Tal como no início, para a página [index]:

  • o servidor executa a página [page2.vue];
  • envia a página gerada para o navegador. Esta é apresentada;
  • os scripts do cliente incorporados na página enviada assumem o controlo e executam novamente a página [page2.vue];
  • a página apresentada é então alterada;

O resultado final é o seguinte:

Image

Desta vez, o valor apresentado é efetivamente aquele definido pelo servidor e, visualmente, não se observa a página a «oscilar» devido a uma alteração, por parte do cliente, do valor apresentado pelo servidor. O que aconteceu desta vez?

O servidor executou a página seguinte: [page2]:


<!-- página 2 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navegação -->
    <Navigation slot="left" />
    <!-- mensagem -->
    <b-alert slot="right" show variant="secondary"> Page 2 - value = {{ value }} </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-console */
/* eslint-disable nuxt/no-timing-in-fetch-data */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'Page2',
  // componentes utilizados
  components: {
    Layout,
    Navigation
  },
  asyncData(context) {
    // quem executa este código?
    console.log('asyncData, client=', process.client, 'serveur=', process.server)
    // apenas para o servidor
    if (process.server) {
      // retorna-se uma promessa
      return new Promise(function(resolve, reject) {
        // normalmente, aqui temos uma função assíncrona
        // simulamo-la com uma espera de um segundo
        setTimeout(() => {
          // este resultado será incluído nas propriedades de [data]
          resolve({ value: 87 })
          // registo
          console.log('asynData terminée')
        }, 1000)
      })
    }
  },
  // ciclo de vida
  beforeCreate() {
    // cliente e servidor
    console.log('[page2 beforeCreate]')
  },
  created() {
    // cliente e servidor
    console.log('[page2 created]')
  },
  beforeMount() {
    // apenas cliente
    console.log('[page2 beforeMount]')
  },
  mounted() {
    // apenas cliente
    console.log('[page2 mounted]')
  }
}
</script>

Foi a linha 36 que definiu o valor apresentado pela linha 7. Foi, portanto, isto que o navegador do cliente recebeu. Mais precisamente, recebe a seguinte página:


<!doctype html>
<html data-n-head-ssr>
<head>
  <title>Introduction à [nuxt.js]</title>
  <meta data-n-head="ssr" charset="utf-8">
  <meta data-n-head="ssr" name="viewport" content="width=device-width, initial-scale=1">
  <meta data-n-head="ssr" data-hid="description" name="description" content="ssr routing loading asyncdata middleware plugins store">
  <link data-n-head="ssr" rel="icon" type="image/x-icon" href="/favicon.ico">
  <base href="/nuxt-02/">
  <link rel="preload" href="/nuxt-02/_nuxt/runtime.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/commons.app.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/vendors.app.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/app.js" as="script">
  ...
</head>
<body>
  <div data-server-rendered="true" id="__nuxt">
    <div id="__layout">
      <div class="container">
        <div class="card">
          <div class="card-body">
            <div role="alert" aria-live="polite" aria-atomic="true" align="center" class="alert alert-success">
              <h4>[nuxt-02] : page serveur, page client</h4>
            </div>
            <div>
              <div class="row">
                <div class="col-2">
                  <ul class="nav flex-column">
                    <li class="nav-item">
                      <a href="/nuxt-02/" target="_self" class="nav-link">
                        Home
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-02/page1" target="_self" class="nav-link">
                        Page 1
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-02/page2" target="_self" class="nav-link active nuxt-link-active">
                        Page 2
                      </a>
                    </li>
                  </ul>
                </div>
                <div class="col-10">
                  <div role="alert" aria-live="polite" aria-atomic="true" class="alert alert-secondary">
                    Page 2 - value = 87
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
  <script>
  window.__NUXT__ = (function (a, b, c) {
      return {
        layout: "default", data: [{ value: 87 }], error: null, state: { counter: 0 }, serverRendered: true,
        logs: [
          { date: new Date(1574096608555), args: ["asyncData, client=", "false", "serveur=", "true"], type: a, level: b, tag: c },
          { date: new Date(1574096608575), args: ["[page2 beforeCreate]"], type: a, level: b, tag: c },
          { date: new Date(1574096608599), args: ["[page2 created]"], type: a, level: b, tag: c }
        ]
      }
    }("log", 2, ""));</script>
  <script src="/nuxt-02/_nuxt/runtime.js" defer></script>
  <script src="/nuxt-02/_nuxt/commons.app.js" defer></script>
  <script src="/nuxt-02/_nuxt/vendors.app.js" defer></script>
  <script src="/nuxt-02/_nuxt/app.js" defer></script>
</body>
</html>
  • linha 48: verifica-se que o valor na página recebida é 87;
  • linha 61: na resposta do servidor, vemos dois objetos: [data] e [state]:
    • [state] é o estado do store [Vuex]. Este foi instanciado a partir do conteúdo da pasta [store] da aplicação [nuxt-02];
    • O [data] contém as propriedades criadas pelo servidor através da função [asyncData]. Encontramos aqui a propriedade [value : 87] criada pelo servidor. Os scripts do cliente irão integrar esta propriedade nas da página [page2];

Voltemos ao código da página [page2]:


<!-- página 2 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navegação -->
    <Navigation slot="left" />
    <!-- mensagem -->
    <b-alert slot="right" show variant="secondary"> Page 2 - value = {{ value }} </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-console */
/* eslint-disable nuxt/no-timing-in-fetch-data */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'Page2',
  // componentes utilizados
  components: {
    Layout,
    Navigation
  },
  asyncData(context) {
    // quem executa este código?
    console.log('asyncData, client=', process.client, 'serveur=', process.server)
    // apenas para o servidor
    if (process.server) {
      // retorna-se uma promessa
      return new Promise(function(resolve, reject) {
        // normalmente, aqui temos uma função assíncrona
        // simulamo-la com uma espera de um segundo
        setTimeout(() => {
          // este resultado será incluído nas propriedades de [data]
          resolve({ value: 87 })
          // registo
          console.log('asynData terminée')
        }, 1000)
      })
    }
  },
  // ciclo de vida
  beforeCreate() {
    // cliente e servidor
    console.log('[page2 beforeCreate]')
  },
  created() {
    // cliente e servidor
    console.log('[page2 created]')
  },
  beforeMount() {
    // apenas cliente
    console.log('[page2 beforeMount]')
  },
  mounted() {
    // apenas cliente
    console.log('[page2 mounted]')
  }
}
</script>
  • a linha 7 utiliza a propriedade [value]. No entanto, a página não define nenhuma propriedade com o nome [value]. Contudo, os scripts do cliente criaram automaticamente esta propriedade graças ao objeto [data: [{ value: 87 }]] recebido do servidor;

Os registos mostram, além disso, que a função [asyncData] não foi executada pelo cliente:

Image

A função [asyncData] foi executada pelo servidor [1], mas não pelo cliente [2]. Além disso, é de salientar que as funções do ciclo de vida não são executadas pelo servidor antes do fim da função [asyncData]. É possível aumentar o tempo de espera na função [asyncData] para verificar isso.

5.4. A página [page3]

Adicionamos uma nova página [page3] à nossa aplicação:

Image

5.4.1. O componente [navigation]

O componente [vavigation] é alterado para permitir a navegação para a nova página:


<template>
  <!-- menu Bootstrap com três opções -->
  <b-nav vertical>
    <b-nav-item to="/" exact exact-active-class="active">
      Home
    </b-nav-item>
    <b-nav-item to="/page1" exact exact-active-class="active">
      Page 1
    </b-nav-item>
    <b-nav-item to="/page2" exact exact-active-class="active">
      Page 2
    </b-nav-item>
    <b-nav-item to="/page3" exact exact-active-class="active">
      Page 3
    </b-nav-item>
  </b-nav>
</template>

5.4.2. O código de [page3]

O código da página [page3] é o seguinte:


<!-- página 3 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navegação -->
    <Navigation slot="left" />
    <!-- mensagem -->
    <b-alert slot="right" show variant="secondary"> Page 3 - value = {{ value }} </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-console */
/* eslint-disable nuxt/no-timing-in-fetch-data */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'Page3',
  // componentes utilizados
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  fetch(context) {
    // quem executa este código?
    console.log('fetch, client=', process.client, 'serveur=', process.server)
    // apenas para o servidor
    if (process.server) {
      // retorna uma promessa
      return new Promise(function(resolve, reject) {
        // normalmente, aqui temos uma função assíncrona
        // simulamo-la com uma espera de um segundo
        setTimeout(() => {
          // sucesso
          resolve()
        }, 1000)
      }).then(() => {
        // altera-se o store
        context.store.commit('increment', 28)
      })
    }
  },
  // ciclo de vida
  beforeCreate() {
    // cliente e servidor
    console.log('[page3 beforeCreate]')
  },
  created() {
    // cliente e servidor
    this.value = this.$store.state.counter
    console.log('[page3 created], value=', this.value)
  },
  beforeMount() {
    // apenas cliente
    console.log('[page3 beforeMount]')
  },
  mounted() {
    // apenas cliente
    console.log('[page3 mounted]')
  }
}
</script>
  • linha 30: a função [fetch] tem um comportamento semelhante ao da função [asyncData]:
    • é executada antes das funções do ciclo de vida;
    • o objeto [this] não é reconhecido nesta função;
    • o seu funcionamento é assíncrono;
    • o ciclo de vida não começa enquanto a função assíncrona não tiver devolvido o seu resultado;
    • o resultado é aqui devolvido pelo método [then] da função [Promise], linha 43;
    • a função [fetch] recebe o parâmetro [context]. Este representa o contexto [nuxt] do momento;
  • linha 30: entre as suas numerosas propriedades, o objeto [context] possui uma propriedade [store] que representa o store [Vuex] da aplicação;
  • linha 41: artificialmente, sinaliza-se o sucesso do [Promise] ao fim de um segundo (ver documento |Introdução à linguagem ECMASCRIPT 6 através de exemplos|);
  • linha 45: o método [then] é então executado. Nele, incrementa-se o contador do [store];

5.4.3. Execução

Executa-se o projeto [nuxt-02] e digita-se manualmente [localhost:81/nuxt-02/page3] para que o servidor seja solicitado. Tal como no arranque para a página [index]:

  • o servidor executa a página [page3.vue];
  • envia a página gerada para o navegador. Esta é apresentada;
  • os scripts do cliente incorporados na página enviada assumem o controlo e executam novamente a página [page3.vue];
  • a página apresentada é então alterada;

O resultado final é o seguinte:

Image

O valor apresentado é, de facto, aquele definido pelo servidor e, visualmente, não se observa que a página «salte» devido a uma alteração, por parte do cliente, do valor apresentado pelo servidor. O que aconteceu desta vez?

O servidor executou a página seguinte, [page3]:


<!-- página 3 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navegação -->
    <Navigation slot="left" />
    <!-- mensagem -->
    <b-alert slot="right" show variant="secondary"> Page 3 - value = {{ value }} </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-console */
/* eslint-disable nuxt/no-timing-in-fetch-data */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'Page3',
  // componentes utilizados
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  fetch(context) {
    // quem executa este código?
    console.log('fetch, client=', process.client, 'serveur=', process.server)
    // apenas para o servidor
    if (process.server) {
      // retorna-se uma promessa
      return new Promise(function(resolve, reject) {
        // normalmente, aqui temos uma função assíncrona
        // simulamo-la com uma espera de um segundo
        setTimeout(() => {
          // sucesso
          resolve()
        }, 1000)
      }).then(() => {
        // altera-se o store
        context.store.commit('increment', 28)
        // registo
        console.log('fetch commit terminé')
      })
    }
  },
  // ciclo de vida
  beforeCreate() {
    // cliente e servidor
    console.log('[page3 beforeCreate]')
  },
  created() {
    // cliente e servidor
    this.value = this.$store.state.counter
    console.log('[page3 created], value=', this.value)
  },
  beforeMount() {
    // apenas cliente
    console.log('[page3 beforeMount]')
  },
  mounted() {
    // apenas cliente
    console.log('[page3 mounted]')
  }
}
</script>
  • linha 45: a função assíncrona [fetch] é a primeira das funções acima a ser executada. Recebe como parâmetro um objeto denominado [context], que corresponde ao contexto [nuxt] do momento. Entre as inúmeras propriedades deste objeto, a propriedade [context.store] representa o armazenamento [Vuex];
  • linha 45: na função assíncrona [fetch], o servidor define o contador do armazenamento temporário para 28;
  • linha 56: quando a função [created] é executada, a [nuxt] garante que a função assíncrona [fetch] tenha concluído o seu trabalho;
  • linha 58: o valor do contador da persiana é atribuído à propriedade [value] da linha 27;
  • linha 7: exibição do valor de [value], ou seja, do contador do store;

O navegador do cliente recebe a seguinte página:


<!doctype html>
<html data-n-head-ssr>
<head>
  <title>Introduction à [nuxt.js]</title>
  <meta data-n-head="ssr" charset="utf-8">
  <meta data-n-head="ssr" name="viewport" content="width=device-width, initial-scale=1">
  <meta data-n-head="ssr" data-hid="description" name="description" content="ssr routing loading asyncdata middleware plugins store">
  <link data-n-head="ssr" rel="icon" type="image/x-icon" href="/favicon.ico">
  <base href="/nuxt-02/">
  <link rel="preload" href="/nuxt-02/_nuxt/runtime.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/commons.app.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/vendors.app.js" as="script">
  <link rel="preload" href="/nuxt-02/_nuxt/app.js" as="script">
  ...
</head>
<body>
  <div data-server-rendered="true" id="__nuxt">
    <div id="__layout">
      <div class="container">
        <div class="card">
          <div class="card-body">
            <div role="alert" aria-live="polite" aria-atomic="true" align="center" class="alert alert-success">
              <h4>[nuxt-02] : page serveur, page client</h4>
            </div>
            <div>
              <div class="row">
                <div class="col-2">
                  <ul class="nav flex-column">
                    <li class="nav-item">
                      <a href="/nuxt-02/" target="_self" class="nav-link">
                        Home
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-02/page1" target="_self" class="nav-link">
                        Page 1
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-02/page2" target="_self" class="nav-link">
                        Page 2
                      </a>
                    </li>
                    <li class="nav-item">
                      <a href="/nuxt-02/page3" target="_self" class="nav-link active nuxt-link-active">
                        Page 3
                      </a>
                    </li>
                  </ul>
                </div> <div class="col-10">
                  <div role="alert" aria-live="polite" aria-atomic="true" class="alert alert-secondary">
                    Page 3 - value = 28
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
  <script>
  window.__NUXT__ = (function (a, b, c) {
      return {
        layout: "default", data: [{}], error: null, state: { counter: 28 }, serverRendered: true,
        logs: [
          { date: new Date(1574169916025), args: ["fetch, client=", "false", "serveur=", "true"], type: a, level: b, tag: c },
          { date: new Date(1574169917038), args: ["fetch commit terminé"], type: a, level: b, tag: c },
          { date: new Date(1574169917137), args: ["[page3 beforeCreate]"], type: a, level: b, tag: c },
          { date: new Date(1574169917167), args: ["[page3 created], value=", "28"], type: a, level: b, tag: c }
        ]
      }
    }("log", 2, ""));</script>
  <script src="/nuxt-02/_nuxt/runtime.js" defer></script>
  <script src="/nuxt-02/_nuxt/commons.app.js" defer></script>
  <script src="/nuxt-02/_nuxt/vendors.app.js" defer></script>
  <script src="/nuxt-02/_nuxt/app.js" defer></script>
</body>
</html>
  • linha 52: verifica-se que o valor na página recebida é 28;
  • linha 65: na resposta do servidor, verifica-se que o servidor enviou ao cliente o estado [state] do armazenamento [Vuex]. Graças a esta informação, os scripts do cliente poderão reconstituir um armazenamento [Vuex];

Os scripts do cliente, por sua vez, irão executar o código da página [page3]:


<!-- página 3 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navegação -->
    <Navigation slot="left" />
    <!-- mensagem -->
    <b-alert slot="right" show variant="secondary"> Page 3 - value = {{ value }} </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-console */
/* eslint-disable nuxt/no-timing-in-fetch-data */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'Page3',
  // componentes utilizados
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  fetch(context) {
    // quem executa este código?
    console.log('fetch, client=', process.client, 'serveur=', process.server)
    // apenas para o servidor
    if (process.server) {
      // retorna-se uma promessa
      return new Promise(function(resolve, reject) {
        // normalmente, aqui temos uma função assíncrona
        // simulamo-la com uma espera de um segundo
        setTimeout(() => {
          // sucesso
          resolve()
        }, 1000)
      }).then(() => {
        // altera-se o store
        context.store.commit('increment', 28)
        // registo
        console.log('fetch commit terminé')
      })
    }
  },
  // ciclo de vida
  beforeCreate() {
    // cliente e servidor
    console.log('[page3 beforeCreate]')
  },
  created() {
    // cliente e servidor
    this.value = this.$store.state.counter
    console.log('[page3 created], value=', this.value)
  },
  beforeMount() {
    // apenas cliente
    console.log('[page3 beforeMount]')
  },
  mounted() {
    // apenas cliente
    console.log('[page3 mounted]')
  }
}
</script>
  • linha 58: a função [created] executada pelo cliente atribui o valor do contador à propriedade [value] da linha 27;
  • a linha 7 apresenta esse valor. Uma vez que é o mesmo que o enviado pelo servidor, não se observa a página a «tremer» devido a uma alteração;

Os registos mostram, além disso, que a função [fetch] não foi executada pelo cliente:

Image

A função [fetch] foi executada pelo servidor [1], mas não pelo cliente [2]. Além disso, é de salientar que as funções do ciclo de vida não são executadas pelo servidor antes do fim da função [fetch] [3]. É possível aumentar o tempo de espera na função [fetch] para verificar isso.

As páginas [page1] e [page3] apresentaram dois métodos que utilizam o store [Vuex] para transmitir informação do servidor para o cliente. É possível questionar se estes métodos são equivalentes. Criamos uma página [page4] para verificar isso.

5.5. A página [page4]

Adicionamos uma nova página [page4] à nossa aplicação:

Image

5.5.1. O componente [navigation]

O componente [navigation] é alterado para permitir a navegação para a nova página:


<template>
  <!-- menu Bootstrap com cinco opções -->
  <b-nav vertical>
    <b-nav-item to="/" exact exact-active-class="active">
      Home
    </b-nav-item>
    <b-nav-item to="/page1" exact exact-active-class="active">
      Page 1
    </b-nav-item>
    <b-nav-item to="/page2" exact exact-active-class="active">
      Page 2
    </b-nav-item>
    <b-nav-item to="/page3" exact exact-active-class="active">
      Page 3
    </b-nav-item>
    <b-nav-item to="/page4" exact exact-active-class="active">
      Page 4
    </b-nav-item>
  </b-nav>
</template>

5.5.2. O código de [page4]

O código da página [page4] é o seguinte:


<!-- página 4 -->
<template>
  <Layout :left="true" :right="true">
    <!-- navegação -->
    <Navigation slot="left" />
    <!-- mensagem -->
    <b-alert slot="right" show variant="secondary"> Page 4 - value = {{ value }} </b-alert>
  </Layout>
</template>

<script>
/* eslint-disable no-console */

import Navigation from '@/components/navigation'
import Layout from '@/components/layout'

export default {
  name: 'Page4',
  // componentes utilizados
  components: {
    Layout,
    Navigation
  },
  data() {
    return {
      value: 0
    }
  },
  // ciclo de vida
  async beforeCreate() {
    // cliente e servidor
    console.log('[page4 beforeCreate]')
    // apenas para o servidor
    if (process.server) {
      // executa-se a função assíncrona
      const valeur = await new Promise(function(resolve, reject) {
        // normalmente, aqui temos uma função assíncrona
        // simula-se com uma espera de 10 segundos
        setTimeout(() => {
          // sucesso — devolve-se o valor do contador
          resolve(52)
        }, 10000)
      })
      // Altera-se o registo
      this.$store.commit('increment', valeur)
      // registo
      console.log('[page4 beforeCreate], fonction asynchrone terminée, compteur=', this.$store.state.counter)
    }
  },
  created() {
    // cliente e servidor
    this.value = this.$store.state.counter
    console.log('[page4 created], value=', this.value)
  },
  beforeMount() {
    // apenas cliente
    console.log('[page4 beforeMount]')
  },
  mounted() {
    // apenas cliente
    console.log('[page4 mounted]')
  }
}
</script>
  • linha 30: o que antes era feito na função [fetch] é agora feito no método [beforeCreate]. Utiliza-se o par async (linha 30) / await (linha 36) para aguardar o fim da função assíncrona;
  • linha 36: recupera-se o resultado da função assíncrona, devolvido na linha 41 após 10 segundos (linha 42);
  • linhas 50-54: no método [created], executado tanto no lado do servidor como no lado do cliente, o contador é atribuído à propriedade [value] da página;

5.5.3. Execução

Executa-se o projeto [nuxt-02] e introduz-se manualmente [localhost:81/nuxt-02/page4] para que o servidor seja solicitado. Tal como no arranque da página [index]:

  • o servidor executa a página [page4.vue];
  • envia a página gerada para o navegador. Esta é apresentada;
  • os scripts do cliente incorporados na página enviada assumem o controlo e executam novamente a página [page4.vue];
  • a página apresentada é então alterada;

O resultado final é o seguinte:

Image

Contrariamente ao esperado, o valor apresentado em [2] não é 52. O que aconteceu?

Os registos são os seguintes:

Image

Pode-se observar que, em [1], o registo de fim da ação assíncrona não foi apresentado. A função [created], que apresenta o valor do contador, apresenta 0. Tudo isto leva a crer que [nuxt] não aguardou o fim da ação assíncrona.

Se voltarmos ao terminal da função VSCode, que serviu para iniciar a aplicação, encontramos os registos da função [3-4]. Verifica-se que a função assíncrona foi efetivamente executada no lado do servidor.

No final, a função [beforeCreate] foi efetivamente executada na íntegra no lado do servidor, mas [nuxt] não esperou pela conclusão da sua execução para enviar a página ao navegador do cliente, ao passo que espera sim pela conclusão da função [fetch]. É, portanto, este método que deve ser utilizado se se pretender que o servidor inicialize um store [Vuex].

5.6. Navegação na aplicação [vue]

Mostrámos o que acontecia quando cada uma das páginas [index, page1, page2, page3, page4] era carregada inicialmente pelo servidor. Na prática, não é isso que acontece: em funcionamento normal, apenas a página [index] é procurada no servidor. Vejamos as três páginas neste caso:

página [index]

Image

Já explicámos este resultado no parágrafo sobre links.

Agora, cliquemos no link [Page 1]:

Image

O valor apresentado é 0. Era 25 quando a página foi solicitada pela primeira vez ao servidor, digitando manualmente o seu URL. A explicação é simples. O código executado é o seguinte:


  created() {
    // cliente e servidor
    console.log('[page1 created]')
    // apenas servidor
    if (process.server) {
      this.$store.commit('increment', 25)
    }
    // cliente e servidor
    this.value = this.$store.state.counter
    console.log('value=', this.value)
},

Era a linha 6 que colocava o contador em 25. Como a página não foi solicitada ao servidor, as linhas 5 a 7 não foram executadas e o contador do store [Vuex] permaneceu em 0.

Agora, vamos clicar na ligação [Page 2]:

Image

Desta vez, não é apresentado qualquer valor e, além disso, surge um aviso nos registos da consola:

Image

  • Em [1], descobrimos que a função [asyncData] foi executada pelo cliente. É sempre assim:
    • é executada pelo servidor se a página for solicitada ao servidor. Nesse caso, não é executada pelo cliente;
    • e sempre que a página for o destino da rota atual do cliente;
  • em [2]: [nuxt] emite um aviso porque, no modelo da página, existe uma expressão reativa {{ value }}, embora a página não tenha a propriedade [value];

Recorde-se o código executado pelo cliente:


asyncData() {
    // quem executa este código?
    console.log('asyncData, client=', process.client, 'serveur=', process.server)
    // apenas para o servidor
    if (process.server) {
      // retorna-se uma promessa
      return new Promise(function(resolve, reject) {
        // normalmente, aqui teríamos uma função assíncrona — mas não é o caso aqui
        // este resultado será incluído nas propriedades de [data]
        resolve({ value: 87 })
      })
    }
  },
  • a linha 3 permitiu verificar que a função [asyncData] foi executada pelo cliente antes mesmo das funções do ciclo de vida;
  • a linha 5 impediu a execução do resto do código que criava a propriedade [value], uma vez que esse código é executado pelo cliente;

Passemos agora à página [page3]:

Image

  • em [2], tínhamos 28 quando a página foi fornecida pelo servidor. Aqui, não é esse o caso;
  • em [4], vemos que a função [fetch] foi executada do lado do cliente;

Vamos analisar o código executado pelo cliente:


...
fetch(context) {
    // quem executa este código?
    console.log('fetch, client=', process.client, 'serveur=', process.server)
    // apenas para o servidor
    if (process.server) {
      // retorna-se uma promessa
      return new Promise(function(resolve, reject) {
        // normalmente, aqui temos uma função assíncrona
        // simulamo-la com uma espera de um segundo
        setTimeout(() => {
          // sucesso
          resolve()
        }, 1000)
      }).then(() => {
        // altera-se o store
        context.store.commit('increment', 28)
        // registo
        console.log('fetch commit terminé')
      })
    }
},
...
  • o cliente executa o método [fetch], linha 2;
  • as linhas 6 a 21 não são executadas porque a condição [process.server] é falsa. Por isso, a linha 17, que define o contador para 28, não é executada. O contador permanece em zero. É por isso que o cliente apresenta 0 em vez de 28;

Passemos agora à página [page4]. Obtemos o seguinte resultado:

Image

  • em [2], o valor do contador;
  • em [3], os registos do cliente;

O código executado pelo cliente é o seguinte:


...
data() {
    return {
      value: 0
    }
  },
  // ciclo de vida
  async beforeCreate() {
    // cliente e servidor
    console.log('[page4 beforeCreate]')
    // apenas para o servidor
    if (process.server) {
      // executa-se a função assíncrona
      const valeur = await new Promise(function(resolve, reject) {
        // normalmente, aqui temos uma função assíncrona
        // simula-se com uma espera de 10 segundos
        setTimeout(() => {
          // sucesso — devolve-se o valor do contador
          resolve(52)
        }, 10000)
      })
      // Altera-se o registo
      this.$store.commit('increment', valeur)
      // registo
      console.log('[page4 beforeCreate], fonction asynchrone terminée, compteur=', this.$store.state.counter)
    }
  },
  created() {
    // cliente e servidor
    this.value = this.$store.state.counter
    console.log('[page4 created], value=', this.value)
  },
...
  • as linhas 12-26 não são executadas pelo cliente, uma vez que a condição [process.server] da linha 12 é falsa. Assim, o contador do registo [Vuex] tem o valor 0 (o seu valor inicial no registo) e é esse valor que a página apresenta;

Podemos perguntar-nos o que acontece quando se coloca em comentário a condição [if] das linhas 12 e 26. Eis a resposta:

  • desta vez, o cliente executa as linhas 14-25, mas o [nuxt] não aguarda o fim da função assíncrona (tal como no servidor) e, por isso, deixa o contador em 0;
  • após 10 s, a função assíncrona termina e o contador é definido para 52 na linha 23;
  • quando se navega novamente para a página [page4], é então o valor 52 que é apresentado;

5.7. Résumé

Dos nossos diversos testes, destacam-se os seguintes pontos:

  • se a página [index] tiver de conter dados externos, estes podem ser recuperados pelo servidor através da função [asyncData];
  • se o servidor tiver de inicializar um store [Vuex] com dados externos ao carregar a página [index], fá-lo-á na função [fetch];
  • a página gerada pelo servidor e a página gerada pelo cliente devem ser idênticas, se quisermos evitar o efeito de «salto» provocado pelo facto de a página do cliente substituir visualmente a página enviada pelo servidor e inicialmente apresentada;

Vamos explorar outros aspetos da função [nuxt] através de um novo exemplo.