I have been fascinated by VueJS for a while now. I played around with React but, honestly, I never got along with either JSX or the CSS-in-JS-which-is-almost-CSS-but-not-really thing. Vue is different, since it lets front-end developers keep on doing front-end development with very little adjustments to their markup and styles. So far, I’ve had the chance to try it in a couple of projects, a Ruby on Rails application and a WordPress website. In both cases, the Vue app was quite simple (i.e. no routing required) and everything went really smooth.
The great thing about Vue is that you can get it to work in an existing project just by including it from a CDN via a script tag. This is probably inappropriate for something complex, but Vue actually works out of the box. Also, it plays really well with jQuery, which is something you need to deal with if you are using a UI framework or a CMS.
I’m about to start a rather large project which will involve four separate single-page applications, and I’ve decided to go with VueJS. I want to exploit Single File Components (
.vue files), as they are easily readable and provide a scalable way to structure front-end code.
I really like Zurb Foundation, I’ve used it in a number of projects, big and small. Although using Foundation 6 in production has been a bit rough with releases 6.0 through 6.2, Foundation 6.3 fixed many issues and I consider it a great tool for layout and styling. I decided I had to get a working setup / boilerplate integrating Vue and Foundation before trying this combination for something real.
EDIT: This project was originally started using a vue-cli template based on Webpack 1. The article and the accompanying repo have now been updated to work with the latest Webpack 2 vue-cli template. The original configuration is still available in the
webpackv1branch of the repo
One really good thing about Vue is its ecosystem, which is kept essential but features some very powerful tools. I started playing around with vue-cli (the recommended way to scaffold a Vue project), using its Webpack template.
DISCLAIMER: I am a complete Webpack noob. There are probably better ways to achieve what I wanted, please leave a comment if you have suggestions.
The CLI is basically a generator with a Yeoman-like interface where you can optionally setup linting (ESLint), unit (Karma + Mocha) and e2e testing (Nightwatch). You can install it with
npm i vue-cli -g. After that,
vue init webpack my-project lets you choose what to include and scaffold your app. I chose the AirBNB linting rules and, for now, I did not configure testing. I also decided to install Vue Router, to simulate a real-world app. Once the generator was done creating folders and files, I could
cd my-project && npm i to install the dependencies, followed by
npm run dev to start the development server and confirm everything went alright. In order to be able to use SASS and include Foundation as an external script, a few extra npm modules are needed, you can get them by running
npm i sass-loader script-loader node-sass --save-dev. Finally, I installed
jquery (I found out that both
--save-dev work here).
I copied the
_settings.scss variables from the npm package into my app folder, to be able to customize the framework. The problem in getting the CSS to work is that, if you
@import ‘foundation' in your root component, its mixins and variables won’t be available to child components. The easy solution to this is to import settings and framework in every component, through this file:
However, this is ugly and would lead to a lot of code duplication. A solution to this problem is to use custom Webpack
build/utils.js), which will require variables and mixins in every component using
<style lang="scss">. The framework can be then initialized with
@include foundation-everything() (or just the parts you need) in the
<style> tag of
- Foundation is not meant to be used with a module system, i.e. you can’t just
import Foundation from 'foundation-sites'at the top of your component JS code and use it to instantiate components (I guess the same applies to Bootstrap);
- In an app with different views, it’s not possible to simply initialize the framework once, because
$(document).foundation()will only work for the elements which are currently in the DOM;
- Changing views in a Vue app will remove some elements from the DOM but leaving orphaned listeners. While this is not an issue with traditional web-apps (a full page reload destroys and re-initializes the framework), it needs to be taken care of in a single-page application, otherwise one gets a lot of weird side-effects (i.e. clicking on a link opens a Reveal modal from a previously visited page).
I looked for strategies to overcome these problems and, indeed, they have been discussed already by a few people. In the end, after playing around for a while, I got things working by combining the solutions proposed in various threads.
'import/no-webpack-loader-syntax': 0 in the
rules section of
.eslintrc.js). Please also note that the script-loader syntax is
script! in Webpack 1 and
script-loader! in Webpack 2.
I tried to use custom Vue directives, which requires adding a
v-foundation attribute to the root tag of a Foundation component. While this works for a number of components (i.e. Reveal, Tooltip), I couldn’t get it to function with others (Off-Canvas and Tabs, for instance), namely those without a single root HTML tag. Instead, I decided to go with a Vue mixin, which can be required in single-file Vue components:
I tried avoiding code duplication by declaring this as a global mixin, but that was a rather poor idea: if you have nested components, Foundation will be initialized on both parent and children (you get a warning in console about that). Also, you might not want to include Foundation in every single component of a Vue application. Instead, this mixin can be used when needed, such as in this example, integrating a Foundation slider in a Vue component:
When mounted, this component sets up a listener for a custom Foundation event, in order to have
v-on:change listener). As explained by Evan You himself in a GitHub issue, this is intentional.
App.vue. Considering that inclusion of the mixin is not a viable option to achieve this (for the above mentioned reasons), I resorted to instantiating the off-canvas using
new Foundation.offCanvas(element, options) in the
mounted() hook of the component.
vendor.js might be too large for some applications (138Kb gzipped). Having said that, I guess this boilerplate should be enough to start building a SPA with Vue and Foundation. You can have a look at the final result by clicking this link, the source code is available here: