Vue.js

From Colettapedia
Jump to navigation Jump to search

Vue.js

Files

index.html

  • has <div id="app" and script includes main.js

main.js

  • contains instantiation of Vue instance
import Vue from 'vue';
import App from './App.vue';
import store from './store';

new Vue( {
    store,
    render: h => h(App)
}).$mount( '#app' );

App.vue

  • top level component into which we compose other components
  • App-level arg array contains: const app = new Vue({
    • el: "#app" - el stands for element, maps back to the div in index.html
    • data
    • computed - contains methods. One-off computations in our dataset. Get access to the data using $this
    • filters - also contains methods that take arguments. Pass arguments to filter method inside the mustache using the pipe separator
    • methods - can take an argument
    • lifecycle methods
      • beforeCreate
      • mounted() fires when your app attaches to the DOM, a good time to fetch some data
      • beforeDestroy()

router/index.js

  • Entrypoint into Vue Router

store/index.js

  • Entrypoint into Vuex

Directives

  • directive inside HTML tags start with v-
  • v-bind:____="someDataStoreObj - bind keeps something up-to-date with some data store property
  • v-if="varName"
  • v-for:"i in datastoreobj"
  • v-on:click="someMethod" - binds a function to button
  • <input v-model="message"> - direct two-way binding between input and app state

Component

  • How you create a new HTML tag, you must register them
  • Template can only return one top level element, if you need to return two, wrap within a div.
  • computed properties of a component are just like regular data members
    • Can be declared methods, but call as if they were attributes.
  • Component scaffold has template, script, and style
  • export default {
    • props: ['id, 'age', 'weight] }
    • name = "component_name"
    • components: [ subcomponent1, subcomponent2 ]
  • <style scoped>

Vue Instance

Vuex

Cheat sheet

  • dispatch actions, commit mutations

Concepts

  • State management library
  • Extract shared state out of components and manage it in a global singleton
  • Use when you do a lot of passing data from one component to another through props.
  • When you don't want your data all over the place
  • Vuex stores are REACTIVE, meaning when vue components retrieves state from it, they will reactively and efficiently update the store's state changes.
  • You cannot directly mutate the store's state. The only way to change a store's state is by explicitly committing mutations. Can log every mutation, take state snapshots, or perform time travel debugging.
  • Modules - encapsulating/separating out logic for different components
  • E.g., whenever store.state.count changes, it will cause the computed property to re-evaluate, and trigger associated DOM updates.
  • By using Vue.use(Vuex) and passing the store augument to the root instance, any component can get at the store by calling this.$store

Flux pattern concepts

  • flux pattern concept created by Facebook
  • All you technically need is an event system
    • Unidirectional dataflow, because all actions go through the same dispatcher
    • All components will know when data has changed and con update appropriately
    1. All starts with an action
    2. Calls a dispatcher to propagate an event
    3. Puts the result into a store
    4. Store emits an event to tell components that are subscribed to it that the data has changed
    5. The views consume the data from the store
  • Many implementations. Redux is another example of Flux pattern but for React framework

Four types of entities in the modules

state

  • put data members in here as an object

getters

  • When you need to compute a derived state based on a store state.
    • like do a filter
  • all getters go in computed properties.
  • Getters are like computed properties for stores: will only re-evaluate when some of its dependencies have changed.
  • Get things out of the store with these methods, otherwise you will need a computed property in your component that pulls directly: this.$store.state.foo
  • getter methods take a single argument state
  • Can also pass arguments to getters by returning a function
    • getTodoById: (state) => (id) => { return state.todos.find(todo => todo.id === id)}
  • Can use getters via this.$store.getters.doneTodos or mapGetters helper

mutations

  • always synchronous - you can't capture the "before" and "after" snapshots if mutations are asynchronous.
  • list out the different possible ways the state can change.
  • Vuex mutations are similar to events.
  • Each mutation has a string type and a handler.
  • Mutation handler methods takes one or two arguments, state, and optionally a payload which can be a scalar or an object.
  • YOU CANNOT DIRECTLY CALL A MUTATION HANDLER, instead invoke store.commit

actions

  • Similar to mutations except:
    • Instead of mutating the state, they commit the mutations
    • Contains arbitrary asynchronous operations.
  • Called from components to commit a mutation
  • Can be synchronous OR synchronous
  • Can do multiple mutations at once
  • All actions do is call the dispatcher with some parameters and an operation
  • Actions method take two arguments
    • context (similar to state object, which exposes the same set of properties/methods on the store instance)
      • Use ES2015 argument destructuring to pull a member out of an object so you don't have to keep using . to call a method on an object
    • payload
  • Can daisy chain, a.k.a "compose" actions
actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // wait for `actionA` to finish
    commit('gotOtherData', await getOtherData())
  }
}


