07/09/2018, 16:56

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

Normal Blade

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

0