Converting your Vue Component to Composition API shouldn’t be hard if you want to keep that orderly grouped with data, computed and methods and without changing
this.

Also I want to point out the current explanation really doesn’t make Vue fun, and that I want to bring that back.

Install Composition API

npm i -S @vue/composition-api
package.json: "@vue/composition-api": "^1.0.0-beta.22"
One of the advantages could be from migrating to Composition API what I could think of is: Reusing a piece of code (in this case a composition) to a different theme of Page Components.

Quick Template Composition

With this template we can use similar logic to make a modular Composition.

import { computed } from '@vue/composition-api'

const toComputed = object => Object.entries(object).reduce((acc, [key, method]) => {
    acc[key] = computed(method)
    return acc
}, {})

export default toComputed
Quick Template toComputed: toComputed.js
import { reactive, toRefs } from '@vue/composition-api'
import toComputed from '../toComputed'

const setup = props => {
    // data: state
    const state = reactive({
    })
    // computed: compute
    const compute = {
    }
    // methods
    const methods = {
    }
    
    return {
        ...toRefs(state),
        ...toComputed(compute),
        ...methods
    }
}

export default setup
Quick Template Composition API: composition/setup-template.js
import setup from '../composition/setup-template.js'

export default {
    name: 'TemplateComponent',
    setup,
    // Life Cycle Hooks
    mount() {}
}
Quick Template Component: vue/TemplateComponent.vue

Things you should mind (for this example).

data

When you have data in your Component.
export default {
	data: () => ({
		list: [],
		filters: []
	})
}
Snippet: data of .vue

Change data as state to:

import { reactive, toRefs } from '@vue/composition-api'

const setup = props => {
    // data
    const state = reactive({
        list: [],
        filters: []
    })
    
    return {
        ...toRefs(state)
    }
}

export default setup
Snippet: data as state of .js
Easy access with by simply: state.list and state.list.push('addMe').

// I find it tedious to write like this:

import { ref } from '@vue/composition-api'

const setup = props => {
    // data
    const list = ref([]),
        filters = ref([])
    
    return {
        list,
        filters
    }
}

export default setup
Tedious...
I find it tedious to write it like this: const list = ref([]), and accessing values doing this list.value.push('something'). This takes fun out of coding zen using Vue.

computed

When you have computed in your Component.
export default {
    computed: {
        selected: {
            get() {
                return this.filters[0]
            },
            set(value) {
                // console.log({ value })
                if (value == undefined) {
                    this.filters = []
                    return
                }

                const { filters } = this
                const index = filters.indexOf(value)

                if (index > 0) {
                    filters.unshift(...filters.splice(index, 1))
                } else {
                    filters.splice(0, 1, value)
                }
            }
        },
        mappedOptionsFromUniqueTagsFromList() {
            const options = this.uniqueTagsFromList.map(item => ({
                value: item.id,
                text: item.name
            }))
            const label = {
                selected: true,
                // disabled: true,
                value: null,
                text: 'Choose Category'
            }

            return [label, ...options]
        }
    }
}
Snippet: computed of .vue

Change computed as compute to:

import { computed } from '@vue/composition-api'

const toComputed = object => Object.entries(object).reduce((acc, [key, method]) => {
    acc[key] = computed(method)
    return acc
}, {})

const setup = props => {
    // computed
    const compute = {
        selected: {
            get() {
                return this.filters[0]
            },
            set(value) {
                // console.log({ value })
                if (value == undefined) {
                    this.filters = []
                    return
                }

                const { filters } = this
                const index = filters.indexOf(value)

                if (index > 0) {
                    filters.unshift(...filters.splice(index, 1))
                } else {
                    filters.splice(0, 1, value)
                }
            }
        },
        mappedOptionsFromUniqueTagsFromList() {
            const options = this.uniqueTagsFromList.map(item => ({
                value: item.id,
                text: item.name
            }))
            const label = {
                selected: true,
                // disabled: true,
                value: null,
                text: 'Choose Category'
            }

            return [label, ...options]
        }
    }
    
    return {
        ...toComputed(compute)
    }
}

export default setup
Snippet: computed as compute of .js

methods

When you have methods in your Component
export default {
    methods: {
        foundByTags(queryElements, item) {
            let found = true

            queryElements.forEach(categoryId => {
                let foundIndex = item.categories.findIndex(el => el['id'] == categoryId)
                found *= foundIndex > -1
            })

            return found
        }
    }
}
Snippet: methods of .vue

Change methods to:

const setup = props => {
    // methods
    const methods = {
        foundByTags(queryElements, item) {
            let found = true

            queryElements.forEach(categoryId => {
                let foundIndex = item.categories.findIndex(el => el['id'] == categoryId)
                found *= foundIndex > -1
            })

            return found
        }
    }
    
    return {
        ...methods
    }
}

export default setup
Snippet: methods of .js

Combined result as a module

import { reactive, toRefs, computed } from '@vue/composition-api'

const toComputed = object => Object.entries(object).reduce((acc, [key, method]) => {
    acc[key] = computed(method)
    return acc
}, {})

const setup = props => {
    // data
    const state = reactive({
        list: [],
        filters: []
    })
    // computed
    const compute = {
        selected: {
            get() {
                return this.filters[0]
            },
            set(value) {
                // console.log({ value })
                if (value == undefined) {
                    this.filters = []
                    return
                }

                const { filters } = this
                const index = filters.indexOf(value)

                if (index > 0) {
                    filters.unshift(...filters.splice(index, 1))
                } else {
                    filters.splice(0, 1, value)
                }
            }
        },
        mappedOptionsFromUniqueTagsFromList() {
            const options = this.uniqueTagsFromList.map(item => ({
                value: item.id,
                text: item.name
            }))
            const label = {
                selected: true,
                // disabled: true,
                value: null,
                text: 'Choose Category'
            }

            return [label, ...options]
        }
    }
    // methods
    const methods = {
        foundByTags(queryElements, item) {
            let found = true

            queryElements.forEach(categoryId => {
                let foundIndex = item.categories.findIndex(el => el['id'] == categoryId)
                found *= foundIndex > -1
            })

            return found
        }
    }
    
    return {
        ...toRefs(state),
        ...toComputed(compute),
        ...methods
    }
}

export default setup
File: lib/compostion/setup-articles.js
import setup from '../../lib/composition/setup-articles.js'

export default {
    name: 'Articles'
    setup,
    // Life Cycle Hooks
    mount () {}
}
File: vue/components/articles.vue

Mirgrate from Vue Component to Composite API

Before

<template>
	<div class="article-overview">
		<div class="main__inner">
			<header class="article-overview__header">
				<h1 class="article-overview__title"><slot name="title" /></h1>
				<span class="u-flex u-flex-cell">
					<SelectOptions
						class="u-flex-cell article-overview__select"
						:options="mappedOptionsFromUniqueTagsFromList"
						v-model="selected"
						v-on:change="onChangeCategory"
					/>
					<object-pulldown class="u-flex-cell filter-options" title="Filter">
						<div class="u-flex-cell">
							<tag-group
								class="filter-type-item"
								:query-items="queryItemsFromFilters"
								:filtered-items="
									differenceQueryItemsWithUniqueTagsFromFilteredList
								"
								:on-remove-tag-from-filter="onRemoveFromFilters"
								:on-add-tag-to-filter="onAddToFilters"
							>
							</tag-group>
						</div>
					</object-pulldown>
				</span>
			</header>
			<div class="article-overview__count">{{ filteredList.length }}</div>
			<div class="post-feed">
				<Card
					class="post-card"
					v-for="(item, index) in filteredList"
					:key="item.id"
					:item="item"
					:class="{ 'card--masthead': !index }"
				/>
			</div>
		</div>
	</div>
</template>

<script>
const uniq = require('lodash/fp/uniq'),
	uniqBy = require('lodash/uniqBy'),
	sortBy = require('lodash/sortBy'),
	differenceBy = require('lodash/differenceBy'),
	dayjs = require('dayjs')

