Vue.js
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 propertyv-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 thestore
augument to the root instance, any component can get at the store by callingthis.$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
- All starts with an action
- Calls a dispatcher to propagate an event
- Puts the result into a store
- Store emits an event to tell components that are subscribed to it that the data has changed
- 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 apayload
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 tostate
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
- Arrow function:
count: state => state.count
- Map a property to a string if you want to rename
countAlias: 'count'
- Or just pass the string
- A normal function to mix local "this" state with store state:
countPlusLocalState (state) {return state.count + this.localCount}
- Arrow function:
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'smethods
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
- Use <router-link> and <router-view> tags inside your component
- Import route components
- Define routes and map components to routes (each object is called a "route record")
- Instantiate router instance and pass the routes to the constructor
- Inject the router to make the whole app router aware
Knobs, dials, and best practices
- Now you get
this.$router
andthis.$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 callingthis.$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 isdefault
- When
props: true
inside route definition, route parameters get defined as a property on the component, so now you don't have to explicitly usethis.$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'
- E.g., pattern /user/:username matches to path /user/evan and this.$route.params is object
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.
- 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
npm install -g @vue/cli
vue --version
vue ui
- Create new project, include vuex and vue router components
- Run Project task 1: serve
- Run Project task 2 (build):
2018 Tech Stack
- container
- nginx
- node.js
- TypeScript lang
- React & webpack + TypeScript