Uma das maiores vantagens do React.js deve ser a renderização do lado do servidor . O problema é que a função chave React.renderComponentToString()
é síncrona, o que torna impossível carregar quaisquer dados assíncronos conforme a hierarquia do componente é renderizada no servidor.
Digamos que eu tenha um componente universal para comentários que posso colocar em qualquer lugar da página. Possui apenas uma propriedade, algum tipo de identificador (por exemplo id de um artigo abaixo do qual os comentários são colocados), e todo o resto é tratado pelo próprio componente (carregar, adicionar, gerenciar comentários).
Eu realmente gosto da arquitetura Flux porque torna muitas coisas muito mais fáceis, e seus armazenamentos são perfeitos para compartilhar estado entre servidor e cliente. Assim que minha loja contendo comentários for inicializada, posso apenas serializá-la e enviá-la do servidor para o cliente, onde é facilmente restaurada.
A questão é qual é a melhor forma de povoar minha loja. Nos últimos dias, tenho pesquisado muito no Google e encontrei poucas estratégias, nenhuma das quais parecia muito boa, considerando o quanto esse recurso do React está sendo "promovido".
Na minha opinião, a maneira mais simples é preencher todas as minhas lojas antes que a renderização real comece. Isso significa algum lugar fora da hierarquia do componente (conectado ao meu roteador, por exemplo). O problema com essa abordagem é que eu teria que definir a estrutura da página duas vezes. Considere uma página mais complexa, por exemplo, uma página de blog com muitos componentes diferentes (postagem real do blog, comentários, postagens relacionadas, postagens mais recentes, fluxo do Twitter ...). Eu teria que projetar a estrutura da página usando componentes React e então em outro lugar eu teria que definir o processo de preencher cada loja necessária para esta página atual. Isso não parece uma boa solução para mim. Infelizmente, a maioria dos tutoriais isomórficos são projetados dessa maneira (por exemplo, este ótimo tutorial de fluxo ).
React-async . Essa abordagem é perfeita. Ele me permite simplesmente definir em uma função especial em cada componente como inicializar o estado (não importa se de forma síncrona ou assíncrona) e essas funções são chamadas conforme a hierarquia está sendo renderizada em HTML. Funciona de forma que um componente não seja renderizado até que o estado seja completamente inicializado. O problema é que requer fibrasque é, até onde eu entendo, uma extensão Node.js que altera o comportamento padrão do JavaScript. Embora goste muito do resultado, ainda me parece que, em vez de encontrar uma solução, mudamos as regras do jogo. E acho que não devemos ser forçados a fazer isso para usar esse recurso central do React.js. Também não tenho certeza sobre o suporte geral dessa solução. É possível usar o Fiber na hospedagem padrão da Web Node.js?
Eu estava pensando um pouco por conta própria. Eu realmente não pensei nos detalhes de implementação, mas a ideia geral é que eu estenderia os componentes de maneira semelhante ao React-async e, em seguida, chamaria repetidamente React.renderComponentToString () no componente raiz. Durante cada passagem, eu coletaria os retornos de chamada estendidos e depois os chamaria no e do passe para preencher as lojas. Eu repetiria esta etapa até que todos os armazenamentos exigidos pela hierarquia de componentes atual fossem preenchidos. Há muitas coisas a serem resolvidas e estou particularmente inseguro quanto ao desempenho.
Perdi alguma coisa? Existe outra abordagem / solução? No momento, estou pensando em adotar o método de reação assíncrona / fibras, mas não estou completamente certo sobre isso, conforme explicado no segundo ponto.
Discussão relacionada no GitHub . Aparentemente, não existe uma abordagem oficial ou mesmo solução. Talvez a verdadeira questão seja como os componentes React devem ser usados. Como camada de visualização simples (basicamente minha sugestão número um) ou como componentes reais independentes e autônomos?
fonte
Respostas:
Se você usar o react-router , pode apenas definir
willTransitionTo
métodos nos componentes, que passam umTransition
objeto que você pode chamar.wait
.Não importa se renderToString é síncrono porque o retorno de chamada para
Router.run
não será chamado até que todas as.wait
promessas ed sejam resolvidas, portanto, quandorenderToString
for chamado no middleware, você poderá ter preenchido os armazenamentos. Mesmo se os armazenamentos forem singletons, você pode apenas definir seus dados temporariamente just-in-time antes da chamada de renderização síncrona e o componente os verá.Exemplo de middleware:
O
routes
objeto (que descreve a hierarquia das rotas) é compartilhado literalmente com o cliente e o servidorfonte
willTransitionTo
método. O que significa que ainda não é possível escrever componentes reutilizáveis completamente autônomos como o que descrevi na pergunta. Mas, a menos que estejamos dispostos a usar o Fibers, esta é provavelmente a melhor e a mais eficaz maneira de implementar a renderização do lado do servidor.transition
objeto como um parâmetro, portanto, simplesmente chamarátransition.wait(yourPromise)
. Isso, claro, significa que você precisa implementar sua API para dar suporte às promessas. Outra desvantagem dessa abordagem é que não há uma maneira simples de implementar um "indicador de carregamento" no lado do cliente. A transição não mudará para o componente de manipulador de rota até que todas as promessas sejam resolvidas..waited
para uma transição. Depois que todos eles forem atendidos, o.run
retorno de chamada é chamado. Antes de.render()
coletar todos os dados juntos das promessas e definir os estados de armazenamento singelton, na próxima linha após a chamada de renderização inicializo os armazenamentos singleton de volta. É bastante hacky, mas tudo acontece automaticamente e o código do aplicativo de armazenamento e componente permanece virtualmente o mesmo.Sei que provavelmente não é exatamente o que você deseja e pode não fazer sentido, mas me lembro de modificar um pouco o componente para lidar com ambos:
Então, algo como:
Lamento não ter o código exato em mãos, então isso pode não funcionar imediatamente, mas estou postando para discussão.
Novamente, a ideia é tratar a maior parte do componente como uma visão burra e lidar com a busca de dados o máximo possível fora do componente.
fonte
<script type=application/json>{initState}</script>
; assim os dados estarão no HTML. Hidrate / vincule eventos de IU à página chamando render no cliente. As páginas subsequentes são criadas pelo código js do cliente (buscando dados conforme necessário) e renderizadas pelo cliente. Dessa forma, qualquer atualização carregará novas páginas SSR e clicar em uma página será CSR. = isomorphic & SEO friendlyFiquei muito confuso com isso hoje e, embora isso não seja uma resposta para o seu problema, usei essa abordagem. Eu queria usar o Express para roteamento em vez do React Router e não queria usar o Fibers porque não precisava de suporte a threading no nó.
Então, acabei de tomar a decisão de que, para os dados iniciais que precisam ser renderizados para o armazenamento de fluxo na carga, realizarei uma solicitação AJAX e passarei os dados iniciais para o armazenamento
Eu estava usando o Fluxxor para este exemplo.
Então, na minha rota expressa, neste caso, um
/products
rota:Em seguida, meu método de renderização inicializar que passa os dados para a loja.
fonte
Sei que esta pergunta foi feita há um ano, mas tínhamos o mesmo problema e o resolvemos com promessas aninhadas derivadas dos componentes que serão renderizados. No final, tínhamos todos os dados do aplicativo e apenas os enviamos.
Por exemplo:
e no roteador
fonte
Quero compartilhar com vocês minha abordagem de renderização do lado do servidor usando
Flux
, pouco ser simplificado, por exemplo:Digamos que temos
component
com os dados iniciais da loja:Se a classe exigir alguns dados pré-carregados para o estado inicial, vamos criar o Loader para
MyComponent
:Loja:
Agora basta carregar os dados no roteador:
fonte
assim como um breve rollup -> GraphQL resolverá isso totalmente para sua pilha ...
-> getDataFromTree encontrará automaticamente todas as consultas envolvidas em seu aplicativo e as executará, pouplating seu cache de Apollo no servidor e, assim, habilitando o SSR totalmente funcional. BÄM
fonte