const urlQueryToParams = require('../lib/urlQueryToParams'),
	changeUrlPushStateFromFilters = require('../lib/changeUrlPushStateFromFilters'),
	milliseconds = require('../lib/milliseconds')

import Card from './Card'
import SelectOptions from './SelectOptions'
import ObjectPulldown from './ObjectPulldown'
import TagGroup from './TagGroup'

export default {
	name: 'ArticleOverviewAlternative',
	components: {
		SelectOptions,
		ObjectPulldown,
		TagGroup,
		Card
	},
	props: {
		endpoint: String
	},
	data() {
		return {
			list: [],
			filters: []
		}
	},
	computed: {
		selected: {
			get() {
				return this.filters[0]
			},
			set(value) {
				if (value == undefined) {
					this.filters = []
					return
				}

				const { filters } = this
				const index = filters.indexOf(value)

				if (index > 0) {
					filters.unshift(...filters.splice(index, 1))
				} else {
					filters.splice(0, 1, value)
				}
			}
		},
		mappedOptionsFromUniqueTagsFromList() {
			const options = this.uniqueTagsFromList.map(item => ({
				value: item.id,
				text: item.name
			}))
			const label = {
				selected: true,
				// disabled: true,
				value: null,
				text: 'Choose Category'
			}

			return [label, ...options]
		},
		queryItemsFromFilters() {
			return this.filters.map(value => ({
				id: +value,
				name: (() => {
					const found = this.uniqueTagsFromList.find(({ id }) => id == value)
					return found ? found.name : value
				})()
			}))
		},
		uniqueTagsFromList() {
			const filters = this.list.reduce(
				(acc, item) => [...acc, ...item.categories],
				[]
			)

			return sortBy(uniqBy(filters, 'id'), 'id')
		},
		filteredList() {
			const {
				filters: queryElements,
				list,
				foundByTags,
				onlyShowPastPublishedAtDate,
				sortByPublishedAt
			} = this

			if (!queryElements.length)
				return onlyShowPastPublishedAtDate(sortByPublishedAt(list))
			const filtered = list.filter(item => foundByTags(queryElements, item))

			return onlyShowPastPublishedAtDate(sortByPublishedAt(filtered))
		},
		uniqueTagsFromFilteredList() {
			const filters = this.filteredList.reduce(
				(acc, item) => [...acc, ...item.categories],
				[]
			)

			return sortBy(uniqBy(filters, 'id'), 'id')
		},
		differenceQueryItemsWithUniqueTagsFromFilteredList() {
			const { uniqueTagsFromFilteredList, queryItemsFromFilters } = this
			return differenceBy(
				uniqueTagsFromFilteredList,
				queryItemsFromFilters,
				'id'
			)
		}
	},
	methods: {
		foundByTags(queryElements, item) {
			let found = true

			queryElements.forEach(categoryId => {
				let foundIndex = item.categories.findIndex(el => el['id'] == categoryId)
				found *= foundIndex > -1
			})

			return found
		},
		setFiltersFromQueryUrl() {
			const queryString = location.search
			const { filters } = urlQueryToParams(queryString)
			this.filters = Array.isArray(filters)
				? filters.map(i => +i)
				: filters === (null || undefined)
				? []
				: [+filters]
		},
		sortByPublishedAt(collection) {
			return collection.sort(
				(a, b) =>
					milliseconds(b['published_at']) - milliseconds(a['published_at'])
			)
		},
		onlyShowPastPublishedAtDate(collection) {
			return collection.filter(
				item => milliseconds(item['published_at']) < dayjs().valueOf()
			)
		},
		// Buttons Events
		onChangeCategory() {
			const value = this.selected
			this.filters = value ? [value] : []
			changeUrlPushStateFromFilters({ filters: value })
		},
		onAddToFilters(categoryId) {
			const { filters } = this
			const foundIndex = filters.indexOf(categoryId)
			if (!(foundIndex > -1)) {
				filters.push(categoryId)
				changeUrlPushStateFromFilters({
					filters
				})
			}
		},
		onRemoveFromFilters(categoryId) {
			console.log('onRemoveFromFilters:', { categoryId })
			const { filters } = this
			const foundIndex = filters.indexOf(+categoryId)
			console.log({ foundIndex })
			if (foundIndex > -1) {
				filters.splice(foundIndex, 1)
				changeUrlPushStateFromFilters({
					filters
				})
			}
		},
		onPopstate(event) {
			const { filters } = event.state
			this.filters = filters || []
		}
	},
	// Life Cycle Hooks
	async beforeMount() {
		const response = await (await fetch(this.endpoint)).json()
		this.list = response
	},
	mounted() {
		window.onpopstate = this.onPopstate
		if (this.filters.length) {
			changeUrlPushStateFromFilters({
				filters: this.filters
			})
		} else {
			this.setFiltersFromQueryUrl()
		}
	}
}
</script>
File: js/vue/ArticleOverview.vue

After

import { computed } from '@vue/composition-api'

const toComputed = object => Object.entries(object).reduce((acc, [key, method]) => {
    acc[key] = computed(method)
    return acc
}, {})
Snippet: js/lib/composition/setup-articles.js
A snippet to easy convert your computed object to Vue Computed ones.
import { reactive, computed, toRefs } from '@vue/composition-api'

const uniq = require('lodash/fp/uniq'),
    uniqBy = require('lodash/uniqBy'),
    sortBy = require('lodash/sortBy'),
    differenceBy = require('lodash/differenceBy'),
    dayjs = require('dayjs')

const urlQueryToParams = require('../urlQueryToParams'),
    changeUrlPushStateFromFilters = require('../changeUrlPushStateFromFilters'),
    milliseconds = require('../milliseconds')

const toComputed = object => Object.entries(object).reduce((acc, [key, method]) => {
    acc[key] = computed(method)
    return acc
}, {})

const setup = props => {
    // data
    const state = reactive({
        list: [],
        filters: []
    })

    // computed
    const compute = {
        selected: {
            get() {
                return state.filters[0]
            },
            set(value) {
                // console.log({ value })
                if (value == undefined) {
                    state.filters = []
                    return
                }

                const { filters } = state
                const index = filters.indexOf(value)

                if (index > 0) {
                    filters.unshift(...filters.splice(index, 1))
                } else {
                    filters.splice(0, 1, value)
                }
            }
        },
        mappedOptionsFromUniqueTagsFromList() {
            const options = this.uniqueTagsFromList.map(item => ({
                value: item.id,
                text: item.name
            }))
            const label = {
                selected: true,
                // disabled: true,
                value: null,
                text: 'Choose Category'
            }

            return [label, ...options]
        },
        queryItemsFromFilters() {
            return state.filters.map(value => ({
                id: +value,
                name: (() => {
                    const found = this.uniqueTagsFromList.find(({ id }) => id == value)
                    return found ? found.name : value
                })()
            }))
        },
        uniqueTagsFromList() {
            const filters = state.list.reduce(
                (acc, item) => [...acc, ...item.categories],
                []
            )

            return sortBy(uniqBy(filters, 'id'), 'id')
        },
        filteredList() {
            const {
                filters: queryElements,
                list
            } = state
            const {
                foundByTags,
                onlyShowPastPublishedAtDate,
                sortByPublishedAt
            } = methods


            if (!queryElements.length)
                return onlyShowPastPublishedAtDate(sortByPublishedAt(list))
            const filtered = list.filter(item => foundByTags(queryElements, item))

            return onlyShowPastPublishedAtDate(sortByPublishedAt(filtered))
        },
        uniqueTagsFromFilteredList() {
            const filters = this.filteredList.reduce(
                (acc, item) => [...acc, ...item.categories],
                []
            )

            return sortBy(uniqBy(filters, 'id'), 'id')
        },
        differenceQueryItemsWithUniqueTagsFromFilteredList() {
            const { uniqueTagsFromFilteredList, queryItemsFromFilters } = this
            return differenceBy(
                uniqueTagsFromFilteredList,
                queryItemsFromFilters,
                'id'
            )
        }
    }

    // methods
    const methods = {
        foundByTags(queryElements, item) {
            let found = true

            queryElements.forEach(categoryId => {
                let foundIndex = item.categories.findIndex(el => el['id'] == categoryId)
                found *= foundIndex > -1
            })

            return found
        },
        setFiltersFromQueryUrl() {
            const queryString = location.search
            const { filters } = urlQueryToParams(queryString)
            state.filters = Array.isArray(filters)
                ? uniq(filters).map(i => +i)
                : filters === (null || undefined)
                    ? []
                    : [+filters]
        },
        sortByPublishedAt(collection) {
            return collection.sort(
                (a, b) =>
                    milliseconds(b['published_at']) - milliseconds(a['published_at'])
            )
        },
        onlyShowPastPublishedAtDate(collection) {
            return collection.filter(
                item => milliseconds(item['published_at']) < dayjs().valueOf()
            )
        },
        // Buttons Events
        onChangeCategory() {
            const { filters } = state
            changeUrlPushStateFromFilters({ filters })
        },
        onAddToFilters(categoryId) {
            const { filters } = state
            const foundIndex = filters.indexOf(categoryId)
            if (!(foundIndex > -1)) {
                filters.push(categoryId)
                changeUrlPushStateFromFilters({
                    filters
                })
            }
        },
        onRemoveFromFilters(categoryId) {
            console.log('onRemoveFromFilters:', { categoryId })
            const { filters } = state
            const foundIndex = filters.indexOf(+categoryId)
            console.log({ foundIndex })
            if (foundIndex > -1) {
                filters.splice(foundIndex, 1)
                changeUrlPushStateFromFilters({
                    filters
                })
            }
        },
        onPopstate(event) {
            const { filters } = event.state
            state.filters = filters || []
        }
    }

    // Spread Operator
    return {
        ...toRefs(state),
        ...toComputed(compute),
        ...methods
    }
}

