Show HN: Under the hood ReactJS(bogdan-lyashenko.github.io) |
Show HN: Under the hood ReactJS(bogdan-lyashenko.github.io) |
The amount of complexity I'm willing to accept is proportional to the the difficulty of the problem. In this case it's manipulating web pages, which shouldn't be too hard. This isn't a knock on React in particular, but it seems all the major vendors are competing on complexity. React has "fibers", Ember has a "virtual machine", Angular has their own overblown architecture (I don't know anything about it, and don't want to).
Now that the DOM API is implemented in a standards-compliant way across most browsers, it should be the perfect time to use it. I don't like the current mess that is front-end web development, and more of the status quo isn't going to get any simpler, quite the opposite.
Shameless plug for my own DOM utility: http://simulacra.js.org
1. To sidestep problems with the DOM. The virtual DOM helps with performance, and fibers help with responsiveness.
2. To implement reactive components. Virtual DOM makes rebuilding sub trees of the DOM cheap, making it possible to have clean components that don't need to constantly attempt to tweak the DOM. Doing this style of programming without DOM diffing is possible, but much slower.
There are other approaches, but this one is significantly nicer to code for than DOM binding and is significantly faster than any naive approach.
Yes, you can extract the essence of React into a several kb bundle. It's been done to death. But why? React is fast, mature, has a great ecosystem, and it's nice to code for. It does nice things like seamlessly support SVG and intelligently batching changes. If you're truly making a complex app, it's worth the bump in bundle size.
The largest part of any web app is the browser. If the DOM wasn't so bloated and ridiculous, maybe we wouldn't need to spend all of our time minimizing our interactions with it. But if you try to count all of the work the browser has to do to layout and render one DOM element, suddenly React feels very tiny.
What's missing is that you can make complex web apps without any of these things, or the abstractions used can be something completely different. The reasons you stated just sound like ex-post facto rationalizations of what they did after they built it.
I've been in same situation once with my OS project. I've did something small that I've been certain does what all those other heavier solutions do. But when users came I've learned that there's unimaginable number of edge cases and scenarios that are common enough to require annoying amount of work.
React.js is not "DOM utility". Its battletested view library running one of busiest social sites in the world. It's highly performant, capable of handling thousands of components at single time, and handles countless edge cases, like maintaining input state while user is typing in it and its moved around DOM, normalizing events between browsers, maintaining scroll between large redraws or jumping user to components #fragment.
Ember.js and Angular are complete app frameworks that implement view, layers, services, data, communications testing, tooling and more. Those are solid options for people writing dashboard applications (think intercom or Podio), even if they are losing ground in public-facing sites to more elastic stacks like React or Vue.
Nobody looks at objective benchmarks. My own DOM utility is faster than React.js, and in fact it is not such a difficult feat, there are a few DOM utilities which are faster, also authored by relative nobodies.
I don't worry about catering to IE8 users, neither do most websites, and that number is vanishingly small. I'm also not interested in competing on complexity, which is what happens when one preemptively builds for edge cases.
I will probably never work on a web app that has a wider reach than Facebook. Setting up arbitrary personal goals for success like working on "one of the busiest social sites in the world" and even achieving that won't make me any happier. How much is enough? I've already proven that I beat Facebook at the DOM performance game, and still I'm the Dunning-Kruger idiot.
In fact I refuse to market my pet project as if it is something to be bought and sold. or to assign it a valuation based on how many big companies are using it.
At Facebook 'most browsers' has a very different meaning compared to other websites. When Facebook deploys code that works for 99.5% of their users that means they're failing ~10,000,000 people. Consequently they have a lot of code to handle the cases where they can't just rely on 'most browsers'. It's entirely valid that they maintain a relatively large library, and pretty awesome that they share it publicly.
With React Native I re-use over 75% of code from the web version of my apps. An actual measurable leap in productivity. Or even just for the web version, React and other frameworks (inb4 "not a framework"), provide their own proven patterns and best practices for scaling a code base, a task that is out of scope of any DOM utility library. And if providing those patterns requires more complexity under the hood, well, you don't refuse a free Lamborghini because it's "more complex under the hood" than your current Toyota.
I even see it in your landing page marketing.
> MINIMAL
> One function
> Its entire API surface area is a single function
Then I scroll down. var bindObject = require('simulacra')
var bindEvents = bindObject.bindEvents
var animate = bindObject.animate
var retainElement = bindObject.retainElement var helpers = require('simulacra/helpers')
var bindEvents = helpers.bindEvents
var animate = helpers.animate
There is no practical difference. It is just for those who read code but lack reading comprehension skills.To demarcate this separation better I have considered moving the optional convenience functions into a separate module, which might help clear things up.
Edit: actually it seems that you were being dishonest when quoting me, this is what it says, you cut it off short:
>Its entire API surface area is a single function (with some optional helpers and symbols)
That makes it pretty clear that the core functionality is a single function.
Calling React "DOM utility" does not sound fair. While one of the major problems it solves—slow DOM operations, I'd say that unidirectional data flow is even more important.
Fancy selling points like "unidirectional data flow" don't actually mean anything. Each fanboy has their own variant of this, like "transclusions", "immutables", "dependency injection", etc. These are all phrases I've heard that don't translate to tangible results.
This is how projects end up with millions of lines of black box dependencies. This is why `node_modules` folders that are hundreds of megabytes exist.
Take a look at benchmarks [0] and tell me, if any framework performs faster than vanilla JS. They probably are faster than vanilla code that you'd be able to write on your own, but that says more about your abilities than the performance of frameworks.
[0] http://www.stefankrause.net/js-frameworks-benchmark6/webdriv...
From a quick look at your approach, I have one concern:
<template id="product">
<h1 class="name"></h1>
...
You seem to use classnames as variable names. What if some other template has a "name" variable too? What if a stylesheet uses it?I prefer simple placeholders. Instead of:
<h1 class="name"></h1>
I would use: <h1>{{NAME}}</h1> <h1 class="name" data-bind="name"></h1>
It was also a design goal not to introduce a templating syntax, so `{{}}` brackets are out of the question.Fantastic work and a great resource for people wanting to dive into React's internals.
Great work by the author nonetheless.
Incidentally, this complexity is what led me to build my own little vdom utility <https://github.com/hyperapp/hyperapp>.
I'll be drawing inspiration from this chart to explain it.
There are simpler ways to get reasonably fast re-usable components on your project that introduce less accidental complexity.
Carefully consider which thing you actually need:
- A view library that uses components as the main building block (this usually means you already have other concerns like data modeling and routing taken care of)
- A full SPA framework (which people often get from the react ecosystem, i.e. React + Reflux/Redux/etc + React-Router)
Based on which one you need, there are other simpler (in the case of view libraries), and more coherent (in the case of full framework) choices.
"Seriously dude, you're still using React? That slow, bloated pig that creates a call stack 75 frames deep to change the label text of a button? Sheesh, get with the times, and use _____.js! It rocks!"
> An instance of what should be created (03)? Component… right, but which one? Well, it’s a good point. No, not <ExampleApplication /> that’s 100% :) We actually should instantiate some internal class. Let’s check out the next scheme at first.
Other libraries don't have that problem (because they didn't accept that trade-off in that fashion).
IMO Libraries that beginners should be shown should not be ES2015-in-every-example, and introducing transpiled DSLs for generating DOM elements.
KO also has components now, and they work super great. Also, while bigger frameworks like Ember (which I love) are just starting to figure out and cement how they work with engines (dynamically loaded chunks of your ember app), KO supported async loading of components with RequireJS so long ago.
Here are some options:
Component libraries -------------------
1) KnockoutJS (just data-binding, quite possibly the simplest I've seen)
2) Vue.JS (More complex, but one of the best sets of docs I've seen, fits in with the DOM model extremely well, few hacks, though I would have loved a simpler data-binding system)
3) Mithril.JS (super small, super fast, very simple. Transpilation tradeoff, it makes you just write the DOM as a JS function with elements like `m('div',[...])`. Have yet to use this one for a large project, but will very soon. Also has a little bit more support for the other thing you might need for a Single Page App.
Full fledged frameworks -----------------------
1) Ember (has been around a super long time, changes fast which is good and bad, and is great for large projects because it has a solid set of conventions)
UML is supposed to be an architectural diagramming standard. What's the point of diagramming in UML the actual code paths?
E.g.: you'd never have object shapes or function calls in an UML activity diagram.
This is just an ad-hoc flow chart AFAICT.
The "entire API surface area," as you put it, surely includes more functions than the only one you need for a landing page's hello-world. I don't evaluate something like Simulacra with a hello-world in mind, else I wouldn't need Simulacra.
My advice is to just change "one function" to "tiny API". Or maybe "just three functions in the public API". That's just as respectable and spares you from needing to split your API into a pointless "helpers" namespace.
const {bindObject, bindEvents, animate} = require('simulacra')
^ looks ideal to me.I've did Mithril for 6 months year an half ago, before that Ember.js for two years and Angular.js for year even before that. I've eventually settled with React.js because it was just view library, with everything else up to you.
https://mithril.js.org/jsx.html
I've never needed any of the others, not sure.
Complexity can be avoided until the number of features reaches some threshold (I don't think there's any debate that it's impossible to avoid complexity entirely if the app is large enough). In those cases, which I think are the majority of use cases, a simple DOM utility like yours would be enough to hide complexity because there wasn't much to begin with.
But some of the websites React powers exceed that feature threshold. For example, if multiple sections of a web app want to update at once, the author could leverage React's lifecycle hooks where needed and rely on its reconciler, but if using a simpler library, might have to explicitly schedule each update in a more hacky/less efficient way.
Your assumption that simpler is more "hacky" or less efficient, just isn't true. One can use built-in constructs like for loops in vanilla JS to update multiple DOM nodes at once, this yields the most optimal performance. Or one could use React/Redux and setup an action and a reducer and action creator and action type constant and... you see where this is going.
That's not the kind of use case that'd require React. I think we're on the same page that for the majority of apps, that setup is overkill. I'm just pointing out that there are apps with complex use cases where the "see where this is going" will end up being more elegant/simpler than fleshing out your own constructs (certainly more than a for loop)
It's 2017 and high time for JavaScript tooling to finally slow down a bit and settle into its bigger britches.
Facebook can come out with a project that's over 40k lines of code, and it is "extremely simple". But no, it is me with my little 5kb function who is the "astronaut-architect".
>It's 2017
I don't care what year it is, you act like the current frameworks are the only viable options in the future. Hint: they aren't.
Yeah… but they do translate to tangible results. Using something like React helps to make front-end code simple, scalable, fast, and testable.
The car analogy is even worse. A modern vehicle is better than a 60s Beetle by almost every conceivable metric – performance, emissions, safety. In both of these cases, it is preposterous to assume that complexity was introduced for no reason.
In the car analogy the improved performance of modern cars have more to do with mechanical engineering than software. And the analogy falls apart when you consider that modern web frameworks are invariably slower or roughly equal in performance to its vanilla JS counterpart, but never faster.
In this case it doesn't make any sense because people who use React (like me) have taken the time to see whether it matches up to its promises. Quite the opposite of "thought-terminating".
Do you have anything to say about how React was the single most loved library in the Stack Overflow developer's survey? https://insights.stackoverflow.com/survey/2017#technology-mo...
Surely it's not just "thought-terminating cliches" getting it up there; if it were, then Angular would have scored just as high as legions of programmers failed to critically analyze whatever framework they were using.
Hopefully you can understand how your ability to say "everyone else thinks in thought terminating cliches about their favorite framework, including everyone I'm talking with, but that doesn't apply to me" doesn't lead to a reasonable debate.
That's a way argument though; just because these are metrics on which frameworks are compared does not mean that there is no comparison possible. In fact, I'd say quite the opposite!
And the analogy falls apart when you consider that modern web frameworks are invariably slower or roughly equal in performance to its vanilla JS counterpart, but never faster
What are you measuring? I'll bet that React will be a whole bunch faster than whatever DOM reconciliation library you build up from scraps of vanilla JS, in the process creating your own half-implemented framework (because I'm pretty confident that nobody builds an entire application using copy-and-pasted Javascript.)
In the car analogy the improved performance of modern cars have more to do with mechanical engineering than software
So what? It's irrelevant. Mechanical engineering of a modern car is also much more complex.
How much of the buzzword is dependency injection you understand when forced to go through entire codebase and replace instantiation of some concrete class with something saner.
Not every user of some framework is its fanboy, some just choose based on the value they think particular framework provides. And React is not a framework.
There are two downsides to this:
1) You have to define twice where the data is displayed. Once in the template and then again in the mapper.
2) <div>{{NAME}}</div> explicitly tells the reader "The name goes here". In your solution one would have to look it up in the mapper to be sure what goes where.
The total absence of templating syntax is a deliberate decision. It enables one to start from an HTML mock-up or static page, and build interactivity on top of that without changing any HTML.
this is exactly how CSS works as well
I beg to differ. There is no mapping needed for CSS. CSS works by once defining where the style shall go: <div class=city></div>
And once what the style is: .city {color: blue; font-family: arial;}
Similar, template syntax once defines where the data shall go: <div class=city>{{NAME}} has {{POPULATION}} residents</div>
And once, what the data is: city={name: "New York", population: 8491000}
Neither need additional mapping.That is why it's called "most loved" and not "best tool to use", it's based on emotions. And this hardly has any relevance unless you're invested in the developer tools market.
IMO this is a good outcome: there is some marginal value in yet another "React-like but faster" library, but the same energy could help a lot more people if applied directly to the source of the madness.
If you're unaware, "fanboys" is a grenade to incite juvenile flamewars instead of collegial discussion.
>What's missing is that you can make complex web apps without any of these things,
To attempt better instructive discussion instead of arguing from vague and generic platitudes about "needless complexity", can you explain how your simulacra.js (~12kb) is better than reactjs (~130kb)?
For example, does Simulacra have the same features that React? If so, how did you eliminate ~100kb of Javascript to accomplish it, or conversely, what redundant or incompetent code takes up additional ~100kb of wasted bytes in React?
If Simulacra accomplishes its smaller 12kb footprint by having less features, which features in React do developers no longer need because of "modern browsers have standards-compliant DOMs".
In other words, some concrete analysis would be a more productive discussion.
But if you're going to accuse React's value proposition as being just a marketing pitch, I'd love to hear what evidence that virtual DOM is in fact pointless, and then why Angular 2 and Ember are doing the same sort of thing despite this, even though they have vastly different programming models than React.
Also, regarding your last point, I really doubt Facebook accidentally made DOM diffing without realizing it would improve performance of reactive programming in JS.
> significantly nicer to code for than DOM binding
There are other ways to code this nicely, but _those_ are slower than virtual DOM. Real DOM _can_ indeed easily be faster, but that's not nearly as nice to program for.
A lot of people like to point out that the "real DOM" can be just as fast, but far fewer people have any good examples of this being done.
Yeah, for making a button that signs you up to a mailing list or a simple SPA or something like that, you can just use the DOM. But if you have a complex app with a huge amount of data flow and many components, React (and Preact and etc.) absolutely will beat the naive answer because it will get rid of needless DOM updates. And it will do that without you having to do caching everywhere, because it's all done in one place at the lowest level before hitting the page. I'd love to see the horrible abomination of hand DOM updates that can magically beat a large React app.
edit: Just noticed this wasn't a reply to me, but I think the points I raise here are still fairly valid, if I'm understanding your reply correctly. Sorry if that was confusing.
https://github.com/guscost/protozoa
Performance optimizations, cross-browser events, and safety features not included. Good luck if you ever have to build a huge UI with it, you'll be the first person to try!
Seriously though, while it's pretty cool for embedding interactive DOM nodes in a webpage, don't think that you always need "simple" when sometimes "easy" is a much better idea.
All I did was write the only critical comment in this thread. Everyone else who replied to the OP contributed, "great job, thanks A+".
Here's all that's needed in the template:
<div class="city"><span class="name"></span> has <span class="population"></span> residents</div>
And the mapping would be: [ 'city': [ '.city', { name: '.name', population: '.population' } ] ]
If you don't want to manually define a mapping, you could do it automatically by assuming that some attribute like `data-bind` corresponds to a binding, but Simulacra.js doesn't make assumptions like that. you could do it automatically by assuming that some
attribute like `data-bind` corresponds to a binding
Yes, that could be done. That would mean to extend Simulacra. Or do it outside of it.To me personally, <div>{{NAME}}</div> is easier to read and reason about. Also, it enables you to do stuff like
<div>{{CITY}} has {{POPULATION}} residents</div>
Which is much shorter then <div><span data-bind="city"></span> has <span data-bind="population"></span> residents</div>
You can also do other useful stuff. For example: <div class="{{STATUS}}">{{MACHINE}}</div>
And then style different statuses via CSS.The reason is that every templating language I've used either is or becomes a Turing-complete language by itself.
Doing my own thing is of course possible and in many cases is fast to get something up. But doing it correct and proper takes a lot of time and attention. When I'm trying to get a project going - time is not on my side, but I still want it done correct and proper. If someone comes in and does a complex part of my app correctly, I'd use it. Its NOT a problem.
Edit: By the way, taking a snippet of a line from what I said and quoting it without its context does not make for good discussion.
But it is. You are depending on some complex third party code to do your job, how you want to punt responsibility to someone else is irrelevant. Web developers aren't owning up to the sorry state of front-end web development, because everyone says "it's not my problem", ask Facebook/Google/whatever.
Correct and proper have little meaning if at all in web development, they're usually dictated by the biggest companies and most popular bloggers. Moreover, these definitions are constantly in flux (no pun intended). Nobody cares if your web app correctly follows the proper conventions of your framework. Front-end web developers seem to focus more on navel-gazing about the "developer experience" than results.
I'm having fun doing it but i'm not doing it for fun.
Yes. It's called a web browser.
The additional complexity of react is a drop in the bucket compared to the complexity of the undertlying platform.
Why do you assume that vanilla JS code is terrible? Is it because the average developer can not be trusted to write competent code without the conventions of a framework? If one cannot write competent code without being completely dependent on frameworks, I wouldn't trust them to write code at all.
I won't be able to write efficient vanilla code for something even slightly more complicated than this benchmark, and I wrote one of the fastest virtual dom implementations out there, so what you expect from an average web developer?
Better performance just isn't a valid reason to use a DOM abstraction, it's a non-argument.
The speed of a for-loop is only a part of what makes a codebase 'fast'.
You don't have to bet, you can verify the results yourself. Look here at these benchmarks [0] and compare, Simulacra.js is 22-42% faster than React v15 (depending on whether you also use Redux or MobX). My "scraps of vanilla JS" performs faster! Vanilla without frameworks/libraries is and always will be king of the performance game, though.
[0] http://www.stefankrause.net/js-frameworks-benchmark6/webdriv...
What happens in an app with multiple views on screen into the same state and multiple pieces of data updating at once is much more interesting.
Remember most of these frameworks are aimed at things that are far more 'web application' than 'web site'.
Actually it is less interesting and indicative of mainly one aspect of web app performance, that is re-rendering. The JS Framework Benchmark gives one a more holistic overview that includes bulk insertion, deletion, swapping, events, startup time, etc.
On this benchmark, Simulacra.js outperforms React by a wider margin.
The purpose of downvoting is to indicate that your comments mostly consist of assertions that "react is bad" without really making a proper case for it, and as such don't really contribute to the signal of the discussion at hand - especially since the discussion at hand is about an explainer of the architecture, not a critique of the architecture itself.
If you look at e.g. discussions on vue vs. react posts you'll find that the "react is overcomplicated, here's a cost/benefit analysis" opinion is not controversial at all, and that a reasoned discussion of it will be responded to constructively (or that non-constructive responses will themselves be downvoted).
>A paragraph of well thought out text will be ignored.
I don't feel that it is necessary to have to explain in depth why having such a complex architecture for doing a simple task such as manipulating web pages is a bad thing. It would be like having to explain a joke. Moreover, such a critique would hardly be specific to React, but rather all of the current mainstream front-end web frameworks.
And maybe, maybe we have.