亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

使用 PHP 來做 Vue.js 的 SSR 服務(wù)端渲染

李增田 / 1954人閱讀

摘要:對于客戶端應(yīng)用來說,服務(wù)端渲染是一個熱門話題。在服務(wù)器預(yù)渲染初始應(yīng)用狀態(tài)。重構(gòu)這段腳本,使其可以在服務(wù)端運行。如果這些原因和你的情況吻合,那么使用進行服務(wù)端渲染將會是個不錯方案。我已經(jīng)發(fā)布兩個庫來支持的服務(wù)端渲染和專為應(yīng)用打造的。


對于客戶端應(yīng)用來說,服務(wù)端渲染是一個熱門話題。然而不幸的是,這并不是一件容易的事,尤其是對于不用 Node.js 環(huán)境開發(fā)的人來說。

我發(fā)布了兩個庫讓 PHP 從服務(wù)端渲染成為可能.spatie/server-side-rendering?和?spatie/laravel-server-side-rendering適配 laravel 應(yīng)用。

讓我們一起來仔細研究一些服務(wù)端渲染的概念,權(quán)衡優(yōu)缺點,然后遵循第一法則用 PHP 建立一個服務(wù)端渲染。

什么是服務(wù)端渲染

一個單頁應(yīng)用(通常也叫做 SPA )是一個客戶端渲染的 App 。這是一個僅在瀏覽器端運行的應(yīng)用。如果你正在使用框架,比如 React, Vue.js 或者 AngularJS ,客戶端將從頭開始渲染你的 App 。

瀏覽器的工作

在 SPA 被啟動并準備使用之前,瀏覽器需要經(jīng)過幾個步驟。

下載 JavaScript 腳本

解析 JavaScript 腳本

運行 JavaScript 腳本

取回數(shù)據(jù)(可選,但普遍)

在原本的空容器渲染應(yīng)用? (首次有意義的渲染)

準備完成!?(可以交互啦)

用戶不會看到任何有意義的內(nèi)容,直到瀏覽器完全渲染 App(需要花費一點時間)。這會造成一個明顯的延遲,直到 首次有意義的渲染 完成,從而影響了用戶體驗。

這就是為什么服務(wù)端渲染(一般被稱作 SSR )登場的原因。SSR 在服務(wù)器預(yù)渲染初始應(yīng)用狀態(tài)。這里是瀏覽器在使用服務(wù)端渲染后需要經(jīng)過的步驟:

渲染來自服務(wù)端的 HTML (首次有意義的渲染)

下載 JavaScript 腳本

解析 JavaScript 腳本

運行 JavaScript 腳本

取回數(shù)據(jù)

使已存在的 HTML 頁面可交互

準備完成!?(可以交互啦)

由于服務(wù)器提供了 HTML 的預(yù)渲染塊,因此用戶無需等到一切完成后才能看到有意義的內(nèi)容。注意,雖然 交互時間 仍然處于最后,但可感知的表現(xiàn)得到了巨大的提升。

服務(wù)端渲染的優(yōu)點

服務(wù)端渲染的主要優(yōu)點是可以提升用戶體驗。并且,如果你的網(wǎng)站需要應(yīng)對不能執(zhí)行 JavaScript 的老舊爬蟲,SSR 將是必須的,這樣,爬蟲才能索引服務(wù)端渲染過后的頁面,而不是一個空蕩蕩的文檔。

服務(wù)端如何渲染?

記住服務(wù)端渲染并非微不足道,這一點很重要。當(dāng)你的 Web 應(yīng)用同時運行在瀏覽器和服務(wù)器,而你的 Web 應(yīng)用依賴 DOM 訪問,那么你需要確保這些調(diào)用不會在服務(wù)端觸發(fā),因為沒有 DOM API 可用。

基礎(chǔ)設(shè)施復(fù)雜性

假設(shè)你決定了服務(wù)端渲染你的應(yīng)用端程序,你如果正在閱讀這篇文章,很大可能正在使用 PHP 構(gòu)建應(yīng)用的大部分(功能)。但是,服務(wù)端渲染的 SPA 需要運行在 Node.js 環(huán)境,所以將需要維護第二個程序。

你需要構(gòu)建兩個應(yīng)用程序之間的橋梁,以便它們進行通信和共享數(shù)據(jù):需要一個 API。構(gòu)建無狀態(tài) API 相比于構(gòu)建有狀態(tài)是比較 困難 的。你需要熟悉一些新概念,例如基于 JWT 或 OAUTH 的驗證,CORS,REST ,添加這些到現(xiàn)有應(yīng)用中是很重要的。

