Skip to content

Instantly share code, notes, and snippets.

@yyx990803
Last active December 8, 2019 09:18
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yyx990803/936668677fab153ba6a726b23eaa02ba to your computer and use it in GitHub Desktop.
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.
/* 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
/* 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