Wait, why could we not render the HTML on the server and only let the browser to the compositing? If there only was a protocol that would allow us to exchange input events and some drawing commands over some arbitrary network.. let's phantasize for a moment and call that protocol X. I would use it when it's mature so maybe from X11 on onwards.
Naturally, sending draw commands has a big downside in efficiency. Maybe we could later exchange X with something that only sends bitmaps?
A common criticism I heard about React a few years back was combining JS and HTML within the same file, as opposed to having them separate. As with most decisions, I think separation is an important trade-off you need to consider: it's easier to reason about the individual, separated parts that are well-contained, but it becomes harder to reason about the system as the whole.
If you have a complex front-end and back-end with a REST API, each time you modify a server route, you need to find all instances in your client where you make an AJAX request to that route and change them appropriately. The potential for inconsistency can cause numerous bugs.
GraphQL solves this by having a very flexible and standardized server (albeit complex), giving your client access to arbitrary structured information using a query language.
Purview solves this by moving your logic to the server-side, where you can access your database directly. Because everything runs on the server, the client-server interface is abstracted away, and you don't need to worry about using GraphQL or REST. You can make database queries, contact external services, etc. directly within your components.
To keep this maintainable, you split up your page into logical, reusable components, just like you would with React, and each component now not only contains your view logic, but also the server-side logic. Given that you've decomposed your components well, this makes it easy to reason about the whole system when it comes to any part of your page.
This is how cold fusion works, written by Adobe for too steep of a price. I wonder if we are just running circles around the same ideas. Are we really moving the ball with Purview? I’m lost.
> If you have a complex front-end and back-end with a REST API, each time you modify a server route, you need to find all instances in your client where you make an AJAX request to that route and change them appropriately. The potential for inconsistency can cause numerous bugs.
Sorry, but any sane codebase usually abstracts away HTTP info like routes through services which means that your components only know about the dataService and don't care if the data is coming from a REST API or localStorage
I've been playing around with a similar idea using nextjs. My idea was to create some kind of RPC interface with typescript that works seamlessly between server/client rendering, so during server rendering, RPC calls would just be regular function calls, and during client rendering/event handling, the calls would be made through sockets or HTTP requests.
There's a lot of criticism in this thread. I'm curious if you're using this in production. I love projects like this that really push the bounds of how these problems are typically solved.
We've been using this in production at the company I work at for about the past ~1.5 months. We've gone through a fair number of fixes and improvements throughout that time (bugs, race conditions, performance, etc.). It's definitely still a nascent project, but we're excited to continue using Purview and developing it. Would love to get your thoughts if you try it out sometime.
- Type-checking: there are extensive JSX typings (https://github.com/karthikv/purview/blob/master/src/types/js...) that ensure you're attaching event handlers with the correct signatures, specifying supported props, using valid HTML tags and attributes, etc. Static-typing guarantees are one of my big priorities.
- I'm not sure if LiveView intends to support nested components like React does. Having the ability to split up complex pages into components that you can nest and reuse (with mostly one-way data flow) is a key part of maintainability. I wanted to maintain a very familiar React interface, so you can pick up Purview quickly if you're comfortable with React.
Thanks for chiming in Chris! If you don't mind, I have a few implementation questions about LiveView:
- I noticed that you said you use morphdom for LiveView. I originally started with this too, but found that the DOM diffing doesn't work well with any client-side JS that may modify the page (e.g. a date picker, a tooltip library, a custom dropdown, etc.). I ended up switching to snabbdom (https://github.com/snabbdom/snabbdom/), a virtual DOM implementation, to avoid updating parts of the page that the user doesn't directly intend to modify. This helps with client-side interop, and it also gave us a nice boost in performance. Did you run into any similar issues with LiveView? Do you still use a DOM diff rather than a virtual DOM diff?
- On that note, what's the recommend way to interop with client-side libraries like I mentioned earlier? We use the WebComponents custom-elements spec to define custom tags that encapsulate client-side JS logic (e.g. we have an <audio-player>, a <date-picker>, etc.), and then in our Purview components we can just send down these tags. We also had to provide the ability to integrate with DOM events triggered by these custom elements. Is there a recommended approach in LiveView to integrate with these libraries?
- With respect to nesting in LiveView, do you call live_render() directly within a view's render() function? Do you pass props in this call? There doesn't seem to be a differentiation between props and state for LiveView--are both consolidated into "assigns"? Hence both are mutable? I'm aware that you use immutable data structures in Erlang/Elixir, but I mean in the sense that you can assign a different value to a prop that was given to you by your parent. If this is the case, how would that work if there's a re-render and the prop passed by the parent changes?
You should have a look at JSF (Java Server Faces) that explored, and failed miserably, in the same context for the last 15 years or so. (Probably less, dunno)
I was thinking the same. I expect elixer/phoenix to easily outperform js/node. I believe with phoenix it actually makes sense what they are going, which I can't really say from this "thing".
In React, forms are often built with this pattern:
* Type/change events fire on the field (e.g. username)
* Username updates the internal model (this.username = 'karthikksv')
* The submit button fires an event so the parent page can act on the entire data model ({ username: 'karthikksv', password: 'fluffykittens' })
I'm concerned that this model won't work well using server-side React components on a network with high latency – each type event would have to round-trip to the server to update in the DOM.
Higher latency connections will certainly have longer delays, but it's quite fast on a reasonable connection thanks to the persistent WebSocket, even if you're sending each letter that's being typed. We're using Purview in production at the company I work at, and we haven't had issues with input-related delays.
If you don't need to send each letter, it's recommended to not do so; the submit event object includes all form data, so you can do one final validation at the end, similar to what you'd do with a normal web server.
You're right, there's validation for the WebSocket messages sent to the server. For certain events (e.g. submit), the validation of user form data is left to you, just as you would normally be responsible for.
I know this comment is a joke, but this is actually how react was created. Facebook made a templating language in php called xhp (https://docs.hhvm.com/hack/XHP/introduction) and some people at fb developed react as a natural extension to xhp but in JavaScript.
I'm sure there's a use case for this but, for me, it's pretty rare to need pull new data or persist anything after most events, let alone every event. A single fetch and single save is usually all that's needed.
Whenever any information needs to be saved or processed by the server, you can perform your server-side logic inline in the event handler (e.g. update the database, contact external services, etc.). There's no need to make an AJAX request and have a corresponding API route. This abstracts away the client-server interface, and you get safety guarantees with type-checking.
Looks an awful lot like old fashioned aspx pages. They're the bane of my existence. The syntax looks a lot nicer and there isn't the odd CodeBehind shenanigans so maybe it's better to work with.
Perhaps I could've explained myself better. I meant that all the business logic of the components (i.e. event handlers, lifecycle hooks, setState() calls, etc.) run on the server, unlike just the initial server-side rendering that React provides. The server maintains the state of all components, and when an event occurs, the client notifies the server to run the appropriate event handler.
All the business logic of the components (i.e. event handlers, lifecycle hooks, setState() calls, etc.) run on the server, unlike just the initial server-side rendering that Next.js provides. The server maintains the state of all components, and when an event occurs, the client notifies the server to run the appropriate event handler.
It's a JS framework that targets server-side (node.js), and is written in a language that is not natively supported - TypeScript, thus requiring transpilation right out the box to use. Does that seem odd to anyone else?
On the update: This does seem kinda neat, and certainly useful in some situations.
You're not required to use TypeScript. As janpot mentioned, the library is transpiled prior to being published on npm, so you can use it with regular JavaScript. This is true of most TypeScript libraries.
God, GOD WHY. Whhhyyyyy. Build APIs, not database calls from templates like Rails or PHP garbage. We know how to do this nowwwwwwww don't make this Rails User.friends.comments garbage DB calls from templates
Naturally, sending draw commands has a big downside in efficiency. Maybe we could later exchange X with something that only sends bitmaps?