Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Vue examples comparisons in 2.x and function-based APIs
<template>
<div id="demo">
<h1>Latest Vue.js Commits</h1>
<template v-for="branch in branches">
<input type="radio"
:id="branch"
:value="branch"
name="branch"
v-model="currentBranch">
<label :for="branch">{{ branch }}</label>
</template>
<p>vuejs/vue@{{ currentBranch }}</p>
<ul>
<li v-for="record in commits">
<a :href="record.html_url" target="_blank" class="commit">{{ record.sha.slice(0, 7) }}</a>
- <span class="message">{{ record.commit.message | truncate }}</span><br>
by <span class="author"><a :href="record.author.html_url" target="_blank">{{ record.commit.author.name }}</a></span>
at <span class="date">{{ record.commit.author.date | formatDate }}</span>
</li>
</ul>
</div>
</template>
<script>
// filters are omitted since they are the same
const apiURL = 'https://api.github.com/repos/vuejs/vue/commits?per_page=3&sha='
function fetch(url, cb) {
const xhr = new XMLHttpRequest()
xhr.open('GET', apiURL + url)
xhr.onload = function () {
cb(JSON.parse(xhr.responseText))
}
xhr.send()
}
// 2.x
const oldAPI = {
data() {
return {
branches: ['master', 'dev'],
currentBranch: 'master',
commits: null
}
},
created: function () {
this.fetchData()
},
watch: {
currentBranch: 'fetchData'
},
methods: {
fetchData: function () {
fetch(this.currentBranch, data => {
this.commits = data
})
}
}
}
const newAPI = {
setup() {
const currentBranch = value('master')
const commits = value(null)
watch(currentBranch, branch => {
fetch(branch, data => {
commits.value = data
})
})
return {
branches: ['master', 'dev'],
currentBranch,
commits
}
}
}
</script>
<template>
<div>
Count is {{ count }}, count * 2 is {{ double }}
<button @click="increment">+</button>
</div>
</template>
<script>
const oldAPI = {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
},
computed: {
double() {
return this.count * 2
}
}
}
const newAPI = {
setup() {
const count = value(0)
const double = computed(() => count.value * 2)
const increment = () => { count.value++ }
return {
count,
double,
increment
}
}
}
</script>
<template>
<table v-if="filteredData.length">
<thead>
<tr>
<th v-for="key in columns"
@click="sortBy(key)"
:class="{ active: sortKey == key }">
{{ key | capitalize }}
<span class="arrow" :class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
</span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="entry in filteredData">
<td v-for="key in columns">
{{entry[key]}}
</td>
</tr>
</tbody>
</table>
<p v-else>No matches found.</p>
</template>
<script>
const oldAPI = {
props: {
data: Array,
columns: Array,
filterKey: String
},
data: function () {
var sortOrders = {}
this.columns.forEach(function (key) {
sortOrders[key] = 1
})
return {
sortKey: '',
sortOrders: sortOrders
}
},
computed: {
filteredData: function () {
var sortKey = this.sortKey
var filterKey = this.filterKey && this.filterKey.toLowerCase()
var order = this.sortOrders[sortKey] || 1
var data = this.data
if (filterKey) {
data = data.filter(function (row) {
return Object.keys(row).some(function (key) {
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
})
})
}
if (sortKey) {
data = data.slice().sort(function (a, b) {
a = a[sortKey]
b = b[sortKey]
return (a === b ? 0 : a > b ? 1 : -1) * order
})
}
return data
}
},
methods: {
sortBy: function (key) {
this.sortKey = key
this.sortOrders[key] = this.sortOrders[key] * -1
}
}
}
const newAPI = {
props: {
data: Array,
columns: Array,
filterKey: String
},
setup(props) {
const sortKey = value('')
const sortOrders = value({})
props.columns.forEach(key => {
sortOrders.value[key] = 1
})
const filteredData = computed(() => {
const filterKey = props.filterKey && props.filterKey.toLowerCase()
const sortKeyValue = sortKey.value
const order = sortOrders.value[sortKeyValue] || 1
let data = props.data
if (filterKey) {
data = data.filter(row => {
return Object.keys(row).some(key => {
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
})
})
}
if (sortKeyValue) {
data = data.slice().sort((a, b) => {
a = a[sortKeyValue]
b = b[sortKeyValue]
return (a === b ? 0 : a > b ? 1 : -1) * order
})
}
return data
})
function sortBy(key) {
sortKey.value = key
sortOrders.value[key] = sortOrders.value[key] * -1
}
return {
sortKey,
sortOrders,
filteredData,
sortBy
}
}
}
</script>
<template>
<div>
<section class="todoapp">
<header class="header">
<h1>todos</h1>
<input class="new-todo"
autofocus autocomplete="off"
placeholder="What needs to be done?"
v-model="newTodo"
@keyup.enter="addTodo">
</header>
<section class="main" v-show="todos.length" v-cloak>
<input class="toggle-all" type="checkbox" v-model="allDone">
<ul class="todo-list">
<li v-for="todo in filteredTodos"
class="todo"
:key="todo.id"
:class="{ completed: todo.completed, editing: todo == editedTodo }">
<div class="view">
<input class="toggle" type="checkbox" v-model="todo.completed">
<label @dblclick="editTodo(todo)">{{ todo.title }}</label>
<button class="destroy" @click="removeTodo(todo)"></button>
</div>
<input class="edit" type="text"
v-model="todo.title"
v-todo-focus="todo == editedTodo"
@blur="doneEdit(todo)"
@keyup.enter="doneEdit(todo)"
@keyup.esc="cancelEdit(todo)">
</li>
</ul>
</section>
<footer class="footer" v-show="todos.length" v-cloak>
<span class="todo-count">
<strong>{{ remaining }}</strong> {{ remaining | pluralize }} left
</span>
<ul class="filters">
<li><a href="#/all" :class="{ selected: visibility == 'all' }">All</a></li>
<li><a href="#/active" :class="{ selected: visibility == 'active' }">Active</a></li>
<li><a href="#/completed" :class="{ selected: visibility == 'completed' }">Completed</a></li>
</ul>
<button class="clear-completed" @click="removeCompleted" v-show="todos.length > remaining">
Clear completed
</button>
</footer>
</section>
<footer class="info">
<p>Double-click to edit a todo</p>
<p>Written by <a href="http://evanyou.me">Evan You</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
</div>
</template>
<script>
import { computed } from "../../packages/reactivity/src";
var STORAGE_KEY = 'todos-vuejs-2.0'
var todoStorage = {
fetch: function () {
// omitted since they are the same across implementations
},
save: function (todos) {
// omitted since they are the same across implementations
}
}
// visibility filters
var filters = {
// omitted since they are the same across implementations
}
const oldAPI = {
// app initial state
data: {
todos: todoStorage.fetch(),
newTodo: '',
editedTodo: null,
visibility: 'all'
},
// watch todos change for localStorage persistence
watch: {
todos: {
handler: function (todos) {
todoStorage.save(todos)
},
deep: true
}
},
// computed properties
// https://vuejs.org/guide/computed.html
computed: {
filteredTodos: function () {
return filters[this.visibility](this.todos)
},
remaining: function () {
return filters.active(this.todos).length
},
allDone: {
get: function () {
return this.remaining === 0
},
set: function (value) {
this.todos.forEach(function (todo) {
todo.completed = value
})
}
}
},
// methods that implement data logic.
// note there's no DOM manipulation here at all.
methods: {
addTodo: function () {
var value = this.newTodo && this.newTodo.trim()
if (!value) {
return
}
this.todos.push({
id: todoStorage.uid++,
title: value,
completed: false
})
this.newTodo = ''
},
removeTodo: function (todo) {
this.todos.splice(this.todos.indexOf(todo), 1)
},
editTodo: function (todo) {
this.beforeEditCache = todo.title
this.editedTodo = todo
},
doneEdit: function (todo) {
if (!this.editedTodo) {
return
}
this.editedTodo = null
todo.title = todo.title.trim()
if (!todo.title) {
this.removeTodo(todo)
}
},
cancelEdit: function (todo) {
this.editedTodo = null
todo.title = this.beforeEditCache
},
removeCompleted: function () {
this.todos = filters.active(this.todos)
}
}
}
const newAPI = {
setup() {
const todos = value(todoStorage.fetch())
const newTodo = value('')
const editedTodo = value(null)
const visibility = value('all')
let beforeEditCache = ''
watch(todos, todos => {
todoStorage.save(todos)
}, { deep: true })
const computeds = {
filteredTodos: computed(() => filters[visibility.value](todos.value)),
remaining: computed(() => filters.active(todos.value).length),
allDone: computed(
() => computeds.remaining.value === 0,
() => todos.value.forEach(todo => {
todo.completed = true
})
)
}
const methods = {
addTodo() {
const value = newTodo.value && newTodo.value.trim()
if (value) {
todos.value.push({
id: todoStorage.uid++,
title: value,
completed: false
})
}
newTodo.value = ''
},
removeTodo(todo) {
todos.value = todos.value.filter(t => t !== todo)
},
editTodo(todo) {
beforeEditCache = todo.title
editedTodo.value = todo
},
doneEdit(todo) {
if (editedTodo.value) {
editedTodo.value = null
todo.title = todo.title.trim()
if (!todo.title) {
methods.removeTodo(todo)
}
}
},
cancelEdit() {
todo.title = todo.title.trim()
if (!todo.title) {
methods.removeTodo(todo)
}
},
removeCompleted() {
todos.value = filters.active(todos.value)
}
}
return {
todos,
newTodo,
editedTodo,
visibility,
...computeds,
...methods
}
}
}
</script>
@Mintonne

This comment has been minimized.

Copy link

@Mintonne Mintonne commented Jun 21, 2019

In the todomvc example, beforeEditCache state is not declared in the old api section.

@reegodev

This comment has been minimized.

Copy link

@reegodev reegodev commented Jun 21, 2019

In the todomvc example, beforeEditCache state is not declared in the old api section.

Because it's not meant to be reactive. In 2.x you need to use an instance property to share a value between methods, while in 3.x you can just use a simple variable declared in the outer scope

@carlmjohnson

This comment has been minimized.

Copy link

@carlmjohnson carlmjohnson commented Jun 21, 2019

Yes, but cancelEdit is different in your 3.x example and doesn't restore the cache value.

@cdmoro

This comment has been minimized.

Copy link

@cdmoro cdmoro commented Jun 26, 2019

Hello! How can I use Vuex and Vue-router inside setup function? Thanks!

@anoop-ananthan

This comment has been minimized.

Copy link

@anoop-ananthan anoop-ananthan commented Nov 11, 2019

Here starts the decline of a good languge 😞

@martinshaw

This comment has been minimized.

Copy link

@martinshaw martinshaw commented Apr 7, 2020

I was ready to be outraged by a syntax change. But I can honestly see the benefits of the new API. Although it may have a slightly increased learning curve for beginners. Either way people rarely take the time to learn how to best structure their components and to make the best use of Vue architecture.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.