WordPress & Vue.js with Webpack and Hot Reload

Image for post
Image for post

What this is about

Today we want to show you our workflow how to develop and include a Vue.js App with Webpack into WordPress while using Docker. For that we will create a simple plugin with a simple admin page that loads the Vue app. In the beginning we were only able to achieve a live reload instead of a hot reload. We got this fixed and are now very happy with the workflow, mixing good old WordPress with amazing Vue :)

Outline

  • Create a simple WordPress Plugin
  • Add a Vue.js app via Vue CLI to the Plugin
  • Link WordPress and the Webpack output *UPDATE
  • Running Vue and watch some hot reloading
  • Tips for deployment

Setup WordPress with Docker

This is not needed but we structure our WordPress projects like this. Create a new directory for your project.

root/
docker-compose.yml
www/
wp-config.php
...
// Rest of WordPress installation

Download and install WordPress. And unzip its content into the www directory. We have a oneliner for you. Inside the root directory type:

wget https://wordpress.org/latest.zip && unzip latest.zip && mv wordpress www && rm latest.zip

Copy this into the docker-compose.yml in the root directory:

version: "3"services:
www:
image: wordpress
ports:
- "8001:80"
environment:
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: root
WORDPRESS_DB_PASSWORD: root
volumes:
- ./www:/var/www/html/
links:
- db:mysql
networks:
- default
db:
image: mysql:5.7.22
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: root
volumes:
- persistent:/var/lib/mysql
networks:
- default
phpmyadmin:
image: phpmyadmin/phpmyadmin
links:
- db:db
ports:
- 8000:80
environment:
MYSQL_ROOT_PASSWORD: root
volumes:
persistent:

This creates a database container with mySQL and phpMyAdmin and also uses a WordPress prepared container that maps the www directory to the containers /var/www/html directory. The environment variables will be automatically set in the wp-config.php file inside the www directory.

Note that links: db:mysql is helpful to easily link the www container with the db container under the alias mysql.

Startup the containers by running (a download will start):

docker-compose up -d

You should now be able to visit your WordPress website at http://localhost:8001 and phpMyAdmin at http://localhost:8000

Follow the installation instructions for WordPress. For development purposes you can choose root as username and password.

When finished try to login to WordPress at http://localhost:8001/wp-admin

Create a simple WordPress Plugin

Go to the www/wp-content/plugins directory and create a new directory called vue-plugin. Inside this directory create a new file called vue-plugin.php. Add this on top of the file:

<?php/*
Plugin Name: Vue Hot Reload Plugin
Description: Vue Hot Reloading inside of WordPress.
Version: 1.0.0
*/

This is the plugin header. WordPress uses it to show information about the plugin on the Plugins page in the Dashboard. Visit the Plugins page now and you should see this:

Image for post
Image for post
Plugin Page

You can already activate the plugin if you want although we still need to add some code to add functionality to the plugin. Add this additional code to your vue-plugin.php:

class VuePlugin {
public function __construct() {
$this->register_hooks();
}
private function register_hooks() {
// Register hook to add a menu to the admin page
add_action('admin_menu', [ $this, 'add_admin_menu' ]);
}
public function add_admin_menu() {
add_menu_page(
'Vue Plugin Example',
'Vue Plugin',
'manage_options',
'vue-plugin',
[ $this, 'load_vue_plugin_page' ],
'dashicons-smiley',
4
);
}
public function load_vue_plugin_page() {
// For a better overview we load page templates separately
require_once 'templates/vue-plugin-admin.php';
}
}new VuePlugin();

This initializes a new class that adds an admin menu in the dashboard. The add_menu_page calls the load_vue_plugin_page function which requires another PHP file that represents the template of the admin page.

Create a new directory inside your plugin directory called templates. Inside of this directory create a file called vue-plugin-admin.php and add this code to it.

<div class="wrap">
<h1><?= get_admin_page_title() ?></h1>
<div id="app"></div>
</div>

Your structure should look like this:

Image for post
Image for post
Plugin Structure

If you refresh the page now you should see a new menu entry called Vue Plugin. When you click on it you should see this:

Image for post
Image for post
Plugin Admin Page

That’s it for the plugin. We are ready to add Vue.js

If you are already familiar with Vue you will notice that we added

<div id="app"></div>

inside of the template page. This will be used as the entry point for our Vue App.

Add a Vue.js app via Vue CLI to the Plugin

npm install -g @vue/cli
# OR
yarn global add @vue/cli

When the installation is finished create a new webpack vue project inside your plugin root directory by typing:

vue create vue

vue will be the name of the directory that will be created.

Setup your Vue Project with the settings you need except for the last part. It is important to put the config files into separate files. For a simple app we prefer these settings:

Image for post
Image for post
Vue CLI Setup

When the installation is finished your plugin directory structure should look like this:

Image for post
Image for post
Plugin Structure after Vue installation

Now comes the essential part of this whole story. You need to configure vue to generate output files that can be enqueued by WordPress. For this create a new file called vue.config.js inside your vue directory. Add this configuration to the file:

const devPort = 8081;module.exports = {
devServer: {
hot: true,
writeToDisk: true,
liveReload: false,
sockPort: devPort,
port: devPort,
headers: { "Access-Control-Allow-Origin": "*" }
},
publicPath:
process.env.NODE_ENV === "production"
? process.env.ASSET_PATH || "/"
: `http://localhost:${devPort}/`,
configureWebpack: {
output: {
filename: "app.js",
hotUpdateChunkFilename: "hot/hot-update.js",
hotUpdateMainFilename: "hot/hot-update.json"
},
optimization: {
splitChunks: false
}
},
filenameHashing: true,
css: {
extract: {
filename: "app.css"
}
}
};

This is a load of configuration and we gonna explain the most important things here step by step.

  1. devServer contains settings that will be used while running the app in development mode.
  2. writeToDisk ensures that even in development mode output files are generated to the filesystem. We will use this to enqueue the generated files into WordPress.
  3. sockPort and port need to be the same otherwise the hot reload module won’t find a connection for the HRM (Hot Reload) module.
  4. headers: { “Access-Control-Allow-Origin”: “*” } is needed since WordPress is hosted on localhost:8001 but our app (and the hot reloading) will run on localhost:8081. Don’t worry this setting is only applied during development and won’t affect production mode.
  5. publicPath this setting is very important as it ensures that vue can find all necessary files of your app. In development mode the app checks http://localhost:8081 which will serve all files and enables hot reload.
  6. filename: “app.js” and filename: “app.css” as well as hotUpdateChunkFilename: “hot/hot-update.js” and hotUpdateMainFilename: “hot/hot-update.json” are important to ensure that the output files generated from vue are always named the same to prevent enqueue errors by WordPress.

You can freely choose the port for the app (except Port 8000 and 8001 since those are taken by the Docker containers)

That is it for the setup of Vue. We will now enqueue the files into WordPress.

Link WordPress and the Webpack output

class VuePlugin
{
public function __construct() {
$this->register_hooks();
}
private function register_hooks() {
...
add_action('admin_enqueue_scripts', [ $this, 'load_scripts' ]);
}
public function add_admin_menu() {
...
}
public function load_vue_plugin_page() {
wp_enqueue_style( 'backend-vue-style' );
wp_enqueue_script( 'backend-vue-script' );
...
}
public function load_scripts() {
$vueDirectory = join( DIRECTORY_SEPARATOR, [ plugin_dir_url(__FILE__), 'vue', 'dist' ] );
wp_register_style( 'backend-vue-style', $vueDirectory . '/app.css' );
wp_register_script( 'backend-vue-script', $vueDirectory . '/app.js', [], '1.0.0', true );
}
}new VuePlugin();

We add the action admin_enqueue_scripts which calls the load_scripts() function. Inside of this function we register our app.css and app.js file for use in WordPress. The load_vue_plugin_page() function enqueues those files when it is loaded inside the browser. We do this to prevent loading of app.css and app.js on every other page than our plugin admin page.

That’s it already it is time to try out Vue.

Running Vue and watch some hot reloading

Image for post
Image for post
Plugin Admin Page Before

Now switch inside your vue directory and run the command npm run serve. The development server for vue will startup and a dist directory will be generated in vue/dist. You should find the app.js and the app.css along with some other files inside that directory. In your console you should see this:

Image for post
Image for post
Vue CLI Output

Reload your admin page and you should see this:

Image for post
Image for post
Plugin Admin Page After

Any changes to your files inside the vue directory (EXCEPT setting files) should trigger a hot reload or probably a live reload of the page.

Tips for deployment

npm run build

The dist directory will contain your production ready files. So: No need to change anything in WordPress :)

When deploying your app you probably don’t want to include all files generated by webpack. You actually only need to deploy the dist directory. We have a CI setup on Gitlab that cleans the directory except the dist and leaves only the app.js and app.css.

Thank you very much for following this story until here. We hope that we could enlight you a bit and open up a new possibility for you to develop modern plugins within WordPress.

You can find the whole code repository here.

Best regards and happy developing

The devs

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store