Open DaTrader opened 2 years ago
Surface: v0.8.4 LiveView: 0.17.12 Phoenix: 1.6.15 Elixir: v1.14.1
I have attached a chromium websocket logs for a simple LiveView app and the same application but converted to Surface.
Here is the index.ex
for the Surface application. I will not be sharing the LiveView application index.ex
since it implements the same exact behaviors as the Surface application:
defmodule BinaryNogginWeb.EventLive.Index do
alias Surface.Components.Link
use BinaryNogginWeb, :surface_view
alias BinaryNoggin.Events
alias BinaryNoggin.Events.Event
alias BinaryNogginWeb.Live.Modal
alias BinaryNogginWeb.Router.Helpers, as: Routes
alias Surface.Components.LivePatch
alias Surface.Components.LiveRedirect
@impl true
def mount(_params, _session, socket) do
{:ok, assign(socket, :events, list_events())}
end
@impl true
def handle_params(params, _url, socket) do
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
end
defp apply_action(socket, :edit, %{"id" => id}) do
socket
|> assign(:page_title, "Edit Event")
|> assign(:event, Events.get_event!(id))
end
defp apply_action(socket, :new, _params) do
socket
|> assign(:page_title, "New Event")
|> assign(:event, %Event{})
end
defp apply_action(socket, :index, _params) do
socket
|> assign(:page_title, "Listing Events")
|> assign(:event, nil)
end
@impl true
def handle_event("delete", %{"id" => id}, socket) do
event = Events.get_event!(id)
{:ok, _} = Events.delete_event(event)
{:noreply, assign(socket, :events, list_events())}
end
defp list_events do
Events.list_events()
end
end
index.html.heex
:
<h1>Listing Events</h1>
<%= if @live_action in [:new, :edit] do %>
<.modal return_to={Routes.event_index_path(@socket, :index)}>
<.live_component
module={BinaryNogginWeb.EventLive.FormComponent}
id={@event.id || :new}
title={@page_title}
action={@live_action}
event={@event}
return_to={Routes.event_index_path(@socket, :index)}
/>
</.modal>
<% end %>
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Capacity</th>
<th></th>
</tr>
</thead>
<tbody id="events">
<%= for event <- @events do %>
<tr id={"event-#{event.id}"}>
<td><%= event.name %></td>
<td><%= event.description %></td>
<td><%= event.capacity %></td>
<td>
<span><%= live_redirect "Show", to: Routes.event_show_path(@socket, :show, event) %></span>
<span><%= live_patch "Edit", to: Routes.event_index_path(@socket, :edit, event) %></span>
<span><%= link "Delete", to: "#", phx_click: "delete", phx_value_id: event.id, data: [confirm: "Are you sure?"] %></span>
</td>
</tr>
<% end %>
</tbody>
</table>
<span><%= live_patch "New Event", to: Routes.event_index_path(@socket, :new) %></span>
index.sface
<h1>Listing Events</h1>
{#if @live_action in [:new, :edit]}
<Modal return_to={Routes.event_index_path(@socket, :index)}>
<BinaryNogginWeb.EventLive.FormComponent action={@live_action} title={@page_title} event={@event} id={@event.id || :new} return_to={Routes.event_index_path(@socket, :index)} />
</Modal>
{/if}
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Capacity</th>
<th></th>
</tr>
</thead>
<tbody id="events">
{#for event <- @events}
<tr id={"event-#{event.id}"}>
<td>{event.name}</td>
<td>{event.description}</td>
<td>{event.capacity}</td>
<td>
<LiveRedirect label="Show" to={Routes.event_show_path(@socket, :show, event)} />
<LivePatch label="Edit" to={Routes.event_index_path(@socket, :edit, event)} />
<Link label="Delete" to="#" opts={phx_click: "delete", phx_value_id: event.id, data: [confirm: "Are you sure?"]}/>
</td>
</tr>
{/for}
</tbody>
</table>
<span><LivePatch label="New Event" to={Routes.event_index_path(@socket, :new)} /></span>
With everything implemented above the LiveView application sends this message over the websocket (Phoenix.LiveView.render/1
):
1034 characters in size
[
"4",
"4",
"lv:phx-FyfZq0b_pvX_hQSF",
"phx_reply",
{
"response": {
"rendered": {
"0": "",
"1": "",
"2": {
"0": "",
"1": "",
"2": "<a data-phx-link="patch" data-phx-link-state="push" href="/events/new">New Event</a>",
"s": [
"<h1>Listing Events</h1>\\n",
"\\n<table>\\n <thead>\\n <tr>\\n <th>Name</th>\\n <th>Description</th>\\n <th>Capacity</th>\\n\\n <th></th>\\n </tr>\\n </thead>\\n <tbody id="events">\\n",
"\\n </tbody>\\n</table>\\n\\n<span>",
"</span>"
]
},
"s": [
"<main class="container">\\n <p class="alert alert-info" role="alert" phx-click="lv:clear-flash" phx-value-key="info">",
"</p>\\n\\n <p class="alert alert-danger" role="alert" phx-click="lv:clear-flash" phx-value-key="error">"
"</p>\\n",
"\\n</main>"
],
"t": "Listing Events"
}
},
"status": "ok"
}
]
This is what is sent to the websocket from our Surface View (1577 characters):
[
"4",
"4",
"lv:phx-FyfZguaRFuSzkANh",
"phx_reply",
{
"response": {
"rendered": {
"0": " phx-click=\"lv:clear-flash\"",
"1": "",
"2": " phx-click=\"lv:clear-flash\"",
"3": "",
"4": {
"0": {
"s": [
""
]
},
"1": "",
"2": {
"0": "",
"1": "",
"2": " data-phx-link-state=\"push\"",
"3": " href=\"/events/new\"",
"4": {
"0": "New Event",
"s": [
"",
""
]
},
"s": [
"<a",
"",
" data-phx-link=\"patch\"",
"",
">",
"</a>\\n"
]
},
"s": [
"<h1>Listing Events</h1>\\n\\n",
"\\n\\n<table>\\n <thead>\\n <tr>\\n <th>Name</th>\\n <th>Description</th>\\n <th>Capacity</th>\\n\\n <th></th>\\n </tr>\\n </thead>\\n <tbody id=\"events\">\\n ",
"\\n </tbody>\\n</table>\\n\\n<span>",
"</span>\\n"
]
},
"s": [
"<main class=\"container\">\\n <p class=\"alert alert-info\" role=\"alert\"",
" phx-value-key=\"info\">",
"</p>\\n\\n <p class=\"alert alert-danger\" role=\"alert\"",
" phx-value-key=\"error\">",
"</p>\\n\\n ",
"\\n</main>\\n"
],
"t": "Listing Events"
}
},
"status": "ok"
}
]
1577 - 1034 = 543 characters
This is pretty significant differences.
If I did everything correctly this should be a nearly 1 to 1 comparision.
chromium-add-event-without-validation.har.txt chromium-add-event-surface-take-without-validation.har.txt
I notice that there is a recently merged (but unreleased) PR relating to optimising diffs for static props: https://github.com/surface-ui/surface/pull/665
Describe the bug
Can't tell if this is a bug, more likely a flaw or technical debt, but I hope it's not the intended behavior.
Altering a SurfaceBulma.Button disabled attribute (only) produces way too much data and I don't think this has anything to do with the component itself, but rather the rendering engine.
Please note that in both cases (Surface and plain LiveView) the provided code is part of a component in a prepended list of such components, thus only the changed component is sent in each case.
1. Plain LiveView
Template code
Rendered html
Diffs when @card.favorite? toggles:
2. SurfaceBulma.Button
Template code:
Rendered html:
Diffs when @card.favorite? toggles:
How to reproduce it
Suffice changing LiveView code to using SurfaceBulma.Button component (or vice-versa).
The behavior you expected
The size of the diffs should be virtually identical, especially given that nothing except for disabled changes and the rendered html code has the same number of elements in both cases i.e. 1 per button.
Your Environment
Surface: v0.7.4 LiveView: v0.17.9 Elixir: v1.13.4