- install
- Introduce
- application & Component instance
- Template syntax
- Data Property And methods
- Compute properties and listeners
- Class And Style binding
- Conditions apply colours to a drawing
- The list of rendering
- Event handling
- Form input binding
- Foundation of organization
install
take Vue.js There are three main ways to add to a project :
-
On the page with CDN package The form of import .
For prototyping or learning , You can use the latest version like this :
<script src="https://unpkg.com/[email protected]"></script>
-
download JavaScript Document and Since the escrow .
If you want to avoid using build tools , But it can not be used in the production environment CDN, Then you can download the relevant information .js File and self hosting on your server . Then you can go through
<script>
Tags introduced , And use CDN Is similar to .` -
Use npm Install it .
In use Vue Recommended for building large applications npm install [1] .NPM Can be very good with Webpack or Rollup Module packer used together .Vue Also provided is the compilation of Single file component The kit for .
# Latest stable version $ npm install [email protected]
-
Use official CLI To build a project , It provides fully functional build settings for modern front-end workflow ( for example , Hot heavy load 、 Tips for saving, etc ).
In use Vue Recommended for building large applications npm install [1] .NPM Can be very good with Webpack or Rollup Module packer used together .Vue Also provided is the compilation of Single file component The kit for .
# Latest stable version
$ npm install [email protected]
Vite
Vite It's a web Develop build tools , Because of its original ES Module import mode , Can achieve lightning cold server startup .
By running the following command in the terminal , have access to Vite Fast build Vue project .
Use npm:
$ npm init @vitejs/app <project-name>
$ cd <project-name>
$ npm install
$ npm run dev
yarn:
$ yarn create @vitejs/app <project-name>
$ cd <project-name>
$ yarn
$ yarn dev
Introduce
Vue.js What is it?
Vue ( pronunciation /vjuː/, Be similar to view) Is a set for building user interfaces Progressive framework . Unlike other large frameworks ,Vue Designed to be applied layer by layer from the bottom up .Vue The core library focuses only on the view layer , Not only easy to get started , It is also easy to integrate with third-party libraries or existing projects . On the other hand , When and Modern tool chain As well as a variety of Support library When used in combination ,Vue It is also fully capable of providing drivers for complex single-page applications .
If you want to learn in depth Vue I know more about it before , We Made a video , Take you to understand its core concepts and an example project .
start
The official guide assumes that you already know about HTML、CSS and JavaScript Intermediate knowledge . If you're just starting to learn front end development , Making the framework your first step may not be the best idea —— Master the basic knowledge and come back ! Previous experience with other frameworks will help , But it's not necessary
Try Vue.js The easiest way is to use Hello World Example , You can open it in the browser's new tab , Follow the examples to learn some basic usage .
Installation tutorial More installation Vue The way . Please note that we Not recommended Novices use it directly vue-cli
, Especially if you are not familiar with the basis Node.js Build the tool .
Declarative rendering
Vue.js At its core is a simple template syntax that allows data to be rendered declaratively into the DOM The system of :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://unpkg.com/[email protected]"></script>
</head>
<body>
<div id="counter">
Counter: {{ counter }}
</div>
<script>
const Counter = {
data () {
return {
counter: 0
}
}
}
Vue.createApp(Counter).mount("#counter")
</script>
</body>
</html>
We have successfully created the first Vue application ! It looks very similar to rendering a string template , however Vue A lot of work has been done behind the scenes . Now the data and DOM It's been linked , Everything is Responsive . How can we confirm ? Please see the following example , among counter
property Incrementing per second , You'll see the rendered DOM How it changed :
const Counter = {
data () {
return {
counter: 0
}
},
mounted () {
setInterval(() => {
this.counter++
}, 1000)
}
}
Except for text interpolation , We can also bind elements like this attribute:
<div id="counter">
<span v-bind:title="message">
message: title
</span>
</div>
const Counter = {
data () {
return {
counter: 0,
message: 'lvhanghmm ' + new Date().toLocaleString()
}
}
}
Here we come across something new . What you see v-bind
attribute go by the name of Instructions . Instructions are prefixed with v-
, To show that they are Vue Special offers attribute. You may have guessed that , They're going to be rendering DOM Apply special responsive behavior on . ad locum , The directive means :“ This element node's title
attribute And the current active instance of message
property bring into correspondence with ”.
Processing user input
In order for users to interact with Applications , We can use v-on
Instruction to add an event listener , It calls the method defined in the instance :
<div id="counter">
<p>{{message}}</p>
<input type="button" value=" reverse " v-on:click="reverseMessage">
</div>
const Counter = {
data () {
return {
counter: 0,
message: 'Hello lvhanghmm!',
}
},
methods: {
reverseMessage () {
this.message = this.message
.split('') // split Is to convert a string into an array of individual elements !
.reverse()
.join('') // join Is to convert an array to a string !
}
}
}
Vue.createApp(Counter).mount("#counter")
Notice in this method , We updated the status of the app , But no touch DOM—— be-all DOM Operations are made by Vue To deal with it , The code you write just needs to focus on the logical level .
Vue It also provides v-model
Instructions , It can easily achieve two-way binding between form input and application state .
<div id="counter">
<p>{{message}}</p>
<input v-model="message">
</div>
const Counter = {
data () {
return {
counter: 0,
message: 'Hello lvhanghmm!',
}
},
methods: {
}
}
Vue.createApp(Counter).mount("#counter")
v-bind and v-model The difference is that -v-bind Binding is a native property !v-model Binding is a custom property !
Conditions and cycles
It's also quite easy to control whether an element is displayed or not
<div id="counter">
<span v-if="seen"> Now you see me !</span>
</div>
const Counter = {
data () {
return {
seen: true,
message: 'Hello lvhanghmm!'
}
},
methods: {
}
}
Vue.createApp(Counter).mount("#counter")
This example demonstrates that we can not only bind data to DOM Text or attribute, You can also bind to DOM Of structure . Besides ,Vue It also provides a powerful transition effect system , Can be in Vue Insert / to update / Automatically apply when removing elements Transition effects .
You can put... In the sandbox below seen
from true
Change to false
, To check the effect :
There are many other instructions , Each has a special function . for example ,v-for
Instruction can bind array data to render a list of items :
<div id="counter">
<ol>
<li v-for="todo in todos">
{{todo.book}}
</li>
</ol>
</div>
const Counter = {
data () {
return {
// seen: true,
message: 'Hello lvhanghmm!',
todos: [
{
book: 'JavaScript Advanced programming fourth edition '
},
{
book: ' Rhinoceros Seventh Edition '
},
{
book: 'xxxxxxxxx'
}
]
}
},
methods: {
}
}
Vue.createApp(Counter).mount("#counter")
Componentized application building
Component systems are Vue Another important concept of , Because it's an abstraction , Allow us to use small 、 Independent and often reusable components build large applications . Think carefully , Almost any type of application interface can be abstracted as a component tree :
stay Vue in , A component is essentially an instance with predefined options . stay Vue It's easy to register components in : If yes App
Object to create a component object , And define it in the components
In the options :
// establish Vue application
const app = Vue.createApp(Counter)
// Definition is named todo-item The new component
<ol>
<!-- Create a todo-item Component instance -->
<todo-item></todo-item>
</ol>
// mount Vue application !
app.mount("#counter")
Now? , You can put it in the template of another component :
<ol>
<!-- Create a todo-item Component instance -->
<todo-item></todo-item>
</ol>
But this will render the same text for each to-do , It doesn't look cool . We should be able to transfer data from the parent component to the child component . Let's modify the definition of a component , Make it accept a prop:
// Definition is named todo-item The new component
app.component('todo-item', {
props: ['todo'],
template: `<li>{{todo.book}}</li>`
})
Now? , We can use v-bind
Instruction passes the to-do item to each component of the loop output
<div id="counter">
<ol>
<!--
Now we're doing it for everyone todo-item Provide todo object
todo Objects are variables , That is, its content can be dynamic .
We also need to provide one for each component “key”, Later again
Explain in detail .
-->
<todo-item
v-for="item in todos"
v-bind:todos="item"
v-bind:key="item.id"
></todo-item>
</ol>
</div>
const Counter = {
data () {
return {
// seen: true,
message: 'Hello lvhanghmm!',
todos: [
{
id: 1,
book: 'JavaScript Advanced programming fourth edition '
},
{
id: 2,
book: ' Rhinoceros Seventh Edition '
},
{
id: 3,
book: 'xxxxxxxxx'
}
]
}
},
methods: {
}
}
// establish Vue application
const app = Vue.createApp(Counter)
// Definition is named todo-item The new component
app.component('todo-item', {
props: ['todos'],
template: `<li>{{todos.book}}</li>`
})
// mount Vue application !
app.mount("#counter")
Although this is only an example of deliberate design , But we've managed to split the application into two smaller units . Subunit pass prop The interface is well decoupled from the parent unit . We can now further improve <todo-item>
Components , Provide more complex templates and logic , Without affecting the parent app .
In a large application , It is necessary to divide the whole application into several components , To make development easier to manage . stay Follow up tutorials We'll go into the components , But here's one ( A hypothetical ) Example , To show what an application template looks like with components :
<div id="app">
<app-nav></app-nav>
<app-view>
<app-sidebar></app-sidebar>
<app-content></app-content>
</app-view>
</div>
Relationship with custom elements
You may have noticed Vue Components are very similar to custom elements —— It is Web Component specifications Part of , This is because Vue The component syntax section of refers to the specification . for example Vue Component implementation Slot API And is
attribute. however , There are still a few key differences :
-
Web Components The specification has been completed and passed , But it's not native to all browsers . at present Safari 10.1+、Chrome 54+ and Firefox 63+ Native support Web Components. by comparison ,Vue Components don't need any polyfill, And in all supported browsers (IE11 And higher ) It's the same thing . When necessary, ,Vue Components can also be wrapped in native custom elements .
-
Vue Components provide some important functions that pure custom elements don't have , The most prominent is cross component data flow 、 Custom event communication and build tool integration .
although Vue No custom elements are used internally , But using custom elements in applications 、 Or in the form of custom elements , There's still good interoperability .Vue CLI Support will also be given to Vue Components build into native custom elements .
application & Component instance
Create an application instance
Every Vue Applications are all through createApp
Function to create a new Application example At the beginning :
const app = Vue.createApp({
/* Options */
}
The application instance is used to register in the application “ overall situation ” Component's . We will discuss it in detail in the guide later , A simple example :
const app = Vue.createApp({})
app.component('SearchInput', SearchInput)
app.directive('focus', FocusDirective)
app.use(LocalPlugin)
Most methods exposed by the application instance will return the same instance , Allow chaining :
Vue.createApp({})
.component('SearchInput', SearchInput)
.directive('focus', FocusDirective)
.use(LocalPlugin)
The root component
Pass to createApp
For configuration The root component . When we mount When applied , This component is used as the starting point for rendering .
An application needs to be mounted to a DOM In the elements . for example , If you want to put one Vue Application mount to <div id="app"></div>
, It should be introduced #app
:
const rootComponent = {
/* Options */
}
const app = Vue.createApp(rootComponent)
const vm = app.mount('#app')
Different from most application methods ,mount
Do not return the application itself . contrary , It returns the root component instance .
Although not completely followed MVVM Model , however Vue It's also inspired the design of . So it's often used in documents vm
(ViewModel Abbreviation ) This variable name represents the component instance .
Although all the examples on this page only need a single component , But most real-world applications are organized into a nest 、 Reusable component tree . for instance , One todo The application component tree might look like this :
Root Component
└─ TodoList
├─ TodoItem
│ ├─ DeleteTodoButton
│ └─ EditTodoButton
└─ TodoListFooter
├─ ClearTodosButton
└─ TodoListStatistics
Each component will have its own component instance vm
. For some components , Such as TodoItem
, There can be multiple instances rendering at any time . All component instances in this application will share the same application instance .
We'll be back later Component basis Chapter specific development . But now, , You just need to understand that the root component is no different from other components , The configuration options are the same , The behavior of the corresponding component instance is the same .
Component instance property
In the previous guide , We know each other data
property. stay data
As defined in property Is exposed through component instances :
const app = Vue.createApp({
data () {
return {
count: 4
}
}
})
const vm = app.mount('#app')
console.log(vm.count); // 4
There are a variety of other component options , User defined property Add to component instance , for example methods
,props
,computed
,inject
and setup
. We'll talk about them in depth in a later guide . All components of a component instance property, Define... Anyway , Can be accessed in the template of the component .
Vue Some built-in components are also exposed through component instances property, Such as $attrs
and $emit
. these property There is one. $
Prefix , To avoid conflicts with user-defined property Name conflict .
Lifecycle hook
Each component has to go through a series of initialization processes when it is created —— for example , Data monitoring needs to be set 、 Compiling templates 、 Mount the instance to DOM And update when the data changes DOM etc. . At the same time, in this process, we will also run something called Lifecycle hook Function of , This gives users the opportunity to add their own code at different stages .
such as created Hooks can be used to execute code after an instance is created :
const app = Vue.createApp({
data () {
return {
count: 4
}
},
created () {
// `this` Point to vm example
console.log('count is: ' + this.count)
}
})
const vm = app.mount('#app')
There are also some other hooks , Called at different stages of the instance lifecycle , Such as mounted、updated and unmounted. Life cycle hook's this
The context points to the current active instance that calls it .
Don't be in the options property Or callback Arrow function , such as
created: () => console.log(this.a)
orvm.$watch('a', newValue => this.myMethod())
. Because the arrow function does notthis
,this
Will always look up the parent lexical scope as a variable , Until it's found , Often leads toUncaught TypeError: Cannot read property of undefined
orUncaught TypeError: this.myMethod is not a function
Or something like that .
Lifecycle diagram
Official picture :
The figure below is provided by blog users pachulia Provided Vue2 Life cycle function diagram of
Template syntax
Vue.js Based on HTML Template syntax for , Allow developers to declaratively put DOM Data bound to the underlying component instance . all Vue.js The templates are all legal HTML, So browsers and HTML Parser parsing .
At the bottom of the implementation ,Vue Compile the template into a virtual DOM Rendering function . Combined with responsive system ,Vue Be able to intelligently calculate the minimum number of components that need to be re rendered , And put DOM Minimize the number of operations .
If you are familiar with virtual DOM And prefer JavaScript The original power of , You can also use no templates , Write directly to render (render) function , Use optional JSX grammar .
interpolation
Text
The most common form of data binding is to use “Mustache” grammar ( Double brace ) Text interpolation of :
<span>Message: {{ msg }}</span>
Mustache The tag will be replaced by the corresponding component instance msg
property Value . No matter when , On the bound component instance msg
property It has changed , The interpolation will be updated .
By using v-once Instructions , You can also perform one-time interpolation , When data changes , The interpolation will not update . But be aware that this will affect other data bindings on this node :
<span v-once> This will not change : {{ msg }}</span>
original HTML
Double braces interpret the data as plain text , Instead of HTML Code . In order to output the real HTML, You need to use v-html
Instructions :
<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
data () {
return {
count: 4,
msg: 'lvhanghmm',
rawHtml: `<h1> I am a h1 label </h1>`
}
}
This span
Will be replaced by property value rawHtml
, Act directly as HTML—— Will ignore parsing property Data binding in values . Be careful , You can't use v-html
To compound the local template , because Vue It's not a string based template engine . conversely , For the user interface (UI), Components are more suitable as reusable and composable basic units .
Dynamically render any image on your site HTML It's very dangerous , Because it can easily lead to XSS attack . Please use... Only for trusted content HTML interpolation , Never The content provided by users is used as interpolation .
Attribute
Mustache Grammar can't be in HTML attribute Use in , However , have access to v-bind
Instructions :
<p v-bind:class="cla1">Using mustaches: {{ rawHtml }}</p>
data () {
return {
count: 4,
msg: 'lvhanghmm',
rawHtml: `<h1> I am a h1 label </h1>`,
cla1: 'lvhanghmm'
}
}
If the value of the binding is null
or undefined
, Then the attribute Will not be included on the rendered element .
For boor attribute ( They are worth as long as they exist true
),v-bind
Work a little different , In this case :
<button v-bind:disabled="isButtonDisabled"> Button </button>
If isButtonDisabled
The value of is truthy[1], that disabled
attribute Will be included . If the value is an empty string , It will also be included , And <button disabled="">
bring into correspondence with . For other wrong values , The attribute Will be omitted .
Use JavaScript expression
so far , In our template , We've always bound simple property Key value . But actually , For all data bindings ,Vue.js All provide complete JavaScript Expressions support .
<div id="app">
<p>{{ number + 1 }}</p>
<p>{{ ok ? "YES" : "NO" }}</p>
<p>{{ msg.split('').reverse().join('') }}</p>
<p v-bind:id="'list' + id"> I am a p label </p>
</div>
const app = Vue.createApp({
data () {
return {
msg: 'lvhanghmm',
id: 'lvchengxin',
ok: false,
number: 5894
}
},
methods: {
setMsg: function () {
console.log(123)
}
}
})
const vm = app.mount('#app')
These expressions are used in the data scope of the current active instance as JavaScript Be resolved . One limitation is , Each binding can only contain A single expression , So the following examples are all Can't take effect .
<!-- This is the statement , It's not an expression :-->
{{ var a = 1 }}
<!-- Flow control will not work , Please use ternary expression -->
{{ if (ok) { return message } }}
Instructions
Instructions (Directives) It's with v-
Special prefixes attribute. Instructions attribute The expected value of is Single JavaScript expression (v-for
and v-on
It's an exception , Let's talk about it later ). The duty of the order is , When the value of an expression changes , The joint and several effects of it , Act responsibly on DOM. Review the examples we saw in the introduction :
<p v-if="seen"> Now you see me </p>
here ,v-if
The instruction will be based on the expression seen
The value of true or false to insert / remove <p>
Elements .
Parameters
Some instructions can receive a “ Parameters ”, After the instruction name, indicate with a colon . for example ,v-bind
Instructions can be used to update... In response HTML attribute:
<a v-bind:href="url"> ... </a>
ad locum href
Is the parameter , inform v-bind
The command will change the href
attribute And the expression url
Value binding for .
Another example is v-on
Instructions , It's used to monitor DOM event :
<a v-on:click="doSomething"> ... </a>
Here, the parameter is the name of the listening event . We'll also discuss event handling in more detail .
Dynamic parameters
It can also be used in instruction parameters JavaScript expression , The method is to use square brackets :
<!--
Be careful , There are some constraints in the way parameter expressions are written , As follows “ Constraints on dynamic parameter expressions ” As described in section .
-->
<a v-bind:[attributeName]="url"> ... </a>
there attributeName
Will be treated as a JavaScript Expressions are evaluated dynamically , The resulting value will be used as the final parameter . for example , If your component instance has a data property attributeName
, Its value is "href"
, So this binding will be equivalent to v-bind:href
.
similarly , You can use dynamic parameters to bind handler functions for a dynamic event name :
<a v-on:[eventName]="doSomething"> ... </a>
In this example , When eventName
The value of is "focus"
when ,v-on:[eventName]
Will be equivalent to v-on:focus
Modifier
Modifier (modifier) It's half a period .
Specified special suffix , Used to indicate that an instruction should be bound in a special way . for example ,.prevent
Modifier tell v-on
The instruction calls... For the triggered event event.preventDefault()
:
<from v-on:submit.prevent="onSubmit">...</from>
Next, I'm going to v-on
and v-for
And so on , You'll see other examples of modifiers .
abbreviation
v-
Prefix as a visual cue , Used to identify... In the template Vue specific attribute. When you are using Vue.js Add dynamic behavior to existing tags (dynamic behavior) when ,v- Prefixes help a lot , However , For some frequently used instructions , You'll feel cumbersome to use . meanwhile , In building by Vue Single page application that manages all templates (SPA - single page application) when ,v-
Prefixes have become less important . therefore ,Vue by v-bind
and v-on
These two most commonly used instructions , Provides a specific abbreviation :
v-bind abbreviation
<!-- Complete grammar -->
<a v-bind:href="url"> ... </a>
<!-- abbreviation -->
<a :href="url"> ... </a>
<!-- Short for dynamic parameters -->
<a :[key]="url"> ... </a>
v-on abbreviation
<!-- Complete grammar -->
<a v-on:click="doSomething"> ... </a>
<!-- abbreviation -->
<a @click="doSomething"> ... </a>
<!-- Short for dynamic parameters (2.6.0+) -->
<a @[event]="doSomething"> ... </a>
They may look like ordinary HTML It's a little different , but :
And @
about attribute Names are legal characters , In all support Vue All browsers can be parsed correctly . and , They don't appear in the final render tag . Abbreviation syntax is completely optional , But as you learn more about what they do , You'll be glad to have them .
Start on the next page , We will use abbreviations in the example , Because this is Vue The most common usage of developers .
matters needing attention
For dynamic parameter value conventions
Dynamic parameters are expected to find a string , In case of abnormality, the value is null
. This special null
Values can be explicitly used to remove bindings . Any other non string value will trigger a warning .
For dynamic parameter expression conventions
Dynamic parameter expressions have some syntax constraints , Because some characters , Like spaces and quotes , Put it in HTML attribute The name is invalid . for example :
<!-- This triggers a compilation warning -->
<a v-bind:['foo' + bar]="value"> ... </a>
The alternative is to use expressions without spaces or quotation marks , Or use Compute properties Replace this complex expression .
stay DOM When using templates in ( Directly in a HTML Writing templates in the document ), You also need to avoid using uppercase characters to name keys , Because the browser will attribute All names are forced to lowercase :
<!--
stay DOM When using templates in, this code is converted to `v-bind:[someattr]`.
Unless there's one in the instance called “someattr” Of property, Otherwise, the code won't work .
-->
<a v-bind:[someAttr]="value"> ... </a>
JavaScript expression
Template expressions are placed in sandbox , Only access A white list of global variables , Such as Math
and Date
. You should not try to access user-defined global variables in template expressions .
Data Property And methods
Data Property
Component's data
Option is a function .Vue This function is called in the process of creating new component instances . It should return an object , then Vue It's wrapped up in a responsive system , And $data
Is stored in the component instance . For convenience , Any top level of the object property It is also exposed directly through component instances :
const app = Vue.createApp({
data () {
return {
count: 4
}
}
})
const vm = app.mount('#app')
console.log(vm.$data.count); // 4
console.log(vm.count); // 4
// modify vm.count The value of will also be updated $data.count
vm.count = 5
console.log(vm.$data.count) // => 5
// vice versa
vm.$data.count = 6
console.log(vm.count) // => 6
These examples property Is added only when the instance is first created , So you need to make sure they're all there data
In the object returned by the function . When necessary, , To update a property Use null
、undefined
Or other values of the occupancy .
Directly not included in data
New in property Adding to a component instance is possible . But because of the property Responsive style without behind $data
In object , therefore Vue Responsive system based on It won't automatically track it .
Vue Use $
Prefixes expose their built-in properties through component instances API. It also serves the interior property Retain _
Prefix . You should avoid using the top level at the beginning of these two characters data
property name .
Method
We use it methods
Option to add a method to a component instance , It should be an object that contains the required methods :
const app = Vue.createApp({
data() {
return { count: 4 }
},
methods: {
increment() {
// `this` Point to the component instance
this.count++
}
}
})
const vm = app.mount('#app')
console.log(vm.count) // => 4
vm.increment()
console.log(vm.count) // => 5
Vue Automatically for methods
binding this
, So that it always points to the component instance . This will ensure that the method remains correct when used as an event listener or callback this
Point to . In defining methods
You should avoid using arrow functions when using , Because it will stop Vue Bind properly this
Point to .
these methods
And all other components of the component instance property It can also be accessed in the template of the component . In the template , They are usually used as event listeners :
<button @click="increment">Up vote</button>
In the example above , Click on <button>
when , Would call increment
Method .
You can also invoke the method directly from the template. . As you'll see in the next chapter , Usually change to Compute properties Will be better . however , In the case that calculating attributes is not feasible , How to use it can be useful . You can support it in the template JavaScript The method is called anywhere in the expression :?!
<span :title="toTitleDate(date)">
{{ formatDate(date) }}
</span>
If toTitleDate
or formatDate
Access any responsive data , Then track it as a rendering dependency , It's like using it directly in a template .
Methods called from templates should not have any side effects , Such as changing data or triggering asynchronous processes . If you want to do this , It should be changed Lifecycle hook .
Anti shake and throttling
Vue No built-in support for anti shake and throttling , But you can use Lodash Wait for the library to implement .
If a component is used only once , Can be in methods
In the application of anti shake :
<script src="https://unpkg.com/[email protected]/lodash.min.js"></script>
<script>
Vue.createApp({
methods: {
// use Lodash The anti shake function of
click: _.debounce(function() {
// ... Response Click ...
}, 500)
}
}).mount('#app')
</script>
however , This approach has potential problems with reusable components , Because they all share the same anti shake function . To make component instances independent of each other , You can hook in the life cycle created
Add the anti shake function in the :
app.component('save-button', {
created() {
// use Lodash The anti shake function of
this.debouncedClick = _.debounce(this.click, 500)
},
unmounted() {
// When removing components , Cancel timer
this.debouncedClick.cancel()
},
methods: {
click() {
// ... Response Click ...
}
},
template: `
<button @click="debouncedClick">
Save
</button>
`
})
Compute properties and listeners
Compute properties
The expressions in the template are very convenient , But they were designed for simple computation . Putting too much logic in a template makes it too heavy and difficult to maintain . for example , There is a nested array object :
Vue.createApp({
data() {
return {
author: {
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
}
}
}
})
We want to author
Are there any books showing different messages
<div id="computed-basics">
<p>Has published books:</p>
<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>
</div>
here , Templates are no longer simple and declarative . You have to look at it first , And then realize that the computation it performs depends on author.books
. If you want to include this calculation multiple times in the template , The problem will get worse .
therefore , For any complex logic that contains responsive data , You should use Compute properties .
Basic examples
<div id="app">
<p>Has published books:</p>
<span>{{ publishedBookMessage }}</span>
</div>
const vm = {
data() {
return {
author: {
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
}
}
},
computed: {
// Computed attribute getter
publishedBookMessage() {
// this perform vm example
return this.author.books.length > 0 ? 'YES' : 'NO'
}
}
}
const app = Vue.createApp(vm)
app.mount('#app')
A calculated property is declared here publishedBooksMessage
.
Try changing the application data
in books
The value of the array , You will see publishedBooksMessage
How to change it accordingly .
You can bind data to a calculated property in a template just like a normal property .Vue know vm.publishedBookMessage
Depend on vm.author.books
, So when vm.author.books
When there is a change , All dependence vm.publishedBookMessage
The binding of will also be updated . And best of all, we've created this dependency in a declarative way : Computed attribute getter Function has no side effects , It's easier to test and understand .
Compute property cache VS Method
You may have noticed that we can achieve the same effect by calling the method in the expression :
<p>{{ calculateBooksMessage() }}</p>
// In components
methods: {
calculateBooksMessage() {
return this.author.books.length > 0 ? 'Yes' : 'No'
}
}
We can define the same function as a method instead of a calculated property . The end result of the two ways is exactly the same . However , The difference is Computed properties are cached based on their reaction dependencies . Computed properties are revalued only when the related responsive dependency changes . This means that as long as author.books
It hasn't changed , Multiple access publishedBookMessage
The calculated property will immediately return the previous calculation result , Instead of executing the function again .
This also means that the following calculated properties will not be updated , because Date.now () Not reactive dependency :
computed: {
now() {
return Date.now()
}
}
by comparison , Whenever re rendering is triggered , Call method will always execute function again .
Why do we need caching ? Let's say we have a computing property with high performance overhead list
, It needs to traverse a huge array and do a lot of calculations . Then we may have other computational properties that depend on list
. If there is no cache , We will inevitably carry out many times list
Of getter! If you don't want a cache , Please use method
To replace .
Computed attribute Setter
The default calculation property is getter, But you can also provide a setter:
// ...
computed: {
fullName: {
// getter
get() {
return this.firstName + ' ' + this.lastName
},
// setter
set(newValue) {
const names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
// ...
Now run vm.fullName = 'John Doe'
when ,setter Will be called ,vm.firstName
and vm.lastName
Will also be updated accordingly .
The listener
Although calculating properties is more appropriate in most cases , But sometimes you need a custom listener . That's why Vue adopt watch
Options provide a more general approach , To respond to changes in data . When you need to perform asynchronous or expensive operations when data changes , This way is the most useful .
for example :
<div id="app">
<p>
Ask a yes/no question:
<input v-model="question" />
</p>
<p>{{ answer }}</p>
</div>
<!-- because AJAX The ecology of libraries and general tools is already quite rich ,Vue The core code is not duplicated -->
<!-- Provide these features to keep things simple . It also gives you the freedom to choose tools you are more familiar with . -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js"></script>
<script>
const vm = {
data() {
return {
question: '',
answer: 'Questions usually contain a question mark. ;-)'
}
},
watch: {
// whenever question changes, this function will
question (newQuestion, oldQuestion) {
if (newQuestion.indexOf('?') > -1) {
this.getAnswer()
}
}
},
methods: {
getAnswer () {
this.answer = 'Thinking...'
axios
.get('https://yesno.wtf/api')
.then(response => {
this.answer = response.data.answer
})
.catch(error => {
this.answer = 'Error! Could not reach the API. ' + error
})
}
}
}
const app = Vue.createApp(vm)
app.mount('#app')
</script>
In this example , Use watch
Option allows us to perform asynchronous operations ( Visit one API), Limit how often we do this , And before we get the final result , Set intermediate state . These are all things that computational properties can't do .
except watch Beyond the options , You can also use imperative vm.$watch API.
Compute properties vs The listener
Vue Provides a more general way to observe and respond to data changes on current active instances : Listening properties . When you have some data that needs to change with other data , It's easy for you to abuse watch
—— Especially if you've used AngularJS. However , It's usually better to use calculated properties rather than imperative watch
Callback . Think about this example :
<div id="demo">{{ fullName }}</div>
const vm = {
data() {
return {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
}
},
watch: {
firstName (val) {
this.fullName = val + ' ' + this.lastName
},
lastName(val) {
this.fullName = this.firstName + ' ' + val
}
},
}
const app = Vue.createApp(vm)
app.mount('#app')
The above code is imperative and repetitive . Compare it with the version of the calculated property :
const vm = {
data() {
return {
firstName: 'Foo',
lastName: 'Bar',
}
},
computed: {
fullName () {
return this.firstName + " " + this.lastName
}
}
}
const app = Vue.createApp(vm)
app.mount('#app')
Much better , isn't it? ?
Class And Style binding
Operating elements class Lists and inline styles are a common requirement for data binding . Because they are all attribute, So we can use v-bind
Deal with them : You just need to calculate the string result through the expression . however , String splicing is troublesome and error prone . therefore , Will be v-bind
be used for class
and style
when ,Vue.js Made a special enhancement . The type of expression result is in addition to string , It can also be an object or an array .
binding HTML Class
Object syntax
We can pass it on :class
(v-bind:class
Abbreviation ) An object , To switch dynamically class:
<div :class="{ active: isActive }"></div>
The above syntax active
This class Whether it exists or not will depend on the data property isActive
Of truthiness.
You can pass in more fields in the object to dynamically switch multiple class. Besides ,:class
Instructions can also be compared with ordinary class
attribute coexistence . When there are the following templates :
You can pass in more fields in the object to dynamically switch multiple class. Besides ,:class
Instructions can also be compared with ordinary class
attribute coexistence . When there are the following templates :
<div id="app">
<div
class="static"
:class="{ active: isActive, lvhanghmm: isLvchengxin}"
></div>
</div>
And the following data:
data() {
return {
isActive: true,
isLvchengxin: true
}
}
The rendered result is :
<div class="static active lvhanghmm"></div>
When isActive
perhaps lvhanghmm When the change ,class The list will be updated accordingly . for example , If lvhanghmm The value of is true
,class The list will change to "static active lvhanghmm"
.
Bound data objects do not need to be defined inline in the template :
<div
class="static"
:class="classObject"
></div>
data() {
return {
classObject: {
isActive: false,
lvhanghmm: true
}
}
}
The rendered result is the same as above . We can also bind a return object here Compute properties . This is a common and powerful model :
<div
class="static"
:class="classObject"
></div>
data() {
return {
isActive: true,
error: null
}
},
computed: {
classObject () {
return {
active: this.isActive && !this.error,
lvhanghmm: this.error && this.error.type === 'fatal'
}
}
}
Array syntax
We can pass an array to :class
, To apply a class list :
<div :class="[activeClass, errorClass]"></div>
data() {
return {
activeClass: 'lvhanghmm',
errorClass: 'lvchengxin'
}
}
The rendered result is :
<div class="lvhanghmm lvchengxin"></div>
If you want to switch between the class, You can use ternary expressions :
<div :class="[isActive ? activeClass : '', errorClass]"></div>
data() {
return {
activeClass: 'dapeng',
isActive: true,
errorClass: 'lvchengxin'
}
}
This will always add errorClass
, But only in isActive
by truthy[1] Only add activeClass
.【 I changed the chestnut , Now both will be displayed !】
however , When there are multiple conditions class It's a little tedious to write like this . So you can also use object syntax in array syntax :
<div :class="[{ active: isActive }, errorClass]"></div>
Use... On components
This chapter assumes that you've learned about Vue Components Have some understanding . Of course, you can also skip here first , Come back later .
When you use it on a custom component with a single root element class
attribute when , these class Will be added to the element . Existing on this element class It will not be overwritten .
for example , If you declare this component :
const app = Vue.createApp(vm)
app.component('my-component', {
template: `<p class="foo bar">Hi!</p>`
})
app.mount('#app')
Then add some... When using it class:
<my-component class="baz boo"></my-component>
HTML Will be rendered as :
<p class="foo bar baz boo">Hi</p>
For with data binding class The same applies :
<my-component :class="{ active: isActive }"></my-component>
data() {
return {
isActive: true,
}
}
<p class="foo bar active">Hi</p>
If your component has more than one root element , You need to define which parts will receive this class . have access to $attrs
Component properties perform this operation :
<div id="app">
<my-component class="baz"></my-component>
</div>
const vm = {
data() {
return {
isActive: true,
}
}
}
const app = Vue.createApp(vm)
app.component('my-component', {
template: `
<p :class="$attrs.class">Hi!</p>
<span>This is a child component</span>
`
})
app.mount('#app')
You can Not prop Attribute Section to learn more about component property inheritance .
Binding inline styles
Object syntax
:style
Object syntax of —— It looks very similar CSS, But it's actually a JavaScript object .CSS property The name can be humped (camelCase) Or a short horizontal line (kebab-case, Remember to put it in quotation marks ) Named after the :
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }">lvhanghmm</div>
data() {
return {
activeColor: 'red',
fontSize: 30
}
}
It's usually better to bind directly to a style object , This will make the template clearer :
<div :style="styleObject">lvhanghmm</div>
data() {
return {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
}
alike , Object syntax is often used in conjunction with the computed properties of the returned object
Array syntax
<div :style="[baseStyles, overridingStyles]">lvhanghmm</div>
data() {
return {
baseStyles: {
color: 'red',
fontSize: '30px'
},
overridingStyles: {
height: '300px',
width: '300px',
backgroundColor: 'pink'
}
}
}
Automatically prefix
stay :style
Need to use in ( Browser engine prefix ) vendor prefixes Of CSS property when , Such as transform
,Vue Will automatically detect and add the corresponding prefix .
Multiple values
It can be for style In binding property Provides an array of multiple values , Often used to provide multiple prefixed values , for example :
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
This will only render the last browser supported value in the array . In this case , If the browser supports flexbox, Then it will only render display: flex
.
Conditions apply colours to a drawing
v-if
v-if
Instructions are used to conditionally render a piece of content . This will only return... In the expression of the instruction truthy It's rendered when it's worth .
<h1 v-if="awesome">Vue is awesome!</h1>
It can also be used. v-else
Add one “else block ”:
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no </h1>
stay template
Use on element v-if
Conditional rendering groups
because v-if
It's an instruction , So you have to add it to an element . But if you want to switch multiple elements ? At this time, you can put a <template>
Element as invisible wrap element , And use it on it v-if
. The final rendering result will not contain <template>
Elements .
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
The code above will not be displayed , because ok Variables are not defined , Its value is false
v-else
You can use v-else
Order to express v-if
Of “else block ”:
<div v-if="Math.random() > 0.5">
Now you see me
</div>
<div v-else>
Now you don't
</div>
v-else
The elements must be followed by v-if
perhaps v-else-if
After the element of , Otherwise it will not be recognized .
v-else-if
v-else-if
, seeing the name of a thing one thinks of its function , act as v-if
Of “else-if block ”, And it can be used continuously :
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
And v-else
The usage of is similar to ,v-else-if
We must also keep up with it v-if
perhaps v-else-if
After the elements of .
v-show
Another option for conditionally displaying elements is v-show
Instructions . The usage is about the same :
<h1 v-show="ok">Hello!</h1>
The difference is that with v-show
Elements of are always rendered and remain in the DOM in .v-show
Simply switching elements CSS property display
.
Be careful ,v-show
I won't support it <template>
Elements , Nor does it support v-else
.
v-if
VSv-show
v-if
yes “ real ” Conditional rendering , Because it will ensure that in the switching process , The event listeners and subcomponents in the condition block are properly destroyed and rebuilt .
v-if
It's also The inertia of the : If the condition is false at the initial render , And do nothing —— Until the condition is true for the first time , Before the condition block is rendered .
by comparison ,v-show
It's much simpler —— Whatever the initial conditions are , Elements are always rendered , And simply based on CSS Switch .
Generally speaking ,v-if
There is higher switching overhead , and v-show
There is a higher initial rendering overhead . therefore , If you need to switch very frequently , Then use v-show
good ; If conditions rarely change at run time , Then use v-if
good
v-if
And v-for
Use it together
Tips
Not recommended Use at the same time
v-if
andv-for
. Please refer to Style guide For more information .
When v-if
And v-for
When used together ,v-if
Ratio v-for
Higher priority . Please refer to List rendering Guide For details .
The list of rendering
use v-for
An array is mapped to a set of elements
We can use v-for
The command renders a list based on an array .v-for
Instruction needs to be used item in items
Special syntax of form , among items It's an array of source data , and item
Is the iterated array element Alias .
<div id="app">
<ul>
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
</div>
const vm = {
data() {
return {
items: [{ message: 'Foo' }, { message: 'Bar' }]
}
}
}
const app = Vue.createApp(vm)
app.mount('#app')
result :
stay v-for
In block , We can access all of the parent scope's property.v-for
An optional second parameter is also supported , That is, the index of the current item .
<div id="app">
<ul>
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
</div>
data() {
return {
parentMessage: 'Parent',
items: [{ message: 'Foo' }, { message: 'Bar' }]
}
}
You can also use it of
replace in
As a separator , Because it's closer to JavaScript Iterator syntax :
<li v-for="(item, index) of items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
stay v-for
The object used in
You can also use it v-for
To traverse an object's property.
<div id="app">
<ul>
<li v-for="(item, index) of myObject">
{{ index }} + {{ item }}
</li>
</ul>
</div>
const vm = {
data() {
return {
myObject: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
}
}
const app = Vue.createApp(vm)
app.mount('#app')
result :
You can also provide the second parameter as property name ( That's the key name key):
It's shown above , You can see that I use the chestnut cycle in front directly !
Now let me change it !
You can also use the third parameter as an index :
<li v-for="(value, name, index) in myObject">
{{ index }}. {{ name }}: {{ value }}
</li>
Tips
When traversing objects , Will press
Object.keys()
Result traversal of , But there's no guarantee it's different JavaScript The results are consistent under the engine .
Maintenance state
When Vue Updating usage v-for
When rendering a list of elements , It uses by default “ in-place update ” The strategy of . If the order of data items is changed ,Vue Will not move DOM Element to match the order of data items , Instead, each element is updated in place , And make sure they render correctly at each index location .
This default mode is efficient , however Only applicable to non dependent sub component status or temporary DOM state ( for example : Form input value ) List render output for .
To give Vue A hint , So that it can track the identity of each node , To reuse and reorder existing elements , You need to provide a unique key
attribute:
<div v-for="item in items" :key="item.id">
<!-- content -->
</div>
It is recommended to use as much as possible v-for
Provide time key
attribute, Unless traversing the output DOM It's very simple , Or deliberately rely on default behavior for performance improvement .
Because it is Vue A general mechanism for identifying nodes ,key
It's not just with v-for
Special relevance . We'll see later in the guide , It also has other uses .
Tips
Don't use non primitive type values such as objects or arrays as
v-for
Of key. Please use a value of string or numeric type .
Array update detection
Change method
Vue Wrapped the change method of the array being listened on , So they will also trigger view updates . These wrapped methods include :
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
You can turn on the console , And then for the previous example items
Array tries to call the change method . such as example1.items.push({ message: 'Baz' })
.
【 Don't understand, !】
Replace array
Change method , seeing the name of a thing one thinks of its function , Will change the original array that called these methods . by comparison , There are also non change methods , for example filter()
、concat()
and slice()
. They don't change the original array , and Always return a new array . When using the non change method , You can replace the old array with a new array :
example1.items = example1.items.filter(item => item.message.match(/Foo/))
You may think that this will lead to Vue Discard the existing DOM And re render the entire list . Fortunately, , This is not the case .Vue In order to make DOM Elements are reused to the maximum extent, and some intelligent heuristics are implemented , So it is very efficient to replace the original array with an array containing the same elements
Display filtering / The result after sorting
Sometimes , We want to display a filtered or sorted version of an array , Without actually changing or resetting the original data . under these circumstances , You can create a calculated property , To return the filtered or sorted array .
for example :
<li v-for="n in evenNumbers" :key="n">{{ n }}</li>
data() {
return {
numbers: [ 1, 2, 3, 4, 5 ]
}
},
computed: {
evenNumbers() {
// Returns an even array !
return this.numbers.filter(number => number % 2 === 0)
}
}
In cases where the calculated property is not applicable ( for example , In nesting v-for
In circulation ) You can use a method :
<div id="app">
<ul v-for="numbers in sets">
<li v-for="n in even(numbers)" :key="n">{{ n }}</li>
</ul>
</div>
const vm = {
data() {
return {
sets: [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]
}
},
methods: {
even(numbers) {
return numbers.filter(number => number % 2 === 0)
}
},
}
const app = Vue.createApp(vm)
app.mount('#app')
stay v-for
Range of values used in
v-for
You can also accept integers . under these circumstances , It will repeat the template for the corresponding number of times .
<div id="app">
<span v-for="n in 10" :key="n">{{ n }} </span>
</div>
stay template
Use in v-for
Be similar to v-if
, You can also take advantage of the v-for
Of <template>
To loop through a piece of content that contains multiple elements . such as :
<ul>
<template v-for="item in items" :key="item.msg">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
v-for
And v-if
Use together
TIP
Pay attention to us No It is recommended to use... On the same element
v-if
andv-for
. More details can be found in Style guide .
When they are at the same node ,v-if
The priority ratio v-for
Higher , It means v-if
You will not have access v-for
Variables in :
<!-- This will throw an error because property "todo" is not defined on instance. -->
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo.name }}
</li>
You can put v-for
Move to <template>
Tag to correct :
<template v-for="todo in todos" :key="todo.name">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
Use... On components v-for
This part assumes that you already know Components Related knowledge . You can also skip it first , Come back later to check .
On custom components , You can use it just like on any normal element v-for
:
<my-component v-for="item in items" :key="item.id"></my-component>
However , No data is automatically passed to the component , Because components have their own independent scopes . To pass iteration data to components , We're going to use props:
<my-component
v-for="(item, index) in items"
:item="item"
:index="index"
:key="item.id"
></my-component>
Not automatically item
The reason for injection into the component is , This makes the component and v-for
The operation of is tightly coupled . Identifying the source of component data enables components to be reused in other situations .
Here's a simple one todo A complete example of the list :
<div id="app">
<form v-on:submit.prevent="addNewTodo">
<label for="new-todo">Add a todo</label>
<input
v-model="newTodoText"
id="new-todo"
placeholder="E.g. Feed the cat"
/>
<button>Add</button>
</form>
<ul>
<todo-item
v-for="(todo, index) in todos"
:key="todo.id"
:title="todo.title"
@remove="todos.splice(index, 1)"
>
</todo-item>
</ul>
</div>
const vm = {
data() {
return {
newTodoText: '',
todos: [
{
id: 1,
title: 'Do the dishes'
},
{
id: 2,
title: 'Take out the trash'
},
{
id: 3,
title: 'Mow the lawn'
}
],
nextTodoId: 4
}
},
methods: {
addNewTodo() {
this.todos.push({
id: this.nextTodoId++,
title: this.newTodoText
})
this.newTodoText = ''
}
},
}
const app = Vue.createApp(vm)
app.component('todo-item', {
props: ['title'],
emit: ['remove'],
template: `
<li>
{{ title }}
<button @click="$emit('remove')">Remove</button>
</li>
`
})
app.mount('#app')
Event handling
Monitoring events
We can use v-on
Instructions ( Commonly abbreviated as @
Symbol ) To listen to DOM event , And execute some actions when the event is triggered JavaScript. Usage for v-on:click="methodName"
Or use a shortcut @click="methodName"
for example :
<div id="app">
<button @click="counter += 1">Add 1</button>
<p>The button above has been clicked {{ counter }} times.</p>
</div>
const vm = {
data() {
return {
counter: 0
}
},
methods: {
},
}
const app = Vue.createApp(vm)
app.mount('#app')
Event handling method
However, many of the event processing logic will be more complex , So directly JavaScript The code is written in v-on
It is not feasible in the instruction . therefore v-on
You can also receive a method name that needs to be called .
for example :
<div id="app">
<!-- `greet` Is the method name defined below -->
<button @click="greet">Greet</button>
</div>
const vm = {
data() {
return {
name: 'Vue.js'
}
},
methods: {
greet (event) {
// `methods` Inside `this` Points to the current active instance
alert('Hello ' + this.name + '!')
// `event` It's original DOM event
if (event) {
alert(event.target.tagName)
}
}
},
}
const app = Vue.createApp(vm)
app.mount('#app')
Methods in introverted processor
In addition to directly binding to a method , You can also inline JavaScript Method is called in the statement :
<div id="app">
<button @click="say('hi')">Say hi</button>
<button @click="say('what')">Say what</button>
</div>
Vue.createApp({
methods: {
say(message) {
alert(message)
}
}
}).mount('#app')
Sometimes you need to access the original... In the inline statement processor DOM event . You can use special variables $event
Put it into the method :
<button @click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>
// ...
methods: {
warn(message, event) {
// You can now access the native event
if (event) {
event.preventDefault()
}
alert(message)
}
}
Multi event processor
There can be multiple methods in an event handler , These methods are separated by the comma operator :
<!-- these two items. one() and two() Click the event to execute the button -->
<button @click="one($event), two($event)">
Submit
</button>
// ...
methods: {
one(event) {
// The first event handler logic ...
},
two(event) {
// The second event handler logic ...
}
}
Event modifier
Call in event handler event.preventDefault()
or event.stopPropagation()
It's a very common requirement . Although we can easily implement this in the method , But the better way is : Method has only pure data logic , Instead of dealing with DOM Details of the incident .
To solve this problem ,Vue.js by v-on
Provides Event modifier . I mentioned before , The modifier is represented by the instruction suffix at the beginning of the dot .
.stop
.prevent
.capture
.self
.once
- .passive`
<!-- Prevent click events from continuing to propagate -->
<a @click.stop="doThis"></a>
<!-- Submit event no longer reloads page -->
<form @submit.prevent="onSubmit"></form>
<!-- Modifiers can be concatenated -->
<a @click.stop.prevent="doThat"></a>
<!-- Only modifiers -->
<form @submit.prevent></form>
<!-- Use event capture mode when adding event listeners -->
<!-- That is, events triggered by internal elements are handled here first , Then it's left to the internal elements to deal with -->
<div @click.capture="doThis">...</div>
<!-- Just be there event.target Is the trigger handler for the current element itself -->
<!-- That is, events are not triggered from internal elements -->
<div @click.self="doThat">...</div>
TIP
When using modifiers , Order matters ; The corresponding code will be generated in the same order . therefore , use
v-on:click.prevent.self
Will block all clicks , andv-on:click.self.prevent
It will only prevent clicking on the element itself .
<!-- The click event will only trigger once -->
<button type="button"@click.once="doThis">asd</button>
Unlike others that can only deal with native DOM The modifier that events work ,.once
Modifiers can also be used in custom Component events On . If you haven't read the documentation about components , Don't worry about it now .
Vue It also corresponds to addEventListener
Medium passive Options Provides .passive
Modifier .
<!-- Default behavior for scrolling Events ( Rolling behavior ) Will trigger immediately -->
<!-- Instead of waiting `onScroll` complete -->
<!-- This includes `event.preventDefault()` The situation of -->
<div @scroll.passive="onScroll">...</div>
This .passive
In particular, modifiers can improve the performance of mobile terminals .
TIP
Don't put the
.passive
and.prevent
Use it together , because.prevent
Will be ignored , At the same time, the browser may show you a warning . please remember ,.passive
Will tell the browser you Don't want to Default behavior for block events .
Key modifier
When listening for keyboard events , We often need to check the detailed buttons .Vue Allow for v-on
perhaps @
Add key modifiers when listening for keyboard events :
<!-- Only in `key` yes `Enter` Called when the `vm.submit()` -->
<input @keyup.enter="submit" />
You can directly KeyboardEvent.key
Any valid key name exposed is converted to kebab-case As a modifier .
<input @keyup.page-down="onPageDown" />
In the example above , The handler will only $event.key
be equal to 'PageDown'
When called .
Key alias
Vue Aliases are provided for the most commonly used keys :
.enter
.tab
.delete
( Capture “ Delete ” and “ Backspace ” key ).esc
.space
.up
.down
.left
.right
System modifier keys
The following modifiers can be used to implement a listener that triggers mouse or keyboard events only when the corresponding key is pressed .
.ctrl
.alt
.shift
.meta
Tips
Be careful : stay Mac On the system keyboard ,meta Corresponding command key (⌘). stay Windows System keyboard meta Corresponding Windows Winkey (⊞). stay Sun On the operating system keyboard ,meta Corresponding to the solid gem key (◆). On other specific keyboards , Especially in MIT and Lisp The keyboard of the machine 、 And its successors , such as Knight keyboard 、space-cadet keyboard ,meta Marked as “META”. stay Symbolics On the keyboard ,meta Marked as “META” perhaps “Meta”.
.exact
Modifier
.exact
Modifiers allow you to control events triggered by a precise combination of system modifiers .
<!-- Even if Alt or Shift It also triggers when pressed together -->
<button @click.ctrl="onClick">A</button>
<!-- Yes and only Ctrl Triggered when pressed -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<!-- No system modifier is triggered until it is pressed -->
<button @click.exact="onClick">A</button>
Mouse button modifier
.left
.right
.middle
These modifiers limit the handler to respond only to specific mouse buttons .
Why is it HTML Listening events in
You may notice that this way of event monitoring goes against separation of concerns (separation of concern) This long tradition of excellence . But don't worry , Because of all the Vue.js Event handling methods and expressions are strictly bound to the ViewModel On , It will not cause any maintenance difficulties . actually , Use v-on
or @
There are several benefits :
- Glance at HTML Templates can be easily located in JavaScript The corresponding method in the code .
- Because you don't have to JavaScript Manually bind events in , Yours ViewModel Code can be very pure logic , and DOM Completely decoupled , Easier to test .
- When one ViewModel Be destroyed when the , All event handlers are automatically deleted . You don't have to worry about how to clean them up .
Form input binding
Basic usage
You can use it. v-model Instruction in form <input>
、<textarea>
And <select>
Create two-way data binding on element . It automatically selects the correct method to update the element based on the control type . Even though it's magical , but v-model
It's just grammar sugar in essence . It is responsible for monitoring user input events to update data , And some special treatment in some extreme situation .
Tips
v-model
All form elements will be ignoredvalue
、checked
、selected
attribute Always use the data of the current active instance as the data source . You should go through JavaScript In the component'sdata
Initial value declared in option .
v-model
Use different... Internally for different input elements property And throw out different events :
- text and textarea Element usage
value
property andinput
event ; - checkbox and radio Use
checked
property andchange
event ; - select Fields will
value
As prop And willchange
As an event .
Tips
For the need to use typewriting ( Such as Chinese 、 Japanese 、 Korean, etc ) Language , You'll find that
v-model
It will not be updated in the process of organizing text with input method . If you also want to respond to these updates , Please useinput
Event listeners andvalue
binding , Instead of usingv-model
.
Text (Text)
<input v-model="message" placeholder="edit me" />
<p>Message is: {{ message }}</p>
Multiline text (Textarea)
<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br />
<textarea v-model="message" placeholder="add multiple lines"></textarea>
Interpolation doesn't work in text areas , You should use v-model
Instead of .
<!-- bad -->
<textarea>{{ text }}</textarea>
<!-- good -->
<textarea v-model="text"></textarea>
Check box (CheckBox)
Single check box , Bind to Boolean :
<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>
Multiple check boxes , Bind to the same array :
<div id="app">
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames" />
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames" />
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames" />
<label for="mike">Mike</label>
<br />
<span>Checked names: {{ checkedNames }}</span>
</div>
const vm = {
data() {
return {
checkedNames: []
}
},
methods: {
}
}
const app = Vue.createApp(vm)
app.mount('#app')
Radio buttons (Radio)
<div id="app">
<input type="radio" id="one" value="One" v-model="picked" />
<label for="one">One</label>
<br />
<input type="radio" id="two" value="Two" v-model="picked" />
<label for="two">Two</label>
<br />
<span>Picked: {{ picked }}</span>
</div>
const vm = {
data() {
return {
picked: ''
}
},
methods: {
}
}
const app = Vue.createApp(vm)
app.mount('#app')
Selection box (Select)
On single selection :
<div id="app">
<select v-model="selected">
<option disabled value="">Please select one</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
</div>
const vm = {
data() {
return {
selected: ''
}
},
methods: {
}
}
const app = Vue.createApp(vm)
app.mount('#app')
Be careful
If
v-model
The initial value of the expression failed to match any options ,<select>
The element will be rendered as “ Not selected ” state . stay iOS in , This prevents the user from choosing the first option . Because in this case ,iOS Not triggerchange
event . therefore , It is more recommended to provide a disable option with a null value like above .
When there are multiple choices ( Bind to an array ):
<select v-model="selected" multiple>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<br />
<span>Selected: {{ selected }}</span>
use v-for
Dynamic options for rendering :
<div id="app">
<select v-model="selected">
<option v-for="option in options" :value="option.value">
{{ option.text }}
</option>
</select>
<span>Selected: {{ selected }}</span>
</div>
const vm = {
data() {
return {
selected: 'A',
options: [
{ text: 'One', value: 'A' },
{ text: 'Two', value: 'B' },
{ text: 'Three', value: 'C' }
]
}
},
methods: {
}
}
const app = Vue.createApp(vm)
app.mount('#app')
Value binding
For radio buttons , Check box and check box options ,v-model
The value of the binding is usually a static string ( The check box can also be Boolean ):
<!-- When elected ,`picked` For the string "a" -->
<input type="radio" v-model="picked" value="a" />
<!-- `toggle` by true or false -->
<input type="checkbox" v-model="toggle" />
<!-- When the first option is selected ,`selected` For the string "abc" -->
<select v-model="selected">
<option value="abc">ABC</option>
</select>
But sometimes we may want to bind the value to a dynamic instance of the current activity property On , You can use v-bind
Realization , Besides , Use v-bind
You can bind an input value to a non string .
Check box (Checkbox)
<input type="checkbox" v-model="toggle" true-value="yes" false-value="no" />
// when checked:
vm.toggle === 'yes'
// when unchecked:
vm.toggle === 'no'
Tips
there
true-value
andfalse-value
attribute Does not affect the input controlvalue
attribute, Because browsers don't include unchecked checkboxes when submitting forms . If you want to make sure that one of these two values in the form can be submitted ,( namely “yes” or “no”), Please change the radio button .
Radio buttons (Radio)
<input type="radio" v-model="pick" v-bind:value="a" />
// When elected
vm.pick === vm.a
Selection box options (Select Options)
<select v-model="selected">
<!-- Inline object literals -->
<option :value="{ number: 123 }">123</option>
</select>
// When selected
typeof vm.selected // => 'object'
vm.selected.number // => 123
Modifier
.lazy
了 Above When input method organizes text ). You can add lazy
Modifier , So it turns into change
event _ after _ To synchronize :
<!-- stay “change” Sometimes not “input” When the update -->
<input v-model.lazy="msg" />
.number
If you want to automatically change the user's input value to a numeric type , You can give v-model
add to number
Modifier :
<input v-model.number="age" type="number" />
This is usually useful , Because even in type="number"
when ,HTML The value of an input element always returns a string . If this value cannot be parseFloat()
analysis , The original value will be returned .
.trim
If you want to automatically filter the first and last blank characters entered by users , You can give v-model
add to trim
Modifier :
<input v-model.trim="msg" />
Use... On components v-model
If you're not familiar with it Vue The components of , You can skip here for a while .
HTML Native input element types don't always meet the requirements . fortunately ,Vue Our component system allows you to create reusable input components with fully customized behavior . These input components can even interact with v-model
Use it together !
To learn more about , See... In the components guide Custom input Components .
Foundation of organization
Basic examples
Here's one Vue Examples of components :
const vm = {
data() {
return {}
},
methods: {}
}
const app = Vue.createApp(vm)
app.component('button-counter', {
data() {
return {
count: 0
}
},
template: `
<button @click="count++">
You clicked me {{ count }} times.
</button>
`
})
app.mount('#app')
INFO
Here's a simple example , But in a typical Vue Application , We use a single file component instead of a string template . You can In this section Find out more about them .
A component is a reusable instance with a name , In this case, yes <button-counter>
. We can use this component as a custom element in the root instance :
<div id="app">
<button-counter></button-counter>
</div>
Because components are reusable component instances , So they receive the same options as the root instance , for example data
、computed
、watch
、methods
Lifecycle hooks and so on .
Reuse of components
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
Notice when you click the button , Each component will maintain its own count
. Because every time you use a component , There will be a new one example Be created .
Organization of components
Usually an application is organized as a nested component tree :
for example , You may have a header 、 Sidebar 、 Content area and other components , Each component also contains other links like navigation 、 Components like blogs .
To be used in templates , These components must first be registered in order to Vue Able to identify . There are two types of component registration : Global registration and Partial registration . thus , All of our components are just through the component
Method globally registered :
const app = Vue.createApp({})
app.component('my-component-name', {
// ... Options ...
})
Globally registered components can be used in the template of any component in the application .
up to now , That's all you need to know about component registration , If you have read this page and mastered it , We'll recommend you to come back Component registration After reading .
adopt Prop Passing data to subcomponents
earlier , We talked about creating a blog component . The problem is that if you can't pass the title or content of a blog post to this component and the like data we want to show , There is no way to use it . This is what prop The origin of .
Prop It's some customizations that you can register on the component attribute. To pass a title to the blog component , We can use props
Option to include it in the component's acceptable prop In the list :
const app = Vue.createApp(vm)
app.component('blog-post', {
props: ['title'],
template: `<h4>{{ title }}</h4>`
})
app.mount('#app')
When a value is passed to a prop attribute when , It becomes one of the component instances property. The property Can be accessed in the template , Just like any other component property equally .
By default, a component can have any number of prop, Any value can be passed to the prop.
<div id="app">
<blog-post title="My journey with Vue"></blog-post>
<blog-post title="Blogging with Vue"></blog-post>
<blog-post title="Why Vue is so fun"></blog-post>
</div>
However, in a typical application , You may be in data
There is an array of blog posts :
const vm = {
data() {
return {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
]
}
},
methods: {}
}
const app = Vue.createApp(vm)
app.component('blog-post', {
props: ['title'],
template: `<h4>{{ title }}</h4>`
})
app.mount('#app')
And want to render a component for each blog post :
<div id="app">
<blog-post
v-for="post in posts"
:key="post.id"
:title="post.title"
>
</blog-post>
</div>
As shown above , You will find that we can use v-bind
To deliver dynamically prop. You don't know exactly what to render at the beginning , It's very useful .
up to now , About prop That's about all you need to know , If you have read this page and mastered it , We'll recommend you to come back prop After reading .
Listen for subcomponent Events
We are developing <blog-post>
When the component , Some of its functions may need to communicate with the parent component . For example, we may introduce an auxiliary function to enlarge the font size of blog posts , And leave the rest of the page with the default font size .
In its parent component , We can do this by adding a postFontSize
data property To support this function :
It can be used to control the font size of all blog posts in the template :
data() {
return {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
],
postFontSize: 1
}
}
It can be used to control the font size of all blog posts in the template :
<div id="app">
<div :sytle="{ fontSize: postFontSize + 'em'}">
<blog-post
v-for="post in posts"
:key="post.id"
:title="post.title"
>
</blog-post>
</div>
</div>
Now let's add a button before the body of each blog post to enlarge the font size :
app.component('blog-post', {
props: ['title'],
template: `
<div class="blog-post">
<h4>{{ title }}</h4>
<button>
Enlarge text
</button>
</div>
`
})
The problem is that this button doesn't do anything :
<button>
Enlarge text
</button>
When you click this button , We need to tell the parent component to enlarge the text of all posts . Fortunately, the component instance provides a custom event system to solve this problem . The parent component can handle native components as well DOM Events pass through v-on
or @
Listen for any events of a subcomponent instance :
<blog-post ... @enlarge-text="postFontSize += 0.1"></blog-post>
At the same time, the sub components can call the built-in $emit Method And pass in the event name to trigger an event :
<button @click="$emit('enlargeText')">
Enlarge text
</button>
Thanks a lot @enlarge-text="postFontSize += 0.1"
Monitor , The parent component can receive events and update them postFontSize
value .
I failed anyway !
My code !:
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="Vue3.js"></script>
</head>
<body>
<div id="app">
<div :sytle="{ fontSize: postFontSize + 'em' }">
<blog-post
v-for="post in posts"
:key="post.id"
:title="post.title"
v-on:enlarge-text="postFontSize += 0.1"
>
</blog-post>
</div>
</div>
<script>
const vm = {
data() {
return {
posts: [
{id: 1, title: 'My journey with Vue'},
{id: 2, title: 'Blogging with Vue'},
{id: 3, title: 'Why Vue is so fun'}
],
postFontSize: 1
}
},
methods: {}
}
const app = Vue.createApp(vm)
app.component('blog-post', {
props: ['title'],
template: `
<div class="blog-post">
<h4>{{ title }}</h4>
<button @click="$emit('enlargeText')">
Enlarge text
</button>
</div>
`
})
app.mount('#app')
</script>
</body>
</html>
Successful results on the document !
We can use the component's emits
Option lists the events that have been thrown :
app.component('blog-post', {
props: ['title'],
emits: ['enlargeText']
})
This will allow us to examine all events thrown by the component , You can also choose Verify them .
Use the event to throw a value ( It's just carrying parameters !)
Sometimes it's very useful to throw a specific value with an event . For example, we might want to <blog-post>
Component determines how much of its text to enlarge . You can use $emit
The second parameter to provide this value :
<button @click="$emit('enlargeText', 0.1)">
Enlarge text
</button>
Then when the parent component listens for this event , We can go through $event
Access to the value being thrown :
<blog-post ... @enlarge-text="postFontSize += $event"></blog-post>
perhaps , If this event handler is a method :
<blog-post ... @enlarge-text="onEnlargeText"></blog-post>
Then this value will be passed into this method as the first parameter :
methods: {
onEnlargeText(enlargeAmount) {
this.postFontSize += enlargeAmount
}
}
Use... On components v-model
Custom events can also be used to create support v-model
The custom input component of . remember :
<input v-model="searchText" />
Equivalent to :
<input :value="searchText" @input="searchText = $event.target.value" />
When used on components ,v-model
That's what happens :
<custom-input
:model-value="searchText"
@update:model-value="searchText = $event"
></custom-input>
WARNING
Please note that , What we use here is
model-value
, Because we're using DOM In the template kebab-case. You can analysis DOM Precautions for formwork Section to find out about kebab cased and camelCased Property
To make it work , In this component <input>
must :
- Put it
value
attribute Bind to a namemodelValue
Of prop On - In its
input
When the event is triggered , Pass the new value through the customupdate:modelValue
Event throw
It's like this after writing the code
app.component('custom-input', {
props: ['modelValue'],
emits: ['update:modelValue'],
template: `
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
`
})
Now? v-model
It should work perfectly on this component :
<custom-input v-model="searchText"></custom-input>
In this component v-model
Another way to do this is to use computed
property To define the function of getter and setter.get
Method should return modelValue
property,set
Method should trigger the corresponding event .
app.component('custom-input', {
props: ['modelValue'],
emits: ['update:modelValue'],
template: `
<input v-model="value">
`,
computed: {
value: {
get() {
return this.modelValue
},
set(value) {
this.$emit('update:modelValue', value)
}
}
}
})
Now you just need to know about custom component events , But once you've read this page and feel good about it , We suggest you read about it later Custom events