React server-side with Laravel
Ngày nay, việc render trên các thiết bị client khi duyệt web ngày càng trở lên phổ biến bằng việc sử dụng các thư viện hay framework như Reactjs, Vuejs, Angularjs.... Tuy nhiên khi trang web ngày càng lớn và phức tạp, dữ liệu ngày càng nhiều thì việc render trên client lại gần như trở thành gánh ...
Ngày nay, việc render trên các thiết bị client khi duyệt web ngày càng trở lên phổ biến bằng việc sử dụng các thư viện hay framework như Reactjs, Vuejs, Angularjs.... Tuy nhiên khi trang web ngày càng lớn và phức tạp, dữ liệu ngày càng nhiều thì việc render trên client lại gần như trở thành gánh nặng cho các thiết bị client. Vậy nên người ta lại nghĩ ra việc vẫn sử dụng những thư viện/framework kia để render giao diện người dùng nhưng việc render này được thực hiện ở trên server.
Trong bài viết này, mình sẽ giới thiệu về việc render React trên server cùng với Laravel. Chính xác hơn thì mình hướng dẫn các bạn cách đặt code React vào trong blade của laravel mà nó vẫn chạy và render bình thường :v. Với cách này, chúng ta chỉ sử dụng react để tạo view cho ứng dụng, các việc quản lý khác laravel sẽ đảm nhiệm.
Ngoài ra các bạn có thể áp dụng theo cách khác như viết API cho laravel để trả về view cho các component trong single-page web.
React
React là 1 thư viện được phát triển bởi facebook, nó được tạo ra để hỗ trợ cho việc xây dựng giao diện người dùng. Đã có khá nhiều bài viết về React trên viblo nên mình không giới thiệu thêm về nó nữa.
React server render with Laravel
Setup
Để thực hiện được cách render như trong bài viết phía dưới, chúng ta cần phải cài đặt thêm extension php v8js
Các bạn tham khảo cách cài đặt ở đây:
- For Linux users
- For Mac users
- For Windows users
Thư viện chúng ta sử dụng trong bài viết là react-laravel.
Để cài đặt thư viện này, chúng ta cần thêm đoạn code sau vào composer.json:
"minimum-stability": "dev"
Sau đó chạy lệnh:
composer require talyssonoc/react-laravel:0.11
Thêm ReactReactServiceProvider::class vào config/app.php.
Và xong, chúng ta bắt đầu đi vào tìm hiểu cách sử dụng nó.
Usage
Thư viện này sử dụng khá là đơn giản:
@react_component(<componentName>, props, options)
//example
@react_component('Message', [ 'title' => 'Hello, World' ], [ 'prerender' => true ])
// example using namespaced component
@react_component('Acme.Message', [ 'title' => 'Hello, World' ], [ 'prerender' => true ])
- componentName: Tên của component mà chúng ta muốn sử dụng. Nếu bạn sử dụng namespaced components thì bạn có thể sử dụng dot-notation cho tên của component.
- props: Dữ liệu mà bạn muốn truyền vào component, tương tự props trong react
- options: mảng chứa các options mà bàn muốn truyền vào react-laravel
Example
Bây giờ chúng ta cần tạo view dể hiển thị tất cả các post. Hãy xem ví dụ ở dưới
// AppHttpControllersPostCotroller.php
use AppModelsPost;
class PostController extends Controller
{
/**
* Display a listing of the posts.
*/
public function index()
{
return view('post.index', ['posts' => Post::all()]);
}
}
Chúng ta tạo view với blade:
// resourcesviewspostindex.php
@extends('layouts.app')
@section('content')
<div class="container">
<div class="posts">
@foreach ($posts as $post)
<div class="post row" style="border-bottom: 1px solid #BBB">
<h3>{{ $post->title }}</h3>
<p>{{ $post->content }}</p>
</div>
@endforeach
</div>
</div>
@endsection
Bây giờ chúng ta sẽ thay thành phần hiển thị title và content của $post bằng 1 component React
// resourcesassetjsPost.js
class Post extends React.Component {
render() {
return (
<div className="row post" style={{borderBottom: '1px solid #BBB'}}>
<h3>{ this.props.post.title }</h3>
<p>{ this.props.post.content }</p>
</div>
)
}
}
export default Post;
// resourcesassetjscomponents.js
import Post from './Post';
window.Post = Post;
Và cập nhật lại view:
@extends('layouts.app')
@section('content')
<div class="container">
<div class="posts">
@foreach ($posts as $post)
@react_component('Post', ['post' => $post])
@endforeach
</div>
</div>
@endsection
Chúng ta có thể thấy, mặc định thư viện mà chúng ta cài đặt sẽ render và đặt component của chúng ta vào 1 tag (xem thêm option tag tại https://github.com/talyssonoc/react-laravel#usage). Và đồng thời thuộc tính data-reactid cũng được render.
Customize
Bằng việc cài đặt và sử dụng thư viện mặc định, chúng ta khó có thể customize việc render như đúng ý muốn. Ví dụ như việc đặt component vào 1 tag là không cần thiết vì khi tạo component chúng ta đã wrap nó lại rồi. Hoặc như việc chúng ta không muốn render thuộc tính data-reactid nữa (react bản mới không còn render thuộc tính này nữa, tuy nhiên thư viện reactjs/react-php-v8js sử dụng phương thức ReactDOMServer.renderToString() nên chúng ta vẫn thấy thuộc tính này).
Việc chỉnh sửa tùy theo ý của các bạn muốn. Ở đây mình chỉnh sửa để render trực tiếp component chứ không đặt nó trong 1 tag như mặc định. Đồng thời thay đổi 1 chút việc render bằng sử dụng phương thức ReactDOMServer.renderToStaticMarkup():
- vendor/react-php-v8js/ReactJS.php
function getMarkup()
{
$js = sprintf(
- "print(ReactDOMServer.renderToString(React.createElement(%s, %s)))",
+ "print(ReactDOMServer.renderToStaticMarkup(React.createElement(%s, %s)))",
$this->component,
$this->data);
- vendor/talyssonoc/react-laravel/lib/React.php
$markup = $this->react->setComponent($component, $props)->getMarkup();
}
- // Pass props back to view as value of `data-react-props`
- $props = htmlentities(json_encode($props), ENT_QUOTES);
-
- // Gets all values that aren't used as options and map it as HTML attributes
- $htmlAttributes = array_diff_key($options, $this->defaultOptions);
- $htmlAttributesString = $this->arrayToHTMLAttributes($htmlAttributes);
-
- return "<{$tag} data-react-class='{$component}' data-react-props='{$props}' {$htmlAttributesString}>{$markup}</{$tag}>";
+ return $markup;
}
Với vài chỉnh sửa nhỏ ở trên, khi render view chúng ta sẽ không còn thấy các thuộc tính thừa nữa
Tham khảo
Ngoài việc sử dụng thư viện ra, các bạn có thể tham khảo source code của 2 thư viện trên và thêm chúng vào project của mình sao cho hợp lý.
Các bạn có thể tham khảo cách của mình tại: https://github.com/DoanhPHAM/laravel-react-server-render