Readouble

Livewire v3 モーフィング

モーフィング

LivewireコンポーネントがブラウザのDOMを更新する際、それは「モーフィング」と呼ばれるインテリジェントな方法で行われます。_morph_という用語は、_replace_のような言葉とは対照的です。When a Livewire component updates the browser's DOM, it does so in an intelligent way we call "morphing". The term morph is in contrast with a word like replace.

コンポーネントが更新されるたびに、コンポーネントのHTMLを新しくレンダリングされたHTMLで_置き換える_のではなく、Livewireは現在のHTMLと新しいHTMLを動的に比較し、違いを特定し、変更が必要な箇所のみHTMLに外科的な変更を加えます。Instead of replacing a component's HTML with newly rendered HTML every time a component is updated, Livewire dynamically compares the current HTML with the new HTML, identifies differences, and makes surgical changes to the HTML only in the places where changes are needed.

これにより、コンポーネント上の既存の、変更されていない要素を保持できるというメリットがあります。たとえば、イベントリスナ、フォーカス状態、およびフォーム入力値はすべて、Livewireの更新間で保持されます。もちろん、モーフィングは、更新のたびに新しいDOMをワイプして再レンダリングするよりも、パフォーマンスが向上します。This has the benefit of preserving existing, un-changed elements on a component. For example, event listeners, focus state, and form input values are all preserved between Livewire updates. Of course, morphing also offers increased performance compared to wiping and re-rending new DOM on every update.

モーフィングの仕組みHow morphing works

LivewireがLivewireリクエスト間でどの要素を更新するかを判断する方法を理解するために、次の単純なTodosコンポーネントを考えてみましょう。To understand how Livewire determines which elements to update between Livewire requests, consider this simple Todos component:

class Todos extends Component
{
    public $todo = '';

    public $todos = [
        'first',
        'second',
    ];

    public function add()
    {
        $this->todos[] = $this->todo;
    }
}
<form wire:submit="add">
    <ul>
        @foreach ($todos as $item)
            <li>{{ $item }}</li>
        @endforeach
    </ul>

    <input wire:model="todo">
</form>

このコンポーネントの最初のレンダリングでは、次のHTMLが出力されます。The initial render of this component will output the following HTML:

<form wire:submit="add">
    <ul>
        <li>first</li>

        <li>second</li>
    </ul>

    <input wire:model="todo">
</form>

ここで、入力フィールドに「third」と入力して[Enter]キーを押したと想像してください。新しくレンダリングされたHTMLは次のようになります。Now, imagine you typed "third" into the input field and pressed the [Enter] key. The newly rendered HTML would be:

<form wire:submit="add">
    <ul>
        <li>first</li>

        <li>second</li>

        <li>third</li> <!-- [tl! add] -->
    </ul>

    <input wire:model="todo">
</form>

Livewireがコンポーネントの更新を処理するとき、元のDOMを新しくレンダリングされたHTMLに_モーフィング_します。次の視覚化は、その仕組みを直感的に理解できるようにするはずです。When Livewire processes the component update, it morphs the original DOM into the newly rendered HTML. The following visualization should intuitively give you an understanding of how it works:

<iframe src="https://player.vimeo.com/video/844600772?badge=0&autopause=0&player_id=0&app_id=58479" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen style="position:absolute;top:0;left:0;width:100%;height:100%;" title="morph_basic"></iframe>
<script src="https://player.vimeo.com/api/player.js"></script>

ご覧のとおり、Livewireは両方のHTMLツリーを同時に歩きます。両方のツリー内の各要素を検出すると、それらを比較して、変更、追加、および削除がないかを確認します。検出された場合、適切な変更を外科的に行います。As you can see, Livewire walks both HTML trees simultaneously. As it encounters each element in both trees, it compares them for changes, additions, and removals. If it detects one, it surgically makes the appropriate change.

モーフィングの欠点Morphing shortcomings

以下は、モーフィングアルゴリズムがHTMLツリーの変更を正しく識別できず、アプリケーションで問題を引き起こすシナリオです。The following are scenarios where morphing algorithms fail to correctly identify the change in HTML trees and therefore cause problems in your application.

中間要素の挿入Inserting intermediate elements

架空のCreatePostコンポーネントの次のLivewire Bladeテンプレートを検討してください。Consider the following Livewire Blade template for a fictitious CreatePost component:

<form wire:submit="save">
    <div>
        <input wire:model="title">
    </div>

    @if ($errors->has('title'))
        <div>{{ $errors->first('title') }}</div>
    @endif

    <div>
        <button>Save</button>
    </div>
