Adding code split React frontend — without losing Vue — to Sails 1.x app

Noitidart
Noitidart
Dec 31, 2018 · 6 min read

Sails comes out of the box with lots of complicated pages all done for you, it’s awesome! Those pages are usually written in Vue though. This article presents a technique where you can install React and move forward with new things in React, WHILE keeping the Vue components until you find time to convert them to React.

With the material presented in this article, you have all you need to completely remove Vue, you just have to finish converting all the out of the box components to React, and then remove the parasails and vue script imports. I will help by writing a future article, showing how to convert the complicated pages (form based pages) to React. (That article is here: Server side rendering in React in a Sails 1.x App).

The article also shows how to make each page script be code-split. It works for both React and Vue page scripts. If you want to check this article out only for the code-split Vue page scripts, you can do that. The way Sails comes out of the box is it loads every page script on the page, and then checks the HTML if the div for that page script is there (by looking for a div with the id for the page script). With the code-split method, only the script for that page is loaded.

If you want to use this article only for code splitting your Vue page scripts, install webpack from step 1, and then copy and paste the code from step 4, in the “Let’s begin…” section.

Why code splitting?

Currently sails, loads all the page scripts onto the homepage. It executes each page script, if the id of the container it is targeting is not found, then it does nothing. This is my reason for adding code splitting.

Let’s begin…

  1. Create a new Sails app following the “Web App” template, and replace Grunt with webpack by following the article here: Replacing Grunt with Webpack in Sails 1.x.
  2. Install some babel transformations for babel — npm i -D @babel/preset-react @babel/plugin-transform-runtime @babel/plugin-syntax-dynamic-import @babel/plugin-proposal-class-properties @babel/plugin-proposal-nullish-coalescing-operator @babel/plugin-proposal-optional-chaining — then apply the babel transformations, by updating /.babelrc to this:

3. Install React — npm i react react-dom

4. Update /assets/js/index.js so we can import all our Vue components at top, and then below render our React pages with SAILS_LOCALS passed in as props. (we currently do not have any React pages) (Note for future: this step is responsible for injected data from actions/controllers to the React component)

We have a variable called pages here, each object takes three keys: isVue, containerId, and lazyComponent. Anytime you add a new page script, you must update it here. If the page script you added is React, then set isVue to false (right now we have no pages in React so in the below code, all are set to false). The containerId is the id attribute of the <div> it will be mounted in, this is the id set in the page’s respective EJS file. lazyComponent is the path to the page script.

5. Now you can do sails lift and your app will work as expected. All the old Vue components will still work. I will now show you how to take over a server rendered page in the frontend, and then show you how to create a new page in React.

Converting server side rendered Vue pages

Lot’s of pages came out of the box with Vue. We want to eventually convert those to React. Let’s start by showing how you can take over the server side rendered FAQ page. This page renders server side, we can see this by looking at /views/pages/faq.ejs. Before, the Vue file/assets/js/pages/faq.page.js was used to add functionality. But in React, we cannot partially add functionality, we have to render over all the content. So let’s re-render the server side content in the frontend.

  1. Create /assets/js/pages/FAQ/index.js this will be our React version. Copy and paste into this file the server rendered HTML from in between the <div id="faq">and its respective </div> tags from /views/pages/faq.ejs. Then fix up the JSX syntax (changing special characters into HTML entities, and replacing class= with className=, and closing unclosed tags like <img> to <img /> it should end up looking like this:

2. Delete /assets/js/pages/faq.page.js.

3. Go to /assets/js/index.js and change the entry in the pages array for the FAQ page to tell it that it is now in React. Change isVue: true to isVue: false and let’s update the import path to point to our React version by updating lazyComponent to () => import('./pages/FAQ').

4. Now do sails lift, and go to the FAQ page, you will see a flicker, as it first loads the server side HTML, then React loads the code split bundle, and renders your component version of it.

Notes on improved server side rendering in React

Ideally we do not want to re-render over the content for with ReactDOM.render. React provides a method to render a string from the server with ReactDOMServer.renderToString, and then it offers us ReactDOM.hydrate to take over the server rendered content in the frontend. I will write an article on how to do this. (That article is here: Server side rendering in React in a Sails 1.x App)

Create a new page in React

  1. Use the generator to create a page — sails generate page hello-react — this creates 4 files:
  • views/pages/hello-react.ejs
  • assets/styles/pages/hello-react.less
  • assets/js/pages/hello-react.page.js
  • api/controllers/view-hello-react.js

2. Go to views/pages/hello-react.ejs and remove the v-cloak attribute from <div id=”hello-react” v-cloak> and then remove all the inner contents. (Unless you want to server side render stuff, like in the above example). It should now look like this:

3. Go to config/routes.js and add a route to our page let’s add in ‘GET /hello-react’: { action: ‘view-hello-react’ } let’s also whitelist this in the policies so we don’t have to login by adding view-hello-react': true, to config/policies.js.

4. Delete hello-react.less and hello-react.page.js.

5. Create /assets/js/pages/HelloReact/index.js and also a style file here, /assets/js/pages/HelloReact/index.less (you can use .css if you want, we installed LESS and CSS loaders into webpack). Then let’s update /assets/js/pages/HelloReact/index.js to be this:

Let’s update /assets/js/pages/HelloReact/index.less to:

6. Let’s go to /assets/js/index.js and change add an entry in the pages array for our new page. Our entry will have isVue: false because we wrote this in React. We set the containerId to hello-react as this is the id of the div in our EJS file (views/pages/hello-react.ejs). We set lazyComponent to a () => import('./pages/HelloReact') as this is the import path to our React component. Updated code is:

7. Then do a sails lift and login to the app and go to /hello-react and you’ll see this page.

Notes on how to server side render this page

This process is the same as “server rendered content”. If you want to render server side content for this page (so search engines can see this content, and also avoid seeing whitespace until the script loads and is injected), just do so in the respective EJS file. You would copy the JSX from your react file (in our case /assets/js/pages/HelloReact/index.js) and convert it to HTML and then paste it into the EJS file (in our case /views/pages/hello-react.ejs). If you had custom CSS, then add it here. We had a LESS file, so we’ll convert that to CSS by hand and paste it here. We manually convert because webpack doesn’t run here. Your EJS file would then look like below. Now if you load this page, you will not see the blank space, you will see your HTML, until the it gets injected.

The article on server side rendering with ReactDOMServer.renderToString will take care of this for us, so we do not have to manually convert CSS or even copy and paste the JSX as HTML. This is the way you should do server side rendering, instead of the manual process noted above. (That article is here: Server side rendering in React in a Sails 1.x App)

Note about data from action/controller to the page

In both methods above (creating new React page, and converting to a React page), the JSON returned from an action/controller are injected into the props of the component. This was done in step 4 of the “Let’s begin…” section.

Future

  • Ideally though, we would write the JSX only and server side render that with ReactDOMServer.renderToString , I will write about that in my next article. Server side rendering is only needed really if you want search engine optimization. As most search engines don’t render javascript, and the one that does (Google) only renders till the first async hit.
  • I will also write an article using react-final-form to conver the Vue forms into React. As most of the out of the box Vue pages come with forms.

Noitidart

Written by

Noitidart

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