收藏本站 收藏本站
積木網首頁 - 軟件測試 - 常用手冊 - 站長工具 - 技術社區
首頁 > JavaScript > JavaScript技巧 > 正文

首頁 - PHP - 數據庫 - 操作系統 - 游戲開發 - JS - Android - MySql - Redis - MongoDB - Win8 - Shell編程 - DOS命令 - jQuery - CSS樣式 - Python - Perl

Access - Oracle - DB2 - SQLServer - MsSql2008 - MsSql2005 - Sqlite - PostgreSQL - node.js - extjs - JavaScript vbs - Powershell - Ruby

詳解React 在服務端渲染的實現

React是最受歡迎的客戶端 JavaScript 框架,但你知道嗎(可以試試),你可以使用 React 在服務器端進行渲染?

假設你已經在客戶端使用 React 構建了一個事件列表 app。該應用程序使用了您最喜歡的服務器端工具構建的API。幾周后,用戶告訴您,他們的頁面沒有顯示在 Google 上,發布到 Facebook 時也顯示不出來。 這些問題似乎是可以解決的,對吧?

您會發現,要解決這個問題,需要在初始加載時從服務器渲染 React 頁面,以便來自搜索引擎和社交媒體網站的爬蟲工具可以讀取您的標記。有證據表明,Google 有時會執行 javascript 程序并且對生成的內容進行索引,但并不總是的。因此,如果您希望確保與其他服​​務(如Facebook,Twitter)有良好的SEO兼容性,那么始終建議使用服務器端渲染。

在本教程中,我們將逐步介紹服務器端的呈現示例。包括圍繞與API交流的React應用程序的共同路障。
在本教程中,我們將逐步向您介紹服務器端的渲染示例。包括圍繞著 APIS 交流一些在服務端渲染 React 應用程序的共同障礙。

服務端渲染的優勢

可能您的團隊談論到服務端渲染的好處是首先會想到 SEO,但這并不是唯一的潛在好處。

更大的好處如下:服務器端渲染能更快地顯示頁面。使用服務器端渲染,您的服務器對瀏覽器進行響應是在您的 HTML 頁面可以渲染的時候,因此瀏覽器可以不用等待所有的 JavaScript 被下載和執行就可以開始渲染。當瀏覽器下載并執行頁面所需的 JavaScript 和其他資源時,不會出現 “白屏” 現象,而 “白屏” 這是在完全有客戶端呈現的 React 網站中可能發生的情況。

入門

接下來讓我們來看看如何將服務器端渲染添加到一個基本的客戶端渲染的使用Babel和Webpack的React應用程序中。我們的應用程序將增加從第三方 API 獲取數據的復雜性。我們在GitHub上提供了相關代碼,您可以在其中看到完整的示例。

提供的代碼中只有一個 React 組件,`hello.js`,這個文件將向 ButterCMS 發出異步請求,并渲染返回的 JSON 列表的博文。ButterCMS 是一個基于API的博客引擎,可供個人使用,因此它非常適合測試現實生活中的用例。啟動代碼中連接著一個 API token,如果你想使用你自己的 API token 可以使用你的 GitHub 賬號登入 ButterCMS。

import React from 'react';
import Butter from 'buttercms'

const butter = Butter('b60a008584313ed21803780bc9208557b3b49fbb');

var Hello = React.createClass({
 getInitialState: function() {
  return {loaded: false};
 },
 componentWillMount: function() {
  butter.post.list().then((resp) => {
   this.setState({
    loaded: true,
    resp: resp.data
   })
  });
 },
 render: function() {
  if (this.state.loaded) {
   return (
    <div>
     {this.state.resp.data.map((post) => {
      return (
       <div key={post.slug}>{post.title}</div>
      )
     })}
    </div>
   );
  } else {
   return <div>Loading...</div>;
  }
 }
});

export default Hello;

啟動器代碼中包含以下內容:

package.json - 依賴項 Webpack 和 Babel 配置 index.html - app 的 HTML 文件 index.js - 加載 React 并渲染 Hello 組件

要使應用運行,請先克隆資源庫:

git clone ...
cd ..

