我們昨天看完了 SSR 的部分,有沒有覺得 我好興奮!我好興奮啊!(並沒有好嗎)。聊了那麼多前端的事情,我們今天來聊一下關於後端的部分。我之所以沒有採用 SSR,最主要還是因為我不太會寫 JavaScript,所以我還是繼續使用 PHP 比較妥當(欸)。

接下來的後端我大概都會用 PHP 當例子,當然你要換成其他的也可以。但是,如果你要用 NodeJS 的話,那你還是回去 SSR 好了(慢走不送)。


index.xxx 樣版

現在我們知道 Vue 的入口是使用 index.html 來當作入口,但,實際上也只是在 DOM 上面有一個 #app 然後把 App 給綁上去而已。就結果來說,只要我們能產生單純的 DOM,然後讓他可以綁定就可以了。所以,到底是用誰來產生這個樣版,就不是那麼重要了。

那麼,我們也可以用 index.php 來產生,那麼,這樣一來我們所擔心的 SEO 問題,也可以一併得到解決。但,為何 SSR 會這麼熱門呢?因為,同一份樣版邏輯的程式碼,你只要寫一次,若你要用其他的方式來產生,那你可能要寫兩次。

但是,我還是可以只寫一次就好,為什麼?

其實我們 SEO 只要能先應付機器人爬蟲先找得到資料,那麼頁面內容是不是可以很快的產生,就可以先不要那麼在意(對,我就不怎麼在意)。所以,我可以使用 PHP 把資料拋出去之後,在 index.php 當中,渲染好 TDK 與 JSON-LD 這些資料,那麼,基本上機器人來爬的時候,就可以先拿到正確的 TDK 跟完整的 JSON-LD 的資料。

至於為何我只寫一次?首先,我的資料都是跟 API 拿的,所以,我有一台 API 的伺服器專門在處理資料的事情,然後,我有一台負責處理 前端畫面 的機器,也是跑 PHP 的,所以我就可以這麼做:

  1. 我知道 前端畫面 的路由需要什麼樣的資料。
  2. 遇到特定的 路由 我就先跟 API 拿資料。
  3. 這一包 資料 可以變化成 TDK, JSON-LD 與 餵給 Vue App 的資料。
  4. 然後用 index.php 把 TDK, JSON-LD 寫好。
  5. 之後把 要餵給 Vue App 的資料 直接吐成一包 JSON。
  6. 最後用 sessionStorage 把那個 JSON 存起來。
  7. 當 Vue App 初始化的時候,去 sessionStorage 拿餵給 Vuex。
  8. Vuex 若是在該頁有需要 fetch 的話,先校驗資料,若正確就不拿直接使用。
  9. 拿完就把 sessionStorage 的資料銷毀。

上面的 6. ~ 8. 有沒有 SSR 在做 serverPrefetchmounted 的感覺?其實概念是相同的,只是我們不用 SSR 的環境,而是使用自己比較習慣的方式去製作。那麼,這樣的方式會不會有什麼缺點?

  1. 若不支援 sessionStorage 就得換個方式。
  2. 要小心 sessionStorage 可能會被竄改。
  3. 後端 必須要知道 前端 的路由規則,兩邊需要自行維護。

畢竟資料都暴露在瀏覽器端,所以你必須小心資料有被竄改的可能性。當然啦,其實路由規則還是有方式可以從 後端 餵給 前端 ,只是這件事情必須要寫一些很奇怪的路由。其實我之前也說過類似的應用方式,要做到也不是非常困難。

本篇邪魔歪道,請小心服用 關於 Functional Component 的邪門歪道,這樣的方法其實你的前端就只會有一組 Router,然後剩下的都是元件的組合伎倆。雖然說也是可以組合出很複雜的東西,不過這種東西維護上會比較困難。


現實世界的操作

雖然我手邊是有幾個 Docker 的操作檔案,不過因為每次寫完文章就沒整理,所以,我這邊直接上一些操作的程式碼讓大家理解一下。但,其實上一段我所提到的應該也不難理解了,只是不貼點程式碼好像就哪裡怪怪的。

由於我是寫 Phalcon 這個冷門框架,所以我就用這個來舉例:

<!DOCTYPE html>
<html lang="{{ lang }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, user-scalable=no">
        <meta name="HandledFriendly" content="True">
        <meta name="keywords" content="{{ keyword }}">
        <meta name="description" content="{{ description }}">
        <meta name="referrer" content="origin">
        <title>{{ title }}</title>
        {% for css in cssFiles %}
        <link rel="stylesheet" href="{{ css }}">
        {% endfor %}
        {{ moreHeaders }}
        <script>window._env = {{ jsEnv }};</script>
    </head>
    <body>
        <script type="application/ld+json">{{ jsonLD | json_encode }}</script>
        <div id="#app"></div>
        {% for js in jsFiles %}
        <script src="{{ js }}"></script>
        {% endfor %}
    </body>
</html>

這樣有沒有看到昨天 SSR 的錯覺?其實說起來就是差不多再做一樣的事情。所以我上面才會說,如果你後端也是寫 NodeJS 的話,那你還是回去 SSR 裡面打滾吧,會比較輕鬆一點。至於官方自己提到的非 Node 環境的運作方式 Usage in non-Node.js Environments ,不管你信不信, 我是不信啦!

Docker Hub, php-v8 就結果來看,最熱門的項目大概是 3 年前。我這麼說吧,PHP 跟 JavaScript 終究是兩種語言,硬是要這樣用也不是不行,只是效能上就不用我再多說。


我們用昨天提到的 Hello 來舉例,你的 後端 需要知道有一個叫做 /hello 的路徑。

你可能會覺得奇怪,為什麼後端要知道?不就把他全部拋給 index.php 然後等 Vue App 啟動後,就可以被 Router 抓到了嗎?為什麼我們還要在後端先處理?

原因在於,所謂的 前端渲染 這件事,代表著 無論我去到了什麼樣的頁面,HTTP State Code 都叫做 200 ,這樣有發現什麼端倪了嗎?是的,如果你的前端 做出了一個 Not Found 的頁面 ,請問你的瀏覽器會不會知道他是 404

瀏覽器不知道,所以, 搜尋引擎的爬蟲也不會知道。

這樣可以理解我們後端為何要 先處理 路由的事情了嗎?當我們進入 /hello 的時候,我們會先去看一下路由,有沒有叫做 /hello 的接口?如果沒有,將 HTTP State Code 設定為 404,然後直接返回前端。

如果路由存在,接著進入邏輯處理前,問一下 API ,確認 /hello 有我們要的資料。如果沒有,一樣就直接返回 404 。如果有,我們就開始準備 TDK, JSON-LD 以及需要餵給前端 Vuex 的資料。

$router->add('/hello', [
    'namespace' => 'IthomeIronman2019\\Controller\\Vue',
    'controller' => 'Hello',
    'action' => 'index',
])->setName('hello');

接著 Controller/Action 裡面就處理資料,例如跟 API 拿東西,組合 TDK 爾等。或許你會說,跟 API 拿好像又要浪費一次連線。歐,我的 API 是內網, 網內戶打免費 ,我又何必在僅止於處理前端資料的機器上面,去處理我的邏輯,你說是不是。

最後的 View 就是上一段,我所寫的那個部分。當然,你可以用 Layout 的模式去擴充,不過我們這邊就先不提那麼複雜的頁面結構,只要有基本的結構即可。然後你會發現一件事,我不管有多少 Controller, Action ,最終我的 View 只有一個。

對,如果只是簡單的頁面,你可以大家共用同一個 View 就好。由於你的畫面其實會全部交給 Vue App 去做渲染的動作,所以也不需要後端 MCV 那種模式,所有的 Controller/Action 都配同一個 View 就好了。

至於說你要有很複雜的頁面,我們在後面的文章絕對會有夠複雜的讓你看,現在,先有一個概念,就是我們可以這樣處理即可。


小結

今天一樣沒有太多程式碼,讓大家先從 SSR 的驚悚中恢復一下。明天,我們會繼續這一篇的概念,然後來說一下我目前比較常使用的,所謂的 48V 輕油電 混合模式 的操作方式。一方面使用習慣的語言,另一方面混合前後端。

而且不用踩 SSR 的雷。

想踩的人我做了一個 Vue CLI 3 SSR with Docker ,我包起來了理論上應該不會爆炸,但是他只是 Hello World 而已。哦你問我為何昨天不放?

我昨天要是一開始就放,你會乖乖看完整篇文章嗎?不會嘛~

ITHome 鐵人賽同步刊登 後端的 Template Layout Day 19