Appearance
Vue Basics
Links
Vue uses vue-router for links
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/foo">Go to Foo</router-link>
https://router.vuejs.org/guide/
Getting Started | Vue Router
https://router.vuejs.org/api/#router-link
API Reference | Vue Router
Single File Components
A concise way to combine the markup (<template>
), logic (<script>
) and styling (<style>
) in a single .vue file.
Often makes use of the "Options API" (vs. the "Composition API" in vue3), but not exclusively.
Web components done right!
https://v3.vuejs.org/guide/single-file-component.html
https://v3.vuejs.org/guide/component-basics.html
https://vuejs.org/v2/guide/components.html#Dynamic-Components Components Basics — Vue.js
<script>
export default {
data() {
return {
greeting: 'Hello World!'
}
}
}
</script>
<template>
<p class="greeting">{{ greeting }}</p>
</template>
<script>
export default {
data() {
return {
greeting: 'Hello World!'
}
}
}
</script>
<template>
<p class="greeting">{{ greeting }}</p>
</template>
Templates
https://vuejs.org/v2/guide/syntax.html
Conditional Rendering
https://vuejs.org/v2/guide/conditional.html
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
For Loops
List rendering
https://vuejs.org/v2/guide/list.html#key
https://stackoverflow.com/questions/44617484/vue-js-loop-via-v-for-x-times-in-a-range
javascript - Vue Js - Loop via v-for X times (in a range) - Stack Overflow
<li v-for="n in 10" :key="n">{{ n }} </li>
<li v-for="n in 10" :key="n">{{ n }} </li>
See all keys / values in a given object:
<ul>
<li v-for="key in Object.keys(item)" :key="key">
{{ key }}: {{ item[key] }}
</li>
</ul>
<ul>
<li v-for="key in Object.keys(item)" :key="key">
{{ key }}: {{ item[key] }}
</li>
</ul>
Scripts
The script block is where you put logic related to your component.
data is made up of properties
computed is made up of getters.
via:
https://stackoverflow.com/questions/58931647/nuxt-component-computed-vs-data
Computed
A computed method will respond to changes made to data properties of the component. Similar to watch. Like a method that is called automatically.
Note: In Vue3 (maybe Vue2 also?), the computed function must be referenced somewhere in the template, otherwise it will not ever trigger!
Binding values
How to share data between the <template>
and <script>
?
Typically, just use a v-model to handle coordinating values.
<input v-model="search">
<input v-model="search">
Some cases where it's easier to separate the value from what action you want to take when events occur
<textarea
class="resize min-h-screen w-3/4 m-auto"
:value="articleString"
@input="updateObject"
/>
<textarea
class="resize min-h-screen w-3/4 m-auto"
:value="articleString"
@input="updateObject"
/>
methods: {
updateObject(event) {
console.log('updateObject', event.target.value)
}
}
methods: {
updateObject(event) {
console.log('updateObject', event.target.value)
}
}
https://dilshankelsen.com/v-model-with-vuex/
How To Use V-Model With Vuex | Dilshan Kelsen
Styles (CSS)
Styling will depend a lot on how you configure your application, which front-end CSS framework you choose, etc.
Reminder: Anything one-off or customizable should go in the corresponding web-compenent file. A CSS utility framework like Tailwind makes this even more streamlined!
Attributes
Trigger if an attribute is added to a DOM element using a boolean value in the script. For example
:disabled="foo"
:disabled="foo"
<textfield label="Name" value.sync="el.name" :disabled="myVar">
<textfield label="Name" value.sync="el.name" :disabled="myVar">
Then in Vue you can just set this.myVar = true and it will disable the input.
https://stackoverflow.com/questions/39247411/how-to-add-dynamically-attribute-in-vuejs
It's possible to use a similar approach to add a class conditionally:
:class="{ 'light-background': index % 2 }"
:class="{ 'light-background': index % 2 }"
Dynamic Styles
Using variable in vue component to affect CSS styles
https://stackoverflow.com/questions/47322875/vue-js-dynamic-style-with-variables
css - Vue.js dynamic style with variables - Stack Overflow
https://stackoverflow.com/questions/42872002/in-vue-js-component-how-to-use-props-in-css/52280182#52280182
In vue.js component, how to use props in css? - Stack Overflow
<template>
<div id="a" :style="style" @mouseover="mouseOver()">
</div>
</template>
<script>
export default {
name: 'SquareButton',
props: ['color'],
computed: {
style () {
return 'background-color: ' + this.hovering ? this.color: 'red';
}
},
data () {
return {
hovering: false
}
},
methods: {
mouseOver () {
this.hovering = !this.hovering
}
}
}
</script>
<style scoped>
<style>
<template>
<div id="a" :style="style" @mouseover="mouseOver()">
</div>
</template>
<script>
export default {
name: 'SquareButton',
props: ['color'],
computed: {
style () {
return 'background-color: ' + this.hovering ? this.color: 'red';
}
},
data () {
return {
hovering: false
}
},
methods: {
mouseOver () {
this.hovering = !this.hovering
}
}
}
</script>
<style scoped>
<style>
:style="{
top: marginTop,
left: marginLeft,
width: maxSquare,
height: maxSquare,
}"
:style="{
top: marginTop,
left: marginLeft,
width: maxSquare,
height: maxSquare,
}"
generating the whole style dictionary in a computed didn't work: :style="margins"
See also ~/design_system/ui/pages/windows.vue
Custom Events / Event Bus
If you need to signal a parent component of something that has happened in a child component, use $emit.
Child component triggers clicked event:
export default {
methods: {
onClickButton (event) {
this.$emit('clicked', 'someValue')
}
}
}
export default {
methods: {
onClickButton (event) {
this.$emit('clicked', 'someValue')
}
}
}
Parent component receive clicked event:
<div>
<child @clicked="onClickChild"></child>
</div>
<div>
<child @clicked="onClickChild"></child>
</div>
Then, in the parent script block, handle the emitted event as needed:
export default {
methods: {
onClickChild (value) {
console.log(value) // someValue
}
}
}
export default {
methods: {
onClickChild (value) {
console.log(value) // someValue
}
}
}
via: https://forum.vuejs.org/t/passing-data-back-to-parent/1201
For multiple levels of children components, there is vm.$listeners
https://vuejs.org/v2/api/#vm-listeners
Note:
If you've hit a situation where an event bus pattern comes up, it may be a good time to consider State Management patterns.
Slots & Props
Use Props when you want to pass in a js object to a child component.
Use Slots when you want to pass in template markup to a child component.
https://medium.com/@nicomeyer/vue-js-slots-vs-props-af87078a8bd Vue.js: slots vs. props - Nico Meyer - Medium
Props
Example props. Linters may encourage the full form
// https://vuejs.org/v2/guide/components-props.html#Type-Checks
// String, Number, Boolean, Array, Object, Date, Function, Symbol
props: {
person: {
type: Object,
default: () => {},
},
list: {
type: Array,
default: () => [],
},
parameter: {
type: String,
default: 'Greetings',
},
something: Boolean,
},
// https://vuejs.org/v2/guide/components-props.html#Type-Checks
// String, Number, Boolean, Array, Object, Date, Function, Symbol
props: {
person: {
type: Object,
default: () => {},
},
list: {
type: Array,
default: () => [],
},
parameter: {
type: String,
default: 'Greetings',
},
something: Boolean,
},
https://vuejs.org/v2/guide/components-props.html Props — Vue.js
Watching Props
Sometimes props arrive to the child component after the component's .mounted() call happens. In these cases, it's necessary to watch
for the arrival of the prop from the parent:
watch: {
// Sometimes you want to act on the values that changed
// propertyName: function (newVal, oldVal) {
propertyName: function () {
// console.log('property value changed', newVal)
this.methodToHandleUpdate()
},
},
watch: {
// Sometimes you want to act on the values that changed
// propertyName: function (newVal, oldVal) {
propertyName: function () {
// console.log('property value changed', newVal)
this.methodToHandleUpdate()
},
},
Slots
Pass in customized markup content from the parent template.
Named slots are handy for layout components:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
via:
https://v3.vuejs.org/guide/component-slots.html#named-slots
https://vuejs.org/v2/guide/components-slots.html#Scoped-Slots
Slots — Vue.js
https://github.com/vuejs/rfcs/blob/master/active-rfcs/0001-new-slot-syntax.md
rfcs/0001-new-slot-syntax.md at master · vuejs/rfcs
https://www.google.com/search?client=ubuntu&channel=fs&q=vue+props+vs+slots&ie=utf-8&oe=utf-8
vue props vs slots - Google Search
Layouts
Slots get us most of the way there for a layout pattern.
https://markus.oberlehner.net/blog/dynamic-vue-layout-components/
Some vue-based framework pre-configure a layout pattern for us, however it can help to understand the pattern at work in cases where those frameworks are not a good fit.
https://awesome-vue.js.org/components-and-libraries/ui-layout.html
Forms
Heavy overlap with Web Components & UI Frameworks.
It helps to study the underlying mechanisms of syncing form content with the local script and parent components.
TODO: v-model binding in vue2? or is that a new feature with vue3?
https://duckduckgo.com/?t=ffab&q=creating+form+components+vue3&ia=web
💤 creating form components vue3 at DuckDuckGo
https://javascript.plainenglish.io/how-to-build-flexible-form-factory-by-vue-3-form-builder-pattern-b88edaf94776
Building a dynamic and scalable Form Factory by Vue 3 | JavaScript in Plain English
Routing
In your script section, use:
router.push({name: "yourroutename")
router.push("yourroutename") is NOT the same as router.push({name: "yourroutename"). First one defines the route directly. The second one takes the route with the specified name.
via: https://stackoverflow.com/questions/35664550/vue-js-redirection-to-another-page
see also: router-link and nuxt-link
Style Guide / Naming Conventions
Components should use more than one word in the name.
Filenames of single-file components should either be always PascalCase or always kebab-case.
In Javascript, camelCase is typically used for most variable and method names in code.
Components are typically named in PascalCase.
kebab-case
is recommended when using components in template markup. I also prefer kebab-case for filenames too.
I find it's more difficult to navigate on the command line when cases are mixed. Have to remember to type upper-cased characters. There are some filesystems that don't support case-sensitivity, but that should be moot in most cases.
This is a good discussion on the topic
https://github.com/vuejs/vuejs.org/issues/1162
Naming in PascalCase or kebab-case · Issue #1162 · vuejs/vuejs.org
Environment Variables (dotenv)
Automatic importing / Bundle Size / Tree-shaking
In an effort to minimize bundle size, it helps to only import the code being used.
https://github.com/antfu/unplugin-vue-components#readme
unplugin-vue-components
helps import components automatically, which in turn helps with tree-shaking.
This is the approach recommended by some ui-frameworks, like Element+:
https://element-plus.org/en-US/guide/quickstart.html#on-demand-import
https://rollupjs.org/guide/en/#tree-shaking
Troubleshooting
If a console.print()
statement is not showing what you expect (especially in the context of a computed value), be sure to render the value in a template.
<button @click="selected = 1">
test select
</button>
{{ selected }}
<button @click="selected = 1">
test select
</button>
{{ selected }}