Dlaczego (nie) warto nauczyć się Vue.js

vue_logo

Za każdym popularnym narzędziem stoi potężny motywator...

angular Google
react Meta (Faceboook)
vue Typ, który musiał pracować z AngularJS
evanyou
Twórca: Evan You

Pierwszy release: luty 2014

Obecna wersja: 3.5 (Tengen Toppa Gurren Lagann)
ttgl
vueversions

The Progressive
JavaScript Framework

  • Strony statyczne (w runtime)
  • Single-Page Application (SPA)
  • Server-Side Rendering (SSR)
  • Static Site Generation (SSG)
  • Web Components
  • A także...
import { createRenderer } from '@vue/runtime-core'

const { render, createApp } = createRenderer({
  patchProp,
  insert,
  remove,
  createElement
  // ...
})

// `render` is the low-level API
// `createApp` returns an app instance
export { render, createApp }

// re-export Vue core APIs
export * from '@vue/runtime-core'
						  

Vue TermUI

termui

TresJS



<template>
  <TresCanvas clear-color=”#82DBC5” window-size>
    <TresPerspectiveCamera />
    <TresMesh @click=”onClick”>
      <TresBoxGeometry :args="[1, 1, 1]" />
      <TresMeshNormalMaterial />
    </TresMesh>
  </TresCanvas>
</template>
							
tresjs

Rozszerzenie statycznego HTMLa

static-meme

Vue w akcji - przykład 1

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app">
	<div>{{ message }}</div>
	<input v-model="message" />
</div>

<script>
  const { createApp } = Vue

  createApp({
    data() {
      return {
        message: 'Hello Vue!'
      }
    }
  }).mount('#app')
</script>
{{ message }}

Single-Page Application (SPA)

spa-meme

Możliwości Vue

  • Text interpolation: {{ variable }}
  • JS expressions:
    {{ message.split('').reverse().join('') }}
  • Attribute binding v-bind:href / :href
  • Event binding v-on:click / @click
  • Conditional rendering v-if / v-else
  • List rendering v-for
  • Two-way data binding v-model

Komponenty: Options API vs Composition API

<script>
export default {
  data() {
    return {
      count: 0
    }
  }
}
</script>

<template>
  <button @click="count++">
		You clicked me {{ count }} times.
	</button>
</template>
<script setup>
import { ref } from 'vue'

const count = ref(0)
</script>

<template>
  <button @click="count++">
		You clicked me {{ count }} times.
	</button>
</template>

Vue w akcji - przykład 2

<script setup>
import { ref } from 'vue'
const msg = ref('');
const msgs = ref(['Hello!']);
const submit = () => {
  msgs.value = [...msgs.value, msg.value];
  msg.value ='';
}
const isEven = (i) => i%2 === 0;
</script>

<template>
  <div style="height: 400px;...">
    <div v-for="(m, i) in msgs" :key="i" :style="{'align-self': isEven(i) ? 'start' : 'end'}">
      <span v-if="isEven(i)">- {{m}}</span>
      <span v-else>{{m}} -</span>
    </div>
  </div>
  <input v-model="msg" @keyup.enter="submit" />
</template>
- {{m}} {{m}} -

Composables

// mouse.js
import { ref, onMounted, onUnmounted } from 'vue'

// by convention, composable function names start with "use"
export function useMouse() {
  // state encapsulated and managed by the composable
  const x = ref(0)
  const y = ref(0)

  // a composable can update its managed state over time.
  function update(event) {
    x.value = event.pageX
    y.value = event.pageY
  }

  // a composable can also hook into its owner component's
  // lifecycle to setup and teardown side effects.
  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))

  // expose managed state as return value
  return { x, y }
}
<script setup>
import { useMouse } from './mouse.js'

const { x, y } = useMouse()
</script>

<template>Mouse position is at: {{ x }}, {{ y }}</template>

State management - Pinia

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const name = ref('Eduardo')
  const doubleCount = computed(() => count.value * 2)
  function increment() {
    count.value++
  }

  return { count, name, doubleCount, increment }
})
<script setup>
import { useCounterStore } from '@/stores/counter'

// access the `store` variable anywhere in the component ✨
const store = useCounterStore()
</script>

Server-Side Rendering (SSR)

ssr-meme

Vue w akcji - przykład 3

import express from 'express'
import { createSSRApp } from 'vue'
import { renderToString } from 'vue/server-renderer'

const server = express()

server.get('/', (req, res) => {
    const app = createSSRApp({
        data: () => ({ msg: 'Hello Kraków' }),
        template: `<div>SSR message: {{msg}}</div>`
    })

    renderToString(app).then((html) => {
        res.send(`
    <!DOCTYPE html>
    <html>
      <head>
        <title>Vue SSR Example</title>
      </head>
      <body>
        <div id="app">${html}</div>
      </body>
    </html>
    `)
    })
})

Nuxt.js

nuxt

Static Site Generation (SSG)

ssg-meme
jamstack
jamstack-build-process
Zesty: What is Jamstack?
vue-ssg
Jamstack website

VitePress

vitepress

WebComponents

wc-meme

Vue w akcji - przykład 4

const { defineCustomElement } = Vue
const MyVueElement = defineCustomElement({
    // normal Vue component options here
    props: {
        text: String,
    },
    emits: {},
    template: `<div>Input text: {{text}}</div>`,

    styles: []
})
customElements.define('my-vue-element', MyVueElement)
<my-vue-element text="custom element text" />

Porównanie

Skalowanie

W górę W dół
Angular
React ✓*
Vue ✓*

* przy użyciu dodatkowych narzędzi

Benchmarki

benchmark1 benchmark1
Link do benchmarku

W kodzie

<script setup>
import { ref } from 'vue'
const msg = ref('');
const msgs = ref(['Hello!']);
const submit = () => {
  msgs.value = [...msgs.value, msg.value];
  msg.value ='';
}
const isEven = (i) => i%2 === 0;
</script>

<template>
  <div style="height: 400px; ...">
    <div v-for="(m, i) in msgs" :style="{'align-self': isEven(i) ? 'start' : 'end'}" :key="i">
      <span v-if="isEven(i)">- {{m}}</span>
      <span v-else>{{m}} -</span>
    </div>
  </div>
  <input v-model="msg" @keyup.enter="submit" />
</template>
import React, { useRef, useState } from 'react'

const App = () => {
  const [msg, setMsg] = useState('')
  const [msgs, setMsgs] = useState(['Hello!'])
  const submit = () => {
    setMsgs(initialMsgs => [...initialMsgs, msg]);
    setMsg('');
  }
  const isEven = i => i%2 === 0;

  return (
    <>
    <div style={{height: 400, ...}}>
      {msgs.map((m, i) => (
      <div key={i} style={{alignSelf: isEven(i) ? 'start' : 'end'}}>
        {isEven(i) ? (<span>- {m}</span>) : (<span>{m} -</span>)}
      </div>
    ))}
    </div>
      <input value={msg} onChange={e => setMsg(e.target.value)} onKeyUp={event => {
                if (event.key === 'Enter') {
                   submit();
                }
              }} />
    </>
  )
}

export default App
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { bootstrapApplication } from '@angular/platform-browser';

@Component({
  imports: [CommonModule, FormsModule],
  selector: 'app-root',
  template: `
    <div style="height: 400px; ...">
    @for (m of msgs; track $index) {
      <div [ngStyle]="{ 'align-self': isEven($index) ? 'start' : 'end'}">
      @if (isEven($index)) {
        <span>- {{ m }}</span>
      } @else {
        <span>{{ m }} -</span>
      }
      </div>
    }
    </div>
    <input [(ngModel)]="msg" (keyup.enter)="submit()" />
  `,
})
export class ChatComponent {
  msg: string = '';
  msgs: string[] = ['Hello!'];
  submit() {
    this.msgs.push(this.msg);
    this.msg = '';
  }
  isEven(i: number): boolean {
    return i % 2 === 0;
  }
}

bootstrapApplication(ChatComponent);

Vue vs React

vuereact vuereact2 vuereact3

Vue vs Angular

vueangular vueangular2 vueangular3

Czy warto?

Do szybkiego prototypingu

See the Pen Drag'n drop points vue by abi-spyro (@abi-spyro) on CodePen.

  • Jako pierwszy framework
  • Do tworzenia bibliotek WebComponents
  • Jeśli chcesz małym kosztem rozszerzać statyczne strony dynamicznie generowanymi elementami (petite-vue ma 6kb)
  • Jeśli lubisz patrzeć na benchmarki
  • Jeśli chcesz rozszerzyć swoje portfolio

Kiedy nie warto?

  • Gdy nie masz do tego ludzi
  • Gdy inne aplikacje są w innych frameworkach
  • Gdy nie istnieje techniczna i biznesowa potrzeba pisania w Vue

Dziękuję!

meme