Vocabulary

  • Dispatch actions versus commit mutations
  • const jokes = await fetch( 'jokes.json' )
  • Commit a mutation

Vuex helper methods

  • helper methods return objects
  • Use the elipsis ... "object spread operator" from ES2015 (Python equivalent is **) if we ever want to use helper methods in combination with other local methods.

mapState

  • import { mapState } from 'vuex'
  • FOUR ways to map store state to component state
    1. Arrow function: count: state => state.count
    2. Map a property to a string if you want to rename countAlias: 'count'
    3. Or just pass the string
    4. A normal function to mix local "this" state with store state: countPlusLocalState (state) {return state.count + this.localCount}

mapGetters

  • Put inside components computed properties

mapMutations

  • Put inside component's methods properties
  • Example: map `this.increment()` to `this.$store.commit('increment')`
    • methods: { ...mapMutations([ 'increment', 'incrementBy']) }

mapActions

  • Like mapMutations, put inside component's methods properties
  • Example: map `this.increment()` to `this.$store.dispatch('increment')`
    • methods: { ...mapActions([ 'increment', 'incrementBy']) }

Code

App.vue

const store = new Vuex.Store({

})

store/index.js

import Vuex from 'vuex';
import ModuleName from './modules/ModulName'

// Load Vuex
Vue.use( Vuex );

// Create store
export default new Vuex.store( {
    modules: {
        ModuleName
    }
}
);

Vue Router

  • Single Page App (SPA)

Basic Directions

  1. Use <router-link> and <router-view> tags inside your component
  2. Import route components
  3. Define routes and map components to routes (each object is called a "route record")
  4. Instantiate router instance and pass the routes to the constructor
  5. Inject the router to make the whole app router aware

Knobs, dials, and best practices

  • Now you get this.$router and this.$route inside of the component
  • router-link automatically gets the .router-link-active class when target route is matched.
  • Clicking on a router-link is totally equivalent to calling this.$router.push( { name: 'search', params: { job_handle: '0123456789ABCDEFFEDCBA9876543210'} } )
  • Can have multiple <router-view>s with different names within a single path, and inside the routes definition component's attribute you can use a viewname: component mapping. The key for the unnamed view in this mapping is default
  • When props: true inside route definition, route parameters get defined as a property on the component, so now you don't have to explicitly use this.$route.params
  • Use VueRouter in history mode, add a catchall route to prevent weirdness related to direct navigation within single-page-apps, and use a try_files directive in your nginx.conf as a backstop: location / { try_files $uri $uri/ /index.html; }


Dynamic Route Matching

Pulling parameters out of URL fragments

  • Use dynamic segment in the route path
  • When a route is matched the value of the dynamic segment will be exposed as this.$route.params
    • E.g., pattern /user/:username matches to path /user/evan and this.$route.params is object username: 'evan'

Nested Routes

  • Use children attribute when defining routes, level of nesting can be arbitrary
  • Children components can have their own nested <router-view> tag
    • Child component will be rendered inside parent component's <router-view> tag when route is matched

Asterisk

  • Matching priority is given by route definition
  • When using an asterisk, a param named pathMatch is automatically added to $route.params.

Navigation Guard methods for reactive components and data fetching

  • If the route is the same and only your dynamic component is changing AND you need for reactive things to happen, you MUST define a beforeRouteUpdate method inside your component definition to make the reactive
  beforeRouteUpdate (to, from, next) {
    // react to route changes...
    // don't forget to call next()
  }
  • to: Route: the target Route Object being navigated to.
  • from: Route: the current route being navigated away from.
  • next: Function: this function must be called to resolve the hook.
    • next()
    • next(false) - abort the navigation
    • next( 'someOtherRoute' ) - redirect to a different location
    • next( error ) - where error is of type Error and can be resolved on callbacks such as router.onError()
 router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  else next()
})

Vue CLI

  1. npm install -g @vue/cli
  2. vue --version
  3. vue ui
  4. Create new project, include vuex and vue router components
  5. Run Project task 1: serve
  6. Run Project task 2 (build):

2018 Tech Stack