Skip to content

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.

AspectoSlashVue 3
ReatividadeObserver Pattern explícitoProxy-based (Composition API)
Virtual DOM❌ Não✅ Sim
Template SyntaxHTM (JSX-like)Template strings ou JSX
SSR✅ renderToString/renderToStream no pacote✅ vue/server-renderer (Nuxt adiciona o resto)
Ecossistema🌱 Crescendo🌳 Maduro
Curva de AprendizadoBaixaMédia
Compile-time❌ Runtime-only✅ Compiler otimizado

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:

  1. ref() cria um objeto reativo com .value
  2. Template é compilado para render function
  3. Vue rastreia acessos durante render
  4. Proxy detecta mudanças e agenda re-render
  5. Virtual DOM diff + patch

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 manual
const 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:

  1. createState() cria estado reativo
  2. get() lê valor, set() atualiza
  3. watch() registra observers
  4. O componente que leu get() re-executa e substitui seus nós (sem VDOM)
  5. Tracking por componente: o que o componente lê durante a execução vira dependência
  6. Sem compiler, tudo em runtime

<!-- 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>
// Slash
import { 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>
`
}

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.


Vue 3SlashNotas
ref()createState()Slash usa .get(), .set() e .watch()
computed()FunçõesApenas funções normais
watch()state.watch()API similar (Observer Pattern)
v-ifTernário ? :JavaScript nativo
v-for.map()JavaScript nativo
v-modelonInput + state.set() (ou textFieldControl)Binding manual
onMountedCódigo fora do componenteO corpo do componente re-executa; não há hook de montagem
onUnmountedSem equivalente públicoGerencie timers/listeners você mesmo
propsFunction paramsTypeScript interfaces
emitCallbacksFunções em props
<!-- 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>
// Slash
import { 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>
`
}

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

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.