

If you already know Vue2 and just want to know the new features of Vue3, please refer to New features of vue3 (opens new window)!
There are already Vue2 projects, and those that need to adapt to Vue3 can be referred to Vue2 project migration vue3 (opens new window)!
What is Vue.js?
Vue (pronounced /vjuː/, like 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 of Vue.js
is a system that allows the use of concise template syntax to declaratively render data into DOM
. Focus on the view layer only, quick start. Everything is responsive.
Thanks
Most of the content in this article comes from the vue3 Chinese document official website (opens new window), but some adjustments have been made in conjunction with uni-app
to make it easier for developers to get started quickly. Heartfelt thanks should be given to the Vue team!
Advantages of vue3:
In traditional development, when the native JavaScript DOM manipulation function is used to frequently manipulate the DOM, the browser must constantly render the new DOM tree, causing the page to look very stuck.
Vue is a single-page application, which makes the page partially refresh, without having to request all the data and DOM every time you jump to the page, which greatly speeds up the access speed and improves the user experience.
Advantages of Vue:
html
large node, there script
and style
nodes; <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
</script>
<style type="text/css">
</style>
</head>
<body>
</body>
</html>
template
is a node for writing tag components, script
and style
is a node in parallel, that is, there are three-level node. <template>
<view>
Note that there must be one view, and only one root view. Everything is written below this view.
</view>
</template>
<script>
export default {
}
</script>
<style>
</style>
<script src="js/jquery-1.10.2.js" type="text/javascript"></script>
<link href="css/bootstrap.css" rel="stylesheet" type="text/css"/>
import
import external js module (note that it is not a file) or css;js needs to come in and become an object .
There is a tool class util.js
in the common
directory of hello uni-app. You can search for this example in hello uni-app. Hello uni-app sample code is available from github (opens new window).
<script>
//Require this js module
var util = require('../../../common/util.js');
//Call the method of the JS module
var formatedPlayTime = util.formatTime(playTime);
</script>
In this util.js
, the prior should function
method for encapsulating as an object
function formatTime(time) {
//There's no logic here
return time;
}
module.exports = {
formatTime: formatTime
}
Of course, there are some advanced usages
// Use the attributes of the js module directly. There are examples in hello uni-app
var dateUtils = require('../../../common/util.js').dateUtils;
// Import and rename js to echarts, and then use echarts to continue executing the method. There are examples in hello uni-app
import * as echarts from '/components/echarts/echarts.simple.min.js';
css external file import. Global styles are written in app.vue
in the root directory, and every page will load the styles in app.vue
.
<style>
@import "./common/uni.css";
.uni-hello-text{
color:#7A7E83;
}
</style>
In addition, Vue supports component import, which makes it easier to encapsulate a library that includes interface, js, and styles See.
It used to be html tags, for example <div>
, now it is a small program component, for example <view>
.
So what is the difference between a label and a component, isn't it all surrounded by angle brackets in English?
uni-app
provides a batch of built-in components (opens new window).
<html>
<head>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded",function () {
document.getElementById("spana").innerText="456"
})
function changetextvalue () {
document.getElementById("spana").innerText="789"
}
</script>
</head>
<body>
<span id="spana">123</span>
<button type="button" onclick="changetextvalue()">changetextvalue</button>
</body>
</html>
uni-app
solves the problem of interaction between js and DOM interface by using data binding method of vue. <template>
<view>
<!-- This demonstrates the binding of component values- -->
<text>{{textvalue}}</text>
<!-- This demonstrates the binding of properties and events -->
<button :type="buttontype" @click="changetextvalue()">changetextvalue</button>
</view>
</template>
<script>
export default {
data() {
return {
textvalue:"123",
buttontype:"primary"
};
},
onLoad() {
// change the value of textValue, 123 becomes 456 and 123 is not displayed
this.textvalue="456"
},
methods: {
changetextvalue() {
//you change the value of textValue, and the page automatically refreshes to 789
this.textvalue="789"
}
}
}
</script>
uni-app
supports all vue syntax when publishing to the H5 side; When publishing to App, due to platform limitations, all vue syntax cannot be implemented, but uni-app
is still the cross-end framework with the highest support for vue syntax.
Compared with the Web platform, the differences in the use of Vue.js in uni-app
are mainly concentrated in two aspects:
uni-app
In addition to supporting the life cycle of Vue instances, it also supports Application life cycle and Page life cycle.Introduction and upgrade guide for uni-app project support vue 3.0 (opens new window)
HBuilderX 3.2.5-alpha
added support for vue 3.0 on the App platform. So far, the support for vue 3.0 of the uni-app
project is as follows:
vite
.vite
,nvue
is temporarily not supported.Precautions
Proxy
implementation, and does not support iOS9
and ie11
.Teleport
,Suspense
components are not supported temporarily.template
is temporarily not supported.HBuilderX 3.2
+, and the previous version can only use cli mode.Vue.js uses an HTML-based template syntax that allows you to declaratively bind the rendered DOM to the underlying component instance's data. All Vue.js templates are valid HTML that can be parsed by spec-compliant browsers and HTML parsers.
Under the hood, Vue compiles the templates into Virtual DOM render functions. Combined with the reactivity system, Vue is able to intelligently figure out the minimal number of components to re-render and apply the minimal amount of DOM manipulations when the app state changes.
The most common form of data binding is text interpolation:
<template>
<view>
<view>Message: {{ msg }}</view>
</view>
</template>
<script>
export default {
data() {
return {
msg: 'Hello Vue!'
}
}
}
</script>
The content in will be replaced with the value of msg on the corresponding data object. Whenever the msg on the bound data object changes, the content at the interpolation point will be updated.
So far we've only been binding to simple property keys in our templates. But Vue.js actually supports the full power of JavaScript expressions inside all data bindings:
Example
<template>
<view>
<view>{{ number + 1 }}</view>
<view>{{ ok ? 'YES' : 'NO' }}</view>
<!-- Split a string into a string array, reverse the order of the elements, and put all the elements in the array into a string -->
<view>{{ message.split('').reverse().join('') }}</view>
</view>
</template>
<script>
export default {
data() {
return {
number:1,
ok:true,
message: 'Hello Vue!'
}
}
}
</script>
Example
<template>
<view>
<view v-for="(item,index) in 10">
<!-- Calculate the remainder through the % operator to achieve the effect of zebra list -->
<view :class="'list-' + index%2">{{index%2}}</view>
</view>
</view>
</template>
<script>
export default {
data() {
return { }
}
}
</script>
<style>
.list-0{
background-color: #aaaaff;
}
.list-1{
background-color: #ffaa7f;
}
</style>
These expressions will be evaluated as JavaScript in the data scope of the current active instance. One restriction is that each binding can only contain one single expression, so the following will NOT work:
Error example
<template>
<view>
<!-- this is a statement, not an expression: -->
<view>{{ var a = 1 }}</view>
<!-- flow control won't work either, use ternary expressions -->
<view>{{ if (ok) { return message } }}</view>
</view>
</template>
<script>
export default {
data() {
return {
ok:true,
message: 'Hello Vue!'
}
}
}
</script>
Template expressions are sandboxed and only have access to a restricted list of globals:
Infinity
undefined
NaN
isFinite
isNaN
parseFloat
parseInt
decodeURI
decodeURIComponent
encodeURI
encodeURIComponent
Math
Number
Date
Array
Object
Boolean
String
RegExp
Map
Set
JSON
Intl
BigInt
You should not attempt to access user defined globals in template expressions.
Directives are special attributes with the v- prefix.
Dynamically bind one or more attributes, or a component prop
to the expression.
prop
upon, prop
it must be declared in the sub-assembly. <!-- full -->
<image v-bind:src="imgUrl"></image>
<!-- short -->
<image :src="imgUrl"></image>
<button v-bind:disabled="isButtonDisabled">Button</button>
Here src is the argument, which tells the v-bind directive to bind the element's src attribute to the value of the expression url.
The disabled attribute will be included if isButtonDisabled has a truthy value. It will also be included if the value is an empty string, maintaining consistency with <button disabled="">
. For other falsy values the attribute will be omitted.
v-on directive, which listens to DOM events。v-on is abbreviated as '@':
<!-- full -->
<view v-on:click="doSomething">click</view>
<!-- short -->
<view @click="doSomething">click</view>
Render the element and component once only. On subsequent re-renders, the element/component and all its children will be treated as static content and skipped. This can be used to optimize update performance.
Unlike front-end framework in understanding the client in order to achieve a logical multiplexing state flag template node will be added to the v-once
guarantee rendering node only once, but not necessarily be able to optimize the rendering performance, but might slow down the client Comparison efficiency when multiplexing nodes.
<!-- A single element -->
<view v-once>This will never change: {{msg}}</view>
<!-- Contains child elements -->
<view v-once>
<text>comment</text>
<text>{{msg}}</text>
</view>
Updates the element's innerHTML
(opens new window) .
Cross-end rich text processing scheme can be found in: https://ask.dcloud.net.cn/article/35772 (opens new window)
<template>
<view>
<view v-html="rawHtml"></view>
</view>
</template>
<script>
export default {
data() {
return {
rawHtml: '<div style="text-align:center;background-color: #007AFF;"><div >text</div><img src="https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-uni-app-doc/d8590190-4f28-11eb-b680-7980c8a877b8.png"/></div>'
}
}
}
</script>
The data
option has been standardized to only accept a function that returns an initial data object (note that the data object returned in the function should not directly refer to the object outside the function); Otherwise, when the page is closed, the data will not be destroyed automatically. When the page is opened again, the last data will be displayed.
//Correct usage, use function to return object
data() {
return {
title: 'Hello'
}
}
//Wrong writing will cause the last data to be displayed when the page is opened again
data: {
title: 'Hello'
}
//Error writing, which will also lead to mutual influence of object data of multiple component instances.
const obj = {
title: 'Hello'
}
data() {
return {
obj
}
}
Object Syntax
We can pass an object to :class (short for v-bind:class) to dynamically toggle classes:
You can have multiple classes toggled by having more fields in the object. In addition, the :class
directive can also co-exist with the plain class attribute. So given the following template:
<template>
<view>
<view class="static" :class="{ active: isActive}">hello uni-app</view>
<view class="static" :class="{ active: isActive, 'text-danger': hasError }">hello uni-app</view>
</view>
</template>
<script>
export default {
data() {
return {
isActive: true,
hasError: false
}
}
}
</script>
<style>
.static{
color: #2C405A;
}
.active{
background-color: #007AFF;
font-size:30px;
}
.text-danger{
color: #DD524D;
}
</style>
It will render:
<view class="static active"></view>
When isActive
or hasError
changes, the class list will be updated accordingly. For example, if hasError
becomes true, the class list will become "static active text-danger"
.
Array Syntax
We can pass an array to :class
to apply a list of classes:
<template>
<view>
<view :class="[activeClass,errorClass]">hello uni-app</view>
</view>
</template>
<script>
export default {
data() {
return {
activeClass: 'active',
errorClass: 'text-danger'
}
}
}
</script>
<style>
.active{
background-color: #007AFF;
}
.text-danger{
font-size:60rpx;
color:#DD524D;
}
</style>
Which will render:
<view class="active text-danger"></view>
If you would like to also toggle a class in the list conditionally, you can do it with a ternary expression:
<view :class="[isActive ? activeClass : '', errorClass]"></view>
This will always apply errorClass, but activeClass will only be applied when isActive is truthy.
In JavaScript, truthy means that the converted value is true in the context of Boolean values. All values are true values unless they are defined as false values (that is, all true values except false, 0, "", null, undefined, and NaN).
However, this can be a bit verbose if you have multiple conditional classes. That's why it's also possible to use the object syntax inside array syntax:
<template>
<view>
<view class="static" :class="[{ active: isActive }, errorClass]">hello uni-app</view>
</view>
</template>
<script>
export default {
data() {
return {
isActive: true,
errorClass: 'text-danger'
}
}
}
</script>
<style>
.static{
font-size:30rpx;
}
.active{
background-color: #007AFF;
}
.text-danger{
font-size:60rpx;
color:#DD524D;
}
</style>
Note: Set the px pixel value in the way: style="". The value is the actual pixel and will not be converted by the compiler.
Further it may also be used computed
to generate a method class
or a style
string, inserted into the page, illustrated:
<template>
<view>
<view class="container" :class="computedClassStr">hello uni-app</view>
<view class="container" :class="{active: isActive}">hello uni-app</view>
</view>
</template>
<script>
export default {
data() {
return {
isActive: true
}
},
computed: {
computedClassStr() {
return this.isActive ? 'active' : ''
}
}
}
</script>
<style>
.active {
background-color: #007AFF;
font-size:30px;
}
</style>
Object Syntax
The object syntax for :style is pretty straightforward - it looks almost like CSS, except it's a JavaScript object. You can use either camelCase
or kebab-case
(use quotes with kebab-case) for the CSS property names:
<template>
<view :style="{ color: activeColor, fontSize: fontSize + 'px' }">hello uni-app</view>
</template>
<script>
export default {
data() {
return {
activeColor: 'red',
fontSize: 30
}
}
}
</script>
It is often a good idea to bind to a style object directly so that the template is cleaner:
<template>
<view :style="styleObject">hello uni-app</view>
</template>
<script>
export default {
data() {
return {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
}
}
</script>
Again, the object syntax is often used in conjunction with computed properties that return objects.
Array Syntax
The array syntax for :style
allows you to apply multiple style objects to the same element:
<template>
<view>
<view :style="[baseStyles, overridingStyles]">hello uni-app</view>
</view>
</template>
<script>
export default {
data() {
return {
baseStyles: {
color: 'green',
fontSize: '30px'
},
overridingStyles: {
'font-weight': 'bold'
}
}
}
}
</script>
Auto-prefixing
When you use a CSS property that requires a vendor prefixesa (opens new window) (opens new window)in :style
, Vue will automatically add the appropriate prefix. Vue does this by checking at runtime to see which style properties are supported in the current browser. If the browser doesn't support a particular property then various prefixed variants will be tested to try to find one that is supported.
Multiple Values
You can provide an array of multiple (prefixed) values to a style property, for example:
<view :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></view>
This will only render the last value in the array which the browser supports. In this example, it will render display: flex
for browsers that support the unprefixed version of flexbox
.
The directive v-if is used to conditionally render a block. The block will only be rendered if the directive's expression returns a truthy value.
Use the v-else
directive to indicate an "else block" for v-if
:
A v-else
element must immediately follow a v-if
or a v-else-if
element - otherwise it will not be recognized.
In JavaScript, truthy means that the converted value is true in the context of Boolean values. All values are true values unless they are defined as false values (that is, all true values except false, 0, "", null, undefined, and NaN).
<template>
<view>
<view v-if="seen">show</view>
<view v-else>hide</view>
</view>
</template>
<script>
export default {
data() {
return {
seen: true
}
}
}
</script>
The v-else-if
, as the name suggests, serves as an "else if block" for v-if
. It can also be chained multiple times:
<template>
<view>
<view v-if="type === 'A'">
A
</view>
<view v-else-if="type === 'B'">
B
</view>
<view v-else-if="type === 'C'">
C
</view>
<view v-else>
Not A/B/C
</view>
</view>
</template>
<script>
export default {
data() {
return {
type:'B'
}
}
}
</script>
Similar to v-else
, a v-else-if
element must immediately follow a v-if
or a v-else-if
element.
Because v-if is a directive, it has to be attached to a single element. But what if we want to toggle more than one element?
In this case we can use v-if on a <template>
element, which serves as an invisible wrapper. The final rendered result will not include the <template>
element.
<template v-if="seen">
<view>title</view>
<view>show</view>
</template>
Another option for conditionally displaying an element is the v-show
directive. The usage is largely the same:
<view v-show="ok">Hello!</view>
The difference is that an element with v-show
will always be rendered and remain in the DOM; v-show
only toggles the display CSS property of the element.
v-show doesn't support the
<template>
element, nor does it work with v-else
v-if
is "real" conditional rendering because it ensures that event listeners and child components inside the conditional block are properly destroyed and re-created during toggles.
v-if
is also lazy: if the condition is false on initial render, it will not do anything - the conditional block won't be rendered until the condition becomes true for the first time.
In comparison, v-show
is much simpler - the element is always rendered regardless of initial condition, with CSS-based toggling.
Choose according to application scenarios
v-if
There is a higher switching overhead. If the conditions rarely change during runtime, it is better to use v-if.v-show
There is a higher initial rendering overhead. If you need to switch very frequently, v-show is better.Note
v-if
and v-for
.v-if
and v-for
when used together, v-if
than v-for
a higher priority.The v-for directive to render a list of items based on an array.
v-for
blocks we have full access to parent scope properties.item
is the alias iterated array elements.index
, is optional. <template>
<view>
<view v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</view>
</view>
</template>
<script>
export default {
data() {
return {
parentMessage: 'Parent',
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
}
}
</script>
Result:
Parent - 0 - Foo
Parent - 1 - Bar
You can also use v-for to iterate through the properties of an object.
value
is an alias iterated array elements.property
name (that is, the key name). <template>
<view>
<view v-for="(value, name, index) in object">
{{ index }}. {{ name }}: {{ value }}
</view>
</view>
</template>
<script>
export default {
data() {
return {
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2021-05-10'
}
}
}
}
</script>
Result:
0.title: How to do lists in Vue,
1.author: Jane Doe,
2.publishedAt: 2021-05-10
When iterating over an object, the order is based on the enumeration order of
Object.keys()
, which isn't guaranteed to be consistent acrossJavaScript
engine implementations.
Similar to template v-if
, you can also use a <template>
tag with v-for to render a block of multiple elements. For example:
<template v-for="item in items">
<view>{{ item.message }}</view>
<view class="divider" role="presentation"></view>
</template>
When Vue is updating a list of elements rendered with v-for
, by default it uses an "in-place patch" strategy. If the order of the data items has changed, instead of moving the DOM elements to match the order of the items, Vue will patch each element in-place and make sure it reflects what should be rendered at that particular index.
This default mode is efficient, but only suitable when your list render output does not rely on child component state or temporary DOM state (e.g. form input values).
To give Vue a hint so that it can track each node's identity, and thus reuse and reorder existing elements, you need to provide a unique key
attribute for each item:
<view v-for="item in items" :key="item.id">
<!-- content -->
</view>
It is recommended to provide a key attribute with v-for whenever possible, unless the iterated DOM content is simple, or you are intentionally relying on the default behavior for performance gains.
Don't use non-primitive values like objects and arrays as v-for keys. Use string or numeric values instead.
If you do not provide: key, one will be reported
warning
. If you know that the list is static or you don't need to pay attention to its order, you can choose to ignore it.
Example:
<template>
<view>
<!-- Some property of item in Array -->
<view v-for="(item,index) in objectArray" :key="item.id">
{{index +':'+ item.name}}
</view>
<!-- When item itself is a unique string or number, we can use item itself -->
<view v-for="(item,index) in stringArray" :key="item">
{{index +':'+ item}}
</view>
</view>
</template>
<script>
export default {
data () {
return {
objectArray:[{
id:0,
name:'li ming'
},{
id:1,
name:'wang peng'
}],
stringArray:['a','b','c']
}
}
}
</script>
v-for="(item, index) in 10"
in, item 1 from the start, other platforms item from zero, the second parameter may be used in index H5 platform consistent.v-for="(value, name, index) in object"
in, index parameter is not supported.<template v-for>
In Vue3
, key
should be set on the <template>
tag
<template v-for="item in list" :key="item.id">
<view>...</view>
<text>...</text>
</template>
Similarly, if there are child nodes that use v-if
when using <template v-for>
, key
should be set on the <template>
tag instead.
<template v-for="item in list" :key="item.id">
<view v-if="item.isVisible">...</view>
<view v-else>...</view>
</template>
On the custom component, you can use it like any ordinary element v-for
.
<my-component v-for="item in items" :key="item.id"></my-component>
When using v-for on the component, the key is required.
Note that it's not recommended to use v-if and v-for together.
When they exist on the same node, v-if
has a higher priority than v-for
. That means the v-if
condition will not have access to variables from the scope of the v-for
:
<!-- This will throw an error because property "todo" is not defined on instance. -->
<view v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</view>
This can be fixed by moving v-for to a wrapping <template>
tag:
<template v-for="todo in todos">
<view v-if="!todo.isComplete">
{{ todo }}
</view>
</template>
We can use the v-on
directive, which we typically shorten to the @ symbol, to listen to DOM events and run some JavaScript when they're triggered.
The usage would be v-on:click="methodName
" or with the shortcut, @click="methodName"
<template>
<view>
<button @click="counter += 1">Add 1</button>
<text>The button above has been clicked {{ counter }} times.</text>
</view>
</template>
<script>
export default {
data() {
return {
counter:0
}
}
}
</script>
The logic for many event handlers will be more complex though, so keeping your JavaScript in the value of the v-on
attribute isn't feasible. That's why v-on
can also accept the name of a method you'd like to call.
Example:
<template>
<view>
<!-- `greet` is the name of a method defined below -->
<button @click="greet">Greet</button>
</view>
</template>
<script>
export default {
data() {
return {
name: 'Vue.js'
}
},
// `this` inside methods points to the current active instance
methods: {
greet(event){
// `event` is the native DOM event
console.log(event);
uni.showToast({
title: 'Hello ' + this.name + '!'
});
}
}
}
</script>
Instead of binding directly to a method name, we can also use methods in an inline JavaScript statement:
<template>
<view>
<button @click="say('hi')">Say hi</button>
<button @click="say('what')">Say what</button>
</view>
</template>
<script>
export default {
methods: {
say(message) {
uni.showToast({
title: message
});
}
}
}
</script>
Sometimes we also need to access the original DOM event in an inline statement handler. You can pass it into a method using the special $event
variable:
<template>
<view>
<button @click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>
</view>
</template>
<script>
export default {
methods: {
warn(message, event) {
// now we have access to the native event
if (event) {
//You can access native event objects such as event.target
console.log("event: ",event);
}
uni.showToast({
title: message
});
}
}
}
</script>
You can have multiple methods in an event handler separated by a comma operator like this:
<template>
<view>
<!-- both one() and two() will execute on button click -->
<button @click="one($event), two($event)">
Submit
</button>
</view>
</template>
<script>
export default {
methods: {
one(event) {
// first handler logic...
console.log("event1: ",event);
},
two(event) {
// second handler logic...
console.log("event2: ",event);
}
}
}
</script>
The modifier is a special suffix specified by a period. It is used to indicate that an instruction should be bound in a special way. For example, .prevent
modifier tells v-on
instructions for event-triggered call event.preventDefault()
:
v-on provides event modifiers:
.stop
: Supported by all platforms, it will prevent the event from bubbling when used, and also prevent the default behavior of the event on the non-H5 side.prevent
: Only supported on H5 platform.capture
: Only supported on H5 platform.self
: Only supported on H5 platform.once
: Only supported on H5 platform.passive
: Only supported on H5 platform <!-- Prevents the click event from propagating further -->
<view @click.stop="doThis"></view>
Order matters when using modifiers because the relevant code is generated in the same order. Therefore using @click.prevent.self will prevent all clicks while @click.self.prevent will only prevent clicks on the element itself.
note
event.preventDefault()
and event.stopPropagation()
methods cannot be used in JS;@touchmove.stop.prevent="moveHandle"
, moveHandle
processing can be used to touchmove
events, but also can be an empty function. <view class="mask" @touchmove.stop.prevent="moveHandle"></view>
uni-app
Run on the mobile phone, with no keyboard event. So the key modifier is not supported.there are several benefits in using v-on or @:
It's easier to locate the handler function implementations within your JS code by skimming the HTML
template.
Since you don't have to manually attach event listeners in JS, your ViewModel
code can be pure logic and DOM-free. This makes it easier to test.
When a ViewModel
is destroyed, all event listeners are automatically removed. You don't need to worry about cleaning it up yourself.
//Event mapping table, with WEB events on the left and corresponding events of ``uni-app``` on the right
{
click: 'tap',
touchstart: 'touchstart',
touchmove: 'touchmove',
touchcancel: 'touchcancel',
touchend: 'touchend',
tap: 'tap',
//It is recommended to use longpress
longtap: 'longtap',
input: 'input',
change: 'change',
submit: 'submit',
blur: 'blur',
focus: 'focus',
reset: 'reset',
confirm: 'confirm',
columnchange: 'columnchange',
linechange: 'linechange',
error: 'error',
scrolltoupper: 'scrolltoupper',
scrolltolower: 'scrolltolower',
scroll: 'scroll'
}
You can use the v-model
directive to create two-way data bindings on form input
, textarea
, and select
elements. It automatically picks the correct way to update the element based on the input type. Although a bit magical, v-model
is essentially syntax sugar for updating data on user input events, plus special care for some edge cases.
v-model will ignore the initial
value
,checked
orselected
attributes found on any form elements. It will always treat the current active instance data as the source of truth. You should declare the initial value on the JavaScript side, inside the data option of your component.
<template>
<view>
<input v-model="message" placeholder="edit me">
<text>Message is: {{ message }}</text>
</view>
</template>
<script>
export default {
data() {
return {
message:""
}
}
}
</script>
It is recommended to use uni-app
: Form component (opens new window) directly in the development process.
select
tag on the H5 side is replaced with the picker
component <template>
<view>
<picker @change="bindPickerChange" :value="index" :range="array">
<view class="picker">
now: {{array[index]}}
</view>
</picker>
</view>
</template>
<script>
export default {
data() {
return {
index: 0,
array: ['A', 'B', 'C']
}
},
methods: {
bindPickerChange(e) {
console.log(e)
this.index = e.detail.value
}
}
}
</script>
radio
is replaced with radio-group
component <template>
<view>
<radio-group class="radio-group" @change="radioChange">
<label class="radio" v-for="(item, index) in items" :key="item.name">
<radio :value="item.name" :checked="item.checked" /> {{item.value}}
</label>
</radio-group>
</view>
</template>
<script>
export default {
data() {
return {
items: [{
name: 'USA',
value: 'USA'
},
{
name: 'CHN',
value: 'CHN',
checked: 'true'
},
{
name: 'BRA',
value: 'BRA'
},
{
name: 'JPN',
value: 'JPN'
},
{
name: 'ENG',
value: 'ENG'
},
{
name: 'TUR',
value: 'TUR'
}
]
}
},
methods: {
radioChange(e) {
console.log('value:', e.target.value)
}
}
}
</script>
Each contains a calculated attribute getter
and a setter
default is to use getter
to read. All getter
and setter
the this
context is automatically bound instance Vue.
In-template expressions are very convenient, but they are meant for simple operations. Putting too much logic in your templates can make them bloated and hard to maintain. For example, if we have an object with a nested array:
data() {
return {
author: {
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
}
}
}
And we want to display different messages depending on if author
already has some books or not
<view>
<view>Has published books:</view>
<view>{{ author.books.length > 0 ? 'Yes' : 'No' }}</view>
</view>
At this point, the template is no longer simple and declarative. You have to look at it for a second before realizing that it performs a calculation depending on author.books
. The problem is made worse when you want to include this calculation in your template more than once.
That's why for complex logic that includes reactive data, you should use a computed property.
Example:
<template>
<view>
<view>OHas published books:</view>
<view>{{ publishedBooksMessage }}</view>
</view>
</template>
<script>
export default {
data() {
return {
author: {
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
}
}
},
computed: {
// a computed getter
publishedBooksMessage() {
// `this` points to the vm instance
return this.author.books.length > 0 ? 'Yes' : 'No'
}
}
}
</script>
Here we have declared a computed property publishedBooksMessage
.
Try to change the value of books array in the application data and you will see how publishedBooksMessage
is changing accordingly.
You can data-bind to computed properties in templates just like a normal property. Vue is aware that publishedBooksMessage
depends on author.books
, so it will update any bindings that depend on publishedBooksMessage
when author.books
changes. And the best part is that we've created this dependency relationship declaratively: the computed getter function has no side effects, which makes it easier to test and understand.
Computed properties can also depend on the data of multiple Vue instances. As long as any of the data changes, the calculated properties will be re-executed and the view will be updated.
When you need it can also provide a setter
function, when manually modify the calculated value of the property, it will trigger setter
function, perform some custom actions.
<template>
<view>
<view>{{ fullName }}</view>
</view>
</template>
<script>
export default {
data() {
return {
firstName: 'Foo',
lastName: 'Bar'
}
},
computed: {
fullName: {
// getter
get(){
return this.firstName + ' ' + this.lastName
},
// setter
set(newValue){
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
}
</script>
Now run fullName = 'John Doe'
time, setter
will be called firstName
and lastName
will be updated accordingly.
We can achieve the same result by invoking a method in the expression:
<template>
<view>
<view>{{ calculateBooksMessage() }}</view>
</view>
</template>
<script>
export default {
data() {
return {
author: {
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
}
}
},
methods: {
calculateBooksMessage() {
return this.author.books.length > 0 ? 'Yes' : 'No'
}
}
}
</script>
Instead of a computed property, we can define the same function as a method. For the end result, the two approaches are indeed exactly the same. However, the difference is that computed properties are cached based on their reactive dependencies.
A computed property will only re-evaluate when some of its reactive dependencies have changed. This means as long as author.books
has not changed, multiple access to the publishedBooksMessage
computed property will immediately return the previously computed result without having to run the function again.
This also means the following computed property will never update, because Date.now()
is not a reactive dependency:
computed: {
now(){
return Date.now()
}
}
In comparison, a method invocation will always run the function whenever a re-render happens.
Why do we need caching?
Imagine we have an expensive computed property list
, which requires looping through a huge array and doing a lot of computations. Then we may have other computed properties that in turn depend on list
. Without caching, we would be executing list’s getter
many more times than necessary! In cases where you do not want caching, use a method
instead.
While computed properties are more appropriate in most cases, there are times when a custom watcher is necessary. That's why Vue provides a more generic way to react to data changes through the watch option. This is most useful when you want to perform asynchronous or expensive operations in response to changing data.
When you have some data that needs to change with other data changes, you can use Watch
to listen to the changes between them.
An object where keys are reactive properties to watch — examples include data or computed properties — and values are the corresponding callbacks. The value can also be a string of a method name, or an Object that contains additional options.
The Vue
instance will call $watch()
when it is instantiated, and traverse each property
of the watch
object.
Example:
<template>
<view>
<input type="number" v-model="a" style="border: red solid 1px;" />
<input type="number" v-model="b" style="border: red solid 1px;" />
<view>Sum:{{sum}}</view>
<button type="default" @click="add">Summation</button>
</view>
</template>
<script>
export default {
data() {
return {
a:1,
b:1,
sum: ""
}
},
watch: {
// Use watch to respond to data changes. The first parameter is the new value of newVal, and the second is the old value of oldVal
a: function(newVal, oldVal) {
console.log("a--newVal: ", newVal, "a--oldVal: ",oldVal);
},
b: function(newVal, oldVal) {
console.log("b--newVal: ", newVal, "b--oldVal: ",oldVal);
}
},
methods: {
add() {
this.sum = parseInt(this.a) + parseInt(this.b)
}
}
}
</script>
One problem with the above example is that it will not be executed when the page is just being loaded because there is no change. Use immediate
to solve the following problems.
Passing in immediate: true
in the option will trigger the callback immediately with the current value of the expression:
The watch
method defaults to handler
, and when immediate:true
, the handler
method will be executed first.
<template>
<view>
<input type="number" v-model="a" style="border: red solid 1px;" />
<input type="number" v-model="b" style="border: red solid 1px;" />
<view>Sum:{{sum}}</view>
<button type="default" @click="add">Summation</button>
</view>
</template>
<script>
export default {
data() {
return {
a:1,
b:1,
sum: ""
}
},
watch: {
a: {
handler(newVal, oldVal) {
console.log("a------: ", newVal, oldVal);
},
// handler method will be executed when the binding is initialized.
immediate: true
},
b: {
handler(newVal, oldVal) {
console.log("b------: ", newVal, oldVal);
},
// handler method will be executed when the binding is initialized.
immediate: true
}
},
methods: {
add() {
this.sum = parseInt(this.a) + parseInt(this.b)
}
}
}
</script>
To also detect nested value changes inside Objects, you need to pass in deep: true
in the options argument. This option also can be used to watch array mutations.
<template>
<view>
<input type="number" v-model="obj.a" style="border: red solid 1px;" />
<input type="number" v-model="obj.b" style="border: red solid 1px;" />
<view>Sum:{{sum}}</view>
<button type="default" @click="add">Summation</button>
</view>
</template>
<script>
export default {
data() {
return {
obj: {
a: 1,
b: 1,
},
sum:""
}
},
watch: {
obj: {
handler(newVal, oldVal) {
console.log('obj-newVal:' + JSON.stringify(newVal), 'obj-oldVal:' + JSON.stringify(oldVal), );
},
// handler method will be triggered once any attribute value in the object changes
deep: true
}
},
methods: {
add() {
this.sum = parseInt(this.obj.a) + parseInt(this.obj.b)
}
}
}
</script>
If you don't want to listen to other values in obj
, just want to listen to the change of the value of obj.a
, you can write it as a string to listen.
export default {
data() {
return {
obj: {
a: 1,
b: 1,
}
}
},
watch: {
"obj.a": {
//listen to the change of individual attribute value in obj objects
handler(newVal, oldVal) {
console.log('obj-newVal:' + newVal, 'obj-oldVal:' + oldVal);
}
}
}
}
Vue does provide a more generic way to observe and react to data changes on a current active instance: watch properties. When you have some data that needs to change based on some other data, it is tempting to overuse watch. However, it is often a better idea to use a computed property rather than an imperative watch callback. Consider this example:
export default {
data() {
return {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
}
},
watch: {
firstName: function(val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function(val) {
this.fullName = this.firstName + ' ' + val
}
}
}
The above code is imperative and repetitive. Compare it with a computed property version:
export default {
data() {
return {
firstName: 'Foo',
lastName: 'Bar'
}
},
computed: {
fullName(){
return this.firstName + ' ' + this.lastName
}
}
}