Vue.js
lastmod 2023-02-15created 2017-02-06199 edits
|
Vue (pronounced /vjuː/, the same as view) is a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable. The core library focuses on the view layer only, making it very easy to integrate with other libraries or existing projects. And when used in combination with modern tooling and supporting libraries, Vue is also perfectly capable of powering sophisticated single-page applications.
Categories
- Vue Cheat sheet - Just see the page directly.
- Vue:MultipleApps - One Host, Multiple Vue Apps
- Vue:API
- Installation
- Getting started
- Vue instance
- Vue:Lifecycle
- Vue:Template - Template syntax
- How to use v-bind (shorthand
:).
- How to define
$.computed,$.computed.get, and$.computed.set.
- About component
$on(eventName),$emit(eventName), andv-onevents.
- this.$watch syntax
- About v-on (shorthand
@), the$eventvariable.
- Accessing child component instances and elements using the ref attribute and this.$refs.
- How to use
$nextTick.
Libraries
- A state management pattern + library for Vue.js applications.
- State store using this.$store.
- Vue Single File Components -
*.vuefiles. - Scoped CSS - Things like
<style scoped>.
- vue.config.js - Description of
vue.config.js, the config file of Vue CLI. - VueCLI:Plugins
- VueCLI:Service
Chart libraries
- Top Vue.js Chart Components
- JSCharting
- Vue FusionCharts
- Syncfusion Vue Charts
- Vue Trend
- Highcharts
- ZingCharts
- Vue ECharts
- Frappe Charts
- Chart.js
Project Template
Starting with Vue CLI 5.x, it seems only babel and eslint get installed and that's it...
- vue2, babel, eslint, typescript, vue-router, vuex
- vue2, babel, eslint, typescript, vue-router, vuex, sass
- vue2, babel, eslint, typescript, vue-router, vuex, sass, jest
- vue2, babel, eslint, typescript, vue-router, vuex, sass, jest, cypress
- vue2, babel, eslint, typescript, vue-router, vuex, sass, jest, cypress, pwa
VSCode Plugin
Examples
- Vue:Components:App - Basic App component.
- Baserow:Scrollbars - A TypeScript conversion of the Scrollbars component used in Baserow.
- Vue Router#HTML5 history mode - Caveats when deploying with vue-router as the frontend and aiohttp as the backend.
E.T.C
- ESLint
- NativeScript - Native app.
Hello world
The index.html file is as follows.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Vue</title>
<!-- CSS -->
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
</head>
<body>
<div id="app">
{{ message }}
</div>
<!-- JS -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="app.js"></script>
</body>
</html>
The app.js file is as follows:
Preparing for TypeScript coding
vue/valid-v-slot error
If you write code like this,
<template>
<!-- ... -->
<template v-slot:item.enable="{item}"> <!-- WARNING !! -->
<v-btn icon @click="click"></v-btn>
</template>
<!-- ... -->
</template>
To fix it, change v-slot:item.enable="{item}" to v-slot:item[`enable`]="{item}".
Or add the following rule to ESLint:
Unrecognized slot name
<template v-slot:item.server_running="{item}">
<v-icon v-show="item.server_running" small disabled>
{{ icons.mdiCheck }}
</v-icon>
</template>
When using IntelliJ-family IDEs, an "Unrecognized slot name" warning is shown at item.server_running.
Accessing child elements
- Handling Edge Cases — Vue.js - See the "Accessing Child Component Instances & Child Elements" section.
- Accessing the DOM through Vue.js $refs
Of course props and events exist, but sometimes you may need to access a child element directly in JavaScript. In that case, you can assign a reference ID to the child element using the ref attribute. For example:
Now, in the component where you defined the ID with ref, you can write:
to access the <base-input> instance.
Example
html file:
<div id="app">
<h1>{{ message }}</h1>
<button ref="myButton" @click="clickedButton">Click Me!</button>
</div>
javascript file:
var vm = new Vue({
el: '#app',
data: {
message: 'Hello World!'
},
methods: {
clickedButton: function() {
console.log(this.$refs);
this.$refs.myButton.innerText = this.message;
}
}
});
Accessing env files
For vue2, add the following to the webpack config:
// webpack.config.js
const webpack = require('webpack');
const dotenv = require('dotenv');
const env = dotenv.config().parsed;
plugins: [
new webpack.DefinePlugin({
VUE_APP_LOCAL_URI: JSON.stringify(env.VUE_APP_LOCAL_URI),
}),
],
For vue3, variables are loaded automatically as long as their names always start with VUE_APP_.
Accessing the element
Use $el.
On re-rendering conditions
Only properties assigned to data are redefined, so modifying sub-properties does not trigger re-rendering.
Also, if you want to force re-rendering, change the property assigned to key.
Lifecycle
See Vue:Lifecycle. The diagram is as follows:
Vue-lifecycle.png
Vue instance guide
The properties and APIs available in an instance are as follows.
- el - The starting point on the screen where the instance is drawn (a specific HTML tag)
- template - Elements to display on the screen (HTML, CSS, etc.)
- data - Data properties with Vue's reactivity applied
- methods - Methods that control screen behavior and event logic
- created - A property related to Vue's lifecycle
- watch - A property that defines additional actions to run when a property defined in data changes
Reactivity in Depth
How changes are tracked
When you pass a JavaScript object to a Vue instance as its data option, Vue converts all of its properties into getter/setters using Object.defineProperty. This is why Vue does not support IE8 and below, which cannot use ES5.
The getter/setters are invisible to the user, but they let Vue perform dependency tracking and change notification when properties are accessed or modified. One caveat is that browsers format getter/setters differently when converted data objects are logged, so it is a good idea to install vue-devtools for a friendlier interface.
Every component instance has a corresponding watcher instance, which records every property "modified" while the component renders dependently. Later, when a dependency's setter is triggered, it notifies the watcher, and the component re-renders.
Vue_-_how_to_track_changes.png
Async update queue
In case you haven't noticed, Vue performs DOM updates asynchronously. Whenever a data change is observed, it opens a queue and buffers all the data changes that happen in the same event loop. If the same watcher is triggered multiple times, it is pushed into the queue only once. This buffered de-duplication is important in avoiding unnecessary calculations and DOM manipulations. Then, in the next event loop "tick", Vue flushes the queue and performs the actual (already de-duplicated) work. Internally, Vue tries native Promise.then and MessageChannel for the async queue and falls back to setTimeout(fn, 0).
For example, when you set vm.someData = 'new value', the component does not re-render immediately. It updates in the next "tick", when the queue is flushed. Most of the time you don't need to care about this, but it can be tricky when you want to do something that depends on the post-update DOM state. Although Vue.js generally encourages developers to think in a "data-driven" way and avoid touching the DOM directly, sometimes it may be necessary to do so. To wait until Vue.js has finished updating the DOM after a data change, you can use Vue.nextTick(콜백) immediately after the data is changed. The callback is called after the DOM has been updated.
HTML:
JavaScript:
var vm = new Vue({
el: '#example',
data: {
message: '123'
}
})
vm.message = 'new message' // 데이터 변경
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
vm.$el.textContent === 'new message' // true
})
There is also the vm.$nextTick() instance method. This is especially handy inside components, because it doesn't need the global Vue and the callback's this context is automatically bound to the current Vue instance
Vue.component('example', {
template: '<span>{{ message }}</span>',
data: function () {
return {
message: '갱신 안됨'
}
},
methods: {
updateMessage: function () {
this.message = '갱신됨'
console.log(this.$el.textContent) // => '갱신 안됨'
this.$nextTick(function () {
console.log(this.$el.textContent) // => '갱신됨'
})
}
}
})
Since $nextTick() returns a promise, you can do the same thing using ES2017 async/await syntax.
methods: {
updateMessage: async function () {
this.message = '갱신됨'
console.log(this.$el.textContent) // => '갱신 안됨'
await this.$nextTick()
console.log(this.$el.textContent) // => '갱신됨'
}
}
Some caveats
- Content inside template slots such as
<template v-slot:...> ...</template> sometimes doesn't get "reactivity" applied... - If the variable passed via v-model is an
Object, modifying a member of the Object may not "react" properly. If you want it to "react" in this case, one way is to useVue.set(object, key, value). - If the variable passed via v-model is an
Objectand all members of the Object are modified, you must$emittheinputevent manually. - If the variable passed via v-model is an
Objectand a particular member of the Object is anArray, to make it "react" when that Array is modified, use something likev-if="!value.arr || value.arr.length === 0".
Detecting window size (window.innerHeight) changes
I am sure there are better ways to do this, but this one will work for you until I come up with something:
Basically you will need just one data prop and one watcher to do this.
new Vue({
el: '#app',
data() {
return {
windowHeight: window.innerHeight,
txt: ''
}
},
watch: {
windowHeight(newHeight, oldHeight) {
this.txt = `it changed to ${newHeight} from ${oldHeight}`;
}
},
mounted() {
this.$nextTick(() => {
window.addEventListener('resize', this.onResize);
})
},
beforeDestroy() {
window.removeEventListener('resize', this.onResize);
},
methods: {
onResize() {
this.windowHeight = window.innerHeight
}
}
});
And this would output the changes
Style guide
- Style Guide — Vue.js
- Vue.js Component Style Guide – Vue.js Korea User Group
- Vue.js style guide | GitLab
devServer.watchOptions
See webpack-dev-server#devServer.watchFiles. In short:
module.exports = {
configureWebpack: {
devServer: {
watchOptions: {
ignored: [/node_modules/, /public/],
}
}
}
}
Tutorials (by. LCW)
- Github - webpack-simple (good to use as a starter project)
- [Recommended]
Learning Webpack through a Vue project. (vue simple webpack project)
Getting started with Webpack.
Custom shims
If you want to add your own custom shims such as shims-recc.d.ts, write it not as
but in the form
for symbol complition to work properly in WebStorm-family IDEs ...
The final form is as follows:
Troubleshooting
You may need an additional loader to handle the result of these loaders
Install/configure an additional loader where needed.
this.$refs.<refField>.value does not exist
You can do this:
class YourComponent extends Vue {
$refs!: {
checkboxElement: HTMLFormElement
}
someMethod () {
this.$refs.checkboxElement.checked
}
}
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders
If you change a component property directly using $refs, as below...
you get the following error telling you not to mutate a Prop directly.
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "loading"
Translated (into Korean):
상위 구성 요소가 다시 렌더링될 때마다 값을 덮어쓰므로 소품을 직접 변경하지 마십시오.
대신 prop의 값을 기반으로 데이터 또는 계산된 속성을 사용하십시오.
Prop 변경 중: "loading"
In short, don't change it directly with ref. Use v-bind.
When the above warning appears for a data member not declared as a Prop
Declaring Reactive Properties
Since Vue does not allow dynamically adding root-level reactive properties, you must initialize Vue instances by declaring all root-level reactive data properties upfront, even with an empty value.
var vm = new Vue({
data: {
// declare message with an empty value
message: ''
},
template: '<div>{{ message }}</div>'
})
// set `message` later
vm.message = 'Hello!'
If you don't declare message in the data option, Vue will warn you that the render function is trying to access a property that doesn't exist.
There is a technical reason behind this restriction: it eliminates a class of edge cases in the dependency tracking system and makes Vue instances work better with type checking systems. But there is also an important consideration in terms of code maintainability: the data object is like the schema for the component's state. Declaring all reactive properties upfront makes the component code easier to understand when you revisit it later or when another developer reads it.
Error /sockjs-node/info
- Stackoverflow - Errors in browser console, requests to /sockjs-node/info?t=1555629946494
- Using the vue proxy
The following error may be printed in the browser console.
Props with type Object/Array must use a factory function to return the default value
How to set the default value when Props are of Object or Array type. In short, the default must be set as a function's return value.{ type: () => { return []; } }
See Vue:Props#Props with type Object/Array must use a factory function to return the default value.
Error in callback for watcher "value": "TypeError: Cannot read property 'call' of undefined"
In the parent component, have the this.current property modified somewhere other than places like created (e.g. an async execution region), as follows.
Pass that object to the template.
If the child (form-user) component uses the current.username property as value.username,
the following error occurs.
[Vue warn]: Error in callback for watcher "value": "TypeError: Cannot read property 'call' of undefined"
This is a guess, but..
The component's watcher instance records every property "modified" during rendering, and this is presumably because all properties passed to Vue's data are redefined with Object.defineProperty. (See the Reactivity in Depth page for details.) The error above references username directly, and it presumably occurs because the object itself was modified again after the this.current instance had been redefined with Object.defineProperty. <- Presumably the child component references value.username directly, and this part changed.
Therefore, when modifying the this.current object in the parent component, modify each property directly as follows.
Not working when using a component's v-model and passing it on to a child component's v-model
Look at the example below.
<template>
<v-card>
<v-text-field v-model="infoValue"></v-text-field>
</v-card>
</template>
<script lang="ts">
import {Component, Prop, Emit, Watch} from 'vue-property-decorator';
import VueBase from '@/base/VueBase';
@Component
export default class CardInfoNew extends VueBase {
@Prop({type: String, default: ''})
readonly initKey!: string;
infoKey = '';
mounted() {
this.infoKey = this.initKey;
}
}
</script>
initKey, declared as a Prop, is readonly, so it can't be set as the v-model of v-text-field.
So you create a separate infoKey, but then changes to initKey from the parent component are not reflected.
It works properly only if you check it with Watch as below.
Maximum call stack size exceeded
Check whether there is a circular reference.
- Repeated component calls
- Function recursion
v-slot directive doesn't support any modifier
Change code like this:
to this:
Cannot GET /...
- Cannot get /... #1254
- Npm run dev “Cannot GET /config” - vue router history mode
- What is historyApiFallback? (webpack.config.js)
In an SPA, even when a path like /bla/bla/bla is accessed, the root /index.html file may need to be loaded. Navigating via page links works as the framework intends, but if you navigate by typing the address directly, the resource at that location (e.g. /bla/bla/bla/index.html) does not exist and routing fails.
When using the HTML5 History API, accessing a URL that includes the configured URL would return a 404 response; historyApiFallback serves /index.html in that case too. It looks as if you first go to http://localhost:3080 and then move to the /bla/bla/bla route.
Set the following in the vue.config.js file:
const {defineConfig} = require('@vue/cli-service');
module.exports = defineConfig({
...
devServer: {
historyApiFallback: true,
},
});
App not updating to the latest version after deployment unless the browser cache is cleared
See Cache Busting.
See also
Favorite site
- Getting Started - Vue.js
- [Recommended] Github - awesome-vue
- [Recommended] Vue.js Examples - A large number of sample projects/libraries
- Comparison with Other Frameworks - Vue.js
- [Recommended] Why do 43% of frontend developers want to learn Vue.js?
- Haruair - Comparing vue.js with other frameworks
- Meetup : TOAST Cloud - Introduction to JavaScript Frameworks 3 - Vue.js 1
- Building Your First App With Vue.js
- I created the exact same app (todo) in React and Vue. Here are the differences. (translation)
Tutorials
Guide
- A look at setting up a development environment with express and vue 3 (express, vue)
- Building a Vue-like reactivity system in 0.7KB - TOAST UI :: Make Your Web Delicious!
- The 3 core structures of Vue.js
- [Recommended] Vue.js 2.3 Complete API Cheat Sheet
- [Recommended] Properties and methods of the Vue instance ($attrs, $set, $delete, $nextTick, $emit, $forceUpdate...)
- Migrating from Vue 2 to Svelte | GeekNews
- (Original) Migrating from Vue 2 to Svelte
Lint
- Applying the official Vue.js ESLint plugin (Vue.js, ESLint)
- Setting up ESLint and Prettier (Vue.js, ESLint, Prettier)
- Applying the official Vue.js ESLint plugin – Vue.js Korea User Group (Vue.js, ESLint)
Examples
- Github - Whiteboard - (Vue.js Paper.js drawing proj)
- VueJS + PaperJS: A Canvas Story. VueJS is a powerful, progressive web… | by Noah Kreiger | Medium (Vue.js, Paper.js)
References
- AngularJS
- ApexCharts
- Baserow
- Cache Busting
- Coc.nvim
- CSS:@font-face
- CSS:Example:HideScrollbar
- Cycle.js
- Documentation generator
- Elder
- Electron
- Electron-vue
- Eleventy
- ESLint
- Express
- Flicking
- Fusion.ssg
- GatsbyJS
- Gitmal
- Hexo
- Hugo
- Icon
- Jamstack
- JavaScript
- JavaScript:Example:DetectOverflow
- JavaScript:Libraries
- JavaScript:ResizeObserver
- Jekyll
- Konsta UI
- Lusift
- Material UI
- MediaWiki
- Meli
- Misskey
- Mustache
- NuxtJS
- Octopress
- Pelican
- Petite-Vue
- Pinia
- PixiJS
- Prettier
- Programming
- React
- React:Component
- RxJS
- Single-page application
- SolutionStack:FullStackDeveloper:Curriculum
- Static Site Generator
- Svelte
- Technical Terms
- TOAST UI Chart
- TypeScript:Example:Storage
- V-bind
- V-fab-transition
- V-for
- V-model
- V-on
- Vetur
- Vitest
and 56 more