有得必有所失,我們已經(jīng)建立了 SSR 以增加 Web 應(yīng)用的用戶體驗,但 SSR 是有成本的。

服務(wù)器端渲染權(quán)衡取舍

服務(wù)器上多了一個額外的操作。一個是服務(wù)器增加了負載壓力,第二個是頁面響應(yīng)時間也會稍微加長。 不過因為現(xiàn)在服務(wù)器返回了有效內(nèi)容,在用戶看來,第二個問題的影響不大。

大部分時候你會使用 Node.js 來渲染你的 SPA 代碼。如果你的后端代碼不是使用 Javascript 編寫的話,新加入 Node.js 堆棧將使你的程序架構(gòu)變得復(fù)雜。

為了簡化基礎(chǔ)架構(gòu)的復(fù)雜度, 我們需要找到一個方法,使已有的 PHP 環(huán)境作為服務(wù)端來渲染客戶端應(yīng)用。

在 PHP 中渲染 JavaScript

在服務(wù)器端渲染 SPA 需要集齊以下三樣?xùn)|西:

一個可以執(zhí)行 JavaScript 的引擎

一個可以在服務(wù)器上渲染應(yīng)用的腳本

一個可以在客戶端渲染和運行應(yīng)用的腳本

SSR scripts 101

下面的例子使用了 Vue.js。你如果習(xí)慣使用其它的框架(例如 React),不必擔(dān)心,它們的核心思想都是類似的,一切看起來都是那么相似。

簡單起見,我們使用經(jīng)典的 “ Hello World ” 例子。

下面是程序的代碼(沒有 SSR):

// app.js
import Vue from "vue"

new Vue({
  template: `
    
Hello, world!
`, el: "#app" })

這短代碼實例化了一個 Vue 組件,并且在一個容器(id 值為 app 的 空 div)渲染。

如果在服務(wù)端運行這點腳本,會拋出錯誤,因為沒有 DOM 可訪問,而 Vue 卻嘗試在一個不存在的元素里渲染應(yīng)用。

重構(gòu)這段腳本,使其 可以 在服務(wù)端運行。

// app.js
import Vue from "vue"

export default () => new Vue({
  template: `
    
Hello, world!
` }) // entry-client.js import createApp from "./app" const app = createApp() app.$mount("#app")

我們將之前的代碼分成兩部分。app.js 作為創(chuàng)建應(yīng)用實例的工廠,而第二部分,即 entry-client.js,會運行在瀏覽器,它使用工廠創(chuàng)建了應(yīng)用實例,并且掛載在 DOM。

現(xiàn)在我們可以創(chuàng)建一個沒有 DOM 依賴性的應(yīng)用程序,可以為服務(wù)端編寫第二個腳本。

// entry-server.js
import createApp from "./app"
import renderToString from "vue-server-renderer/basic"

const app = createApp()

renderToString(app, (err, html) => {
  if (err) {
    throw new Error(err)
  }
  // Dispatch the HTML string to the client...
})

我們引入了相同的應(yīng)用工廠,但我們使用服務(wù)端渲染的方式來渲染純 HTML 字符串,它將包含應(yīng)用初始狀態(tài)的展示。

我們已經(jīng)具備三個關(guān)鍵因素中的兩個:服務(wù)端腳本和客戶端腳本?,F(xiàn)在,讓我們在 PHP 上運行它吧!

執(zhí)行 JavaScript

在 PHP 運行 JavaScript,想到的第一個選擇是 V8Js。V8Js 是嵌入在 PHP 擴展的 V8 引擎,它允許我們執(zhí)行 JavaScript。

使用 V8Js 執(zhí)行腳本非常直接。我們可以用 PHP 中的輸出緩沖和 JavaScript 中的 print 來捕獲結(jié)果。

$v8 = new V8Js();

ob_start();

// $script 包含了我們想執(zhí)行的腳本內(nèi)容

$v8->executeString($script);

