Одним из недостатков использования Laravel в качестве бэкэнда для Vue.js была невозможность серверного рендеринга вашего кода. Был . В выпуске Vue.js 2.5.0 появилась поддержка рендеринга на стороне сервера для сред, отличных от Node.js, включая PHP, Python , Ruby и т. Д.
В этом руководстве я расскажу вам о шагах по настройке Laravel и продемонстрирую простое приложение, отображаемое на сервере. Получить код для этого здесь, на GitHub .
Краткий обзор рендеринга на стороне сервера
Если вы не знакомы с рендерингом на стороне сервера (SSR), вот простой пример: скажем, у нас есть приложение Vue.js, построенное из компонентов. Если мы используем инструменты разработки браузера для просмотра DOM страницы после загрузки страницы, мы увидим наше полностью визуализированное приложение:
HTML
1
<div id="app">
2
<ul>
3
<li>Component 1</li>
4
<li>Component 2</li>
5
<li>
6
<div>Component 3</div>
7
</li>
8
</ul>
9
</div>
Но если мы посмотрим на источник документа, т.е. index.html, как это было при отправке сервером, вы увидите, что он просто содержит наш элемент mount:
HTML
xxxxxxxxxx
1
<div id="app"></div>
Почему расхождение? Поскольку JavaScript отвечает за создание страницы, и, ipso facto, JavaScript должен запускаться до создания страницы. Только что с сервера, на странице не будет контента.
Но при рендеринге на стороне сервера наша страница содержит HTML-код, необходимый браузеру для построения DOM до загрузки и запуска JavaScript, т.е. исходный код страницы будет выглядеть как в первом примере выше. Это достигается путем запуска приложения Vue.js на сервере и захвата вывода, а затем внедрения этого вывода на страницу перед его отправкой пользователю.
При использовании SSR ваше приложение не загружается и не запускается быстрее, более того, оно может работать немного медленнее, поскольку на сервере есть дополнительная задача рендеринга приложения. Но содержание страницы показывается раньше, поэтому пользователь может видеть, что происходит со страницей раньше.
Вам также может понравиться:
Клиентский v / s серверный рендеринг: что выбрать, когда?
Почему Laravel Do Vue SSR до сих пор не могла?
Очевидно, что SSR требует среды JavaScript на сервере, так как приложение Vue создано с использованием JavaScript. Для не-Node.js бэкэндов, таких как PHP, Ruby и Python, должна быть создана песочница JavaScript с сервера, чтобы запустить приложение Vue и сгенерировать вывод.
V8Js — это проект, который позволяет вам установить среду выполнения V8 JavaScript в среде PHP и создать такую песочницу. Но до версии Vue 2.5.0 этого было недостаточно, поскольку Vue SSR требовала определенных API-интерфейсов Node.js для корректной работы. Недавнее обновление позволило убедиться, что средство рендеринга сервера теперь «не зависит от среды» и, следовательно, может быть запущено в Node.js, V8Js, Nashorn и т. Д.
Vue / Laravel SSR Демо
Давайте теперь получим простую демонстрацию Vue SSR в приложении Laravel.
Среда
php-v8js — это расширение PHP, которое даст доступ к движку Google V8 JavaScript. Несомненно, самой сложной частью настройки Vue SSR с PHP является установка V8J. Из-за моего ограниченного знания Linux, мне потребовалось несколько часов, чтобы заставить его работать.
Если у вас есть навыки работы с DevOps, вы можете попробовать установить его самостоятельно. Если нет, я рекомендую вам использовать этот образ Docker и установить на него Laravel.
Установка зависимостей
Как только у вас будет работать расширение и новый проект Laravel, вам нужно будет установить как Vue, так и vue-server-renderer . Вам потребуется минимальная версия 2.5.0, чтобы получить независимые от среды функции SSR.
Оболочка
xxxxxxxxxx
1
npm i --save-dev vue@>=2.5.0 vue-server-renderer@>=2.5.0
Vue.js
Давайте начнем с настройки простого полнофункционального приложения Vue.js / Laravel . У него пока не будет никаких функций SSR, но мы заложим основы, которые нам понадобятся. Для начала мы поместим основные функциональные возможности приложения в однофайловый компонент App.vue .
ресурсы / активы / JS / компоненты / App.vue
HTML
xxxxxxxxxx
1
<template>
2
<div id="app">
3
{{ message }}
4
</div>
5
</template>
6
<script>
7
export default {
8
data() {
9
return {
10
message: 'Hello World'
11
}
12
}
13
}
14
</script>
Наш входной файл приложения, app.js , будет отвечать только за рендеринг компонента и его монтирование в шаблон. Использование функции рендеринга здесь вместо шаблона DOM необходимо по причинам, которые скоро станут понятны.
ресурсы / активы / JS / app.js
JavaScript
xxxxxxxxxx
1
import App from './components/App.vue';
2
import Vue from 'vue';
3
new Vue({
5
el: '#app'
6
render: h => h(App)
7
});
Смешанная конфигурация
Давайте настроим конфигурацию Mix, которая создает файл ввода. Обратите внимание, что я также перезаписываю сборку Vue по умолчанию, чтобы использовать сборку только во время выполнения. Поскольку мы используем функции рендеринга и однофайловые компоненты, нам не понадобится рендерер шаблонов.
webpack.mix.js
JavaScript
xxxxxxxxxx
1
let mix = require('laravel-mix');
2
mix
4
.js('resources/assets/js/app.js', 'public/js')
5
;
6
mix.webpackConfig({
8
resolve: {
9
alias: {
10
'vue$': 'vue/dist/vue.runtime.common.js'
11
}
12
}
13
});
После этого вы сможете создать приложение Vue.js:
Оболочка
xxxxxxxxxx
1
$ npm run dev
2
## Outputs to public/js/app.js
Blade View
Нам понадобится шаблон Blade для доставки нашего приложения Vue в браузер. Убедитесь, что вы добавили пустой div
с id, app
который будет служить элементом монтирования. Также включите скрипт сборки.
ресурсы / виды / app.blade.php
HTML
xxxxxxxxxx
1
2
<html lang="{{ app()->getLocale() }}">
3
<head>
4
<meta charset="utf-8">
5
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6
<meta name="viewport" content="width=device-width, initial-scale=1">
7
<title>Vue/Laravel SSR App</title>
8
</head>
9
<body>
10
<div id="app"></div>
11
<script src="{{ asset('js/app.js') }}" type="text/javascript"></script>
12
</body>
13
</html>
Контроллер и Маршрут
Давайте создадим новый класс контроллера, который скоро будет включать логику для сервера, отображающего приложение.
Оболочка
xxxxxxxxxx
1
$ php artisan make:controller AppController
Для начала мы создадим метод, get
который будет возвращать наше представление приложения:
приложение / HTTP / Контроллеры / AppController.php
PHP
xxxxxxxxxx
1
2
namespace App\Http\Controllers;
4
class AppController extends Controller
6
{
7
public function get() {
8
return view('app');
9
}
10
}
Мы добавим веб-маршрут для корневого пути, который вызывает этот метод контроллера:
маршруты / web.php
JavaScript
xxxxxxxxxx
1
Route::get('/', 'AppController@get');
После этого мы сможем просмотреть наше скромное полнофункциональное приложение:
Рендеринг на стороне сервера
Приложение Vue.js, которое мы запускаем в песочнице, должно немного отличаться от того, которое мы запускаем в браузере, поскольку, хотя используется один и тот же язык, эти среды довольно разные. Например, в песочнице нет объекта окна или документа .
Поэтому нам понадобятся две сборки. Они будут максимально похожими, но будут иметь небольшие отличия. Мы будем хранить любой общий (то есть универсальный) код в app.js , но любой специфический для среды код будет добавлен в новые входные файлы, которые мы вскоре создадим.
В app.js давайте удалим el
свойство из конфигурации Vue, так как оно не имеет смысла в серверной среде, так как у приложения нет документа для подключения. Мы также сделаем так, чтобы этот файл экспортировал функцию, которая возвращает свежий экземпляр приложения, который можно импортировать в наши новые входные файлы.
ресурсы / активы / JS / app.js
JavaScript
xxxxxxxxxx
1
export function createApp() {
2
return new Vue({
3
render: h => h(App)
4
});
5
}
Входные файлы
Теперь нам нужно создать два новых файла ввода, один для браузера (клиента) и один для сервера.
Оболочка
xxxxxxxxxx
1
$ touch resources/assets/js/entry-client.js resources/assets/js/entry-server.js
Запись клиента просто заново реализует функциональность, которую мы только что удалили из app.js, то есть импортирует универсальное приложение и монтирует его в шаблон.
ресурсы / активы / JS / вход-client.js
JavaScript
xxxxxxxxxx
1
import { createApp } from './app'
2
createApp().$mount('#app');
Файл входа на сервер немного интереснее. Во-первых, он вызывает глобальный метод renderVueComponentToString
. Этот метод предоставляется vue-server-renderer, который мы скоро представим в нашей настройке SSR. Во-вторых, он вызывает метод print
. Этот метод является частью API V8J и является механизмом для получения чего-либо из песочницы JavaScript обратно в среду PHP.
ресурсы / активы / JS / вход-server.js
JavaScript
xxxxxxxxxx
1
import { createApp } from './app'
2
renderVueComponentToString(createApp(), (err, res) => {
4
print(res);
5
});
Теперь нам нужно обновить конфигурацию Mix, чтобы получить сборку каждой версии приложения из двух новых входных файлов:
webpack.mix.js
Оболочка
xxxxxxxxxx
1
mix
2
.js('resources/assets/js/entry-client.js', 'public/js')
3
.js('resources/assets/js/entry-server.js', 'public/js')
4
;
После npm run dev
повторного запуска у вас, конечно, будет два файла сборки. Нам нужно обновить наше представление Blade, чтобы обеспечить загрузку нового файла сборки клиента вместо app.js :
resoures / просмотров / app.blade.php
HTML
xxxxxxxxxx
1
<script src="{{ asset('js/entry-client.js') }}" type="text/javascript"></script>
Если вы обновите страницу в браузере, вы не увидите никакой разницы в поведении.
Laravel
Теперь мы наконец-то добрались до серверной функциональности рендеринга. Добавьте новый метод render
в AppController
который работает следующим образом:
- Модуль vue-server-renderer и серверная сборка приложения загружаются из файловой системы.
- Выходная буферизация включена. Это означает, что любой вывод, отправленный из сценария, захватывается внутри, а не выводится на экран.
- Передайте некоторые необходимые переменные окружения в V8J.
- Код рендерера и файл сборки сервера затем выполняются. Помните, что в entry-server.js мы используем
print
метод для вывода чего-либо. Это будет захвачено выходным буфером. - Вернуть содержимое буфера и удалить текущий выходной буфер.
приложение / HTTP / Контроллеры / AppController.php
PHP
xxxxxxxxxx
1
2
namespace App\Http\Controllers;
4
use Illuminate\Support\Facades\File;
6
class AppController extends Controller
8
{
9
private function render() {
10
$renderer_source = File::get(base_path('node_modules/vue-server-renderer/basic.js'));
11
$app_source = File::get(public_path('js/entry-server.js'));
12
$v8 = new \V8Js();
14
ob_start();
16
$v8->executeString('var process = { env: { VUE_ENV: "server", NODE_ENV: "production" }}; this.global = { process: process };');
18
$v8->executeString($renderer_source);
19
$v8->executeString($app_source);
20
return ob_get_clean();
22
}
23
public function get() {
25
$ssr = $this->render();
26
return view('app', ['ssr' => $ssr]);
27
}
28
}
Возвращаемое значение render
будет отображаться на сервере нашего приложения. Это строка HTML. Теперь мы назначим это переменной шаблона и отправим в представление. Обязательно пропустите экранирование строки, используя {!! !!}
фигурные скобки, чтобы HTML-код печатался как есть.
ресурсы / виды / app.blade.php
HTML
xxxxxxxxxx
1
<body>
2
{!! $ssr !!}
3
<script src="{{ asset('js/entry-client.js') }}" type="text/javascript"></script>
4
</body>
Теперь серверная визуализация работает! Однако, если вы загрузите приложение, вы, вероятно, не заметите никакой разницы, поскольку улучшение загрузки страницы на локальном сервере не будет ощутимым. Чтобы убедиться, что это работает, просмотрите исходный документ, и вы увидите это:
Вместо пустого <div id="app">
, у нас есть актуальный контент на нашей странице. Обратите внимание на специальный атрибут , который вя-сервер-визуализатор добавляет: data-server-rendered="true"
. Это сделано для того, чтобы при монтировании экземпляра Vue вместо повторной сборки содержимого он пытался монтировать поверх него.
Заключение
Отсутствие рендеринга на стороне сервера было одним из самых больших минусов использования Laravel в качестве бэкэнда Vue.js. Он по-прежнему второй по сравнению с Node.js, поскольку требуется песочница, но здорово, что теперь она работает.
Для получения дополнительной информации о Ве ССР, проверить на сторону сервера Rendering Руководство Vue.js .