Laravel & Vue.js - Building a Todo list, Part 4: Setting up the frontend application with Webpack
Guillaume Briday
5 minutes
This is one of the most important parts of the frontend: setting up the environment with Webpack.
Before starting
I didn't start from a blank project; there are official templates you can install to get a basic configuration for your project. To use them, you first need to install vue-cli:
$ npm install -g vue-cli
The available templates are hosted in the vuejs-templates organization. Personally, I wanted to start from the simplest setup to truly understand the fundamentals of Webpack.
Before this project, I used Webpack only with Laravel Mix, which is an excellent tool but doesn't, in my opinion, allow you to fully grasp how Webpack works if you've never used it before.
Webpack
On the frontend side, Webpack will handle everything. It will provide us with a server featuring Hot Module Replacement and manage our dependencies in both production and development environments. It is by far the most powerful tool available today, but it isn't easy to get started with initially.
I won't explain how Webpack works; for that, I invite you to read the Concepts page of the official documentation, which is very helpful. Instead, I'll detail the modifications and additions I made to the Webpack configuration to meet the project's needs and to delve deeper into certain points.
To install our template:
$ vue init webpack-simple todolist-frontend
Our entry points
Even though with Webpack you can manage CSS via JS, as Adam Wathan does in his example with Tailwind—I personally prefer to keep them separate. Importing a CSS file from an index.js
still unsettles me. Feel free to change this if you prefer.
So, I'll have two entry points: one for JS and one for CSS, located in two distinct folders.
entry: {
app: [
'./src/js/app.js',
'./src/styles/app.scss'
]
},
Our modules
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
extractCSS: true
}
},
{
test: /\.s[ac]ss$/,
use: ExtractTextPlugin.extract({
use: [
{loader: 'css-loader', options: {importLoaders: 2}},
'postcss-loader',
'sass-loader'
],
fallback: 'style-loader'
})
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [path.resolve(__dirname, './src')],
options: {
emitWarning: true
}
},
{
test: /.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
}
}]
}
]
},
A few important points to note.
We'll, of course, use vue-loader to create components in .vue
files. This is essential for developing with Vue.js, in my opinion.
I always use Sass to write my CSS. I simply use the sass-loader with a test on .sass
or .scss
files.
I want the final CSS to be extracted into a separate app.css
file, not embedded within a <style>
tag as is the default with the style-loader
.
To achieve this, we need to use the Extract Text Plugin. It allows us to extract the result into a separate file if possible; otherwise, it will use the default style-loader
. Coupled with vue-loader
, it enables us to extract the CSS written in .vue
files directly into the main CSS file using the option extractCSS: true
.
For .js
files, we'll use Babel to take advantage of ES2015 syntax.
Tailwind CSS
To install Tailwind CSS with Webpack, we need to use PostCSS. I use the sass-loader
after the postcss-loader
because the order matters, as indicated here.
PostCSS
also allows us to automatically autoprefix our CSS.
// postcss.config.js
var tailwindcss = require('tailwindcss')
module.exports = {
plugins: [tailwindcss('./tailwind.config.js'), require('autoprefixer')],
}
Font Awesome
To install fonts, we'll need the file-loader
. Indeed, we need to extract the fonts into files to load them via our browser.
In my case, I want to extract them into a /fonts
folder.
I just need to modify the Font Awesome path variable in the SCSS and import the main file:
// src/styles/app.scss
$fa-font-path: '~font-awesome/fonts';
@import '~font-awesome/scss/font-awesome.scss';
ESLint
For .vue
and .js
files, we'll use ESLint to lint our code and maintain consistency in syntax, especially if other developers might work on the project. There are several configuration options; I use the JavaScript Standard Style. The configuration is located in the .eslintrc.js
file.
If you use VS Code, there's a great extension that uses your current project's configuration and reports errors directly in your editor!
$ code --install-extension dbaeumer.vscode-eslint
Plugins
var inProduction = process.env.NODE_ENV === 'production'
// ...
plugins: [
new ExtractTextPlugin({
filename: 'css/[name].[contenthash].css',
disable: !inProduction,
}),
new HtmlWebpackPlugin({
template: 'index.html',
}),
]
It's not necessary to extract the CSS into a file during development phases, which is why I disable ExtractTextPlugin
for environments other than production. The plugin will then use the fallback
we configured earlier.
The HTML Webpack plugin will generate an index.html
file, automatically injecting, among other things, our JavaScript and CSS with the corresponding tags. This is very handy for generating the final project with the correct URLs containing a hash for versioning.
In a section dedicated solely to production, I added the PurifyCSS plugin.
It scans our .html
and .vue
files (we can configure others) to keep only the CSS used inside them. It also allows us to minify the CSS once filtered.
new PurifyCSSPlugin({
// Give paths to parse for rules. These should be absolute!
paths: glob.sync([
path.join(__dirname, './src/js/components/**/*.vue'),
path.join(__dirname, 'index.html')
]),
minimize: true
}),
Docker and Docker-Compose
To use Node and NPM, I prefer to use Docker to facilitate deployment and ensure a consistent environment among all project developers.
The default Node image doesn't expose any ports and doesn't have a WORKDIR
. We could change the WORKDIR
via the docker-compose.yml
file, but I prefer to modify it in the Dockerfile to make it easier to do without Docker Compose in the future if needed.
So I created a super simple Dockerfile
to handle these two cases:
FROM node
LABEL maintainer="[email protected]"
WORKDIR /app
EXPOSE 8080
I don't add any command (CMD
) at startup because the goal is to use Docker Compose only to launch the containers more easily.
Using Docker Compose just for this is completely overkill, we agree. Using Docker directly would work perfectly, but I prefer it this way. It allows me to always have the same command to launch even if the configuration changes in the future.
NPM is installed by default in this image, so we don't need to install it ourselves.
The docker-compose.yml
is also very simple:
version: '3'
services:
node:
build: ./provisioning
image: todolist-frontend
ports:
- 8080:8080
volumes:
- .:/app:cached
In our case, running docker-compose up
wouldn't have any purpose; I don't want there to be a default command for now.
Instead, we can use it to run commands:
# Install our dependencies
$ docker-compose run node npm install
# Launch the Webpack server with Hot Module Replacement
$ docker-compose run --service-ports node npm run hot
# Compile the application in development mode
$ docker-compose run --rm node npm run dev
# Compile the application in development mode with watch mode
$ docker-compose run --rm node npm run watch
# Compile the application in production mode (minification, etc.)
$ docker-compose run --rm node npm run production
# Run ESLint
$ docker-compose run --rm node npm run lint
The --service-ports
flag activates port mapping between the containers and the host when executing a command. Indeed, outside of docker-compose up
, it's disabled by default.
Travis CI
To ensure everything works, I've set up Travis CI to test if the project compiles successfully and if ESLint doesn't report any errors. In the near future, I might add unit and functional tests for the application.
language: node_js
node_js:
- '7'
- '8'
- '9'
cache:
directories:
- 'node_modules'
script:
- npm run lint
- npm run production
notifications:
email: false
Conclusion
The configuration is likely to evolve further during development, of course.
Everything is available on the GitHub repository: guillaumebriday/todolist-frontend-vuejs.