Notes: All the examples below are only tested on Vue.js 1.0 (above).
Notes: The examples below use ES6 so it's recommended to use browserify or webpack to easily integrate babel.
When you need to access DOM attribute, be careful with the lifecycle. Vue.js has a few useful lifecycle hooks.
Let's say we want to scroll our component to the bottom (imagine it's a long list with overflow-y: auto
) once it's instantiate.
We need to access this.$el
property to get this done.
new Vue({
el: '#ele',
created() { console.log(this.$el) }, // null
beforeCompiled() { console.log(this.$el) }, // div#ele ...
...
ready() { console.log(this.$el) } // div#ele ...
});
After the beforeCompiled
hook, we can start using this.$el
.
If you had experiences in React.js and coming to Vue.js, you will notice something is missing in the lifecycle hooks. How do I get updates of my data changes. Let's look at a more concrete example. Imagine we are building a chat application. When we have new message, we want to scroll the chat list to the bottom. In React.js:
// <ul ref="longList" messages={this.state.messages}>...</ul>
React.createClass({
...
componentDidUpdate: function() {
/* when state.messages get updated */
var node = findDOMNode(this.refs.longList);
node.scrollTop = node.scrollHeight;
}
},
...
});
In Vue.js we do not have componentDidUpdate
lifecycle but what we have is $watch. You can watch a value and get notified when it changes.
// <ul :messages="messages">...</ul>
new Vue({
...
props: ['messages'],
watch: {
'messages': function(val, oldVal) {
this.$nextTick(function() {
this.$el.scrollTop = this.$el.scrollHeight;
});
}
}
...
});
Notice that you will need this.$nextTick
or setTimeout()
. This is because as the documentation stated:
By default, Vue.js performs DOM updates asynchronously. ... Then, in the next event loop “tick”, Vue flushes the queue and performs only the necessary DOM updates.
It is so easy to create a new Vue component using existing 3rd party modules as long as the module used is designed for modularity. Some good example including dragula, pikaday.
Let's wrap a Vue datepicker with the pikaday module.
This example expects you to use the .vue
extension approach to write Vue.js application. So here is how we will use our datepicker.
<template>
// ... other stuff
<v-datepicker :value.sync="value"></v-datepicker>
</template>
<script>
import datepicker from '../_components/datepicker.vue'
new Vue({
// ...
components: {
'v-datepicker': datepicker
},
// ...
})
</script>
Let's see how we can create our component.
// datepicker.vue
<template>
<input type="text" v-model="value">
</template>
<script>
import Pikaday from 'pikaday'
import moment from 'moment'
export default {
props: ['value'],
ready() {
let picker = new Pikaday({
field: this.$el,
onSelect: function() {
this.value = picker.toString()
}.bind(this)
})
}
}
</script>
// your own style
// no more default bootstrap style hooray
We can take a step further to let our component accept more props like format
. For more configuration, you can have a look at pikaday documentation.
When you start your Vue.js application, the first thing you do is probably getting data from your backend or api. Let's say we have a list of comments from our backend server.
new App({
created() {
this.comments = this.loadCommentsFromServer(); // ajax etc
}
})
Doing this is totally fine but we can reduce this http request. So why not ? The tricks is to use HTML5 custom attribute. What we need to do is when our backend return rendered html root, we will insert the data as custom attribute.
// from any backend, syntax might different according to template engines
<div id="commentList" data-comments='{{ comments }}'>
... other components here
</div>
Note that you might need to wrap your template engine variable into a quote so that the json will be injected properly.
// example in PHP
// assume $comments is an array
<div id="commentList" data-comments='<?php echo json_encode($comments, JSON_HEX_APOS); ?>'>
... other components here
</div>
Here #commentList div is our Vue.js root component.
new Vue({
el: '#commentList'
created() {
this.comments = this.$el.dataset.comments
}
}
Website like airbnb is using this approach.
Hey! Instead of using a watcher to simulate
didComponentUpdate
you can actually use theupdated
lifecycle hook.You can also track if your child component has updated by using
@hook:updated="handler"
event listener.See examples:
https://jsfiddle.net/shentao/eywraw8t/367805/