export default setup
File: js/lib/composition/setup-articles.js
- Taking out data as state, computed as compute and methods and local dependencies.
- Correct reference this to state or methods or leave as this.
<template>
	<div class="article-overview">
		<div class="main__inner">
			<header class="article-overview__header">
				<h1 class="article-overview__title"><slot name="title" /></h1>
				<span class="u-flex u-flex-cell">
					<SelectOptions
						class="u-flex-cell article-overview__select"
						:options="mappedOptionsFromUniqueTagsFromList"
						v-model="selected"
						v-on:change="onChangeCategory"
					/>
					<object-pulldown class="u-flex-cell filter-options" title="Filter">
						<div class="u-flex-cell">
							<tag-group
								class="filter-type-item"
								:query-items="queryItemsFromFilters"
								:filtered-items="
									differenceQueryItemsWithUniqueTagsFromFilteredList
								"
								:on-remove-tag-from-filter="onRemoveFromFilters"
								:on-add-tag-to-filter="onAddToFilters"
							>
							</tag-group>
						</div>
					</object-pulldown>
				</span>
			</header>
			<div class="article-overview__count">{{ filteredList.length }}</div>
			<div class="post-feed">
				<Card
					class="post-card"
					v-for="(item, index) in filteredList"
					:key="item.id"
					:item="item"
					:class="{ 'card--masthead': !index }"
				/>
			</div>
		</div>
	</div>
</template>

<script>
import Card from './Card'
import SelectOptions from './SelectOptions'
import ObjectPulldown from './ObjectPulldown'
import TagGroup from './TagGroup'

import setup from '../lib/composition/setup-articles'

export default {
	name: 'ArticleOverviewAlternative',
	components: {
		SelectOptions,
		ObjectPulldown,
		TagGroup,
		Card
	},
	props: {
		endpoint: String
	},
	setup,
	// Life Cycle Hooks
	async beforeMount() {
		const response = await (await fetch(this.endpoint)).json()
		this.list = response
	},
	mounted() {
		window.onpopstate = this.onPopstate
		if (this.filters.length) {
			changeUrlPushStateFromFilters({
				filters: this.filters
			})
		} else {
			this.setFiltersFromQueryUrl()
		}
	}
}
</script>
File: js/vue/ArticleOverview.vue
- Importing setup from library setup-articles.
- Removing libraries and refactor.