{"id":1578,"date":"2019-04-19T00:00:00","date_gmt":"2019-04-18T22:00:00","guid":{"rendered":"https:\/\/demo.sidekick.be\/itenium\/vuejs-tutorial\/"},"modified":"2025-11-28T13:31:44","modified_gmt":"2025-11-28T12:31:44","slug":"vuejs-tutorial","status":"publish","type":"post","link":"https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/","title":{"rendered":"Vue.js Tutorial"},"content":{"rendered":"<h4 id=\"official-projects\">Official Projects<\/h4>\n<p><a href=\"https:\/\/github.com\/vuejs\/vue-router\" class=\"github\"><img decoding=\"async\" src=\"https:\/\/img.shields.io\/github\/stars\/vuejs\/vue-router.svg?style=social&amp;label=Star\" \/><\/a><a href=\"https:\/\/github.com\/vuejs\/vue-router\">vuejs\/vue-router<\/a>: The official router for Vue.js. <a href=\"https:\/\/github.com\/vuejs\/vuex\" class=\"github\"><img decoding=\"async\" src=\"https:\/\/img.shields.io\/github\/stars\/vuejs\/vuex.svg?style=social&amp;label=Star\" \/><\/a><a href=\"https:\/\/github.com\/vuejs\/vuex\">vuejs\/vuex<\/a>: Centralized State Management for Vue.js. <a href=\"https:\/\/github.com\/vuejs\/vue-cli\" class=\"github\"><img decoding=\"async\" src=\"https:\/\/img.shields.io\/github\/stars\/vuejs\/vue-cli.svg?style=social&amp;label=Star\" \/><\/a><a href=\"https:\/\/github.com\/vuejs\/vue-cli\">vuejs\/vue-cli<\/a>: Standard Tooling for Vue.js Development <a href=\"https:\/\/github.com\/vuejs\/vue-devtools\" class=\"github\"><img decoding=\"async\" src=\"https:\/\/img.shields.io\/github\/stars\/vuejs\/vue-devtools.svg?style=social&amp;label=Star\" \/><\/a><a href=\"https:\/\/github.com\/vuejs\/vue-devtools\">vuejs\/vue-devtools<\/a>: \u2699 Chrome and Firefox devtools extension for debugging Vue.js applications. <a href=\"https:\/\/github.com\/vuejs\/vetur\" class=\"github\"><img decoding=\"async\" src=\"https:\/\/img.shields.io\/github\/stars\/vuejs\/vetur.svg?style=social&amp;label=Star\" \/><\/a><a href=\"https:\/\/github.com\/vuejs\/vetur\">vuejs\/vetur<\/a>: Vue tooling for VS Code.<\/p>\n<h1 id=\"hello-vue\">Hello Vue<\/h1>\n<p class=\"title-url\"><a href=\"https:\/\/itenium-be.github.io\/blog-posts-new\/assets\/blog-assets\/vue-HelloVue.html\">HelloVue.html source<\/a><\/p>\n<h4 id=\"html\">Html<\/h4>\n<pre><code class=\"language-html\">&lt;script src=\"https:\/\/unpkg.com\/vue\"&gt;&lt;\/script&gt;\n\n&lt;div id=\"app\"&gt;\n  &lt;!-- Binding --&gt;\n  &lt;h1&gt;{{ product }}&lt;\/h1&gt;\n\n\n  &lt;!-- v-bind directive --&gt;\n  &lt;img v-bind:src=\"image\" :title=\"imageTitle\"&gt;\n  &lt;!-- :src, :alt, :title, :class, :style, :disabled --&gt;\n\n\n  &lt;!-- Conditionals --&gt;\n  &lt;div&gt;\n    &lt;!-- Falsy will not include in DOM --&gt;\n    &lt;p v-if=\"inventory &gt; 10\"&gt;In Stock&lt;\/p&gt;\n    &lt;p v-else-if=\"inventory\"&gt;Almost sold out&lt;\/p&gt;\n    &lt;p v-else&gt;Out of Stock&lt;\/p&gt;\n\n    &lt;!-- Render with display: none --&gt;\n    &lt;p v-show=\"inventory &lt; 0\"&gt;Wuuk?&lt;\/p&gt;\n  &lt;\/div&gt;\n\n\n  &lt;!-- Events --&gt;\n  &lt;!-- v-on directive shorthand is @ --&gt;\n  &lt;button\n    v-on:click=\"cart++; inventory--\"\n    :disabled=\"!inventory\"\n    :class=\"inventory ? '' : 'disabledButton'\"\n  &gt;\n    Add to Cart\n  &lt;\/button&gt;\n  &lt;div class=\"cart\"&gt;{{cart}}&lt;\/div&gt;\n\n\n  &lt;!-- Loops --&gt;\n  &lt;div v-for=\"variant in variants\"\n    :key=\"variant.id\"\n    :style=\"{backgroundColor: variant.color}\"\n    @mouseover=\"setImage(variant.color, $event)\"\n  &gt;&lt;\/div&gt;\n&lt;\/div&gt;\n<\/code><\/pre>\n<h4 id=\"javascript\">JavaScript<\/h4>\n<pre><code class=\"language-javascript\">const vm = new Vue({\n    el: '#app',\n    data: {\n      product: 'Socks',\n      image: '.\/assets\/socks-green.jpg',\n      imageTitle: 'Green Socks',\n      variants: [\n        {id: 1, color: 'green'},\n        {id: 2, color: 'blue'}\n      ],\n      inventory: 3,\n      cart: 0\n    },\n    methods: {\n      addToCart() {\n        this.cart++;\n        this.inventory--;\n      },\n      setImage(color, event) {\n        console.log('Vue instance with data and methods', this, event);\n        this.image = `.\/assets\/socks-${color}.jpg`;\n      }\n    }\n});\nconsole.log('Modify data directly from the console with `vm.product = \"Shoes\";`');\n<\/code><\/pre>\n<h1 id=\"templates\">Templates<\/h1>\n<p class=\"title-url\"><a href=\"https:\/\/vuejs.org\/guide\/essentials\/template-syntax.html#text-interpolation\">Interpolations guide<\/a><\/p>\n<h4 id=\"displaying-values\">Displaying values:<\/h4>\n<pre><code class=\"language-html\">&lt;h1&gt;{{ name.toUpperCase() }}&lt;\/h1&gt;\n&lt;p v-text=\"name\"&gt;&lt;\/p&gt;\n&lt;p v-html=\"rawHtml\"&gt;&lt;\/p&gt;\n<\/code><\/pre>\n<h4 id=\"computed-properties\">Computed Properties<\/h4>\n<p>Prefer <code>computed<\/code> properties over doing string formatting in the templates directly as they will only be re-evaluated when a relevant dependency changes.<\/p>\n<p class=\"title-url\"><a href=\"https:\/\/vuejs.org\/guide\/essentials\/computed.html#computed-properties\">Computed Properties guide<\/a><\/p>\n<p>Usage computed property: <code>&lt;h1&gt;{{ title }}&lt;\/h1&gt;<\/code>:<\/p>\n<pre><code>new Vue({\n    data: {name: 'Vuer'},\n    computed: {\n        title() {\n            return this.name.toUpperCase();\n        }\n    }\n});\n<\/code><\/pre>\n<h2 id=\"attributes\">Attributes<\/h2>\n<p class=\"title-url\"><a href=\"https:\/\/vuejs.org\/api\/built-in-directives.html#v-bind\">Attributes guide<\/a><\/p>\n<p>Binding to attribute with <code>v-bind<\/code> directive:<\/p>\n<pre><code class=\"language-html\">&lt;img v-bind:src=\"val\"&gt;\n&lt;img v-bind:[evaluated]=\"val\"&gt;\n&lt;img :src=\"val\"&gt;\n<\/code><\/pre>\n<p>Evaluated should return a bindable property or <code>null<\/code>.<\/p>\n<p>Shorthands: <code>:src<\/code>, <code>:alt<\/code>, <code>:title<\/code>, <code>:class<\/code>, <code>:style<\/code>, <code>:disabled<\/code><\/p>\n<h4 id=\"style\">:style<\/h4>\n<pre><code class=\"language-html\">:style=\"{fontSize: '13px'}\"\n:style=\"{'font-size': styles.size}\"\n:style=\"styleObject\"\n:style=\"[obj1, obj2]\"\n<\/code><\/pre>\n<ul>\n<li>Use camelCase or kebab-case (quoted)<\/li>\n<li>Vendor prefixes are added automatically<\/li>\n<li>Can combine html <code>style<\/code> and Vue <code>:style<\/code> attributes.<\/li>\n<\/ul>\n<h4 id=\"class\">:class<\/h4>\n<pre><code class=\"language-html\">:class=\"'btn' + btn.type\"\n:class=\"[cond ? 'active' : '']\"\n:class=\"{active: cond}\"\n<\/code><\/pre>\n<p class=\"title-url\"><a href=\"https:\/\/vuejs.org\/guide\/essentials\/conditional.html#conditional-rendering\">Conditional Rendering guide<\/a><\/p>\n<h2 id=\"conditions\">Conditions<\/h2>\n<pre><code class=\"language-html\">&lt;!-- Falsy values will not render --&gt;\n&lt;p v-if=\"inventory &gt; 10\"&gt;In Stock&lt;\/p&gt;\n&lt;p v-else-if=\"inventory\"&gt;Almost sold out&lt;\/p&gt;\n&lt;p v-else&gt;Out of Stock&lt;\/p&gt;\n\n&lt;!-- Render a block of multiple elements --&gt;\n&lt;!-- The template tag itself will not be rendered --&gt;\n&lt;template v-if=\"cond\"&gt;\n    &lt;h1&gt;Title&lt;\/h1&gt;\n    &lt;div&gt;...&lt;\/div&gt;\n&lt;\/template&gt;\n\n&lt;!-- Falsy values render with display: none --&gt;\n&lt;!-- Less DOM changes === better performance --&gt;\n&lt;!-- There is no v-else-show and it does not work with templates --&gt;\n&lt;p v-show=\"inventory\"&gt;In Stock&lt;\/p&gt;\n<\/code><\/pre>\n<p><code>v-show<\/code><\/p>\n<ul>\n<li>Use when you need to toggle something often<\/li>\n<li>Always rendered: Costlier initial rendering<\/li>\n<\/ul>\n<p><code>v-if<\/code><\/p>\n<ul>\n<li>Use when the condition is unlikely to change<\/li>\n<li>Rendered only when the condition is true<\/li>\n<\/ul>\n<p><code>v-once<\/code>: Will never be updated after initial render.<\/p>\n<p class=\"title-url\"><a href=\"https:\/\/vuejs.org\/guide\/essentials\/list.html#list-rendering\">Lists guide<\/a><\/p>\n<h2 id=\"looping\">Looping<\/h2>\n<pre><code class=\"language-html\">&lt;!-- Render 5 stars --&gt;\n&lt;i v-for=\"i in 5\" class=\"fa fa-star\" :key=\"i\"&gt;&lt;\/i&gt;\n\n&lt;!-- Loop through an Array --&gt;\n&lt;li v-for=\"item in ['Gold', 'Silver', 'Bronze']\" :key=\"item\"&gt;{{ item }}&lt;\/li&gt;\n&lt;li v-for=\"(item, index) in [1, 2, 3]\" :key=\"item\"&gt;{{ index + ' ' + item }}&lt;\/li&gt;\n\n&lt;!-- Loop through an Object --&gt;\n&lt;li v-for=\"(value, key) in {a: 1}\" :key=\"key\"&gt;{{ value }}&lt;\/li&gt;\n&lt;li v-for=\"(value, key, index) in {a: 1}\" :key=\"key\"&gt;{{ value }}&lt;\/li&gt;\n<\/code><\/pre>\n<p>Like <code>v-if<\/code> you can use a template to render a block of multiple elements.<\/p>\n<p><code>:key<\/code> attribute:<\/p>\n<ul>\n<li>Should be a primitive type<\/li>\n<li>Recommended unless you want to intentionally rely on the default behavior<\/li>\n<li>Required when using <code>v-for<\/code> with a custom component <!-- :key => React=key, Angular=trackBy --><\/li>\n<\/ul>\n<p class=\"title-url\"><a href=\"https:\/\/vuejs.org\/guide\/essentials\/event-handling.html#event-handling\">Events guide<\/a><\/p>\n<h2 id=\"events\">Events<\/h2>\n<pre><code class=\"language-html\">&lt;button v-on:click=\"nr++\"&gt;&lt;\/button&gt;\n&lt;form @submit=\"methodName\"&gt;&lt;\/form&gt;\n&lt;input @keyup.enter=\"fn(arg1, arg2, ...)\"&gt;\n<\/code><\/pre>\n<h4 id=\"event-modifiers\">Event Modifiers<\/h4>\n<ul>\n<li><code>@keyup.enter<\/code>: <code>.enter<\/code> is called a <a href=\"https:\/\/vuejs.org\/guide\/essentials\/event-handling.html#key-modifiers\">Key Modifier<\/a><\/li>\n<li>Modifiers are stackable: <code>.mod1.mod2<\/code><\/li>\n<li><code>@keyup.page-down=\"onPageDown\"<\/code><\/li>\n<li><code>.prevent<\/code> == <code>event.preventDefault()<\/code><\/li>\n<li><code>.stop<\/code> == <code>event.stopPropagation()<\/code>: Shorthand <code>~<\/code>.<\/li>\n<li><code>.once<\/code>: Unregister after first trigger<\/li>\n<li><code>.self<\/code>: Trigger only when the element is the <code>.target<\/code><\/li>\n<li><code>@click.ctrl.exact<\/code>: Only trigger when ctrl is pressed without any other keys<\/li>\n<li><code>@click.capture<\/code>: An event on an inner element is handled here before the <code>.target<\/code>. Shorthand <code>!<\/code>.<\/li>\n<li><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/EventTarget\/addEventListener#Parameters\"><code>.passive<\/code><\/a>: Corresponds to <code>addEventListener<\/code>\u2019s passive option. Shorthand <code>&amp;<\/code>.<\/li>\n<\/ul>\n<p><!-- TODO: not covered? .native modifier etc --><!-- .native (native events in custom components) --><!-- v-on:update:propName=\"doStuff\" ==> .sync == two way binding? --><\/p>\n<h1 id=\"components\">Components<\/h1>\n<p class=\"title-url\"><a href=\"https:\/\/vuejs.org\/guide\/essentials\/component-basics.html#components-basics\">Components guide<\/a><\/p>\n<h4 id=\"usage\">Usage<\/h4>\n<pre><code class=\"language-javascript\">\/\/ Html\n\/\/ &lt;product :premium=\"premium\" @add-to-cart=\"updateCart\" class=\"main-prod\"&gt;&lt;\/product&gt;\n\n\/\/ JavaScript\nnew Vue({\n  data: {\n    premium: true,\n    cart: [],\n  },\n  methods: {\n    updateCart(product) {\n      this.cart.push(product);\n    }\n  }\n});\n<\/code><\/pre>\n<h4 id=\"declare\">Declare<\/h4>\n<pre><code class=\"language-javascript\">const comp = Vue.component('product', {\n  \/\/ Props with validation\n  props: {\n    premium: {type: Boolean, required: true, default: false},\n    \/\/ Types: String, Number, Array, Object, Function, Promise, any ctor\n    \/\/ Null and undefined always pass validation\n    \/\/ oneOf: [String, Number],\n    \/\/ special: {\n    \/\/   default() { return 'calculatedValue'; },\n    \/\/   validator(value) { return true; }\n    \/\/ }\n  },\n  \/\/ Props without validation\n  \/\/ props: ['premium'],\n  template: `\n    &lt;!-- Must have single root element --&gt;\n    &lt;div class=\"product\"&gt;\n      &lt;h1&gt;{{ product }}&lt;\/h1&gt;\n      &lt;p v-if=\"premium\"&gt;&lt;b&gt;FREE Shipping&lt;\/b&gt;&lt;\/p&gt;\n\n      &lt;button @click=\"addToCart\"&gt;\n        Add to Cart\n      &lt;\/button&gt;\n    &lt;\/div&gt;\n  `,\n  data() {\n    \/\/ data is a function inside a Vue.component()\n    \/\/ Do return a new object reference each time.\n    return {\n      product: 'Socks',\n      inventory: 3,\n    };\n  },\n  methods: {\n    addToCart() {\n      this.inventory--;\n      this.$emit('add-to-cart', {product: this.product});\n    }\n  },\n\n  \/\/ Local Subcomponent Registration\n  \/\/ const LocalComponent = {};\n  \/\/ components: {\n  \/\/   'local-component': LocalComponent,\n  \/\/ }\n\n  \/\/ Watches\n  watch: {\n    iventory(newInventory, oldInventory) {\n      \/\/ TODO: Call backend to check if product is still available\n    }\n  },\n\n\n  \/\/ LifeCycle Methods\n  created() {\n    console.log('Http requests probably here');\n  },\n  mounted() {},\n  updated() {},\n  destroyed() {},\n  beforeDestroy() {},\n});\n<\/code><\/pre>\n<h2 id=\"slots\">Slots<\/h2>\n<p>Slots, like React <code>props.children<\/code> or Angular <code>ng-content<\/code>.<\/p>\n<p class=\"title-url\"><a href=\"https:\/\/vuejs.org\/guide\/components\/slots.html#slots\">Slots guide<\/a><\/p>\n<h4 id=\"usage-1\">Usage<\/h4>\n<pre><code class=\"language-html\">&lt;alert-box&gt;\n  &lt;!-- Named Slot --&gt;\n  &lt;template v-slot:title&gt;\n    &lt;!-- Shorthand #title --&gt;\n    Oh noes!\n  &lt;\/template&gt;\n\n  &lt;!-- Default Slot --&gt;\n  Something bad happened.\n  &lt;!-- Could put default slot into: v-slot:default --&gt;\n&lt;\/alert-box&gt;\n<\/code><\/pre>\n<h4 id=\"declare-1\">Declare<\/h4>\n<pre><code class=\"language-javascript\">Vue.component('alert-box', {\n  template: `\n    &lt;div&gt;\n      &lt;slot name=\"title\"&gt;Error!&lt;\/slot&gt;\n      &lt;slot&gt;Default value&lt;\/slot&gt;\n    &lt;\/div&gt;\n  `\n});\n<\/code><\/pre>\n<h1 id=\"forms\">Forms<\/h1>\n<p class=\"title-url\"><a href=\"https:\/\/vuejs.org\/guide\/essentials\/forms.html#form-input-bindings\">Forms guide<\/a><\/p>\n<h4 id=\"html-1\">Html<\/h4>\n<pre><code class=\"language-html\">&lt;form @submit.prevent=\"onSubmit\"&gt;\n  &lt;input v-model=\"name\" @keydown.ctrl.v.prevent=\"blockPaste\"&gt;\n\n  &lt;div v-for=\"error in errors\" :key=\"error\"&gt;\n    * {{ error }}\n  &lt;\/div&gt;\n\n  &lt;select v-model.number=\"rating\"&gt;\n    &lt;!-- Other modifiers: --&gt;\n    &lt;!-- .lazy =&gt; Sync on change event --&gt;\n    &lt;!-- .trim =&gt; Strip whitespace --&gt;\n    &lt;option disabled value=\"\"&gt;select&lt;\/option&gt;\n    &lt;option v-for=\"i in 5\" :key=\"i\"&gt;{{ i }}&lt;\/option&gt;\n  &lt;\/select&gt;\n\n  &lt;br&gt;\n  &lt;input\n    type=\"checkbox\"\n    v-model=\"acceptTerms\"\n    true-value=\"yes\"\n    false-value=\"no\"\n  &gt;\n  Accept terms\n\n  &lt;button&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;\n<\/code><\/pre>\n<h4 id=\"javascript-1\">JavaScript<\/h4>\n<pre><code class=\"language-javascript\">new Vue({\n  data: {\n    name: null,\n    rating: '',\n    acceptTerms: false,\n    errors: []\n  },\n  methods: {\n    onSubmit() {\n      this.errors = [];\n      if (!this.rating) {\n        this.errors.push('Please select a rating');\n        return;\n      }\n\n      const review = {name: this.name, rating: this.rating};\n      console.log('Submitting', review);\n    },\n    blockPaste() {\n      console.log('Control + V is not allowed!');\n    }\n  },\n});\n<\/code><\/pre>\n<h4 id=\"validation-not-included\">Validation not included<\/h4>\n<p><a href=\"https:\/\/github.com\/vuelidate\/vuelidate\" class=\"github\"><img decoding=\"async\" src=\"https:\/\/img.shields.io\/github\/stars\/vuelidate\/vuelidate.svg?style=social&amp;label=Star\" \/><\/a><a href=\"https:\/\/github.com\/vuelidate\/vuelidate\">vuelidate\/vuelidate<\/a>: Simple, lightweight model-based validation for Vue.js <a href=\"https:\/\/github.com\/baianat\/vee-validate\" class=\"github\"><img decoding=\"async\" src=\"https:\/\/img.shields.io\/github\/stars\/baianat\/vee-validate.svg?style=social&amp;label=Star\" \/><\/a><a href=\"https:\/\/github.com\/baianat\/vee-validate\">baianat\/vee-validate<\/a>: Template Based Validation Framework for Vue.js<\/p>\n<h4 id=\"caveat\">Caveat<\/h4>\n<p>Reuse of <code>&lt;input&gt;<\/code> elements could lead to strange behavior.<\/p>\n<pre><code class=\"language-html\">&lt;template v-if=\"loginType === 'username'\"&gt;\n  &lt;!-- Using `key` so the inputs are not reused when loginType changes --&gt;\n  &lt;input placeholder=\"Enter your username\" key=\"username-input\"&gt;\n&lt;\/template&gt;\n\n&lt;template v-else&gt;\n  &lt;input placeholder=\"Enter your email address\" key=\"email-input\"&gt;\n&lt;\/template&gt;\n<\/code><\/pre>\n<p class=\"title-url\"><a href=\"https:\/\/vuejs.org\/guide\/components\/v-model.html#component-v-model\">Using v-model on Components<\/a><\/p>\n<h2 id=\"custom-model-binding\">Custom Model Binding<\/h2>\n<pre><code class=\"language-javascript\">Vue.component('custom-input', {\n    props: ['value'],\n    template: `&lt;input :value=\"value\" @input=\"$emit('input', $event.target.value)\"&gt;`\n})\n\n\/\/ Html\n\/\/ &lt;custom-input v-model=\"searchText\"&gt;&lt;\/custom-input&gt;\n\nnew Vue({\n    el: '#app',\n    data: {\n      searchText: ''\n    },\n});\n<\/code><\/pre>\n<h1 id=\"reactivity\">Reactivity<\/h1>\n<h4 id=\"array\">Array<\/h4>\n<p>Vue.js has wrapped array functions so that they are reactive:<br \/><code>push<\/code>, <code>pop<\/code>, <code>shift<\/code>, <code>unshift<\/code>, <code>splice<\/code>, <code>sort<\/code>, <code>reverse<\/code><\/p>\n<p>Assigning a new array will reuse DOM elements if possible on the re-render.<\/p>\n<p>It <strong>will not detect <code>this.items[0] = 'newVal'<\/code><\/strong>. If you must, use:<\/p>\n<pre><code class=\"language-javascript\">Vue.set(vm.items, indexOfItem, newValue);\nvm.items.splice(indexOfItem, 1, newValue);\nvm.$set(vm.items, indexOfItem, newValue);\n<\/code><\/pre>\n<h4 id=\"objects\">Objects<\/h4>\n<p>Assigning a new property to an object is not reactive. Assign it a default value on initialization.<\/p>\n<p>If you must, use:<\/p>\n<pre><code class=\"language-javascript\">Vue.set(vm.userProfile, 'age', 27);\nvm.$set(vm.userProfile, 'age', 27);\n<\/code><\/pre>\n<p><\/p>\n<h1 id=\"closing-thoughts\">Closing Thoughts<\/h1>\n<p>I was really surprised by Vue\u2019s simplicity and terse syntax due its many shorthands. Tooling in Visual Studio Code and Chrome is really good. The CLI was pretty amazing at setting up a full blown starter project.<\/p>\n<p>So, I learned me some Vue.js for a technical session and am already set on using it for a few small projects.<\/p>\n<h1>Extras<\/h1>\n<p><b>Stuff that came into being during the making of this post<\/b><\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/itenium-be\/Vue.js-Tutorial\">Project used for itenium Vue.js technical session<\/a><\/li>\n<\/ul>\n<p><b>Other interesting reads<\/b><\/p>\n<ul>\n<li><a href=\"https:\/\/chrome.google.com\/webstore\/detail\/vuejs-devtools\/nhdogjmejiglipccpnnnanhbledajbpd?hl=en\">Vue.js devtools Chrome extension<\/a><\/li>\n<li><a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=octref.vetur\">Vetur Visual Studio Code extension<\/a><\/li>\n<\/ul>\n<h1> Package Versions <i class=\"fa fa-info-circle\" style=\"float: right; color: cornflowerblue; font-size: 28px\" title=\"Package versions used for this post and in the accompanying code on Github\"><\/i><\/h1>\n<div><a href=\"https:\/\/github.com\/vuejs\/vue\" class=\"github\"><img decoding=\"async\" src=\"https:\/\/img.shields.io\/github\/stars\/vuejs\/vue.svg?style=social&#038;label=Star\" \/><\/a><a href=\"https:\/\/github.com\/vuejs\/vue\">vuejs\/vue<\/a>: v2.6.6 <\/div>\n<p> vue-router: v3.0.1 <\/p>\n<p> vuex: v3.0.1 <\/p>\n<p> vue-cli: v3.5.3 <\/p>\n","protected":false},"excerpt":{"rendered":"<p><a href=\"https:\/\/github.com\/vuejs\/vue\" class=\"github\"><img decoding=\"async\" src=\"https:\/\/img.shields.io\/github\/stars\/vuejs\/vue.svg?style=social&amp;label=Star\" \/><\/a><a href=\"https:\/\/github.com\/vuejs\/vue\">vuejs\/vue<\/a>: Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.<\/p>\n<h4 id=\"why-vue\">Why Vue<\/h4>\n<ul>\n<li><code>100k+ \u2b50<\/code>: So many people can\u2019t be wrong<\/li>\n<li>Declarative rendering with a cool, terse syntax<\/li>\n<li>Low learning curve<\/li>\n<li>Reactive and composable view components<\/li>\n<li>Optional official <code>vue-router<\/code> and <code>vuex<\/code> (statemanagement)<\/li>\n<li><code>@vue\/cli<\/code>: Scaffold project with optional support for TypeScript, PWA, CSS Pre-processors, Linters, Tests, \u2026<\/li>\n<li>Automatic dependency tracking<\/li>\n<li>Virtual DOM<\/li>\n<li>Supports IE9+<\/li>\n<\/ul>\n","protected":false},"author":1,"featured_media":2074,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[3],"tags":[6],"class_list":["post-1578","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-javascript","tag-tutorial"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Vue.js Tutorial - Itenium<\/title>\n<meta name=\"robots\" content=\"noindex, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Vue.js Tutorial - Itenium\" \/>\n<meta property=\"og:description\" content=\"vuejs\/vue: Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.Why Vue100k+ \u2b50: So many people can\u2019t be wrongDeclarative rendering with a cool, terse syntaxLow learning curveReactive and composable view componentsOptional official vue-router and vuex (statemanagement)@vue\/cli: Scaffold project with optional support for TypeScript, PWA, CSS Pre-processors, Linters, Tests, \u2026Automatic dependency trackingVirtual DOMSupports IE9+\" \/>\n<meta property=\"og:url\" content=\"https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/\" \/>\n<meta property=\"og:site_name\" content=\"Itenium\" \/>\n<meta property=\"article:published_time\" content=\"2019-04-18T22:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-11-28T12:31:44+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/demo.sidekick.be\/itenium\/wp-content\/uploads\/2025\/11\/vue-tutorial.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"360\" \/>\n\t<meta property=\"og:image:height\" content=\"300\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Usdevelop\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Usdevelop\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/\",\"url\":\"https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/\",\"name\":\"Vue.js Tutorial - Itenium\",\"isPartOf\":{\"@id\":\"https:\/\/demo.sidekick.be\/itenium\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/demo.sidekick.be\/itenium\/wp-content\/uploads\/2025\/11\/vue-tutorial.jpg\",\"datePublished\":\"2019-04-18T22:00:00+00:00\",\"dateModified\":\"2025-11-28T12:31:44+00:00\",\"author\":{\"@id\":\"https:\/\/demo.sidekick.be\/itenium\/#\/schema\/person\/6ab401ce01e858184ee4a8667fe0f022\"},\"breadcrumb\":{\"@id\":\"https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/#primaryimage\",\"url\":\"https:\/\/demo.sidekick.be\/itenium\/wp-content\/uploads\/2025\/11\/vue-tutorial.jpg\",\"contentUrl\":\"https:\/\/demo.sidekick.be\/itenium\/wp-content\/uploads\/2025\/11\/vue-tutorial.jpg\",\"width\":360,\"height\":300},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/demo.sidekick.be\/itenium\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Vue.js Tutorial\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/demo.sidekick.be\/itenium\/#website\",\"url\":\"https:\/\/demo.sidekick.be\/itenium\/\",\"name\":\"Itenium\",\"description\":\"We build software. We build careers.\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/demo.sidekick.be\/itenium\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/demo.sidekick.be\/itenium\/#\/schema\/person\/6ab401ce01e858184ee4a8667fe0f022\",\"name\":\"Usdevelop\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/demo.sidekick.be\/itenium\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/cc8cf9b9e9d5bbe7e8d247907cc9ac8457bbfa2854b52eaddf6529adfe88ceab?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/cc8cf9b9e9d5bbe7e8d247907cc9ac8457bbfa2854b52eaddf6529adfe88ceab?s=96&d=mm&r=g\",\"caption\":\"Usdevelop\"},\"sameAs\":[\"https:\/\/demo.sidekick.be\/itenium\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Vue.js Tutorial - Itenium","robots":{"index":"noindex","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"og_locale":"en_US","og_type":"article","og_title":"Vue.js Tutorial - Itenium","og_description":"vuejs\/vue: Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.Why Vue100k+ \u2b50: So many people can\u2019t be wrongDeclarative rendering with a cool, terse syntaxLow learning curveReactive and composable view componentsOptional official vue-router and vuex (statemanagement)@vue\/cli: Scaffold project with optional support for TypeScript, PWA, CSS Pre-processors, Linters, Tests, \u2026Automatic dependency trackingVirtual DOMSupports IE9+","og_url":"https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/","og_site_name":"Itenium","article_published_time":"2019-04-18T22:00:00+00:00","article_modified_time":"2025-11-28T12:31:44+00:00","og_image":[{"width":360,"height":300,"url":"https:\/\/demo.sidekick.be\/itenium\/wp-content\/uploads\/2025\/11\/vue-tutorial.jpg","type":"image\/jpeg"}],"author":"Usdevelop","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Usdevelop","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/","url":"https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/","name":"Vue.js Tutorial - Itenium","isPartOf":{"@id":"https:\/\/demo.sidekick.be\/itenium\/#website"},"primaryImageOfPage":{"@id":"https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/#primaryimage"},"image":{"@id":"https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/#primaryimage"},"thumbnailUrl":"https:\/\/demo.sidekick.be\/itenium\/wp-content\/uploads\/2025\/11\/vue-tutorial.jpg","datePublished":"2019-04-18T22:00:00+00:00","dateModified":"2025-11-28T12:31:44+00:00","author":{"@id":"https:\/\/demo.sidekick.be\/itenium\/#\/schema\/person\/6ab401ce01e858184ee4a8667fe0f022"},"breadcrumb":{"@id":"https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/#primaryimage","url":"https:\/\/demo.sidekick.be\/itenium\/wp-content\/uploads\/2025\/11\/vue-tutorial.jpg","contentUrl":"https:\/\/demo.sidekick.be\/itenium\/wp-content\/uploads\/2025\/11\/vue-tutorial.jpg","width":360,"height":300},{"@type":"BreadcrumbList","@id":"https:\/\/demo.sidekick.be\/itenium\/blog\/javascript\/vuejs-tutorial\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/demo.sidekick.be\/itenium\/"},{"@type":"ListItem","position":2,"name":"Vue.js Tutorial"}]},{"@type":"WebSite","@id":"https:\/\/demo.sidekick.be\/itenium\/#website","url":"https:\/\/demo.sidekick.be\/itenium\/","name":"Itenium","description":"We build software. We build careers.","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/demo.sidekick.be\/itenium\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/demo.sidekick.be\/itenium\/#\/schema\/person\/6ab401ce01e858184ee4a8667fe0f022","name":"Usdevelop","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/demo.sidekick.be\/itenium\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/cc8cf9b9e9d5bbe7e8d247907cc9ac8457bbfa2854b52eaddf6529adfe88ceab?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/cc8cf9b9e9d5bbe7e8d247907cc9ac8457bbfa2854b52eaddf6529adfe88ceab?s=96&d=mm&r=g","caption":"Usdevelop"},"sameAs":["https:\/\/demo.sidekick.be\/itenium"]}]}},"_links":{"self":[{"href":"https:\/\/demo.sidekick.be\/itenium\/wp-json\/wp\/v2\/posts\/1578","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/demo.sidekick.be\/itenium\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/demo.sidekick.be\/itenium\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/demo.sidekick.be\/itenium\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/demo.sidekick.be\/itenium\/wp-json\/wp\/v2\/comments?post=1578"}],"version-history":[{"count":3,"href":"https:\/\/demo.sidekick.be\/itenium\/wp-json\/wp\/v2\/posts\/1578\/revisions"}],"predecessor-version":[{"id":2292,"href":"https:\/\/demo.sidekick.be\/itenium\/wp-json\/wp\/v2\/posts\/1578\/revisions\/2292"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/demo.sidekick.be\/itenium\/wp-json\/wp\/v2\/media\/2074"}],"wp:attachment":[{"href":"https:\/\/demo.sidekick.be\/itenium\/wp-json\/wp\/v2\/media?parent=1578"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/demo.sidekick.be\/itenium\/wp-json\/wp\/v2\/categories?post=1578"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/demo.sidekick.be\/itenium\/wp-json\/wp\/v2\/tags?post=1578"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}