Vue — Deep dive to build a vue app experience

Kevin Hu
ITNEXT
Published in
7 min readFeb 9, 2018

--

In the past more year, Vue has upgraded from 1 to 2, vue-cli became more and more powerful and Vue announced changed vue-resource to vue-axios. Following are some useful information when you use Vue to build an application. We can discuss more better solution. :)
(this article is suit for someone already use vue.)

1. About project setting by vue-cli:

Using:

vuex, vue-router, vuex, karma or jest (depend on your vue-cli version), nightwatch (I prefer cypress).

Structure (In src folder):

  • components (Components in here can work by itself even out of this project)
  • common (Gloabal project components like header, footer…etc.)
  • pages (You can add more sub page folder and sub componenet inside bigger page.)
  • store (Use module folder to categorize your state.)
  • api (About some async request setting, here I seperate api and action.)
  • router (vue-router and relative setting.)
  • js (Global project js file like util.js, constants.js…etc.)
  • stylus / sass/scss / less..etc. (Global project style setting.)
  • stories (storybook)
  • locales (vue-i18n)
  • mixins
  • directive

About webpack alias:

// in webpack.bas.conf.js
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'), // vue-cli default setting
'styl': resolve('src/stylus'), // for stylus using
'~assets': resolve('src/assets'), // for js using
'assets': resolve('src/assets') // for stylus using
}
},

We can import file like “@/components/IModal” in js. How about image src? Here I use “~assets/example.png”. Why? Because when we writing style in css preprocessor scope, the alias setting must add “~” to use.

<style lang="stylus" scoped>
@import '~styl/variables' // Here we have add more "~" to use the alias setting in stylus scope.
div {
background-image: url('~assets/select-arrow-double.svg') // Here we have add more "~" to use the alias setting in stylus scope.
}
</style>

So '~assets': resolve('src/assets') is for js. Than we can write the same setting in js/css.
I seperate it from all ‘@’ to js, css, img(assets).
If the project structure is changing, we just need to change the alias setting.

What’s wrong? Unit test is broken!

It’s very complicated to brief, no matter in karma(old vue-cli) or jest(new vue-cli) with your webpack setting. You may face some problem like es6, import library which use es6, ignore css preprocessor, img…etc.

// in test/unit/index.js (old vue-cli karama setting ) be careful of the test scope and file.
// ignore api, stylus folder..etc.
const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$|(?:api|stylus|stories)(?![^\/]))/)

Add storybook

As now, it’s very easy to use storybook in vue project. Some bug is already resovle.

npm i -g @storybook/cli
cd my-vue-project
getstorybook
npm run storybook

You can check more detail in my github:
https://github.com/sky790312/new-website

2. Using vue to develop:

Discuss the data init and change:

You may use vuex for initial data and pass to component, you want to change it but you can’t change the state directly. So you must declare component’s data. The scenario like editing user information, but sharing user’s state in project. There are some solution:

  • Init the value in beforeMount /mounted /beforeRouterEnder…etc by vuex to use. Change vuex / change component’s data depend on your case. You may have sub component and change user state in other component. If you are using realtime database like “firebase”, the user state may change not only when you editing in edit page but anywhere and anytime. So you may need more “watch” to change your component’s data and handle your vuex.
  • Use watch with immediately, then you don’t have write the same code in “beforeMount” and “watch”.(add method handle like “handleInit()” use both in beforeMount and “watch”.)
  • Use computed and data. Be careful use “get” to get vuex state and “set” to call action and change vuex state.

About the async data:

  • Must be careful the rendor lifecycle! The most easy way is use “xxxReady” v-if to prevent sub component rendor if your data is not ready and trigger error. You have to clarify your app’s flow. When and what should need to be ready to prevent the template render error.

Can’t call vue method in component data:

  • You may config some same vue data to v-for. Take a look:
// html template
<i-tab :isCentered="false">
<i-tab-pane
v-for="iTab in iTabs
:key="iTab.name"
:name="iTab.name"
:to="iTab.to"
:method="iTab.method">
</i-tab-pane>
</i-tab>
// js
// this iTabs config can't use directly in vue data, you can use compueted.
iTabs () {
const { name = '' } = this.$route.query
return [{
name: 'tabA',
to: `/buildings/${name}`,
method: () => {
this.handleTabA()
}
}, {
name: 'tabB',
to: `/buildings/${name}`,
method: () => {
this.handleTabB()
}
}]

Event likes buttons. We may want to write the less code by v-for and config in js. You can use computed.

Be careful of vue-i18n when you want to change the language in client site:

If we can change the language in client site, we may face some problem when we showing / editing and changing language. watch your lang, and set the editing component data. Some case like select option. or some data just initial in component mount not in template.

If you have some generator component, the more complicated should be handle:

Some generator component like “IForm”, “ITable”, you may config it in your vue data/computed. All of we mention should be careful!

Mixins is good, but you may not found the method in component.

Mixins can’t import like:

import { 
toUpperCase,
sort,
...
} from '@/js/utils'
// import and use it to vue method.

So it may hard to debug where the method is. But it’s still a good thing to use mixins data/method. Just need more careful!

Use vuex to handle async request.

I like to use vuex to controll all async request. All async request handle in action, but not all action need to handle async request.

3. About HTML in vue:

Be careful of html in vue:

Vue html template rendor continuously. We may face some troubles to write something like “user.plan.id” in html which from component or vuex.

// You may fetch some async data. You must make sure when your html template render this line, your object is already define! (user.id may from "undefined" to "some value", but when user.plan is"undefined", user.plan.id will trigger error!)You can define your data structure fully. Or you can use v-if to make sure your flow and the component will render correct without problem.

You may import some method from js file like util.js. If you want to use it directly in vue html template, it may trigger error because when vue render, vue doesn’t know what’s the method is.

<template><select>
<option
v-for="country in countriesList" // use it directly may trigger error!
:key="country.id">
</option>
...
</select>
</template>
// You can import it and attach it to vue method like:
import {
countriesList
} from '@/util'

methods: {
countriesList,
...

If it use frequently, you can add it into vue prototype directly.

“v-if” is very easy to use, move two more logic to js control:

<div
v-if="itemType === 'list' && isPayUser()"
class="container">
...
</div>
// replace this to..
<div
v-if="shouldShowList()"
class="container">
...
</div>
<div
:type="itemType === 'list' ? 'list' : 'item'"
class="container">
...
</div>
<div
:type="getItemType()"
class="container">
...
</div>
// replace this to..

Be careful of using v-if v-else with v-for, v-for execute before v-if. Don’t use more html tag, use “template”

// don't use html tag, you can use more template tag
<template v-if="isBrowserSupport">
</template>
...
<template v-else>
</template>

v-if in v-for: https://github.com/vuejs/vue/issues/3106

4. About CSS in vue:

Project style and component style

// No matter the component create by yourself, or by library/framework. Component should have itself style even in other projects. This kind of component like modal, dropdown, table, alert, datepicker..etc. If you create your own components. Be careful of mixing the project style into your component. (same to js config)

Global project style setting

It’s one of solutions to use common style in your project:

// in global stylus setting, you may have variable.stylus, extend,stylus..etc.
// in App.vue, use it without "scoped"
<style lang="stylus">
@import '~styl/index'
</style>
// use "scoped" vue way to have css scoped for all components.
<style lang="stylus" scoped>
@import '~styl/variables'
#app {

You may face changing component style out of component, but it’s already rendor with data-xxxx.
You can change it by “>>>”:

// in App.vue
<style lang="stylus" scoped>
>>> .i-modal {
...
}

Class setting

Set class name directly to normal html tag and use js to config vue component.

// assign it's class name directly to normal html tag.
<button
class="btn btn-round btn-primary"
:disabled="isUserExpired()"
@click="handleEditItem()">
{{ $t('button.update') }}
</button>
// assign it's class name setting to vue component(If the component create by yourself).
// some plugin maybe control style by js directly
<i-modal
v-if="exampleModal.options.shouldShow"
:className="exampleModal.className"
:options="exampleModal.options">
</i-modal>

Should style control by js or css?

Complex way use js, easy for css. Still, some animation must control it directly.

// control in js
<i-icon
:name="socialIcon.name"
:color="socialIcon.color"
:width="socialIcon.width"
:height="socialIcon.height">
</i-icon>
// replace this to..
<i-icon
:name="socialIcon.name"
:className="socialIcon.className">
</i-icon>
// you may have default icon style, and setting your project style by class.

Transation

All the principle is same, if the animation is complex, or can be reuse even combine with complex rule. You should use vue transation. If you just want a fade in after two second. Just use css directly. Try to use js to config and control style by css.

// html
<transition :name="animationType">
<keep-alive>
<router-view id="view"></router-view>
</keep-alive>
</transition>
// js
this.animationType =
animationType === 1 ? 'slide-left' :
animationType === 2 ? 'slide-right' :
animationType === 3 ? 'slide-top' :
animationType === 4 ? 'slide-bottom' : ''

There are some transation example in vue official website, and you can extend it by yourself or just use some library.

Conclusion

It’s a good experience to use vue to develop appplication. Vue become more powerful now! I wish that vue will get more attention in any workplace. Welcome to discuss the best way to develop vue project. Thanks for reading this post. :)

Thanks

--

--

— — Frontend Developer —— my personal website: https://sky790312-v2.web.app — — — — — — — — — — — my consultation site: https://f2e-camp.web.app/