多页面应用程序(MPA)正变得越来越不流行。著名的平台,如Facebook、Twitter、YouTube、Github和其他许多平台已经在使用单页应用程序(SPA)技术来代替。
这种时髦的技术允许用户快速地参与到网络应用中去,并且反应迅速,因为一切都在客户端渲染。然而,对于使用Laravel或Django等框架构建服务器端渲染的应用程序的开发者来说,这可能是一种痛苦。
幸运的是,Inertia.js插手进来,拯救了我们。
在这篇文章中,我们将展示如何将Inertia.js与Laravel、Vue.js和Tailwind CSS结合在一起使用,以创建一个现代博客网络应用。我们还将分享如何使SPA更有利于SEO,以及其他一些小技巧。
如果你刚刚开始使用Laravel,我们建议你先阅读这篇文章,这样你就可以准备好了。
- 为什么是SPA?
- 为什么选择Inertia?
- Inertia如何工作
- 开始使用Inertia
- 创建Inertia页面
- Laravel路由和Inertia渲染
- Tailwind CSS与Inertia.js整合
- Inertia链接
- Laravel集成Inertia技巧和窍门
- SEO技巧
为什么是SPA?
在我们问为什么要使用Inertia之前,我们必须先问:”为什么是SPA?”
为什么会有人喜欢客户端渲染的应用程序而不是传统的服务器端应用程序? 是什么迫使一个全栈式的Laravel开发者告别了blade组件?
简短的回答:因为速度和响应性是任何成功的用户参与的关键。
在MPA的情况下, 浏览器不断向后端发送请求, 然后执行大量的数据库查询。在数据库和服务器处理查询并将其传递给浏览器之后,页面就被渲染了。
但SPA是不同的。应用程序将用户需要的一切直接带到页面上,消除了浏览器发送查询或重新加载页面以呈现新的HTML元素的需要。
由于这种独一无二的用户体验,许多大牌公司都吵着要把他们的网站变成单页应用程序。
也就是说, 创建一个单页面应用程序对Laravel开发人员来说是很困难的, 因为这需要他们开始使用Vue.js或React而不是blade模板, 导致许多Laravel宝石的损失, 以节省时间和精力.
不过,现在我们有了Inertia.js,这一切都改变了。
为什么选择Inertia?
如果Laravel开发者要在Inertia之前用Vue构建Web SPAs, 他们必须用Laravel设置API并返回JSON数据, 然后用类似AXIOS的东西来检索Vue组件中的数据. 他们还需要像Vue Router这样的东西来管理路由,这将意味着失去Laravel的路由,以及中间件和控制器。
另一方面, Inertia.js, 使得开发者能够使用经典的服务器端路由和控制器来构建现代的单页Vue, React和Svelte应用程序。Inertia是为Laravel、Ruby on Rails和Django开发者设计的,允许他们在不改变创建控制器、从数据库获取数据和渲染视图的编码技术的情况下构建应用程序。
多亏了Inertia.js,Laravel开发者会感到宾至如归。
Inertia如何工作
只用Laravel和Vue构建SPA会给你的前端提供一个完整的JavaScript页面,但这并不能为你提供一个单页面的应用体验。每一个点击的链接都会导致你的客户端框架在下一个页面加载时重新启动。
这就是Inertia进入画面的地方。
Inertia基本上是一个客户端路由库。它允许你在页面之间进行导航,而不需要重新加载整个页面。这是通过 <Link>
组件实现的,它是一个围绕标准锚标签的轻量级包装。
当你点击一个Inertia链接时,Inertia会拦截该点击并将你重定向到XHR。浏览器不会以这种方式重新加载页面,给用户一个完整的单页体验。
开始使用Inertia
一个用Inertia.js制作的示例页面
为了了解Inertia以及如何将其与Laravel集成,我们将使用最强大的组合建立一个博客网络应用,Laravel用于后端,Vue.js用于JavaScript前端,Tailwind CSS用于风格设计。
如果你愿意在本地环境下学习这个教程,你可以使用DevKinsta,这是一个为开发者、设计师和机构提供的强大工具,使他们能够构建单页和多页的WordPress网络应用。幸运的是,WordPress可以使用Corcel包轻松地与Laravel集成。
前提条件
要从本教程中获得最大的收获, 你应该熟悉以下内容:
- Laravel基础知识(安装,数据库,数据库迁移,Eloquent模型,控制器,和路由)
- Vue.js基础知识(安装, 结构, 和表单)
如果你觉得不确定,可以看看这些精彩的Laravel免费和付费教程。否则,让我们开始吧。
第1步:安装核心元素
为了专注于Inertia.js并直接进入有趣的部分, 请确保你已经准备好以下设置:
- 新安装的Laravel 9项目名为
blog
- 在我们的Laravel项目中安装了Tailwind CSS CLI
- blog/resources/views中的两个blade组件,用于查看博客的主页和博客上的一篇文章,如下图所示:
“/resources/views/index.blade.php“:<!DOCTYPE html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>博客 - 闪电博</title> </head> <body> <header> <h1>博客 - 闪电博</h1> </header> <main> <h2>Read our latest articles</h2> <section> <article> <div> <img src="/images/wbolt-logo.png" alt="Article thumbnail" /> </div> <h3>Title for the blog</h3> <p> Lorem, ipsum dolor sit amet consectetur adipisicing elit. Illum rem itaque error vel perferendis aliquam numquam dignissimos, expedita perspiciatis consectetur! </p> <a href="#">Read more</a> </article> </section> </main> <footer> <h2>Join our Newsletter</h2> <input type="email" /> </footer> </body> </html>
“/resources/views/show.blade.php“:
<!DOCTYPE html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>博客 - 闪电博</title> </head> <body> <main> <article> <div> <img src="/images/wbolt-logo.png" alt="Article thumbnail" /> </div> <h1>Title for the blog</h1> <p>Article content goes here</p> </article> </main> <footer> <h2>Join our Newsletter</h2> <input type="email" /> </footer> </body> </html>
- 名为
blog
的MySQL本地数据库连接到我们的项目:”.env“:DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=blog DB_USERNAME=root DB_PASSWORD=
- 文章模型、迁移和工厂:”app/Models/Article.php“。
<?php namespace AppModels; use IlluminateDatabaseEloquentFactoriesHasFactory; use IlluminateDatabaseEloquentModel; class Article extends Model { use HasFactory; protected $fillable = ['title', 'excerpt', 'body']; }
“database/migrations/create_articles_table.php“:
<?php use IlluminateDatabaseMigrationsMigration; use IlluminateDatabaseSchemaBlueprint; use IlluminateSupportFacadesSchema; return new class extends Migration { public function up() { Schema::create('articles', function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('excerpt'); $table->text('body'); $table->timestamps(); }); } public function down() { Schema::dropIfExists('articles'); } };
“database/factories/ArticleFactory.php“:
<?php namespace DatabaseFactories; use IlluminateDatabaseEloquentFactoriesFactory; class ArticleFactory extends Factory { public function definition() { return [ 'title' => $this->faker->sentence(6), 'excerpt' => $this->faker->paragraph(4), 'body' => $this->faker->paragraph(15), ]; } }
这就是我们需要开始工作的全部内容! 现在让我们进入正题,将Inertia.js引入我们的项目。
第2步:安装Inertia
Inertia的安装过程分为两个主要阶段: 服务器端(Laravel)和客户端(VueJs).
Inertia文档中的官方安装指南有点过时了,因为Laravel 9现在默认使用Vite,但我们也会去看看。
1. Server-Side
我们需要做的第一件事是通过Composer用以下终端命令安装Inertia服务器端适配器。
composer require inertiajs/inertia-laravel
现在我们将设置我们的根模板, 这将是一个单一的blade文件,将用于加载你的CSS和JS文件,以及一个Inertia根,将用于启动我们的JavaScript应用程序。
因为我们使用的是最新版本的Laravel 9 v9.3.1, 我们还必须让Vite发挥它的魔力,在/resources/views/app.blade.php的标签中包含它:
<!DOCTYPE html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <!-- Fetch project name dynamically --> <title inertia>{{ config('app.name', 'Laravel') }}</title> <!-- Scripts --> @vite('resources/js/app.js') @inertiaHead </head> <body class="font-sans antialiased"> @inertia </body> </html>
请注意我们是如何通过在 <title>
标签中添加 Inertia
属性来动态地获取项目标题的。
我们还在头部添加了 @vite
指令,以便让Vite知道我们创建应用程序和导入CSS的JavaScript主文件的路径。Vite是一个帮助JavaScript和CSS开发的工具,它允许开发人员在本地开发过程中查看前端的变化而不必刷新页面。
我们的下一步将是创建HandleInertiaRequests中间件并将其发布到我们的项目中。我们可以通过在我们项目的根目录下发射下面的终端命令来实现。
php artisan inertia:middleware
一旦完成,前往 “App/Http/Kernel “并注册 HandleInertiaRequests
作为你的网络中间件的最后一项:
'web' => [ // ... AppHttpMiddlewareHandleInertiaRequests::class, ],
2. Client-Side
接下来,我们必须以与服务器端相同的方式安装我们的前端Vue.js 3依赖项:
npm install @inertiajs/inertia @inertiajs/inertia-vue3 // or yarn add @inertiajs/inertia @inertiajs/inertia-vue3
接下来,你需要拉入Vue.js 3:
npm install vue@next
然后更新你的主要JavaScript文件,用Vue.js 3、Vite和Laravel初始化Inertia.js:
“resources/js/app.js“:
import "./bootstrap"; import "../css/app.css"; import { createApp, h } from "vue"; import { createInertiaApp } from "@inertiajs/inertia-vue3"; import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers"; createInertiaApp({ title: (title) => `${title} - ${appName}`, resolve: (name) => resolvePageComponent( `./Pages/${name}.vue`, import.meta.glob("./Pages/**/*.vue") ), setup({ el, app, props, plugin }) { return createApp({ render: () => h(app, props) }) .use(plugin) .mount(el); }, });
在上面的代码片段中, 我们使用Laravel的插件 resolvePageComponent
,并告诉它从目录./Pages/$name.vue中解析我们的组件。这是因为我们将在以后的项目中把我们的Inertia组件保存在这个目录中,而这个插件将协助我们从正确的目录中自动加载这些组件。
剩下的就是安装 vitejs/plugin-vue
:
npm i @vitejs/plugin-vue
并更新 vite.config.js 文件:
import { defineConfig } from "vite"; import laravel from "laravel-vite-plugin"; import vue from "@vitejs/plugin-vue"; export default defineConfig({ plugins: [ laravel({ input: ["resources/css/app.css", "resources/js/app.js"], refresh: true, }), vue({ template: { transformAssetUrls: { base: null, includeAbsolute: false, }, }, }), ], });
最后一步是安装我们的依赖项并编译我们的文件。
npm install npm run dev
然后就可以了! 你已经有了一个使用Vue.js 3和Vite的Laravel 9应用程序。现在,我们需要看到一些实际发生的情况!
创建Inertia页面
你还记得那两个用于查看我们的主页和一篇文章的blade文件(index和show)吗?
在使用 Inertia 时,我们唯一需要的blade文件是 app.blade.php,我们在安装 Inertia 时已经用过一次。那么,现在这些文件会怎样呢?
我们将把这些文件从blade组件转化为 Inertia.js 组件。
你的应用程序中的每个页面都有自己的控制器和Inertia的JavaScript组件。这让你只获得该页面所需的数据,而无需使用API。Inertia页面只不过是JavaScript组件,在我们的例子中,它们是Vue.js组件。它们并没有什么特别值得注意的地方。所以我们要做的是在 <template>
标签之间包裹所有的HTML内容,任何与JavaScript有关的内容都将用 <script>
标签来包裹。
创建一个名为 “Pages “的文件夹,将你的文件移到那里。因此,我们将把 “index.blade.php“和 “show.blade.php“放在”./resources/js/Pages“。然后我们将改变文件格式为”.vue”,而不是”.blade.php”,同时使它们名字的第一个字母大写,并将其内容变成一个标准的Vue.js组件。我们将排除 <html>
, <head>
和 <body>
标签,因为它们已经包含在主根blade组件中。
“resources/js/Pages/Index.vue“:
<script setup> // </script> <template> <header> <h1>博客 - 闪电博</h1> </header> <main> <h2>Read our latest articles</h2> <section> <article> <div> <img src="/images/wbolt-logo.png" alt="Article thumbnail" /> </div> <h3>Title for the blog</h3> <p> Lorem, ipsum dolor sit amet consectetur adipisicing elit. Illum rem itaque error vel perferendis aliquam numquam dignissimos, expedita perspiciatis consectetur! </p> <a href="#">Read more</a> </article> </section> </main> <footer> <h2>Join our Newsletter</h2> <input type="email" /> </footer> </template>
“resources/js/Pages/Show.vue“:
<script setup> // </script> <template> <header> <h1>欢迎来到闪电博的博客频道</h1> </header> <main> <article> <h1>Title for the blog</h1> <p>Article content goes here</p> </article> </main> <footer> <h2>Join our Newsletter</h2> <input type="email" /> </footer> </template>
有一件事真的让我很困扰!我们一直在每个组件中复制和粘贴我们的页眉和页脚,这不是很好的做法。我们不断地在每个组件中复制和粘贴我们的页眉和页脚,这并不是一个很好的做法。让我们创建一个Inertia基本布局来存储我们的持久化组件。
在”/resources/js“中创建一个名为 “Layouts “的文件夹,在该文件夹中创建一个名为 “WboltLayout.vue “的文件。这个文件将有我们的页眉和页脚,以及带有 <slot />
的 main
,以允许所有用这个布局包装的组件嵌入其中。这个文件应该看起来像这样:
“resources/js/Layouts/WboltLayout.vue“:
<script setup></script> <template> <header> <h1>博客 - 闪电博</h1> </header> <main> <slot /> </main> <footer> <h2>Join our Newsletter</h2> <input type="email" /> </footer> </template>
然后,我们将在我们的页面中导入这个新的布局,并将所有的HTML内容都包在其中。我们的组件应该看起来像这样:
Index.vue:
<script setup> import WboltLayout from "../Layouts/WboltLayout.vue"; </script> <template> <WboltLayout> <section> <h2>Read our latest articles</h2> <article> <div> <img src="/images/wbolt-logo.png" alt="Article thumbnail" /> </div> <h3>Title for the blog</h3> <p> Lorem, ipsum dolor sit amet consectetur adipisicing elit. Illum rem itaque error vel perferendis aliquam numquam dignissimos, expedita perspiciatis consectetur! </p> <a href="#">Read more</a> </article> </section> </WboltLayout> </template>
Show.vue:
<script setup> import WboltLayout from "../Layouts/WboltLayout.vue"; </script> <template> <WboltLayout> <article> <h1>Title for the blog</h1> <p>Article content goes here</p> </article> </WboltLayout> </template>
Laravel路由和惯性渲染
首先,让我们使用我们的教程起点中的 “ArticleFactory“文件,将一些文章播种到我们的数据库。
“database/seeders/databaseSeeder.php“:
<?php namespace DatabaseSeeders; use AppModelsArticle; use IlluminateDatabaseSeeder; class DatabaseSeeder extends Seeder { public function run() { Article::factory(10)->create(); } }
然后点击下面的终端命令,迁移你的表,并从工厂中播种假数据:
php artisan migrate:fresh --seed
这将在数据库中创建10个假的文章, 我们将需要使用Laravel路由来传递给我们的视图。现在我们使用Inertia来渲染视图, 我们过去写路由的方式将略有改变。让我们在 “routes/web.php“中创建我们的第一个Laravel Inertia路由,并从”/resources/js/Pages/Index.vue“返回主页视图。
“routes/web.php“:
<?php use AppModelsArticle; use IlluminateSupportFacadesRoute; use InertiaInertia; Route::get('/', function () { return Inertia::render('Index', [ 'articles' => Article::latest()->get() ]); })->name('home');
注意,我们导入了Inertia,没有使用Laravel的view()助手来返回视图,而是使用了 Inertia::render
。Inertia也会默认寻找我们在Pages文件夹下 “resources/js “中提到的文件名。
前往索引文件,并将检索到的数据设置为道具,用 v-for
在它们上面循环,以显示结果。在脚本标签之间,将传递的数据定义为一个道具。Inertia需要知道的是你所期望的数据类型,在我们的例子中是一个包含文章数组的 “Article”对象。
“resources/js/Pages/Index.vue“:
<script setup> import WboltLayout from "../Layouts/WboltLayout.vue"; defineProps({ Articles: Object, }); </script>
注意,只把它定义为一个道具而不返回就足够了,因为我们使用的是Vue.js 3 composition API的 setup
格式。如果我们使用的是选项API,那么我们就需要返回它。
让我们来做这个循环:
<template> <WboltLayout> <h2>Read our latest articles</h2> <section> // Looping over articles <article v-for="article in articles":key="article.id"> <div> <img src="/images/wbolt-logo.png" alt="Article thumbnail" /> </div> <h3>{{article.title}}</h3> <p>{{article.excerpt}}</p> <a href="#">Read more</a> </article> </section> </WboltLayout> </template>
npm run dev
(让它运行,因为我们使用的是Vite)和 php artisan serve
来启动laravel开发服务器,访问我们的网站,我们会看到预期的页面,显示数据库中的所有十篇文章。
现在, 我们正在使用谷歌浏览器的Vue DevTools扩展, 它允许我们调试我的应用程序。让我们来看看我们的数据是如何被传递给组件的。
检查Inertia的属性
“Articles”作为一个道具对象传递给组件, 包含一个文章数组; 数组中的每个文章也是一个对象, 其属性与它从数据库获得的数据相对应. 这意味着我们从Laravel传输到Inertia的任何数据都将被视为一个道具。
Tailwind CSS与Inertia.js整合
由于Tailwind在开始时已经安装在我们的项目中,我们所需要做的就是告诉它读取我们的Inertia组件。这可以通过编辑 “tailwind.config.js“来实现,如下所示:
/** @type {import('tailwindcss').Config} */ module.exports = { content: [ "./storage/framework/views/*.php", "./resources/views/**/*.blade.php", "./resources/js/**/*.vue", ], theme: { extend: {}, }, plugins: [], };
然后确保我们已经在 “resources/js/app.js“中导入我们的CSS文件:
import "../css/app.css";
现在我们准备为我们的组件设置样式。
“resources/js/Pages/Index.vue“:
<script setup> import WboltLayout from "../Layouts/WboltLayout.vue"; defineProps({ articles: Object, }); </script> <template> <WboltLayout> <h2 class="text-2xl font-bold py-10">Read our latest articles</h2> <section class="space-y-5 border-b-2 pb-10"> <article v-for="article in articles" :key="article.id" class="flex justify-center items-center shadow-md bg-white rounded-xl p-4 mx-auto max-w-3xl" > <img src="/images/wbolt-logo.png" class="w-32 h-32 rounded-xl object-cover" alt="" /> <div class="flex flex-col text-left justify-between pl-3 space-y-5"> <h3 class="text-xl font-semibold text-indigo-600 hover:text-indigo-800" > <a href="#">{{ article.title }}</a> </h3> <p> {{ article.excerpt }} </p> <a href="#" class="text-indigo-600 hover:text-indigo-800 w-fit self-end font-semibold" >Read more</a > </div> </article> </section> </WboltLayout> </template>
“resources/js/Layouts/WboltLayout.vue“:
<script setup></script> <template> <Header class="bg-gradient-to-r from-blue-700 via-indigo-700 to-blue-700 w-full text-center py-4" > <h1 class="text-white font-bold text-4xl">博客 - 闪电博</h1> </Header> <main class="container mx-auto text-center"> <slot /> </main> <footer class="bg-gradient-to-b from-transparent to-gray-300 w-full text-center mt-5 py-10 mx-auto" > <h2 class="font-bold text-xl pb-5">Join our Newsletter</h2> <input class="rounded-xl w-80 h-12 px-3 py-2 shadow-md" type="email" placeholder="Write your email.." /> </footer> </template>
如果你看一下浏览器,你会发现Vite已经用Tailwind magic更新了页面。
渲染的Inertia属性
Inertia链接
现在我们有了一个可以显示数据库中所有文章的工作主页,我们需要创建另一个路由来显示个别文章。让我们创建一个新的路由,并将URL设置为 “id “通配符:
“routes/web.php”
<?php use AppModelsArticle; use IlluminateSupportFacadesRoute; use InertiaInertia; Route::get('/', function () { return Inertia::render('Index', [ 'articles' => Article::latest()->get() ]); })->name('home'); Route::get('/posts/{article:id}', function (Article $article) { return Inertia::render('Show', [ 'article' => $article ]); })->name('article.show');
我们导入了“Article”模型,并添加了一条新路线以返回Inertia组件Show.vue。我们还利用了Laravel的路由模型绑定,它允许Laravel自动获取我们所指的文章。
我们现在需要的是一种方法,通过点击主页上的链接来访问这条路线,而无需重新加载整个页面。这可以通过Inertia的神奇工具 <Link>
实现。我们在介绍中提到,Inertia使用 <Link>
作为标准锚标记 <a>
的包装器,该包装器旨在使页面访问尽可能无缝。在Inertia中,<Link>
标记可以充当执行 <GET>
请求的锚标记,但它也可以同时充当 <button>
和 <form>
。让我们看看如何将其应用于我们的项目。
在我们的Index.vue,我们将从Inertia导入 <Link>
,并移除锚标记 <a>
并将其替换为Inertia的 <Link>
标记。href
属性将设置为我们之前为查看文章而创建的路由URL:
<script setup> import WboltLayout from "../Layouts/WboltLayout.vue"; import { Link } from "@inertiajs/inertia-vue3"; defineProps({ articles: Object, }); </script> <template> <WboltLayout> <section class="space-y-5 border-b-2 pb-10"> <h2 class="text-2xl font-bold pt-10 mx-auto text-center"> Read our latest articles </h2> <article v-for="article in articles" :key="article.id" class="flex justify-center items-center shadow-md bg-white rounded-xl p-4 mx-auto max-w-3xl" > <img src="/images/wbolt-logo.png" class="w-32 h-32 rounded-xl object-cover" alt="" /> <div class="flex flex-col text-left justify-between pl-3 space-y-5" > <h3 class="text-xl font-semibold text-indigo-600 hover:text-indigo-800" > <Link :href="'/posts/' + article.id">{{ article.title }}</Link> </h3> <p> {{ article.excerpt }} </p> <Link :href="'/posts/' + article.id" class="text-indigo-600 hover:text-indigo-800 w-fit self-end font-semibold" >Read more </Link> </div> </article> </section> </WboltLayout> </template>
让我们使用Tailwind对Show.vue执行样式化,使其看起来更加整洁,为我们的访问做好了准备。我们还需要让它知道,它应该期待一个“Article”对象,并将其设置为道具:
<script setup> import WboltLayout from "../Layouts/WboltLayout.vue"; defineProps({ article: Object, }); </script> <template> <WboltLayout> <article class="mx-auto mt-10 flex justify-center max-w-5xl border-b-2"> <img src="/images/wbolt-logo.png" class="w-80 h-80 rounded-xl mx-auto py-5" alt="" /> <div class="text-left flex flex-col pt-5 pb-10 px-10"> <h1 class="text-xl font-semibold mb-10">{{ article.title }}</h1> <p>{{ article.body }}</p> </div> </article> </WboltLayout> </template>
现在,当我们点击文章标题或“Read more”时,我们将被神奇地传送到Show.vue,而无需刷新页面。
Inertia链接示例
在我们的例子中,我们使用 <Link>
作为锚标记,向路由发送 GET
请求并返回新数据,但我们也可以使用 <Link>
到 POST
, PUT
, PATCH
和 DELETE
“routes/web.php“:
<Link href="/logout" method="post" as="button" type="button">Logout</Link>
Laravel集成Inertia技巧和窍门
我们现在有一个用Laravel、Inertia和Tailwind CSS构建的工作SPA。但惯性可以帮助我们实现更多。现在是时候掌握一些惯性技术了,这对开发者和应用访问者都有帮助。
生成URLs
你可能已经注意到,我们一直在为我们的Laravel路由添加名字,却没有使用它。Inertia允许我们在组件中使用我们的命名路由,而不是手动写下完整的路由。
我们可以通过在我们的项目中安装Ziggy包来实现这个目标:
composer require tightenco/ziggy
然后前往 “resources/js/app.js”,像这样更新它:
import "./bootstrap"; import "../css/app.css"; import { createApp, h } from "vue"; import { createInertiaApp } from "@inertiajs/inertia-vue3"; import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers"; import { ZiggyVue } from "../../vendor/tightenco/ziggy/dist/vue.m"; createInertiaApp({ title: (title) => `${title} - ${appName}`, resolve: (name) => resolvePageComponent( `./Pages/${name}.vue`, import.meta.glob("./Pages/**/*.vue") ), setup({ el, app, props, plugin }) { return createApp({ render: () => h(app, props) }) .use(plugin) .use(ZiggyVue, Ziggy) .mount(el); }, });
前往”/resources/views/app.blade.php“,用 @route
指令更新头部:
<!DOCTYPE html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Fetch project name dynamically --> <title inertia>{{ config('app.name', 'Laravel') }}</title> <!-- Scripts --> @routes @vite('resources/js/app.js') @inertiaHead </head> <body class="font-sans antialiased"> @inertia </body> </html>
…并通过点击以下两个终端命令刷新你的NPM包:
npm install && npm run dev
这个包允许我们在我们的Inertia组件中使用命名的路由,所以让我们前往Index.vue,并删除旧的手动路由,用路由名称替换它,同时像我们在控制器中一样正常传递数据。
我们将替换下面的:
<Link :href="'/posts/' + article.id"> {{ article.title }} </Link>
……为这个:
<Link :href="route('article.show', article.id)"> {{ article.title }} </Link>
这将给我们带来与我们完全相同的行为,但它对开发者更友好,而且当你的路线期望有许多参数时,它非常有帮助。
进度条指示器
这是Inertia.js最棒的功能之一;因为SPA提供了一种交互式的用户体验,如果能不断地反馈一个请求是否正在加载,这将是对应用程序的一个绝妙补充。这可以通过Inertia提供的一个单独的库来实现。
“@inertiajs/progress “库是一个围绕NProgress的包装器,可以根据惯性事件有条件地显示加载指标。你其实不需要知道它在幕后是如何工作的,所以我们只需要让它工作。
我们可以用下面的终端命令安装这个库:
npm install @inertiajs/progress
一旦它被安装,我们需要在 “resources/js/app.js“中导入它。
import "./bootstrap"; import "../css/app.css"; import { createApp, h } from "vue"; import { createInertiaApp } from "@inertiajs/inertia-vue3"; import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers"; import { ZiggyVue } from "../../vendor/tightenco/ziggy/dist/vue.m"; import { InertiaProgress } from "@inertiajs/progress"; createInertiaApp({ title: (title) => `${title} - ${appName}`, resolve: (name) => resolvePageComponent( `./Pages/${name}.vue`, import.meta.glob("./Pages/**/*.vue") ), setup({ el, app, props, plugin }) { return createApp({ render: () => h(app, props) }) .use(plugin) .use(ZiggyVue, Ziggy) .mount(el); }, }); InertiaProgress.init({ color: "#000000", showSpinner: true });
这将显示一个黑色的加载条和一个加载旋钮,但我们可以改变颜色以及其他有用的选项,这些都可以在Inertia.js进度指示器文档中找到。
Inertia进度指示器(右上方)
滚动管理
在某些情况下,你可能想在保持滚动位置不变的情况下导航到一个新的页面。也许你需要这样做,如果你允许用户留下评论;这将提交一个表单,并将新的评论从数据库加载到你的组件中;你希望这一切发生时用户不会失去滚动位置。惯性为我们解决了这个问题。
在我们的例子中,让我们把它应用于Index.vue中的 <Link>
标签。为了在使用Inertia的 <Link>
重定向到不同的页面时保留滚动位置,我们所需要做的就是在 <Link>
中添加 preserve-scroll
属性。
<Link :href="route('article.show', article.id)" preserve-scroll> {{ article.title }} </Link>
SEO技巧
自从SPA诞生以来,人们一直在关注搜索引擎优化(SEO)问题。人们普遍知道,如果你使用SPA方式,搜索引擎将很难抓取你的网络应用程序,因为所有的东西都是客户端渲染的,导致你的网站不能显示在搜索结果的顶部;尽管如此,为什么那些流行的平台,如Facebook和Github,现在都是SPA,并且在SEO方面仍然表现良好?
好吧,这不再是一个任务:不可能。Inertia正在提供一些解决方案来帮助你的SPA变得对搜索引擎友好。
Laravel和Vite的Inertia Vue SSR
搜索引擎总是在你的网站上寻找HTML,以便识别内容;但是,如果你的URL中没有HTML,这项工作就变得更加困难。在开发SPA时,你的页面上只有JavaScript和JSON。Inertia引入了一个服务器端渲染(SSR)功能,你可以将其添加到你的应用程序中。这允许你的应用程序在服务器上预先渲染一个初始页面访问,然后将渲染的HTML发送到浏览器。这可以让用户在你的页面完全加载之前看到并与之互动,而且它还有其他好处,比如缩短搜索引擎索引你的网站的时间。
总结一下它的工作原理,Inertia将识别它是否在Node.js服务器上运行,并将组件名称、属性、URL和资产版本渲染成HTML。这将为用户和搜索引擎提供你的页面所能提供的几乎所有内容。
然而, 因为我们正在处理Laravel, 这没有什么意义, 因为Laravel是一个PHP框架, 不在Node.js服务器上运行. 因此, 我们会把请求转发给一个Node.js服务, 它将渲染页面并返回HTML. 这将使我们的Laravel Vue应用程序默认为SEO友好。
首先, 我们需要安装Vue.js SSR npm包:
npm install @vue/server-renderer
另一个有用的Inertia “NPM “包提供了一个简单的 “HTTP “服务器。我们强烈建议你安装它:
npm install @inertiajs/server
然后,在 “resources/js/”中,我们将添加一个名为ssr.js的新文件。这个文件将与我们在安装Inertia时创建的app.js文件非常相似,只是它将在Node.js而不是浏览器中执行:
import { createSSRApp, h } from "vue"; import { renderToString } from "@vue/server-renderer"; import { createInertiaApp } from "@inertiajs/inertia-vue3"; import createServer from "@inertiajs/server"; import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers"; import { ZiggyVue } from "../../vendor/tightenco/ziggy/dist/vue.m"; const appName = "Laravel"; createServer((page) => createInertiaApp({ page, render: renderToString, title: (title) => `${title} - ${appName}`, resolve: (name) => resolvePageComponent( `./Pages/${name}.vue`, import.meta.glob("./Pages/**/*.vue") ), setup({ app, props, plugin }) { return createSSRApp({ render: () => h(app, props) }) .use(plugin) .use(ZiggyVue, { ...page.props.ziggy, location: new URL(page.props.ziggy.location), }); }, }) );
确保不要在ssr.js文件中包含所有内容,因为它不会被访问者看到;这个文件只是为了让搜索引擎和浏览器在你的页面中显示数据,所以只包含对你的数据重要的内容,或者只包含能让你的数据可用的内容。
“默认情况下,Inertia的SSR服务器将在13714端口运行。然而,你可以通过向createServer方法提供第二个参数来改变这一点。” Inertia DOCss。
Inertia.js DOCs并没有解释如何将Inertia SSR与Vite整合在一起,但我们现在会去了解一下。前往vite.config.js并粘贴以下内容:
import { defineConfig } from "vite"; import laravel from "laravel-vite-plugin"; import vue from "@vitejs/plugin-vue"; export default defineConfig({ plugins: [ laravel({ input: "resources/js/app.js", ssr: "resources/js/ssr.js", }), vue({ template: { transformAssetUrls: { base: null, includeAbsolute: false, }, }, }), ], });
接下来,前往package.json,修改构建脚本:
"build": "vite build && vite build --ssr"
现在,如果我们运行 npm run build
,Vite将为生产构建我们的SSR包。更多相关信息,你可以访问 Inertia SSR 文档 和 Vite SSR 文档。
标题和Meta
因为JavaScript应用程序是在文档的 <body>
中渲染的,它们不能向文档的 <head>
渲染标记,因为它在它们的范围之外。Inertia有一个 <Head>
组件,可以用来设置页面的 <title>
, <meta>
标签和其他 <head>
组件。
要在你的页面上添加 <head>
元素,我们必须从Inertia导入 <head>
,就像我们对 <Link>
组件做的那样:
import { Head } from '@inertiajs/inertia-vue3' <Head> <title>博客 - 闪电博</title> <meta name="description" content="WordPress爱好者博客"> </Head>
我们还可以为所有页面添加一个全局标题,这将在每个页面的标题旁边添加你的应用程序名称。我们已经在app.js文件中这样做了:
createInertiaApp({ title: (title) => `${title} - ${appName}`, // });
这意味着,如果我们在应用程序的主页中添加 <head title="Homepage">
,并加上一个标题,这将被渲染成这样: <title>Home - My App</title>
。
监控你的应用程序
速度是优化网站SEO性能的最重要因素之一。如果你的网站使用WordPress,为此,Kinsta APM将协助你监测并密切关注你的应用程序的运行情况,它可以帮助你识别WordPress的性能问题。
小结
Inertia.js是目前最重要的技术之一; 把它和Laravel混合在一起, 你就有了一个用PHP和JavaScript构建的现代单页面应用程序. Taylor Otwell, Laravel的创建者, 对Inertia非常感兴趣,以至于Laravel推出了其最受欢迎的入门套件,Laravel Breeze和Jetstream,并支持Inertia和SSR。
如果你是Laravel的粉丝或者专业的开发者, Inertia.js无疑会吸引你的目光. 在这个教程中,我们只用了几分钟就做出了一个非常基本和直接的博客。关于Inertia,还有很多东西需要学习,这可能只是许多文章和教程中的第一个。
评论留言