Inertia.js SSR | 让 Laravel x Vue 实现服务端渲染

本文同步刊载于我的博客 「星星的筆記.Lucas」:star-note-lucas.vercel.app/posts/i...

2023/03/04 更新:将 Inertia 的使用方式更新到 v1 版,以及把 Laravel Mix 换成 Vite。

构建 Laravel 全端网站我最爱用的 Inertia,现在终于也正式释出 SSR 的功能,补足了 SPA 网站会有的 SEO 问题了!

这一篇文章中会记录在在一个现成的 Inertia 专案中加入 SSR 功能的过程,下面我们一起来看吧~

Inertia 中 SSR 的运作原理

先大概介绍一下运作原理,因为 Inertia 要适配 PHP 或 Ruby 等不同语言,使用了 Node.js 来实作 SSR 服务。在 SSR 模式下接收到请求后会直接将 page 物件 传入 SSR Server 做解析,最后再回传 HTML 给浏览器,结束这一次的请求。

更新 Inertia

首先需要一个现成的 Inertia 专案,在现在新版 Laravel 预设都使用 Vite 来打包前端,因此我下面都会用 Vite 的配置。还有要更新 Inertia 到最新版才会支援 SSR 功能。

安装 Laravel 端到最新版:

composer require inertiajs/inertia-laravel

安装 Vue 端 (使用 NPM 或 Yarn):

npm install @inertiajs/vue3
yarn add @inertiajs/vue3

如果你的专案里还有 @inertiajs/inertia@inertiajs/inertia-vue3 的套件的话,表示还是在旧版,可以参考 升级指南 升级,基本上都是改改套件名字、变数名字等,升级难度应该不会太大。 :

设置 SSR 应用

现在就要开始配置 SSR 了,如果是在 Vue 3 新版可以不需要安装官方的 SSR 套件 @vue/server-renderer,因为 Vue 核心套件会连带安装了,可以改用 vue/server-renderer 来使用。

同样 Inertia v1 版也会一起安装 @inertiajs/server 套件,里面包含了一个执行 SSR 渲染的 HTTP Server。

之后新增一个 resources/js/ssr.js 档案:

touch resources/js/ssr.js

这个 ssr.js 档案跟 app.js 有点相似,但是个只会在服务端 (Node.js) 中执行的入口档案,基本上不影响服务端渲染的套件都可以不用载入。而服务端/用户端两边都会载入的部分就需要做两边兼容,比如需要顾虑到 Node.js 中没有 windowdocument 的问题。

下面提供 resources/js/ssr.js 的范例:部分就需要做两边兼容,比如需要顾虑到 Node.js 中没有 windowdocument 的问题。

import { createSSRApp, h } from 'vue'
import { renderToString } from 'vue/server-renderer'
import { createInertiaApp } from '@inertiajs/vue3'
import createServer from '@inertiajs/vue3/server'
createServer(page =>
  createInertiaApp({
    page,
    render: renderToString,
    resolve: name => {
      const pages = import.meta.glob('./Pages/**/*.vue', { eager: true })
      return pages[`./Pages/${name}.vue`]
    },
    setup({ App, props, plugin }) {
      return createSSRApp({
        render: () => h(App, props),
      }).use(plugin)
    },
  })
)

这里稍微提一下 Vue 的 hydration 操作,在 SSR 模式中因为已经把 HTML 渲染好了,Vue 接手的时候就不需要再重新渲染一遍,而是使用 hydration 的方式,直接把静态的 HTML 转换成可以与 Vue 互动的动态 DOM。但有个前提是 服务端 生成的 DOM 和 用户端 的必须保持一致,如果遇到不同的话会直接中断 hydration,不管原本的 DOM,直接重新渲染新的。

在 Vue 3 要启用 hydration 时,要将 app.js 里的 createApp 替换成 createSSRApp

import { createSSRApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'
createInertiaApp({
  resolve: name => {
    const pages = import.meta.glob('./Pages/**/*.vue', { eager: true })
    return pages[`./Pages/${name}.vue`]
  },
  setup({ el, App, props, plugin }) {
    createSSRApp({ render: () => h(App, props) })
      .use(plugin)
      .mount(el)
  },
})

Hydration 相关更详细的部分可以参考 Vue 的 hydration 注意事项

设置 Vite

然后就进到 Vite 的编译部分了,这里需要在 vite.config.js 里增加刚才的 ssr.js 路径:

export default defineConfig({
  plugins: [
    Laravel({
      input: ['resources/css/app.css', 'resources/js/app.js'],
      ssr: 'resources/js/ssr.js',
      refresh: true,
    }),
    // ...
  ],
})

现在就可以执行 Vite 编译 JS。原本的 vite build 是编译用户端的,所以再加一个 vite build --ssr 用于编译 SSR 模式的指令:

  "scripts": {
    "dev": "vite",
    "build": "vite build && vite build --ssr"
  },

之后只要执行就可以同时编译用户端跟 SSR 了:

npm run build

在 Laravel 中启用 SSR

开启 app.blade.php,也就是 Inertia 项目的进入点,把 @inertiaHead 加到 <head> 的底部:

<!DOCTYPE html>
<html>
  <head>
    ...
    @inertiaHead
  </head>
  <body>
    @inertia
  </body>
</html>

好了之后就可以来启用 SSR 功能了!先发布 config/inertia.php 设定档到项目中:

php artisan vendor:publish --provider="Inertia\ServiceProvider"

然后会看到以下设定,基本上可以先用预设的,如果要改设定可以在这边改。 enabled 是一个单纯的开关,url 是 SSR server 的网址,bundle 则是 vite build --ssr 产生的档案的位置:

<?php

return [

    'ssr' => [

        'enabled' => true,

        'url' => 'http://127.0.0.1:13714',

        // 'bundle' => base_path('bootstrap/ssr/ssr.mjs'),

    ],

];

毕竟编译出来的档案不要送进版控,当然要加到 .gitignore 里:

/bootstrap/ssr
/node_modules
...

执行 SSR 应用

当以上步骤都做完之后,现在就可以来启动 SSR 网站了,启动基本的 Artisan Serve:

php artisan serve

然后启动 SSR 服务:

php artisan inertia:start-ssr

如果在正式环境下,更新完程式码会需要手动关闭可以执行:

php artisan inertia:stop-ssr

现在就可以开浏览器看看啦~ 正常的话直接看是没有差别的,这时候打开网页源代码,你就会看到差别所在。

首先是没有 SSR,单纯的用户端渲染,内容很单纯:

Inertia 用户端渲染源代码

然后是启用 SSR 的服务端渲染,就会看到已经渲染好的 <title> 以及 <body> 中的 HTML 了:

可以把浏览器左上的 自动换行 打开

Inertia 服务端渲染源代码

结语

其实在 2021 年 Inertia 的 SSR 功能早期释出时我就已经体验过了,不过当时还没有把功能包进套件中,使用起来比较麻烦一些,现在正式释出的版本终于可以比较容易使用了。

在还没出 SSR 功能之前,我都把 Inertia 定位成只能做后台操作的部分,需要 SEO 的页面就只能用 blade 来顶替。SSR 功能的出现终于可以做完整的网页功能了。可以把 Laravel 和 Vue 的功能发挥到极致,这就是我喜欢 Inertia 的原因~ 期望之后 Inertia 可以完善更多功能,让用 Laravel + Vue 来建立 Modern SPA 网站变得比较轻松~

参考资料

本文同步刊载于我的博客 「星星的筆記.Lucas」:star-note-lucas.vercel.app/posts/i...

本作品采用《CC 协议》,转载必须注明作者和本文链接
即使是一颗小星星,也会闪耀着光芒~~
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 3

都用上 blade 了,为什么还要用 js 做服务端渲染,直接模板变量不就可以了嘛

2年前 评论
sanyuan

楼主遇到过这个问题吗 Hydration completed but contains mismatches.

7个月前 评论
ycs77_Lucas (楼主) 1个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!