Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2.0 React comparison guide collaboration #364

Closed
chrisvfritz opened this issue Aug 12, 2016 · 33 comments
Closed

2.0 React comparison guide collaboration #364

chrisvfritz opened this issue Aug 12, 2016 · 33 comments

Comments

@chrisvfritz
Copy link
Member

chrisvfritz commented Aug 12, 2016

@gaearon I've just updated the Scaling Up section of the Comparison with other Frameworks doc. If there's anywhere I've misrepresented create-react-app or have left out an important detail, please do let me know and I'll fix it. 😃

If you have thoughts on any other part of the React section, they'd also be very welcome! Maintaining accuracy and giving credit where its due are two very important goals of this document.

@gaearon
Copy link
Contributor

gaearon commented Aug 12, 2016

Sounds right 👍

@yyx990803
Copy link
Member

I'll leave some ideas regarding the section here:

  • The tone could be less definitive in the CSS scoping section, and we should definitely mention CSS modules.
  • Some React users have concerns that the JSX vs. templates example is a bit esoteric which makes the "syntax noise" issue more prominent than it is, I've merged Make the React example less esoteric #368 and we probably want to revise the comparison accordingly.

@chrisvfritz
Copy link
Member Author

@yyx990803 Just updated the CSS scoping language and slightly softened the syntax noise complaint. Thoughts?

@gaearon
Copy link
Contributor

gaearon commented Aug 15, 2016

I'm a little concerned about this section:

Vue should then perform about 20 times as fast in this scenario

I understand you specifically said you don't want to provide any benchmarks, but in this case using specific numbers like this might give users the wrong impression. Can you show that Vue is 20 times faster than React in these scenarios? Or perhaps it might be better to rephrase this section to avoid such suggestions. (If Vue is 20 times faster, it is amazing, and I’d love to see this demonstrated!)

@yyx990803
Copy link
Member

@chrisvfritz re CSS scoping: I think we can still improve the section, because some people have been having success with certain CSS-in-JS solutions, and we should avoid suggesting there isn't any good ones out there, but rather focus on how approachable and frictionless Vue's scoping solution is (and the fact that it plays well with existing tools like postcss).

@gaearon it probably won't be after your recent optimizations ;) But yes, I agree we should avoid these "magic numbers" without benchmarks because people tend to take it seriously whenever there are numbers cited.

@gaearon
Copy link
Contributor

gaearon commented Aug 15, 2016

Yeah, hopefully it will be better 😄
The main issue for me is that it’s weird to both use exact numbers and not use benchmarks.

@chrisvfritz
Copy link
Member Author

@yyx990803 Just update the Component-Scoped CSS section again. Let me know if this is more what you had in mind.

@gaearon Just updated the JSX vs Templates section for the new code example you provided. Would love to hear your thoughts. I'm also thinking of replacing "20 times as fast" and similar numbers there with less concrete language like "significantly faster", then putting together a simple visualization demo that displays framerate, so that people can see how each performs on their own machine and browser - I personally prefer that to a benchmark, since it'll be in a real context. Would that sound good to you?

@gaearon
Copy link
Contributor

gaearon commented Aug 16, 2016

Yes, this sounds like a great proposition. 👍

@gaearon
Copy link
Contributor

gaearon commented Aug 16, 2016

This is because JSX requires frequent compromises between readability and declarativeness that come down to individual judgment calls. These are some of the factors to be considered:

I would change this to

This is because JSX, just like JavaScript, requires frequent compromises between readability and declarativeness that come down to individual judgment calls. These are some of the factors to be considered:

@gaearon
Copy link
Contributor

gaearon commented Aug 16, 2016

To address the above two concerns, many choose to do as much as possible outside JSX, then only embed a variable, like children in the example above. The downside is that the render function then ceases to be a declarative description of the generated view, instead becoming an imperative list of operations with a final result.

I would add that the common solution is to extract functions (or more components), just like in JavaScript. It’s not a problem unique to UI at all.

@gaearon
Copy link
Contributor

gaearon commented Aug 16, 2016

The downside is that the render function then ceases to be a declarative description of the generated view

I would not agree with that statement. It is still declarative because its result describes the UI. Whether you use map or push, ternary of if, to produce this result, is an irrelevant implementation detail.

@blocka
Copy link

blocka commented Aug 17, 2016

I would like to add one point, that I think has not been mentioned. React, in general, is more "friendly" to functional programming paradigms. It embraces concepts like immutability, for example, and there are plenty of articles about using React in more functional terms. Vue, relies on mutability, however, which is not inline with functional programming. If somebody wanted to use redux with vue, they are at a serious disadvantage as there are no hooks to prevent unnecessary re-renders (componentWillUpdate).

So bottom line, if enjoy functional programming, or "functional style" programming, react offers a lot more for you.

If you don't care much for that paradigm, then vue offers a lot.

@yyx990803
Copy link
Member

yyx990803 commented Aug 17, 2016

@blocka fyi using Redux with Vue doesn't entail too much unnecessary re-renders, because Redux reducers simulate structural sharing to some extent, and any part of the state tree that retains old references will not trigger any re-render. There's also the opportunity to perform optimization in the component connectors, similar to that in react-redux, which makes shouldComponentUpdate unnecessary.

I'd agree React is in general more functional-friendly, but it's more of a design philosophy / ecosystem thing rather than technical advantage/disadvantages.

@blocka
Copy link

blocka commented Aug 17, 2016

The comparison is not just, or should not be just, about technical advantages...but also philosophy. Ecosystem is also a huge thing when deciding what library to use.

