Skip to content

Instantly share code, notes, and snippets.

@yang-wei
Last active April 19, 2021 14:49
Show Gist options
  • Star 35 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save yang-wei/30639fea44956a6467da to your computer and use it in GitHub Desktop.
Save yang-wei/30639fea44956a6467da to your computer and use it in GitHub Desktop.
Vue.js tips and tricks

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 or where can we use this.$el

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.

react style didComponentUpdate

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.

Wrapping Vue component

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.

Server side rendered data

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.

@shentao
Copy link

shentao commented Sep 15, 2018

Hey! Instead of using a watcher to simulate didComponentUpdate you can actually use the updated 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/

@bethfraser
Copy link

Thanks for this - the nextTick() after a watch() was exactly what I was looking for!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment