{"id":18681716,"url":"https://github.com/stowball/quench-vue","last_synced_at":"2025-04-12T04:09:38.861Z","repository":{"id":50158082,"uuid":"95294017","full_name":"stowball/quench-vue","owner":"stowball","description":"Simple, client-side hydration of pre-rendered Vue.js apps","archived":false,"fork":false,"pushed_at":"2021-06-04T05:37:37.000Z","size":424,"stargazers_count":76,"open_issues_count":3,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-12T04:09:33.826Z","etag":null,"topics":["javascript","spa","ssr","vuejs"],"latest_commit_sha":null,"homepage":"https://codesandbox.io/s/quench-vue-demo-foe55","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stowball.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-06-24T11:39:05.000Z","updated_at":"2025-01-19T13:56:25.000Z","dependencies_parsed_at":"2022-09-24T03:12:50.193Z","dependency_job_id":null,"html_url":"https://github.com/stowball/quench-vue","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stowball%2Fquench-vue","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stowball%2Fquench-vue/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stowball%2Fquench-vue/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stowball%2Fquench-vue/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stowball","download_url":"https://codeload.github.com/stowball/quench-vue/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248514222,"owners_count":21116903,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","spa","ssr","vuejs"],"created_at":"2024-11-07T10:09:05.414Z","updated_at":"2025-04-12T04:09:38.835Z","avatar_url":"https://github.com/stowball.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://i.imgur.com/CbwlGnn.png\" height=\"200\" alt=\"Quench Vue logo\" /\u003e\n  \u003cdiv align=\"center\"\u003e\n    \u003ca href=\"https://travis-ci.org/stowball/quench-vue\"\u003e\n      \u003cimg src=\"https://img.shields.io/travis/stowball/quench-vue/master.png?style=flat-square\" alt=\"Travis build status\" /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://www.npmjs.org/package/quench-vue\"\u003e\n      \u003cimg src=\"https://img.shields.io/npm/v/quench-vue.png?style=flat-square\" alt=\"npm package\" /\u003e\n    \u003c/a\u003e\n  \u003c/div\u003e\n\u003c/p\u003e\n\n# Quench Vue\n\n**Simple, tiny, client-side hydration of pre-rendered [Vue.js](https://vuejs.org) apps**\n\nQuench Vue allows server-rendered/static markup to be used as a Vue app's `data`, `template` and local components' `template`s. It's great for when you can't/don't want to use \"real\" [server-side rendering](https://vuejs.org/v2/guide/ssr.html).\n\nAll of Vue's existing features will work as normal when the app is initialised in the browser.\n\n## Table of Contents\n\n- [Demo](#demo)\n- [Installation](#installation)\n  - [npm](#npm)\n  - [Direct `\u003cscript\u003e` include](#direct-script-include)\n- [Usage](#usage)\n- [Defining the app `data` and `template`](#defining-the-app-data-and-template)\n  - [Method 1: Defining the `data` with `[q-data]`](#method-1-defining-the-data-with-q-data)\n    - [Rendering the data with `[v-text]`](#rendering-the-data-with-v-text)\n  - [Method 2: Defining the `data` with inline `[q-binding]` bindings](#method-2-defining-the-data-with-inline-q-binding-bindings)\n    - [Simple bindings](#simple-bindings)\n    - [Array and Object bindings](#array-and-object-bindings)\n      - [Array](#array)\n      - [Object](#object)\n      - [Array of Objects](#array-of-objects)\n    - [Non-element bindings](#non-element-bindings)\n  - [Referencing global variables as `data` properties](#referencing-global-variables-as-data-properties)\n  - [Excluding elements from the app template compiler](#excluding-elements-from-the-app-template-compiler)\n  - [Instantiating the app](#instantiating-the-app)\n    - [With a module bundler, such as webpack](#with-a-module-bundler-such-as-webpack)\n    - [For direct `\u003cscript\u003e` include](#for-direct-script-include)\n- [Defining local component `template`s](#defining-local-component-templates)\n  - [Specifying a component with `[q-component]`](#specifying-a-component-with-q-component)\n  - [Specifying a component's `template`](#specifying-a-components-template)\n  - [Handling a `template`'s logic](#handling-a-templates-logic)\n    - [Method 1: Add the logic within the markup](#method-1-add-the-logic-within-the-markup)\n    - [Method 2: Add the logic to the component's JavaScript using `partials`](#method-2-add-the-logic-to-the-components-javascript-using-partials)\n    - [Define a completely different component](#define-a-completely-different-component)\n  - [Excluding elements from the component template compiler](#excluding-elements-from-the-component-template-compiler)\n  - [Rendering future components dynamically](#rendering-future-components-dynamically)\n  - [Updating our app initialization to support pre-rendered components](#updating-our-app-initialization-to-support-pre-rendered-components)\n    - [With a module bundler, such as webpack](#with-a-module-bundler-such-as-webpack-1)\n    - [For direct `\u003cscript\u003e` include](#for-direct-script-include-1)\n- [Hiding elements in the pre-rendered HTML](#hiding-elements-in-the-pre-rendered-html)\n- [Embedding additional app templates](#embedding-additional-app-templates)\n- [Benefits](#benefits)\n\n## Demo\n\nA complete demo is available here: https://codesandbox.io/s/quench-vue-demo-foe55\n\n## Installation\n\n### npm\n\n```sh\nnpm install quench-vue --save\n```\n\n### Direct `\u003cscript\u003e` include\n\n```html\n\u003cscript src=\"https://unpkg.com/quench-vue/umd/quench-vue.min.js\"\u003e\u003c/script\u003e\n```\n\n*Note: You will need to use [the full build of Vue.js](https://vuejs.org/v2/guide/installation.html#Explanation-of-Different-Builds), which includes the compiler.*\n\n## Usage\n\n1. [Defining the app `data` and `template`](#defining-the-app-data-and-template).\n2. [Defining local component `template`s](#defining-local-component-templates).\n\n## Defining the app `data` and `template`\n\nThere are 2 ways of defining and using `data` for the app:\n\n1. With a stringified JSON object in the app container's `q-data` attribute; and/or\n2. With an inline `q-binding` attribute on an element, when `q-convert-bindings` is added to the app container.\n\nBoth techniques can be used together or on their own, but the `q-data` is preferred as it's faster, simpler and more versatile.\n\nLet's look at some examples:\n\n### Method 1: Defining the `data` with `[q-data]`\n\nThis method allows you to easily specify the `data` for the app, including arrays and objects.\n\n```html\n\u003cdiv id=\"app\" q-data='{\n  \"title\": \"Hello, world!\",\n  \"year\": 2018,\n  \"tags\": [\n    \"js\",\n    \"library\"\n  ],\n  \"author\": {\n    \"firstName\": \"Matt\",\n    \"lastName\": \"Stow\"\n  },\n  \"skills\": [\n    {\n      \"name\": \"JS\",\n      \"level\": 4\n    },\n    {\n      \"name\": \"CSS\",\n      \"level\": 5\n    }\n  ]\n}'\u003e\n…\n\u003c/div\u003e\n```\n\n#### Rendering the data with `[v-text]`\n\nWe obviously duplicate the \"data\" in the markup, and inform Vue which elements are bound to which `data` properties using a [`v-text`](https://vuejs.org/v2/api/#v-text) attribute whose value points to a property name, such as:\n\n```html\n\u003ch1 v-text=\"title\"\u003eHello, World!\u003c/h1\u003e\n\u003cp v-text=\"year\"\u003e2018\u003c/p\u003e\n\n\u003cul\u003e\n  \u003cli v-for=\"tag in tags\"\u003e\n    \u003cspan v-text=\"tag\"\u003ejs\u003c/span\u003e\n  \u003c/li\u003e\n  \u003c!-- \u003cq\u003e --\u003e\n  \u003cli\u003e\n    \u003cspan\u003elibrary\u003c/span\u003e\n  \u003c/li\u003e\n  \u003c!-- \u003c/q\u003e --\u003e\n\u003c/ul\u003e\n\n\u003cul\u003e\n  \u003cli v-for=\"key in author\"\u003e\n    \u003cspan v-text=\"key\"\u003eMatt\u003c/span\u003e\n  \u003c/li\u003e\n  \u003c!-- \u003cq\u003e --\u003e\n  \u003cli\u003e\n    \u003cspan\u003eStow\u003c/span\u003e\n  \u003c/li\u003e\n  \u003c!-- \u003c/q\u003e --\u003e\n\u003c/ul\u003e\n\n\u003cul\u003e\n  \u003cli v-for=\"skill in skills\"\u003e\n    \u003cspan v-text=\"skill.name\"\u003eJS\u003c/span\u003e\n    \u003cspan v-text=\"skill.level\"\u003e4\u003c/span\u003e\n  \u003c/li\u003e\n  \u003c!-- \u003cq\u003e --\u003e\n  \u003cli\u003e\n    \u003cspan\u003eCSS\u003c/span\u003e\n    \u003cspan\u003e5\u003c/span\u003e\n  \u003c/li\u003e\n  \u003c!-- \u003c/q\u003e --\u003e\n\u003c/ul\u003e\n```\n\nFor iterating over lists, we also need to use another syntax, `\u003c!-- \u003cq\u003e --\u003e … \u003c!-- \u003c/q\u003e --\u003e`, which [we'll describe later](#hiding-elements-from-the-compiler).\n\n*Note:*\n* *You can also use [`v-html`](https://vuejs.org/v2/api/#v-html) to render HTML, but ensure that it's sanitized and trusted.*\n* *You only need to output the `v-for` and the `v-text`/`q-binding` attributes on the first iteration of the loop.*\n\n### Method 2: Defining the `data` with inline `[q-binding]` bindings\n\nWhile we don't recommend the following approach for anything but the simplest of apps, when `q-convert-bindings` is set on the app's container, we can also use the `q-binding` attribute to create a `data` variable that is equal to the value of the element's `.textContent`.\n\n*Note:*\n* *Bindings specified in the global `q-data` object take precedence over inline bindings.*\n* *Do not nest elements inside a `q-binding` element, or you'll have unexpected results.*\n\nThe following examples all perfectly re-create the global `q-data` object from before.\n\n#### Simple bindings\n\n```html\n\u003cdiv id=\"app\" q-convert-bindings\u003e\n  \u003ch1 q-binding=\"title\"\u003eHello, World!\u003c/h1\u003e\n  \u003cp q-binding=\"year\"\u003e2018\u003c/p\u003e\n\u003c/div\u003e\n```\n\n#### Array and Object bindings\n\nVue supports iterating over arrays and objects via [the `v-for` directive](https://vuejs.org/v2/guide/list.html) with the syntax `item in items`, where `items` is the source data list and `item` is an **alias** for the array element being iterated on.\n\nTo inline bind with Quench, we need to use another special syntax `itemsSource as item`.\n\n##### Array\n\nTo replicate the `tags` array from above, we would:\n\n```html\n\u003cdiv id=\"app\" q-convert-bindings\u003e\n  \u003cul\u003e\n    \u003cli v-for=\"tag in tags\"\u003e\n      \u003cspan q-binding=\"tags[0] as tag\"\u003ejs\u003c/span\u003e\n    \u003c/li\u003e\n    \u003c!-- \u003cq\u003e --\u003e\n    \u003cli\u003e\n      \u003cspan q-binding=\"tags[1] as tag\"\u003elibrary\u003c/span\u003e\n    \u003c/li\u003e\n    \u003c!-- \u003c/q\u003e --\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n```\n\nwhere `itemsSource` is the name of the array (`tags`) plus the index in the array `[0]`/`[1]` which we wish to populate, and `tag` is the `item` alias in the `v-for`.\n\n##### Object\n\nTo replicate the `author` object from above, we would:\n\n```html\n\u003cdiv id=\"app\" q-convert-bindings\u003e\n  \u003cul\u003e\n    \u003cli v-for=\"key in author\"\u003e\n      \u003cspan q-binding=\"author.firstName as key\"\u003eMatt\u003c/span\u003e\n    \u003c/li\u003e\n    \u003c!-- \u003cq\u003e --\u003e\n    \u003cli\u003e\n      \u003cspan q-binding=\"author.lastName as key\"\u003eStow\u003c/span\u003e\n    \u003c/li\u003e\n    \u003c!-- \u003c/q\u003e --\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n```\n\nwhere `itemsSource` is the name of the object (`author`) plus the relevant object key `.firstName`/`.lastName` which we wish to populate, and `key` is the `item` alias in the `v-for`.\n\n##### Array of Objects\n\nBoth of the above techniques can be combined, so to replicate the `skills` array from above, we would:\n\n```html\n\u003cdiv id=\"app\" q-convert-bindings\u003e\n  \u003cul\u003e\n    \u003cli v-for=\"skill in skills\"\u003e\n      \u003cspan q-binding=\"skills[0].name as skill.name\"\u003eJS\u003c/span\u003e\n      \u003cspan q-binding=\"skills[0].level as skill.level\"\u003e4\u003c/span\u003e\n    \u003c/li\u003e\n    \u003c!-- \u003cq\u003e --\u003e\n    \u003cli\u003e\n      \u003cspan q-binding=\"skills[1].name as skill.name\"\u003eCSS\u003c/span\u003e\n      \u003cspan q-binding=\"skills[1].level as skill.level\"\u003e5\u003c/span\u003e\n    \u003c/li\u003e\n    \u003c!-- \u003c/q\u003e --\u003e\n  \u003c/ul\u003e\n\u003c/div\u003e\n```\n\nwhere `itemsSource` is the name of the array and index (`skills[0]`) plus the relevant object key `.name`/`.level` which we wish to populate, and `skill.name`/`skill.level` is the `item` alias in the `v-for` plus the object key.\n\nHopefully you'll agree that using inline bindings to set the `data` is more complicated than using the `q-data` method, but it can still have its uses.\n\n*Note: When using inline bindings, arrays and objects are limited to a depth of 1 level.*\n\n#### Non-element bindings\n\nSince v0.7.0, you can also create `data` from bindings using specially formatted comments, thus not requiring to have actual elements to attach to. This can be useful if you need to create extra `data` that is \"invisible\" to the user, or if you need to create more complex values, such as nested arrays and objects.\n\nUsing the syntax `\u003c!-- q-binding:[dataPropertyName] = [value] --\u003e` we can easily recreate the `data` from the previous examples:\n\n```html\n\u003cdiv id=\"app\" q-convert-bindings\u003e\n  \u003c!-- q-binding:title = \"Hello, World!\" --\u003e\n  \u003c!-- q-binding:year = 2018 --\u003e\n  \u003c!-- q-binding:tags[0] = \"js\" --\u003e\n  \u003c!-- q-binding:tags[1] = \"library\" --\u003e\n  \u003c!-- q-binding:author.firstName = \"Matt\" --\u003e\n  \u003c!-- q-binding:author.lastName = \"Stow\" --\u003e\n  \u003c!-- q-binding:skills[0].name = \"JS\" --\u003e\n  \u003c!-- q-binding:skills[0].level = 4 --\u003e\n  \u003c!-- q-binding:skills[1].name = \"CSS\" --\u003e\n  \u003c!-- q-binding:skills[1].level = 5 --\u003e\n\u003c/div\u003e\n```\n\nHowever, we can also create more complex `data` values. The following recreates the above arrays and objects succinctly.\n\n```html\n\u003cdiv id=\"app\" q-convert-bindings\u003e\n  \u003c!-- q-binding:tags = [\"js\", \"library\"] --\u003e\n  \u003c!-- q-binding:author = { \"firstName\": \"Matt\", \"lastName\": \"Stow\" } --\u003e\n  \u003c!-- q-binding:skills = [{ \"name\": \"JS\", \"level\": 4 }, { \"name\": \"CSS\", \"level\": 5 }] --\u003e\n\u003c/div\u003e\n```\n\n*Note: Non-element bindings must be JSON-serializable and written on one line.*\n\n### Referencing global variables as `data` properties\n\nYou can also pass global variables (on `window`) to be used as data properties. Similarly to `q-data`, we can pass a stringified JSON object of key/value pairs to a `q-r-data` attribute, where *key* is the name of the `data`'s property and *value* the name of the global variable to be used, which can also use dot notation to access properties of an object.\n\n```html\n\u003cscript\u003e\n  var ENV = 'dev';\n  var PORT = 3000;\n  var obj = {\n    foo: 'bar',\n    baz: 'qux',\n  }\n\u003c/script\u003e\n\u003cdiv id=\"app\" q-r-data='{\n  \"env\": \"ENV\",\n  \"port\": \"PORT\",\n  \"foo\": \"obj.foo\",\n  \"baz\": \"obj.baz\"\n}'\u003e\u003c/div\u003e\n```\n\nwhich will produce the following `data`:\n\n```js\n{\n  env: 'dev',\n  port: 3000,\n  foo: 'bar',\n  baz: 'qux',\n}\n```\n\n*Note: Bindings specified in the `q-r-data` object take precedence over those in `q-data`.*\n\n### Excluding elements from the app template compiler\n\nIn the previous sections, we introduced the `\u003c!-- \u003cq\u003e --\u003e … \u003c!-- \u003c/q\u003e --\u003e` syntax. These are a pair of opening and closing comments that exclude the contents within from being passed to the template compiler.\n\nThe most obvious use case (and necessary when using inline bindings) is to strip all but the first element of a `v-for` loop as demonstrated earlier.\n\nAnother use case is to replace static markup for a component, such as:\n\n```html\n\u003c!-- \u003cq\u003e --\u003e\n\u003cdiv\u003eI will be stripped in the app and \"replaced\" with the component version below\u003c/div\u003e\n\u003c!-- \u003c/q\u003e --\u003e\n\u003cmy-component text=\"I'm not visible until parsed through the compiler\"\u003e\u003c/my-component\u003e\n```\n\n*Note: Nesting comments is not supported.*\n\n### Instantiating the app\n\nVery little needs to change from the way you'd normally instantiate an app.\n\n#### With a module bundler, such as webpack\n\n```js\nimport Vue from 'vue';\nimport { createAppData, createAppTemplate } from 'quench-vue';\n\nvar appEl = document.getElementById('app');\nvar data = createAppData(appEl);\nvar template = createAppTemplate(appEl);\n\nvar app = new Vue({\n  el: appEl,\n  data: data,\n  template: template,\n});\n```\n\n#### For direct `\u003cscript\u003e` include\n\n```html\n\u003cscript src=\"https://unpkg.com/vue\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/quench-vue/umd/quench-vue.min.js\"\u003e\u003c/script\u003e\n```\n\n```js\nvar appEl = document.getElementById('app');\nvar data = quenchVue.createAppData(appEl);\nvar template = quenchVue.createAppTemplate(appEl);\n\nvar app = new Vue({\n  el: appEl,\n  data: data,\n  template: template,\n});\n```\n\n## Defining local component `template`s\n\nVue applications are often comprised of multiple components, which promotes reuse and reduces repetition. However, the more components you have, the greater your JavaScript bundle will be. While complex components, such as interactive UI widgets should probably be defined in JavaScript, simpler, more display-only components can easily be defined from existing, pre-rendered HTML with Quench Vue.\n\nDefining local component `template`s from existing markup suits situations such as an infinite scroll of news cards, where the original \"page\" of cards are pre-rendered, and as a user scrolls, your Vue app needs to fetch and append more cards from a JSON API response.\n\n*Note: You cannot use Quench Vue to specify the `template` of global components created with `Vue.component()`. We consider global components an anti-pattern anyway.*\n\n### Specifying a component with `[q-component]`\n\nAny element within your app's `el` can be used as the markup for a component by adding an attribute of `q-component=\"NAME\"`, where `\"NAME\"` is the name of the local component defined in your Vue app.\n\nTypically, this would be on a `\u003cdiv\u003e` or similar, which sets the `outerHTML` of the element to be used as the `template`. However, you can also use a `\u003ctemplate\u003e` element as the component definition, in which case the `innerHTML` becomes the `template`.\n\nFor instance:\n\n```html\n\u003c!-- \u003cq\u003e --\u003e\n\u003cdiv q-component=\"card\"\u003e\n  \u003ch3\u003eCard\u003c/h3\u003e\n\u003c/div\u003e\n\u003c!-- \u003c/q\u003e --\u003e\n```\n\nwill create a `template` of:\n\n```html\n\u003cdiv\u003e\n  \u003ch3\u003eCard\u003c/h3\u003e\n\u003c/div\u003e\n```\n\nbut:\n\n```html\n\u003c!-- \u003cq\u003e --\u003e\n\u003ctemplate q-component=\"card\"\u003e\n  \u003ch3\u003eCard\u003c/h3\u003e\n\u003c/template\u003e\n\u003c!-- \u003c/q\u003e --\u003e\n```\n\nwill create a `template` of:\n\n```html\n\u003ch3\u003eCard\u003c/h3\u003e\n```\n\n*Note:*\n* *Only the first instance of `q-component=\"NAME\"` will be used as the component's `template`.*\n* *Components need to be wrapped in `\u003c!-- \u003cq\u003e --\u003e … \u003c!-- \u003c/q\u003e --\u003e` comments to prevent them from being included in the app's template.*\n* *We opted for a custom, `q-component` syntax (compared with Vue's native [`inline-template`](https://vuejs.org/v2/guide/components.html#Inline-Templates) syntax) for various reasons, but mainly because it allows for greater flexibility in the types of templates that can be defined (such as `\u003ctr\u003e`s with multiple children), and we can supplement with additional features as described below.*\n\n### Specifying a component's `template`\n\nThe previous templates aren't particularly useful, but as you'd expect, any Vue directives or template syntax can be used and will be converted, similar to the app's template above (with the exception of not supporting Quench Vue's `q-binding`).\n\nTake the following HTML:\n\n```html\n\u003cdiv\n  class=\"card\"\n  q-component=\"card\"\n  v-bind:class=\"{ 'card--feature': props.isFeatured }\"\n\u003e\n  \u003ca\n    href=\"/news/nintendo-arcade\"\n    v-bind:href=\"props.href\"\n  \u003e\n    \u003ch3 v-text=\"props.title\"\u003e\n      Mario Bros And Other Nintendo Arcade Games Coming To Nintendo Switch\n    \u003c/h3\u003e\n    \u003cp v-text=\"props.date\"\u003e\n      September 13, 2017\n    \u003c/p\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\n\u003cdiv\n  class=\"card\"\n  q-component=\"card\"\n  v-bind:class=\"{ 'card--feature': props.isFeatured }\"\n\u003e\n  \u003ca\n    href=\"/news/doom-2016\"\n    v-bind:href=\"props.href\"\n  \u003e\n    \u003ch3 v-text=\"props.title\"\u003e\n      Doom 2016 Is Coming To Nintendo Switch\n    \u003c/h3\u003e\n    \u003cp v-text=\"props.date\"\u003e\n      September 12, 2017\n    \u003c/p\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n```\n\nwhich will define the `card` component's `template` as:\n\n```html\n\u003cdiv\n  class=\"card\"\n  v-bind:class=\"{ 'card--feature': props.isFeatured }\"\n\u003e\n  \u003ca v-bind:href=\"props.href\"\u003e\n    \u003ch3 v-text=\"props.title\"\u003e\u003c/h3\u003e\n    \u003cp v-text=\"props.date\"\u003e\u003c/p\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n```\n\n### Handling a `template`'s logic\n\nWhile the previous template will handle content and style differences, it's not uncommon for a component's markup to also change based on certain conditions. There a 3 ways in which we can handle these logic requirements:\n\n1. Add the logic within the markup (often using `\u003ctemplate\u003e` so they're invisible in the pre-rendered markup).\n2. Add the logic within the component's JavaScript using a proprietary `partials` object and reference it with a special `\u003cq-component-partial name=\"NAME\"\u003e\u003c/q-component-partial\u003e` syntax; or\n3. Define a completely different component.\n\nLet's look at some examples:\n\n#### Method 1: Add the logic within the markup\n\n```html\n\u003cdiv\n  class=\"card\"\n  q-component=\"card\"\n  v-bind:class=\"{ 'card--feature': props.isFeatured }\"\n\u003e\n  \u003ca\n    href=\"/news/nintendo-arcade\"\n    v-bind:href=\"props.href\"\n  \u003e\n    \u003c!-- \u003cq-component\u003e --\u003e\n    \u003cimg src=\"/nintendo-arcade.jpg\" alt=\"Screenshot of Nintendo arcade games\" /\u003e\n    \u003c!-- \u003c/q-component\u003e --\u003e\n    \u003ctemplate v-if=\"props.image\"\u003e\n      \u003cimg\n        v-bind:src=\"props.image\"\n        v-bind:alt=\"props.alt\"\n      /\u003e\n    \u003c/template\u003e\n    \u003ctemplate v-else\u003e\n      \u003cdiv class=\"fallback\"\u003e\u003c/div\u003e\n    \u003c/template\u003e\n    \u003ch3 v-text=\"props.title\"\u003e\n      Mario Bros And Other Nintendo Arcade Games Coming To Nintendo Switch\n    \u003c/h3\u003e\n    \u003cp v-text=\"props.date\"\u003e\n      September 13, 2017\n    \u003c/p\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n```\n\nThis will most likely be the primary method used to handle template logic, however, with complex conditions, you may like to consider the following 2 approaches.\n\n#### Method 2: Add the logic to the component's JavaScript using `partials`\n\nWhen defining the component in JavaScript, add a `partials` object with a name and template string key/value pair, like so:\n\n```js\ncomponents: {\n  card: {\n    partials: {\n      image: `\n        \u003ctemplate v-if=\"props.image\"\u003e\n          \u003cimg\n            v-bind:src=\"props.image\"\n            v-bind:alt=\"props.alt\"\n          /\u003e\n        \u003c/template\u003e\n        \u003ctemplate v-else\u003e\n          \u003cdiv class=\"fallback\"\u003e\u003c/div\u003e\n        \u003c/template\u003e\n      ` // Using ES6 template literals, but any string concatenation method works\n    },\n    template: 'local'\n  }\n}\n```\n\nand reference it in the pre-rendered component with `\u003cq-component-partial name=\"image\"\u003e\u003c/q-component-partial\u003e`:\n\n```html\n\u003cdiv\n  class=\"card\"\n  q-component=\"card\"\n  v-bind:class=\"{ 'card--feature': props.isFeatured }\"\n\u003e\n  \u003ca\n    href=\"/news/nintendo-arcade\"\n    v-bind:href=\"props.href\"\n  \u003e\n    \u003c!-- \u003cq-component\u003e --\u003e\n    \u003cimg src=\"/nintendo-arcade.jpg\" alt=\"Screenshot of Nintendo arcade games\" /\u003e\n    \u003c!-- \u003c/q-component\u003e --\u003e\n    \u003cq-component-partial name=\"image\"\u003e\u003c/q-component-partial\u003e\n    \u003ch3 v-text=\"props.title\"\u003e\n      Mario Bros And Other Nintendo Arcade Games Coming To Nintendo Switch\n    \u003c/h3\u003e\n    \u003cp v-text=\"props.date\"\u003e\n      September 13, 2017\n    \u003c/p\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n```\n\nWhen the template is compiled, this `\u003cq-component-partial name=\"image\"\u003e` will be converted to the value of the app's `components.partials.image` property.\n\nThis method allows you to move more complex or repetitive logic into the JavaScript to reduce the size of the pre-rendered HTML.\n\n*Note: Having a `\u003cq-component-partial\u003e` element within your markup could affect your layout. There are 2 solutions to this problem:*\n1. *Add `q-component-partial { display: none; }` to your CSS; or*\n2. *Wrap HTML comments around the tag `\u003c!-- \u003cq-component-partial\u003e\u003c/q-component-partial\u003e --\u003e`, which reduces the need for extra CSS, but may make the HTML less obvious in your editor.*\n\n#### Define a completely different component\n\nInstead of handling the logic in the front-end, you could define separate component variations in the pre-rendered HTML. The downside to this approach is, that if your original markup didn't contain a component variation which was later used, Vue would throw an error. A workaround to this is to output a `\u003ctemplate q-component=\"NAME\"\u003e` as a fallback for all possible variations that weren't in the original data.\n\nHere we define 2 components, `card--default` and `card--fallback`, and remove all the `v-if` logic from the markup.\n\n```html\n\u003cdiv\n  class=\"card\"\n  q-component=\"card--default\"\n  v-bind:class=\"{ 'card--feature': props.isFeatured }\"\n\u003e\n  \u003ca\n    href=\"/news/nintendo-arcade\"\n    v-bind:href=\"props.href\"\n  \u003e\n    \u003cimg\n      src=\"/nintendo-arcade.jpg\"\n      alt=\"Screenshot of Nintendo arcade games\"\n      v-bind:src=\"props.image\"\n      v-bind:alt=\"props.alt\"\n    /\u003e\n    \u003ch3 v-text=\"props.title\"\u003e\n      Mario Bros And Other Nintendo Arcade Games Coming To Nintendo Switch\n    \u003c/h3\u003e\n    \u003cp v-text=\"props.date\"\u003e\n      September 13, 2017\n    \u003c/p\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\n\u003ctemplate q-component=\"card--fallback\"\u003e\n  \u003cdiv\n    class=\"card\"\n    v-bind:class=\"{ 'card--feature': props.isFeatured }\"\n  \u003e\n    \u003ca\n      href=\"/news/doom-2016\"\n      v-bind:href=\"props.href\"\n    \u003e\n      \u003cdiv class=\"fallback\"\u003e\u003c/div\u003e\n      \u003ch3 v-text=\"props.title\"\u003e\n        Doom 2016 Is Coming To Nintendo Switch\n      \u003c/h3\u003e\n      \u003cp v-text=\"props.date\"\u003e\n        September 12, 2017\n      \u003c/p\u003e\n    \u003c/a\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n```\n\n### Excluding elements from the component template compiler\n\nSimilarly to [excluding elements from the app template compiler](#excluding-elements-from-the-app-template-compiler), elements within a component can be excluded from its template by being wrapped in a pair of `\u003c!-- \u003cq-component\u003e --\u003e … \u003c!-- \u003c/q-component\u003e --\u003e` comments as demonstrated in the earlier examples.\n\n### Rendering future components dynamically\n\nWhile components are normally rendered with their name in angled brackets (`\u003cnavigation\u003e\u003c/navigation\u003e`), Vue also [supports a meta component (`\u003ccomponent\u003e\u003c/component\u003e`)](https://vuejs.org/v2/api/#component) which allows us to programmatically render a component of our choice.\n\nAssuming we had 4 card variations: `card--default`, `card--fallback`, `card--twitter` and `card--instagram`, we can use a unique variable (often a property on the card's `props` object such as `type`) to selectively render future components of that type using the `is` attribute.\n\nHere we iterate over our `cards` array, dynamically render the correct card component based on `card.type` and pass the card's data to the `props` prop.\n\n```html\n\u003cli v-for\"card in cards\"\u003e\n  \u003ccomponent\n    v-bind:is=\"card.type\"\n    v-bind:props=\"card\"\n  \u003e\u003c/component\u003e\n\u003c/li\u003e\n```\n\n```js\n[\n  {\n    type: 'card--twitter',\n    title: 'Arguing on the Internet still rampant',\n    …\n  },\n  {\n    type: 'card--default',\n    title: 'Mario Bros And Other Nintendo Arcade Games Coming To Nintendo Switch',\n    …\n  },\n  {\n    type: 'card--instagram',\n    title: 'Check out this selfie of me and my avocado',\n    …\n  },\n  {\n    type: 'card--fallback',\n    title: 'Doom 2016 Is Coming To Nintendo Switch',\n    …\n  }\n]\n```\n\n### Updating our app initialization to support pre-rendered components\n\nVery little needs to change [from our earlier example](#instantiating-the-app).\n\n#### With a module bundler, such as webpack\n\n```js\nimport Vue from 'vue';\nimport { createAppData, createAppTemplate, createComponentTemplates } from 'quench-vue'; // import createComponentTemplates\n\nvar appEl = document.getElementById('app');\nvar data = createAppData(appEl);\nvar components = {\n  'card--default': { // Register all possible components for this app\n    props: ['props'], // Define props as you normally would\n    template: 'local', // Specify that the component's template is \"local\"\n  },\n  'card--fallback': {\n    props: ['props'],\n    template: 'local',\n  },\n  'card--instagram': {\n    props: ['props'],\n    template: 'local',\n  },\n  'card--twitter': {\n    props: ['props'],\n    template: 'local',\n  }\n};\ncomponents = createComponentTemplates(appEl, components); // Convert and add templates to your components\nvar template = createAppTemplate(appEl); // createAppTemplate has to be called after createComponentTemplates\n\nvar app = new Vue({\n  el: appEl,\n  components: components,\n  data: data,\n  template: template,\n});\n```\n\n*Note: camelCase component names are converted to kebab-case, so you can use [object shorthand notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Property_definitions) to define your components after `import`ing them*\n\n#### For direct `\u003cscript\u003e` include\n\n```html\n\u003cscript src=\"https://unpkg.com/vue\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/quench-vue/umd/quench-vue.min.js\"\u003e\u003c/script\u003e\n```\n\n```js\nvar appEl = document.getElementById('app');\nvar data = quenchVue.createAppData(appEl);\nvar components = {\n  'card--default': { // Register all possible components for this app\n    props: ['props'], // Define props as you normally would\n    template: 'local', // Specify that the component's template is \"local\"\n  },\n  'card--fallback': {\n    props: ['props'],\n    template: 'local',\n  },\n  'card--instagram': {\n    props: ['props'],\n    template: 'local',\n  },\n  'card--twitter': {\n    props: ['props'],\n    template: 'local',\n  }\n};\ncomponents = quenchVue.createComponentTemplates(appEl, components); // Convert and add templates to your components\nvar template = quenchVue.createAppTemplate(appEl); // createAppTemplate has to be called after createComponentTemplates\n\nvar app = new Vue({\n  el: appEl,\n  components: components,\n  data: data,\n  template: template,\n});\n```\n\n## Hiding elements in the pre-rendered HTML\n\nTo prevent layout jumping and repositioning when the app's template gets compiled, it can be beneficial to visually (and accessibly) hide elements and content that is inappropriate without JavaScript, such as a `\u003cbutton\u003e`.\n\nBy adding a class on your app container and an appropriate CSS rule, this can be achieved easily:\n\n```html\n\u003cdiv id=\"app\" class=\"pre-quench\"\u003e\n  \u003cbutton\n    class=\"hide-when-pre-quench\"\n    v-on:click=\"doSomething\"\n  \u003e\n    I'm a button that only works with JS\n  \u003c/button\u003e\n\u003c/div\u003e\n```\n\n```css\n.pre-quench .hide-when-pre-quench {\n  visibility: hidden;\n}\n```\n\nWhen Quench compiles our template for Vue, it removes any `pre-quench` classes and adds a `quenched` class, thus providing the ability to style elements based on the pre and post-quenched state.\n\n## Embedding additional app templates\n\nYou may also like to embed an additional JavaScript string template within the compiled, pre-rendered template. This can easily be achieved by passing the template as the second parameter to `createAppTemplate()`, and describing where it will appear with a `\u003cq-template\u003e\u003c/q-template\u003e` tag.\n\nA possible use case for this technique is when you need to create multiple apps of simple components (such as a video player), but you don't want to have to continually repeat the `\u003cvideo-player /\u003e` component definition.\n\n```html\n\u003cdiv\n  class=\"video-player\"\n  q-data='{\n    \"autoplay\": true,\n    \"id\": \"someId\"\n  }'\n\u003e\n  \u003cq-template\u003e\u003c/q-template\u003e\n\u003c/div\u003e\n```\n\n```js\nvar baseTemplate = '\u003cvideo-player v-bind:autoplay=\"autoplay\" v-bind:id=\"id\"\u003e\u003c/video-player\u003e';\nvar template = createAppTemplate(appEl, baseTemplate);\n```\n\n*Note: You can also wrap HTML comments around the tag `\u003c!-- \u003cq-template\u003e\u003c/q-template\u003e --\u003e`, to hide the placeholder from the browser's parser.*\n\n## Benefits\n\nHopefully you've recognized that you're now able to render fast, SEO-friendly static markup (either from a CMS, static-site generator or component library such as [Fractal](http://fractal.build/)) and have it quickly and easily converted into a fully dynamic, client-side Vue.js application, without having to set up more complicated server-side rendering processes.\n\n---\n\nCopyright (c) 2018 [Matt Stow](http://mattstow.com)  \nLicensed under the MIT license *(see [LICENSE](https://github.com/stowball/quench-vue/blob/master/LICENSE) for details)*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstowball%2Fquench-vue","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstowball%2Fquench-vue","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstowball%2Fquench-vue/lists"}