Marco Solazzi
Mar 5, 2018 · 9 min read

On Autumn 2016 I started writing my online portfolio. At that time I had little knowledge of React so I thought it would have been a good idea to test it on a side project like that.

After some months of coding (yep it was a side project of a rather lazy developer as I am, it took time) I ended up with a pretty satisfying result. My frontend stack consisted of React, Redux and GSAP. I also ended up writing a nice frontend-ops boilerplate which I used as base to build the main template of some scaffolding tools I was working on (yes cli + node.js === love).

The codebase was fairly nicely written, linted and I struggled to use everything that was best practice at the time. Since it was an online portfolio I opted for a hash based one page navigation with a toggleable overlay-ed menu which acted as an anchor list (with smooth scroll, I’m sort of a creative developer, you know).

My other big concerns were browser support (at least IE11) and accessibility.

For the first point I used Babel, its 2015 preset and kept CSS awesomeness at a low level (ie: just Flexbox, nothing more).

As for accessibility I wrote the HTML as semantically as possible, provided keyboard navigation, focus management and a fair color contrast. What I wasn’t able to provide was a usable scrolling experience for keyboard users. This is due to the usage of react-smooth-scrollbar, an awesome, cross-browser, high-performance smooth scroll library (you guessed it from the name, didn’t you) which is sadly not yet optimized for tab scrolling nor keyboard scrolling. I had to sacrifice usability for coolness… something I do a lot on my daily job; shame on me.

For the performance-lover among us, the code ended up being pretty huge, in my opinion. I split the application in two bundles: one for the application — which was average in size — and a big vendors bundle where I put everything imported from node_modules.

webpack output with the two JavaScript bundles

About this latter bundle, I was almost sure that GSAP was the one to blame for the overall package size. Don’t get me wrong, GSAP is an amazing animation library, something many creative developers have become addicted to for its performances and ease of use; but its lack of any real modularity makes you load a lot of useless stuff (Oh yeah, you could load Tweenlite and the every plugin you’d need, but then you have to keep track of every feature you use in your project).

To proof my point here is the output of webpack-bundle-analyzer:

TweenMax.js font-size is soooo huge!

As you can see, the most offending library after React+ReactDOM (weighting around 40KB gzipped) is GSAP (36.5KB).


Once in a life opportunity

Later in 2017 I had an opportunity that comes maybe once in a life. Due to a delay in my plans to move to Japan, I was stuck in Italy, at my parents’ house, unemployed. For some it might looks like an issue, but I took it the chinese way: as an opportunity.

I then decided to rework the very same portfolio to make it smaller, more performant and (hopefully) cooler.

Of React and Vue.js

One of the library I follow since its early steps is Vue.js. I’ve always been fond of it’s declarative Angular-style templating and of the amount of magic that comes with its simple APIs.
I know it’s not for everyone: people tend to avoid magic in favor of control and flexibility. In fact one of the main advantage of React+Redux in terms of developer experience is the complete control over components’ tree and application lifecycle.

But I develop websites — maybe small to mid scale ones — not Facebook or an APIed-PWA-SPA. I spend hours playing and re-playing an animation, tweeking a translateX transition or just figuring out the best way to make that cool image carousel more accessible. I don’t really want to waste time guessing whether I should use componentShouldUpdate and then testing if prop diffing impacted much more than “let it render”... Actually the projects I work on usually don’t have any budget for performance testing!

The main reason which drove me towards React in place of other libraries was SSR (Server Side Rendering). I needed it for SEO (the marketing people seems to love it).
When Vue.js announced SSR support in v2, it became a good candidate for something more than a small test application or a not-so-optimal frontend website with 3rd party pre-rendering.

The plan

Let’s go back to my spare time project… What I planned to do was:

  1. replace React with Vue.js;
  2. replace GSAP with the much smaller anime.js;
  3. refresh the codebase and refactor the templates while keeping the outcome unaltered;
  4. apart from switching to CSS-modules, don’t touch the SCSS codebase, else a rm -rf on the entire project would have been a saner option;
  5. see if I could add some new coolness.

In this first part I’ll cover the first two points, trying to rationalize the decisions I make when choosing a stack.


Replace React with Vue.js

Apart from my love for Vue.js, my assumption here was that switching from React would decrease both application and vendor size. At the time I wrote v1 of my portfolio React + ReactDOM + Redux weighted 46KB, while Vue.js + Vuex weight 24KB. Even compared to React 16, Vue.js is still lighter.

To get started I implemented a boilerplate with .vue file support. The main win of using .vue files is to keep both template and component logic in the same file making the DX similar to the one provided by React and JSX.

For those skeptical (or horrified) about templating, I’d like to remind you that Vue.js allows a render function in component’s options where you can even use JSX. Furthermore when used with vuetify or vue-loader, templates are compiled at build-time into optimized render functions; then why should you write them by hand when the machine is doing it for you?

Aside for this there are two more win points with Vue.js: attribute merging and the transition system.

Attribute merging

