Dependency Injection
in Vue.js
Who Am I?
- Alex Riviere
- Web Developer
- Co-Organizer for Atlanta Vue.js Meetup
- Unicorn Enthusiast
Methods of Passing State Between Components
- Props/Emit
- External Store (Vuex)
- $root/$parent (Not Advised)
- Provide/Inject
Props Down, Events Up
<parent-component />
<child-component />
<grand-child-component />
$props
$props
$emit
$emit
Props Down, Events Up
- Creates reusable components
- Doesn't rely on outside data structures
- Easily testable
Global Store
<parent-component />
<child-component />
<grand-child-component />
Vuex $store
state
state
state
mutation
mutation
mutation
Global Store
Vuex $store
- Useful for large applications with shared state
- Can be used with other state management strategies
- Relies on external dependency
- Not ideal for publishing modules
$parent Knows Best
<parent-component />
<child-component />
<grand-child-component />
$parent.knowledge
$parent.$parent.knowledge
$parent Knows Best ?
<parent-component />
<grand-child-component />
$parent.$parent.knowledge?
$parent Knows Best ?
- Good for quick experiments
- Relies way too heavily on very specific structure
- Not ideal for production
$root of All Evil
<parent-component />
<child-component />
<grand-child-component />
$root.evil
$root of All Evil
<parent-component />
<child-component />
<grand-child-component />
$root.evil
<grand-parent-component />
$root of All Evil
- Single Source of truth
- Relies on single source of truth to exist in root component
- Can break if you change root structure
- Good for experimenting, Not advised for production
Dependency Injection
<parent-component />
<child-component />
<grand-child-component />
provide
inject
<child-component />
<child-component />
Dependency Injection
- Great for encapsulated state
- Provided data is not reactive
- Extremely useful in very specific cases
SHOW ME
HOW ALREADY!
<div>
<p>This is the count:</p>
<p>{{count}}</p>
<simple-inject/>
<button @click="count++"
class="button">
Increment!
</button>
</div>
SimpleProvide.vue
import SimpleInject from './SimpleInject';
export default {
name: 'simple-provide',
data(){
return {
count: 1,
};
},
provide(){
return {'count': this.count};
},
components: {
SimpleInject,
},
}
Template
Script
<div>
<p>This is the injected count:</p>
<p>{{count}}</p>
</div>
SimpleInject.vue
export default {
name: "simple-inject",
inject: ['count'],
}
Template
Script
Simple Demo!
Recap
- provide should be a function similar to data
- inject can be an array of strings
- Values returned from provide are not reactive by default
<div>
<p>{{message}}</p>
<p>^ This is the message</p>
<p><input type="text"
v-model="message"></p>
<button class="button"
@click="buttonClick">
Modify Nested Component
</button>
<promise-inject/>
</div>
PromiseProvide.vue
import PromiseInject from "./PromiseInject"
export default {
data() {
return {
message: "Hello World",
}
},
methods: {
buttonClick() {
this.$emit('button-push', this.message)
}
},
components: {
PromiseInject
},
provide() {
const hp = (res,rej) =>{
this.$on('button-push', res)
}
return {
providedPromise: new Promise(hp),
}
}
}
Template
Script
<div>
<p>I'm a nested component!</p>
<p>{{childMessage}}</p>
</div>
PromiseInject.vue
export default {
name: "promise-inject",
data: function () {
return {
childMessage: 'I am waiting on a promise!'
}
},
methods: {
changeChildMessage(response) {
this.childMessage = response
}
},
inject: {
providedPromise:{
type:Promise,
}
},
mounted: function () {
this.providedPromise
.then(this.changeChildMessage)
}
}
Template
Script
Promise Demo!
Recap
- By providing a promise, we can give data later
- inject can be formatted similar to the way props can be
- Promises only resolve once. So data is still not reactive.
How do we make this Reactive?
RxJS!!
NO.
<div>
<p>{{message}}</p>
<p>^ This is message</p>
<input type="text"
v-model="message">
<reactive-inject/>
</div>
ReactiveProvide.vue
import ReactiveInject from "./ReactiveInject"
export default {
data() {
return {
message: "Hello World",
}
},
provide() {
const get = ()=>this.message
const set = (v)=>this.message=v
return {
getParentMessage: get,
setParentMessage: set,
}
},
components: {
ReactiveInject
},
}
Template
Script
<div>
<p>I'm a nested component!</p>
<p>
<code>injectedMessage</code>
being reactive after injection:
</p>
<p>{{injectedMessage}}</p>
<p>
We can also set
<code>injectedMessage</code>
from this component
</p>
<p>
<input type="text"
class="input"
v-model="injectedMessage">
</p>
</div>
ReactiveInject.vue
export default {
name:"reactive-inject",
inject: [
"getParentMessage",
"setParentMessage",
],
computed: {
injectedMessage: {
get() {
return this.getParentMessage()
},
set(d) {
this.setParentMessage(d)
},
}
},
}
Template
Script
Reactive Demo
Recap
- By providing a scoped function, we can get updated injected values
- We can also update injected values with a provided function to set them in the parent
- This can be a headache if you don't know what is setting the value
Where should I use this?
- When you need internal state encapsulated from everything else
- Potentially if you are creating a series of components for handling known outcomes
- Themes!
- Maps!
Questions?
-
@fimion on twitter
Dependency Injection in Vue.js - First Draft
By Alex Riviere
Dependency Injection in Vue.js - First Draft
- 598