Installing Vue, Webpack & Eslint

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

To use Vue, we, of course, need to install it in our app. And, because we're using modern JavaScript practices, we're not going to include a script tag to a CDN or manually download Vue. We're going to install it with yarn.

But first, in addition to downloading Vue into our project, we also need to teach Webpack how to parse .vue files. Like React, Vue uses some special - not-actually-JavaScript - syntaxes, which Webpack needs to transform into real JavaScript.

To tell Webpack to parse vue files, open webpack.config.js. Near the bottom, though it doesn't matter where, add .enableVueLoader().

... lines 1 - 8
... lines 10 - 63
... lines 65 - 81
... lines 83 - 85

Yep! That's all you need. If you want to use Vue 3, you can pass an extra argument with a version key set to 3. Eventually, 3 will be the default version that Encore uses.

And, even though Encore is watching for changes, whenever you update webpack.config.js, you need to stop and restart Encore. I'll hit Control+C and then re-run:

yarn watch

When we do this... awesome! Encore is screaming at us! To use Vue, we need to install a few packages. Copy the yarn add line, paste and run it:

yarn add vue@^2.5 vue-loader@^15 vue-template-compiler --dev

Once these are done downloading, restart Encore again with:

yarn watch

It works! Nothing has really changed yet, but Encore is ready for Vue.

What is Vue?

In the simplest sense, Vue is a templating engine written in JavaScript. That over-simplifies it... but it's more or less true. In Symfony, if you go back to ProductController, we're accustomed to using Twig. It's easy: we tell it what template to render and we can pass variables into that template.

The Twig file itself is just HTML where we have access to a few Twig syntaxes, like {{ variableName }} to print something.

Vue works in much the same way: instead of Twig rendering a template with some variables, Vue will render a template with some variables. And the end-result will be the same: HTML. Of course, the one extra super power of Vue is that you can change the variables in JavaScript, and the template will automatically re-render.

Creating a Target Element for Vue

So instead of rendering this markup in Twig, delete all of it and just add <div id="app">. That id could be anything: we're creating an empty element that Vue will render into.

... lines 1 - 2
{% block body %}
<div id="app"></div>
{% endblock %}

Our Non-Single Page Application

Now, what we're building will not be a single page application, and that's on purpose. Using Vue or React inside of a traditional web app is actually trickier than building a single page application. On our site, the homepage will soon contain a Vue app... but the layout - as you can see - is still rendered in Twig. We also have a login page which is rendered completely with Twig and a registration page that's the same. We'll purposely use Vue for part of our site, but not for everything... at least not in this tutorial.

Creating a Second Webpack Entry

Go back and open webpack.config.js again. This has one entry called app. The purpose of app is to hold any JavaScript or CSS that's used across our entire site, like to power the layout. We actually don't have any JavaScript, but the app.scss contains the CSS for the body, header and other things. The app script and link tags are included on every page.

But, our Vue app isn't going to be used on every page. So instead of adding our code to app.js, let's create a second entry and include it only on the pages that need our Vue app.

Copy the first addEntry() line, paste, and rename it to products - because the Vue app will eventually render an entire product section: listing products, viewing one product and even a cart and checkout in the next tutorial.

... lines 1 - 8
... lines 10 - 26
.addEntry('products', './assets/js/products.js')
... lines 28 - 82
... lines 84 - 86

Now, in assets/js, create that file: products.js. Let's start with something exciting: a console.log():

Boring JavaScript file: make me cooler!

console.log('Boring JavaScript file: make me cooler!');


Oh, we will. But before we do, I'm going to open my PhpStorm settings and search for ESLint. Make sure "Automatic ESLint configuration" is selected. Because... I've already added a .eslintrc config file to the app. ESLint enforces JavaScript coding standards and PhpStorm can automatically read this and highlight our code when we mess something up. I love it! We're using a few basic rule sets including one specifically for Vue. You definitely don't need to use this exact setup, but I do recommend having this file.

Back in products.js, ha! Now PhpStorm is highlighting console:

Unexpected console statement (no-console)

One of our rules says that we should not use console because that's debugging code. Of course, we are debugging right now, so it's safe to ignore.

Ok: we added a new entry and created the new file. The last step is to include the script tag on our page. Open up templates/product/index.html.twig. Here, override a block called javascripts, call parent() and then I'll use an Encore function - encore_entry_script_tags() - to render all the script tags needed for the products entry.

... lines 1 - 12
{% block javascripts %}
{{ parent() }}
{{ encore_entry_script_tags('products') }}
{% endblock %}

If you look in the base template - base.html.twig - it's quite traditional: we have a block stylesheets on top and a block javascripts at the bottom.

