English
已经了解 Vue2,只想了解 Vue3 新功能可以参阅vue3新功能!
If you already have a Vue2 project and need to adapt to Vue3, please refer to vue2 project migration vue3!
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 of this article comes from vue3 Chinese document official website, but some adjustments have been made in combination with uni-app
to make it easier for developers to get started quickly. Thanks to the Vue team!
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:
Advantages of vue3 compared to vue2:
If you know html, js, then this chapter will let you quickly understand uni-app and their differences.
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 first-level node, used to write tag components, script
and style
are parallel first-level nodes, that is, there are 3 first-level nodes. This is called vue single file component specification sfc. <template>
<view>
注意必须有一个view,且只能有一个根view。所有内容写在这个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 require 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.
<script>
var util = require('../../../common/util.js'); //require这个js模块
var formatedPlayTime = util.formatTime(playTime); //调用js模块的方法
</script>
And in this util.js
, the previous function
should be packaged as a module method and exported (exports). Only exported methods and properties can be called externally, and those not exported belong to internal functions and variables of the module. This is the module specification for es6.
function formatTime(time) {
return time;//这里没写逻辑
}
module.exports = {
formatTime: formatTime
}
Of course, there are some advanced usages, such as renaming when exporting
// 直接使用js模块的属性。在hello uni-app有示例
var dateUtils = require('../../../common/util.js').dateUtils;
// 将js导入并重命名为echarts,然后使用echarts.来继续执行方法。在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 can more conveniently encapsulate a library including interface, js, and styles. See details
It used to be html tags like <div>
, now applet components like <view>
.
So what is the difference between a label and a component, isn't it all surrounded by angle brackets in English?
uni-app
refers to the applet specification and provides a batch of built-in components.
<script type="text/javascript">
var a;
function funa () {
}
</script>
Now the script has export default by default, write data, events and methods in it
export default {
are global variables inside the page and can be used in various methods.export default {}
is a large json, and data, life cycle, and method need to be separated by commas.textvalue
in the following example. The following globalvar cannot be bound and used in the template. In HBuilderX, typing the vdata code block can quickly generate the code structure of data.onLoad
below, are at the same level as data.methods
. Each method also needs to be separated by a comma. There is no need to use the function
statement, as long as the function written under methods
can be called in the template. Similarly, typing the vmethods
code block in HBuilderX can also generate the corresponding structure.<template>
<view>
<text>{{textvalue}}</text><!-- 这里演示了组件值的绑定 -->
<button :type="buttontype" @click="changetextvalue()">修改为789</button><!-- 这里演示了属性和事件的绑定 -->
</view>
</template>
<script>
var globalvar = 1
function globalfun(){}
export default {
data() {
return {
textvalue:"123",
buttontype:"primary"
};
},
onLoad() {
globalvar = 2
globalfun()
this.textvalue="456"//这里修改textvalue的值
},
methods: {
changetextvalue() {
this.textvalue="789"//这里修改textvalue的值
}
}
}
</script>
In the above example, the variable globalvar and function globalfun defined in the traditional way can be used in export default { }
, but they cannot be directly bound and called in the template. Templates can only bind variables in data and call methods in methods.
<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()">修改为789</button>
</body>
</html>
uni-app
solves the problem of interaction between js and DOM interface by using data binding method of vue. <template>
<view>
<text>{{textvalue}}</text><!-- 这里演示了组件值的绑定 -->
<button :type="buttontype" @click="changetextvalue()">修改为789</button><!-- 这里演示了属性和事件的绑定 -->
</view>
</template>
<script>
export default {
data() {
return {
textvalue:"123",
buttontype:"primary"
};
},
onLoad() {
this.textvalue="456"//这里修改textvalue的值,其实123都来不及显示就变成了456
},
methods: {
changetextvalue() {
this.textvalue="789"//这里修改textvalue的值,页面自动刷新为789
}
}
}
</script>
onclick
was used in the html tag to write the click event@click
(actually the abbreviation of v-on:click
, which is basically used in uni-app) to call the method in methods.uni-app
supports all vue syntaxes when it is published to H5; when it is published to apps and applets, due to platform limitations, all vue syntaxes cannot be implemented, but uni-app
is still the cross-platform with the highest support for vue syntax. end frame.
Compared with the Web platform, the differences in the use of Vue.js in uni-app
are mainly concentrated in two aspects:
Introduction and upgrade guide for uni-app project support vue 3.0
The vue 3.0 version supported by uni-app
project is as follows:
HBuilderX 3.3.3+
compiler is changed to vite
, the compiler of the previous version is webpack
.uni-app 3.2.5+
. From HBuilderX 3.3.13
, the nvue
compiler is upgraded to vite
.Precautions
Proxy
implementation, and does not support iOS9
and ie11
.Teleport
,Suspense
components are not supported temporarily.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 msg
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.
DOM
when the value of its expression changes.To dynamically bind one or more attributes, v-bind
is abbreviated to : ', and can be used to update
HTML attributes` responsively:
<!-- 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 '@':
<!-- 完整语法 -->
<view v-on:click="doSomething">点击</view>
<!-- short -->
<view @click="doSomething">点击</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.
h5, WeChat applet are not supported
<!-- 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
.
v-html
, WeChat applet will be converted to rich-text
, other side does not support v-html
.Cross-end rich text processing scheme can be found in: https://ask.dcloud.net.cn/article/35772
<template>
<view>
<view v-html="rawHtml"></view>
</view>
</template>
<script>
export default {
data() {
return {
rawHtml: '<div style="text-align:center;background-color: #007AFF;"><div >我是内容</div><img src="https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/uni@2x.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 intuitive - it looks very CSS-like, but is actually a JavaScript
object. CSS property names can be named using camelCase (camelCase
) or dash-separated (kebab-case
, remember to enclose them in quotes):
<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)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">现在你看到我了</view>
<view v-else>你看不到我了</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>标题</view>
<view>内容:现在你看到我了</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.
不要使用对象或数组之类的非基本类型值作为 v-for 的 key。请用字符串或数值类型的值。
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 (often abbreviated to the @ symbol, hereinafter referred to as: @event) to listen to DOM events and execute some JavaScript
when the event is triggered.
The usage is v-on:click="methodName"
or use the shortcut @click="methodName"
(the @ abbreviation method is generally used in uni-app)
The value of the command, directly write js in the string. For example, counter += 1
below is a piece of js.
<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>
However, many event handling logic will be more complex, so it is not feasible to directly write JavaScript
code in component property values. So @event can also receive a method name that needs to be called.
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>
<!-- 这两个 one() 和 two() 将执行按钮点击事件 -->
<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.
注意
uni-app x
只支持 stop
和 once
。bind
和 catch
进行事件绑定;也不能在 JS 中使用event.preventDefault()
和event.stopPropagation()
方法。@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 @:
A glance at the template
template can easily locate the corresponding method in the JavaScript
code.
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',
longtap: 'longtap', //推荐使用longpress代替
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.
在下面的示例中,输入框通过v-model
绑定了message
,用户在输入框里输入内容时,这个内容会实时赋值给message
。当然在代码里为message
赋值也会实时同步到界面上input里。这就是双向绑定。
<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 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">
当前选择:{{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: '美国'
},
{
name: 'CHN',
value: '中国',
checked: 'true'
},
{
name: 'BRA',
value: '巴西'
},
{
name: 'JPN',
value: '日本'
},
{
name: 'ENG',
value: '英国'
},
{
name: 'TUR',
value: '法国'
}
]
}
},
methods: {
radioChange(e) {
console.log('radio发生change事件,携带value值为:', e.target.value)
}
}
}
</script>
Each computed property contains a getter
function and a setter
function, which is read by default using the getter
function. The this
context of all getter
and setter
functions is automatically bound to the Vue instance.
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'
]
}
}
}
We want to display different messages depending on whether the author already has some books, we can use the expression in the template
<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 using computed properties
<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}}</view>
<button type="default" @click="add">求和</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 parameter oldVal is the old value
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}}</view>
<button type="default" @click="add">求和</button>
</view>
</template>
<script>
export default {
data() {
return {
a:1,
b:1,
sum: ""
}
},
watch: {
a: {
handler(newVal, oldVal) {
console.log("a------: ", newVal, oldVal);
},
immediate: true//初始化绑定时就会执行handler方法
},
b: {
handler(newVal, oldVal) {
console.log("b------: ", newVal, oldVal);
},
immediate: true//初始化绑定时就会执行handler方法
}
},
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}}</view>
<button type="default" @click="add">求和</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), );
},
deep: true//对象中任一属性值发生变化,都会触发handler方法
}
},
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": {//监听obj对象中的单个属性值的变化
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
}
}
}