Vue Composition API Workshop

@akryum

Vue.js Core Team

Why the Composition API?

Why the Composition API?

Component readability as it grows

Code Reusing Patterns have drawbacks

Limited TypeScript support

Vue 2 Limitations:

export default {
  data () {
    return {
      searchText: '',
    }
  },
  
  computed: {
    filteredItems () {
      // ...
    },
  },
}

Component readability as it grows

Search ▶

Search ▶

export default {
  data () {
    return {
      searchText: '',
      sortBy: 'name',
    }
  },
  
  computed: {
    filteredItems () {
      // ...
    },
    
    sortedItems () {
      // ...
    },
  },
}

Component readability as it grows

Search ▶

Search ▶

Sort ▶

Sort ▶

Features are organized by component options

Organized by component options

Organized by features

With the Composition API

export default {
  data () {
    return {
      searchText: '',
      sortBy: 'name',
    }
  },
  
  computed: {
    filteredItems () {
      // ...
    },
    
    sortedItems () {
      // ...
    },
  },
}

Search ▶

Search ▶

Sort ▶

Sort ▶

With the Composition API

Search ▶

Sort ▶

export default {
  setup () {
    // Search feature
    const searchText = ref('')
    const filteredItems = computed(() => /* ... */)
    
    // Sort feature
    const sortBy = ref('name')
    const sortedItems = computed(() => /* ... */)
  },
}

With the Composition API

Search ▶

Sort ▶

export default {
  setup () {
    // Search feature
    const searchText = ref('')
    const filteredItems = computed(() => /* ... */)
    
    // Sort feature
    const sortBy = ref('name')
    const sortedItems = computed(() => /* ... */)
  },
}

Organized by feature

With the Composition API

Search ▶

Sort ▶

export default {
  setup () {
    // Search feature
    const searchText = ref('')
    const filteredItems = computed(() => /* ... */)
    
    // Sort feature
    const sortBy = ref('name')
    const sortedItems = computed(() => /* ... */)

    return {
      searchText,
      sortBy,
      sortedItems,
    }
  },
}

With the Composition API

Search ▶

Sort ▶

export default {
  setup () {
    // Search feature
    const searchText = ref('')
    const filteredItems = computed(() => /* ... */)
    
    // Sort feature
    const sortBy = ref('name')
    const sortedItems = computed(() => /* ... */)

    return {
      searchText,
      sortBy,
      sortedItems,
    }
  },
}

Expose to the template

With the Composition API

Search ▶

Sort ▶

export default {
  setup () {
    const { searchText, filteredItems } = useSearch()
    const { sortBy, sortedItems } = useSort(filteredItems)
    return {
      searchText,
      sortBy,
      sortedItems,
    }
  },
}

// Search feature
function useSearch (items) {
  const searchText = ref('')
  const filteredItems = computed(() => /* ... */)
  return { searchText, filteredItems }
}

// Sort feature
function useSort (items) {
  const sortBy = ref('name')
  const sortedItems = computed(() => /* ... */)
  return { sortBy, sortedItems }
}

With the Composition API

Search ▶

Sort ▶

export default {
  setup () {
    const { searchText, filteredItems } = useSearch()
    const { sortBy, sortedItems } = useSort(filteredItems)
    return {
      searchText,
      sortBy,
      sortedItems,
    }
  },
}

// Search feature
function useSearch (items) {
  const searchText = ref('')
  const filteredItems = computed(() => /* ... */)
  return { searchText, filteredItems }
}

// Sort feature
function useSort (items) {
  const sortBy = ref('name')
  const sortedItems = computed(() => /* ... */)
  return { sortBy, sortedItems }
}

Extract features into Composition Functions

Vue 2
Code Reuse Patterns have drawbacks

Vue 2 Code Reuse Patterns

Mixins

Mixin Factories

Scoped Slots

Mixins

export default {
  data () {
    return {
      searchText: '',
      sortBy: 'name',
    }
  },
  
  computed: {
    filteredItems () {
      // ...
    },
    
    sortedItems () {
      // ...
    },
  },
}

Mixins

const searchMixin = {
  data () {
    return { searchText: '' }
  },
  computed: {
    filteredItems () { /* ... */ },
  },
}

const sortMixin = {
  data () {
    return { sortBy: 'name' }
  },
  computed: {
    sortedItems () { /* ... */ },
  },
}


export default {
  mixins: [ searchMixin, sortMixin ],
}

Mixins

const searchMixin = {
  data () {
    return { searchText: '' }
  },
  computed: {
    filteredItems () { /* ... */ },
  },
}

