VUE AND VUEXVUE AND VUEX
CHRIS NORINGCHRIS NORING
Google Developer Expert
@chris_noring
McKinsey
1
WHY SHOULD YOUWHY SHOULD YOU
CARE?CARE?
Vue.js 93000 stars on github, React 95000 stars on
github, Angular 36000 stars on github AngularJS
58000 stars on github
It's really easy, great documentation
The interest is there, now we are just waiting for
adoption
2 . 1
VUE BASICSVUE BASICS
3 . 1
Let's start with the DOM
3 . 2
<html>
<body>
<div id="app">
{{ message }} {{ other message }}
</div>

<div>Footer</div>
</div>
5 . 7
How to navigate?
Answer: Define a link
5 . 8
<router-link to="/products">Products</router-link>
5 . 9
What about routing with params in them, aka wildcard
routing ?
5 . 10
const routes = [
{ path: '/products', component: Products },
{ path: '/products/:id', component: ProductDetail }
]
5 . 11
Ok so we can now match /products/11 or
/products/114
How do we dig out the parameter?
5 . 12
this.$route.params.id gives us the parameter
const ProductDetail = {
template: `
<div>
Product {{ product.name }}
</div>
`,
data() {
let product = products.find(p => this.$route.params.id);
return {
product
};
}
}
5 . 13
What about query params?
/products?pageSize=10&page=2
$route.query.queryParam
5 . 14
this.$route.query.queryParam gives us the parameter
const ProductDetail = {
template: `
<div>
Product {{ product.name }}
</div>
`,
async data() {
let product = await getProducts($route.query.page, $route.
return {
product
};
}
}
5 . 15
Let's face it, our user will mistype the url at some
point, we need a 'Catch all route'
5 . 16
const routes = [
{ path: '/products', component: Products, name: 'products' }
{ path: '/products/:id', name: 'product', component: Product
{ path: '/**', component: NotFound }
]
5 . 17
Programmatic routing, we can navigate by url as well
as name
5 . 18
methods: {
back() {
this.$router.push('/products?a=1&b=2');
},
backName() {
this.$router.push({ name: 'products' });
}
}
5 . 19
You can have Multiple view ports
5 . 20
<div>
<router-view class="header" name="header"></router-view>
</div>
<div>
<router-view class="body"></router-view>
</div>
<div>
<router-view class="footer" name="footer"></router-view>
</div>
5 . 21
Give router-view a name and we can route to it
5 . 22
default - refers to an unnamed router-view
footer - is our named router-view
const routes = [
{ path: '/products',
components: {
default: Products,
footer: Footer
},
},
]
5 . 23
Don't tell me, there is more right?
5 . 24
Well yes, we need to learn how test our app...
5 . 25
TESTINGTESTING
6 . 1
The easiest way to get started with testing is using vue-
cli
6 . 2
Type of testing you can do
Unit testing, Karma, Mocha, Chai or Jest
E2E testing, Nightwatch
6 . 3
HOW DO WE CONSTRUCT A UNIT TEST?HOW DO WE CONSTRUCT A UNIT TEST?
Get a constructor reference
Instantiate constructor
Call mount
Find the element in question
Assert
6 . 4
import Vue from 'vue'
import Test from '@/components/Test'
describe('Test.vue', () => {
it('should render correct contents', () => {
const Constructor = Vue.extend(Test)
const vm = new Constructor().$mount()
expect(vm.$el.querySelector('.test h1').textContent)
.to.equal('Testing...')
})
})
6 . 5
Scenario: we have a component with input props
6 . 6
<template>
<div class="about">
<h1>{{msg}}</h1>
<h2>{{title}}</h2>
</div>
</template>
<script>
export default {
name: 'About',
data () {
return { msg: 'about page of the app' }
},
props: ['title']
}
</script>
6 . 7
And now to the test
6 . 8
propsData needs to be set
describe('About.vue', () => {
it('should render correct contents', () => {
const Constructor = Vue.extend(About)
const vm = new Constructor({
propsData: { title: 'abc' }
}).$mount()
expect(vm.$el.querySelector('.about h2').textContent)
.to.equal('abc')
})
})
6 . 9
How do we deal with events?
6 . 10
<template>
<div class="test">
<h1>{{msg}}</h1>
<button class="change-button" @click="change">Change</butt
</div>
</template>
<script>
export default {
name: 'Test',
data () { return { msg: 'Testing...' } },
methods: { change () { this.msg = 'clicked' } }
}
</script>
6 . 11
Now for the test - we need to trigger a click event:
6 . 12
it('should render clicked if button is clicked', () => {
const Constructor = Vue.extend(Test)
const vm = new Constructor().$mount()
const button = vm.$el.querySelector('.test .change-button')
const clickEvent = new window.Event('click')
button.dispatchEvent(clickEvent)
vm._watcher.run()
expect(vm.$el.querySelector('.test h1').textContent)
.to.equal('clicked')
})
6 . 13
e2e test - Nightwatch enables us to spin up a browser.
Interact with our app and assert on the result
6 . 14
Scenario: take our test component, click the button
and assert it shows 'clicked'
6 . 15
browser
.url(devServer)
.waitForElementVisible('#app', 5000)
.assert.elementPresent('.test')
.assert.containsText('.test h1', 'Testing...')
.assert.elementPresent('.change-button')
.click('.change-button')
.assert.containsText('.test h1', 'clicked')
.end()
6 . 16
Scenario: take our test component, write in the input
field, assert our component shows that data
6 . 17
browser
.url(devServer)
.waitForElementVisible('#app', 5000)
.assert.elementPresent('.test')
.setValue('.test-input', 'abc')
.assert.containsText('.test h1', 'abc')
.end()
6 . 18
Core team member, @eddyerburgh Edd Yerburgh,
https://www.manning.com/books/testing-vuejs-
applications
6 . 19
Wasn't that exciting
6 . 20
zzzzz...
6 . 21
Just one more thing, state management with VUEX
6 . 22
VUEXVUEX
CENTRALIZED STATE MANAGEMENT FOR VUE.JSCENTRALIZED STATE MANAGEMENT FOR VUE.JS
7 . 1
All of our application state live in a store
7 . 2
Hello store
7 . 3
<html>
<body>
<div id="app">
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/
<script src="https://unpkg.com/vuex"></script>
<script src="app.js"></script>
<script src="//0.0.0.0:35729/livereload.js?snipver=1" async=
</html>
7 . 4
Instantiating the store
7 . 5
const store = new Vuex.Store({
state: {
count: 0,
},
})
7 . 6
Read from it
7 . 7
In a component
set up a computed property
show through interpolation {{}}
7 . 8
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
},
computed: {
count: function() {
return this.$store.count;
}
},
store
})
7 . 9
And showing it
7 . 10
{{ count }} // 0
7 . 11
change it
7 . 12
MUTATIONSMUTATIONS
7 . 13
const store = new Vuex.Store({
state: {
count: 0,
},
mutations: {
increment (state) {
state.count++; // mutating state
},
decrement (state) {
state.count--; // mutating state
}
}
})
7 . 14
calling a mutation
7 . 15
this.$store.commit('mutationName')
Vue.component('Counter', {
template: `
<div>
<button @click="increment">Increment</button>
<button @click="decrement">Decrement</button>
</div>>
`,
methods: {
increment() {
this.$store.commit('increment');
},
decrement() {
this.$store.commit('decrement');
}
}
7 . 16
We can also commit() with a payload
7 . 17
increment() {
this.$store.commit('addProduct', this.newProduct);
}
7 . 18
How do we access the payload in the store?
7 . 19
mutations: {
addProduct(state, payload) {
this.products = [ ...this.state.products, { ...payload }]
}
}
7 . 20
ACTIONSACTIONS
7 . 21
Does NOT mutate state
Calls .commit() when done
Can be asynchronous
we dispatch() actions
7 . 22
Defining it
7 . 23
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
// called 2nd
increment() {
this.state.count++;
}
},
actions: {
// called 1st on dispatch()
increment(context) {
context.commit('increment')
}
7 . 24
Calling an action from a component
7 . 25
Vue.component('product', {
template: `
<div>
{{ count }}
<button v-on:click="increment">Increment</button>
</div>
`,
methods: {
increment() {
this.$store.dispatch('increment'); // calling action
}
}
})
7 . 26
Asynchronous Scenario
7 . 27
set loading state to true ( e.g show spinner )
fetch our data
set data or error
set loading to false
7 . 28
actions: {
loadProducts({ commit, state }) {
commit('loading');
try{
const products = await api.getProducts();
commit('products', products);
} catch (err) {
commit('error', err);
}
}
}
7 . 29
our state can be over crowded
7 . 30
SOLUTION: store modules
7 . 31
We go from having all the state in one store to having
many stores
7 . 32
modules
const store = new Vuex.Store({
state: {
count: 0
},
modules : {
moduleA,
moduleB
}
})
7 . 33
We define a store module, just like a normal store
7 . 34
const moduleB = {
state: {
b: 'bbbb'
},
mutations: {
change(state, val) {
console.log('local state', state.b);
console.log('global state', this.state.moduleB.b);
this.state.moduleB.b = val;
}
}
}
7 . 35
SUMMARYSUMMARY
8 . 1
We had a look at Vue and we tried to cover
Components
Routing
Testing
Vuex
8 . 2
FURTHER READINGFURTHER READING
8 . 3
Official documentation, https://vuejs.org/v2/guide/
The rest of the core team, well worth following,
https://vuejs.org/v2/guide/team.html
8 . 4
Good bye and thank you for listening
@chris_noring,
https://github.com/so chris/vuejs-book
8 . 5

Vue fundamentasl with Testing and Vuex