安裝依賴:

npm install

然后啟動服務器:

npm run start

瀏覽器輸入 http://localhost:8000 可以看到這個 app: (這里譯者進行補充,package.json 里的 start 命令改為如下:"start": webpack-dev-server --watch)

查看圖片

如果您查看渲染頁面的源代碼,您將看到發送到瀏覽器的標記只是一個到 JavaScript 文件的鏈接。這意味著頁面的內容不能保證被搜索引擎和社交媒體平臺抓取:

查看圖片

增加服務器端渲染

接下來,我們將實現服務器端渲染,以便將完全生成的HTML發送到瀏覽器。如果要同時查看所有更改,請查看GitHub上的差異。

To get started, we'll install Express, a Node.js server side application framework:

開始前,讓我們安裝 Express,一個 Node.js 的服務器端應用程序框架:

npm install express --save

我們要創建一個渲染我們的 React 組件的服務器:

import express from 'express';
import fs from 'fs';
import path from 'path';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import Hello from './Hello.js';

function handleRender(req, res) {
 // 把 Hello 組件渲染成 HTML 字符串
 const html = ReactDOMServer.renderToString(<Hello />);

 // 加載 index.html 的內容
 fs.readFile('./index.html', 'utf8', function (err, data) {
  if (err) throw err;

  // 把渲染后的 React HTML 插入到 div 中
  const document = data.replace(/<div id="app"></div>/, `<div id="app">${html}</div>`);

  // 把響應傳回給客戶端
  res.send(document);
 });
}

const app = express();

// 服務器使用 static 中間件構建 build 路徑
app.use('/build', express.static(path.join(__dirname, 'build')));

// 使用我們的 handleRender 中間件處理服務端請求
app.get('*', handleRender);

// 啟動服務器
app.listen(3000);

讓我們分解下程序看看發生了什么事情...

handleRender 函數處理所有請求。在文件頂部導入的ReactDOMServer 類提供了將 React 節點渲染成其初始 HTML 的 renderToString() 方法

ReactDOMServer.renderToString(<Hello />);

這將返回 Hello 組件的 HTML ,我們將其注入到 index.html 的 HTML 中,從而生成服務器上頁面的完整 HTML 。

const document = data.replace(/<div id="app"></div>/,`<div id="app">${html}</div>`);

To start the server, update the start script in package.json and then run npm run start:

要啟動服務器,請更新 `package.json` 中的起始腳本,然后運行 npm run start :

"scripts": {
 "start": "webpack && babel-node server.js"
},

瀏覽 http://localhost:3000 查看應用程序。瞧!您的頁面現在正在從服務器渲染出來了。但是有個問題,如果您在瀏覽器中查看頁面源碼,您會注意到博客文章仍未包含在回復中。這是怎么回事?如果我們在Chrome中打開網絡標簽,我們會看到客戶端上發生API請求。

查看圖片

雖然我們在服務器上渲染了 React 組件,但是 API 請求在 componentWillMount 中異步生成,并且組件在請求完成之前渲染。所以即使我們已經在服務器上完成渲染,但我們只是完成了部分。事實上,React repo 有一個 issue,超過 100 條評論討論了這個問題和各種解決方法。

在渲染之前獲取數據

要解決這個問題,我們需要在渲染 Hello 組件之前確保 API 請求完成。這意味著要使 API 請求跳出 React 的組件渲染循環,并在渲染組件之前獲取數據。我們將逐步介紹這一步,但您可以在GitHub上查看完整的差異。

To move data fetching before rendering, we'll install react-transmit:

要在渲染之前獲取數據,我們需安裝 react-transmit:

npm install react-transmit --save

React Transmit 給了我們優雅的包裝器組件(通常稱為“高階組件”),用于獲取在客戶端和服務器上工作的數據。

這是我們使用 react-transmit 后的組件的代碼:

import React from 'react';
import Butter from 'buttercms'
import Transmit from 'react-transmit';

const butter = Butter('b60a008584313ed21803780bc9208557b3b49fbb');

