Intro
I read this article of Lisa Armstrong to use VueJS to create a plugin for Wordpress. My post will extend Lisa's way and show a solution with a little bit more VueJS.
This is just a really short and quick article. I will write more about this next time.
Step 1
First we have to create a Vue application with vue create wp-vue
. Select the default configuration and run npm run build
.
Step 2
Now we can follow the steps of Lisas' article.
The shorthand-version
- We create a file wp-vue.php in /wp-content/plugin/wp-vue/
- with the following code:
<?php
/**
* Plugin Name: WordPress Vue
* Description: Vue-App in WordPress.
*/
function func_load_vuescripts() {
wp_register_script('wpvue_vuejs', plugin_dir_url( __FILE__ ).'dist/js/app.c8d5a15f.js', true);
wp_register_script('wpvue_vuejs1', plugin_dir_url( __FILE__ ).'dist/js/chunk-vendors.5e0c61d5.js', true);
}
add_action('wp_enqueue_scripts', 'func_load_vuescripts');
//Add shortscode
function func_wp_vue(){
wp_enqueue_script('wpvue_vuejs');
wp_enqueue_script('wpvue_vuejs1');
$str= "<div id='app'>"
."Message from Vue: "
."</div>";
return $str;
} // end function
add_shortcode( 'wpvue', 'func_wp_vue' );
?>
- one very important change is the id attribute of the div element. For the connection to our Vue-App
- a second important change is that we won't load the VueJS-Script with CDN. We load the chunk-vendors.js and the app.js from our Vue-App
- now we have to activate the plugIn in Wordpress
Step 3
To get the js files we have to upload the dist folder which we created in Step 1 to the plugin folder wp-vue
Step 4
If you already added the shortcode [wpvue]
in one of your pages the whole Vue-App shows up. We only have to reduce the code in HelloWorld.vue a little bit:
<template>
<div class="hello">
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
...and run the npm run build
task again. After that we can upload the necessary files and delete the old ones. We also have to change the scripts in our wp-vue.php. Now we get a perfect start for a new Vue-App plugin.
The hash behind app.
and chunk-vendors.
should be delete to avoid editing the wp-vue.php script. We can solve this by adding a file vue.config.js
next to our package.json with the following code:
module.exports = {
filenameHashing: false,
}
We just have to remove the hashs out of our wp-vue.php
and it will work. But you have to be careful with that because the caching should be handled at any point. Look at Jony's suggestion
Development
To develope in the development mode (so that we are able to use all advantages of VueJS) we can run npm run serve
and create a local Vue-App to show our plugin.
A huge advantage of this way is that we also can realize an atomic design workflow and are also able to scale this up by using the Vue-App for more than one plugin.
Top comments (28)
Cool stuff!
I would like to contribute with a quick piece of code to handle the
enqueue_script
cache andfilenameHashing
issue.We can use the filemtime on the
func_load_vuescripts
function a to handle files being cached:I've also added the
chunck-vendors
as a dependency onwpvue_vuejs
so it gets enqueue automatically:Nice! Thanks for this! I extended your code to use glob and regex so I could keep the hash.
Awesome!!
Although - if you allow me to share a small thought - if you are using the hash, you do not need
filemtime
. I mean, the purpose of the hash is to avoid whatever new code you create from being cached...I guess it's a way to make the browser "think" it's a new file... WordPress handles this is by adding?v=XXX
to the end of your path where XXX is whatever you pass as the last argument for thewp_register_script
function.So, to make your code just a smidge more performant, I would replace
filemtime
withnull
(or simply remove the argument) :DThank you Jony! :)
That are two really good points! I added it to the article.
Excellent article! I ended up using this to build a new map plugin which is coming along very nicely. Something I am struggling with is trying to figure out a way to get the proper paths for some things like images. Usually in WP I would use something like "<?php echo get_template_directory_uri(); ?>" to handle that but obviously I can not use a PHP function in my JS app.
I have read about wp_localize_script() which can allow you to pass the values of said PHP functions to JS but I have no idea how to get that to work with this type of set up since it is a plugin.
Any ideas?
Hi Dallas Earle and thank you!
Is it possible for you to use the Wordpress API? If this is the case, maybe you can get your images from "demo.wp-api.org/wp-json/wp/v2/media"?
Well I am currently using WP API to fetch data from custom posts but the images I need are stored in a folder in the Theme. Wouldnt the "/wp/v2/media" endpoint only be good to retrieve media from posts? I am pretty new to using WP so I am not entirely familar with how the WP API works yet.
I'm sorry. I did not read that correctly.
If you know where the pictures are stored, can you access this folder in your Vue app?
Hi.
I'm still reallu unclear with the policies of Wordpress repository, about this, if they would allow to publish a plugin to WP, that would be done using NPM and compilation... even if we keep the full .vue files content into the plugin...
Any feedbacks on this ?
Isn't it something that would make our pluign rejected by WP team?
thanks
That's a good question. My understanding is that Vue, npm, etc. are just build tools, which means they shouldn't be included in the final production version of a plugin if it's going to be released to the public. Typically you would have the build files on your local copy, and when you run the build command, it will create a distribution folder with everything compiled down to plain PHP/HTML/CSS/JS. That distribution folder is what you want to include with the released version of your plugin, not the build files.
If you're using Git for version control, you can tell Git to ignore the build files (npm, node_modules, Vue files, SCSS files, etc.) when you push up versions of your plugin. Those files would only live on your local copy since their sole purpose is to make the development workflow easier/faster.
Hope that helps clarify!
Thanks Chris for your response! And your totally right :)
Hi Paloma, thanks, you did exactly what I needed so thanks again ... and thanks to Lisa too
all work perfectly except the image and the style css!! strange no? do you have an idea?
the .json works fine. I add to have more page and I did single page I mean template/script/style in each one
each image are present in /img and in the page the link refer to /img
it's works perfect in local
Franck from Paris
franckysmith,
If you're saying you weren't able to get CSS working, I may be able to partially help. I've created a Vue/Quasar app and was able to load its CSS like this:
wp_register_style('wpvue_style1', plugin_dir_url( FILE ).'dist/spa/css/app.0e433876.css', true);
and
wp_enqueue_style('wpvue_style1');
per Paloma's guidance.
thanks Terry,
maybe you saw I found myself the same solution... or almost
Hey Franck,
thank you very much!
I didn't get this one "I add to have more page and I did single page I mean template/script/style in each one" completly. Can you explain that a bit more? Maybe then I can say something about it. Are the read & write permissions of the folders in the server file system sufficient?
About the images:
The link for the images called up in the network tab (prod) ended in nothing? What would have to be changed so that the link to the image is correct?
Maybe the local link is not completely the same as the productive link?
Hi Paloma, and thanks for your response?
Finaly I fixed all:
for the style css I add in wp-vue.php: wp_register_style( 'stylesheet', plugins_url( 'dist/css/app.css' , FILE ) ); and
wp_enqueue_style( 'stylesheet' );
and for the image I put the img folder next to wp-content , wp-admin ...
of course it should be better if we could let it in the dist folder ... but for now it works fine
thanks again
Happy to hear that! :)
And you are right. Maybe this solution helps you: stackoverflow.com/questions/522163...
hello I perfectly executed your tutorial and finally, thanks to you, I found one to use vue without tag script, which has involved me a great deal of performance on GTMAtrix. I got stuck in point 4, activating 'npm run build' I have to go to localhost: 8080 to view the page. But on the shortcode page I only see 'Message from Vue:'. Could you kindly be more specific on how to implement new vue code on that shortcode? How do I add new files as in the initial tutorial you refer to. I have another question, my shortcode will have an output parameter which identifies it 'id'. As it could be called multiple times on the same page. How can I change, according to this 'id', also the id (el) Vue, so that they do not conflict? one last thing. When I finish the plugin, just send the folder via ftp to the site remotely or do I have to take the distribution? if the second in what way. Forgive me for all these questions, but I'm very confused.
Hi Salvo,
you have two parts. The first part is to run "npm run build" in your local Vue-App. Out of this vue creates an distribution folder. In this folder the files are stored, which should be uploaded in the second part. Second part: You can upload the dist folder into you plugin folder via ftp. Important point is to change the name of the scripts in your wp-vue.php with the correct hash value if filename-hashing is activated in your vue app.
It's probably the most rudimentary way, but you can easily exchange the files via ftp. Of course you can also build a pipeline and connect your ftp server to it, but for a little test this would be a bit of overkill.
I'm not sure I understood your intention correctly. You want to be able to place your Vue plugin on the website multiple times and the id of both the WP-shortcode and the initial Vue tag is the same? Unfortunately, this is not easy to do because the id of the Vue app is already fixed before the backend code runs.
Awesome article Paloma, really helped me get my basic Vue app working (mostly) in WP site. However, I've run into some tricky (for me at least) styling issues. I guess various styles of my WP site's theme are overriding my Vue app's styles. I'm loading the styles using 'wp_register_style' and 'wp_enqueue_style' using the pattern you show above. I've confirmed the styles are being loaded in my WP site, but apparently being overridden. I've experimented with trying to affect their load order by adding (and removing) the 'true' parameter on wp_register_style, as well as moving the order of my register and enqueue calls first and last. I also saw a suggestion to add a high index number parameter to the 'add_action' call like this:
add_action('wp_enqueue_scripts', 'func_load_vuescripts', 99999);
One conflict I found was with jquery which was included with my site's theme (I believe). When I removed it in my browser's web tools, my app's layout improved some.
Any ideas?
Hi Terry,
thank you! :)
Several problems may have arisen here. Actually Vue.js scopes its styling if the scope attribute is used. Furthermore I recommend only styling using/via classes (preferably in BEM) so that nothing can be overwritten. The advantage of BEM is also the readability. The specificity of the CSS selectors is also an important indicator (css-tricks.com/specifics-on-css-sp...).
It could be that styles are reloaded using jQuery. By removing this, this may be omitted.
Hi, and thanks for this article!
I have a problem making it work though. The Vue example app will not display on the page. This is what I got:
chunk-vendors.js and app.js are loaded on the same page along with the shortcode generated div (#app). As confirmed by browser network tab and elements tab.
I have tested with yarn serve that a dev server version works and displays the application.
Any hints to what I am missing?
Edit: Now it works!
I had to add DomContentLoaded listener to delay the Vue instantiation in main.js. Without it it will not work for me with what is provided in this tutorial. I'd be happy to get a comment telling me I am wrong and why.
Hey oivinds!
sorry for my late response and i'm happy to hear that you get it working now.
It is a bit difficult for me to debug this one but is it working with a new and clean wordpress installation?
Hi, how would you handle the shortcode beeing used twice (or more often - after all its up to the wordpress user)? You would have the same element id twice. I am trying to find a solution in which I bind it to a class but can't get it running:
'''
let elements = document.getElementsByClassName('conversionform')
for(var i = 0; i < elements.length; i++){
new Vue({
store,
render: h => h(App)
}).$mount( ???? )
}
Hey Dennis!
I didn't had time to expand this little tut and i didn't tested it with WP but a maybe better solution would be to create a shortcode with a component tag of the vue component. So that you would create a string like this:
To mount the vue-app to the whole page you could set an id to a global tag like the body tag. I think this could be even a better solution for the integration of vue.js in wordpress. I'm still working on this to create a simple solution for using vue tags. I hope i can put this way to an article soon.
In addition you have to handle the script imports, otherwise it could be that it would be imported every time the shortcode is used, but i think the browser is smart enough to handle that.
great work i would like to contribute with a quick piece of code to handle the css after
run build
module.exports = {
filenameHashing: false,
css: { extract: false }
}
Got this up and running pretty smoothly, thanks for the walkthrough.
I have a situation where a site is mostly Wordpress, but I'm using Vue + Wordpress REST API for a few features that are more dynamic. These Vue features are on ~6 different pages. They use some shared components, but have different base templates. Eg PostsPage.vue and PeoplePage.vue both use RadioFilter.vue and are on different WP pages.
I'm wondering how to deploy this on different pages, either with different shortcodes or passing a param through the shortcode. How can I structure my codebase so I can benefit from reusable components while also having essentially 5 or 6 different apps to be injected?
Hey Cole!
You could create a globals folder, where you can store all your global vue-components, stylings, extra scripts and so on. On the same level of the globals folder you can set up your different Vue instances. These can access the necessary global stuff.
Each Vue instance get a shortcode so that all instances can work independently. This makes sense if they are used on different pages. Otherwise you may have duplicate code.