echo ob_get_contents();
print("
Hello, world!
")

這種方法的缺點是需要第三方 PHP 擴展,而擴展可能很難或者不能在你的系統(tǒng)上安裝,所以如果有其他(不需要安裝擴展的)方法,它會更好的選擇。

這個不一樣的方法就是使用 Node.js 運行 JavaScript。我們可以開啟一個 Node 進程,它負責(zé)運行腳本并且捕獲輸出。
Symfony 的?Process 組件就是我們想要的。

use SymfonyComponentProcessProcess;

// $nodePath 是可執(zhí)行的 Node.js 的路徑
// $scriptPath 是想要執(zhí)行的 JavaScript 腳本的路徑

new Process([$nodePath, $scriptPath]);

echo $process->mustRun()->getOutput();
console.log("
Hello, world!
")

注意,(打印)在 Node 中是調(diào)用 console.log 而不是 print 。

讓我們一起來實現(xiàn)它吧!

spatie/server-side-rendering 包的其中一個關(guān)鍵理念是?引擎?接口。引擎就是上述 JavaScript 執(zhí)行的一個抽象概念。

namespace SpatieSsr;

/**
 * 創(chuàng)建引擎接口。
 */
interface Engine
{
    public function run(string $script): string;
    public function getDispatchHandler(): string;
}

run?方法預(yù)期一個腳本的輸入 (腳本 內(nèi)容,不是一條路徑),并且返回執(zhí)行結(jié)果。?getDispatchHandler?允許引擎聲明它預(yù)期腳本如何展示發(fā)布。例如 V8 中的print?方法,或是 Node 中的 console.log?。

V8Js 引擎實現(xiàn)起來并不是很花俏。它更類似于我們上述理念的驗證,帶有一些附加的錯誤處理機制。

namespace SpatieSsrEngines;

use V8Js;
use V8JsException;
use SpatieSsrEngine;
use SpatieSsrExceptionsEngineError;

/**
 * 創(chuàng)建一個 V8 類來實現(xiàn)引擎接口類 Engine 。
 */
class V8 implements Engine。
{
    /** @var V8Js */
    protected $v8;

    public function __construct(V8Js $v8)
    {
        $this->v8 = $v8;
    }

    /**
     * 打開緩沖區(qū)。
     * 返回緩沖區(qū)存儲v8的腳本處理結(jié)果。
     */
    public function run(string $script): string
    {
        try {
            ob_start();

            $this->v8->executeString($script);

            return ob_get_contents();
        } catch (V8JsException $exception) {
            throw EngineError::withException($exception);
        } finally {
            ob_end_clean();
        }
    }

    public function getDispatchHandler(): string
    {
        return "print";
    }
}

注意這里我們將?V8JsException?重新拋出作為我們的?EngineError。 這樣我們就可以在任何的引擎視線中捕捉相同的異常。

Node 引擎會更加復(fù)雜一點。不像 V8Js,Node 需要?文件?去執(zhí)行,而不是腳本內(nèi)容。在執(zhí)行一個服務(wù)端腳本前,它需要被保存到一個臨時的路徑。

namespace SpatieSsrEngines;

use SpatieSsrEngine;
use SpatieSsrExceptionsEngineError;
use SymfonyComponentProcessProcess;
use SymfonyComponentProcessExceptionProcessFailedException;

/**
 * 創(chuàng)建一個 Node 類來實現(xiàn)引擎接口類 Engine 。
 */
class Node implements Engine
{
    /** @var string */
    protected $nodePath;

    /** @var string */
    protected $tempPath;

    public function __construct(string $nodePath, string $tempPath)
    {
        $this->nodePath = $nodePath;
        $this->tempPath = $tempPath;
    }

    public function run(string $script): string
    {
        // 生成一個隨機的、獨一無二的臨時文件路徑。
        $tempFilePath = $this->createTempFilePath();

        // 在臨時文件中寫進腳本內(nèi)容。
        file_put_contents($tempFilePath, $script);

        // 創(chuàng)建進程執(zhí)行臨時文件。
        $process = new Process([$this->nodePath, $tempFilePath]);

        try {
            return substr($process->mustRun()->getOutput(), 0, -1);
        } catch (ProcessFailedException $exception) {
            throw EngineError::withException($exception);
        } finally {
            unlink($tempFilePath);
        }
    }

    public function getDispatchHandler(): string
    {
        return "console.log";
    }

    protected function createTempFilePath(): string
    {
        return $this->tempPath."/".md5(time()).".js";
    }
}

除了臨時路徑步驟之外,實現(xiàn)方法看起來也是相當(dāng)直截了當(dāng)。

我們已經(jīng)創(chuàng)建好了 Engine 接口,接下來需要編寫渲染的類。以下的渲染類來自于 spatie/server-side-rendering 擴展包,是一個最基本的渲染類的結(jié)構(gòu)。

渲染類唯一的依賴是 Engine 接口的實現(xiàn):

class Renderer
{
    public function __construct(Engine $engine)
    {
        $this->engine = $engine;
    }
}

渲染方法 render 里將會處理渲染部分的邏輯,想要執(zhí)行一個 JavaScript 腳本文件,需要以下兩個元素:

我們的應(yīng)用腳本文件;

一個用來獲取解析產(chǎn)生的 HTML 的分發(fā)方法;

一個簡單的 render?如下:

class Renderer
{
    public function render(string $entry): string
    {
        $serverScript = implode(";", [
            "var dispatch = {$this->engine->getDispatchHandler()}",
            file_get_contents($entry),
        ]);

        return $this->engine->run($serverScript);
    }
}

此方法接受 ?entry-server.js?文件路徑作為參數(shù)。

我們需要將解析前的 HTML 從腳本中分發(fā)到 PHP 環(huán)境中。dispatch 方法返回 Engine 類里的 getDispatchHandler 方法,dispatch 需要在服務(wù)器腳本加載前運行。

還記得我們的服務(wù)器端入口腳本嗎?接下來我們在此腳本中調(diào)用我們的 ?dispatch 方法:

// entry-server.js
import app from "./app"
import renderToString from "vue-server-renderer/basic"

renderToString(app, (err, html) => {
  if (err) {
    throw new Error(err)
  }
  dispatch(html)
})

Vue 的應(yīng)用腳本無需特殊處理,只需要使用 ?file_get_contents 方法讀取文件即可。

我們已經(jīng)成功創(chuàng)建了一個 PHP 的 SSR 。spatie/server-side-rendering 中的完整渲染器 Renderer?跟我們實現(xiàn)有點不一樣,他們擁有更高的容錯能力,和更加豐富的功能如有一套 PHP 和 JavaScript 共享數(shù)據(jù)的機制。如果你感興趣的話,建議你閱讀下源碼 server-side-rendering 代碼庫?。

三思而后行

我們弄清楚了服務(wù)器端渲染的利和弊,知道 SSR 會增加應(yīng)用程序架構(gòu)和基礎(chǔ)結(jié)構(gòu)的復(fù)雜度。如果服務(wù)器端渲染不能為你的業(yè)務(wù)提供任何價值,那么你可能不應(yīng)該首先考慮他。

如果你 確實 想開始使用服務(wù)器端渲染,請先閱讀應(yīng)用程序的架構(gòu)。大多數(shù) JavaScript 框架都有關(guān)于 SSR 的深入指南。Vue.js 甚至有一個專門的 SSR 文檔網(wǎng)站,解釋了諸如數(shù)據(jù)獲取和管理用于服務(wù)器端渲染的應(yīng)用程序方面的坑。

如果可能,請使用經(jīng)過實戰(zhàn)檢驗的解決方案

有許多經(jīng)過實戰(zhàn)檢驗的解決方案,能提供很好的 SSR 開發(fā)體驗。比如,如果你在構(gòu)建 React 應(yīng)用,可以使用 Next.js,或者你更青睞于 Vue?則可用 Nuxt.js,這些都是很引人注目的項目。

還不夠?嘗試 PHP 服務(wù)端渲染

你僅能以有限的資源來管理基礎(chǔ)架構(gòu)上的復(fù)雜性。你想將服務(wù)端渲染作為大型 PHP 應(yīng)用中的一部分。你不想構(gòu)建和維護無狀態(tài)的 API。 如果這些原因和你的情況吻合,那么使用 PHP 進行服務(wù)端渲染將會是個不錯方案。

我已經(jīng)發(fā)布兩個庫來支持 PHP 的服務(wù)端 JavaScript 渲染: ?spatie/server-side-rendering? 和專為 Laravel 應(yīng)用打造的 spatie/laravel-server-side-rendering??。Laravel 定制版在 Laravel 應(yīng)用中近乎 0 配置即可投入使用,通用版需要根據(jù)運行環(huán)境做一些設(shè)置調(diào)整。當(dāng)然,詳細內(nèi)容可以參考軟件包自述文件。

如果你僅是想體驗,從 spatie/laravel-server-side-rendering-examples? 檢出項目并參考指南進行安裝。

如果你考慮服務(wù)端渲染,我希望這類軟件包可以幫到你,并期待通過 Github 做進一步問題交流和反饋!

更多現(xiàn)代化 PHP 知識,請前往 Laravel / PHP 知識社區(qū)

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/28506.html

相關(guān)文章

  • 服務(wù)預(yù)渲染之Nuxt(介紹篇)

    摘要:為了解決問題,推出了服務(wù)端預(yù)渲染,以便提高對優(yōu)化。應(yīng)用,到了,單頁面應(yīng)用優(yōu)秀的用戶體驗,逐漸成為了主流,頁面整體式渲染出來的,稱之為客戶端渲染??蛻舳私邮諗?shù)據(jù),然后完成最終渲染。通過對客戶端服務(wù)端基礎(chǔ)框架的抽象組織,主要關(guān)注的是應(yīng)用的渲染。 現(xiàn)在前端開發(fā)一般都是前后端分離,mvvm和mvc的開發(fā)框架,如Angular、React和Vue等,雖然寫框架能夠使我們快速的完成開發(fā),但是由于前...

    Shonim 評論0 收藏0
  • vue服務(wù)渲染demo將vue-cli生成項目轉(zhuǎn)為ssr

    摘要:無需使用服務(wù)器實時動態(tài)編譯,而是使用預(yù)渲染方式,在構(gòu)建時簡單地生成針對特定路由的靜態(tài)文件。與可以部署在任何靜態(tài)文件服務(wù)器上的完全靜態(tài)單頁面應(yīng)用程序不同,服務(wù)器渲染應(yīng)用程序,需要處于運行環(huán)境。更多的服務(wù)器端負載。 目錄結(jié)構(gòu) -no-ssr-demo 未做ssr之前的項目代碼用于對比 -vuecli2ssr 將vuecli生成的項目轉(zhuǎn)為ssr -prerender-demo 使用prer...

    whinc 評論0 收藏0
  • Vue.js SSR 內(nèi)容總結(jié)

    摘要:本文只是對官方文檔和對官方的個人學(xué)習(xí)總結(jié),說得不夠完整的請見諒本文主要對以下幾方面內(nèi)容對的內(nèi)容進行分析總結(jié)出現(xiàn)的原因的總體原理當(dāng)中的數(shù)據(jù)預(yù)取在編寫代碼時候的限制的構(gòu)建原理出現(xiàn)的原因單頁應(yīng)用有一個很大的缺點就是問題,搜索引擎目前只能對同步的進 本文只是對Vue.js官方SSR文檔和對官方hackernews demo的個人學(xué)習(xí)總結(jié),說得不夠完整的請見諒 本文主要對以下幾方面內(nèi)容對Vue....

    曹金海 評論0 收藏0
  • Vue 服務(wù)渲染實踐 ——Web應(yīng)用首屏耗時最優(yōu)化方案

    摘要:好在后是支持服務(wù)端渲染的,零零散散花費了兩三周事件,通過改造現(xiàn)有項目,基本完成了在現(xiàn)有項目中實踐了服務(wù)端渲染。在服務(wù)端生成對應(yīng)的字符串,客戶端接收到對應(yīng)的字符串,能立即渲染,最高效的首屏耗時。服務(wù)端渲染的原理是虛擬。實現(xiàn)前后端同構(gòu)應(yīng)用。 隨著各大前端框架的誕生和演變,SPA開始流行,單頁面應(yīng)用的優(yōu)勢在于可以不重新加載整個頁面的情況下,通過ajax和服務(wù)器通信,實現(xiàn)整個Web應(yīng)用拒不更新...

    terasum 評論0 收藏0
  • Vue.js 服務(wù)渲染業(yè)務(wù)入門實踐

    摘要:說起,其實早在出現(xiàn)之前,網(wǎng)頁就是在服務(wù)端渲染的。沒有涉及流式渲染組件緩存對的服務(wù)端渲染有更深一步的認識,實際在生產(chǎn)環(huán)境中的應(yīng)用可能還需要考慮很多因素。選擇的服務(wù)端渲染方案,是情理之中的選擇,不是對新技術(shù)的盲目追捧,而是一切為了需要。 作者:威威(滬江前端開發(fā)工程師)本文原創(chuàng),轉(zhuǎn)載請注明作者及出處。 背景 最近, 產(chǎn)品同學(xué)一如往常笑嘻嘻的遞來需求文檔, 縱使內(nèi)心萬般拒絕, 身體倒是很誠實...

    miya 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<