I was under the impression that the reducers work top down and replace the state on every action (I actually don't have much practical usage of redux, just pretty well read up.

I'm sure both of us don't want this thread to get too out of hand, but can I ask you to elaborate on the statement "There's also the opportunity to perform optimization in the component connectors"?

@yyx990803
Copy link
Member

yyx990803 commented Aug 17, 2016

@blocka we obviously can mention the philosophical differences, I was just pointing out that the technical assumptions in your comment was inaccurate.

When you return a new state object in a reducer in the form of { ...oldState, changedField: value }, any field other than changedField are copied into the new state object, and still retains the old reference. To Vue, this means any non-container components that receive the exact same props will not re-render (consider shouldComponentUpdate already implemented for you).

When you connect state from the Redux store into a container component, you essentially add a subscribe callback to the store, in which you reduce the whole state tree to the subset that the connected component is interested in before triggering component re-render. This is what the connect function in react-redux does. During this process there is the opportunity to do a shallow compare of the reduced state - if everything remains the same you can skip the re-render.

So technically you can achieve very efficient updates when using Redux and Vue without using something like shouldComponentUpdate.

@chrisvfritz
Copy link
Member Author

@gaearon Just made the updates you suggested. Then regarding a couple other items:

Defining declarativeness

I would not agree with that statement. It is still declarative because its result describes the UI. Whether you use map or push, ternary of if, to produce this result, is an irrelevant implementation detail.

With the definitions I'm familiar with, mutation within the function makes it imperative rather than declarative. I agree that the final return statement is declarative and I've updated to make that more clear. Let me know if the new language makes sense to you.

Just noticed a missing ul in React example

So I just updated this:

children = items.map(item =>
  <li key={item.id}>{item.name}</li>
)

to:

children = (
  <ul>
    {items.map(item =>
      <li key={item.id}>{item.name}</li>
    )}
  </ul>
)

If that doesn't match your preferred style though, I'm happy to update. 😃

@gaearon
Copy link
Contributor

gaearon commented Aug 17, 2016

With the definitions I'm familiar with, mutation within the function makes it imperative rather than declarative. I agree that the final return statement is declarative and I've updated to make that more clear. Let me know if the new language makes sense to you.

I think when people talk about imperative vs declarative UI, they talk about intent in a wider model. In React’s case, elements describe resulting component trees. This is why it is called declarative. Not because your code doesn’t use let or if statements. This is actually rather wrong—a function can be pure and still use them just fine as long as it doesn’t have any side effects or doesn’t cause any external mutations. Local variables don’t count: they are implementation details, and aren’t part of the function contract.

How you put items in array of those elements, or where you assign a variable value, has very little to do with the paradigm. It has zero observable effects and exactly the same semantics. I don’t want to make a big argument about it in case you disagree, but I find it weird to put any accent on this.

@gaearon
Copy link
Contributor

gaearon commented Aug 17, 2016

I would rewrite

To address the above two concerns, the simplest option is to do as much as possible outside JSX, then only embed a variable, like children in the example above. The downside is that the render function then ceases to be a purely declarative description of the generated view, instead becoming an imperative list of operations with a declarative (but less descriptive) result.

as

To address the above two concerns, the simplest option is to do as much as possible outside JSX, then only embed a variable, like children in the example above. The downside is that now you have to keep track of what these variables are assigned to, just like in regular JavaScript functions.

The part about “imperative list of operations” and “declarative (but less descriptive) result” seems very off to me. Beginners will think React somehow stops being declarative if you use if or let (no it doesn’t). Whether result is “descriptive” is completely subjective.

@chrisvfritz
Copy link
Member Author

@gaearon I definitely wasn't trying to imply an effect on the declarativeness of the overall UI paradigm or the purity of the render function, but just speaking of declarative/imperative programming styles. I can definitely see your point though, that the distinction may be unclear to beginners.

Thinking about it more, this entire section has grown far beyond what we originally intended. We're more interested in just showing off what makes templates great and I think we can do that without the deeper dive into the render function, so I've just deleted it. Let me know if that resolves your concerns.

@gaearon
Copy link
Contributor

gaearon commented Aug 18, 2016

Looks great 👍

@chrisvfritz
Copy link
Member Author

chrisvfritz commented Aug 18, 2016

So bottom line, if enjoy functional programming, or "functional style" programming, react offers a lot more for you.

If you don't care much for that paradigm, then vue offers a lot.

@blocka I'd like to add a little more to explain why this is the wrong way to think about it.

First, it's important to note that functional paradigms are a means, not an end. Colleagues of mine would certainly report that I "enjoy functional programming", but what I actually enjoy is code that is simple to reason about and produces extremely few bugs. A functional programming style is one tool that often helps me achieve this. In Vue, I have never missed immutable data, because it's made unnecessary by enhancing mutable data with automatic dependency tracking. This allows me to write:

this.todos.push(this.newTodo)

Then the UI and other computed data will automatically update accordingly - and very efficiently. I don't have to worry about state bugs or side effects and it's very clear what this code does. React, on the other hand, handles the state problem in a technically more functional way by forcing all updates to go through setState and encouraging the creation of new objects:

this.setState({ todos: todos.concat(this.state.newTodo) })

This is not only more verbose, but also slower. First, concat is less performant than push. Then without a custom shouldComponentUpdate function there's also no smart dependency-tracking, so re-renders will happen whether they need to or not. However, if we do add custom shouldComponentUpdate functions and incorporate immutable data structures using ImmutableJS:

this.setState(({data}) => ({
  data: data.update('todos', list => list.push(data.get('newTodo')))
}))

Then we can finally come close to the efficiency of Vue out-of-the-box. But at what cost? Being more "functional" in this case has made our code more time-consuming to write and more difficult to read. So Vue's reactivity system actually achieves the ends of immutable data better than immutable data itself - at least within the context of JavaScript, where immutability is far from a first-class citizen.

@gaearon
Copy link
Contributor

gaearon commented Aug 18, 2016

I don't see

return n < 2 ?
  n :
  fib(n - 1) + fib(n - 2)

as any more declarative than

result = n
if n > 1
  a = fib(n - 1)
  b = fib(n - 2)
  result = a +b
return result

You don't lose any properties of the declarative system if you start to use if statements or vars.

If you never reassign variables the point is even mooter. Otherwise it seems like you suggest that the only way to keep things declarative is to never give names to them.

Maybe my point will be a little more clear if you imagine numbers I nstead of JSX. It would seem strange that using ternary operator somehow makes number operations declarative, and using if statements somehow makes the exact same operations imperative.

(And yes, I'd love to have an if expression in JavaScript but we are all aware of the language we are dealing with so let's not be purists 😉 )

@chrisvfritz
Copy link
Member Author

chrisvfritz commented Aug 18, 2016

@gaearon It sounds like we may simply be working off different definitions. ¯\_(ツ)_/¯

Without using the words declarative or imperative, what I was referring to was the additional complexity and cognitive overhead of writing and reading code that tells a story, in comparison to code that describes a law.

Code that tells a story requires describing how we arrive at a value (i.e implementation details), which means implementation decisions. Then later, we have to walk ourselves through that story and keep it all in our heads as we do, because each line might change how lines below it evaluate - there's a whole new dimension we have to think about: time. For the example you provided:

return n < 2 ?
  n :
  fib(n - 1) + fib(n - 2)

Understanding when/how lines 2 and 3 are evaluated depend on line 1. It's relatively low in complexity, because to understand it, we need only concern ourselves with a single moment in time. In the version that tells a story:

result = n
if n > 1
  a = fib(n - 1)
  b = fib(n - 2)
  result = a +b
return result

Line 6 depends on lines 1 and 5, which depends on line 2 to know if it's evaluated at all, then if it is, on lines 3 and 4 to know what we're adding together.

Both examples are equivalent in terms of function purity, so the larger application is not polluted or otherwise affected, but one is definitely more complex (and therefore more difficult to read, modify, and debug) than the other. I agree that this is inevitable in render functions though, due to inherent limitations in JavaScript. It's certainly not due to any flaw in React, as the same compromises have to be made using Vue's render functions.

@chrisvfritz
Copy link
Member Author

@gaearon For the render performance section, I ended up going even simpler than a visualization demo, in order to demonstrate render performance more purely (outside the context of updates). If there's anything in the React examples that makes the comparisons unfair, please do let me know!

I also tried to make it clearer that what we're comparing is overhead over the minimal required work to render the UI, since neither Vue nor React can make individual DOM manipulations any faster. That seemed to be unclear to some readers previously.

@gaearon
Copy link
Contributor

gaearon commented Aug 25, 2016

There are a few reasons why I think the benchmark is still a little problematic:

  • JSFiddle is not a realistic environment. I would prefer to see a comparison done with a real production-ready setup, like what Create React App produces on npm run build (and a similar Vue template, respectively).
  • Measuring start time of both React.createClass and ReactDOM.render() seems a little unfair because Vue example doesn’t seem to differentiate class declaration and rendering operation. It’s known that React.createClass() takes more time to initialize, which is the reason we’re encouraging people to use ES6 classes (or functional components) instead.
  • It’s also not clear to me why Vue example uses a variable but React example uses component state. I’m not saying it makes a difference, but it was a bit weird in such a small comparison.
  • It would be great to make measurements after a warmup. In real apps, there are more components than a single component with a thousands of rows. We don’t optimize the code for “jsfiddle benchmark” use case. So I would recommend creating a hierarchy of components (2 or 3 levels), changing both examples to mount those instead, and making a few passes before taking the measurements.

@chrisvfritz
Copy link
Member Author

@gaearon Thanks for taking a look! The variable in the Vue example was a typo. 😅 To address the other items, I'll take some time this weekend to write a pair of benchmark apps.

@gaearon
Copy link
Contributor

gaearon commented Aug 26, 2016

Thanks!

@chrisvfritz
Copy link
Member Author

@gaearon Alright, here are those benchmark apps and some revised copy to go along with them. As always, thoughts/PRs are very welcome! 😄

@chrisvfritz
Copy link
Member Author

Hey @gaearon, just pinging on this. If you have time, we're thinking of officially publishing the 2.0 docs soon and want to make sure we're not spreading misinformation by mistake! I've also tried to better emphasize the benefits of render functions and JSX, so if you happen to think of something I've missed, let me know and I'll add it to the list.

@gaearon
Copy link
Contributor

gaearon commented Sep 3, 2016

This is great, thank you so much for doing it. 👍
Sorry I missed your comment earlier.

@chrisvfritz
Copy link
Member Author

Excellent! I'll close this issue for now then, but happy to reopen if something else catches your eye.

@51mon
Copy link

51mon commented Oct 8, 2016

What's the point of having a children variable into the JSX code ?
It would be simpler to have the if / else statement directly inside the return.

@chrisvfritz
Copy link
Member Author

@51mon We could endlessly bikeshed this example, arguing about style preferences. Many people find this version of the code more natural. Some will not. It was provided by Dan Abramov though, so it will be similar in style to doc examples people see throughout the React community.

kazupon added a commit to kazupon/vuejs.org that referenced this issue Nov 24, 2017
* pick up from commit

NOTE:
  ref: vuejs@8f55bb0

* translate prev commit
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants