In search of the perfect JavaScript framework(dev.opera.com) |
In search of the perfect JavaScript framework(dev.opera.com) |
Strongly disagree. I find one-way bindings and one-way data flow much easier to reason about. A little less boilerplate code is not worth mental overhead, cascading updates and hunting down the source of wrong data in my experience.
What is important is not updating the DOM from the code and instead describing it with a pure function. React, Cycle, Mithril, Mercury do it, and it's time we get used to this. This is the real timesaver, not two-way bindings.
`Object.observe` is the wrong way to approach this problem. If you own the data, why invent a complex approach to watch it, if you could update it in a centralized fashion in the first place? Here is a great presentation on that topic: http://markdalgleish.github.io/presentation-a-state-of-chang.... I strongly suggest you read it ("Space" to switch slides) if these ideas are still alien to you.
Even Angular is abandoning two-way bindings. http://victorsavkin.com/post/110170125256/change-detection-i...
I, for one, welcome our new immutable overlords.
Approaches like jQuery soup or making SQL queries from PHP templates aren't “wrong” either. They are friendlier to beginner, too.
When I wrote “wrong” I meant “in my experience, inferior for building applications with complex stateful user interfaces and later maintaining them for a period more than several months in the face of changing requirements”.
Also, I'm not talking specifically about React. Of course it has its own shortcomings. All I'm saying is that relying on `Object.observe` and giving out global state to all components to mutate is the same as using global variables throughout the program. I don't know whether it is helpful to call global variables “right” or ”wrong” but I'd certainly stay away from them when there are ways to achieve the same without future maintenance nightmare.
My dumb takeaway is something like '2-way bindings demo well but are rarely what you actually want in real apps'.
I think (not 100% sure) Tom & Yehuda (of Ember) talk about how they became disenfranchised with 2-way bindings in their recent Changelog podcast episode on Ember 2 [1].
“Two” looks better than “one” on a feature checklist.
Quoting Sebastian Markbåge from React, “Angular is intuitively better to most engineers based on previous experience and ideals. React is better in practice. This is a biased opinion, but based one large org's experience of trying both models extensively.”
Normally not a huge issue, but it helps put AngularJS + Phonegap in an even worse position than before against native apps.
Two-way data binding to properties is generally not good. It increases likelihood of problems as updates to one value have to be observed to adjust other values. It is easy for bugs to slip in and hard to reason about the code.
Two-way data binding to methods is better. Methods that aren't focused on setting one value. In effect not direct data binding. It creates easy to reason about explicit code paths for changing of values. Observation of the change of properties is no longer required.
It is a useful tool and should continue to be seen as such.
[1]: http://facebook.github.io/react/docs/two-way-binding-helpers...
I don't even know how the second option is better to be honest. I may research it further.
:-)
Shouldn't updates just be event driven?
Is it really so hard for developers of parent components to subscribe to events of child components?
I've become more or less convinced that over-reliance on events is a big anti-pattern. Combined with mutable state, you get a lot of difficult to track changes in state. Some describe it as COMEFROM-based programming, the dual of the much-maligned GOTO paradigm. The COMEFROMs are your listeners and event triggers are labels they reference. The downfalls of each are pretty much the same: code that becomes increasingly difficult to reason about as it scales, because you have arbitrary global movement in control flow. COMEFROM is even worse because it introduces concurrency. Concurrency + shared mutable state is a recipe for subtle bugs.
So much of the time in a web app, an event only really has one listener. In these cases, a simple callback passed to the child component suffices. But even when you might need to have more than one component react to an event in a child component, I would argue that whatever is responsible for creating the child should still pass a single callback, which might simply be an entry point to dispatch logic on a controller whose only job is to mediate that behavior. Said controller might use or something else as its mechanism.
The fact that Javascript people keep saying this with a straight face is getting really absurd.
You do realize Javascript is also just an abstraction, right? And that the browsers that run it also abstractions, and the operating systems, and the kernels, and even the hardware itself has multiple layers of abstraction?
"Abstraction is dangerous" is just fundamentally wrong. Abstraction is the only way we get anything done.
What you really mean to say is that bad abstractions are bad. But stated so clearly, it becomes obvious that it's a tautology. Well-designed abstractions that leak as little as possible are essential to everything we do.
This stuff matters, because instead of having stupid arguments over "how much" abstraction we want (which really boils down to 99 layers vs 100 layers) we should be debating exactly what abstractions we want.
Get rid of all the abstraction, local state, dependency injection, symbol management and so on. Take HTML/HTTP seriously and think about REST in terms of HTML rather than JSON.
That's intercooler.js:
Here's an image I tweeted trying to explain how to get there mentally:
https://pbs.twimg.com/media/B9QNU-ZCQAECP-K.png:large
Yes, it's a simple model. And no, it doesn't work for every app. But many apps would be infinitely simpler and more usable in a browser by using this approach, and almost all apps have some part of them that would be simpler to implement using it.
Building the core and then using micro frameworks or components like react, jquery, etc leads to less walls as swapping is easier as time progresses.
You don't want to be caught high and dry stuck in years of monolithic to cleanup when the fad dies and at that point having abstracted away everything you need to know.
Outside of javascript, .NET WebForms and Drupal are classic examples of too much abstraction in monolithic fashion (those poor bastards stuck there - dead man walking), Angular might be another. The whole time you spent building addendums and machinations to a framework, not building the core of what needs to be known.
If the framework changes everything you do and abstracts core logic or the systems you are building doing things without you being aware, it might be easy to start 90% but there are gonna be problems and eventually walls and walls against you.
The only thing that should be monolithic and the base is programming languages and platforms. Everything else should be micro components or messaging.
When we're coding, we're optimizing for a couple of different things, really. First is real-world performance (represented by slowpoke DOM manipulation). Second is programmer performance (represented by inappropriate abstractions). A lot of things we can do in Javascript to make programming less difficult and complex result in poor real-world performance, and vice versa.
But what do I know? I'm not by any stretch of the imagination a Javascript expert.
Nevertheless, I have a favor to ask any framework developer out there - please, make it disassemblable and usable piece by piece outside of framework.
OP was right - sometimes i find some aspect of framework nice, but more often than not it is monolith part of the whole framework, which as a whole I dislike.
ps: current combination it seems to fit my mind workflow is Backbone (models + collections) + Ractive.js (Views) + Machina.js (for routing and defining "controllers"/states.) Although I am looking to use something else besides Machina.js in next project, as I want to have hierarchy now. And since it is all loosely coupled, I can replace parts.
The primary complaint appears to be that abstraction eliminates your ability to operationally trace the meaning of a program. This is true, but sacrificing operational denotations only hurts if you replace it with nothing else—and abstractions of general purpose languages are almost always more interpretable than the operational denotation of the base language itself!
Of course, there are always places for poor abstractions. I am not talking about these. Abstractions which are intentionally opaque, have confusing action-at-a-distance, etc---you're bringing down the name of abstraction in general. "Leaky" is insufficiently demeaning.
A good abstraction will have its own semantics. These can be equational, denotational, operational, what-have-you but, essentially, these semantics must be easier/simpler/more relevant than the semantics of the base language they're embedded in. Otherwise why abstract?
So what does React give you? It gives you, more or less, a value-based compositional semantics. Components have some "living" nature (an operational semantics w.r.t. to state) but they're mostly defined by their static nature. Because you can build whole applications thinking only about the static, compositional nature of components you can take massive advantage of this abstraction.
Ultimately, you do not want operational semantics for React. This is what gives us React Native, background rendering, and probably what will lead to sensible animations (in time). To define operational semantics, especially ones which have to look like or (worse) be identical to those of Javascript, would destroy almost all possibility of extension. At the cost of making things more complex and harder to reason about.
All so that you can just stick to "obvious" Javascript base operations.
True statement. Of course, it's more or less true, depending on how much the abstraction you're using leaks. Few (if any) abstractions completely encapsulate complexity, almost all will leak. But there's a range. Some abstractions elegantly cover a modular portion of your problem space and do it so well you only rarely have to think about what's going on under the hood (and will even produce effective clues as to what's going wrong when something does go wrong). Some abstractions awkwardly cover only part of a modular portion of your problem space, require a high intellectual down payment to even start to use, have gotcha cases that chew up performance or even break things, and require continual attention to what's going on just to keep development going.
Most are probably in between.
I think this is what JWZ is talking about in his famous "now you have two problems" assessment of regular expressions. I don't read him as saying "regular expressions suck," I read him as saying anything but tools from the high end of the abstraction quality spectrum means now you have two problems: (1) the problem you started with (2) the problem of keeping the model/details of how the tool works in your head. Regular expressions are arguably in the (maybe high) middle of the spectrum -- they may not cover your case well (ahem, markup) and they can send your program's performance to hell or even halt it if you don't know what you're doing.
Now, they're also broadly useful enough in all kinds of development that the benefits go up with the costs and so they're probably worth investing in anyway, as part of a suite of other parsing tools/techniques. So I'm not bringing the topic up to bash them.
But to take us back to the topic, I might be bringing it up to question the ROI of popular JS frameworks, which, as far as I can tell, are generally not at the the high end of the abstraction quality spectrum, don't have the broad usefulness of regular expressions to recommend them, and may not even survive longer than a handful of years.
It's a bastard child of React and Backbone.
* I have bunch of helper functions (UI and non-UI). Each function define in its own file and independent (easy to unit test). Personal library like jQuery but not a jQuery replacement.
* App is route based. One route to many controllers. Each controller is a page/screen on mobile.
* There is only one model (API) that interface with 3rd party library. API layer talks to 3rd party library to get data or gets data from server directly, caches data, etc. Provides sync (Cached data) and async (Cached data or fresh from server) interface to controllers.
* There is a app class or I call it a page manager. Responsible for managing pages like ordering, loading, unloading etc (Kind of big and complex 200+ lines of logic).
- Decides which page to animate in which direction on mobile (Loading new page or going back).
- Order of pages (Back button)
- Passes events to its controllers
- Decides which pages to keep in DOM, and which to remove.
--- If you go from homepage to comments to profile page, all pages are in DOM.
--- When you go back to comments page from profile page, profile page will be destroyed and controller will be notified. Same happens when you go from comments to home page.
--- If you go to same comments page again, it will be loaded as a new page.
* Controller:
- Each controller may have multiple CSS and templates
- Controller uses its template to render
- Using sync API to get data to renders page.
- If sync API returns no data, renders empty page with loading, and makes async API call.
- Controller are idle when transitioning (animating) from one page to another on mobile. (Very important for smooth animation)
- Simple but fat controllers
- Controller handles events, UI logic
- Self cleaning so that browser can collect garbage when necessary
I package app using node/gulp. Anything that is not specific to page/app related, it becomes part of a helper library. Each app has its own model (Data layer), and controllers. I use micro templates, precompile using node for faster performance.
I think author except in 1,2 points didn't bother to take side with performance aspects.
One answer to this problem of opaqueness in abstractions is having a well defined denotational semantics. This makes it clear that something can work in one way & only one way (without the need to dive into library internals). I feel that Elm is doing a pretty good job of tackling this for GUIs and signals.
I spent time today working in Clojurescript which wraps the Closure library. In the last month I have used Ember.js, Clojure with hiccup, and meteor.js. I really like all of these tools and frameworks. I used to use GWT a lot, and almost committed to Dart. So many good choices.
cough React Native cough
Javascript itself is a black box. It would totally suck if you had to know how it's implemented inside to use it.
"Leaky Black Boxes" however (like angular), are awful.
I quite like having a black box with two sockets on it, that I can plug cables into and have it just do whatever it's supposed to.
However, if the sockets are a weird shape, or there 39 of them, or they leak a strange green ooze, then I get upset.
Maybe this metaphor will help:
"Using other people’s code is like surfing. You control and surrender. You’ve got to trust the wave to carry you, but stop paying attention, and the first abstraction leak will throw you under the water."
https://medium.com/@dan_abramov/youre-missing-the-point-of-r...
Sure, you could say "bad abstractions are bad." And it's true there's a spectrum. Some are much less leaky than others, of course, some have the boundaries of what they do and their interface outside more elegantly shaped than others. But saying "well, we just don't want bad abstractions" doesn't capture well the fact that all abstractions either leak to some degree or are awkward coverings for some problem domains.
Nor does it really get us where we want to go in terms of "exactly what abstractions we want." We want useful ones. We want minimally dangerous ones. And the way you get the latter is to understand how abstractions are dangerous.
I stopped expecting anything of value in the article after reading this in the introduction, too.
You can't just say "remove local state and use HTTP instead" - people didn't invent local state from nowhere, it has a lot of advantages to relying on an HTTP call for everything you do. Particularly when you're scaling out widely or when you're dealing with mobile devices.
You do appear to be overly keen on pimping it, but it looks kinda unnecessary.
Anyhow, move that "use strict;" line into the IIFE. If it's global, you can't merge that file with other files. Some of the code out there breaks in strict mode. That's why it isn't enabled by default.
On "use strict", my understanding was that it applies per script:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
Strict mode applies to entire scripts or to individual functions.
Am I misunderstanding?
I looked at various data-binding javascript libraries, but they all seem like targeting much bigger problems than mine. So I resort to classic jquery, data attributes and ajax calls, but it feels very messy, like a hack not intended to work that way. It may be the fact that I'm new to this but it feels as if I'm missing something very obvious whenever I have to deal with that kind of very basic interactivity and dynamic. It's a simple form that temporarily needs to calculate and process inputs until the user saves the record and solving this seems way too complicated and messy. Are there any options for those simple cases and apps that otherwise would do just fine with a document-based architecture?
jQuery is fine for this too, but can become painful to use and organize once you have a lot of DOM manipulations.
React only starts to get complicated once you dive into the Routing + Data Fetching + Flux stuff IMO. React on it's own if you don't need any of that is pretty simple.
It hits the sweet spot between easy learning curve, abstraction, and interacting with the DOM nicely.
Sadly, it's not as hip as React/Angular/Ember these days.
So basically go back to writing Javascript like we were doing ten years ago?
No thanks.
I was there, it was hell, I hated every second of it.
The Javascript framework scene is very chaotic today but it's exciting, a lot of new concepts and approaches are being discovered on a weekly basis. A lot of them won't pan out but some will, and they will make writing Javascript even more fun than it is today.
And one thing I know for sure: it's hell of a lot more fun to write Javascript today than it was ten years ago.
People get so comfortable with their familiar abstractions that they forget they're still abstractions.
Taking the article as an example, even his "less abstracted" examples are absurdly abstract, and I doubt anybody here can really say for sure how they work completely, underneath all the abstractions. That's a good thing, because it lets us get things done and express ideas in hardware-independent ways.
Alongside Alien Blue, your app is my most used app on my phone. Your work is very appreciated!
If you want the events to be a directed tree, that's basically what angular 2 implements. Immutabilty is optional.
I can easily visualize a tree and reason about it. Gotos are very different. What you wrote seems like a bit of equivocation.
The parent creates the child (or at least it should, in my opinion). The parent should just give the child the callbacks it wants directly
That's what setting event listeners is. The parent knows about the interface of the child, not necessarily the other way around. So the child won't be able to know what callbacks to call. That is also why inversion-of-control leads to more stable platforms.
The problem is that people have a habit of misusing them and start using two-way bindings where they should be using events or similar patterns.
Two-way binding: Read & Write. Read & Write happens through getter/setter methods.
One-way binding: Read only. Writes happen by external event listeners.
Both can be used to enforce one way flow of data. Just as one way binding can be used to have a uni-directional flow of data.
A pseudo code example with two-way data binding with one-way flow of data:
<input type="text" value=getOrSetValue() />
function getOrSetValue(newValue) {
if (newValue) {
// Send an event to a dispatcher with updated value.
} else {
return value // can be single value from model or derived value, such as calculation of multiple values.
}
}Microsoft uses it their recent remake of their Azure portal; this page has a video on building large apps with Knockout: http://jbeckwith.com/2014/09/20/how-the-azure-portal-works/
Additional complexity without additional functionality, increased dependencies without increasing stability, poor documentation and buggy implementations; strongly coupled things which have no business being coupled and overt assumptions on what kind of application you are writing and how you ought to structure it.
They have fundamentals which don't interact with anything else - events that won't be caught, arrays that use their own iterator, etc.
They decide to suppress language features like console logging and replace it using different names. You never learn this by reading the outdated documentation but by startling discoveries 45 minutes into what ought to have been a 15 second bug.
And let's not get started on how utterly useless the call stack or object inspector becomes.
They presume there are fewer coherent approaches to programming in a language then there actually are so they shoehorn you into writing things in one specific poorly documented, buggy, highly complex, inappropriate, monolithic, and fragile way.
One of the main arguments for the abstraction approach is that they assist average programmers like a golfing handicap. But the reality is that the quality of the programmers work remains the same - but now with the abuse and misuse of the language AND a convoluted abstraction. You are far worse off.
The historical problem is that these things despite all of these clear flaws become immensely popular - each one for about 6 months.
And then your business critical application gets locked in. Locked in to the hottest framework from 5 years ago.
Awesome
btw, I tweet about my hatred of this specific topic here: https://twitter.com/frameworkhater ... I think a proper manifesto may be in order.
function _MY_framework.enterprise.displayMessageInConsole(msg) {
console.log(msg);
}
That does something that would actually be easier in vanilla JS.
All years of writing JS may have made me blind, but that's how I see most JS frameworks. Mostly useless.That said, manipulating the DOM in JS is a PITA. I can totally understand why you would want to do it another way.
I guess the DOM is not meant to be manipulated with ... And breaks if JS is turned off.
I think in the near future in maybe 5-6 years we'll see apps that are a mix of Browser and Native. That has the protected environment like a browser, that ask for permission to use hardware features, that can be accessed through high level API's, have native performance and run by typing an domain address much like WWW.
I bet you don't write web apps adding DOM nodes to the DOM document.You're already using an abstraction if you're using any DHTML api.But you didn't know that.
Since the rest of your script is already enclosed inside a single function, simply move the "use strict" a few lines down so it is the first line in that function. Then it will only apply to your own code.
If it has an entry point, it's not a library, it's an application. If said application does basically nothing on its own and you're supposed to extend it, it's a framework.
http://en.wikipedia.org/wiki/Software_framework
Pay close attention to the 4 bullet points in the first section.
> On "use strict", my understanding was that it applies per script
Yes. However, when you merge scripts, there is only one script at the end.
The site you linked to also mentions this problem.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
"This syntax has a trap that has already bitten a major site: it isn't possible to blindly concatenate non-conflicting scripts. [...] It is thus recommended that you enable strict mode on a function-by-function basis [...]"
Good point on the concatenation issue, I'll fix it for the next release.
It does.
There is an IIFE which is run when the script is loaded. Right before the object is returned to the global "Intercooler" variable, a callback is registered via a jQuery.ready() shorthand.
Practically speaking, this callback is the entry point (theoretically, it's the IIFE itself).
If it wouldn't have an entry point, adding attributes to elements would do nothing.
http://en.wikipedia.org/wiki/Entry_point
"an entry point is where control enters a program or piece of code"