Last active
December 8, 2019 09:18
-
-
Save yyx990803/936668677fab153ba6a726b23eaa02ba to your computer and use it in GitHub Desktop.
Svelte/Vue generated code size comparison of the TodoMVC implementation. Note: (1) imports are replaced with a const declaration to enable compression. (2) The comparison is between components' "own code" and doesn't include imported runtime code.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* App.svelte generated by Svelte v3.14.1 */ | |
/* After terser compression: min:6.00kb / gzip:2.43kb / brotli:2.15kb */ | |
const { | |
SvelteComponent, | |
append, | |
attr, | |
destroy_block, | |
detach, | |
element, | |
empty, | |
init, | |
insert, | |
listen, | |
noop, | |
run_all, | |
safe_not_equal, | |
set_data, | |
space, | |
text, | |
update_keyed_each | |
} = Svelte | |
function get_each_context(ctx, list, i) { | |
const child_ctx = Object.create(ctx); | |
child_ctx.item = list[i]; | |
child_ctx.each_value = list; | |
child_ctx.index = i; | |
return child_ctx; | |
} | |
// (83:0) {#if items.length > 0} | |
function create_if_block(ctx) { | |
let section; | |
let input; | |
let input_checked_value; | |
let t0; | |
let label; | |
let t2; | |
let ul0; | |
let each_blocks = []; | |
let each_1_lookup = new Map(); | |
let t3; | |
let footer; | |
let span; | |
let strong; | |
let t4; | |
let t5; | |
let t6_value = (ctx.numActive === 1 ? "item" : "items") + ""; | |
let t6; | |
let t7; | |
let t8; | |
let ul1; | |
let li0; | |
let a0; | |
let t9; | |
let a0_class_value; | |
let t10; | |
let li1; | |
let a1; | |
let t11; | |
let a1_class_value; | |
let t12; | |
let li2; | |
let a2; | |
let t13; | |
let a2_class_value; | |
let t14; | |
let dispose; | |
let each_value = ctx.filtered; | |
const get_key = ctx => ctx.item.id; | |
for (let i = 0; i < each_value.length; i += 1) { | |
let child_ctx = get_each_context(ctx, each_value, i); | |
let key = get_key(child_ctx); | |
each_1_lookup.set(key, each_blocks[i] = create_each_block(key, child_ctx)); | |
} | |
let if_block = ctx.numCompleted && create_if_block_1(ctx); | |
return { | |
c() { | |
section = element("section"); | |
input = element("input"); | |
t0 = space(); | |
label = element("label"); | |
label.textContent = "Mark all as complete"; | |
t2 = space(); | |
ul0 = element("ul"); | |
for (let i = 0; i < each_blocks.length; i += 1) { | |
each_blocks[i].c(); | |
} | |
t3 = space(); | |
footer = element("footer"); | |
span = element("span"); | |
strong = element("strong"); | |
t4 = text(ctx.numActive); | |
t5 = space(); | |
t6 = text(t6_value); | |
t7 = text(" left"); | |
t8 = space(); | |
ul1 = element("ul"); | |
li0 = element("li"); | |
a0 = element("a"); | |
t9 = text("All"); | |
t10 = space(); | |
li1 = element("li"); | |
a1 = element("a"); | |
t11 = text("Active"); | |
t12 = space(); | |
li2 = element("li"); | |
a2 = element("a"); | |
t13 = text("Completed"); | |
t14 = space(); | |
if (if_block) if_block.c(); | |
attr(input, "id", "toggle-all"); | |
attr(input, "class", "toggle-all"); | |
attr(input, "type", "checkbox"); | |
input.checked = input_checked_value = ctx.numCompleted === ctx.items.length; | |
attr(label, "for", "toggle-all"); | |
attr(ul0, "class", "todo-list"); | |
attr(span, "class", "todo-count"); | |
attr(a0, "class", a0_class_value = ctx.currentFilter === "all" ? "selected" : ""); | |
attr(a0, "href", "#/"); | |
attr(a1, "class", a1_class_value = ctx.currentFilter === "active" ? "selected" : ""); | |
attr(a1, "href", "#/active"); | |
attr(a2, "class", a2_class_value = ctx.currentFilter === "completed" ? "selected" : ""); | |
attr(a2, "href", "#/completed"); | |
attr(ul1, "class", "filters"); | |
attr(footer, "class", "footer"); | |
attr(section, "class", "main"); | |
dispose = listen(input, "change", ctx.toggleAll); | |
}, | |
m(target, anchor) { | |
insert(target, section, anchor); | |
append(section, input); | |
append(section, t0); | |
append(section, label); | |
append(section, t2); | |
append(section, ul0); | |
for (let i = 0; i < each_blocks.length; i += 1) { | |
each_blocks[i].m(ul0, null); | |
} | |
append(section, t3); | |
append(section, footer); | |
append(footer, span); | |
append(span, strong); | |
append(strong, t4); | |
append(span, t5); | |
append(span, t6); | |
append(span, t7); | |
append(footer, t8); | |
append(footer, ul1); | |
append(ul1, li0); | |
append(li0, a0); | |
append(a0, t9); | |
append(ul1, t10); | |
append(ul1, li1); | |
append(li1, a1); | |
append(a1, t11); | |
append(ul1, t12); | |
append(ul1, li2); | |
append(li2, a2); | |
append(a2, t13); | |
append(footer, t14); | |
if (if_block) if_block.m(footer, null); | |
}, | |
p(changed, ctx) { | |
if ((changed.numCompleted || changed.items) && input_checked_value !== (input_checked_value = ctx.numCompleted === ctx.items.length)) { | |
input.checked = input_checked_value; | |
} | |
const each_value = ctx.filtered; | |
each_blocks = update_keyed_each(each_blocks, changed, get_key, 1, ctx, each_value, each_1_lookup, ul0, destroy_block, create_each_block, null, get_each_context); | |
if (changed.numActive) set_data(t4, ctx.numActive); | |
if (changed.numActive && t6_value !== (t6_value = (ctx.numActive === 1 ? "item" : "items") + "")) set_data(t6, t6_value); | |
if (changed.currentFilter && a0_class_value !== (a0_class_value = ctx.currentFilter === "all" ? "selected" : "")) { | |
attr(a0, "class", a0_class_value); | |
} | |
if (changed.currentFilter && a1_class_value !== (a1_class_value = ctx.currentFilter === "active" ? "selected" : "")) { | |
attr(a1, "class", a1_class_value); | |
} | |
if (changed.currentFilter && a2_class_value !== (a2_class_value = ctx.currentFilter === "completed" ? "selected" : "")) { | |
attr(a2, "class", a2_class_value); | |
} | |
if (ctx.numCompleted) { | |
if (if_block) { | |
if_block.p(changed, ctx); | |
} else { | |
if_block = create_if_block_1(ctx); | |
if_block.c(); | |
if_block.m(footer, null); | |
} | |
} else if (if_block) { | |
if_block.d(1); | |
if_block = null; | |
} | |
}, | |
d(detaching) { | |
if (detaching) detach(section); | |
for (let i = 0; i < each_blocks.length; i += 1) { | |
each_blocks[i].d(); | |
} | |
if (if_block) if_block.d(); | |
dispose(); | |
} | |
}; | |
} | |
// (97:5) {#if editing === index} | |
function create_if_block_2(ctx) { | |
let input; | |
let input_value_value; | |
let dispose; | |
return { | |
c() { | |
input = element("input"); | |
input.value = input_value_value = ctx.item.description; | |
attr(input, "id", "edit"); | |
attr(input, "class", "edit"); | |
input.autofocus = true; | |
dispose = [ | |
listen(input, "keydown", ctx.handleEdit), | |
listen(input, "blur", ctx.submit) | |
]; | |
}, | |
m(target, anchor) { | |
insert(target, input, anchor); | |
input.focus(); | |
}, | |
p(changed, ctx) { | |
if (changed.filtered && input_value_value !== (input_value_value = ctx.item.description)) { | |
input.value = input_value_value; | |
} | |
}, | |
d(detaching) { | |
if (detaching) detach(input); | |
run_all(dispose); | |
} | |
}; | |
} | |
// (89:3) {#each filtered as item, index (item.id)} | |
function create_each_block(key_1, ctx) { | |
let li; | |
let div; | |
let input; | |
let t0; | |
let label; | |
let t1_value = ctx.item.description + ""; | |
let t1; | |
let t2; | |
let button; | |
let t3; | |
let t4; | |
let li_class_value; | |
let dispose; | |
function input_change_handler() { | |
ctx.input_change_handler.call(input, ctx); | |
} | |
function dblclick_handler(...args) { | |
return ctx.dblclick_handler(ctx, ...args); | |
} | |
function click_handler(...args) { | |
return ctx.click_handler(ctx, ...args); | |
} | |
let if_block = ctx.editing === ctx.index && create_if_block_2(ctx); | |
return { | |
key: key_1, | |
first: null, | |
c() { | |
li = element("li"); | |
div = element("div"); | |
input = element("input"); | |
t0 = space(); | |
label = element("label"); | |
t1 = text(t1_value); | |
t2 = space(); | |
button = element("button"); | |
t3 = space(); | |
if (if_block) if_block.c(); | |
t4 = space(); | |
attr(input, "class", "toggle"); | |
attr(input, "type", "checkbox"); | |
attr(button, "class", "destroy"); | |
attr(div, "class", "view"); | |
attr(li, "class", li_class_value = "" + ((ctx.item.completed ? "completed" : "") + " " + (ctx.editing === ctx.index ? "editing" : ""))); | |
dispose = [ | |
listen(input, "change", input_change_handler), | |
listen(label, "dblclick", dblclick_handler), | |
listen(button, "click", click_handler) | |
]; | |
this.first = li; | |
}, | |
m(target, anchor) { | |
insert(target, li, anchor); | |
append(li, div); | |
append(div, input); | |
input.checked = ctx.item.completed; | |
append(div, t0); | |
append(div, label); | |
append(label, t1); | |
append(div, t2); | |
append(div, button); | |
append(li, t3); | |
if (if_block) if_block.m(li, null); | |
append(li, t4); | |
}, | |
p(changed, new_ctx) { | |
ctx = new_ctx; | |
if (changed.filtered) { | |
input.checked = ctx.item.completed; | |
} | |
if (changed.filtered && t1_value !== (t1_value = ctx.item.description + "")) set_data(t1, t1_value); | |
if (ctx.editing === ctx.index) { | |
if (if_block) { | |
if_block.p(changed, ctx); | |
} else { | |
if_block = create_if_block_2(ctx); | |
if_block.c(); | |
if_block.m(li, t4); | |
} | |
} else if (if_block) { | |
if_block.d(1); | |
if_block = null; | |
} | |
if ((changed.filtered || changed.editing) && li_class_value !== (li_class_value = "" + ((ctx.item.completed ? "completed" : "") + " " + (ctx.editing === ctx.index ? "editing" : "")))) { | |
attr(li, "class", li_class_value); | |
} | |
}, | |
d(detaching) { | |
if (detaching) detach(li); | |
if (if_block) if_block.d(); | |
run_all(dispose); | |
} | |
}; | |
} | |
// (122:3) {#if numCompleted} | |
function create_if_block_1(ctx) { | |
let button; | |
let dispose; | |
return { | |
c() { | |
button = element("button"); | |
button.textContent = "Clear completed"; | |
attr(button, "class", "clear-completed"); | |
dispose = listen(button, "click", ctx.clearCompleted); | |
}, | |
m(target, anchor) { | |
insert(target, button, anchor); | |
}, | |
p: noop, | |
d(detaching) { | |
if (detaching) detach(button); | |
dispose(); | |
} | |
}; | |
} | |
function create_fragment(ctx) { | |
let header; | |
let h1; | |
let t1; | |
let input; | |
let t2; | |
let if_block_anchor; | |
let dispose; | |
let if_block = ctx.items.length > 0 && create_if_block(ctx); | |
return { | |
c() { | |
header = element("header"); | |
h1 = element("h1"); | |
h1.textContent = "todos"; | |
t1 = space(); | |
input = element("input"); | |
t2 = space(); | |
if (if_block) if_block.c(); | |
if_block_anchor = empty(); | |
attr(input, "class", "new-todo"); | |
attr(input, "placeholder", "What needs to be done?"); | |
input.autofocus = true; | |
attr(header, "class", "header"); | |
dispose = listen(input, "keydown", ctx.createNew); | |
}, | |
m(target, anchor) { | |
insert(target, header, anchor); | |
append(header, h1); | |
append(header, t1); | |
append(header, input); | |
insert(target, t2, anchor); | |
if (if_block) if_block.m(target, anchor); | |
insert(target, if_block_anchor, anchor); | |
input.focus(); | |
}, | |
p(changed, ctx) { | |
if (ctx.items.length > 0) { | |
if (if_block) { | |
if_block.p(changed, ctx); | |
} else { | |
if_block = create_if_block(ctx); | |
if_block.c(); | |
if_block.m(if_block_anchor.parentNode, if_block_anchor); | |
} | |
} else if (if_block) { | |
if_block.d(1); | |
if_block = null; | |
} | |
}, | |
i: noop, | |
o: noop, | |
d(detaching) { | |
if (detaching) detach(header); | |
if (detaching) detach(t2); | |
if (if_block) if_block.d(detaching); | |
if (detaching) detach(if_block_anchor); | |
dispose(); | |
} | |
}; | |
} | |
const ENTER_KEY = 13; | |
const ESCAPE_KEY = 27; | |
function uuid() { | |
return ("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx").replace(/[xy]/g, function (c) { | |
var r = Math.random() * 16 | 0, v = c == "x" ? r : r & 3 | 8; | |
return v.toString(16); | |
}); | |
} | |
function instance($$self, $$props, $$invalidate) { | |
let currentFilter = "all"; | |
let items = []; | |
let editing = null; | |
try { | |
$$invalidate("items", items = JSON.parse(localStorage.getItem("todos-svelte")) || []); | |
} catch(err) { | |
$$invalidate("items", items = []); | |
} | |
const updateView = () => { | |
$$invalidate("currentFilter", currentFilter = "all"); | |
if (window.location.hash === "#/active") { | |
$$invalidate("currentFilter", currentFilter = "active"); | |
} else if (window.location.hash === "#/completed") { | |
$$invalidate("currentFilter", currentFilter = "completed"); | |
} | |
}; | |
window.addEventListener("hashchange", updateView); | |
updateView(); | |
function clearCompleted() { | |
$$invalidate("items", items = items.filter(item => !item.completed)); | |
} | |
function remove(index) { | |
$$invalidate("items", items = items.slice(0, index).concat(items.slice(index + 1))); | |
} | |
function toggleAll(event) { | |
$$invalidate("items", items = items.map(item => ({ | |
id: item.id, | |
description: item.description, | |
completed: event.target.checked | |
}))); | |
} | |
function createNew(event) { | |
if (event.which === ENTER_KEY) { | |
$$invalidate("items", items = items.concat({ | |
id: uuid(), | |
description: event.target.value, | |
completed: false | |
})); | |
event.target.value = ""; | |
} | |
} | |
function handleEdit(event) { | |
if (event.which === ENTER_KEY) event.target.blur(); else if (event.which === ESCAPE_KEY) $$invalidate("editing", editing = null); | |
} | |
function submit(event) { | |
$$invalidate("items", items[editing].description = event.target.value, items); | |
$$invalidate("editing", editing = null); | |
} | |
function input_change_handler({ item }) { | |
item.completed = this.checked; | |
(($$invalidate("filtered", filtered), $$invalidate("currentFilter", currentFilter)), $$invalidate("items", items)); | |
} | |
const dblclick_handler = ({ index }) => $$invalidate("editing", editing = index); | |
const click_handler = ({ index }) => remove(index); | |
let filtered; | |
let numActive; | |
let numCompleted; | |
$$self.$$.update = (changed = { currentFilter: 1, items: 1 }) => { | |
if (changed.currentFilter || changed.items) { | |
$: $$invalidate("filtered", filtered = currentFilter === "all" | |
? items | |
: currentFilter === "completed" | |
? items.filter(item => item.completed) | |
: items.filter(item => !item.completed)); | |
} | |
if (changed.items) { | |
$: $$invalidate("numActive", numActive = items.filter(item => !item.completed).length); | |
} | |
if (changed.items) { | |
$: $$invalidate("numCompleted", numCompleted = items.filter(item => item.completed).length); | |
} | |
if (changed.items) { | |
$: try { | |
localStorage.setItem("todos-svelte", JSON.stringify(items)); | |
} catch(err) { | |
} | |
} | |
}; | |
return { | |
currentFilter, | |
items, | |
editing, | |
clearCompleted, | |
remove, | |
toggleAll, | |
createNew, | |
handleEdit, | |
submit, | |
filtered, | |
numActive, | |
numCompleted, | |
input_change_handler, | |
dblclick_handler, | |
click_handler | |
}; | |
} | |
class App extends SvelteComponent { | |
constructor(options) { | |
super(); | |
init(this, options, instance, create_fragment, safe_not_equal, {}); | |
} | |
} | |
// to avoid terser dropping it | |
window.App = App |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* render function generated by Vue 3 compiler @9e16ea3 */ | |
/* After terser compression: min:3.98kb / gzip:1.60kb / brotli:1.42kb */ | |
const { createVNode, vModelText, withKeys, withDirectives, openBlock, vModelCheckbox, renderList, createBlock, Fragment, toString, resolveDirective, createCommentVNode } = Vue | |
const _hoisted_1 = { class: "todoapp" } | |
const _hoisted_2 = { class: "header" } | |
const _hoisted_3 = createVNode("h1", null, "todos") | |
const _hoisted_4 = { | |
key: 0, | |
class: "main" | |
} | |
const _hoisted_5 = createVNode("label", { for: "toggle-all" }, "Mark all as complete") | |
const _hoisted_6 = { class: "todo-list" } | |
const _hoisted_7 = { class: "view" } | |
const _hoisted_8 = { | |
key: 0, | |
class: "footer" | |
} | |
const _hoisted_9 = { class: "todo-count" } | |
const _hoisted_10 = { class: "filters" } | |
function render() { | |
const _ctx = this | |
const _cache = _ctx.$cache | |
const _directive_todo_focus = resolveDirective("todo-focus") | |
return (openBlock(), createBlock("section", _hoisted_1, [ | |
createVNode("header", _hoisted_2, [ | |
_hoisted_3, | |
withDirectives(createVNode("input", { | |
class: "new-todo", | |
autofocus: "", | |
autocomplete: "off", | |
placeholder: "What needs to be done?", | |
modelValue: _ctx.state.newTodo, | |
"onUpdate:modelValue": _cache[1] || (_cache[1] = $event => (_ctx.state.newTodo = $event)), | |
onKeyup: _cache[2] || (_cache[2] = withKeys($event => (_ctx.addTodo($event)), ["enter"])) | |
}, null, 8 /* PROPS */, ["modelValue"]), [ | |
[vModelText, _ctx.state.newTodo] | |
]) | |
]), | |
(openBlock(), (_ctx.state.todos.length) | |
? createBlock("section", _hoisted_4, [ | |
withDirectives(createVNode("input", { | |
id: "toggle-all", | |
class: "toggle-all", | |
type: "checkbox", | |
modelValue: _ctx.state.allDone, | |
"onUpdate:modelValue": _cache[3] || (_cache[3] = $event => (_ctx.state.allDone = $event)) | |
}, null, 8 /* PROPS */, ["modelValue"]), [ | |
[vModelCheckbox, _ctx.state.allDone] | |
]), | |
_hoisted_5, | |
createVNode("ul", _hoisted_6, [ | |
(openBlock(false), createBlock(Fragment, null, renderList(_ctx.state.filteredTodos, (todo) => { | |
return (openBlock(), createBlock("li", { | |
class: ["todo", { completed: todo.completed, editing: todo === _ctx.state.editedTodo }], | |
key: todo.id | |
}, [ | |
createVNode("div", _hoisted_7, [ | |
withDirectives(createVNode("input", { | |
class: "toggle", | |
type: "checkbox", | |
modelValue: todo.completed, | |
"onUpdate:modelValue": $event => (todo.completed = $event) | |
}, null, 8 /* PROPS */, ["modelValue", "onUpdate:modelValue"]), [ | |
[vModelCheckbox, todo.completed] | |
]), | |
createVNode("label", { | |
onDblclick: $event => (_ctx.editTodo(todo)) | |
}, toString(todo.title), 9 /* TEXT, PROPS */, ["onDblclick"]), | |
createVNode("button", { | |
class: "destroy", | |
onClick: $event => (_ctx.removeTodo(todo)) | |
}, null, 8 /* PROPS */, ["onClick"]) | |
]), | |
withDirectives(createVNode("input", { | |
class: "edit", | |
type: "text", | |
modelValue: todo.title, | |
"onUpdate:modelValue": $event => (todo.title = $event), | |
onBlur: $event => (_ctx.doneEdit(todo)), | |
onKeyup: [ | |
withKeys($event => (_ctx.doneEdit(todo)), ["enter"]), | |
withKeys($event => (_ctx.cancelEdit(todo)), ["escape"]) | |
] | |
}, null, 8 /* PROPS */, ["modelValue", "onUpdate:modelValue", "onBlur", "onKeyup", "onKeyup"]), [ | |
[vModelText, todo.title], | |
[_directive_todo_focus, todo === _ctx.state.editedTodo] | |
]) | |
], 2 /* CLASS */)) | |
}), 64 /* KEYED_FRAGMENT */)) | |
]) | |
]) | |
: createCommentVNode("v-if", true)), | |
(openBlock(), (_ctx.state.todos.length) | |
? createBlock("footer", _hoisted_8, [ | |
createVNode("span", _hoisted_9, [ | |
createVNode("strong", null, toString(_ctx.state.remaining), 1 /* TEXT */), | |
createVNode("span", null, toString(_ctx.state.remainingText), 1 /* TEXT */) | |
]), | |
createVNode("ul", _hoisted_10, [ | |
createVNode("li", null, [ | |
createVNode("a", { | |
href: "#/all", | |
class: { selected: _ctx.state.visibility === 'all' } | |
}, "All", 2 /* CLASS */) | |
]), | |
createVNode("li", null, [ | |
createVNode("a", { | |
href: "#/active", | |
class: { selected: _ctx.state.visibility === 'active' } | |
}, "Active", 2 /* CLASS */) | |
]), | |
createVNode("li", null, [ | |
createVNode("a", { | |
href: "#/completed", | |
class: { selected: _ctx.state.visibility === 'completed' } | |
}, "Completed", 2 /* CLASS */) | |
]) | |
]), | |
(openBlock(), (_ctx.state.todos.length > _ctx.state.remaining) | |
? createBlock("button", { | |
key: 0, | |
class: "clear-completed", | |
onClick: _cache[4] || (_cache[4] = $event => (_ctx.removeCompleted($event))) | |
}, " Clear completed ") | |
: createCommentVNode("v-if", true)) | |
]) | |
: createCommentVNode("v-if", true)) | |
])) | |
} | |
const { createApp, reactive, computed, watch, onMounted, onUnmounted } = Vue | |
const STORAGE_KEY = 'todos-vuejs-3.x' | |
const todoStorage = { | |
fetch () { | |
const todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]') | |
todos.forEach((todo, index) => { | |
todo.id = index | |
}) | |
todoStorage.uid = todos.length | |
return todos | |
}, | |
save (todos) { | |
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos)) | |
} | |
} | |
const filters = { | |
all (todos) { | |
return todos | |
}, | |
active (todos) { | |
return todos.filter((todo) => { | |
return !todo.completed | |
}) | |
}, | |
completed (todos) { | |
return todos.filter(function (todo) { | |
return todo.completed | |
}) | |
} | |
} | |
function pluralize (n) { | |
return n === 1 ? 'item' : 'items' | |
} | |
const App = { | |
render, | |
setup () { | |
const state = reactive({ | |
todos: todoStorage.fetch(), | |
editedTodo: null, | |
newTodo: '', | |
beforeEditCache: '', | |
visibility: 'all', | |
remaining: computed(() => { | |
return filters.active(state.todos).length | |
}), | |
remainingText: computed(() => { | |
return ` ${pluralize(state.remaining)} left` | |
}), | |
filteredTodos: computed(() => { | |
return filters[state.visibility](state.todos) | |
}), | |
allDone: computed({ | |
get: function () { | |
return state.remaining === 0 | |
}, | |
set: function (value) { | |
state.todos.forEach((todo) => { | |
todo.completed = value | |
}) | |
} | |
}) | |
}) | |
watch(() => { | |
todoStorage.save(state.todos) | |
}) | |
onMounted(() => { | |
window.addEventListener('hashchange', onHashChange) | |
onHashChange() | |
}) | |
onUnmounted(() => { | |
window.removeEventListener('hashchange', onHashChange) | |
}) | |
function onHashChange () { | |
const visibility = window.location.hash.replace(/#\/?/, '') | |
if (filters[visibility]) { | |
state.visibility = visibility | |
} else { | |
window.location.hash = '' | |
state.visibility = 'all' | |
} | |
} | |
function addTodo () { | |
const value = state.newTodo && state.newTodo.trim() | |
if (!value) { | |
return | |
} | |
state.todos.push({ | |
id: todoStorage.uid++, | |
title: value, | |
completed: false | |
}) | |
state.newTodo = '' | |
} | |
function removeTodo (todo) { | |
state.todos.splice(state.todos.indexOf(todo), 1) | |
} | |
function editTodo (todo) { | |
state.beforeEditCache = todo.title | |
state.editedTodo = todo | |
} | |
function doneEdit (todo) { | |
if (!state.editedTodo) { | |
return | |
} | |
state.editedTodo = null | |
todo.title = todo.title.trim() | |
if (!todo.title) { | |
removeTodo(todo) | |
} | |
} | |
function cancelEdit (todo) { | |
state.editedTodo = null | |
todo.title = state.beforeEditCache | |
} | |
function removeCompleted () { | |
state.todos = filters.active(state.todos) | |
} | |
return { | |
state, | |
addTodo, | |
removeTodo, | |
editTodo, | |
doneEdit, | |
cancelEdit, | |
removeCompleted | |
} | |
}, | |
directives: { | |
'todo-focus': (el, { value }) => { | |
if (value) { | |
el.focus() | |
} | |
} | |
} | |
} | |
createApp().mount(App, '#app') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment