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

commented Jun 21, 2019

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

@matteo-rigon

This comment has been minimized.

Copy link

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

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

commented Jun 26, 2019

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

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.