Slash vs Vue
Slash vs Vue
Section titled “Slash vs Vue”Esta página compara Slash e Vue em termos de sistema de reatividade, APIs, performance e casos de uso. Vue e Slash compartilham filosofia similar de reatividade, mas com implementações diferentes.
Resumo Rápido
Section titled “Resumo Rápido”| Aspecto | Slash | Vue 3 |
|---|---|---|
| Reatividade | Observer Pattern explícito | Proxy-based (Composition API) |
| Virtual DOM | ❌ Não | ✅ Sim |
| Template Syntax | HTM (JSX-like) | Template strings ou JSX |
| SSR | ✅ renderToString/renderToStream no pacote | ✅ vue/server-renderer (Nuxt adiciona o resto) |
| Ecossistema | 🌱 Crescendo | 🌳 Maduro |
| Curva de Aprendizado | Baixa | Média |
| Compile-time | ❌ Runtime-only | ✅ Compiler otimizado |
Sistema de Reatividade
Section titled “Sistema de Reatividade”Vue 3: Composition API com Proxies
Section titled “Vue 3: Composition API com Proxies”Vue 3 usa Proxies para rastrear dependências automaticamente:
<script setup>import { ref, computed } from 'vue'
const count = ref(0)const doubled = computed(() => count.value * 2)
function increment() { count.value++}</script>
<template> <div> <p>Count: {{ count }}</p> <p>Doubled: {{ doubled }}</p> <button @click="increment">Increment</button> </div></template>Como funciona:
ref()cria um objeto reativo com.value- Template é compilado para render function
- Vue rastreia acessos durante render
- Proxy detecta mudanças e agenda re-render
- Virtual DOM diff + patch
Slash: Observer Pattern Explícito
Section titled “Slash: Observer Pattern Explícito”Slash usa Observer Pattern com API explícita:
import { html, createState } from '@_bashell/slash'
// O state fica fora do componente (dentro dele seria recriado a cada render)const count = createState(0)
// Computed manualconst doubled = () => count.get() * 2
// Monte como <${Counter} />function Counter() { return html` <div> <p>Count: ${count.get()}</p> <p>Doubled: ${doubled()}</p> <button onClick=${() => count.set(count.get() + 1)}> Increment </button> </div> `}Como funciona:
createState()cria estado reativoget()lê valor,set()atualizawatch()registra observers- O componente que leu
get()re-executa e substitui seus nós (sem VDOM) - Tracking por componente: o que o componente lê durante a execução vira dependência
- Sem compiler, tudo em runtime
Sintaxe e APIs
Section titled “Sintaxe e APIs”Componentes Básicos
Section titled “Componentes Básicos”<!-- Vue 3 --><script setup>import { ref } from 'vue'
const count = ref(0)</script>
<template> <div> <p>Count: {{ count }}</p> <button @click="count++">Increment</button> </div></template>// Slashimport { html, createState } from '@_bashell/slash'
// State fora do componente; monte como <${Counter} />const count = createState(0)
function Counter() { return html` <div> <p>Count: ${count.get()}</p> <button onClick=${() => count.set(count.get() + 1)}> Increment </button> </div> `}<!-- Vue 3 --><script setup>// defineProps e defineEmits são macros do compilador: não precisam de importconst props = defineProps({ title: String, count: Number})
const emit = defineEmits(['update'])
function handleClick() { emit('update', props.count + 1)}</script>
<template> <div> <h2>{{ title }}</h2> <p>{{ count }}</p> <button @click="handleClick">Update</button> </div></template>// Slashimport { html } from '@_bashell/slash'
interface Props { title: string count: number onUpdate: (count: number) => void}
function MyComponent({ title, count, onUpdate }: Props) { return html` <div> <h2>${title}</h2> <p>${count}</p> <button onClick=${() => onUpdate(count + 1)}> Update </button> </div> `}<!-- Vue 3 --><script setup>import { ref, computed, watch } from 'vue'
const firstName = ref('John')const lastName = ref('Doe')
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
watch(fullName, (newVal) => { console.log('Name changed:', newVal)})</script>
<template> <div> <input v-model="firstName" /> <input v-model="lastName" /> <p>{{ fullName }}</p> </div></template>// Slashimport { html, createState } from '@_bashell/slash'
const firstName = createState('John')const lastName = createState('Doe')
// Computed como funçãoconst fullName = () => `${firstName.get()} ${lastName.get()}`
// Watcher (criado uma vez, fora do componente)firstName.watch((newVal) => { console.log('First name changed:', newVal)})
// Só este componente lê os statesconst FullName = () => html`<p>${fullName()}</p>`
// Os inputs não leem states, então não são recriados a cada teclafunction NameForm() { return html` <div> <input value="John" onInput=${(e: Event) => firstName.set((e.target as HTMLInputElement).value)} /> <input value="Doe" onInput=${(e: Event) => lastName.set((e.target as HTMLInputElement).value)} /> <${FullName} /> </div> `}<!-- Vue 3 --><script setup>import { ref, onMounted, onUnmounted } from 'vue'
const seconds = ref(0)let interval
onMounted(() => { interval = setInterval(() => { seconds.value++ }, 1000)})
onUnmounted(() => { clearInterval(interval)})</script>
<template> <p>Seconds: {{ seconds }}</p></template>// Slashimport { html, createState } from '@_bashell/slash'
const seconds = createState(0)
// Slash não tem onMounted/onUnmounted: o efeito vive fora do componente// e você controla início e fimconst interval = setInterval(() => { seconds.set(seconds.get() + 1)}, 1000)// clearInterval(interval) quando não precisar mais
function Timer() { return html`<p>Seconds: ${seconds.get()}</p>`}Performance
Section titled “Performance”Benchmarks e tamanho
Section titled “Benchmarks e tamanho”Esta página não traz benchmarks nem comparação de tamanho com o Vue: não há medições reproduzíveis no repositório para sustentá-las. Meça no seu caso de uso.
Para o Slash, o repositório mede o tamanho de um app mínimo (createState + html + render, minificado) com bun test scripts/bundle-size.test.ts no pacote core. Resultado medido: ≈ 8,3 KB gzip. O número depende do que seu app importa.
Migração de Vue para Slash
Section titled “Migração de Vue para Slash”Conceitos Mapeados
Section titled “Conceitos Mapeados”| Vue 3 | Slash | Notas |
|---|---|---|
ref() | createState() | Slash usa .get(), .set() e .watch() |
computed() | Funções | Apenas funções normais |
watch() | state.watch() | API similar (Observer Pattern) |
v-if | Ternário ? : | JavaScript nativo |
v-for | .map() | JavaScript nativo |
v-model | onInput + state.set() (ou textFieldControl) | Binding manual |
onMounted | Código fora do componente | O corpo do componente re-executa; não há hook de montagem |
onUnmounted | Sem equivalente público | Gerencie timers/listeners você mesmo |
props | Function params | TypeScript interfaces |
emit | Callbacks | Funções em props |
Exemplo de Migração
Section titled “Exemplo de Migração”<!-- Vue 3 --><script setup>import { ref, computed } from 'vue'
const todos = ref([])const filter = ref('all')
const filteredTodos = computed(() => { if (filter.value === 'active') { return todos.value.filter(t => !t.done) } if (filter.value === 'completed') { return todos.value.filter(t => t.done) } return todos.value})
function addTodo(text) { todos.value.push({ id: Date.now(), text, done: false })}</script>
<template> <div> <ul> <li v-for="todo in filteredTodos" :key="todo.id"> {{ todo.text }} </li> </ul> </div></template>// Slashimport { html, createState } from '@_bashell/slash'
const todos = createState([])const filter = createState('all')
const filteredTodos = () => { const f = filter.get() const t = todos.get()
if (f === 'active') return t.filter(t => !t.done) if (f === 'completed') return t.filter(t => t.done) return t}
const addTodo = (text) => { todos.set([ ...todos.get(), { id: Date.now(), text, done: false } ])}
function TodoList() { return html` <div> <ul> ${filteredTodos().map(todo => html` <li key=${todo.id}>${todo.text}</li> `)} </ul> </div> `}Trade-offs Principais
Section titled “Trade-offs Principais”Prós:
- ✅ Ecossistema maduro e rico
- ✅ Compiler optimizations
- ✅ Template syntax familiar
- ✅ Excelentes ferramentas (DevTools, Volar)
- ✅ Documentação extensa
- ✅ Comunidade grande
Contras:
- ❌ Mais dependências e conceitos (compiler, Virtual DOM)
- ❌ Necessita build step para melhor DX
- ❌ Mais abstrações/magic
- ❌ Curva de aprendizado maior
Prós:
- ✅ Bundle pequeno (app mínimo medido em ≈ 8,3 KB gzip, veja a seção Performance)
- ✅ API simples e explícita
- ✅ Sem build step necessário
- ✅ TypeScript first-class
- ✅ Sem magic, previsível
Contras:
- ❌ Ecossistema menor
- ❌ Sem compiler optimizations
- ❌ Mais verboso em alguns casos
- ❌ Ferramentas em desenvolvimento
- ❌ Comunidade pequena
Conclusão
Section titled “Conclusão”Escolha Vue 3 se:
- Precisa de ecosystem robusto
- Quer Nuxt para SSR/SSG
- Valoriza DX com compiler
- Prefere template syntax
- Team já conhece Vue
- Projeto médio/grande
Escolha Slash se:
- Quer uma biblioteca pequena (app mínimo: ≈ 8,3 KB gzip)
- Prefere simplicidade
- Não quer build step
- Gosta de explicitness
- Projeto pequeno/médio
- Quer controle total
Ambas são ótimas escolhas. Vue é mais “batteries included”, Slash é minimalista e direto.
Próximos Passos
Section titled “Próximos Passos”- Slash vs React - Comparação com React
- Slash vs Solid - Comparação com SolidJS
- Exemplos Práticos - Apps completas