Symfony with Vue.js frontend Integration
with tailwindcss
Post by Pedro Resende on the 6 of February of 2020 at 10:30
Tags: Vue.jsMVCSymfonyVueWebpack
A few years ago, I've dedicated a post to the integration of React.js with Symfony (Symfony with React.js frontend Integration).
For the last year and a half I've been working on a daily basis using Vue.js has a dedicated frontend framework. I must say that when I started, I was against it since I had already quite some experience with React.js and it was strange, however I have to admit it was a good surprise. The learning curve is way better than React.js, and when comparing Redux with VueX it's amazing how simple it is to use.
Therefor, I've decided, like I've done 4 years ago with React.js to write a possible integration with Symfony.
A kind reminder that this is a possible integration and if any of you believe there is a better approach, please feel free to leave a comment.
Let's start by installing a clean Symfony 5.x environment for this example:
$ symfony new vuejssymfonyintegration
Let's start by installing twig integration in order to have a frontend
$ cd vuejssymfonyintegration
$ composer req twig
$ symfony serve
Access to your browser, using the address provided, probably something like
[OK] Web server listening on https://127.0.0.1:8000 (PHP CLI 7.4.2)
Let's start by creating a controller to return our main page
$ composer req make doctrine/annotations symfony/webpack-encore-bundle
$ ./bin/console make:controller
Back to the editor, now you'll see the presence of `VueFrontEndController.php` on the folder `src/Controller`. If you open your file, you'll have something like
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
class VueFrontendController extends AbstractController
{
/**
* @Route("/vue/frontend", name="vue_frontend")
*/
public function index()
{
return $this->render('vue_frontend/index.html.twig', [
'controller_name' => 'VueFrontendController',
]);
}
}
Let's change the Route from "/vue/frontend" to "/" in order to show it has our homepage.
Now, let change the contents of the file `templates/vue_frontend_index.html.twig` which has the following:
{% extends 'base.html.twig' %}
{% block title %}Hello VueFrontendController!{% endblock %}
{% block body %}
<style>
.example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
.example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
</style>
<div class="example-wrapper">
<h1>Hello {{ controller_name }}! ✅</h1>
This friendly message is coming from:
<ul>
<li>Your controller at <code><a href="{{ '/home/pedroresende/Projects/vuejssymfonyintegration/src/Controller/VueFrontendController.php'|file_link(0) }}">src/Controller/VueFrontendController.php</a></code></li>
<li>Your template at <code><a href="{{ '/home/pedroresende/Projects/vuejssymfonyintegration/templates/vue_frontend/index.html.twig'|file_link(0) }}">templates/vue_frontend/index.html.twig</a></code></li>
</ul>
</div>
{% endblock %}
to something like
{% extends 'base.html.twig' %}
{% block title %}Hello VueFrontendController!{% endblock %}
{% block stylesheets %}
{{ encore_entry_link_tags('app') }}
{% endblock %}
{% block body %}
<div id="app"></div>
{% endblock %}
{% block javascripts %}
{{ encore_entry_script_tags('app') }}
{% endblock %}
Now that we have our Symfony structure ready, let's go into the JS part.
Let's add encore.js to our project, by running
$ composer req symfony/webpack-encore-bundle
$ yarn install
$ yarn add vue tailwindcss --save
$ yarn add @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props vue-loader@^15.0.11 vue-template-compiler postcss-loader@^3.0.0 sass-loader@^7.0.1 node-sass --dev
Open webpack.config.js file, which is located at the root of our project and change to have the following:
var Encore = require('@symfony/webpack-encore');
if (!Encore.isRuntimeEnvironmentConfigured()) {
Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
}
Encore
.setOutputPath('public/build/')
.setPublicPath('/build')
.addEntry('app', './assets/js/app.js')
.enableSingleRuntimeChunk()
.cleanupOutputBeforeBuild()
.enableBuildNotifications()
.enableSourceMaps(!Encore.isProduction())
.enableVersioning(Encore.isProduction())
.configureBabelPresetEnv((config) => {
config.useBuiltIns = 'usage';
config.corejs = 3;
})
.enableVueLoader(() => {}, {
useJsx: true
})
.enablePostCssLoader()
.enableSassLoader(function (options) {}, {
resolveUrlLoader: false
})
;
module.exports = Encore.getWebpackConfig();
Let's add a postcss.config.js file, with the following contents
module.exports = {
plugins: [
// ...
require('tailwindcss'),
require('autoprefixer'),
// ...
]
}
Open /assets/app.js file and change it to
import '../css/app.css';
import Vue from 'vue';
import vuefrontend from './../vue/vuefrontend';
if (document.getElementById('app') !== null) {
new Vue({
el: '#app',
template: '<vuefrontend/>',
components: {
vuefrontend
}
})
}
console.log('Hello Webpack Encore! Edit me in assets/js/app.js');
Let's create the file assets/vue/vuefrontend.vue with the following content
<template>
<div class="container m-auto">
<div class="p-20">
{{ message }}
</div>
</div>
</template>
<script>
export default {
name: 'vuefrontend',
data() {
return {
message: 'Hello World'
}
}
}
</script>
Let's boot the encore watcher, to take into account our changes
$ yarn encore dev --watch
Back to your browser, you should see Hello World
That is it, please let me know if you propose a different approach, the code is available at this link.