</form>

ユーザーがフォームを送信しようとしたが、検証エラーが発生した場合、次の問題が発生します。If a user tries submitting the form, but encounters a validation error, the following problem occurs:

<iframe src="https://player.vimeo.com/video/844600840?badge=0&autopause=0&player_id=0&app_id=58479" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen style="position:absolute;top:0;left:0;width:100%;height:100%;" title="morph_problem"></iframe>
<script src="https://player.vimeo.com/api/player.js"></script>

ご覧のとおり、Livewireがエラーメッセージの新しい<div>を検出したとき、既存の<div>をその場で変更するか、新しい<div>を途中に挿入するかを判断できません。As you can see, when Livewire encounters the new <div> for the error message, it doesn't know whether to change the existing <div> in-place, or insert the new <div> in the middle.

何が起こっているかをより明確に繰り返すと、次のようになります。To re-iterate what's happening more explicitly:

  • Livewireは両方のツリーの最初の<div>を検出します。それらは同じなので、続行します。Livewire encounters the first <div> in both trees. They are the same, so it continues.
  • Livewireは両方のツリーの2番目の<div>を検出し、それらは同じ<div>であり、コンテンツが1つ変更されただけだと考えます。そのため、エラーメッセージを新しい要素として挿入する代わりに、<button>をエラーメッセージに変更します。Livewire encounters the second <div> in both trees and thinks they are the same <div>, just one has changed contents. So instead of inserting the error message as a new element, it changes the <button> into an error message.
  • Livewireは、前の要素を誤って変更した後、比較の最後に要素が追加されていることに気付きます。次に、要素を作成し、前の要素の後に付加します。Livewire then, after mistakenly modifying the previous element, notices an additional element at the end of the comparison. It then creates and appends the element after the previous one.
  • したがって、本来は単に移動されるはずだった要素を破棄してから再作成します。Therefore, destroying, then re-creating an element that otherwise should have been simply moved.

このシナリオは、ほぼすべてのモーフィング関連のバグの根本にあります。This scenario is at the root of almost all morph-related bugs.

これらのバグの具体的な問題点をいくつか示します。Here are a few specific problematic impacts of these bugs:

  • イベントリスナと要素の状態は、更新間で失われますEvent listeners and element state are lost between updates
  • イベントリスナと状態は、誤った要素間で誤って配置されますEvent listeners and state are misplaced across the wrong elements
  • Livewireコンポーネント全体は、Livewireコンポーネントも単にDOMツリー内の要素であるため、リセットまたは複製できますEntire Livewire components can be reset or duplicated as Livewire components are also simply elements in the DOM tree
  • Alpineコンポーネントと状態は、失われたり、誤って配置されたりする可能性がありますAlpine components and state can be lost or misplaced

幸いなことに、Livewireは次のアプローチを使用して、これらの問題を軽減するために懸命に取り組んできました。Fortunately, Livewire has worked hard to mitigate these problems using the following approaches:

内部ルックアヘッドInternal look-ahead

Livewireには、要素を変更する前に、後続の要素とそのコンテンツをチェックするモーフィングアルゴリズムの追加の手順があります。Livewire has an additional step in its morphing algorithm that checks subsequent elements and their contents before changing an element.

これにより、多くの場合、上記のシナリオが発生しなくなります。This prevents the above scenario from happening in many cases.

以下は、動作中の「ルックアヘッド」アルゴリズムの視覚化です。Here is a visualization of the "look-ahead" algorithm in action:

<iframe src="https://player.vimeo.com/video/844600800?badge=0&autopause=0&player_id=0&app_id=58479" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen style="position:absolute;top:0;left:0;width:100%;height:100%;" title="morph_lookahead"></iframe>
<script src="https://player.vimeo.com/api/player.js"></script>

モーフマーカーの挿入Injecting morph markers

バックエンドでは、LivewireはBladeテンプレート内の条件を自動的に検出し、それらをHTMLコメントマーカーでラップします。LivewireのJavaScriptは、モーフィング時にガイドとして使用できます。On the backend, Livewire automatically detects conditionals inside Blade templates and wraps them in HTML comment markers that Livewire's JavaScript can use as a guide when morphing.

以下は、前のBladeテンプレートの例ですが、Livewireの挿入されたマーカーが含まれています。Here's an example of the previous Blade template but with Livewire's injected markers:

<form wire:submit="save">
    <div>
        <input wire:model="title">
    </div>

    <!--[if BLOCK]><![endif]--> <!-- [tl! highlight] -->
    @if ($errors->has('title'))
        <div>Error: {{ $errors->first('title') }}</div>
    @endif
    <!--[if ENDBLOCK]><![endif]--> <!-- [tl! highlight] -->

    <div>
        <button>Save</button>
    </div>
</form>

これらのマーカーがテンプレートに挿入されると、Livewireは変更と追加の違いをより簡単に検出できます。With these markers injected into the template, Livewire can now more easily detect the difference between a change and an addition.

この機能はLivewireアプリケーションにとって非常に有益ですが、正規表現を介してテンプレートを解析する必要があるため、条件を適切に検出できない場合があります。この機能がアプリケーションに役立つよりもむしろ妨げになっている場合は、アプリケーションのconfig/livewire.phpファイルで次の構成を使用して無効にできます。This feature is extremely beneficial to Livewire applications, but because it requires parsing templates via regex, it can sometimes fail to properly detect conditionals. If this feature is more of a hindrance than a help to your application, you can disable it with the following configuration in your application's config/livewire.php file:

'inject_morph_markers' => false,

条件のラッピングWrapping conditionals

上記の2つのソリューションで状況をカバーできない場合、モーフィングの問題を回避する最も信頼性の高い方法は、条件とループを常に存在する独自の要素でラップすることです。If the above two solutions don't cover your situation, the most reliable way to avoid morphing problems is to wrap conditionals and loops in their own elements that are always present.

たとえば、以下は上記のBladeテンプレートを、ラッピング<div>要素で書き換えたものです。For example, here's the above Blade template rewritten with wrapping <div> elements:

<form wire:submit="save">
    <div>
        <input wire:model="title">
    </div>

    <div> <!-- [tl! highlight] -->
        @if ($errors->has('title'))
            <div>{{ $errors->first('title') }}</div>
        @endif
    </div> <!-- [tl! highlight] -->

    <div>
        <button>Save</button>
    </div>
</form>

条件が永続的な要素でラップされたため、Livewireは2つの異なるHTMLツリーを適切にモーフィングします。Now that the conditional has been wrapped in a persistent element, Livewire will morph the two different HTML trees properly.

モーフィングのバイパスBypassing morphing

要素のモーフィングを完全にバイパスする必要がある場合は、wire:replaceを使用して、既存の要素をモーフィングしようとする代わりに、要素のすべての子を置き換えるようにlivewireに指示できます。If you need to bypass morphing entirely for an element, you can use wire:replace[/docs/wire-replace] to instruct livewire to replace all children of an element instead of attempting to morph the existing elements.

章選択

パッケージ

設定

バージョン変更
linkv3 linkv2
明暗テーマ
light_mode
dark_mode
brightness_auto システム設定に合わせる
テーマ選択
photo_size_select_actual デフォルト
photo_size_select_actual モノクローム(白黒)
photo_size_select_actual Solarized風
photo_size_select_actual GitHub風(青ベース)
photo_size_select_actual Viva(黄緑ベース)
photo_size_select_actual Happy(紫ベース)
photo_size_select_actual Mint(緑ベース)
コードハイライトテーマ選択

明暗テーマごとに、コードハイライトのテーマを指定できます。

テーマ配色確認
スクリーン表示幅
640px
80%
90%
100%

768px以上の幅があるときのドキュメント部分表示幅です。

インデント
無し
1rem
2rem
3rem
原文確認
原文を全行表示
原文を一行ずつ表示
使用しない

※ 段落末のEボタンへカーソルオンで原文をPopupします。

Diff表示形式
色分けのみで区別
行頭の±で区別
削除線と追記で区別

※ [tl!…]形式の挿入削除行の表示形式です。

テストコード表示
両コード表示
Pestのみ表示
PHPUnitのみ表示
OS表示
全OS表示
macOSのみ表示
windowsのみ表示
linuxのみ表示
JSフレームワーク
両フレームワーク
Reactのみ表示
Vueのみ表示
JSのみ表示

(JSが存在しない場合は、他を全表示)

和文変換

対象文字列と置換文字列を半角スペースで区切ってください。(最大5組各10文字まで)

本文フォント

総称名以外はCSSと同様に、"〜"でエスケープしてください。

コードフォント

総称名以外はCSSと同様に、"〜"でエスケープしてください。

保存内容リセット

localStrageに保存してある設定項目をすべて削除し、デフォルト状態へ戻します。

ヘッダー項目移動

キーボード操作