Skip to content

Latest commit

 

History

History
495 lines (375 loc) · 12.8 KB

File metadata and controls

495 lines (375 loc) · 12.8 KB

Component Guide for Vue "1.5"

Do's and Don'ts

Don't use .sync on v-bind option

Don't use twoWay prop option

Don't mutate props in vm

// NOK
props: {
  externalProp: String
},

methods: {
  someMethod () {
    // don't mutate props!
    this.externalProp = 'newValue'
  }
}
// OK
// $emit
methods: {
  someMethod () {
    this.$emit('update:external-prop', 'newValue')
  }
}
// OK
// $emit through update method
methods: {
  someMethod () {
    this.updateExternalProp('newValue')
  },

  updateExternalProp (value) {
    this.$emit('update:external-prop', value)
  }
}
// OK
// mapOneWayProp creator
computed: {
  // will emit 'update:external-prop' event by default with the new value
  externalPropInt: mapOneWayProp('externalProp')
},

methods: {
  someMethod () {
    // you can assign safely
    this.externalPropInt = 'newValue'
  },
}
// OK
// mapOneWayProps creator
computed: {
  ...mapOneWayProps({
    'externalProp1Int': 'externalProp1', // will emit 'update:external-prop1' event by default with the new value
    'externalProp2Int': 'externalProp2', // will emit 'update:external-prop2' event by default with the new value
  })
},

methods: {
  someMethod () {
    // you can assign safely
    this.externalProp1Int = 'newValue1'
    this.externalProp2Int = 'newValue2'
  },
}
// OK
// get/set computed
computed: {
  externalPropInt: {
    get () {
      return this.externalProp
    },

    set (value) {
      this.$emit('update:external-prop', value)
      // or
      // this.updateExternalProp(value)
    }
  }
},

methods: {
  someMethod () {
    // you can assign safely
    this.externalPropInt = 'newValue'
  },

  // updateExternalProp (value) { ... }
}
// OK
// local data (initialized once, parent notified)
data () {
  return {
    someProp: this.externalProp // set initial value
  }
},

methods: {
  someMethod () {
    // you can assign safely
    this.someProp = 'newValue'
    // if you want to update the externalProp too
    this.$emit('update:external-prop', value)
    // or
    // this.updateExternalProp(value)
  },

  // updateExternalProp (value) { ... }
}

Don't mutate props via v-model in template

// NOK

// component/index.js
props: {
  externalProp: String
}

// component/template.html
// dont use v-model directly with props
<input type="text" v-model="externalProp">
// OK
// v-one-way-model

// component/index.js
props: {
  externalProp: String
}

// component/template.html
<input type="text" v-one-way-model="externalProp"> <!-- will emit `update:external-prop` event by default -->
// OK
// one way computed + v-model

// component/index.js
props: {
  externalProp: String
},

computed: {
  externalPropInt: mapOneWayProp('externalPropInt')
}

// component/template.html
<input type="text" v-model="externalPropInt">

Don't mutate props via expression in template

// NOK

// component/index.js
props: {
  externalProp: String
}

// component/template.html
// don't assign values directly to props
<button @click="externalProp='newValue'">
// OK
// handler method

// component/index.js
props: {
  externalProp: String
},

methods: {
  handleClick () {
    this.$emit('update:external-prop', 'newValue')
  }
}

// component/template.html
<button @click="handleClick">

Don't mutate elements of array prop or the array itself

// NOK
// foo/index.js
props: {
  externalArrayProp1: Array
},

methods: {
  someMethods () {
    // don't mutate array prop elements
    this.externalArrayProp1[0] = 'newValue1'

    // don't mutate array prop object element properties
    this.externalArrayProp1[0].prop1 = 'newValue2'

    // don't call mutable operations on array prop
    this.externalArrayProp1.push(...)
    this.externalArrayProp1.sort(...)
    this.externalArrayProp1.splice(...)
    // ...
  }
},

// foo/template.html
// don't mutate array prop elements
<input type="text" v-model="externalArrayProp1[0]"></input>

// don't mutate array prop object element properties
<input type="text" v-model="externalArrayProp1[0].prop1"></input>

// don't call mutable operations on array prop
<button @click="externalArrayProp1.sort()"><button>

Don't mutate properties of an object prop

// NOK
// foo/index.html
props: {
  externalObjectProp1: Object
},

