Vue.js

núria soriano

Por qué Vue?

Rápido

Vue + vue-router + vuex 26kb min+gzip

Se adapta a ti

Templates vs Render Functions vs JSX

Javascript vs Typescript

No opinionado

Escalable

Core: Declarative rendering & component system

Routing: vue-router

Large Scale State Management: vuex

Build system: vue-loader/vueify, vue-cli

SSR: vue-server-renderer

Fácil


    <div id="app">
    
      <h1>{{ message }}</h1>
    
    </div>
    
    <script>
    
      var vm = new Vue({
        data: {
          message: 'Hello World!'
        }
      }).$mount('#app')
    
    <script>

Iniciar nuestro proyecto con Vue
y Webpack

Vue CLI

    
    npm install -g vue-cli

    vue init <template-name> <project-name>

Templates

Estructura del proyecto

├─ build/            # Configuración de webpack
├─ config/           # Configuración general, variables de entorno…
├─ src/
│   ├─ main.js       # Archivo principal de entrada
│   ├─ App.vue       # Componente raíz
│   ├─ components/   # Componentes
│   └─ assets/       # Assets procesados por webpack (sass…)
├─ static/           # Assets estáticos no procesados por Webpack
├─ .babelrc          # babel config
├─ .postcssrc.js     # postcss config
├─ .eslintrc.js      # eslint config
├─ .editorconfig     # editor config
├─ index.html        # Plantilla html
└─ package.json      # Scripts y dependencias

Anatomía de un componente

Vue Single File Component

Plantilla, lógica y estilo

Usando HTML + Javascript + CSS

O Pug, CoffeeScript, SASS…

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>

Propiedades


    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

Parent to Child

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
        }
      },
      // ...
    }

Child to Parent

Child

    
    this.$emit('eventName', data)

Parent

    
    <child-component @eventName="method">
    </child-component>

Sync

Child

    
    this.$emit('update:foo', newValue)

Parent

    
    <child-component :foo.sync="bar">
    </child-component>

Desde Vue 2.3

    
    <child-component :foo="bar" @update:foo="val => bar = val">
    </child-component>

Escalando
nuestra app:

Routing

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

    
    <router-link to="home"></router-link>

    <router-link :to="{ 
                   name: 'Detail', 
                   params: {
                      id: 1
                   }
                 }">
    </router-link>
    
    this.$router.push({ 
       name: 'Detail', 
       params: {
          id: 1
       }
     })

Escalando
nuestra app:
State management

State Management

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 }) {
         api.getMovies()
            .then( data => {
              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'
        ])
      }
    }

Bonus

Bonus

Localización: vue-i18n

Validación: vuelidate

Y más: vue-awesome

Gracias!

nuria-fl

soriano.nuria@gmail.com

pincfloit

Introducción a Vue.js

By nuria-fl

Introducción a Vue.js

  • 1,559