Separating Monolith Laravel + VueJS to Backend and Frontend with Webpack5

Nima Habibkhoda
CodeX
Published in
9 min readMay 4, 2022
Decoupling Monolith to Backend — Frontend

We in Trengo decided to decouple the Monolith architecture into Backend and Frontend and I was honored to do so. For sure it depends on the project potential, I believe it is useful to know how we discovered the rabbit holes and how you can solve them.
I am Nima and welcome to my blog.

The problem

It is up to you and your team structure to work on a Monolith architecture or a Backend Frontend or Microfrontend, etc… I am not going to explain the pros and cons, but if you decided to separate your Laravel+Vuejs Monolith into backend and frontend, it would be a good article for you.
If you have a Monolith architecture, probably you have a Monorepo that would cause chaos in big teams. Having your separate repository would be very helpful from the CI/CD and technical perspectives.

Solutions

The first phrase that you will search is “Can I completely separate Vue.js Front-end to Laravel Back-end” which the answer is “Yes”, but how?
We found four solutions:
Separating with Webpack5 — Separating with Vite — Separating with Webpack4 — Separating with Laravel-mix itself.
There are pros and cons here, I will explain why we choose Webpack5, but it is totally up to you.

Laravel-mix

It is useless to separate the Monolith architecture with Laravel-mix when we are not going to use most of the features in it. Laravel-Mix is very powerful only if you are going to have a Monolith app with Laravel. A lot of Webpack configuration, which is very time-consuming to configure, also you can not upgrade your Webpack version from 4 to 5 or 5 to 6 in the future by yourself till the Laravel-Mix gives updates which means you can not be updated with the latest technologies.

Webpack4

Webpack is a good solution to replace it with Laravel-Mix, but which version? as Webpack said, always try to use the latest version if you want to have a performed application. Webpack 4 has too much interference with new technologies such as module federation, a conflict between sass-loader@11.0.0 and vue@2.6.12 that will throw this.getOptions() the error that is handled in Webpack 5, etc… If you care about application updates, I would highly recommend choosing other options.

Vite

Maybe you heard about Vite,

Vite is a build tool that aims to provide a faster and leaner development experience for modern web projects

I have had good experiences with Vite, it helped me a lot to configure my application with fewer efforts. but why did we not choose it for our Vue project?

Caution: if you have a list of routes in .js it will try to build all the routes in develop mode, depending on your project it would take a lot of minutes.

Vite is not mature yet and you can not count on it in production mode (2022). you can not be denied the Vite performance, but also for legacy codes and structures, you will face some bigger issues. If you want to enjoy its power, it would be a good tip to use dynamic import to help code splitting and SplitChunk more.
Whereas the Vite is so powerful, it was not suitable for our project and legacy structure, but if I want to embark on a new project from scratch, definitely I will use Vite.

Webpack 5

It is very gratifying there is a wide variety of libraries to choose from. Webpack 5 is the best library based on our project, a big project with a lot of configuration on Laravel-Mix which needs to migrate to the new bundler.
We will not have any problem with PostCss or bundling the router.js, of course, the Webpack HMR ( Hot module Reload ) is not very fast as it is using the socket.js to make it real-time, but I will show you that it is two times faster than Laravel-Mix 5.

Step by Step

It is time to dive into the code and find the rabbit holes. First of all, we have to find all the challenges to find a solution for them. I will list some possibilities based on my experience.

I am using Laravel routes instead of REST

I would highly recommend migrating to REST at first, it is the most important part of the migration. E.g. We have a ‘/authenticate/login’ route that handles the user login process, in this case, the best approach is to migrate it into ‘/api/authenticate/login’ that will accept JSON parameters and will respond to JSON as well. if your entire application is not in REST, so let’s create a refactor card in your task management system with a “critical” badge.

I have an “index.blade.php” instead of index.html

We will handle it later with Webpack, the only thing is to copy/paste the entire file into a new index.html, that is it, let Webpack handle it.

How to handle environments?

All the “env” variables will change from “import.meta.EnvName” into “process.env.EnvName” which means we have to replace all of them and also create a “.env” file in the root of the project with desired variables.

What about the “import” method?

Only replace “import(…).default’ with ‘require(…).default’.

Will our codebase change?

Of course not, if you have a standard code base, you will have no issue.

How to configure Webpack5 on Vuejs 2?

It is pretty straightforward, I would highly recommend decoupling “development” and “production” configurations, like that you will have better dominance over each environment configuration. It is very usual to have a conflict between these two env.
Let’s start by creating a new directory named your application (E.g myapp ) and create the directory structure as below:

.
|-- README.md
|-- package.json
|-- public
|-- src
-- webpack
|-- paths.js
|-- webpack.common.js
|-- webpack.config.dev.js
`-- webpack.config.prod.js

It is time to install dependencies.

npm i -D @webpack-cli/serve clean-webpack-plugin compression-webpack-plugin copy-webpack-plugin css-minimizer-webpack-plugin dotenv-webpack html-webpack-plugin optimize-css-assets-webpack-plugin speed-measure-webpack-plugin terser-webpack-plugin webpack webpack-bundle-analyzer webpack-cli webpack-dev-server webpack-hot-middleware webpack-merge css-loader vue-loader esbuild-loader 

I tried to install all the devDependencies, if some of them were not installed, we will do it further.
Now let’s see what “webpack.common.js” looks like.

It is too long, yes, I know, I am not going to explain loaders as you can find them in Medium or their Git repositories, for this reason, I tried to put comments if it was necessary, but let’s do a quick recap.

SpeedMeasurePlugin: This plugin will help us to measure Webpack performance. It is so useful to find out which loader has a lack of performance. we should wrap it around our configuration.

Entry: The entry should address the main javascript file which is running the Vuejs application.

ProvidePlugin:

Automatically load modules instead of having to import or require them everywhere.

If you are working with an underscore library like “lodash” or “Jquery” which has a special syntax that is not ordinary for Webpack and is not imported inside the files, you have to provide them inside this plugin.

CopyWebpackPlugin: This plugin is so useful for us because we have the “public” folder from “Laravel” and we will need it after decoupling. As you know, Laravel-mix has been connected ‘public’ to the “Vuejs” application. Because of that, after the migration, none of the images or any assets will load correctly, because Webpack has no access to the “public” folder ( out of the folder scope ), this plugin will try to make this connection.

ESBuild: It is time to replace the babel-loader with ES-build to speed up our Webpack build. no more explanation, let’s read the documentation: https://github.com/privatenumber/esbuild-loader

We are good now, let’s build up the development. it is so simple.

As it is so simple, I just want to explain HotModuleReplacementPlugin and source-map.

HotModuleReplacementPlugin: Or HMR is a plugin that will reload the page anytime we save our changes in IDE, the benefit related to other watchers is state persistence which means your states will persist after each reloads as the plugin will reload only the specific and related DOM.

devtool: Perhaps it is not so clear at first. Let’s see what Devtool is inside the Webpack documentation.

The source-map-loader extracts existing source maps from all JavaScript entries. This includes both inline source maps as well as those linked via URL. All source map data is passed to webpack for processing as per a chosen source map style specified by the devtool option in webpack.config.js.

The most used would be debugging the code in the browser, source-maps are not transpile yet and you can see your Vuejs codes in them to find which part has a bug. as it will reduce the performance, the best practice is to keep it only in development mode. you can find a list of different source-maps here: https://webpack.js.org/configuration/devtool/

I prefer to use “inline-source-map” which will give me enough details and will increase my build and rebuild time in development mode.

So exciting for production? so let’s go.

At first, you will see that devtool is false, it does make sense to not provide the debuggable version in production as it will increase the bundle size, build time, and also the client will not need it.

splitChunks:In the optimization phase, we are using splitChunks which is increasing the performance too much by splitting the bundle into separated modules, https://webpack.js.org/plugins/split-chunks-plugin/

TerserPlugin: Anybody knows the Terser plugin affects the performance, Terser is using a lot of algorithms to minify the javascript code into the most minified version as much as possible. https://webpack.js.org/plugins/terser-webpack-plugin/

The only thing that remains, is paths.js

const paths = require('path')

module.exports = {
// Source files
src: paths.resolve(__dirname, '../src'),

// Production build files
build: paths.resolve(__dirname, '../dist'),

// Static files that get copied to build folder
public: paths.resolve(__dirname, '../public'),
}

It is too simple and needs no explanation. 😄

Mostly done, let's modify our package.json

"scripts": {
"dev": "webpack server --mode=development --config ./webpack/webpack.config.dev.js",
"build": "webpack --mode=production --config ./webpack/webpack.config.prod.js",
"serve:dev": "pm2 start ecosystem.config.js --env development",
"serve:prod": "pm2 start ecosystem.config.js --env production"
},

It is so clear, only I want to explain the “serve:dev and prod”. I prefer to use PM2 as our process manager, if you are not familiar with it, I would highly recommend taking a look at its documentation: https://pm2.keymetrics.io/. the “ecosystem.config.js” is the main configuration file or PM2 to define different behaviours on production or development, you can check the built project on your local machine by running “serve:dev” and it is actually the same as the production, it is very important to try to make both dev and production environment same to decrease bugs.

ecosystem.config.js

module.exports = {
script: 'serve',
env_development: {
NODE_ENV: 'development',
PM2_SERVE_PATH: './dist',
PM2_SERVE_PORT: 3000,
PM2_SERVE_SPA: 'true',
},
env_production: {
NODE_ENV: 'production',
PM2_SERVE_PATH: './dist',
PM2_SERVE_PORT: 8080,
PM2_SERVE_SPA: 'true',
},
deploy: {
development: {
host: ['localhost', '127.0.0.1'],
},
production: {
host: ['production.domain', 'production IPAddress'],
},
},
};

Time to deal with numbers

Now it is time to review the benchmark of the new Webpack configuration with the current Laravel-mix project. Of course, it is related to your project. These benchmarks are related to a big project with a lot of files.

Laravel Mix Build time — 3.30 minutes

Webpack5 Build time — 1.9461 minutes

You can see it is almost two times faster. but it is not the only benchmark, I have another interesting benchmark on running tests

Laravel-mix — 6.44 second

Webpack 5–3.318 seconds

The result is the same as above. we experienced better performance in Trengo which these changes.

Conclusion

Choosing the best module bundler is dependent on your project, there is no specific recipe to increase your project performance, for some projects, Laravel-mix is the best, or maybe Vite. The most important thing is to know your projects and the rabbit holes inside them. For E.g. in the Laravel Monolith project if you are using direct routers like: ‘authenticate/forget-password’ you have to change it to a REST API, and handle everything with JWT or other encryptions.

One important thing is to copy the whole entire master.blade.php to “public/index.html” inside the new structure which is the main HTML template.
There is no other thing to mention, enjoy your coding 😉

--

--