Back in our template, also override the stylesheets block and call encore_entry_link_tags. Eventually, we'll start using CSS in our products entry. When we do, this will render the link tags to the CSS files that Encore outputs.

... lines 1 - 6
{% block stylesheets %}
{{ parent() }}
{{ encore_entry_link_tags('products') }}
{% endblock %}
... lines 12 - 18

Before we try this - because we just updated the webpack.config.js file - we need to restart Encore one more time:

yarn watch

When that finishes, move back over, refresh... then open your browser's debug tools. Got it! Our boring JavaScript file is alive!

Our First Vue Instance

Let's... make it cooler with Vue! Back in products.js, start by importing Vue: import vue from 'vue'. This is one of the few parts that will look different in Vue 3 - but the ideas are the same.

import Vue from 'vue';
... lines 2 - 7

If you imagine that Vue is a templating engine - like Twig - then all we should need to do is pass Vue some template code to render. And... that's exactly what we're going to do. Add const app = new Vue() and pass this some options. The first is el set to #app. That tells Vue to render inside of the id="app" element. Then, pass one more option: template. This is the HTML template - just like a Twig template - except that, for now, we're going to literally add the HTML right here, instead of in a separate file:

<h1>Hello Vue! Is this cooler?</h1>

... lines 1 - 2
const app = new Vue({
el: '#app',
template: '<h1>Hello Vue! Is this cooler?</h1>',

That's... all we need! Moment of truth: find your browser and refresh. There it is! We just built our first Vue app in about 5 lines of code.

Next, let's make it more interesting by passing variables to the template and witnessing Vue's awesomeness first hand.

Leave a comment!

  • 2020-07-24 weaverryan

    Hey Dennis de Best!

    Hmm. Let's simplify a little bit :). On your dev locally, if you run yarn encore production and then try it, do you see the error? I want to rule out that the hold gitlab-ci & rsync process isn't part of the problem (I can't think *why* it would be, but we need to eliminate it).


  • 2020-07-24 Dennis de Best

    Hi thanks for the help, I'm sorry to only reply now, I never see disqus notifications, something I have to work on aswell.

    It is based on the tutorial but my dev environment always runs with docker-compose.yml, alpine based images and traefik to get everything to communicate.

    The packages are upto date, maybe I'm missing an apk on alpine node but I can't see anything that points me in that direction.

    My packages are in dev locally and in production gitlab-ci builds them and rsyncs them to the /public/build. I can't see any depencies that minicssextract would need that are not in the correct part of the package.json

    Stuff works now but still brings up some errors. Anyway thanks for your reply it's not something urgent for me

  • 2020-06-26 Matias Jose

    Hi Denis!

    It's hard to know what the issue would be given the information provided. Since the issue is SCSS related, I don't think this would have anything to do with TypeScript. Are your packages up to date? Are you referring to our tutorial or your own project? Have you tried upgrading the packages and removing the localIdentName config entry in the webpack.config.js file? Are you running the production script in your same local environment or have you checked the repo out in a production server? Remember "dev" packages won't install if your env is "production". In this case, the idea is that you bluild your production files locally and then commit them, so you don't have to download and do complex stuff in the prod server.

    May be you can give us some more info?

  • 2020-06-25 Dennis de Best

    Hello ! Thanks for the tutorial it is helping me a lot to optimize my default Symfony/VueJs base. I now have the HMR working nicely however when I go into production mode and run `yarn build` I get some MiniCssExtract plugin errors I can't find a solution for (for the last couple of hours ...)

    I find something like :

    [KQP3] ./node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!./node_modules/resolve-url-loader??ref--8-oneOf-1-2!./node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./assets/scss/pdf.scss 1.48 KiB {mini-css-extract-plugin} [depth 0] [built] [failed] [1 error]
    ModuleConcatenation bailout: Module is not an ECMAScript module

    when running with yarn build --verbose. I have seen a couple a of github issues and stack overflow questions that seem to say you need to use `require` and not `import` in the typescript files but this seems like something that is not the problem anymore.

    Some reckon that the

    '@': path.resolve(__dirname, 'assets', 'js'),
    styles: path.resolve(__dirname, 'assets', 'scss'),

    Breaks stuff when you import with `@import ~styles/variables` instead of just `@import variables` (some even say that it's without ending with .scss that it breaks ...)

    On other once I have read that the MiniCssExtractPlugin needs the esModule option set to false ...

    All this to say I have looked everywhere and tried all of the above but I might be completely missing something.

    This is probably not much information to work with and not a lovely question but any hints will be greatly appreciated ;)

    Thanks for all the courses !