{"id":2064,"date":"2022-01-30T22:16:39","date_gmt":"2022-01-30T15:16:39","guid":{"rendered":"https:\/\/wptips.dev\/?p=2064"},"modified":"2022-02-17T11:52:47","modified_gmt":"2022-02-17T04:52:47","slug":"vue-wordpress-with-webpack","status":"publish","type":"post","link":"https:\/\/pixelstudio.id\/blog\/vue-wordpress-with-webpack\/","title":{"rendered":"How to Use Vue in WordPress (with Webpack)"},"content":{"rendered":"\n<blockquote class=\"wp-block-quote\"><p>Recommended before reading this: <a rel=\"noreferrer noopener\" href=\"https:\/\/pixelstudio.id\/blog\/webpack-in-wordpress\/\" target=\"_blank\">How to use Webpack<\/a> and <a rel=\"noreferrer noopener\" href=\"https:\/\/pixelstudio.id\/blog\/vue-without-webpack\/\" target=\"_blank\">How to use Vue without Webpack<\/a>.<\/p><\/blockquote>\n\n\n\n<p>Combining Vue and Webpack is very powerful. It allows you to create <code>.vue<\/code> file that contains HTML, JS, and CSS in one place.<\/p>\n\n\n\n<p><strong>Wait, that sounds like a bad idea!<\/strong> We&#8217;ve always been separating those three, why combine them now?<\/p>\n\n\n\n<p>Continue reading to know why this is a good thing.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote\"><p><strong>TABLE OF CONTENTS<\/strong><\/p><p>1. <a href=\"#problem\">The Problem with Splitting HTML, JS, and CSS<\/a><br>2. <a href=\"#vue-file\">The <code>.vue<\/code> File<\/a><br>3. <a href=\"#package-setup\">Package and Webpack Setup<\/a><br>4. <a href=\"#app-setup\">App Setup (Enqueue and Shortcode)<\/a><br>5. <a href=\"#the-script\">The Script<\/a><\/p><\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"problem\">1. The Problem with Splitting HTML, JS, and CSS<\/h2>\n\n\n\n<p>Let&#8217;s say you saw this HTML button in your WordPress theme:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">&lt;button id=\"calculate\"&gt; Calculate &lt;\/button&gt;<\/code><\/pre>\n\n\n\n<p>Do you know what it does? <strong>No! It tells nothing.<\/strong><\/p>\n\n\n\n<p>You need to find the JS file that contains its event listener. Maybe it&#8217;s even mixed within thousand lines of other code!<\/p>\n\n\n\n<p>The <code>.vue<\/code> file solves this problem.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"vue-file\">2. The <code>.vue<\/code> File<\/h2>\n\n\n\n<pre title=\"Example.vue\" class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript\">&lt;template&gt;\n  &lt;!-- HTML --&gt;  \n&lt;\/template&gt;\n\n&lt;script&gt;\n\/\/ JS\nexport default {\n}\n&lt;\/script&gt;\n\n&lt;style&gt;\n  \/** CSS *\/\n&lt;\/style&gt;<\/code><\/pre>\n\n\n\n<p>That is what the file looks like: You have <code>&lt;template&gt;<\/code>, <code>&lt;script&gt;<\/code>, and <code>&lt;style&gt;<\/code> to hold the HTML, JS, and CSS respectively.<\/p>\n\n\n\n<p>Everything is in one place. If you see a button, the listener is just a few lines away. If you want to change its color, simply write it under the style tag.<\/p>\n\n\n\n<p>If you&#8217;re convinced, then let&#8217;s do the initial setup.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"package-setup\">3. Package and Webpack Setup<\/h2>\n\n\n\n<p>This is the <code>package.json<\/code> file I&#8217;m using for most of my WordPress Vue projects:<\/p>\n\n\n\n<pre title=\"my-theme\/package.json\" class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">{\n  \"name\": \"vue-test\",\n  \"private\": true,\n  \"dependencies\": {\n    \"@babel\/polyfill\": \"^7.11.5\",\n    \"vue\": \"^2.6.12\"\n  },\n  \"devDependencies\": {\n    \"@babel\/cli\": \"^7.15.7\",\n    \"@babel\/core\": \"^7.11.6\",\n    \"@babel\/preset-env\": \"^7.11.5\",\n    \"@vue\/test-utils\": \"^1.1.0\",\n    \"autoprefixer\": \"^10.0.0\",\n    \"babel-core\": \"^7.0.0-bridge.0\",\n    \"browser-sync\": \"^2.27.5\",\n    \"browser-sync-webpack-plugin\": \"^2.2.2\",\n    \"css-loader\": \"^4.3.0\",\n    \"babel-eslint\": \"^10.1.0\",\n    \"babel-jest\": \"^26.3.0\",\n    \"eslint\": \"^7.32.0\",\n    \"eslint-config-airbnb-base\": \"^14.2.1\",\n    \"eslint-plugin-import\": \"^2.24.2\",\n    \"eslint-plugin-jest\": \"^24.4.0\",\n    \"eslint-plugin-vue\": \"^6.2.2\",\n    \"file-loader\": \"^6.1.0\",\n    \"jest\": \"^26.4.2\",\n    \"jest-transform-stub\": \"^2.0.0\",\n    \"mini-css-extract-plugin\": \"^0.11.2\",\n    \"node-sass\": \"^4.14.1\",\n    \"postcss\": \"^8.3.9\",\n    \"postcss-loader\": \"^4.0.2\",\n    \"sass-loader\": \"^10.0.2\",\n    \"url-loader\": \"^4.1.0\",\n    \"vue-jest\": \"^3.0.7\",\n    \"vue-loader\": \"^15.9.3\",\n    \"vue-style-loader\": \"^4.1.2\",\n    \"vue-template-compiler\": \"^2.6.12\",\n    \"webpack\": \"^4.44.1\",\n    \"webpack-cli\": \"^3.3.12\"\n  },\n  \"scripts\": {\n    \"build\": \"webpack --mode production\",\n    \"dev\": \"webpack --mode development --watch\",\n    \"test\": \"jest\"\n  },\n  \"browserslist\": [\n    \"last 2 versions\"\n  ],\n  \"babel\": {\n    \"presets\": [\n      \"@babel\/preset-env\"\n    ]\n  },\n  \"jest\": {\n    \"moduleFileExtensions\": [\n      \"js\",\n      \"json\",\n      \"vue\"\n    ],\n    \"transform\": {\n      \"^.+\\\\.vue$\": \"vue-jest\",\n      \"^.+\\\\.js$\": \"babel-jest\"\n    }\n  }\n}\n<\/code><\/pre>\n\n\n\n<p>Then my <code>webpack.config.js<\/code> setup:<\/p>\n\n\n\n<pre title=\"my-theme\/webpack.config.js\" class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript\">const { VueLoaderPlugin } = require('vue-loader');\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin');\nconst BrowserSyncPlugin = require('browser-sync-webpack-plugin');\nconst path = require('path');\n\n\n\/\/ Change this to fit your project structure\nconst jsPath= '.\/js';\nconst cssPath = '.\/css';\nconst outputPath = 'dist';\nconst localDomain = 'http:\/\/mysite.local';\n\nmodule.exports = {\n  entry: {\n    'calc-app': `${jsPath}\/calc-app.js`,\n  },\n  output: {\n    path: path.resolve(__dirname, outputPath),\n    filename: '[name].js',\n  },\n  module: {\n    rules: [\n      {\n        test: \/\\.vue$\/,\n        use: 'vue-loader',\n      },\n      {\n        test: \/\\.s?[c]ss$\/i,\n        use: [\n          MiniCssExtractPlugin.loader,\n          'css-loader',\n          'sass-loader',\n        ],\n      },\n      {\n        test: \/\\.sass$\/i,\n        use: [\n          MiniCssExtractPlugin.loader,\n          'css-loader',\n          {\n            loader: 'sass-loader',\n            options: {\n              sassOptions: { indentedSyntax: true },\n            },\n          },\n        ],\n      },\n      {\n        test: \/\\.(jpg|jpeg|png|gif|woff|woff2|eot|ttf|svg)$\/i,\n        use: 'url-loader?limit=2048',\n      },\n    ],\n  },\n  plugins: [\n    new VueLoaderPlugin(),\n    new BrowserSyncPlugin({\n      proxy: localDomain,\n      files: [`${outputPath}\/*.css`],\n      injectCss: true,\n    }, {\n      reload: false,\n    }),\n    new MiniCssExtractPlugin({\n      filename: '[name].css',\n    }),\n  ],\n  resolve: {\n    alias: { vue: 'vue\/dist\/vue.esm.js' },\n  },\n};\n\n<\/code><\/pre>\n\n\n\n<p>After creating those two files, run the command to install the packages. Read our <a rel=\"noreferrer noopener\" href=\"https:\/\/pixelstudio.id\/blog\/webpack-in-wordpress\/\" target=\"_blank\">Webpack tutorial<\/a> if you don&#8217;t know how.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"app-setup\">4. App Setup (Enqueue and Shortcode)<\/h2>\n\n\n\n<p>We will output the Vue app using shortcode.<\/p>\n\n\n\n<p>I know shortcode is archaic, but it&#8217;s still the quickest way to insert a custom code into whatever page builder you&#8217;re using. It also lets us enqueue the Vue script <strong>only when it&#8217;s used.<\/strong><\/p>\n\n\n\n<p>For example, we want to create a cost calculator:<\/p>\n\n\n\n<pre title=\"functions.php\" class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">add_action('wp_enqueue_scripts', 'my_enqueue_cost_calculator');\nadd_shortcode('cost-calculator', 'my_shortcode_cost_calculator');\n\nfunction my_enqueue_cost_calculator() {\n\n  $dir = get_stylesheet_directory_uri() . '\/dist';\n\n  wp_register_style('calc-app', $dir . '\/calc-app.css', [], '');\n  wp_register_script('calc-app', $dir . '\/calc-app.js', [], '', false);\n}\n\nfunction my_shortcode_cost_calculator($atts, $content = null) {\n  wp_enqueue_style('calc-app');\n  wp_enqueue_script('calc-app');\n\n  \/\/ Assume this is taken from get_posts() or from API\n  $items = [\n    [ 'id' =&gt; 0, 'name' =&gt; 'Fried Rice', 'price' =&gt; 8 ],\n    [ 'id' =&gt; 1, 'name' =&gt; 'Dumpling',   'price' =&gt; 5 ],\n    [ 'id' =&gt; 2, 'name' =&gt; 'Tea',        'price' =&gt; 2 ],\n  ];\n  $items_json = json_encode($items);\n\n  return \"&lt;div id='cost-calculator'&gt;\n    &lt;cost-calculator data-items='{$items_json}' \/&gt;\n  &lt;\/div&gt;\";\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-script\">5. The Script<\/h2>\n\n\n\n<pre title=\"js\/calc-app.js\" class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript\">import Vue from 'vue';\nimport CostCalculator from '.\/CostCalculator.vue';\n\nfunction onReady() {\n  const app = new Vue({\n    el: '#cost-calculator',\n    components: {\n      'cost-calculator': CostCalculator,\n    },\n  });\n}\n\ndocument.addEventListener('DOMContentLoaded', onReady);\n<\/code><\/pre>\n\n\n\n<p>First, we import Vue, so we don&#8217;t need to enqueue it separately.<\/p>\n\n\n\n<p>After that, we import the <code>.vue<\/code> file that we will register as a component.<\/p>\n\n\n\n<p>Then, we add a ready listener that will convert <code>#cost-calculator<\/code> (that we echoed using shortcode) into a Vue app.<\/p>\n\n\n\n<p>Now let&#8217;s take a look at <code>CostCalculator.vue<\/code>:<\/p>\n\n\n\n<pre title=\"js\/CostCalculator.vue\" class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript\">&lt;template&gt;\n  &lt;div class=\"cost-calculator\"&gt;\n    &lt;ul&gt;\n      &lt;li v-for=\"i in items\" :key=\"i.name\"&gt;\n        &lt;strong&gt;{{ i.name }}&lt;\/strong&gt;\n        &lt;span&gt;${{ i.price }}&lt;\/span&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;\n&lt;\/template&gt;\n\n&lt;script&gt;\nexport default {\n  props: {\n    dataItems: {\n      type: [String],\n      required: true,\n    },\n  },\n\n  data() {\n    return {\n      total: 0,\n      items: [],\n    };\n  },\n\n  created() {\n    this.items = JSON.parse(this.$props.dataItems);\n  },\n\n  methods: {\n    addToCart(item) {\n      this.total += item.price;\n    },\n  },\n};\n&lt;\/script&gt;\n\n&lt;style lang=\"sass\"&gt;\n.cost-calculator\n  ul\n    display: flex\n    flex-direction: column\n    row-gap: 1rem\n    list-style-type: none\n    padding: 0\n\n  li\n    display: flex\n    column-gap: 1rem\n\n  .button\n    display: inline-flex\n    align-items: center\n    padding: 0.25rem 0.5rem\n    font-size: 0.75rem\n&lt;\/style&gt;\n<\/code><\/pre>\n\n\n\n<p>As you can see, everything is in one place.<\/p>\n\n\n\n<p>If you&#8217;re wondering what is <code>addToCart()<\/code> function, the answer is just a few lines below it.<\/p>\n\n\n\n<p>If you want to change the button styling, it&#8217;s just under the style tag. Additionally, we can use Sass by adding <code>lang=\"sass\"<\/code> in the style tag.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Hopefully, this article will encourage you to try using Vue in WordPress. It took me months to figure out this workflow because there aren&#8217;t many resources about it.<\/p>\n\n\n\n<p>Vue helps immensely in creating an interactive section of your website. But not everything has to be Vue. Simple stuff like a menu toggle is better done with jQuery or plain JavaScript.<\/p>\n\n\n\n<p>Also if you&#8217;re curious about my ESLint setup, <a rel=\"noreferrer noopener\" href=\"https:\/\/gist.github.com\/hrsetyono\/7dd16346767b81ba992ee8e84ad6aca7\" target=\"_blank\">get it here<\/a>.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote\"><p>If you have any question, feel free to comment below.<\/p><\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>Combining Vue and Webpack is very powerful. It allows you to create .vue file that contains HTML, JS, and CSS in one place.<\/p>\n","protected":false},"author":1,"featured_media":2071,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12],"tags":[49,50],"class_list":["post-2064","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-frontend","tag-vue","tag-webpack"],"blocksy_meta":"","_links":{"self":[{"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts\/2064","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=2064"}],"version-history":[{"count":10,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts\/2064\/revisions"}],"predecessor-version":[{"id":2095,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts\/2064\/revisions\/2095"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/media\/2071"}],"wp:attachment":[{"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/media?parent=2064"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/categories?post=2064"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/tags?post=2064"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}