Skip to content

Instantly share code, notes, and snippets.

@yyx990803
Last active May 13, 2022 16:43
Show Gist options
  • Save yyx990803/762ec427882a61be3e4affe02f8af555 to your computer and use it in GitHub Desktop.
Save yyx990803/762ec427882a61be3e4affe02f8af555 to your computer and use it in GitHub Desktop.
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
Copy link

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

@reegodev
Copy link

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

@earthboundkid
Copy link

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

@cdmoro
Copy link

cdmoro commented Jun 26, 2019

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

@anoop-ananthan
Copy link

Here starts the decline of a good languge 😞

@martinshaw
Copy link

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