Skip to content

Instantly share code, notes, and snippets.

@yyx990803
Last active May 13, 2022 16:43
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • 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>
@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