10 ms·
Fusion.js: A Plugin-Based Universal Web Framework
- kevining 8y agoI've been working with the Fusion.js team for the last year now, and I think it's evolved into a really interesting and modern web framework. Internally we've started rolling this framework to a few dozen web applications, and soon we will have hundreds of web apps running it. I think it's a great choice to use as a base for high-performance and complex web applications.
- navd 8y agoLooks like a competitor to next.js, and from their docs built on koa. It’s nice to see more libs in this space, but it seems a little over complicated to me with the “plugin” style arch.
- koboll 8y agoIt looks very similar to the Vue plugin system, which is one of the nicest parts about using Vue.
- kevining 8y agoCorrect about Fusion.js being based on koa. We find that this fits really nicely within the plugin system. The diagram is complex, but we inject the render phase into the middleware stack. Everything before `await next()` is pre-render, and everything after `await next()` is post-render. It makes it very easy to reason about the lifecycle of a plugin, as everything is in one place.
- tlrobinson 8y agoKoa is an improvement over Express, but I think it would neat if the JavaScript ecosystem adopted an even more functional middleware style, like Python's WSGI, Ruby's Rack, Clojure's Ring, etc. Conceptually it's simple: middleware is a function that typically accepts a downstream "app", and returns a new "app" which accepts a "request" and returns a "response": const middleware = (app) => async (request) => { // do stuff with request const response = await app(request); // do stuff with response return response; }; I worked on this idea way back in ~2009 (creatively called "JSGI" for the interface spec and "Jack" for an implementation) but promises weren't really a thing in JS, and async/await definitely wasn't a thing, so it was awkward. More recently Michael Jackson had a project called Mach which was a similar idea, but it's no longer active either: https://github.com/mjackson/mach https://github.com/mjackson/mach As an aside, one of my favorite aspects of this style is having symmetric interfaces for HTTP servers and clients. You could do neat things like use a cache middleware for both a client and server, or write a simple HTTP proxy in a couple lines. Anyway, with the addition of promises, async/await, and async iterators to JS I'm starting to dust off these old ideas. prototype here https://github.com/tlrobinson/twosixonesix https://github.com/tlrobinson/twosixonesix
- danneu 8y agoYears ago, I played with the idea: https://github.com/danneu/klobb https://github.com/danneu/klobb I went on to implement it in Swift (https://github.com/danneu/hansel https://github.com/danneu/hansel) and then seriously improved on it in Kotlin (https://github.com/danneu/kog https://github.com/danneu/kog). It was fun but hard to really make a convincing upgrade to Koa once you consider the rest of the ecosystem. For example, since Koa exposes Node's req/res, then you can still use existing Node/Express middleware.
- tlrobinson 8y agoI think it should be possible to write an adapter to use existing Express middleware. I haven't tried, but it's on my TODO list.
- carapace 8y ago"Oh? What's this? ... 'uber.com'? Pass." It may not be fair but that's literal though stream right there.
- deleted 8y ago[deleted]
- KaoruAoiShiho 8y agoCan someone write a sales pitch / direct comparison to next.js please? Does it only support react?
- lhorie 8y agoI wrote a small comparison here: https://fusionjs.com/docs/getting-started/framework-comparison#nextjs https://fusionjs.com/docs/getting-started/framework-comparis... The main difference is Fusion.js has more support for backend things. For example, we provide a GraphQL plugin, and plugins such as I18N are bundle-splitting-aware out of the box. The plugin system is universal (meaning you can isolate concerns by what the library is responsible for, rather than whether the code is server code, browser code, React provider boilerplate, HTML hydration code, etc). This plugin architecture has already proved to be very valuable on more than a few occasions. One example is a service worker implementation we've been working on. It needs a middleware, browser-side registration code, etc, but all of this complexity is encapsulated in a plugin that can be added to any app with one line of code. > Does it only support react? Many plugins have a `-react` version that allow them to auto-integrate with React, but the core itself is view library agnostic.
- deltron3030 8y agoThere's a typo in the beginning of the Next.js paragraph, developer -> developed.
- simplify 8y agoWill it be compatible with mithril? :)
- egeozcan 8y agoSeems to use Flow instead of Typescript. I wish MS and FB just merged their projects already. Really unnecessary friction when their syntax is 90% the same AFAICT.
- seanmcdirmid 8y agoThey aren’t the same though. Flow’s type system is nominal, typescript’s is structural, which is a huge language difference.
- merkaloid 8y agoi thought nominal typing was only for the classes?
- sverrejoh 8y agoThat's true. Both Flow and TypeScript generally uses structural typing, but flow also has nominal typing for classes.
- paavohtl 8y agoAre they though? As a TypeScript user with very little Flow experience it seems like they are about the same: https://flow.org/en/docs/lang/nominal-structural/ https://flow.org/en/docs/lang/nominal-structural/ "For example, Flow uses structural typing for objects and functions, but nominal typing for classes." This statement also applies to TS. Flow does have nominally typed Opaque Type Aliases[1], which are essentially newtypes from what I've gathered. However, you can build similar zero-cost newtypes in TypeScript using union types, casting and "unique symbol"[2]. [1] https://flow.org/en/docs/types/opaque-types/ https://flow.org/en/docs/types/opaque-types/ [2] https://github.com/Microsoft/TypeScript/issues/4895#issuecomment-398821503 https://github.com/Microsoft/TypeScript/issues/4895#issuecom...
- rictic 8y agoTypeScript seems to do structural typing for classes too: https://www.typescriptlang.org/play/#src=class%20Foo%20%7B%0D%0A%20%20field%3A%20string%20%3D%20''%3B%0D%0A%7D%0D%0A%0D%0Aclass%20Bar%20%7B%0D%0A%20%20field%3A%20string%20%3D%20''%3B%0D%0A%7D%0D%0A%0D%0Alet%20foo%3A%20Foo%3B%0D%0Afoo%20%3D%20new%20Foo()%3B%0D%0Afoo%20%3D%20new%20Bar()%3B%0D%0A https://www.typescriptlang.org/play/#src=class%20Foo%20%7B%0...
- megamindbrian2 8y agoLooks like Angular 1
- rajangdavis 8y agoCan you explain what you mean?
- lhorie 8y agoThe dependency injection (DI) system is indeed inspired by Angular's. It has some major differences though: - Fusion.js DI is token-based rather than string-based, so no naming collisions - We support statically typing injectables (similar to Angular 2+) - plugins are the only injectable entity type (whereas Angular is conceptually complex: e.g. modules, services, providers, factories, etc) - plugins are isomorphic, whereas AngularJS injectables are not I sorta already expected that people might get mixed feelings when seeing a DI system, but we spent a lot of time designing/tinkering with the plugin architecture to make it truly useful for managing complex library integrations and complex backends. I'd be happy to answer any questions about how we've been using it.
- rtsao 8y agoThe Fusion plugin system is rather powerful and enables colocation of related/coupled code that would normally be spread across several places. For example, the Styletron plugin for Fusion (an integration for a CSS-in-JS implementation) will do several things: * Wrap the application component tree in a React context provider component (which provides an instance that components will render styles into) * On the server, extract rendered styles after SSR from provided instance and add necessary markup into the server-rendered page * On the client, hydrate the provided instance from the server-rendered styles * On the server and in development, set up a route handler that serves two assets, a web worker implementation and associated WebAssembly binary [1] * On the client and in development, fetch and execute the web worker. Normally, this would be a somewhat difficult integration because of CSP-related issues with web workers, but because the plugin sets up its own route handlers, the requests will be same-origin, sidestepping most CSP issues that normally arise. Additionally, Fusion plugins can also modify response headers for requests, so if needed, CSP headers could also be set appropriately. All the code to do this actually is related to a single concern, namely styling, but in a universal web app, such things typically requires the involvement of many different parts of the application lifecycle and both server and client code. Fusion plugins allow you to slice up the independent parts of your application logic in this fashion, somewhat analogous to how colocating HTML/CSS/JS for individual components in CSS-in-JSX is often much nicer than splitting apart component implementations across separate HTML/CSS/JS files. [1]: This web worker generates debug CSS at runtime that maps rendered CSS to the source styled component definitions in JS using source maps, making it easier to reverse map the rendered CSS to the source CSS-in-JS when inspecting the DOM with the styles pane. https://github.com/rtsao/css-to-js-sourcemap https://github.com/rtsao/css-to-js-sourcemap
- calebm 8y agoDependency injection scares me as it feels like a move towards the JavaEE/Spring world.
- pjmlp 8y agoNah, we already had it on the COM/CORBA world.
- deleted 8y ago[deleted]
- erlich 8y agoMe too. The increase in complexity and lack of visibility from a DI system, vs what the manual wiring code would look like, can be immense.
- lhorie 8y agoHaving used other DI systems, I think the parts that most confused me were that the injector would usually be hidden away (meaning you couldn't see _what_ was being injected), and also the conflation with various initialization patterns (factories, providers, singletons). In Fusion.js, the injectable registration is done in the entry file, and initialization patterns are the concern of the service API. I think these design choices simplify things a lot.
- swah 8y agoThe author seems to be the same person behind http://mithril.js.org http://mithril.js.org
- lhorie 8y agoHi, that's me :) Fusion.js is a team effort though: https://fusionjs.com/team https://fusionjs.com/team
- erlich 8y agoHow many full-time employees working on the framework?
- lhorie 8y agoAll of the people in that page are full time employees and part of the Web Platform team at Uber. The only minor technicality is that Swojit is interning with us.
- sbjs 8y agoI thought your name sounded familiar! I played around with Mithril.js for a bit before settling on React.js and was really impressed with how you took the simplicity of JS and used it to your advantage with Mithril! Sad to say that I couldn't replicate that cleverness in my own code because I could not really wrap my head around the reasoning you used to come to code you did in your examples of using Mithril, but that's just more evidence that you're a smart guy, so thanks for making Mithril which helped widen my view of what could be done with JavaScript frameworks! Also is there any reason you chose Flow over TypeScript?
- keb_ 8y agoFunny you say that, as for me it was the exact opposite. I had tried multiple frameworks before discovering Mithril, and it was the first that clicked with me. I think what appealed to me the most was the lack of magic, its minimalism, and flexibility. Whereas with other frameworks, I felt I was always encouraged to `npm install <another-package-or-plugin>`, Mithril made me dig deeper and understand the nuances of JavaScript.
- polskibus 8y agoIs this framework tightly bound to nodejs or can it be used with other backend stacks like asp.net core or spring?
- kevining 8y agoFusion.js is a javascript framework meant to be run on Node. There's nothing stopping you from calling out to other backends and asp.net if you stand up a Fusion.js frontend Node server.
- panoply 8y agoI'll use anything that the author of this article (Leo Horie) releases or is attached too. He is one of the greatest minds in JavaScript of this generation. After Angular, Vue and React I stumbled upon his framework Mithril.js and it was like I stumbled across the shroud of fucking turin, that framework is true poetry.
- jdonaldson 8y agoThat's a funny analogy, the Shroud of Turin is one of civilization's biggest forgeries and hoaxes.
- swyx 8y agohmm. so he made Mithril but he's using React here? any insight on why that decision?
- brlewis 8y agoI certainly don't speak for him, but I think it's a combination of things. First, Fusion is a group project and not his personal project. Second, React has a virtuous cycle where because there are so many jobs calling for React, more people learn it. People also seek out jobs that involve React because it will help their resumes. Demand keeps feeding supply and vice versa.
- lhorie 8y agoI joined Uber last year. They had already standardized on React long before that.
- watty 8y agoLooks awesome and love the focus on plugins. I also like some of the choices made, Redux, React Router, Security, etc. but going with Flow?? Our company has standardized on Typescript, it has practically "won". Unfortunately for me, introducing Flow would get shot down.
- t1amat 8y agoIt’s a library so either they need to be convinced to provide definitions or the community needs to be convinced to build them. But at the point that they are built it’s no different than anything else in your stack.
- erlich 8y agoHave any of the maintainers used Next.js? What was your opinion? What does Fusion.js do differently/better?
- lhorie 8y agoWe did evaluate it. Some of the things off the top of my head that I think Fusion.js does better: - isomorphic treeshaking - more powerful bundle splitting (i.e. lazy loading can happen in any component rather than only at "page" level) - async server-side rendering is composable via HOCs rather than only at top-level getInitialProps - more things are provided by the team via plugins (e.g. I18N, CSRF protection, atomic CSS, async font loading, etc) - better support for maintaining server-side complexity (e.g. by using Koa + DI system to make testing/mocking easier) - out-of-band brotli level 11 compression for static assets The team is about a dozen people now and we've been working on Fusion.js for over a year, so there's probably other stuff I'm forgetting right now :)
- erlich 8y agoThanks! > lazy loading can happen in any component rather than only at "page" level You can use dynamic imports anywhere in Next using `react-loadable`. > more things are provided by the team via plugins Next relies on its thorough `examples` dir for integrations. But it means a lot of manual coding. I had a crack at a plugin system in the past (i.e plugins for adding features like apollo, redux, etc. AOT like stuff). I found that it obfuscated the code too much. Makes it hard to trace what is happening and make adjustments. The closer the code is to the "Getting Started" examples of Redux, I18n, Apollo, the easier it is to tweak and understand. E.g. Sometimes it might be clearer to just wrap a Provider manually around the app root, than expose a plugin hook. Because you can see the React tree, whereas with plugins everything is dynamic so you must rely on logging. I think devs are naturally drawn to DRYness and dividing code up by feature, which is what a plugin system offers, but there are big tradeoffs. I'm interested to dive into Fusion to learn more about their approach though. > - better support for maintaining server-side complexity (e.g. by using Koa + DI system to make testing/mocking easier) Great to see you adopting Koa! I use it myself, and feared that the community had stagnated, but now with Fusion, it may be reinvigorated. > DI system to make testing/mocking easier Very cautious of DI systems. Same reasons as plugin systems: its hard to see what is going on. And sometimes manually wiring up all the dependencies is actually not much code at all, and you can manage cyclic dependencies and ordering easier. Keen to dive in deeper though.
- Bahamut 8y agoThe idea behind this looks awesome - if I understand correctly, this is to encourage people to program more to interfaces provided by plugins so that implementations can be swapped more easily. This does not require a type system either to use, which lowers the barrier. The one thing I think should change though is decoupling from requiring Node from the runtime. I think the broader JS ecosystem could benefit from some of the ideas this library seems to promote.
- busstram 8y agoThis looks really promising. Amazing work! I hope this will catch on, so it can be improved even more.
- brillout 8y agoReframe (https://github.com/reframejs/reframe https://github.com/reframejs/reframe) is an alternative that focuses on flexibility. Fusion.js locks you in, Reframe doesn't. (I'm Reframe's main author.)
- simonrobb 8y agoCan you expand on why you think Fusion locks you in? Without a more detailed argument this sounds like you're just plugging your product.