There are a number of style guides out there. Here a few important things that we should follow:
- 1. Linting / Auto-formatting
- 2. Naming conventions
- 3. Comments and JSDoc
- 4. Single statement
- 5. Vue 3 Best Practice
- Testing
- Debugging on Mobile Phone
The foremost goal is that reading and understanding your javascript code is easy for someone else (or yourself in a few months time).
For our javascript projects we are using Prettier to automatically format your code. We highly recommend to configure you IDE to format your code using Prettier on Save. Here below is a Prettier configuration example
printWidth: 100
singleQuote: true
semi: false
trailingComma: es5
tabWidth: 4
jsxSingleQuote: false
jsdocParser: true
jsdocDescriptionWithDot: true
jsdocVerticalAlignment: true
overrides:
- files: '*.md'
options:
tabWidth: 2
proseWrap: 'always'
Although formatting is good, it doesn't check for syntax errors or bad code practice. Therefore we also use a linter; Eslint
Usually we should have such ESlint configuration:
{
"env": {
"es2021": true,
"node": true
},
"plugins": ["prettier"],
"extends": [
"eslint:recommended",
"plugin:vue/essential",
"prettier",
"plugin:vue/recommended",
"plugin:cypress/recommended",
"plugin:prettier-vue/recommended",
],
"parserOptions": {
"ecmaVersion": 12,
"parser": "babel-eslint",
},
"rules": {
"camelcase": ["error", { "properties": "never" }],
"curly": "error",
"max-len": ["warn", { "code": 100 }],
"no-unused-vars": "warn"
}
}
There might be good reason to disable locally some linting messages. When doing this, the reason why we disable a rule should be documented next to the disable pragma.
For more detail in ignoring eslint issues see ESLint Disabling Rules
Javascript code must follow these naming conventions:
- file name: camelCase
- constant: UPPER_CASE
- variable: camelCase
- function/method: camelCase
- argument: camelCase
- class: PascalCase
Code is documented using JSDoc. Make sure to document every public class and functions.
ALWAYS enclosed with curly brace single if/else/while/do while/for single statements
❌ Incorrect
if (test) return
if (test)
doSomething()
while(1)
doSomething()
for (let i=0; i < test; i++) doSomething()
✅ Correct
if (test) {
return
}
if (test) {
doSomething()
}
while(1) {
doSomething()
}
for (let i=0; i < test; i++) {
doSomething()
}
See first Vue 3 Best Practice
-
Always use directive shorthands;
:
forv-bind
,@
forv-on
,#
forv-slot
-
Always use Single-file component whenever possible
-
Single-file component must always have the following element order
<script setup>/* ... */</script> <template>...</template> <style>/* ... */</style>
-
Use indentation within
<template></template>
tag but not on<script></script>
and<style></style>
tags<script setup> const myString = '<script></script> tag omit the first indentation level' const isScript = true if (isScript) { console.log(myString) } </script> <template> <div class="my-template"> <p>My HTML template is fully indented</p> </div> </template> <style scoped> .my-template { background-color: green; } </style>
-
Avoid using element selector with
scoped
(see Vue 3 Best Practice - Element selectors withscoped
), but usescoped
class or id selector instead.<!-- BAD --> <template> <button>X</button> </template> <style scoped> button { background-color: red; } </style>
<!-- GOOD --> <template> <button class="btn btn-close">X</button> </template> <style scoped> .btn-close { background-color: red; } </style>
-
Always add the
*.vue
extension in import of Vue single file component. This is required by some IDE (Vscode/Volar) and omitting the extension has been deprecated by Vue 3.❌ Incorrect
import MyComponent from "@/components/MyComponent"
✅ Correct
import MyComponent from "@/components/MyComponent.vue"
Don't use complex object (e.g. instance from an external library) in the data
section of a component (Option API) or wrapped in a ref
(Composition API). Refs and the data section are made deeply reactive which means that each object is recursively analysed by Vue and each object properties are proxied. This would have a negative impact on performance and might break the complex object.
❌ BAD
// BAD
import { Map } from 'ol'
export default {
data(): {
return {
myBoolean: true
myMap: new Map({ controls: [] })
}
},
}
import { ref } from 'vue'
import { Map } from 'ol'
const myBoolean = ref(true)
const myMap = ref(new Map({ controls: [] }))
✅ GOOD
// GOOD
import { Map } from 'ol'
export default {
data(): {
return {
myBoolean: true
}
},
created(): {
this.map = new Map({ controls: [] })
},
}
import { ref } from 'vue'
import { Map } from 'ol'
const myBoolean = ref(true)
const myMap = new Map({ controls: [] })
Don't use arrow function to define Option API component method (arrow function prevent Vue
from binding the appropriate this
value, see Vue 3 - Methods)
❌ BAD
// BAD
const app = Vue.createApp({
data() {
return {count: 0}
},
methods: {
increment: () => {
// `this` don't necessarily refer to the component depending on the context
this.count++
}
}
})
✅ GOOD
// GOOD
const app = Vue.createApp({
data() {
return {count: 0}
},
methods: {
increment() {
// `this` always refer to the component
this.count++
}
}
})
Also prefer the short syntax increment() {}
to increment: function() {}
Methods called from a template (except for event listeners) should not have any side effects, such as changing data or triggering asynchronous processes. If you find yourself tempted to do that you should probably use a lifecycle hook instead. For more infos about this see Vue 3 - Methods.
In general for kind of end to end test for web application we use Cypress.io.
For cypress best practice see Cypress Best practice
We also the following best practices:
- Don't create too much test case (
it()
)- Each test case cost on cypress cloud
- Each test case takes time to initialized
Debugging issue that only touch Mobile Phones, especially IPhones, can be quite difficult without a Mac. For this task you can use inspect.dev which works quite well.
This tool requires a subscriptions for troubleshooting on IOS devices, currently the following user have a yearly subscription:
- Brice Schaffner (private subscription)