Vuepress is a boilerplate used to build smooth, responsive WordPress templates with Vue.js.
For a quick start, look at the Example Workflow. For a more in-depth introduction, head to the Reading List and continue from there.
- Install
- Common Tasks
- Reading List
- Building a Vuepress Site: Back-End
- Vuex and State
- Building a Vuepress Site: Front-End
- Recommended Reading
git clone https://github.com/funkhaus/vuepress my-theme
cd my-theme
npm install
- Go to the WordPress back-end, activate, the Vuepress theme, and follow the instructions to install Rest-Easy.
npm run dev
- To build:
npm run build
- To build and deploy to server (using
.deployrc
for fh-deploy configuration):npm run deploy
Paste these anywhere in your <script>
tags to use them in your own templates. This assumes you've included
import _get from 'lodash/get'
All results from The Loop:
_get( this.$store, 'state.loop' )
The first result from The Loop:
this.$store.getters.post
The featured image from the first result in The Loop:
_get( this.$store.getters.post, 'featured_attachment' )
The children of the first result of The Loop:
_get(this.$store.getters.post, 'related.children')
Vuepress includes the Google/Typekit Web Font Loader in parts/font-loader.php
. Follow the instructions on that repo to load fonts from Google, Typekit, or your own uploads.
// font loader example
WebFontConfig = {
google: {
// your google fonts families here
families: []
},
typekit: {
id: ''
},
custom: {
// your custom font families here
families: [],
urls: ['<?php echo get_template_directory_uri(); ?>/static/fonts/fonts.css']
}
};
- Place the .svg file in
src/svgs/
. - In the
script
section of the template where you want to use the SVG, add:import exampleSvg from 'src/svgs/example.svg' export default { data(){ return { exampleSvg } } }
- In the location where you want to use the SVG:
That's it!
<div v-html="exampleSvg"></div>
Vuepress comes with a component called responsive-image
that provides some built-in image handling, including fading in images as they load. You can pass an image object from Rest-Easy's attachment serializer and it will build itself automatically:
<!-- Build a responsive image component from the featured image of the first post in The Loop -->
<responsive-image :object="$store.state.loop[0].featured_attachment"/>
You can also include any of the following attributes:
<responsive-image
src="source-url"
height="height in px"
width="width in px"
aspect="aspect ratio, as percent (ie '56' for 56%)"
size="WordPress-defined size slug"
color="background color of pre-loaded space"
/>
You must include either an object
or a src
parameter on a responsive-image
element; all other values are optional.
Vuepress supports SCSS out of the box, and comes with a style vars file in src/styles/_vars.scss
and the base styling for the entire site in src/styles/_base.scss
.
You can import the vars file in any Vue template like this:
<style lang="scss">
@import 'src/styles/vars';
// Now you have access to the $vars!
</style>
Vuepress also comes with a few suggested breakpoints in that same vars file - you can use them in a media query like this:
@media #{ $lt-phone } {
// your styling
}
The default breakpoints (with lt
for "less than" and gt
for "greater than") are:
gt-cinema
-only screen and (min-width: 1800px)
lt-desktop
-only screen and (max-width: 1100px)
lt-phone
-only screen and (max-width: 750px)
lt-phone-landscape
-only screen and (max-width: 750px) and (orientation: landscape)
To get started with Vuepress, you should already know or familiarize yourself with:
- WordPress
- Node.js and NPM (npm is included with Node)
- Vue.js
To get the most out of Vuepress, you can continue with:
- Rest-Easy, a WordPress plugin by Funkhaus
- The Vue router
- Vuex
Vuepress requires the Rest-Easy plugin to work correctly, so make sure you have that installed before getting started. Vuepress ships with TGM Plugin Activation to make Rest-Easy installation simpler.
In Vuepress, you'll be building individual pages with Vue instead of PHP templates. This can take some getting used to, but ultimately allows for all of the flexibility and power of Vue right from the start.
Any page on a Vuepress site will use the index.php
template, which contains some automatically generated header tags and a single div called #app
. This is where the main Vue component lives, with its content controlled by the Vue router.
A Vuepress site's routing table is built at runtime by functions/router.php
's add_routes_to_json
] function. The table uses the Vue router, which in turn uses path-to-regexp to parse paths.
jsonData['routes'] = array(
// The key is the relative path that a page must match, while the value is the Vue template name
'' => 'FrontPage',
'/path' => 'VueComponent',
'/path/:var' => 'ComponentWithVar',
'/path/:optional*/:var' => 'WildcardAndVar',
path_from_dev_id('dev-id') => 'DefinedByDeveloperId',
path_from_dev_id('dev-id', '/append-me') => 'DevIdPathPlusAppendedString'
);
Building the routing table dynamically lets the Vue router treat the WordPress database as the source of truth, ensuring pages route as expected.
For example, if you wanted to build a front page and an About page, you might set up the following in add_routes_to_json
:
// Don't do it like this! See below for more
jsonData['routes'] = array(
// The key is the relative path that a page must match, while the value is the Vue template name
'' => 'FrontPage',
'/about' => 'About'
);
That'd work just fine as long as the user never needed to change the URL to the About page - but what if they wanted to switch it to our-team
?
Since URLs can easily change in the WordPress backend, Vuepress includes a new WP role, Developer, that has access to a set of controls that other roles (even Administrator) can't see. One of these controls is for a page's "Developer ID" - an arbitrary value that can reliably identify a page.
The Developer ID is accessible via a post object's custom_developer_id
property - for example, $post->custom_developer_id
.
If we set the About page's Developer ID to about
, then rewrite the relevant line in add_routes_to_json
like the following:
...
// path_from_dev_id is a Vuepress function that retrieves a page's relative path from its Developer ID
path_from_dev_id('about') => 'About'
...
This will guarantee that the path to this page will always render the About template, even if the user changes that path later on.
The Developer role in Vuepress has a few extra capabilities available:
Any missing page in the add_routes_to_json
function (for example, if get_page_by_dev_id('about')
didn't find any pages) would break the given route; a Developer can lock pages to prevent this type of bug. Check the "Prevent non-dev deletion" box in the Developer Meta screen to prevent other users from placing that page in the Trash accidentally.
Check the "Hide Rich Editor" box to prevent non-Developer users from using WordPress's rich editor. This can be helpful to maintain stricter controls over the template and class names in a page's content.
Take a look at the path-to-regexp documentation for examples of routing using regex capabilities.
The routing table in Vuepress automatically converts a string-string key-value pair to a Vue route object:
array(
path_from_dev_id('my-developer-id') => 'MyComponentName'
)
...turns to:
const router = new VueRouter({
routes: [
{ path: '/my-developer-id', component: 'MyComponentName.vue' }
]
})
You can take advantage of the Vue router's more advanced capabilities, like redirect/alias, naming, and more by setting the value to an object:
array(
path_from_dev_id('your-developer-id') => array(
// Redirect to a path - in this case, to the path of the first child
'redirect' => get_child_of_dev_id_path('work')
),
path_from_dev_id('your-developer0id', '/:medium*') => array(
// Define a component and a name for the route
'component' => 'WorkGrid',
'name' => 'work-grid'
),
path_from_dev_id('your-developer-id') => array(
// Redirect to a named route
'redirect' => array(
'name': => 'work-grid'
)
)
)
This isn't the limit of the routing table's capabilities - anything the Vue router can do, you can build in the add_routes_to_json
function.
Vuepress defines a few utility functions to make building the routing table easier:
get_child_of_dev_id($dev_id, $nth_child = 0)
- Get the post object of the nth child (zero-based, default0
) of a page with the given Developer ID.get_child_of_dev_id_path($dev_id, $nth_child = 0, $after = '')
- Get the relative path of the nth child of a page with the given Developer ID. Adds$after
to the retrieved path.path_from_dev_id($dev_id, $after = '')
- Get the relative path of a page with a given Developer ID. Adds$after
to the retrieved path.slug_from_dev_id($dev_id)
- Get the slug of a page with a given Developer ID.
If you need to upgrade your version of Rest-Easy, change the $latest_rest_easy
variable in functions/vuepress-plugins.php
to match the latest Rest Easy version. You'll be prompted to upgrade the next time you load any page on the WordPress backend.
Vuepress uses Vuex to handle a site's state. The default store in src/utils/store.js
is set up like this:
{
// From Rest-Easy
site: jsonData.site,
meta: jsonData.meta,
loop: jsonData.loop,
// Vuepress-specific
transitioning_in: false,
transitioning_out: false,
loaded: true
}
See the Rest-Easy documentation for more information on jsonData
and its properties, as well as the Vuex documentation for Vuex terms like store, state, mutation, etc.
You can commit a mutation from any Vue component by using:
this.$store.commit('MUTATION_NAME', payload)
Default Vuepress mutations:
'REPLACE_QUERYDATA', { site, meta, loop }
- Replaces thestore
'ssite
,meta
, andloop
properties with thesite
,meta
, andloop
properties of the payload.'SET_TRANSITIONING_IN, true | false'
- Setsstate.transitioning_in
to the given value.'SET_TRANSITIONING_OUT, true | false'
- Setsstate.transitioning_out
to the given value.'SET_LOADED', true | false
- Setsstate.loaded
to the given value.'OPEN_MENU'
- Setsstate.menuOpened
totrue
.'CLOSE_MENU'
- Setsstate.menuOpened
tofalse
.'UPDATE_REFERRAL_ROUTE'
- Setsstate.referral
to given referral object.
Where mutations are synchronous, actions are asynchronous:
this.$store.dispatch('ACTION_NAME', payload)
Default Vuepress actions:
'LOAD_AND_REPLACE_QUERYDATA, { path: 'url string' }'
- Runs the following process:- Sets
state.loaded
tofalse
. - Checks
src/utils/cache.js
(which is a global cache that can beimport
ed into any other file) for the givenpath
key. If none is found:- Commits
'SET_LOADED', false
- Fetches the data from the the URL at the payload path.
- Saves the data to the cache.
- Commits
- Commits
'REPLACE_QUERYDATA'
with the data from the cache. - Commits
'SET_LOADED', true
- Sets
Getters are shortcuts to dynamic state properties:
$store.state.getters.desiredGetter
Default Vuepress getters include:
loading
- Returns the opposite of$store.state.loaded
.post
- Returns either the first post in$store.state.loop
or, if none, an empty object.referralPath
- Returns either thefullPath
of the current value of$store.state.referral
or, if none, an empty string.
Once you've set up the routing for a Vuepress site and understand its state functions, you can start working with Vue templates themselves.
-
Plan and document the general structure of the site in your README.md. Example:
* Front page * About * Our History * Our Employees * Employee Bio * ...
-
Figure out which pages are necessary to the structure of the site. Give those pages Developer IDs and prevent non-Dev deletion. Example:
Front Page, About, and Our Employees all have child pages, so we'll give them Developer IDs of 'front-page', 'about', and 'employees', respectively, as well as lock them.
-
Create routing conditions in
functions/router.php
'sadd_routes_to_json
function:array( '' => 'FrontPage', path_from_dev_id('about') => 'About', path_from_dev_id('employees) => 'EmployeesGrid', path_from_dev_id('about', '/:child') => 'AboutChildGeneric', path_from_dev_id('employees', '/:employee') => 'EmployeeDetail' );
-
Create the necessary Vue templates. Example:
We defined 'FrontPage', 'About', 'EmployeesGrid', 'AboutChildGeneric', and 'EmployeeDetail' above, so we'll be creating each of those as a .vue file in
src/views/
. -
npm run dev
and start building in Vue!
Throttled resize and scroll events are available to any child of the App component:
this.$root.$on('throttled.resize', () => {
// your throttled resize event here...
// default: 1 per 10ms
})
this.$root.$on('throttled.scroll', () => {
// your throttled scroll event here...
// default: 1 per 10ms
})
Both events are fired after the $root
element saves updated window dimensions/scroll positions for resize/scroll events.
Vuepress comes with a few SCSS partials to make writing CSS easier. In a Vue template file:
<style lang="scss">
@import 'src/styles/desired-partial';
</style>
Default partials include:
-
base
- Style applied inApp.vue
, affecting every page on the site. -
transitions
- Common transitions applied inApp.vue
, affecting every page on the site. Includes:fade
slide-left
slide-right
Usable with:
<transition name="transition-name"><your-code-here/></transition>
-
vars
- Variables to use across the site. Import in any given template to make global CSS changes much easier to manage. Defaults include:$white: #ffffff;
$black: #000000;
$font-family: 'Helvetica';
$desktop-padding: 50px;
$mobile-padding: 20px;
$header-height: 80px;
The following are breakpoints that can be used with:
@media #{$size} { /* your rules here */ }
$gt-cinema: "only screen and (min-width: 1800px)";
$lt-desktop: "only screen and (max-width: 1100px)";
$lt-phone: "only screen and (max-width: 750px)";
$lt-phone-landscape: "only screen and (max-width: 750px) and (orientation: landscape)";
Not Vuepress-specific reading material, but rather good practices and articles.
- Maintainable CSS, a guide to writing readable and easily-maintained CSS
- SVG Tips for Designers
Vuepress
Version: 1.1.4
- 1.1.4 - Restructuring according to this issue
- 1.1.3 - Split Vuepress functionality into
/functions
directory - 1.1.2 - Added TGM Plugin Activation to require plugins. Switching to x.x.x version numbering.
- 1.11 - Switched
_custom_developer_id
tocustom_developer_id
- 1.1 - Switched
_custom_guid
to_custom_developer_id
- 1.0 - Initial release