{"id":1873,"date":"2020-09-30T23:19:34","date_gmt":"2020-09-30T16:19:34","guid":{"rendered":"https:\/\/wptips.dev\/?p=1873"},"modified":"2022-01-30T22:18:31","modified_gmt":"2022-01-30T15:18:31","slug":"vue-without-webpack","status":"publish","type":"post","link":"https:\/\/pixelstudio.id\/blog\/vue-without-webpack\/","title":{"rendered":"How to Use Vue in WordPress (Without Webpack)"},"content":{"rendered":"\n<p>If you are making a complex front-end feature in WordPress, <strong>don&#8217;t use jQuery<\/strong>. Get a proper library like Vue!<\/p>\n\n\n\n<p>It will save you lots of time and headache from maintaining it.<\/p>\n\n\n\n<p>In this article, we will learn how to <strong>setup Vue in WordPress<\/strong>. This is something that I struggled with because it&#8217;s quite different compared to setting it in plain HTML.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What is Vue JS?<\/h2>\n\n\n\n<p><a rel=\"noreferrer noopener\" href=\"https:\/\/vuejs.org\/\" target=\"_blank\">Vue JS<\/a> is a library to create an interactive element. It is the easiest to learn compared to React or Angular.<\/p>\n\n\n\n<p>I won&#8217;t teach you the basics of Vue. There are many people covering that already, such as this awesome tutorial from Academind:<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"GETTING STARTED with VueJS | VueJS | Learning the Basics\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/nyJSd6V2DRI?list=PL55RiY5tL51p-YU-Uw90qQH419BM4Iz07\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><figcaption>Vue Tutorial Playlist by Academind<\/figcaption><\/figure>\n\n\n\n<p>But those tutorials are in <strong>plain HTML<\/strong>. Let&#8217;s integrate it with WordPress!<\/p>\n\n\n\n<hr class=\"wp-block-separator is-style-dots\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1: Register JavaScript<\/h2>\n\n\n\n<p>Minified Vue doesn&#8217;t give proper error messages, so during development, you need to use the expanded version.<\/p>\n\n\n\n<p>Here&#8217;s how to set the conditional:<\/p>\n\n\n\n<pre title=\"functions.php\" class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">add_action( 'wp_enqueue_scripts', 'enqueue_vue' );\n\nfunction enqueue_vue() {\n  if( WP_DEBUG ) {\n    wp_register_script( 'vue', 'https:\/\/cdn.jsdelivr.net\/npm\/vue\/dist\/vue.js', [], '', true );\n  } else {\n    wp_register_script( 'vue', 'https:\/\/cdn.jsdelivr.net\/npm\/vue\/dist\/vue.min.js', [], '', true );\n  }\n\n  \/\/ your app code\n  wp_register_script( 'my-app', get_stylesheet_directory_uri()\n . '\/js\/my-app.js', [], '', true );\n}\n<\/code><\/pre>\n\n\n\n<p>Then, change the <code>WP_DEBUG<\/code> constant in your local site&#8217;s config to <code>true<\/code>. Your live site should stay false.<\/p>\n\n\n\n<pre title=\"wp-config.php\" class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">...\n\ndefine( 'WP_DEBUG', true );\n\n...<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2: Enqueue JavaScript<\/h2>\n\n\n\n<p>You should only enqueue the script when the page requires it.<\/p>\n\n\n\n<p>For example, we have a Vue app called Cost Calculator that is outputted with <code>[cost-calculator]<\/code> shortcode.<\/p>\n\n\n\n<p>Here&#8217;s how to load the scripts <strong>only when the shortcode is used<\/strong>:<\/p>\n\n\n\n<pre title=\"functions.php\" class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">add_shortcode( 'cost-calculator', 'shortcode_cost_calculator' );\n\nfunction shortcode_cost_calculator( $atts ) {\n  wp_enqueue_script( 'vue' );\n  wp_enqueue_script( 'my-app' );\n\n  \/\/ ...\n}<\/code><\/pre>\n\n\n\n<p><strong>Note<\/strong>: If you use the app in all pages, then enqueue it right after you register it in Step 1.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3: Pass in PHP Variables (if needed)<\/h2>\n\n\n\n<p>Use localize feature to pass them like shown below:<\/p>\n\n\n\n<pre title=\"functions.php\" class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">add_shortcode( 'cost-calculator', 'shortcode_cost_calculator' );\n\nfunction shortcode_cost_calculator( $atts ) {\n  wp_enqueue_script( 'vue' );\n  wp_enqueue_script( 'cost-calculator' );\n\n  wp_localize_script( 'cost-calculator', 'ccLocalize', [\n    'API_URL' =&gt; 'https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2',\n    'items' =&gt; [\n      [ 'name' =&gt; 'Item 1', 'price' =&gt; 100 ],\n      [ 'name' =&gt; 'Item 2', 'price' =&gt; 150 ],\n    ]\n  ] );\n\n  \n  return get_template_part( 'shortcode\/cost-calculator' );\n}<\/code><\/pre>\n\n\n\n<p>Now your script can use the variable <code>ccLocalize<\/code> that contains those values.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4: Template and Script<\/h2>\n\n\n\n<p>Usually, it&#8217;s easier to write the <strong>template<\/strong> first.<\/p>\n\n\n\n<p>In this example, we want to list our items with buy button and total cost at the bottom:<\/p>\n\n\n\n<pre title=\"shortcode\/cost-calculator.php\" class=\"wp-block-code\"><code lang=\"html\" class=\"language-html\">&lt;div id=\"cost-calculator\" class=\"cost-calculator\" v-cloak&gt;\n  &lt;h1&gt;Cost Calculator&lt;\/h1&gt;\n  &lt;ul&gt;\n    &lt;li v-for=\"i in items\" :key=\"i.name\"&gt;\n      &lt;h3&gt;{{ i.name }}&lt;\/h3&gt;\n      &lt;p&gt;{{ i.price }}&lt;\/p&gt;\n      &lt;a class=\"button\" @click=\"addToCart( i )\"&gt;Buy This&lt;\/a&gt;\n    &lt;\/li&gt;\n  &lt;\/ul&gt;\n\n  &lt;h2&gt;Total Cost: {{ total }}&lt;\/h2&gt;\n&lt;\/div&gt;<\/code><\/pre>\n\n\n\n<p>The <code>v-cloak<\/code> attribute will hide the element before it finished loading. You simply need to add this in your CSS:<\/p>\n\n\n\n<pre title=\"style.css\" class=\"wp-block-code\"><code lang=\"css\" class=\"language-css\">[v-cloak] {\n  display: none\n}<\/code><\/pre>\n\n\n\n<p>Now for the <strong>script<\/strong>, here&#8217;s how I write it. Wrapped with self-invoking function, on-ready listener, and no jQuery in sight!<\/p>\n\n\n\n<pre title=\"js\/my-app.js\" class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript\">(function() { 'use strict';\n\ndocument.addEventListener( 'DOMContentLoaded', onReady );\nfunction onReady() {\n  initApp();\n}\n\nfunction initApp() {\n  new Vue({\n    el: '#cost-calculator',\n    data: {\n      API_URL: ccLocalize.API_URL,\n      items: ccLocalize.items,\n      total: 0\n    },\n    methods: {\n      addToCart( item ) {\n        this.total += item.price\n      },\n    },\n  });\n}\n\n})();<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 5: Need Components?<\/h2>\n\n\n\n<p>Let&#8217;s say we want to split the items into Component, here&#8217;s how I would organize them:<\/p>\n\n\n\n<pre title=\"shortcode\/cost-calculator.php\" class=\"wp-block-code\"><code lang=\"html\" class=\"language-html\">&lt;div id=\"cost-calculator\" class=\"cost-calculator\" v-cloak&gt;\n  &lt;h1&gt;Cost Calculator&lt;\/h1&gt;\n  &lt;ul&gt;\n    &lt;product-item v-for=\"i in items\" :key=\"i.name\"\n      :name=\"i.name\"\n      :price=\"i.price\"\n      @cart=\"onAddToCart\" \/&gt;\n  &lt;\/ul&gt;\n\n  &lt;h2&gt;Total Cost: {{ total }}&lt;\/h2&gt;\n&lt;\/div&gt;\n\n&lt;!-- Template for ProductItem --&gt;\n&lt;template id=\"item-template\"&gt;\n  &lt;li&gt;\n    &lt;h3&gt;{{ name }}&lt;\/h3&gt;\n    &lt;p&gt;{{ price }}&lt;\/p&gt;\n    &lt;a class=\"button\" @click=\"addToCart\"&gt;Buy This&lt;\/a&gt;\n  &lt;\/li&gt;\n&lt;\/template&gt;<\/code><\/pre>\n\n\n\n<pre title=\"js\/my-app.js\" class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript\">(function() { 'use strict';\n\ndocument.addEventListener( 'DOMContentLoaded', onReady );\nfunction onReady() {\n  initApp();\n}\n\nfunction initApp() {\n  new Vue({\n    el: '#cost-calculator',\n    \/\/ register the component here\n    components: {\n      ProductItem: ProductItem()\n    },\n    data: {\n      API_URL: ccLocalize.API_URL,\n      items: ccLocalize.items,\n      total: 0\n    },\n    methods: {\n      onAddToCart( item ) {\n        this.total += item.price\n      },\n    },\n  });\n}\n\n\/\/ &lt;product-item&gt; component\nfunction ProductItem() {\n  return {\n    template: '#item-template',\n    props: [ 'name', 'price' ],\n    data: () =&gt; { return {} },\n    methods: {\n      addToCart() {\n        this.$emit( 'cart', this.$props );\n      },\n    }\n  };\n}\n\n})();<\/code><\/pre>\n\n\n\n<p>I organize it this way because it&#8217;s similar to Webpack format. It will help you transition into it in the future.<\/p>\n\n\n\n<hr class=\"wp-block-separator is-style-dots\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>This is a quick and dirty way to create Vue app in WordPress. It&#8217;s much tidier to use build tools like Webpack, but its barrier of entry can be intimidating.<\/p>\n\n\n\n<p>If you&#8217;re wondering how to use Vue with Webpack, check out my <a href=\"https:\/\/pixelstudio.id\/blog\/vue-wordpress-with-webpack\/\">other tutorial<\/a>.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote\"><p>Let me know in the comment if you have any question regarding this topic \ud83d\ude42<\/p><\/blockquote>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you need a complex front-end feature in WordPress, don&#8217;t use jQuery. Get a proper library like Vue! We will learn how to set it up in this article.<\/p>\n","protected":false},"author":1,"featured_media":1881,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12],"tags":[32,31,49],"class_list":["post-1873","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-frontend","tag-javascript","tag-php","tag-vue"],"blocksy_meta":"","_links":{"self":[{"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts\/1873","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/comments?post=1873"}],"version-history":[{"count":11,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts\/1873\/revisions"}],"predecessor-version":[{"id":2075,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts\/1873\/revisions\/2075"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/media\/1881"}],"wp:attachment":[{"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/media?parent=1873"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/categories?post=1873"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/tags?post=1873"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}