VueJS-Cheatsheet
文章目录

VueJS: Cheatsheet

Basic

Expressions

1
2
3
4
5
6
<div id="app">
<p>I have a {{ product }}</p>
<p>{{ product + 's' }}</p>
<p>{{ isWorking ? 'YES' : 'NO' }}</p>
<p>{{ product.getSalePrice() }}</p>
</div>

See: Delimiters

Binding

1
<a v-bind:href="url">...</a>

Shorthand syntax

1
<a :href="url">...</a>

True or false will add or remove attribute

1
<button :disabled="isButtonDisabled">...

If isActive is truthy, the class ‘active’ will appear

1
<div :class="{ active: isActive }">...

Style color set to value of activeColor

1
<div :style="{ color: activeColor }">

See: v-bind

Directives

Element inserted/removed based on truthiness

1
<p v-if="inStock">{{ product }}</p>
1
2
<p v-else-if="onSale">...</p>
<p v-else>...</p>

Toggles the display: none CSS property

1
<p v-show="showProductDetails">...</p>

Two-way data binding

1
<input v-model="firstName" >
usage desc
v-model.lazy="..." Syncs input after change event
v-model.number="..." Always returns a number
v-model.trim="..." Strips whitespace

See: Directives

Actions/Events

Calls addToCart method on component

1
<button v-on:click="addToCart">...

Shorthand syntax

1
<button @click="addToCart">...

Arguments can be passed

1
2
<button @click="addToCart(product)">...
}

To prevent default behavior (e.g.page reload)

1
<form @submit.prevent="addProduct">...

Only trigger once

1
<img @mouseover.once="showImage">...
usage desc
.stop Stop all event propagation
.self Only trigger if event.target is element itself

Keyboard entry example

1
<input @keyup.enter="submit">

Call onCopy when control-c is pressed

1
<input @keyup.ctrl.c="onCopy">

See: Events

List rendering

1
2
3
4
<li v-for="item in items"
:key="item.id">
{{ item }}
</li>

To access the position in the array

1
<li v-for="(item, index) in items">...

To iterate through objects

1
<li v-for="(value, key) in object">...

Using v-for with a component

1
2
3
<cart-product v-for="item in products"
:product="item"
:key="item.id">

See: List Rendering

Component

Component anatomy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Vue.component('my-component', {
components: {
// Components that can be used in the template
ProductComponent,
ReviewComponent
},
props: {
// The parameters the component accepts
message: String,
product: Object,
email: {
type: String,
required: true,
default: "none"
validator: function (value) {
// Should return true if value is valid
}
}
},
data: function() {
// `data` must be a function
return {
firstName: 'Vue',
lastName: 'Mastery'
}
},
computed: {
// Return cached values until dependencies change
fullName: function () {
return this.firstName + ' ' + this.lastName
}
},
watch: {
// Called when firstName changes value
firstName: function (value, oldValue) { ... }
},
methods: { ... },
template: '<span>{{ message }}</span>',
// Can also use backticks in `template` for multi-line
})

See: Components Basics

Lifecycle hooks

event desc
beforeCreate After the instance has been initialized #
created After the instance is created #
beforeMount Before the first render #
mounted After the instance has been mounted #
beforeUpdate When data changes, before the DOM is patched #
updated After a data change #
beforeDestroy Before the instance is destroyed #
destroyed After a Vue instance has been destroyed #

See: Lifecycle Hooks

Custom events

Set listener on component, within its parent

1
<button-counter v-on:incrementBy="incWithVal">

Inside parent component

1
2
3
methods: {
incWithVal: function (toAdd) { ... }
}

Inside button-counter template

1
2
3
4
this.$emit(
'incrementBy', // Custom event name
5 // Data sent up to parent
)

Use props to pass data into child components, custom events to pass data to parent elements.

See: Custom Events

#Single file components

Single file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<p>{{ greeting }} World!</p>
</template>

<script>
module.exports = {
data: function () {
return {
greeting: 'Hello'
}
}
}
</script>

<style scoped>
p {
font-size: 2em;
text-align: center;
}
</style>

See: Single File Components

Separation

1
2
3
4
5
<template>
<div>This will be pre-compiled</div>
</template>
<script src="./my-component.js"></script>
<style src="./my-component.css"></style>

See: What About Separation of Concerns?

#Slots

Using a single slot

Component template

1
2
3
4
5
6
<div>
<h2>I'm a title</h2>
<slot>
Only displayed if no slot content
</slot>
</div>

Use of component with data for slot

1
2
3
<my-component>
<p>This will go in the slot</p>
</my-component>

See: Slots

Multiple slots

Component template

1
2
3
4
5
6
7
8
9
10
11
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot>Default content</slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>

Use of component with data for slots

1
2
3
4
5
<app-layout>
<h1 slot="header">Page title</h1>
<p>the main content.</p>
<p slot="footer">Contact info</p>
</app-layout>