const sortMixin = {
  data () {
    return { sortBy: 'name' }
  },
  computed: {
    sortedItems () { /* ... */ },
  },
}


export default {
  mixins: [ searchMixin, sortMixin ],
}

Organized by feature

Conflict Prone

Unclear relationships

Not easily reusable

Mixin Factories

Functions that return a mixin

Mixin Factories

function searchMixinFactory ({ ... }) {
  return {
    data () {
      return { searchText: '' }
    },
    computed: {
      filteredItems () { /* ... */ },
    },
  }
}

function sortMixinFactory ({ ... }) {
  return {
    data () {
      return { sortBy: 'name' }
    },
    computed: {
      sortedItems () { /* ... */ },
    },
  }
}

export default {
  mixins: [ searchMixinFactory(), sortMixinFactory() ],
}

Mixin Factories

function searchMixinFactory ({ ... }) {
  return {
    data () {
      return { searchText: '' }
    },
    computed: {
      filteredItems () { /* ... */ },
    },
  }
}

function sortMixinFactory ({ ... }) {
  return {
    data () {
      return { sortBy: 'name' }
    },
    computed: {
      sortedItems () { /* ... */ },
    },
  }
}

export default {
  mixins: [ searchMixinFactory(), sortMixinFactory() ],
}

Organized by feature

Weak namespacing

Implicit property addition

No instance access to customize the behavior

Reusable & configurable

Clearer relationship

Scoped slots

Scoped slots

<script>
export default {
  // Search feature here
}
</script>

<template>
  <div>
    <slot v-bind="{ searchText, filteredItems }" />
  </div>
</template>
<script>
export default {
  // Sort feature here
}
</script>

<template>
  <div>
    <slot v-bind="{ sortBy, sortedItems }" />
  </div>
</template>
<SearchService
  v-slot="searchProps"
>
  <SortService
    :item="searchProps.filteredItems"
    v-slot="sortProps"
  >
    <li v-for="item of sortProps.sortedItems">
      ...
    </li>
  </SearchService>
</SearchService>
SearchService.vue
SortService.vue

Scoped slots

<script>
export default {
  // Search feature here
}
</script>

<template>
  <div>
    <slot v-bind="{ searchText, filteredItems }" />
  </div>
</template>
<script>
export default {
  // Sort feature here
}
</script>

<template>
  <div>
    <slot v-bind="{ sortBy, sortedItems }" />
  </div>
</template>
<SearchService
  v-slot="searchProps"
>
  <SortService
    :item="searchProps.filteredItems"
    v-slot="sortProps"
  >
    <li v-for="item of sortProps.sortedItems">
      ...
    </li>
  </SearchService>
</SearchService>

Solves mixin problems

Increased indentation

Lost of configuration

Less flexible

Less performant

Composition API

Composition API

export default {
  data () {
    return {
      searchText: '',
      sortBy: 'name',
    }
  },
  
  computed: {
    filteredItems () {
      // ...
    },
    
    sortedItems () {
      // ...
    },
  },
}

Composition API

export default {
  setup () {
    const { searchText, filteredItems } = useSearch()
    const { sortBy, sortedItems } = useSort(filteredItems)
    return {
      searchText,
      sortBy,
      sortedItems,
    }
  },
}

// Search feature
function useSearch (items) {
  const searchText = ref('')
  const filteredItems = computed(() => /* ... */)
  return { searchText, filteredItems }
}

// Sort feature
function useSort (items) {
  const sortBy = ref('name')
  const sortedItems = computed(() => /* ... */)
  return { sortBy, sortedItems }
}

Composition API

export default {
  setup () {
    const { searchText, filteredItems } = useSearch()
    const { sortBy, sortedItems } = useSort(filteredItems)
    return {
      searchText,
      sortBy,
      sortedItems,
    }
  },
}

// Search feature
function useSearch (items) {
  const searchText = ref('')
  const filteredItems = computed(() => /* ... */)
  return { searchText, filteredItems }
}

// Sort feature
function useSort (items) {
  const sortBy = ref('name')
  const sortedItems = computed(() => /* ... */)
  return { sortBy, sortedItems }
}

Less code

Familiar functions

Extremely flexible

Tooling friendly

Advanced syntax

Workshop
Time

Movie DB

Movie DB

Movie DB

Client

Vue

Apollo Client

Server

Node

GraphQL

Apollo Server

Let's build this!

@Akryum

Thank you!

Workshop Summit 2020 - Vue Composition API and GraphQL

By Guillaume Chau

Workshop Summit 2020 - Vue Composition API and GraphQL

  • 4,705