我們昨天看完了 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 的,所以我就可以這麼做:
- 我知道 前端畫面 的路由需要什麼樣的資料。
- 遇到特定的 路由 我就先跟 API 拿資料。
- 這一包 資料 可以變化成 TDK, JSON-LD 與 餵給 Vue App 的資料。
- 然後用
index.php
把 TDK, JSON-LD 寫好。 - 之後把 要餵給 Vue App 的資料 直接吐成一包 JSON。
- 最後用
sessionStorage
把那個 JSON 存起來。 - 當 Vue App 初始化的時候,去
sessionStorage
拿餵給 Vuex。 - Vuex 若是在該頁有需要
fetch
的話,先校驗資料,若正確就不拿直接使用。 - 拿完就把
sessionStorage
的資料銷毀。
上面的 6. ~ 8. 有沒有 SSR 在做 serverPrefetch
跟 mounted
的感覺?其實概念是相同的,只是我們不用 SSR 的環境,而是使用自己比較習慣的方式去製作。那麼,這樣的方式會不會有什麼缺點?
- 若不支援
sessionStorage
就得換個方式。 - 要小心
sessionStorage
可能會被竄改。 - 後端 必須要知道 前端 的路由規則,兩邊需要自行維護。
畢竟資料都暴露在瀏覽器端,所以你必須小心資料有被竄改的可能性。當然啦,其實路由規則還是有方式可以從 後端 餵給 前端 ,只是這件事情必須要寫一些很奇怪的路由。其實我之前也說過類似的應用方式,要做到也不是非常困難。
本篇邪魔歪道,請小心服用 關於 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 而已。哦你問我為何昨天不放?
我昨天要是一開始就放,你會乖乖看完整篇文章嗎?不會嘛~