See: Slots

Vue-Router

Detect Route Change

1
2
3
4
5
6
7
8
watch: {
$route(to, from) {
this.$store.dispatch('onHomeRouteChange', {
to,
from,
});
},
},

Route navigation guards

这个模式设计下整个APP要被切割成不同的页面.
并且当路由转换的时候我们会获取到当前页面所需要的数据. 可以通过一些勾子来实现:

1
2
3
4
5
6
7
8
9
import axios from 'axios';
//比如说这个地方就在路由转换之前进行了一些操作
router.beforeRouteEnter((to, from, next) => {
axios.get( `/api${to.path}` ).then(({
data
}) => {
next(vm => Object.assign(vm.$data, data))
});
})

Pros

  • UI 设计变得更加可预测

Cons

  • 在数据准备好之前, 页面不可能加载出来.
  • 有些时候甚至需要使用本地缓存.
  • 如果你不使用路由的话, 这个东西根本一点用都没有.

VueJS: AJAX Pattern for vuejs

  1. Root instance
  2. Components
  3. Vuex actions
  4. Route navigation guards

Root Instance

这个其实很容易理解, 就是在当前页面加载的时候, 将所有的初始数据先给处理好.
然后在需要刷新数据的时候将有一个自定义方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
new Vue({
data: {
message: ''
},
methods: {
refreshMessage(resource) {
this.$http.get('/message').then((response) {
this.message = response.data.message;
});
}
}
})
Vue.component('sub-component', {
template: '<div>{{ message }}</div>',
props: ['message']
methods: {
refreshMessage() {
this.$emit('refreshMessage');
}
}
});

Pros

  • 将所有的网络请求放在同一个地方.
  • 将逐渐变为傻瓜式的, 这样就可以专注在展示数据之上

Cons

  • 当项目变大的时候, 这些自定义方法就会变得越来越多

Components

在这种模式下对应的每一个组件会自己管理自己的 state
比如一个 contact-list, 内部可能包含多个 contact-item, 在 contact-list 建立的时候就应该包含 AJAX 逻辑, 然后将获取到的信息保存起来
然后这个模式建议通过 mixin 执行 ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let mixin = {
methods: {
//mixin 里面执行对应的 AJAX
callAJAX(resource) {
...
}
}
}
Vue.component('container-comp', {
// No meaningful template, I just manage data for my children
template: '<div><presentation-comp :mydata="mydata"></presentation-comp></div>',
mixins: [myMixin], //mix 到对应的 components 里面
data() {
return {
...
}
},
})
Vue.component('presentation-comp', {
template: < div > I just show stuff like {
{
mydata
}
} < /div>,
props: ['mydata']
})

Pros

  • 保持组件可以复用
  • 按需请求 AJAX真是

Cons

  • 组件较多的时候不是很方便进行数据交换
  • 很有可能对一些组件会设计出重复的功能

VueX Actions

这种模式是将状态以及对应的 AJAX 逻辑放到 Vuex Store 里面, 看看组件可以通过 dispatch action 来获取数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
store = new Vuex.Store({
state: {
message: ''
},
mutations: {
updateMessage(state, payload) {
state.message = payload
}
},
actions: {
refreshMessage(context) {
return new Promise((resolve) => {
// 这里执行 http 请求
this.$http.get('...').then((response) => {
context.commit('updateMessage', response.data.message);
resolve();
});
});
}
}
});
Vue.component('my-component', {
template: '<div>{{ message }}</div>',
methods: {
refreshMessage() {
this.$store.dispatch('refeshMessage').then(() => {
// 就是在这里 Dispatch action
});
}
},
computed: {
message: {
return this.$store.state.message;
}
}
});

Pros

  • 包含了类似上方 Root Instance 模式的优点

Cons

  • Vuex 增加了一些开销

TroubleShooting

‘v-for’ directives require ‘v-bind:key’ directives.

使用 v-for 的时候记得带上 :key
注意 key 是放到 v-for 的循环体里面

method 里面无法获取到 props //Cannot access props in methods

Best Practise:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
props: {
headers: Array,
dataIdentifier: String,
},
computed: {
...mapState({
getHeader(state) {
return this.header;
},
data(state) { // 不用使用箭头函数。
return state.data[this.dataIdentifier];
},
}),
},
1
2
3
4
<DataTable :colorColumns="['feeling', 'graphic', 'sound', 'gameplay','score']" :headers="[
{ text: '名称', value: 'gameName' },
{ text: '平均分', value: 'score' },
]" dataIdentifier="gameLog" // 这里我们将这个字符串传进去, 没有使用 bind 模式 />

Hints:

  1. 不要使用箭头函数,不要使用箭头函数。
  2. 关于调用的细节:
    1. 如果是传一个数组, 需要使用 v-bind: 或者直接加一个冒号.
    2. 如果是传一个字符串,那么就千万不要加冒号。

Reference