var Hello = React.createClass({
 render: function() {
  if (this.props.posts) {
   return (
    <div>
     {this.props.posts.data.map((post) => {
      return (
       <div key={post.slug}>{post.title}</div>
      )
     })}
    </div>
   );
  } else {
   return <div>Loading...</div>;
  }
 }
});

export default Transmit.createContainer(Hello, {
 // 必須設定 initiallVariables 和 ftagments ,否則渲染時會報錯
 initialVariables: {},
 // 定義的方法名將成為 Transmit props 的名稱
 fragments: {
  posts() {
   return butter.post.list().then((resp) => resp.data);
  }
 }
});

我們已經使用 Transmit.createContainer 將我們的組件包裝在一個高級組件中,該組件可以用來獲取數據。我們在 React 組件中刪除了生命周期方法,因為無需兩次獲取數據。同時我們把 render 方法中的 state 替換成 props,因為 React Transmit 將數據作為 props 傳遞給組件。

為了確保服務器在渲染之前獲取數據,我們導入 Transmit 并使用 Transmit.renderToString 而不是 ReactDOM.renderToString 方法

import express from 'express';
import fs from 'fs';
import path from 'path';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import Hello from './Hello.js';
import Transmit from 'react-transmit';

function handleRender(req, res) {
 Transmit.renderToString(Hello).then(({reactString, reactData}) => {
  fs.readFile('./index.html', 'utf8', function (err, data) {
   if (err) throw err;

   const document = data.replace(/<div id="app"></div>/, `<div id="app">${reactString}</div>`);
   const output = Transmit.injectIntoMarkup(document, reactData, ['/build/client.js']);

   res.send(document);
  });
 });
}

const app = express();

// 服務器使用 static 中間件構建 build 路徑
app.use('/build', express.static(path.join(__dirname, 'build')));

// 使用我們的 handleRender 中間件處理服務端請求
app.get('*', handleRender);

// 啟動服務器
app.listen(3000);

重新啟動服務器瀏覽到 http://localhost:3000。查看頁面源代碼,您將看到該頁面現在完全呈現在服務器上!

查看圖片

更進一步

我們做到了!在服務器上使用 React 可能很棘手,尤其是從 API 獲取數據時。幸運的是,React社區正在蓬勃發展,并創造了許多有用的工具。如果您對構建在客戶端和服務器上渲染的大型 React 應用程序的框架感興趣,請查看 Walmart Labs 的 Electrode 或Next.js。或者如果要在 Ruby 中渲染 React ,請查看 AirBnB 的 Hypernova 。

原文地址:https://css-tricks.com/server-side-react-rendering/

原文作者:Roger Jin

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持積木網。

圖片加載完成再執行事件的實例
實例如下://圖片加載functionload(imgSrc,callback){varimgs=[];varc=0;for(vari=0;iimgSrc.length;i++){imgs[i]=newImage();imgs[i].src=imgSrc[i];imgs[i].onload=function(){c++if(c==imgSrc.length){i

React/Redux應用使用Async/Await的方法
Async/Await是尚未正式公布的ES7標準新特性。簡而言之,就是讓你以同步方法的思維編寫異步代碼。對于前端,異步任務代碼的編寫經歷了callback到現在流

js的函數的按值傳遞參數(實例講解)
js的函數傳參的方式是按值傳遞,正常情況下,改變函數參數的值,并不會對函數外部的變量造成影響。例如:'usestrict';varlist=[1,2,3];list.forEach(function(item

本周排行

更新排行

強悍的草根IT技術社區,這里應該有您想要的! 友情鏈接:b2b電子商務
Copyright © 2010 Gimoo.Net. All Rights Rreserved  京ICP備05050695號
36选7走势图大全 山西快十开奖结果查询 极速赛车全天计划网址 09快乐双彩24期中奖号码 贵州11选五一定牛遗漏 下载股票行情交易 排列五杀号彩经网 福彩三d计算器 内蒙古11选5玩法说明 江苏十一选五开奖结果历史开奖结果 湖北快3开奖结果昨天 北京赛车pk拾精准计划 哪里有免费的股票推荐 湖北快3个位走势图 山东十一运夺金预测 炒股是什么意思解释 福建快三基本走势图今天