One thing I really hate about React and JSX is that it fools you into an HTML-like environment without actually being that way. If you think of plain HTML tags as components (actually in the browser they are DOM components) you could notice how some of them, let’s say <img>, requires it's own props (src, alt...) while allowing some other props which are not consumed by the component itself, but are used to create relations with the surrounding elements. It's the case for aria-* attributes:

React components force you to define every prop you’d want to apply onto the element else it’s lost. Take this example:

This application will render:

<img src="rabbit.jpg" alt="A lovely rabbit" /> 
^--------- where's aria-describedby?

Since we just defined src and alt props in Image component, we lost the passed in aria-describedby attribute. Sure, we could refactor Image like this:

But that’s sometimes considered bad practice and if for some reason we should implement some sort of prop processing we’d end up with something verbose like:

Another scenario is when, using BEM or other CSS architectures, you try to mix CSS classes from a parent’s context with component’s own. You may end up with something like:

Which, with an empty className, will leave you with an ugly trailing whitespace (yes, I'm a source code perfectionist). Ah, sure: you could use classnames... for something a view library should already provide...

The only elegant solution I’ve came up without going styled-component, was leveraging css-modules composes:

Useless to say that Vue.js comes with attribute merging and pass-through. It’s something that also good-ol Angular.js gave us!

Transitions with Vue.js

Vue.js has the most amazing and developer friendly transition system I’ve ever seen since Angular’s ng-transition. It's super flexible and effective, allows CSS and/or JS animations... with promises! (shame on you GSAP still requiring that old-fashioned onComplete callback)

React on the other hand, has the most ugly and unusable transition system I’ve ever seen. react-transition-group v1 was just useless and the new version, while promising, it's very... react-y (read: a complicated bunch or render props, JSX, div nesting and prescribed props).

This code doesn’t look so straight-forward to me

Without that library — or react-motion — you have to rely on refs to implement JavaScript animations. That's verbose and looks like an anti-pattern, making you feel dirty at every keystroke; they even made it somehow clear by deprecating the ref="name" syntax in favor of ref={(el) => this.name = el} — if you want to discourage a pattern make it annoying to realize.

Vue.js doesn’t blame you for that. It knows that sometimes you need to step aside from the virtual-dom logic and poke the DOM. Sure, it’s a great power; and from great powers…

That said, by leveraging Vue.js transitions, v-show toggler and some CSS magics I was able to almost completely remove components refs, for I used them just as tween target in GSAP.

Speaking of GSAP…


GSAP or anime.js?

I already wrote how much I rely on GSAP for my daily dose of coolness, but I admit its heaviness and old-style AMD-like source code give me severe hangovers. I wanted to try something lighter and with a modern interface; so I yarn-ed anime.js.

You can smell tween performances just from the logo…

Moving from 37KB to just 5KB already looks like a big win and the overall experience has been good: I really appreciated its flexible APIs where everything can be a function, but I soon realized its lightweightness has a price: element’s initial state (for example opacity) isn't always picked up correctly and timelines aren't as complete as TimelineMax; also CSS manipulation isn't as easy as with GSAP.

I still believe it’s a good alternative for experienced developers, since with some tweaking you can profit from its small footprint and compact API.


Side effect: Vuex

v1 of my portfolio came with Redux, the most popular Flux-like state management library in the React ecosystem. As a side effect of switching to Vue.js, I decided to use its official state management package: Vuex.

Vuex follows a centralized state paradigm like any other Flux library; anyway the most notable difference from Redux is that you mutate the state instead of returning a fresh copy of it.
After some time with Redux you might feel that as a mistake, but it’s totally in line with how Vue.js data management works so, at the end of the day, you still have full control over what’s mutating.

Vuex also provides a lot of helper functions to integrate the state and it’s actions inside a component, much more like react-redux does. As a bonus you don't need to use any fancy HOC to connect a component to the store, just import mapActions and mapState from Vuex and use them to map actions to methods and state to computed props!

If this might sound not so valuable, remember that you cannot tween a React component if it’s wrapped in connect HOC. Again, I cannot talk about the latest react-transition-group version but: man... they're coming late to the game!


So far…

I must admit that porting — and by porting I mean coding — the portfolio from React to Vue.js was the easiest part. Once I made up my mind around the new stack and because most of the application design was already in place, it was just a matter of rewriting components and wiring them together.

In the process, a decision that actually paid off was to use as few third party components as possible. That way my codebase wasn’t tied to special features provided by React-only components.
The only library I used was react-smooth-scrollbar which is a wrapper around the standalone smooth-scrollbar. It has been pretty easy to write a thin Vue.js component around it.

In the next article I’ll cover some optimizations I did on the codebase to improve the overall website’s performances.

Stay tuned!

Update 20/03/2018: part 2 is live. Check it out here: https://medium.com/@dwightjack/history-of-a-rewrite-part-2-7daa89761b41

Marco Solazzi

Written by

Frontend Web Developer, technical writer and speaker from Verona (Italy). Co-founder of Frontenders Verona Meetup.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade