Reusable Vue.js

Slots, Scoped Slots, and Render Function

My assumption of topic difficulty:

๐Ÿ˜Ž Slots

๐Ÿ˜… Scoped Slots

๐Ÿคฏ Render Function

Survey Results of topic understanding:

๐Ÿคฏ Slots

๐Ÿคฏ Scoped Slots

๐Ÿ˜Ž Render Function

About Me

I build interfaces on the web.

I'm on CodePen, Twitter and Instagram as @coltborg.

My Mindset:

"I want to write code today that allows me to write less code tomorrow."

Goal for this talk:

"Show examples of code I've written to help others build more reusable components."

Overview

Slots allow for fluid content

Scoped Slots allow for reusable functionality

Render function gives you the power of programmatic templating

(But can also be used for renderless components)

Render Function

There are situations, where you really need the full programmatic power of JavaScript. Thatโ€™s where you can use the render function, a closer-to-the-compiler alternative to templates.

Interesting facts about render

๐ŸŽ <template> compiles to render functions under the hood.

โš›๏ธ A way to write Vue with JSX

โš™๏ธ Useful for functional (stateless) components.

Docs have useful example of programmatic templating

          
              
              
          
        
            
              // AnchoredHeading.vue
              export default {
                render: function (createElement) {
                  return createElement(
                    'h' + this.level,   // tag name
                    this.$slots.default // array of children
                  )
                },
                props: {
                  level: {
                    type: Number,
                    required: true
                  }
                }
              };
            
          

On Outside Click

A renderless component

What if you want to componentise functionality and define the markup uniquely?

The render function needs to return a VNODE.

          
            {
              name: 'SomeComponent',
              render() {
                return createElement('h1', this.blogTitle);
              },
            }
          
        
          
            {
              name: 'SomeComponent',
              render() {
                return this.$slots.default[0];
              },
            }
          
        
          
            {
              name: 'SomeComponent',
              render() {
                return this.$scopedSlots.default({
                  childData: this.childData,
                  childMethod: this.childMethod,
                });
              },
            }
          
        

Slots

Vue implements a content distribution API thatโ€™s modeled after the current Web Components spec draft, using the element to serve as distribution outlets for content.

Slots are useful when you want to inject content in a specific place of a component.

Button

Using Slots

Example of a button with enter text.
          
            
            
          
        
          
            
            
              Slot Text
            
          
        

Quick note, my-button (kebab-case) and MyButton (PascalCase) are both acceptable and are only truely different when using non-string templates.

Alert

Using Default Content

An orange alert warning with a generic title and text explaining something wrong is happening.
          
            
            
          
        
          
            
            
          
        

Label & Input

Using Named Slots

An input with a label for a name. An input with a label and a warning message that is empty.
          
            
            
          
        
          
            
            
          
        
          
            
            
          
        
          
            
            
          
        
Without using named slots and having two slot tags, content appears in both slot tags. Vue warns us that have two slot tags may cause render issues.
          
            
            
          
        

Transitions

An exaggerated transition for a dropdown menu to appear and then slide up and fade away on close.

Vue Transition Docs

          
            // MyTransition.vue
            
          
        
          
            
          
        
          
            /* MyTransition.vue */
            
          
        
          
            
              
            
          
        

Scoped Slots

Sometimes youโ€™ll want to provide a component with a reusable slot that can access data from the child component.

When you want a template inside of a slot to access data (or methods) from the child component.

A diagram showing how data and methods flow from a child component into a parent template through scoped slots.

Simple List Items

With Scoped Slot Data

          
            
            

            
          
        
          
            
            

            
          
        

ES6 FTW ๐Ÿ™Œ๐Ÿป

          
            
            
          
        

Popper

Encapsulating 3rd Party JavaScript

The homepage of popper.js, Popper.js is just ~6KB minified and gzipped, with zero dependencies.
          
            
          
        
          
              ...
              data() {
            -   return {};
            +   return {
            +     isOpen: false,
            +     triggerEl: null,
            +     popperEl: null,
            +   };
              },
            + mounted() {
            +   this.triggerEl = this.$el.querySelector('[data-trigger]');
            +   this.popperEl = this.$el.querySelector('[data-popper]');
            + },
              ...
          
        
          
            // ...
            {
              methods: {
                setupPopper() {
                  if (this.popper === undefined) {
                    this.popper = new Popper(this.triggerEl, this.popperEl, this.config);
                  } else {
                    this.popper.scheduleUpdate();
                  }
                },
              },
            }
            // ...
          
        
          
            // ...
            {
              methods: {
                // ...
                open() {
                  if (this.isOpen) {
                    return;
                  }

                  this.isOpen = true;
                  this.$nextTick(() => {
                    this.setupPopper();
                  });
                },
                close() {
                  if (!this.isOpen) {
                    return;
                  }

                  this.isOpen = false;
                },
              },
            }
            // ...
          
        

Don't forget to ๐Ÿšฎ

          
              // ...
              {
                data() {
                  // ...
                },
                beforeDestroy() {
                  if (!this.popper) {
                    return;
                  }

                  this.popper.destroy();
                },
                mounted() {
                  // ...
                },
                // ...
              }
              // ...
          
        
          
            // ...
            {
              // ...
              render() {
            -   return this.$scopedSlots.default({});
            +   return this.$scopedSlots.default({
            +     isOpen: this.isOpen,
            +     open: this.open,
            +     close: this.close,
            +   });
              },
            }
          
        
          
            
            

            
          
        

Component Composition

Components In Components In Components

(Cue Inception Horns)

Tooltip

Hover activated, icon as trigger, dark popper, standard transition
          
            
            
          
        

Input Tooltip

Regex activated, input + validation as trigger, red popper, standard transition
          
            
            
          
        

Dropdown

Click activated, button as trigger, can close on outside click, menu appearance, standard transition
          
            
            
          
        

Resources