本文同步刊载于我的博客 「星星的筆記.Lucas」:lucas-yang.vercel.app/post/inertia...
构建 Laravel 全端网站我最爱用的 Inertia,现在终于也正式释出 SSR 的功能,补足了 SPA 网站会有的 SEO 问题了!
这一篇文章中会记录我在官方示例 PingCRM Vue 2/3 中加入 SSR 功能的过程,下面我们一起来看吧~
Inertia 中 SSR 的运作原理
首先先大概介绍一下运作原理,Inertia 要适配 PHP 或 Ruby 等不同语言,因此使用了 Node.js 来实作 SSR 服务。在 SSR 模式下接收到请求后会直接将 page 物件 传入 SSR Server 做解析,最后再回传 HTML 给浏览器,结束这一次的请求。
安装 PingCRM
为了能马上体验 SSR 功能,我直接用官方的 PingCRM 示例来实作 SSR 功能,安装方式使用了 README 的步骤,可以节省配置其他设定的时间。如果你已经熟悉 Laravel 的安装方式的话,执行这些步骤应该很容易~
下面看要使用哪个 Vue 版本的 PingCRM,直接照 README 的方式来安装:
不过 Vue 2 的 PingCRM 需要更新一下 resources/js/app.js
的 createInertiaApp()
设置部分:
...
createInertiaApp({
resolve: name => require(`./Pages/${name}`),
title: title => `${title} - Ping CRM`,
setup({ el, App, props, plugin }) {
Vue.use(plugin)
new Vue({
render: h => h(App, props),
}).$mount(el)
},
})
更新 Inertia
装好 PingCRM 后,需要更新 Inertia 到最新版,才会支援 SSR 功能。
更新 Laravel 端:
composer require inertiajs/inertia-laravel:^0.5.0
更新 Vue 端 (使用 NPM 或 Yarn):
# Vue 2
npm install @inertiajs/inertia@^0.11.0 @inertiajs/inertia-vue@^0.8.0
yarn add @inertiajs/inertia@^0.11.0 @inertiajs/inertia-vue@^0.8.0
# Vue 3
npm install @inertiajs/inertia@^0.11.0 @inertiajs/inertia-vue3@^0.6.0
yarn add @inertiajs/inertia@^0.11.0 @inertiajs/inertia-vue3@^0.6.0
设置 SSR 应用
然后就要开始配置 SSR 了,先要安装 Vue 官方提供的 SSR 套件:
# Vue 2
npm install vue-server-renderer
yarn add vue-server-renderer
# Vue 3
npm install @vue/server-renderer
yarn add @vue/server-renderer
然后要安装 Inertia 的 @inertiajs/server
套件,这个套件包含了一个 HTTP Server 来执行 SSR 渲染。虽然这个套件并不是非装不可,不过有装的话,倒是可以省去自己写 Server 的时间:
npm install @inertiajs/server
yarn add @inertiajs/server
之后新增一个 resources/js/ssr.js
档案:
touch resources/js/ssr.js
这个 ssr.js
档案跟 app.js
有点相似,但是个只会在服务端 (Node.js) 中执行的入口档案,基本上不影响服务端渲染的套件都可以不用载入,比如 @inertiajs/progress
套件不需要在服务端运行;而服务端/用户端两边都会载入的部分就需要做两边兼容,比如需要顾虑到 Node.js 中没有 window
和 document
的问题。
下面提供 resources/js/ssr.js
的 Vue 2/3 示例:
Vue 2:
import Vue from 'vue'
import { createRenderer } from 'vue-server-renderer'
import { createInertiaApp } from '@inertiajs/inertia-vue'
import createServer from '@inertiajs/server'
createServer((page) => createInertiaApp({
page,
render: createRenderer().renderToString,
resolve: name => require(`./Pages/${name}`),
setup({ app, props, plugin }) {
Vue.use(plugin)
return new Vue({
render: h => h(app, props),
})
},
}))
Vue 3:
import { createSSRApp, h } from 'vue'
import { renderToString } from '@vue/server-renderer'
import { createInertiaApp } from '@inertiajs/inertia-vue3'
import createServer from '@inertiajs/server'
createServer((page) => createInertiaApp({
page,
render: renderToString,
resolve: name => require(`./Pages/${name}`),
setup({ app, props, plugin }) {
return createSSRApp({
render: () => h(app, props),
}).use(plugin)
},
}))
设置 Laravel Mix
然后就进到 Laravel Mix 的编译部分了,这里需要用到 webpack-node-externals
套件:
npm install webpack-node-externals
yarn add webpack-node-externals
开一个 webpack.ssr.mix.js
档案来编译 SSR 的部分,因为也是只需要在服务端运行,所以只编译 JS,不需要管 CSS 了。在这里比较需要注意的是,webpackConfig()
里的 target
必须要设为 node
,externals
需要引入 [webpackNodeExternals()]
,还有有使用到的 alias
也必须都要设定:
const path = require('path')
const mix = require('laravel-mix')
const webpackNodeExternals = require('webpack-node-externals')
mix
.options({ manifest: false })
.js('resources/js/ssr.js', 'public/js')
.vue({ options: { optimizeSSR: true } })
.alias({ '@': path.resolve('resources/js') })
.webpackConfig({
target: 'node',
externals: [webpackNodeExternals()],
})
并执行 Mix 编译 JS。注意这里就有两个部分要执行,一个是原本给用户端的,另一个是刚刚建立给 SSR 的,一旦应用中有任何修改,都必须要执行这两个编译指令:
npx mix
npx mix --mix-config=webpack.ssr.mix.js
如果要编译 production
的版本,可以把以下设定更新到 package.json
的 scripts
中,之后就可以执行 npm run prod
了:
"prod": "mix --production && mix --production --mix-config=webpack.ssr.mix.js",
在 Laravel 中启用 SSR
开启 app.blade.php
,也就是 Inertia 项目的进入点,把 @inertiaHead
加到 <head>
的底部:
<!DOCTYPE html>
<html>
<head>
...
@inertiaHead
</head>
<body>
@inertia
</body>
</html>
好了之后就可以来启用 SSR 功能了!先发布 inertia.php
设定档到项目中:
php artisan vendor:publish --provider="Inertia\ServiceProvider"
然后会看到以下设定,把 ssr
的 enabled
设定改成 true
就可以了:
<?php
return [
/*
|--------------------------------------------------------------------------
| Server Side Rendering
|--------------------------------------------------------------------------
|
| These options configures if and how Inertia uses Server Side Rendering
| to pre-render the initial visits made to your application's pages.
|
| Do note that enabling these options will NOT automatically make SSR work,
| as a separate rendering service needs to be available. To learn more,
| please visit https://inertiajs.com/server-side-rendering
|
*/
'ssr' => [
'enabled' => true,
'url' => 'http://127.0.0.1:13714/render',
],
// ...
执行 SSR 应用
当以上步骤都做完之后,现在就可以来启动 SSR 网站了。如果照着 PingCRM 的步骤安装的话,现在需要启动基本的 Artisan Serve:
php artisan serve
然后启动 SSR 服务:
node public/js/ssr.js
现在就可以开浏览器看看啦~ 正常的话直接看是没有差别的,这时候打开网页源代码,你就会看到差别所在。
首先是没有 SSR,单纯的用户端渲染,内容很单纯:
然后是启用 SSR 的服务端渲染,就会看到已经渲染好的 <title>
以及 <body>
中的 HTML 了:
可以把浏览器左上的 自动换行 打开
这里稍微提一下 Vue 的 hydration 操作,在 SSR 模式中因为已经把 HTML 渲染好了,Vue 接手的时候就不需要再重新渲染一遍,而是使用 hydration 的方式,直接把静态的 HTML 转换成可以与 Vue 互动的动态 DOM。但有个前提是 服务端 生成的 DOM 和 用户端 的必须保持一致,如果遇到不同的话会直接中断 hydration,不管原本的 DOM,直接重新渲染新的。想要了解详细的部分可以参考 Vue 2 / Vue 3 的 hydration 注意事项。
结语
其实在去年 (2021) Inertia 的 SSR 功能早期释出时我就已经体验过了,不过当时还没有把功能包进套件中,使用起来比较麻烦一些,现在正式释出的版本终于可以比较容易使用了。
在还没出 SSR 功能之前,我都把 Inertia 定位成只能做后台操作的部分,需要 SEO 的页面就只能用 blade 来顶替。SSR 功能的出现终于可以做完整的网页功能了。可以把 Laravel 和 Vue 的功能发挥到极致,这就是我喜欢 Inertia 的原因~ 期望之后 Inertia 可以完善更多功能,让用 Laravel + Vue 来建立 Modern SPA 网站变得比较轻松~
参考资料
本文同步刊载于我的博客 「星星的筆記.Lucas」:lucas-yang.vercel.app/post/inertia...