Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add composition api to style-guide #2585

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>