methods: {
  someMethods () {
    // don't mutate properties of an object prop
    this.externalObjectProp1.prop1 = 'newValue1'

    // don't add new properties to an object prop
    this.externalObjectProp1.newProp1 = 'newValue2'

    // don't remove properties from an object prop
    delete this.externalObjectProp1.prop2
  }
}

// foo/template.html
// don't mutate properties of an object prop
<input type="text" v-model="externalObjectProp1.prop1"></input>

Don't mutate store data directly

// NOK
vuex: {
  getters: {
    vuexProp1: state => state.some.prop1
  }
},

methods: {
  someMethod () {
    // don't mutate store data directly
    this.vuexProp1 = 'newValue1'
    // nor do this
    this.$store.state.some.prop2 = 'newValue2';
  }
}

// also don't mutate them with `v-model` or expressions in template

Don't use this.$dispatch

  • Use $emit(s) if you want to reach some of the parent components. You may need to re-emit the same or another event per parent.
  • Use emit/emitOnNextTick if you want to reach a component/thing outside of the actual component hierarchy
  • Use Vuex action (action/mutation)

Don't use this.$broadcast

  • Use emit/emitOnNextTick
  • Use Vuex action (action/mutation)

Don't use events option

  • Use v-on (@) in the template if the event emitted with $emit. (You may use $on in vm in some cases but @ is preferred)
  • Use eventBusMixin if the event emitted with emit/emitOnNextTick. (You may use on as well but you have to clean up after yourself which is done by eventBusMixin automatically so that is the preferred way)
  • Use Vuex getters to react changes made by Vuex action if that is the case

Use filters only in mustache interpolation and v-bind expressions

<!-- in mustaches -->
{{ message | capitalize }}

<!-- in v-bind -->
<div :id="rawId | formatId"></div>

Don't use interpolation within attributes

<!-- NOK -->
<button class="btn btn-{{ size }}"></button>

<!-- OK -->
<button :class="'btn btn-' + size"></button>

<!-- OK -->
<button :class="buttonClasses"></button>

computed: {
  buttonClasses () {
    return 'btn btn-' + size
  }
}

Use v-html instead of HTML interpolation

  • so replace {{{ somePropWithHtml }}} with v-html

Tools

$emit

  • Useful to notify parent component about something (update of props, other events, etc.)

Doc: API/vm.$emit

emit/emitOnNextTick/eventBusMixin

  • Useful for emitting global events
  • Useful to notify components outside of the current component hierarchy (parents should be notified via $emit)
  • To listen on events in components prefer using eventBusMixin instead of on, because it cleans up automatically on component destroy
  • Note: the events are emitted outside of the component vue life cycle which means that some component prop may not changed when the listener runs, in this cases emitOnNextTick may help

Doc: app/scripts/util/event-bus

Doc: app/scripts/mixins/event-bus

get/set computed

computed: {
  someProp: {
    get () {
      // return local data
      // return other computed
      // return vuex prop
      // ...
    },

    set (value) {
      // set local data
      // $emit event
      // call vuex action
      // emit global event
      // ...
    }
  }
}

Pros

  • universal power tool
  • plays well with v-model and/or prop mutation in vm

Cons

  • you need to create a new prop with a new name (isLoading => isLoadingInt)
  • could be boilerplate => use the more compact mapOneWayProp/mapOneWayProps if applicable

map-one-way-prop/map-one-way-props

Doc: app/scripts/utils/component/map-one-way-prop

Doc: app/scripts/utils/component/map-one-way-props

Pros

  • plays well with v-model and/or prop mutation in vm
  • compact

Cons

  • you need to create a new prop with a new name (isLoading => isLoadingInt)
  • could be to simple, if you need more power => get/set computed

v-model

  • Watch out because it is a two way directive so it modifies the bound prop (which is ok for local data but NOK for props)
  • It can be used with one way data flow if the prop is a get/set computed
  • It can be used freely with local data

Doc: API/v-model

v-one-way-model

Note: use get/set computed or mapOneWayProp/mapOneWayProps instead until this gets implemented properly in Vue 2 (vue2 fork)

Doc: app/scripts/directives/one-way-model

Pros

  • works the same way as v-model except it is a one way directive so it emits an event and/or calls a callback method on change
  • you don't need to create a new prop and think of a new name for it

Cons