Vue.js
núria soriano
Facts & Opinions
FACTS
¿Que es Vue?
Framework progresivo
No opinionado
Rápido
Vue Single File Component
Plantilla, lógica y estilo
Usando HTML + Javascript + CSS
Se importa como un módulo ES2015
Hot-reload, scoped CSS…
<template>
<my-component></my-component>
</template>
<script>
import 'myComponent' from './MyComponent'
export default {
name: 'app',
components: {
myComponent
}
}
</script>
<style lang="scss" scoped>
div {
color: blue
}
</style>
<template>
<div>
<div>
{{ item.name }}
</div>
<img :src="url"/>
<button @click="someMethod"></button>
<div v-for="item in items">{{ item.name }}</div>
<div v-if="someCondition">
print this
</div>
<div v-else>
print that
</div>
<div v-show="someOtherCondition">
display this
</div>
</div>
</template>
Template
Estado
data() {
return {
collection: [1, 2, 3, 4]
}
}
Métodos
methods: {
addNumbers() {
var moreNumbers = [5,6,7,8]
this.collection.push(...moreNumbers)
}
}
Propiedades computadas
Reactivas
computed: {
evenNumbers() {
return this.collection.filter(number => number %2 === 0)
}
}
Solo se recalculan cuando es necesario
Lifecycle hooks
Ejecutar lógica en determinados momentos
created() {
http.get('http://jsonplaceholder.typicode.com/posts')
.then(response => {
this.posts = response.data
})
.catch(e => {
this.errors.push(e)
})
}
Comunicación entre componentes
Child
export default {
props: [ 'propName' ],
// ...
}
Parent
<child-component :propName="someData">
</child-component>
export default {
props: {
propName: {
type: Boolean,
default: true
},
otherProp: {
type: String,
required: true
}
},
// ...
}
Comunicación entre componentes
Child
this.$emit('eventName', data)
Parent
<child-component @eventName="method">
</child-component>
Router
import VueRouter from 'vue-router';
import User from 'src/components/User';
const router = new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user',
component: User
}
]
})
<div id="app">
<router-view></router-view>
</div>
<router-link to="/foo">Foo</router-link>
<router-link :to="{
name: 'foo',
params: {
id: bar
}
}">
Foo
</router-link>
Vuex
Varios componentes son dependientes del mismo estado
Sincronizar estado sin necesidad de eventos
Solución escalable para aplicaciones complejas
Fácil de debuggar
El estado
const store = new Vuex.Store({
state: {
movies: [
{ id: 1, title: '...', saved: true },
{ id: 2, title: '...', saved: false }
]
}
})
const store = new Vuex.Store({
state: {
movies: [
{ id: 1, title: '...', saved: true },
{ id: 2, title: '...', saved: false }
]
},
getters: {
savedMovies: state => {
return state.movies.filter(movie => movie.saved)
}
}
})
Objeto
No mutable directamente
Usar el estado en nuestros componentes
computed: {
movies () {
return this.$store.state.movies
},
savedMovies () {
return this.$store.getters.savedMovies
}
}
import { mapState, mapGetters } from 'vuex'
export default {
computed: {
...mapState({
'movies'
}),
...mapGetters([
'savedMovies'
])
}
}
Definir mutaciones
const store = new Vuex.Store({
state: {
movies: []
},
mutations: {
addMovie (state, movie) {
state.movies.push(movie)
}
}
})
Mutaciones asíncronas: Acciones
const store = new Vuex.Store({
state: { ... },
mutations: { ... },
actions: {
getMovies ({ commit }) {
return api.getMovies()
.then( data => {
return data.forEach(movie => commit('addMovie', movie))
})
}
}
})
Usar mutaciones y acciones en componentes
methods: {
addMovie (movie) {
return this.$store.commit('addMovie')
},
getMovies () {
return this.$store.dispatch('getMovies')
}
}
import { mapMutations, mapActions } from 'vuex'
export default {
methods: {
...mapMutations({
'addMovie'
}),
...mapActions([
'getMovies'
])
}
}
OPINIONS
¿Por qué Vue?
Curva de aprendizaje
HTML, Javascript, CSS
Templates vs render functions vs JSX
Flexible
O Pug, Typescript, SASS…
Dev Friendly
Devtools
Dev Friendly
CLI
Dev Friendly
Configurar webpack totalmente sin necesidad de eject
Dev Friendly
// vue.config.js
module.exports = {
configureWebpack: {
plugins: [
new MyAwesomeWebpackPlugin()
]
}
}
¿Cuando usar Vue?
TL;DR:
Equipo con experiencia diversa
Prototipos, MVP…
Desarrollo rápido y flexible
Tips & Tricks
Convention over configuration
Seguir la style guide de Vue
Prettier, ESLint vue/recommended
Nuxt
Best practices
Evitar lógica en el template
<template>
<div>
{{ numbers.sort() }}
</div>
</template>
<template>
<div>
{{ sortedNumbers }}
</div>
</template>
Best practices
Evitar side effects en propiedades computadas
<template>
<div>
{{ sortedNumbers }}
</div>
</template>
<script>
export default {
data() {
return {
numbers: [4, 5, 2, 7, 1]
};
},
computed: {
sortedNumbers() {
return this.numbers.sort()
}
}
};
</script>
<template>
<div>
{{ sortedNumbers }}
</div>
</template>
<script>
export default {
data() {
return {
numbers: [4, 5, 2, 7, 1]
};
},
computed: {
sortedNumbers() {
return [...this.numbers].sort()
}
}
};
</script>
Best practices
Separar lógica de negocio de componentes
export default {
data() {
return {
movies: null
};
},
created: {
axios.get('https://api.example.com/movies')
.then(function (response) {
this.movies = response
})
}
}
export default {
data() {
return {
movies: null
};
},
created: {
someService.getMovies()
.then(movies => {
this.movies = movies
})
}
}
export default {
...mapGetters([
'movies'
]),
...mapActions([
'getMovies'
]),
created: {
this.getMovies()
}
}
Unit Testing
Unit Testing
Snapshots para los componentes presentacionales
Testear servicios y store es más fácil
No sobretestear: Component contract
Ready?
Empieza con un proyecto pequeño
Prefiere soluciones framework-agnostic
Documentación
"El progreso se debe a personas vagas en busca de formas más fáciles de hacer las cosas"
Robert A. Heinlein
¡Gracias!
nuria-fl
soriano.nuria@gmail.com
pincfloit
Vue.js - JSCoders
By nuria-fl
Vue.js - JSCoders
- 1,470