Skip to content

Commit

Permalink
docs: add composition api to style-guide (#2585)
Browse files Browse the repository at this point in the history
  • Loading branch information
Shinigami92 committed Nov 30, 2023
1 parent 80bb429 commit 8dc0a33
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 4 deletions.
4 changes: 0 additions & 4 deletions src/style-guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ outline: deep

# Style Guide {#style-guide}

:::warning Status Notice
The style guide is currently a bit outdated. Most examples are in Options API only, and there are no rules regarding `<script setup>` and Composition API. We are planning to improve it in the future.
:::

This is the official style guide for Vue-specific code. If you use Vue in a project, it's a great reference to avoid errors, bikeshedding, and anti-patterns. However, we don't believe that any style guide is ideal for all teams or projects, so mindful deviations are encouraged based on past experience, the surrounding tech stack, and personal values.

For the most part, we also avoid suggestions about JavaScript or HTML in general. We don't mind whether you use semicolons or trailing commas. We don't mind whether your HTML uses single-quotes or double-quotes for attribute values. Some exceptions will exist however, where we've found that a particular pattern is helpful in the context of Vue.
Expand Down
39 changes: 39 additions & 0 deletions src/style-guide/rules-essential.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Detailed [prop definitions](/guide/components/props#prop-validation) have two ad
:::

<div class="options-api">

<div class="style-example style-example-bad">
<h3>Bad</h3>

Expand Down Expand Up @@ -83,9 +84,11 @@ props: {
```

</div>

</div>

<div class="composition-api">

<div class="style-example style-example-bad">
<h3>Bad</h3>

Expand Down Expand Up @@ -123,6 +126,7 @@ const props = defineProps({
```

</div>

</div>

## Use keyed `v-for` {#use-keyed-v-for}
Expand All @@ -132,6 +136,8 @@ const props = defineProps({
::: details Detailed Explanation
Let's say you have a list of todos:

<div class="options-api">

```js
data() {
return {
Expand All @@ -149,6 +155,25 @@ data() {
}
```

</div>

<div class="composition-api">

```js
const todos = ref([
{
id: 1,
text: 'Learn to use v-for'
},
{
id: 2,
text: 'Learn to use key'
}
])
```

</div>

Then you sort them alphabetically. When updating the DOM, Vue will optimize rendering to perform the cheapest DOM mutations possible. That might mean deleting the first todo element, then adding it again at the end of the list.

The problem is, there are cases where it's important not to delete elements that will remain in the DOM. For example, you may want to use `<transition-group>` to animate list sorting, or maintain focus if the rendered element is an `<input>`. In these cases, adding a unique key for each item (e.g. `:key="todo.id"`) will tell Vue how to behave more predictably.
Expand Down Expand Up @@ -214,6 +239,8 @@ Will throw an error, because the `v-if` directive will be evaluated first and th

This could be fixed by iterating over a computed property instead, like this:

<div class="options-api">

```js
computed: {
activeUsers() {
Expand All @@ -222,6 +249,18 @@ computed: {
}
```

</div>

<div class="composition-api">

```js
const activeUsers = computed(() => {
return users.filter((user) => user.isActive)
})
```

</div>

```vue-html
<ul>
<li
Expand Down
65 changes: 65 additions & 0 deletions src/style-guide/rules-recommended.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ This is the default order we recommend for component options. They're split into

When components begin to feel cramped or difficult to read, adding spaces between multi-line properties can make them easier to skim again. In some editors, such as Vim, formatting options like this can also make them easier to navigate with the keyboard.

<div class="options-api">

<div class="style-example style-example-bad">
<h3>Bad</h3>

Expand Down Expand Up @@ -156,6 +158,7 @@ computed: {
}
}
```

</div>

<div class="style-example style-example-good">
Expand Down Expand Up @@ -188,6 +191,68 @@ computed: {

</div>

</div>

<div class="composition-api">

<div class="style-example style-example-bad">
<h3>Bad</h3>

```js
defineProps({
value: {
type: String,
required: true
},
focused: {
type: Boolean,
default: false
},
label: String,
icon: String
})
const formattedValue = computed(() => {
// ...
})
const inputClasses = computed(() => {
// ...
})
```

</div>

<div class="style-example style-example-good">
<h3>Good</h3>

```js
defineProps({
value: {
type: String,
required: true
},

focused: {
type: Boolean,
default: false
},

label: String,
icon: String
})

const formattedValue = computed(() => {
// ...
})

const inputClasses = computed(() => {
// ...
})
```

</div>

</div>

## Single-file component top-level element order {#single-file-component-top-level-element-order}

**[Single-File Components](/guide/scaling-up/sfc) should always order `<script>`, `<template>`, and `<style>` tags consistently, with `<style>` last, because at least one of the other two is always necessary.**
Expand Down
101 changes: 101 additions & 0 deletions src/style-guide/rules-use-with-caution.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ An ideal Vue application is props down, events up. Sticking to this convention m

The problem is, there are also many _simple_ cases where these patterns may offer convenience. Beware: do not be seduced into trading simplicity (being able to understand the flow of your state) for short-term convenience (writing less code).

<div class="options-api">

<div class="style-example style-example-bad">
<h3>Bad</h3>

Expand Down Expand Up @@ -148,3 +150,102 @@ app.component('TodoItem', {
```

</div>

</div>

<div class="composition-api">

<div class="style-example style-example-bad">
<h3>Bad</h3>

```vue
<script setup>
defineProps({
todo: {
type: Object,
required: true
}
})
</script>
<template>
<input v-model="todo.text" />
</template>
```

```vue
<script setup>
import { getCurrentInstance } from 'vue'
const props = defineProps({
todo: {
type: Object,
required: true
}
})
const instance = getCurrentInstance()
function removeTodo() {
const parent = instance.parent
if (!parent) return
parent.props.todos = parent.props.todos.filter((todo) => {
return todo.id !== props.todo.id
})
}
</script>
<template>
<span>
{{ todo.text }}
<button @click="removeTodo">×</button>
</span>
</template>
```

</div>

<div class="style-example style-example-good">
<h3>Good</h3>

```vue
<script setup>
defineProps({
todo: {
type: Object,
required: true
}
})
const emit = defineEmits(['input'])
</script>
<template>
<input :value="todo.text" @input="emit('input', $event.target.value)" />
</template>
```

```vue
<script setup>
defineProps({
todo: {
type: Object,
required: true
}
})
const emit = defineEmits(['delete'])
</script>
<template>
<span>
{{ todo.text }}
<button @click="emit('delete')">×</button>
</span>
</template>
```

</div>

</div>

0 comments on commit 8dc0a33

Please sign in to comment.