<?xml-stylesheet href="/pretty-feed-v2.xsl" type="text/xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Trys Mudford's Blog</title>
    <link>https://www.trysmudford.com/tags/vue/</link>
    <description>Posts, thoughts, links and photos from Trys</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Wed, 13 Nov 2019 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://www.trysmudford.com/blog/index.xml" rel="self" type="application/rss+xml"/>
    
    <item>
      <title>Microfrontends with Vue.js</title>
      <link>https://www.trysmudford.com/blog/microfrontends-with-vue-js/</link>
      <pubDate>Wed, 13 Nov 2019 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/microfrontends-with-vue-js/</guid>
      <description><![CDATA[
<p>We&rsquo;ve recently been building a complex search integration for a holiday provider using a microfrontends approach in Vue.js.</p>
<p>I&rsquo;ve <a href="https://www.trysmudford.com/blog/city-life/">noted in the past</a>, that there&rsquo;s a common trap many fall into with frontend frameworks:</p>
<blockquote>
<p>Not all of your app/site needs to be controlled by JavaScript. When you dive into the framework pool, the natural, and most documented step is to write it all in the framework.</p>
</blockquote>
<p>This site uses a traditional server-rendered stack, so we began from a position of great markup, and added layers of functionality. Instead of using Vue CLI to spin up a new Vue website, we opted to <code>import</code> Vue into the existing JS pipeline - a Gulp build system provided by the client. It meant losing some of the <abbr title="Hot Module Reloading">HMR</abbr> benefits that come from a well-tuned Webpack setup, but it was definitely the most appropriate and comfortable option to fit for the client.</p>
<p>That&rsquo;s one of the fun parts of agency-life, each project is a case of finding the balance between user needs, client appropriateness, and developer appetite.</p>
<h2 id="requirements">Requirements</h2>
<p>The site needed to render components for various features on the results page:</p>
<ol>
<li>Search panel</li>
<li>Search results &amp; pagination</li>
<li>Search filters</li>
<li>Search sorting</li>
</ol>
<p>And a different set on the details page:</p>
<ol>
<li>Search panel</li>
<li>Holiday details</li>
<li>Edit holiday details</li>
<li>Holiday pricing</li>
</ol>
<h2 id="choosing-an-approach">Choosing an approach</h2>
<p>The next decision was working out how to slice up the Vue parts of the site. As we&rsquo;d ruled out letting Vue control the entire page, our options were limited to:</p>
<h3 id="option-1">Option 1</h3>
<p>Write isolated Vue instances that read data from localStorage &amp; the URL, and work independently.</p>
<p><img src="/images/blog/microfrontends-option-1.jpg" alt=""></p>
<p>There was some definite initial appetite for this choice. Truly encapsulated components is the utopian dream, but it has a major downside: code duplication. If each component is working in isolation, they all have to do a lot of similar grunt work (reading URLs, catching errors, responding to APIs).</p>
<h3 id="option-2">Option 2</h3>
<p>Use a single Vue instance wrapper and use <code>portal-vue</code> to push the components into the correct parts of the non-Vue DOM</p>
<p><img src="/images/blog/microfrontends-option-2.jpg" alt=""></p>
<p>The client had previous experience with <code>portal-vue</code>, so this seemed like a sensible route. But for our use-case, <code>portal-vue</code> simply acted as an unnecessary abstraction around Vue&rsquo;s default instance mounting logic. Sharing a Vue instance would also mean prop-drilling and event bubbling galore. Not fun.</p>
<h3 id="option-3">Option 3</h3>
<p>Write a Vue instance per feature, mount them to various DOM nodes, and link them all together with a Vuex store.</p>
<p><img src="/images/blog/microfrontends-option-3.jpg" alt=""></p>
<p>Vue already has a &lsquo;portal&rsquo; system out of the box: the <code>.mount()</code> function. This, combined with Vue&rsquo;s ability to automatically inject the store dependency into all child components, makes this option extremely powerful. Every feature of the website is its own distinct Vue application, but with the option to hook into global data if required.</p>
<h2 id="the-code">The code</h2>
<p>Here&rsquo;s how we instantiated the various Vue applications:</p>
<div class="highlight"><pre class="chroma"><code class="language-js" data-lang="js"><span class="kr">import</span> <span class="nx">Vue</span> <span class="nx">from</span> <span class="s1">&#39;vue&#39;</span><span class="p">;</span>
<span class="kr">import</span> <span class="nx">SearchPanel</span> <span class="nx">from</span> <span class="s1">&#39;./components/SearchPanel/SearchPanel.vue&#39;</span><span class="p">;</span>
<span class="kr">import</span> <span class="nx">SearchResults</span> <span class="nx">from</span> <span class="s1">&#39;./components/SearchResults/SearchResults.vue&#39;</span><span class="p">;</span>
<span class="kr">import</span> <span class="nx">Feefo</span> <span class="nx">from</span> <span class="s1">&#39;./components/Reviews/Feefo.vue&#39;</span><span class="p">;</span>
<span class="kr">import</span> <span class="nx">store</span> <span class="nx">from</span> <span class="s1">&#39;./store&#39;</span><span class="p">;</span>

<span class="nx">store</span><span class="p">.</span><span class="nx">dispatch</span><span class="p">(</span><span class="s1">&#39;init&#39;</span><span class="p">,</span> <span class="nx">storeDefaults</span><span class="p">);</span>

<span class="kr">const</span> <span class="nx">vueRoots</span> <span class="o">=</span> <span class="p">[</span>
  <span class="p">{</span>
    <span class="nx">id</span><span class="o">:</span> <span class="s1">&#39;search-panel&#39;</span><span class="p">,</span>
    <span class="nx">component</span><span class="o">:</span> <span class="nx">SearchPanel</span><span class="p">,</span>
    <span class="nx">store</span>
  <span class="p">},</span>
  <span class="p">{</span>
    <span class="nx">id</span><span class="o">:</span> <span class="s1">&#39;search-results&#39;</span><span class="p">,</span>
    <span class="nx">component</span><span class="o">:</span> <span class="nx">SearchResults</span><span class="p">,</span>
    <span class="nx">store</span>
  <span class="p">},</span>
  <span class="p">{</span>
    <span class="nx">id</span><span class="o">:</span> <span class="s1">&#39;reviews-box&#39;</span><span class="p">,</span>
    <span class="nx">component</span><span class="o">:</span> <span class="nx">Feefo</span>
  <span class="p">}</span>
<span class="p">];</span>

<span class="nx">vueRoots</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(({</span> <span class="nx">id</span><span class="p">,</span> <span class="nx">store</span><span class="p">,</span> <span class="nx">component</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="nx">id</span><span class="p">))</span> <span class="p">{</span>
    <span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
      <span class="nx">store</span><span class="p">,</span>
      <span class="nx">render</span><span class="o">:</span> <span class="nx">h</span> <span class="p">=&gt;</span> <span class="nx">h</span><span class="p">(</span><span class="nx">component</span><span class="p">)</span>
    <span class="p">}).</span><span class="nx">$mount</span><span class="p">(</span><span class="sb">`#</span><span class="si">${</span><span class="nx">id</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">});</span>
</code></pre></div><p>After importing our top-level components, we instantiate the store, passing in any default state (driven by server-rendered JSON). This backbone of data includes items like API endpoints and feature flags.</p>
<p>Next we have a list of all the Vue instances we wish to load. It&rsquo;s here where we pass in the reference to the store for the components that require it. In the example above, the Feefo component doesn&rsquo;t need access to the store, so it doesn&rsquo;t get lumbered with it.</p>
<p>Finally, we loop around the objects and check whether the mounting node is on the current page. Vue will still instantiate a component, even if element isn&rsquo;t available. While this can be helpful, it&rsquo;s not the desired effect for this site.</p>
<h2 id="accessing-data">Accessing data</h2>
<p>The beauty of this approach over option two, is the way Vue injects the store as a property of <code>this</code> in all child components &amp; mixins.</p>
<p>Reading global data is so clean:</p>
<div class="highlight"><pre class="chroma"><code class="language-js" data-lang="js"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
  <span class="nx">computed</span><span class="o">:</span> <span class="p">{</span>
    <span class="nx">hasResults</span><span class="p">()</span> <span class="p">{</span>
      <span class="k">return</span> <span class="o">!!</span><span class="k">this</span><span class="p">.</span><span class="nx">$store</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">results</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">};</span>
</code></pre></div><p>Writing data is a little more involved. Due to the actions/mutations pattern in Vuex, we can&rsquo;t write directly to <code>this.$store.state</code> properties.</p>
<p>One approach I tend to lean towards is a generic state updater mutation, which works for most use-cases:</p>
<div class="highlight"><pre class="chroma"><code class="language-js" data-lang="js"><span class="kr">const</span> <span class="nx">mutations</span> <span class="o">=</span> <span class="p">{</span>
  <span class="nx">updateState</span><span class="p">(</span><span class="nx">state</span><span class="p">,</span> <span class="nx">payload</span><span class="p">)</span> <span class="p">{</span>
    <span class="kr">const</span> <span class="nx">payloads</span> <span class="o">=</span> <span class="o">!</span><span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span> <span class="o">?</span> <span class="p">[</span><span class="nx">payload</span><span class="p">]</span> <span class="o">:</span> <span class="nx">payload</span><span class="p">;</span>

    <span class="k">for</span> <span class="p">(</span><span class="kr">const</span> <span class="p">{</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">value</span> <span class="p">}</span> <span class="k">of</span> <span class="nx">payloads</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">state</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="o">?</span> <span class="p">[...</span><span class="nx">value</span><span class="p">]</span> <span class="o">:</span> <span class="nx">value</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">};</span>
</code></pre></div><p>Writing to global state is achieved with the following call:</p>
<div class="highlight"><pre class="chroma"><code class="language-js" data-lang="js"><span class="k">this</span><span class="p">.</span><span class="nx">$store</span><span class="p">.</span><span class="nx">commit</span><span class="p">(</span><span class="s1">&#39;updateState&#39;</span><span class="p">,</span> <span class="p">{</span>
  <span class="nx">key</span><span class="o">:</span> <span class="s1">&#39;stateKey&#39;</span><span class="p">,</span>
  <span class="nx">value</span><span class="o">:</span> <span class="s1">&#39;new-value&#39;</span>
<span class="p">});</span>

<span class="c1">// Multiple value update
</span><span class="c1"></span><span class="k">this</span><span class="p">.</span><span class="nx">$store</span><span class="p">.</span><span class="nx">commit</span><span class="p">(</span><span class="s1">&#39;updateState&#39;</span><span class="p">,</span> <span class="p">[</span>
  <span class="p">{</span>
    <span class="nx">key</span><span class="o">:</span> <span class="s1">&#39;stateKey&#39;</span><span class="p">,</span>
    <span class="nx">value</span><span class="o">:</span> <span class="s1">&#39;new-value&#39;</span>
  <span class="p">},</span>
  <span class="p">{</span>
    <span class="nx">key</span><span class="o">:</span> <span class="s1">&#39;aDifferentStateKey&#39;</span><span class="p">,</span>
    <span class="nx">value</span><span class="o">:</span> <span class="s1">&#39;another-value&#39;</span>
  <span class="p">}</span>
<span class="p">]);</span>
</code></pre></div><h2 id="vuex-helpers">Vuex helpers</h2>
<p>Store state is generally read in with computed properties, but it can be cumbersome writing <code>this.$store.state.key</code> if you&rsquo;re pulling in a lot of data. Vuex comes with some handy helper methods out of the box for this very purpose.</p>
<div class="highlight"><pre class="chroma"><code class="language-js" data-lang="js"><span class="kr">import</span> <span class="p">{</span> <span class="nx">mapState</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;vuex&#39;</span><span class="p">;</span>

<span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
  <span class="nx">computed</span><span class="o">:</span> <span class="p">{</span>
    <span class="p">...</span><span class="nx">mapState</span><span class="p">([</span><span class="s1">&#39;results&#39;</span><span class="p">]),</span>

    <span class="nx">hasResults</span><span class="p">()</span> <span class="p">{</span>
      <span class="k">return</span> <span class="o">!!</span><span class="k">this</span><span class="p">.</span><span class="nx">results</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">};</span>
</code></pre></div><p><code>mapGetters</code>, <code>mapActions</code> and <code>mapMutations</code> work in the same way, and go a long way towards cleaning up your code.</p>
<h2 id="microfrontends-benefits">Microfrontends benefits</h2>
<p>The biggest win from this approach came when we realised that the &lsquo;Edit holiday details&rsquo; component really should be working against the same dataset as the &lsquo;Search panel&rsquo;. Had they been written as isolated components; reading data in from the URL and working independently, it would&rsquo;ve been a nightmare to unpick. This approach made is very straightforward to conform the two components retrospectively.</p>
<p>This approach doesn&rsquo;t give you the ability to communicate directly between components, but that&rsquo;s broadly seen as a feature, not a bug. Firing events out and receiving data in is a scalable component design paradigm. Components, where possible, should be a <a href="https://www.trysmudford.com/blog/coding-with-contracts-components/">reflection of state, not stateful</a>.</p>
<p>We&rsquo;re used to the idea of systematic design, and component-based thinking, but this frontend infrastructure modularity feels like an interesting space. There&rsquo;s certainly more to be explored when it comes to bundle sizes, dynamic imports, and working with multiple stores/modules, but on the back of this project, it feels like a very viable approach to adding a bit of framework spice to a server-rendered site.</p>
<p><a href="https://preactjs.com/">Preact</a> &amp; <a href="https://github.com/developit/unistore">Unistore</a> does a great job of injecting different dependencies into a component if you&rsquo;re on the other side of the JS fence! I&rsquo;ve written about that previously <a href="https://www.trysmudford.com/blog/injecting-dependencies-with-unistore/">here</a>.</p>
]]>
      </description>
    </item>
    
    <item>
      <title>Dynamic scoped slots in Vue.js</title>
      <link>https://www.trysmudford.com/blog/dynamic-scoped-slots-in-vue-js/</link>
      <pubDate>Thu, 11 Apr 2019 00:00:00 +0000</pubDate>
      
      <guid>https://www.trysmudford.com/blog/dynamic-scoped-slots-in-vue-js/</guid>
      <description><![CDATA[
<p><a href="https://vuejs.org/v2/guide/components-slots.html">Slots</a> are super-powerful in Vue.js. They let you inject components from a parent into a various parts of the child, helping you compose nice and generic components. But that&rsquo;s not all! We&rsquo;ll take a run-up to dynamic scoped slots through the following:</p>
<ul>
<li><a href="/blog/dynamic-scoped-slots-in-vue-js#default-slots">Default slots</a></li>
<li><a href="/blog/dynamic-scoped-slots-in-vue-js#named-slots">Named slots</a></li>
<li><a href="/blog/dynamic-scoped-slots-in-vue-js#scoped-slots">Scoped slots</a></li>
<li><a href="/blog/dynamic-scoped-slots-in-vue-js#dynamic-scoped-slots">Dynamic scoped slots</a></li>
</ul>
<p>Btw, apologies for the syntax highlighting, it struggles a bit with Vue!</p>
<h2 id="default-slots">Default slots</h2>
<p>This is the most basic of slots. It takes the content you provide in the child declaration: <code>&lt;comp&gt;THIS BIT&lt;/comp&gt;</code>, and injects it into the component.</p>
<div class="highlight"><pre class="chroma"><code class="language-jsx" data-lang="jsx"><span class="c1">// Component: PageHeader
</span><span class="c1"></span><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">header</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span><span class="nx">Our</span> <span class="nx">header</span><span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">slot</span> <span class="p">/&gt;</span>
  <span class="p">&lt;/</span><span class="nt">header</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>

<span class="c1">// Parent
</span><span class="c1"></span><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">PageHeader</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">h2</span><span class="p">&gt;</span><span class="nx">Custom</span> <span class="nx">content</span><span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
    <span class="p">&lt;/</span><span class="nt">PageHeader</span><span class="p">&gt;</span>
  <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</code></pre></div><p>This will render as:</p>
<div class="highlight"><pre class="chroma"><code class="language-jsx" data-lang="jsx"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">header</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span><span class="nx">Our</span> <span class="nx">header</span><span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">h2</span><span class="p">&gt;</span><span class="nx">Custom</span> <span class="nx">content</span><span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
    <span class="p">&lt;/</span><span class="nt">header</span><span class="p">&gt;</span>
  <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</code></pre></div><h2 id="named-slots">Named slots</h2>
<p>Named slots allow you to inject multiple bits of content within a child component:</p>
<div class="highlight"><pre class="chroma"><code class="language-jsx" data-lang="jsx"><span class="c1">// Component: PageTemplate
</span><span class="c1"></span><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">header</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">slot</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;header&#34;</span> <span class="p">/&gt;</span>
    <span class="p">&lt;/</span><span class="nt">header</span><span class="p">&gt;</span>

    <span class="p">&lt;</span><span class="nt">slot</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;content&#34;</span> <span class="p">/&gt;</span>

    <span class="p">&lt;</span><span class="nt">footer</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">slot</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;footer&#34;</span> <span class="p">/&gt;</span>
    <span class="p">&lt;/</span><span class="nt">footer</span><span class="p">&gt;</span>
  <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>

<span class="c1">// Parent
</span><span class="c1"></span><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">PageTemplate</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">template</span> <span class="na">v</span><span class="err">-</span><span class="na">slot</span><span class="err">:</span><span class="na">header</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span><span class="nx">Header</span> <span class="nx">content</span><span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
    <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>

    <span class="p">&lt;</span><span class="nt">template</span> <span class="na">v</span><span class="err">-</span><span class="na">slot</span><span class="err">:</span><span class="na">content</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span><span class="nx">Page</span> <span class="nx">content</span><span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
    <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>

    <span class="p">&lt;</span><span class="nt">template</span> <span class="na">v</span><span class="err">-</span><span class="na">slot</span><span class="err">:</span><span class="na">footer</span><span class="p">&gt;</span>
      <span class="o">&amp;</span><span class="nx">copy</span><span class="p">;</span> <span class="mi">2019</span>
    <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
  <span class="p">&lt;/</span><span class="nt">PageTemplate</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</code></pre></div><h2 id="scoped-slots">Scoped slots</h2>
<p>Scoped slots let you pass data/methods from the child component, <strong>back into the parent</strong>. It&rsquo;s really useful for list rendering, where the child does some data-wrangling and global styling, but the parent can still define the shape of each list item:</p>
<div class="highlight"><pre class="chroma"><code class="language-jsx" data-lang="jsx"><span class="c1">// Component: List
</span><span class="c1"></span><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">li</span> <span class="na">v</span><span class="err">-</span><span class="na">for</span><span class="o">=</span><span class="s">&#34;item in items&#34;</span> <span class="err">:</span><span class="na">key</span><span class="o">=</span><span class="s">&#34;item.id&#34;</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">slot</span>
        <span class="na">name</span><span class="o">=</span><span class="s">&#34;item&#34;</span>
        <span class="err">:</span><span class="na">item</span><span class="o">=</span><span class="s">&#34;item&#34;</span>
        <span class="err">:</span><span class="na">remove</span><span class="o">=</span><span class="s">&#34;removeItem&#34;</span>
      <span class="p">/&gt;</span>
    <span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
  <span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>

<span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
<span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
  <span class="nx">props</span><span class="o">:</span> <span class="p">{</span>
    <span class="nx">items</span><span class="o">:</span> <span class="p">{</span>
      <span class="nx">type</span><span class="o">:</span> <span class="nb">Array</span><span class="p">,</span>
      <span class="nx">required</span><span class="o">:</span> <span class="kc">true</span>
    <span class="p">}</span>
  <span class="p">},</span>

  <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
    <span class="nx">removeItem</span><span class="p">(</span><span class="nx">id</span><span class="p">)</span> <span class="p">{</span>
      <span class="c1">// ... handle item removal
</span><span class="c1"></span>    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</code></pre></div><p>The parent can then implement it&rsquo;s own rendering style for different data structures:</p>
<div class="highlight"><pre class="chroma"><code class="language-jsx" data-lang="jsx"><span class="c1">// Parent
</span><span class="c1"></span><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">List</span> <span class="err">:</span><span class="na">items</span><span class="o">=</span><span class="s">&#34;items&#34;</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">template</span> <span class="na">v</span><span class="err">-</span><span class="na">slot</span><span class="err">:</span><span class="na">item</span><span class="o">=</span><span class="s">&#34;{ item, remove }&#34;</span><span class="p">&gt;</span>
      <span class="p">{{</span> <span class="nx">item</span><span class="p">.</span><span class="nx">name</span> <span class="p">}}</span>
      <span class="p">&lt;</span><span class="nt">button</span>
        <span class="na">type</span><span class="o">=</span><span class="s">&#34;button&#34;</span>
        <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;remove(item.id)&#34;</span>
      <span class="p">&gt;</span><span class="nx">Delete</span><span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span>
    <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
  <span class="p">&lt;/</span><span class="nt">List</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>

<span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
<span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
  <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">{</span>
      <span class="nx">items</span><span class="o">:</span> <span class="p">[</span>
        <span class="p">{</span>
          <span class="nx">id</span><span class="o">:</span> <span class="s1">&#39;1&#39;</span><span class="p">,</span>
          <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;Trys&#39;</span>
        <span class="p">}</span>
      <span class="p">]</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</code></pre></div><p>Note the attribute: <code>v-slot:item=&quot;{ item, remove }&quot;</code>. It tells Vue which named slot to use, exposes the passed props, which we can destructure. This could be a method or data and works a bit like a render-function in React.</p>
<h2 id="dynamic-scoped-slots">Dynamic scoped slots</h2>
<p>Right, the final piece of the puzzle. Dynamic scoped slots are much like regular scoped slots, but they have a name derived at runtime. How is that useful, you may ask&hellip;</p>
<p>Take a multi-step form. You want a wrapper element that&rsquo;ll handle all the styling and step navigation, and you want to feed the steps as an array. Most steps have a consistent header, but a few of them need additional components to be rendered within. If this was React-land, we could pass full components in the array easily enough, but Vue makes this sorta thing a bit more tricksy. Let&rsquo;s approach this from the outside, and define how we want to use this component before building it.</p>
<div class="highlight"><pre class="chroma"><code class="language-jsx" data-lang="jsx"><span class="c1">// Parent
</span><span class="c1"></span><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">multi</span><span class="err">-</span><span class="na">step</span><span class="err">-</span><span class="na">form</span> <span class="na">steps</span><span class="o">=</span><span class="s">&#34;steps&#34;</span><span class="p">&gt;</span>
    <span class="c1">// Our default header
</span><span class="c1"></span>    <span class="p">&lt;</span><span class="nt">template</span> <span class="na">v</span><span class="err">-</span><span class="na">slot</span><span class="err">:</span><span class="na">header</span><span class="o">=</span><span class="s">&#34;{ step }&#34;</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;{{</span> <span class="nx">step</span><span class="p">.</span><span class="nx">title</span> <span class="p">}}&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
    <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>

    <span class="c1">// Our additional content for the step one header
</span><span class="c1"></span>    <span class="p">&lt;</span><span class="nt">template</span> <span class="na">v</span><span class="err">-</span><span class="na">slot</span><span class="err">:[&#39;</span><span class="na">header_step</span><span class="err">-</span><span class="na">1</span><span class="err">&#39;]=&#34;</span><span class="p">{</span> <span class="na">save</span> <span class="p">}</span><span class="err">&#34;</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span><span class="nx">Additional</span> <span class="nx">content</span><span class="p">,</span> <span class="nx">just</span> <span class="k">for</span> <span class="nx">step</span> <span class="nx">one</span><span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">button</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;button&#34;</span> <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;save&#34;</span><span class="p">&gt;</span><span class="nx">Save</span><span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span>
    <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
  <span class="o">&lt;</span><span class="err">/multi-step-form&gt;</span>
<span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>

<span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
<span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
  <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">{</span>
      <span class="nx">steps</span><span class="o">:</span> <span class="p">[</span>
        <span class="p">{</span>
          <span class="nx">id</span><span class="o">:</span> <span class="s1">&#39;step-1&#39;</span><span class="p">,</span>
          <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;First step&#39;</span>
        <span class="p">},</span>
        <span class="p">{</span>
          <span class="nx">id</span><span class="o">:</span> <span class="s1">&#39;step-2&#39;</span><span class="p">,</span>
          <span class="nx">title</span><span class="o">:</span> <span class="s1">&#39;Second step&#39;</span>
        <span class="p">}</span>
      <span class="p">]</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</code></pre></div><p>Cool, onto building this component:</p>
<div class="highlight"><pre class="chroma"><code class="language-jsx" data-lang="jsx"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">form</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">v</span><span class="err">-</span><span class="na">for</span><span class="o">=</span><span class="s">&#34;step in steps&#34;</span> <span class="err">:</span><span class="na">key</span><span class="o">=</span><span class="s">&#34;step.id&#34;</span><span class="p">&gt;</span>
      <span class="c1">// Our default header
</span><span class="c1"></span>      <span class="p">&lt;</span><span class="nt">slot</span>
        <span class="na">name</span><span class="o">=</span><span class="s">&#34;header&#34;</span>
        <span class="err">:</span><span class="na">step</span><span class="o">=</span><span class="s">&#34;step&#34;</span>
        <span class="err">:</span><span class="na">save</span><span class="o">=</span><span class="s">&#34;save&#34;</span>
      <span class="p">/&gt;</span>

      <span class="c1">// Our additional content for any step
</span><span class="c1"></span>      <span class="p">&lt;</span><span class="nt">slot</span>
        <span class="err">:</span><span class="na">name</span><span class="o">=</span><span class="s">&#34;`header_${step.id}`&#34;</span>
        <span class="err">:</span><span class="na">step</span><span class="o">=</span><span class="s">&#34;step&#34;</span>
        <span class="err">:</span><span class="na">save</span><span class="o">=</span><span class="s">&#34;save&#34;</span>
      <span class="p">/&gt;</span>
    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
  <span class="p">&lt;/</span><span class="nt">form</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>

<span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
<span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
  <span class="nx">props</span><span class="o">:</span> <span class="p">{</span>
    <span class="nx">steps</span><span class="o">:</span> <span class="nb">Array</span>
  <span class="p">},</span>

  <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
    <span class="nx">save</span><span class="p">()</span> <span class="p">{</span>
      <span class="c1">// ... Saving logic
</span><span class="c1"></span>    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</code></pre></div><p>With that intriguing looking <code>&lt;slot /&gt;</code> tag, we create a dynamic scoped slot that&rsquo;ll only be rendered when a corresponding <code>&lt;template name=&quot;header_step-1&quot;&gt;</code> is used by the parent. We can still pass in custom data and methods like any other scoped slot.</p>
<p>You could even hide the default header if a dynamic slot has been passed in with the following code:</p>
<div class="highlight"><pre class="chroma"><code class="language-jsx" data-lang="jsx"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">form</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">v</span><span class="err">-</span><span class="na">for</span><span class="o">=</span><span class="s">&#34;step in steps&#34;</span> <span class="err">:</span><span class="na">key</span><span class="o">=</span><span class="s">&#34;step.id&#34;</span><span class="p">&gt;</span>
      <span class="c1">// Our default header
</span><span class="c1"></span>      <span class="p">&lt;</span><span class="nt">slot</span>
        <span class="na">v</span><span class="err">-</span><span class="na">if</span><span class="o">=</span><span class="s">&#34;!$scopedSlots[`header_${step.id}`]&#34;</span>
        <span class="na">name</span><span class="o">=</span><span class="s">&#34;header&#34;</span>
        <span class="err">:</span><span class="na">step</span><span class="o">=</span><span class="s">&#34;step&#34;</span>
        <span class="err">:</span><span class="na">save</span><span class="o">=</span><span class="s">&#34;save&#34;</span>
      <span class="p">/&gt;</span>

      <span class="c1">// Our alternative header for provided steps
</span><span class="c1"></span>      <span class="p">&lt;</span><span class="nt">slot</span>
        <span class="err">:</span><span class="na">name</span><span class="o">=</span><span class="s">&#34;`header_${step.id}`&#34;</span>
        <span class="err">:</span><span class="na">step</span><span class="o">=</span><span class="s">&#34;step&#34;</span>
        <span class="err">:</span><span class="na">save</span><span class="o">=</span><span class="s">&#34;save&#34;</span>
      <span class="p">/&gt;</span>
    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
  <span class="p">&lt;/</span><span class="nt">form</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</code></pre></div><p>Pretty neat!</p>
]]>
      </description>
    </item>
    
  </channel>
</rss>