In another comment I mention that the biggest conceptual improvement that Marko offers is async and streaming rendering. Async changes how you think about passing data to your view (you start rendering immediately and you can retrieve backend data asynchronously to start rendering parts of the page asynchronously). Also, Marko is not tightly coupled to a VDOM (while React is) and because Marko is not tightly coupled to VDOM rendering we can achieve significantly better performance (sometimes over an oder magnitude faster than React on the server) and this absolutely can make a difference. The slowness of React (and the sync rendering) were huge blockers from the very beginning when we considered using React at eBay. --Marko author
Web components do not have a great story about passing complex data. If you want to pass typed data (not just strings) then declarative HTML suffers since HTML parses all attribute values as strings. You can use JavaScript properties to provide typed data to Web component custom elements, but properties cannot be provided declaratively in HTML and now you have two ways of providing data. With that said, I think there is value in building UI components in Marko and offering web component custom elements as one option for embedding your Marko-based UI component into a page (using a small UI bridge layer... something that we plan on open sourcing). --Marko author
Async and streaming rendering is probably the biggest conceptual difference since it changes how you think about providing data to your view (in a good way). The paradigms of how you build UI components is very similar across all of the major UI libraries, but Marko aims to reduce boilerplate and offers what I think to be a superior syntax. --Marko author
Building web apps is definitely hard work, but it can be fun if you are instantly rewarded with working components/pages that didn't require a ton of code :)
There's really no magic with implicit imports. The imports are resolved at compile-time and fully resolved imports are used in the compiled code. I see developers making mistakes with explicit imports all the time (using the wrong path or forgetting to change the variable name or moving a component to a new directory and forgetting to update all relative paths).
Webpack aliases are a Webpack-only feature that I would consider a bad practice since it relies on globally configured aliases. Implicit imports in Marko are resolved relative to the template file (not global). Marko can be used server-side under Node.js with no need for a JavaScript bundler.
> I like Vue's idea of separation computed/methods/data blocks more than mixing all together. It for example helps the green devs to better understand the framework's lifecycle/internals and separation of concern ideas.
I see separation of computed/methods/data as unnecessary boilerplate. Methods are just functions on the component definition. On a related note, In Marko there's no need for "computed" properties and there is no need for a "data" block.
A lot of hard work has gone into making Marko one of the fastest and simplest JavaScript libraries for building web applications based on UI components. Many of the features of Marko were heavily inspired by React, but Marko is very different with a lot of unique features (async, streaming, concise syntax, etc.) and optimizations.
You don't need a virtual DOM or real DOM on the server to render HTML. The fastest way to render pages on the server is to compile templates into JavaScript programs that produce an HTML string (not virutal DOM nodes and definitely not real DOM nodes) and you can use almost any HTML templating engine to do client+server side rendering. morphdom is only meant for use in the browser when you need to update the DOM based on new HTML/DOM nodes.
Here's the notable differences based on my understanding after looking over the docs and the source code:
- morphdom does diffing between two real DOM trees while incremental-dom does diffing between virtual DOM nodes and real DOM nodes
- incremental-dom is only meant to be used with templates that compile to use incremental-dom (won't work with templates that render to HTML strings)
- morphdom can be used with any templating engine that produces an HTML string (or real DOM nodes)
- With additional improvements, morphdom could be used to do diffing between a real DOM tree and a virtual DOM tree as long as the virtual DOM nodes are upgraded to real DOM nodes if they need to be added to the final real DOM. In addition, the virtual DOM nodes would need to have an API that overlaps with the API of real DOM nodes.
Also, it appears that incremental-dom doesn't reuse DOM nodes that move to a new parent, but I could be mistaken. Hopefully someone more familiar with incremental-dom will chime in.
> it should become faster whenever browser DOMs become faster
The killer feature of React is that it made it possible to efficiently rerender the entire UI and for developers to not have to write code to manually manipulate the DOM. I hope browser vendors take note of this and do more to support this use case. Ideally, morphdom should not even exist and we could instead have the browser natively do the job of diffing/patching the DOM. Admittedly, there would have to be a lot of details to work out, but React, virtual-dom and morphdom are working in the right direction. Behind the scenes, the implementation might change and any good UI framework should make this a non-breaking change.
For the record, I got an email from Hacker News asking me to repost it since they thought it was a good story and it did not get many views. It's not wise to post something to Hacker News late on a Friday :)
Author of morphdom here. I don't necessarily disagree with anything you said. However, morphdom was designed to be fast enough in situations where you can't or don't want to introduce a virtual DOM. What matters more than a micro benchmark is the performance in a real application and paired with a real UI framework. Would I use morphdom in situations that required rerendering 1000 nodes every 10ms? Probably not. However, it will perform very well in almost most of the typical use cases. There was a lengthy discussion on performance in the following Github issue where I go into more detail on my thoughts: https://github.com/patrick-steele-idem/morphdom/issues/2
> virtual-DOM is fairly mature at this point where as MorphDOM looks very young.
morphdom is small and focused (about 220 lines of code). It either works or it doesn't and I have a decent amount of test cases to hopefully prove that it works (not to say that it is bug free... the DOM has lots of edge cases in some web browsers).
- virtual DOM implementations allow you to do things like React Native
This was built for the real DOM. While there can be value in abstractions, this was designed for a web browser.
Thanks for the feedback zghst. Here's my response to your comments:
> I'm sure it would help if these tests used the minified version of React on the server.
Minification sometimes makes runtime performance worse due to the tricks that minifiers use to make code smaller. Even if it did help, it really only makes sense to run this benchmark against the unmodified react package installed from npm. If someone wants to look into make the react module run faster by modifying the source then that would be great, but I consider that outside the scope of this benchmark.
> First (in SearchResults.jsx), setState accepts a callback in its second parameter, you don't need componentDidUpdate() or this.doneCallback (which is triggering more GC).
Didn't notice that `setState` supported a callback. I made the change and it did not make any noticeable change in the benchmark (it only removed the need for a few extra assignments). NOTE: Adding a "this.doneCallback" is not trigging more GC because there is only one done callback every being created and it is common across both the Marko benchmark and the React benchmark.
> Second in SearchResultsItem.jsx, you have an empty componentDidMount() function, you should get rid of that.
Removing the empty function did make any noticeable difference in the numbers but I removed it anyway for those who want to re-run the tests on their own.
> In SearchResultsItem.jsx, you should just go with a className (className={"search-results-item" + (this.state.purchased && ' user-has-purchased')}) instead of a style object, which becomes useless if you don't have a purchase.
I used styles specifically because most React guides recommend using inline styles (not CSS class names). Also, as part of the benchmark I wanted to test behavior and I defined the behavior to be: "When the user clicks on the "Buy Now" button the search results item should turn yellow". I attempted to implement this in the most appropriate way for both Marko and React and I think I did it in a fair way.
> By giving each SearchResultsItem their own state management, you're making it more dirty than you has to be, it should just be a simple list item. Typically in this type of app (Search), the SearchResultsItem component would be stateless (no this.state, getInitialState). When someone clicks handleBuyButtonClick, that communicates the change to the store, which then flows down to SearchResults, which "re-renders" the list of SearchResultsItems.
What you are suggesting is the Flux way of updating the data store and view and I think that is a great approach. However, if I were to update this benchmark to do things in the Flux way then it would complicate the benchmark and it would make the React test slower given that it would try to re-render all of the search results items instead of just re-rendering the one search results item that was modified.
This comparison benchmark was not designed to answer all of those questions. Perhaps just the following: "Does it scale well?"
The conclusion from these tests is that React currently does not scale well on the server (at least for this type of page) due to high CPU and memory usage while requests per second were low for a very modest page of 100 search results items. This is not surprising since the focus of React has been on the client-side performance, but it is important because many projects are adopting React as an "isomorphic" solution.
Hopefully the community and the authors of React can use this information to make React better. There is always something that can be learned from benchmarks like this.
React does do a good job, but I also think Marko+Marko Widgets will be a much better fit for certain types of applications since it also includes a very strong UI component model and it is lighter. If nothing else, hopefully others can learn something from looking at the code and seeing two different approaches.
funkiee, good catch on that issue. I just tried using the current index during iteration, instead of the actual unique ID of the search results item and that sped things up a lot. In fact, now React and Marko perform almost exactly on par for client-side rendering. Server-side rendering of React components is still very slow in comparison. I will update the README with the new results.
On a related note, the React docs were very misleading when they stated the following:
"In practice, finding a key is not really hard. Most of the time, the element you are going to display already has a unique id. When that's not the case, you can add a new ID property to your model or hash some parts of the content to generate a key."
Source: http://facebook.github.io/react/docs/reconciliation.html#key...
I tried following their advice and used the unique ID of the search results record and it turns out that was the wrong thing to do. It was better to use the index of the current iteration and I think that should always be the recommended thing to do given the performance difference.
funkiee, would you be interested in opening a Github issue or submitting a Pull Request if you believe that is the right thing to do? I'll give it a try in the meantime, but it would be great to have a public discussion on that so others don't make the same mistake (if it does indeed help). Thanks!
When the template compiler understands the actual HTML structure of the document the compiler can do some really interesting and smart things that would otherwise be impossible with template compilers that just see the document as text. Personally, I really like how the HTML template has the exact same nesting as the output HTML markup that it produces. To me, making the HTML template look as close to the output HTML markup makes things a lot cleaner and easier to understand.
Thanks for the feedback! I'm the lead developer on RaptorJS and I completely agree that the messaging needs to be improved and that is what we are working on right now. We haven't actually started actively promoting RaptorJS, so yeah, I suppose you could say that it was announced a bit prematurely (thanks zengr!). Nevertheless, the toolset itself is very stable and continuously being improved so can be presently used.
While I'm at it, I may as well take this opportunity to try to "sell" you on the merits of this toolset :)
RaptorJS is a collection of front-end tools and modules that work really well together, but each module/tool can be used independently. Everything is designed to be optimized and simple to use. There's a lot there so I always recommend that people look at each piece independently:
- RaptorJS AMD: Very lightweight (~1.8k gzipped) and optimized AMD implementation that integrates seamlessly with Node and Rhino on the server and works in the browser as well
- RaptorJS Optimizer: Extensible tool for optimizing web resources (bundling, minification, compiling resources, etc.) that can be used at runtime or at build time
- RaptorJS Async Package Loader: Uses information generated by the RaptorJS Optimizer to optimally download packages of any type of code (JavaScript, CSS, LESS, compiled templates, etc.)
- Raptor Templates: An extensible templating language that also works really well for UI layout (embedding UI components, etc.) and allows for really clean HTML templates. Best-in-class performance. Try it out: http://raptorjs.org/raptor-templates/try-online/
- RaptorJS Widget Framework: Simplifies the initialization of widgets associated with rendered UI components (regardless of whether or not they were rendered on the server or the client). Also provides a mechanism for binding widgets to HTML elements and inter-widget communication.
Sometimes the easiest way to explain things to developers is through sample apps so I would encourage you to take a look at the following Github repo:
https://github.com/raptorjs/samples
Feedback welcome and appreciated. Thanks for looking!