Vue document
1. Awareness and preparation
Vue is a progressive JavaScript framework
Specific: Easy to use, flexible, efficient (super fast virtual DOM)
Progressive understanding:
- If you already have an existing server application, you can embed Vue as part of that application for a richer interactive experience
- If you want to put more interaction on the front end, Vue’s core library and ecosystem can also meet your various needs (Core +vuex+ VUE-Router). Like other front-end frameworks, Vue allows you to split a web page into reusable components, each containing its own HTML, CSS, and javascript for rendering specific parts of the page
- If we are building a large application, at this point we may need to split things up into separate components and files, and VUE can scaffolding the development project
- Progressive is the layer of structural tools that starts from the central view layer and spreads out. This process goes through five levels: View layer rendering -> component mechanism -> routing mechanism -> State management -> Build tools
Compatibility: The Vue does not support Internet Explorer 8 or later because It uses ECMAScript5 that Internet Explorer 8 cannot simulate. However, it supports all ECMAScrip5t compatible browsers
Vue Devtools: The browser installs Vue Devtools to debug and review applications
Using the Vue
<body>
<div id="app"></div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue();
</script>
</body>
Copy the code
The source code
function Vue(options) {
/** * If in a development environment, and this is not an instance of the Vue object constructor, the object constructor calls warn and passes in a string as an argument, telling the developer to call Vue as a constructor using the new keyword. ** In a development environment, when does this not point to Vue */
// If the current production environment is &&
if( process.env.NODE_ENV ! = ="production" &&
// Check if this is not an instance of the Vue object constructor, which asks if the user is new Vue! (this instanceof Vue)
) {
warn("Vue is a constructor and should be called with the `new` keyword");
}
// The Vue constructor calls this._init to pass in options
this._init(options);
}
Copy the code
2. The MVC and MVVM
MVC(Model-View-Controller)
M and V here mean the same as M and V in MVVM. MVC is used to separate M and V from C page business logic. MVC is one-way communication, that is, View to Model. The link must be through the Controller.
MVVM(Model-View-ViewModel)
MVVM is an improved VERSION of MVC. MVVM abstracts the state and behavior of the View, allowing us to separate the View from the business logic.
Model: Data passed by the back end
View: The page you see
Viewmodel: The MVVM core, which is the bridge between the View and model, can be divided into two directions:
- Turn the Model into a View, and turn the data on the back end into the page you see. Implementation mode Data binding
- The view is converted to model, and the page is converted to back-end data. DOM event listening is implemented
Both of these methods implement what we call two-way data binding
Attempts to communicate directly with the model in the MVVM framework are not possible. They communicate through the ViewModel, which typically implements an observer. When data changes, the ViewModel can listen for that change and then make updates through the corresponding view. The ViewModel can also listen for changes in the view as the user manipulates it, and then make changes through the data, effectively binding the data in both directions
MVC and MVVM are not viewModels replacing controllers, The ViewModel exists to extract the business logic displayed in the Controller, not the Controller, and other view operations are implemented in the Controller. In other words, MVVM implements the reuse of business logic components
In the Vue
- Model: JS data, such as objects, arrays, etc.
- View: 页面视图 view
- ViewModel: Vue instantiates the object
3. The basic
3.1 Vue life cycle
Vue life cycle diagram
BeforeCreate: is the first hook triggered after new Vue(), and data on data, Methods, computed, and watch cannot be accessed in the current phase.
created: The updated function does not trigger the updated function. You can get some initial data. In the current stage, you cannot interact with the DOM. DOM can be accessed via vm.$nextTick
BeforeMount: Occurs before mounting, before the tempate template is compiled by importing the render function. At this stage, the virtual DOM has been created and is about to start rendering. At this point, the data is also changed and the updated is not triggered
Mounted Occurs after the DOM is mounted. In this stage, the DOM is mounted and data is bidirectional bound. You can access the DOM node and use the $refs attribute to operate the DOM
BeforeUpdate: Triggered before updates, that is, before reactive data is updated and the virtual DOM is re-rendered, you can make data changes at this stage without re-rendering
Updated occurs after the update is complete, and the component DOM in the current phase completes the update. Be careful not to change the data during this period as it may cause an infinite loop of updates
BeforeDestory occurs before the instance is destroyed, and the instance is fully available in the current phase, at which point we can do after-sale finishing work, such as clearing timers
Destoryed occurs after instance destruction, leaving only the DOM shell. Components have been disassembled, data bindings removed, listeners removed, and self-instances destroyed
Life cycle list
Activated, Deactivated and errorCaptured have been added
Activated: called when a component cached by keep-alive is activated. This hook is not called during server-side rendering
Deactivated: called when a component cached by keep-alive is disabled
errorCaptured
:
- Type: (err: Error, VM: Component, info: string) =>? boolean
- Details: Called when an error from a descendant component is caught. The hook takes three arguments: the error object, the component instance where the error occurred, and a string containing information about the source of the error. This hook returns false to prevent further propagation of the error
Note: You can change the state of a component in this hook. So when catching errors, it’s important to have a conditional judgment in the template or rendering function to bypass everything else; Otherwise the component might enter an infinite render loop
Error propagation rule
- By default, if global
config.errorHandler
It is defined that all error throws are sent to it, so these errors are still reported to a single analysis service place - If a component has more than one descendant or parent slave link
errorCaptured
Hook, they will be invoked one by one by the same error - If this
errorCaptured
The hook itself throws an error, and both the new error and the original caught error are sent globallyconfig.errorHandler
- a
errorCaptured
The hook can returnfalse
To stop the error from propagating upward. Essentially saying “This error has been fixed and should be ignored”. It will prevent anything else from being aroused by the errorerrorCaptured
Hooks and globalconfig.errorHandler
.
Parent component life cycle
- Create a stage
Parent beforeCreate -> Parent created -> parent beforeMount -> child beforeCreate -> child created -> child beforeMount -> Child Mounted -> parent Mounted
- Update the stage
Parent beforeUpdate -> Child beforeUpdate -> Child updated -> Parent updated
- Destruction of phase
Parent beforeDestory -> Child beforeDestory -> child deStoryed -> parent destoryed ‘ ‘
3.2 Declarative rendering
At the heart of Vue is a system that allows you to declaratively render data into the DOM using concise template syntax
By describing the mapping between the state and the DOM, you can render the state into the DOM for rendering in the user interface, that is, on a web page
At the bottom of the implementation, Vue compiles templates into virtual DOM rendering functions. Combined with a responsive system, Vue can simply calculate how many components need to be centrally rendered and minimize DOM operations
If you don’t like the virtual DOM and prefer native JS, you can write the render function without templates, using the optional JSX syntax
If expressions are used in template syntax: {{message.split(” “).reverse().join(” “)}} these expressions are parsed as JavaScript in the data scope of the owning Vue instance
Here are the 14 instructions v-text, V-HTML, V-pre, V-once and V-cloak
<body>
<div id="app">
<! {{message}} becomes Hello Vue! -->
<div>{{message}}</div>
<! -- v-cloak: This instruction stays on the element until the associated instance finishes compiling, and CSS rules' [v-cloak]{display: Used with None} ', this directive can hide the uncompiled Mustache tag until the instance is ready (it won't be shown until the compilation is complete) and then this way the problem will flash -->
<style>
[v-cloak] {
display: none;
}
</style>
<div v-cloak>{{message}}</div>
<! The value of v-text: will replace all, part of the data in the div, only difference expressions, no flashing -->
<div v-text="message"></div>
<! -- V-once: render elements and components only once. After re-rendering, elements, components and all their children will be treated as static content. Skipping this will optimize performance.
<div v-once>{{message}}</div>
<! -- V-html: updates the innerHTML of the element. Note: The content is inserted as normal HTML -- it is not compiled as a Vue template, so the HTML template in data is inserted directly below, and the {{message}} inside is not compiled -->
<div v-html="html"></div>
<! --v-pre: Skip this element and subelement compilation process, which can be used to display raw data and labels, skip a large number of nodes without instructions to speed up compilation -->
<div v-pre>{{message}}</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
// Get the view container based on querySelector: the specified container cannot be an HTML or body tag
el: "#app".data() {
return {
message: "Hello Vue!".html: `
{{message}}
`}; }});</script>
</body>
Copy the code
Note that the message above is reactive. If you modify the message in the data below, the contents in the div above will be modified accordingly
Browser render as
<div id="app">
<div>Hello Vue!</div>
<div>Hello Vue!</div>
<div>Hello Vue!</div>
<div>
<div>{{message}}</div>
</div>
<div>Hello Vue!</div>
<div>{{message}}</div>
</div>
Copy the code
3.3 the Vue. Set
Add a property to the reactive object and make sure the new property is also reactive and triggers view updates. It must be used for adding a new property to reactive object, because the Vue cannot detect common new property (such as this. MyObject. NewProperty = ‘hi’)
Vue. Set (target, propertyName/index, value)
3.4 the Vue. Delete
Deletes an attribute of an object. If the object is reactive, make sure the deletion triggers an update to the view
Vue. Delete (target, propertyName/index)
3.5 Binding property, Class, and Style
3.5.1 Binding Properties
Directive V-bind: dynamic binding attribute: V-bind :attribute=”value”, short :attribute=”value”
<body>
<div id="app">
<div v-bind:title="message">123</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app".data() {
return {
message: "Hello Vue!"}; }});</script>
</body>
Copy the code
Browser render as
<body>
<div id="app">
<div title="Hello Vue!">123</div>
</div>
</body>
Copy the code
3.5.2 binding class
Object syntax and array syntax, as well as a component, see below
<body>
<style>
.active {
color: red;
}
</style>
<div id="app">
<! -- Notice that if the attribute is 'text-danger' the connection type needs to be quoted -->
<div v-bind:class="{active: isActive, 'text-danger': hasError}">The binding type is on the inline style</div>
<! -- If you already have a class attribute, append it -->
<div
class="static"
v-bind:class="{active: isActive, 'text-danger': hasError}"
>
456
</div>
<! -- 2. Bind class name on object -->
<div class="static" :class="classObj">The binding type is on the object</div>
<! -- 3. Binding returns computed properties, which is a very powerful mode -->
<div class="static" :class="classObject">Class names are computed on attributes</div>
<! -- 4. Bind class name to array -->
<div :class="[activeClass, errorClass]">The class name is bound to the array</div>
<! -- Change class name according to condition -->
<div :class="[isActive ? activeClass:'', errorClass]">Toggle array class names based on conditions</div>
<! -- Conditional write, if too much logic is responsible, so you can also use objects in arrays -->
<div :class="[{active: isActive}, errorClass]">Arrays use object syntax toggles</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app".data() {
return {
isActive: true.hasError: false.error: null.classObj: {
active: true."text-danger": false,},activeClass: "active".errorClass: "text-danger"}; },computed: {
classObject() {
return {
/ /! This. error = null returns true
active: this.isActive && !this.error,
"text-danger": this.error && this.error.type == "fatal"}; ,}}});</script>
</body>
Copy the code
Browser Rendering
<div id="app">
<div class="active">The binding type is on the inline style</div>
<div class="static active">456</div>
<div class="static active">The binding type is on the object</div>
<div class="static active">Class names are computed on attributes</div>
<div class="active text-danger">The class name is bound to the array</div>
<div class="active text-danger">Toggle array class names based on conditions</div>
<div class="active text-danger">Arrays use object syntax toggles</div>
</div>
Copy the code
Use on components
<body>
<div id="app">
<my-component class="baz boo" :class="{ active: isActive}"> </my-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component("my-component", {
template: '
}); 'new Vue({
el: "#app".data() {
return {
isActive: true}; }});</script>
</body>
Copy the code
Browser effects
<div id="app">
<div class="foo bar baz boo active">A class is bound to a component</div>
</div>
Copy the code
3.5.3 binding style
V-bind :style has the advantage that vue.js automatically detects and prefixes CSS properties that need to be prefixed by the browser engine
Since 2.3.0 it is possible to provide an array of values for property in the style binding, often used to provide multiple prefixed values
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
Copy the code
This will render only the last value in the array supported by the browser. If the browser does not support prefixes one by one, display: flex will be rendered
<body>
<style></style>
<div id="app">
<! If the property name is two concatenated names: the property name can be written in small camel form or raised by reference.
<div :style="{color: activeColor, fontSize: fontSize}">Style property binding</div>
<div :style="{color: activeColor, 'font-size': fontSize}">Style property binding</div>
<! It is better to bind directly to a style object, the code is clearer.
<div :style="styleObj">Writes attributes and attribute values to an object</div>
<! Returns the calculated properties of the object -->
<div :style="styleObject">Returns the property as a calculated property</div>
<! Class is a class name, and this is a class name.
<div :style="[styleObject]">Bind multiple class names in arrays</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app".data() {
return {
activeColor: "red".fontSize: "20px".styleObj: {
color: "red".fontSize: "20px"."border-bottom": "1px solid red",}}; },computed: {
styleObject() {
return {
color: "red".fontSize: "20px"}; ,}}});</script>
</body>
Copy the code
Browser Rendering
<div id="app">
<div style="color: red; font-size: 20px;">Style property binding</div>
<div style="color: red; font-size: 20px;">Style property binding</div>
<div style="color: red; font-size: 20px; border-bottom: 1px solid red;">Writes attributes and attribute values to an object</div>
<div style="color: red; font-size: 20px;">Returns the property as a calculated property</div>
<div style="color: red; font-size: 20px;">Bind multiple class names in arrays</div>
</div>
Copy the code
3.6 Conditions and loops
Six of the 14 instructions are already touched: V-text, V-HTML, V-pre, V-once, V-cloak, v-bind
Loop and conditional instructions: V-if, V-else, V-else -if, V-for, V-show
3.6.1 Conditional rendering
<body>
<div id="app">
<div v-if="isShow">According to</div>
<div v-else>hidden</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app".data() {
return {
isShow: true}; }});</script>
</body>
Copy the code
Commonly used for grouping with V-if conditional rendering on
elements
<body>
<div id="app">
<div v-if="type === 'A'">A</div>
<div v-else-if="type === 'B'">B</div>
<div v-else>A | B</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app".data() {
return {
type: "C"}; }});</script>
</body>
Copy the code
V-show is used to display elements’ options based on conditions. Unlike V-if, which always renders elements and remains in the DOM, V-show simply toggles the element’s CSS attribute display
Note: V-show does not support
elements, nor does v-else
v-if
withv-show
The difference between
V-if is “true” conditional rendering because it ensures that event listeners and subcomponents within the condition are properly destroyed and rebuilt during the switch
V-if is lazy too: if the condition is false at initial rendering, do nothing until the condition is true for the first time, and then start rendering the conditional block
In contrast,v-show is much simpler, no matter what the initial conditions are, the elements are always rendered and simply switched based on CSS
In general, V-if has a higher overhead, while V-show has a higher initial rendering overhead. So if you need to switch very frequently, use V-show. It is better to use V-IF if the operating conditions rarely change
3.6.2 List rendering
You can use the V-for directive to render a list based on an array or object.
Item is the alias of each element of the iterated array, and index is the index of the current item, which can be omitted
Value Specifies the value of the property being iterated. Name Indicates the name of the iterated attribute. Index Indicates the index of the current item
Note: When traversing an Object, the result of object.keys () is traversed, but its results are not guaranteed to be consistent across different JavaScript engines
When Vue is updating a list of elements rendered using V-for, it defaults to using the “update in place” policy. If the order of data items is changed,Vue does not move DOM elements to match the order of data items, but instead updates each element in place, ensuring that they are rendered correctly at each index location. This pattern is efficient, but only for list rendering outputs that do not depend on child component state or DOM state (for example, form input values). To give Vue a hint that it can aggregate the identity of each node and thus reuse and reorder existing elements, you need to provide a unique key attribute for each item
Note: Do not use non-primitive values such as objects or arrays as v-for keys
Provide the key attribute when using V-for whenever possible, unless traversing the output DOM content is very simple or you deliberately rely on default behavior for performance gains
Because it is a general mechanism for Vue to identify nodes, key is not specifically associated with V-for
<body>
<div id="app">
<div class="arr" v-for="(item, index) in arr" v-bind:key="index">
{{item.name}} -- {{index}}
</div>
<div class="obj" v-for="(value, name, index) of obj" v-bind:key="id">
{{name}} -- {{value}} -- {{index}}
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app".data() {
return {
arr: [{ name: "Zhang" }, { name: "Bill"}].obj: {
id: 1.title: "Science and technology".author: "lisi",}}; }});</script>
</body>
Copy the code
v-for
Can be found in<template>
To loop over a piece of content that contains multiple elements,v-if
Can also be used in<template>
, but thev-show
withv-else
Does not support<template>
- Not recommended at the same time
v-if
withv-for
When the two of them use it together,v-for
thanv-if
Higher priority, which meansv-if
Will repeat each run separatelyv-for
In a loop; If you want to skipfor
The priority of the loop can bev-if
Put the outer element (or<template>
)
3.7 conputed and watch
3.7.1 Calculating Attributes
Expressions in templates are very handy, but they are designed for simple calculations. Putting too much logic into a template can make it too heavy and difficult to maintain
<div id="example">{{ message.split('').reverse().join('') }}</div>
Copy the code
The above template is no longer simple declaration logic. You have to look at it for a while to realize that this is an inverted string that wants to display the variable message. This is more difficult to handle when you want to include more inversion strings here in your template, so you should use computed properties for any complex logic
<body>
<div id="app">
<div>{{message}}</div>
<div>Calculated properties: {{reverMessage}}</div>
<div>Methods: {{reverMethodsMessage ()}}</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app".data: {
message: "Hello",},computed: {
// Calculates the getter for the property
reverMessage() {
// Here this points to an instance of Vue
return this.message.split("").reverse().join(""); }},methods: {
reverMethodsMessage() {
return this.message.split("").reverse().join(""); ,}}});</script>
</body>
Copy the code
But methods can also achieve this result, we can define the same function as a method instead of evaluating properties. The end result is exactly the same. The difference, however, is that computed attributes are cached based on their reactive dependencies. They are reevaluated only if the associated reactive dependencies change. This means that as long as the Message has not changed, the computation property accessing the reverdMessage will immediately return the result of the previous calculation without having to execute the function again
This also means that the following computed properties will not be updated because date.noe () is not a reactive dependency
computed: {
now: function () {
return Date.now()
}
}
Copy the code
By contrast, the calling method will always execute the function again whenever a rerender is triggered
Why do we need to mix? Suppose we have A computationally expensive property, A, that iterates through A large array and does A lot of computations. And then we might have other computational properties that depend on A. Without caching, we will inevitably execute A’s getter multiple times, and if we don’t want caching, we will use methods instead
3.7.2 setter
Computed properties only have getters by default, although setters can be provided when needed
<body>
<div id="app">Compute attributes: {{fullNames}}<button v-on:click="handle">Am I</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app".data: {
firstName: "foo".lastName: "Bar".fullName: "Foo Bar",},methods: {
handle: function () {
this.firstName = "GOO"; }},computed: {
fullNames: {
get() {
// getter
return this.firstName + this.lastName;
},
set(val) {
// setter
var name = val.split("");
this.firstName = name[0];
this.lastName = name[name.length - 1]; }},}});</script>
</body>
Copy the code
3.7.3 listener
Vue provides a more general way to observe changes in data on and corresponding Vue instances: listening properties. It’s easy to abuse Watch when you have some data that needs to change with other data, however, it’s often better to use computed properties rather than imperative Watch callbacks
Although evaluating attributes is more appropriate in most cases, sometimes a custom listener is required. That’s why Vue provides a more generic way to respond to changes in data with the Watch option. This approach is more suitable when asynchronous or expensive operations need to be performed when data changes
<body>
<div id="app">Listener: {{fullName}} {{firstName}}<button v-on:click="handle">Am I</button>Compute attributes: {{fullNames}}</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app".data: {
firstName: "foo".lastName: "Bar".fullName: "Foo Bar",},methods: {
handle: function () {
this.firstName = "GOO"; }},watch: {
firstName(val) {
this.fullName = val + "" + this.lastName;
},
lastName(val) {
this.fullName = this.lastName + ""+ val; }},computed: {
// This is much better than the listener
fullNames() {
return this.firstName + this.lastName; ,}}});</script>
</body>
Copy the code
4. The filter
Vue.js allows you to customize filters that can be used for some common text formatting. Filters can be used in two places: double curly brace interpolation and v-bind expressions (the latter supported as of 2.1.0+). Filters should be added to the end of JavaScript expressions, indicated by the “pipe” symbol:
<! -- In double braces -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
Copy the code
Define local filters in the component’s options
filters: {
capitalize: function (value) {
if(! value)return ' '
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)}}Copy the code
Or define filters globally before creating Vue instances
Vue.filter("capitalize".function (value) {
if(! value)return "";
value = value.toString();
return value.charAt(0).toUpperCase() + value.slice(1);
});
Copy the code
If the global filter and the local filter have the same name, the local filter is used
5. Event handling
5.1 Event Handling Methods
Having touched 11 of the 14 instructions, I now listen for events using the V-ON directive and run some JavaScript code when triggered
Sometimes you also need to access the original DOM event in the inline clause processing, passing it into a method with the special variable $event
Listen for an event
<body>
<div id="app">
{{count}}
<button v-on:click="count++">Am I</button>
<button v-on:click="handleAdd">Event handling methods</button>
<button v-on:click="handle(3)">Methods in inline processors</button>
<button type="submit" @click="submitHandle('Hello', $event)">Submit the form</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app".data() {
return {
count: 0}; },methods: {
handleAdd() {
this.count += 1;
},
handle(val) {
console.log(val);
this.count += val;
},
submitHandle(val, event) {
if(event) event.peventDefault(); ,}}});</script>
</body>
Copy the code
5.2 Event modifiers
Calling event.preventDefault() or eventPropagation() in the event handler is a very common requirement. Although we can do this in methods, it’s important that methods have pure data logic instead of handling DOM event details
To handle this, vue.js provides v-Ons with event modifiers:.stop,.prevent,.capture,.self,.once, and.passive
<! -- Prevent the click event from propagating -->
<a v-on:click.stop="doThis"></a>
<! -- Commit events no longer reload the page, preventing default commit events -->
<form v-on:submit.prevent="onSubmit"></form>
<! -- modifiers can be concatenated -->
<a v-on:click.stop.prevent="doThat"></a>
<! -- only modifiers -->
<form v-on:submit.prevent></form>
<! Add event listener with event capture mode
<! Events triggered by an inner element are processed here before they are processed by the inner element.
<div v-on:click.capture="doThis">.</div>
<! Trigger handler only if event.target is the current element itself -->
<! -- that is, events are not triggered from internal elements -->
<div v-on:click.self="doThat">.</div>
<! Click event will only trigger once -->
<a v-on:click.once="doThis"></a>
<! -- The default behavior of the scroll event (i.e. the scroll behavior) will be triggered immediately -->
<! Instead of waiting for 'onScroll' to finish -->
<! -- This includes' event.preventdefault () '-->
<div v-on:scroll.passive="onScroll">.</div>
Copy the code
Do not use.passive with.prevent as.prevent will be ignored and the browser may print a warning. .passive tells the browser that you do not want to block the event’s default behavior
Why listen for events in HTML?
Because all vue.js event handling methods and expressions are strictly tied to the ViewModel of the current view, it does not cause any maintenance difficulties. Actually, there are several benefits to using v-ON:
- A glance at an HTML template makes it easy to locate the corresponding method in JavaScript code.
- Because you don’t have to manually bind events in JavaScript, your ViewModel code can be very logical and completely decoupled from DOM, making it easier to test.
- When a ViewModel is destroyed, all event handlers are automatically removed. You don’t have to worry about cleaning them.
6. The form
6.1 Use of Forms
Instruction 13: V-model
The V-model directive creates a bidirectional binding on the ,
Note: V-model cannot put expressions. If expressions need to be put, use V-bind and @input
<body>
<div id="app">
<input type="text" v-on:input="handle" v-bind:value="message" />
{{message}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app".data() {
return { message: "123" };
},
methods: {
handle(e) {
this.message = e.target.value; ,}}});</script>
</body>
Copy the code
The V-Model ignores the initial values of the value, Checked, and Selected attributes of all form elements and always uses the Vue instance data as the data source
The V-Model internally uses different attributes for different input elements and throws different events:
- Text and Textarea elements are used
value
Properties andinput
The event - Checkbox and radio use
checked
Properties andchange
The event - The select field will
value
As prop and willchange
As the event
Note: Multi-line text does not work, use v-model instead
<body>
<div id="app">
<form action="https://www.baidu.com/">
<div>
<span>Name:</span>
<span>
<input type="text" v-model="uname" />
</span>
</div>
<div>
<span>Gender:</span>
<span>
<input type="radio" id="male" v-model="gender" value="1" />
<label for="male">male</label>
<input type="radio" id="monmale" v-model="gender" value="2" />
<label for="monmale">female</label>
</span>
</div>
<div>
<span>Hobbies:</span>
<input type="checkbox" id="ball" v-model="hobby" value="1" />
<label for="ball">basketball</label>
<input type="checkbox" id="sing" v-model="hobby" value="2" />
<label for="sing">Sing a song</label>
<input type="checkbox" id="code" v-model="hobby" value="3" />
<label for="code">Write the code</label>
</div>
<div>
<span>Career:</span>
<select v-model="occupation">
<option value="1">Choosing a career...</option>
<option value="2">Teachers'</option>
<option value="3">Software engineer</option>
<option value="4">The lawyer</option>
</select>
</div>
<div class="item">
<span style="vertical-align: top;">Career:</span>
<select v-model="occupation1" multiple="multiple">
<option value="1">Choosing a career...</option>
<option value="2">Teachers'</option>
<option value="3">Software engineer</option>
<option value="4">The lawyer</option>
</select>
</div>
<div>
<span style="vertical-align: top;">Personal Profile:</span>
<textarea
rows="10px"
cols="20px"
style="resize:none; overflow: hidden;"
v-model="desc"
>
</textarea>
</div>
<div>
<input type="submit" value="Submit" @click.prevent="handle" />
</div>
</form>
</div>
<script type="text/javascript" src=".. /vue.js"></script>
<script type="text/javascript">
let vm = new Vue({
el: "#app".data: {
uname: "123".gender: 1.hobby: [].occupation: 1.occupation1: [1.2].desc: "nihao",},methods: {
handle: function () {
console.log(this.hobby.toString() + "-" + this.occupation); ,}}});</script>
</body>
Copy the code
6.2 modifier
.lazy
By default, the V-Model synchronizes the value of the input box with the data after each input event. Lazy modifiers can be added to convert the v-Model into a change event.
.number
If you want to automatically convert user-entered values to numeric types, you can add the number modifier to the V-Model, which is often useful because even when type=”number”, the value of the HTML input element always returns a string. If the value cannot be resolved by parseFloat(), the original value is returned
Replace (/[^0-9]/g, “); replace(/[^0-9]/g, “); replace(/[^0-9]/g, “);
trim
You can add the trim modifier to the V-Model if you want to automatically filter the leading and trailing whitespace characters entered by the user
<input v-model.lazy="msg" />
<input v-model.number="age" type="number" />
<input v-model.trim="msg" />
Copy the code
7. template syntax
At the bottom of the implementation, Vue compiles the template syntax into a virtual DOM rendering function. Combined with a responsive system,Vue intelligently calculates how many components need to be rerendered and minimizes DOM operations
<body>
<div id="app">{{count}} <button @click="handle(2)">I + 2 points</button></div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const vm = new Vue({
el: "#app".data() {
return {
count: 1}; },methods: {
handle(e) {
this.count += e; ,}}});// Output the render function Vue generated for us
console.log(vm.$options.render);
</script>
</body>
Copy the code
Output:
(function anonymous() {
with (this) {
return _c("div", { attrs: { id: "app" } }, [
_v("\n " + _s(count) + ""),
_c(
"button",
{
on: {
click: function ($event) {
return handle(2);
},
},
},
[_v("Point me + 2")]),]); }});Copy the code
<body>
<div id="app"></div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const vm = new Vue({
el: "#app".data() {
return {
count: 1}; },methods: {
handle(e) {
this.count += e; }},render() {
// This is the rendering function compiled in the #app template syntax above, which can also get the same result as the template
with (this) {
return _c("div", { attrs: { id: "app" } }, [
_v("\n " + _s(count) + ""),
_c(
"button",
{
on: {
click: function ($event) {
return handle(2);
},
},
},
[_v("Point me + 2")]),]); }}});</script>
</body>
Copy the code
8. Component basics
8.1 Component Usage
When registering a component, you need to give it a name. The name of the component is the first parameter in Vue.com Ponent. The name given to the component may depend on what you intend to do with it. When using a component directly in the DOM (not a string template or a single-file component), group the component name (all lowercase and must contain a hyphen). This avoids conflicts with current and future HTML elements
8.2 Global Components
Globally registered components can be used in any newly created Vue root instance (via New Vue) after they have been registered, as well as in templates for all children of their component tree
<body>
<div id="app">
<! -- Component reuse -->
<button-count></button-count>
<button-count></button-count>
</div>
<! - < template id = "temp" > < div > {{count}} < button @ click = "handle (2)" > I add for 2 < / button > < / div > < / template > -- >
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component("button-count", {
data() {
return { count: 0 };
},
methods: {
handle(e) {
this.count += e; }},template: ` < div > {{count}} < button @ click = "handle (2)" > I add for 2 < / button > < / div > `.// template: '#temp'
});
new Vue({
el: "#app"});</script>
</body>
Copy the code
The component is a reusable Vue instance with a name:
in the above example. We can use this component as a custom element in the Vue root instance created by new Vue()
Because components are reusable Vue instances, they receive the same options as new Vue, such as data, computed, Watch, Methods, and lifecycle hooks. The only exceptions are root instance-specific options like EL
A component’s data option must be a function, so each instance can maintain a copy of the object being returned
8.3 Local Components
Global registration is often less than ideal. For example, if you use a build system like WebPack, registering all components globally means that even if you no longer need the group price, it will still be included in your final build. This leads to an unnecessary increase in JavaScript downloads by users
In this case, the component can be defined through a plain JavaScript object
var HelloTom = {
// Local components
data() {
return {
msg1: "Hell0 local component"}; },template: `<div>{{msg1}}</div>`};let vm = new Vue({
el: "#app".components: {
// Local components
"hello-tom": HelloTom,
},
});
Copy the code
8.4 prop
All prop causes a one-way downlink binding between their parent prop: updates to the parent prop flow down to the child, but not the other way around. This prevents accidental changes in the parent component’s state from the child component, making your application’s data flow difficult to understand, and every time the parent component changes, all prop in the child component will be refreshed to the latest value. This means that you should not change a prop inside a child component. If you do, Vue will warn you in the browser console
There are two situations when you try to change the case of a local prop:
- The prop is used to pass down the initial value, and the child component then wants to use it as a local prop data. In this case, it is best to define a local data property and use the prop as its initial value
- This prop is passed in as a raw value and needs to be converted. In this case it is best to use the prop value to define a calculated property
Props to verify
Vue.component("my-component", {
// propps: ['propA', 'propB', 'propC', ...] // Not recommended, generally used for the first draft of a project, rapid development
props: {
// Basic type checking (' null 'and' undefined 'will pass any type verification)
propA: Number.// Multiple possible types
propB: [String.Number].// A mandatory string
propC: {
type: String.required: true,},// A number with default values
propD: {
type: Number.default: 100,},// Objects with default values
propE: {
type: Object.// Object or array defaults must be obtained from a factory function
default: function () {
return { message: "hello"}; }},// Customize the validation function
propF: {
validator: function (value) {
This value must match one of the following strings
return ["success"."warning"."danger"].indexOf(value) ! = = -1; }},}});Copy the code
Vue will generate a console warning when prop validation fails in the development environment
8.5 Parent → Child Communication
A Prop is a set of custom properties that you can register on a component. When a value is passed to a Prop property, it becomes a property of that component instance
By default, a component can have any number of props, and any value can be passed to any prop. In the following template, you can access this value in the component instance just as you would in data
Note:
- If the parent component passes to the child component
data-count
In this format, the props receiver needs either a large camel DataCount or a small camel DataCount in the child component - If the parent component passes to the child component
DATA
If this is all uppercase, the child component props needs to be converted to all lowercasedata
- The tag built-in property on the parent parent
Style, class, id, etc
, inherits the outermost HTML tag of the quilt component, not by overwriting, but by appending (Except for the ID attribute: the ID attribute on the parent parent overrides the ID attribute from the outermost HTML tag of the attachment
)
Case in point: Click the New button frequently used in projects to pop up
/ / the parent component<template>
<div class="valueTransfer">
<el-button type="primary" @click="addDialog">new</el-button>
<dialog
:dialogVisible="dialogVisible"
id="dialogId"
class="dialogClass"
data-id="1-1"
style="color: red"
></dialog>
</div>
</template>
<script>
import Dialog from "./components/Dialog";
export default {
components: {
Dialog,
},
data() {
return {
dialogVisible: false.// Controls the display and hiding of the Dialog component
};
},
methods: {
addDialog() {
this.dialogVisible = true; ,}}};</script>
Copy the code
// Subcomponent <template> <el-dialog title=" prompt "width="30%" :visible. Sync ="dialogVisible"> <span> This is a message </span> <span <el-button @click="dialogVisible = false"> </el-button> <el-button type="primary" @click="dialogVisible = false"> </el-button> </span> </el-dialog> </template> <script> export default {props: { dialogVisible: { type: Boolean, default: false, }, }, }; </script>Copy the code
8.6 $attrs
withinheritAttrs
,$listener
,The native modifier
- $attrs
All attributes passed down from the parent component (v-bind, custom attributes, id, etc.) are in the $attrs child object (except for functions and events @click, class, style). And you can pass in internal components with V-bind =”$attrs” — useful when creating higher-level components
- inheritAttrs
By default, properties (style, ID, class, etc.) on the parent component that cannot be passed down by props are bound to the root tag of the child component (the unique tag below the template) and applied to the root element of the child component as normal HTML properties
By default, attributes in the parent scope that cannot be supported by the Props Attribute bindings will be “rolled back” and applied to the root element of the child component as normal HTML attributes. This may not always behave as expected when writing a component that wraps around a target element or another component (the child component root element does not want this property, but class and style do not apply). These default behaviors are removed by setting the inheritAttrs to false. Property $attrs (also added in 2.4) makes these attributes valid (because $attrs has attributes from the parent component) and can be explicitly bound to non-root elements by V-bind.
Note: This option does not affect the class and style bindings.
Use the inheritAttrs attribute with <template> <div> <input v-bind="$attrs" /> </div> </template> <script> export default {// [inheritAttrs: false,}] [inheritAttrs: false,}] [inheritAttrs: false,}] [inheritAttrs: false,}] [inheritAttrs: false,}] [inheritAttrs: false,}] </script>Copy the code
- $listener
V-on event listeners (not including. Native modifiers) on parent components can be passed into internal components via V-ON =”$Listeners “- useful for creating higher-level components
The $Listeners are an object that contains all listeners working on the component
{
click: function () { / *... * / }
input: function (value) { / *... * /}},Copy the code
.native
If you want to listen for a native event directly on the child component root element (the only child below the template tag), you can use the V-on. native modifier
<template> <div class="valueTransfer"> <el-button type="primary" @click="dialogVisible = true"> Create a new one </el-button> <Dialog :dialogVisible="dialogVisible" :disabled="disabled" id="dialogId" class="dialogClass" data-id="1-1" style="color: red" @click="dialogClickHandle" @focus="dialogFocusHandle" ></Dialog> </div> </template> <script> import Dialog from "./components/Dialog"; Export default {components: {Dialog,}, data() {return {dialogVisible: false, // Control Dialog component display and hide, disabled: true, }; }, methods: { dialogClickHandle() { this.dialogVisible = ! this.dialogVisible; Console. log(" Listen for child component cancel button to be clicked "); }, dialogFocusHandle() {console.log(" form gets focus trigger "); ,}}}; </script>Copy the code
{{$attrs}} <span> Create </span> </span> </span> <el-input v-model="num" @input="num = num.replace(/[^0-9]/g, '')" v-on="inputListeners" ></el-input> <myInput @focus.native="focusHandle" /> <span slot="footer" Class ="dialog-footer"> <el-button V-on ="$listeners"> </el-button> <el-button type="primary" @click="dialogVisible = False ":disabled="$attrs.disabled" > specify </el-button> </span> </el-dialog> </template> <script> import myInput from "./myInput"; export default { props: { dialogVisible: { type: Boolean, default: false, }, }, components: { myInput, }, data() { return { num: "", }; }, computed: { inputListeners() { let vm = this; Return object.assign ({}, this.$listeners, {focus: function () {vm.$listeners ("focus"); }}); }, }, methods: { focusHandle(e) { console.log(e); ,}}}; </script>Copy the code
// my-input component <template> <input type="text" V-model ="num" /> </template> <script> export default {name: "myInput", data() { return { num: "", }; }}; </script>Copy the code
8.7 Communication between Child and Parent$emit
The child component defines the custom event buttonClick, then the child triggers the custom event, and the parent component binds the custom event (@custom event name), and when the child triggers the custom event, the parent component executes the custom event
<ul> <li>$emit,.sync, v-model</li> </ul> <div> {{ count }}</div> <my-button @buttonClick="buttonOnClick"></my-button> </div> </template> <script> import myButton from "./components/myButton"; export default { components: { myButton, }, data() { return { count: 0 }; }, methods: { buttonOnClick(val) { this.count = val; ,}}}; </script>Copy the code
<div class="myButton"> <button class="el-button el-button--primary" @click="$emit('buttonClick'), </button> </div> </template> <script> export default {data() {return {count: 0}; }}; </script>Copy the code
8.8 Bidirectional Data Binding of Components
<template> <div class="valueTransfer2"> <div> {{ value }}</div> <my-input :value="value" @input="(e) => (value = e)"></my-input> <! <my-input v-model="value"></my-input> </div> </template> <script> import myInput from "./components/myInput"; export default { components: { myInput, }, data() { return { value: "" }; }}; </script>Copy the code
<template> <input type="text" :value="$attrs.value" @input="$emit('input', $event.target.value)" /> </template>Copy the code
8.9 The sync modifier
We might need to “bidirectional bind” a prop (change parent data in child components). True bidirectional binding creates maintenance problems because a child component can change its parent without an obvious source of change in either parent or child.
The.sync modifier has been reintroduced since 2.3.0, but this time it only exists as a compile-time syntactic sugar. It is extended to a V-ON listener that automatically updates the parent component’s properties
Note that v-bind with the.sync modifier cannot be used with expressions (e.g. V-bind :title.sync= “doc.title + ‘! ‘” is invalid). Instead, you can only provide the name of the property you want to bind to, similar to the V-Model.
We can also use the.sync modifier with v-bind when setting multiple prop objects at the same time:
doc: { a: 2, b: false, c: '11111' }
<text-document v-bind.sync="doc"></text-document>
Copy the code
This passes each property (such as title) in the doc object as a separate prop, and then adds a separate V-on listener for updates.
Using v-bind.sync on a literal object, such as v-bind.sync= “{title: doc.title}”, does not work because there are many edge cases to consider when parsing a complex expression like this
// Parent component, click the New button, <template> <div class="valueTransfer"> <el-button type="primary" @click="dialogVisible = true"> Create a Dialog window </el-button> <! -- will be extended to <Dialog :dialogVisible="dialogVisible" @update:dialogVisible="val => val=dialogVisible" @click="dialogClickHandle" />--> <Dialog :dialogVisible.sync="dialogVisible" @click="dialogClickHandle" @returnHandle="returnHandle" ></Dialog> <hr /> </div> </template> <script> import Dialog from "./components/Dialog"; Export default {components: {Dialog,}, data() {return {dialogVisible: false, // control Dialog component display and hide,}; }, methods: { dialogClickHandle() { this.dialogVisible = ! this.dialogVisible; Console. log(" Listen for child component cancel button to be clicked "); }, returnHandle(flag) { this.dialogVisible = flag; ,}}}; </script>Copy the code
The child component clicks the button to close the parent component popup
- Method 1:
$emit
The child component custom event triggers the custom method to receive parameters, thereby changing the parent component ‘dialogVisible - Method 2: Write a click method in the parent component, and then the child component listens
$listeners
- Method 2:
update:myPropName
(.sync) triggers the mode event
</ / sub-component <template> <el-dialog title=" prompt "width="30%" :visible="dialogVisible" @close="closeHandle" > <span slot="footer" class="dialog-footer"> <el-button @click="$emit('returnHandle', </el-button> <el-button @click="$listeners ('update:dialogVisible'), </el-button> </span> </el-dialog> </template> <script> import myInput from './myInput' import inputContent from './inputContent' export default { props: { dialogVisible: { type: Boolean, default: false, } } methods: { closeHandle() { this.$emit('update:dialogVisible', false) } }, } </script>Copy the code
8.10 $emit
with$on
$emit: Fires events on the current instance. Additional parameters are passed to the listener callback
On: Listens for custom events on the current instance. Events can be defined by ‘vm.on: listening for custom events on the current instance. Events can be defined by ‘vm.on: listening for custom events on the current instance. The event can be emitted by ‘vm.emit’. The callback function receives any additional arguments that are passed in to the event that triggers the function
8.11 $parent
,$children
,$root
,$ref
- $parent
An instance of the parent of the current component, or its own instance if the current component does not have a parent
- $children
Examples of all the children on the current component, saved as an array (children is not ordered and is not reactive), if you find yourself trying to use ‘children is not ordered and is not reactive’, If you find yourself trying to use ‘children’ which is not sequential and not reactive), if you find yourself trying to use ‘children’ for data binding, consider using an Array with V-for to generate child components and using Array as the real source
- $root
The root Vue instance of the current component tree. If the current instance has no parent, the instance will be itself
- $ref
Sometimes you need to access a child component, and you can use the ref attribute to assign an ID reference to the child component
< BASE-input ref="usernameInput"></base-input> this.$refs. UsernameInput // get the DOM of the subcomponent "reference as usernameInput"Copy the code
8.12 Sibling Communication $BUS
By adding a Vue instance on top of the Vue prototype as an event bus, components can communicate with each other, independent of the relationships between components
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import "@/plugins/element-ui";
import "element-ui/lib/theme-chalk/index.css";
Vue.config.productionTip = false;
Vue.prototype.$bus = new Vue(); // Add $bus to get global access to the Vue instance
new Vue({
router,
store,
render: (h) = > h(App),
}).$mount("#app");
Copy the code
$emit and $ON in Vue
this.$emit('msg'.this.msg) // Send data
this.$on('msg'.funtion(msg){/* Data manipulation */ })
Copy the code
Native ="focusHandle"/> <myContent></myContent> </div>Copy the code
// myInput <template> <input type="text" v-model="num" /> </template> <script> export default { name: "myInput", data() { return { num: "", }; }, watch: {num(newVal) {this.$bus.$emit("num", newVal); ,}}}; </script>Copy the code
<template> <div class="inputContent">{{ num }}</div> </template> <script> export default { name: "inputContent", data() { return { num: "", }; }, mounted () {/ / data operation. $bus. $on (" num "(num) = > {this. Num = num; }); $bus.$off("num");}, destroyed() {$bus. }}; </script>Copy the code
8.13 Value Transfer for Grandparent and Grandparent Components (provide
/inject
)
type
- provide:
Object | () => Object
Returns an object or an object function. This object contains properties that can be injected into its descendants. Symbol can be used as the key value in the object, but only in environments where symbol and reflect.ownkeys are natively supported
- inject:
Array<string> | { [key: string]:string | Stmbol | Object }
Inject is an array of strings or an object whose key is the local binding name, Value is the key(string or Symbol) to search for in the available injection content or an object whose form property is the key(string or Symbol) to search for in the available injection content
Provide and Inject need to be used together to allow an ancestor component to inject a dependency into its descendants, no matter how deep the hierarchy
Provide and inject are mainly used when developing high-level plug-in/component libraries. It’s not recommended in normal application code, if you have a deep hierarchy than if you know where the value is passed down or if you go up many layers of the value below, it’s not very coupling; Also, ProVideo and Inject binding are not reactive. However, if you are passing a listening object, the object’s property is still responsive
var Father = { // Parent provides 'foo'
provide: { foo: 'bar'}}var child ={
inject: ['foo'].// The following method calls this.foo
}
// 2. Use the symbol function proide and object inject
const s = Symbol(a)var Father = {
provide() {
provide() {
return { [s]: 'foo'}}}}var child = {
inject: { s } // The memory of this s is different each time
}
// 3. Use the default property for props (inject foo as the default property for props)
var child = {
inject: ['foo'].props: {bar: { default() { return this.foo}}}}// 4. Insert the value into the data entry (the initial value of the data).
var child = {
inject: ['foo'].data() {
return { bar: this.foo }
}
}
// 5. Set the default value for injection (to make it optional by injecting this property)
const child = {
inject: { foo: { default: 'foo'}}}// 6. If it needs property injection with a different name, use from to represent its source property
const child = {
inject: {
foo: {
from: 'bar'.default: 'foo'}}}// 7. Similar to the default values of prop, factory methods are required for non-raw values
const child = {
inject: {
foo: {
from: 'bar'.default: () = > [1.2.3]}}}Copy the code
8.14 Programmatic event listeners
Now that we know how $emit is used, it can be listened on by V-ON, but the Vue instance also provides other methods in its event interface, we can:
- through
$on(eventName, eventHandler)
Listen for an event - through
$once(eventName, eventHandler)
Listen for one event at a time - through
$off(eventName, eventHandler)
Stops listening for an event
They come in handy when you need to manually listen for events on a component instance. They can also be used in code organization tools
// Attach the date picker to an input box once
// It will be mounted to the DOM.
mounted: function () {
// Pikaday is a library of third-party date pickers
this.picker = new Pikaday({
field: this.$refs.input,
format: 'YYYY-MM-DD'})},// Before the component is destroyed,
// Also destroy the date picker.
beforeDestroy: function () {
this.picker.destroy()
}
Copy the code
There are two potential problems:
- It needs to save this in the component instance
picker
If possible, only lifecycle hooks can access it. This is not a serious problem, but it can be considered clutter. - Our build code is separate from our cleanup code, which makes it harder to programmatically clean up everything we build.
You should solve these two problems with a programmatic listener:
mounted: function () {
var picker = new Pikaday({
field: this.$refs.input,
format: 'YYYY-MM-DD'
})
this.$once('hook:beforeDestroy'.function () {
picker.destroy()
})
}
Copy the code
Using this strategy, I can even have multiple input field elements using different Pikaday at the same time, with each new instance programmatically cleaning itself up later
mounted: function () {
this.attachDatepicker('startDateInput')
this.attachDatepicker('endDateInput')},methods: {
attachDatepicker: function (refName) {
var picker = new Pikaday({
field: this.$refs[refName],
format: 'YYYY-MM-DD'
})
this.$once('hook:beforeDestroy'.function () {
picker.destroy()
})
}
}
Copy the code
8.15 Dynamic Components
When switching between components in a multi-tab interface, you want to keep the state of those components in order to avoid performance problems caused by repeated rerendering
If you have time to do something on this page, then switch pages and switch back, you’ll find that the page has been re-rendered, not the last one, but a new instance created by Vue every time you switch
It is often useful to recreate dynamic component behavior, but sometimes it is desirable that component instances be cached when they are first created. To solve this problem, we can dynamically wrap it with a
element
<! -- Deactivated components will be cached! -->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
Copy the code
So the component will be cached, no matter how you switch, or switch the previous action
Note: This
requires that the component being switched to have its own name, either through the name option or local/global registration
Keep-alive for details
8.16 Asynchronous Components
In large applications, we may need to break the application into smaller code blocks and load a module from the server only when needed. For simplicity, Vue allows you to define your component as a factory function that asynchronously parses your component definition. Vue fires the factory function only when the component needs to be rendered, and caches the result for future re-rendering
Vue.component("async-example".function (resolve, reject) {
setTimeout(function () {
// Pass the component definition to the 'resolve' callback
resolve({
template: "
I am async!
"}); },1000);
});
Copy the code
As you can see, the factory function receives a resolve callback, which is called when you get the component definition from the server. You can also call reject(Reason) to indicate a load failure. SetTimeout here is for demonstration purposes, and it’s up to you to retrieve the component. One recommendation is to use asynchronous components in conjunction with Webpack’s code-splitting feature
Vue.component("async-webpack-example".function (resolve) {
// This particular 'require' syntax will be told to Webpack
// Automatically splits your build code into multiple packages
// Will be loaded via Ajax request
require(["./my-async-component"], resolve);
});
Copy the code
You can also return a Promise in the factory function, so adding webPack 2 and ES2015 syntax together, we can use dynamic import like this:
Vue.component(
"async-webpack-example".// This dynamic import returns a 'Promise' object.
() = > import("./my-async-component"));Copy the code
When using local registrations, you can also directly provide a function that returns a Promise:
new Vue({
// ...
components: {
"my-component": () = > import("./my-async-component"),}});Copy the code
Handling load state
The asynchronous component factory function here can also return an object of the following format:
const AsyncComponent = () = > ({
// The component to load (should be a 'Promise' object)
component: import("./MyComponent.vue"),
// The component used when the asynchronous component is loaded
loading: LoadingComponent,
// The component used when loading failed
error: ErrorComponent,
// Display the component delay time when loading. The default is 200 (ms)
delay: 200.// If a timeout is provided and the component loads time out,
// Use the component used when the load failed. The default value is' Infinity '
timeout: 3000});Copy the code
Note that you must use Vue Router version 2.4.0+ if you wish to use this syntax in the routing component of the Vue Router.
8.17 Componentized understanding
Componentization is the essence of Vue. Vue applications are made up of components. In interviews, people often ask about their understanding of Vue componentization
Definition: Components are reusable Vue instances. Specifically, they are instances of VueComponent that inherit from Vue
Advantages: As you can see from the example above, componentization increases code reusability, maintainability, and testability
Usage scenario: When to use components?
- General components: to achieve the most basic functions, with versatility, reuse, such as button components, input box components, layout components and so on
- Business components: They complete specific services and have certain reuse, such as login components and multicast map components
- Page components: Organize the opposing content of different parts of the application, and switch between different page components when necessary, such as list page, details page
How to use components
- Definitions: VUe.component(), Component option, SFC
- Classification: stateful component, functional, abstract
- Communication: props, emit/emit/emit/on, dojo.provide/inject, children/children/children/parent/root/root/root/attrs / $listeners
- Content distribution:
<slot>
,<template>
,v-slot
- Usage and optimization: IS, keep-alive, asynchronous components
Component nature
Components in Vue go through the following process: Component configuration -> VueComponent instance -> Render () -> Vireual DOM -> DOM, so the essence of the component is to generate a virtual DOM
9 slots
9.1 Basic Slots
Now for the last instruction, and the 14th instruction: V-slot
Vue implements a set of content distribution apis, using
elements as outlets to host distribution content, which allows composition of components as follows
<navigation-link url="/profile"> Your Profile </navigation-link>
<! -- Navigation link template can be written like this -->
<a v-bind:href="url" class="nav-link">
<slot></slot>
</a>
Copy the code
will be replaced with “Your Profile” when the above component is rendered. Slots can contain any template code, including HTML, or even other components
Most of the slots in the middle are empty. Sometimes it is useful to specify the contents of the slots, which will only be rendered when no content is provided
Declaration: The slot content of the child component is displayed in the middle of the parent component
<button type="submit">
<slot></slot>
</button>
Copy the code
Now you might want to render the text “Submit” most of the time in this
<! -- Parent component -->
<subimt-button></subimt-button>
<! -- Subcomponent -->
<button type="submit">
<slot>Submit</slot>
</button>
Copy the code
Note that the parent tag does not provide any slot content in the middle, and the backup “Submit” will be rendered
<button type="submit">Submit</button>
Copy the code
If the parent component provides content, the default content is overridden
<submit-button>Save<submit></submit></submit-button>
Copy the code
The supplied content will be rendered instead of the backup content
<button type="submit">Save</button>
Copy the code
9.2 Named Slot
<div class="container">
<header>
<! -- We want to put the header here -->
</header>
<main>
<! -- We want to put the main content here -->
</main>
<footer>
<! -- We want to put footer here -->
</footer>
</div>
Copy the code
In the case above where we need multiple slots, the
element has a special attribute: name, which can define an external slot
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
Copy the code
A
exit without a name will have the implied name “default”; When feeding content to a named slot, we can use the V-slot directive on a
element and supply its name as an argument to the V-slot
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
Copy the code
Now everything in the template element will be passed into the appropriate slot. Anything that is not wrapped in a
with v-slot is treated as the default slot content, however, if you want to be more explicit, anything that can still be in a
is treated as the default slot content
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<template v-slot:default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
Copy the code
Note: V-slot can only be added to
9.3 Scope Slot
Sometimes it is useful to have slot content access to data in child components.
<! The user attribute is a child component, the code will not run if it is accessed by the parent. Only 'user' can be accessed from the <current-user> component, and the content we provide is rendered at the parent level.
<current-user>{{user.firstName}}</current-user>
Copy the code
To make user available in the parent’s slot content, we can bind user as an attribute of the
element
<span>
<slot v-bind:user="user"></slot>
</span>
Copy the code
Properties bound to
elements are called slot prop. Now in the parent scope, we can define the name of our supplied slot prop using v-slot with a value
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
Copy the code
The label of the component can only be used as a template for the slot if it is on the default slot. This allows us to apply v-SLO directly to components
<current-user v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</current-user>
Copy the code
I could have written it even simpler
<current-user v-slot="slotProps"> {{ slotProps.user.firstName }} </current-user>
Copy the code
Note that the default slot abbreviation syntax should not be mixed with named slots, as it would result in undefined scope
<! -- invalid, will result in warning -->
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
<template v-slot:other="otherSlotProps">
slotProps is NOT available here
</template>
</current-user>
Copy the code
Whenever multiple slots are present, clock the full
based syntax for all slots
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
<template v-slot:other="otherSlotProps">.</template>
</current-user>
Copy the code
Structural slot Prop
The inner workings of a scoped slot are to include the contents of your slot in a function that passes in a single argument
function (slotProps) { // Slot contents}
Copy the code
This means that the value of this V-slot can actually be any JavaScript expression that can be used as a parameter in the function definition. So in supported environments (single-file components or modern browsers), you can use ES2015 deconstruction to pass in a specific slot prop
<current-user v-slot="{ user }"> {{ user.firstName }} </current-user>
Copy the code
This can make the template more concise, especially if the slot provides multiple prop. It also opens up other possibilities for prop renaming, such as renaming user to person:
<current-user v-slot="{ user: person }"> {{ person.firstName }} </current-user>
Copy the code
You can even define the backup content for cases where slot prop is undefined:
<current-user v-slot="{ user = { firstName: 'Guest' } }">
{{ user.firstName }}
</current-user>
Copy the code
The dynamic slot
<base-layout>
<template v-slot:[dynamicSlotName] >.</template>
</base-layout>
Copy the code
V – short for slot
V-slot can also be abbreviated, that is, the content before the argument (v-slot:) is replaced with the string #, for example, V-slot :header can be abbreviated to #header
<base-layout>
<template #header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template #footer>
<p>Here's some contact info</p>
</template>
</base-layout>
Copy the code
However, as with other instructions, this abbreviation is only available if it has arguments. This means that the following syntax is invalid:
<! -- This will trigger a warning -->
<current-user# ="{ user }"> {{ user.firstName }} </current-user>
Copy the code
If you want to use abbreviations, you must always specify the slot name instead:
<current-user #default="{ user }"> {{ user.firstName }} </current-user>
Copy the code
9.4 Reusable Slots
Slot Prop allows us to convert slots into reusable templates. These boards can render different content based on the input prop, which is most useful when designing reusable components that encapsulate data logic while allowing components to customize their partial layout
<ul>
<li v-for="todo in filteredTodos" v-bind:key="todo.id">{{ todo.text }}</li>
</ul>
Copy the code
We can control each todo through the parent component by using it as a slot for the parent component, and then bind todo as a slot Prop
<ul>
<li v-for="todo in filteredTodos" v-bind:key="todo.id">
<! We prepare a slot for each todo, passing the 'todo' object as a prop for the slot. -->
<slot name="todo" v-bind:todo="todo">
<! -- Backup content -->
{{ todo.text }}
</slot>
</li>
</ul>
Copy the code
Now when we use the
component, we can choose to define a different
for todo as an alternative, and we can get data from the child component:
<todo-list v-bind:todos="todos">
<template v-slot:todo="{ todo }">
<span v-if="todo.isComplete">✓</span>
{{ todo.text }}
</template>
</todo-list>
Copy the code
10. Custom instructions
In addition to the core functionality’s default built-in directives (V-model and V-show), Vue also allows the registration of custom directives. Note that in VU 2.0, the main form of code reuse and abstraction is components. However, there are cases where you still need to perform low-level operations on normal DOM elements, and custom directives are used.
// Register a global custom directive 'V-focus'
Vue.directive("focus", {
// When the bound element is inserted into the DOM...
inserted: function (el) {
// Focus elementsel.focus(); }});Copy the code
If you want to register local directives, the component also accepts a caching option:
directives: {
focus: {
// The definition of a directive
inserted: function (el) {
el.focus()
}
}
}
Copy the code
You can then use the new V-focus property on any element in the template, as shown below
<input v-focus />
Copy the code
<body>
<div id="app">
<input type="text" v-focus />
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.directive("focus", {
inserted(el){ el.focus(); }});new Vue({
el: "#app"});</script>
</body>
Copy the code
10.1 Custom instruction hooks
Vue has 14 built-in instructions, but these instructions can not meet the daily development or want to add special functions to the DOM, this is the Vue custom instructions application scenario
An instruction definition object can provide the following hook functions (all optional) :
-
Bind: Called only once, the first time a directive is bound to an element. This is where you can perform one-time initialization Settings.
-
Inserted: Called when the bound element is inserted into a parent (the parent is guaranteed to exist, but not necessarily inserted into the document).
-
Update: called when the component’s VNode is updated, but may occur before its child VNodes are updated. The value of the instruction may or may not have changed. However, you can ignore unnecessary template updates by comparing the values before and after the update (see below for detailed hook function parameters).
-
ComponentUpdated: Invoked when the VNode of the component where the directive resides and its child VNodes are all updated.
-
Unbind: Called only once, when an instruction is unbound from an element.
Let’s look at the parameters of the hook function (that is, EL, Binding, vNode, and oldVnode).
Hook function arguments
The instruction hook function is passed the following arguments:
-
El: The element bound by the directive that can be used to manipulate the DOM directly.
-
Binding: An object containing the following properties:
-
Name: indicates the command name, excluding the V – prefix.
value
: The binding value of the directive, for example:v-my-directive="1 + 1"
, the binding value is2
.oldValue
: The value preceding the instruction binding, only inupdate
和componentUpdated
Hooks are available. Available regardless of whether the value changes.expression
: Command expression in the form of a string. For example,v-my-directive="1 + 1"
Where, the expression is"1 + 1"
.arg
: Optional parameter passed to the instruction. For example,v-my-directive:foo
Where, the parameter is"foo"
.modifiers
: an object that contains modifiers. Such as:v-my-directive.foo.bar
, the modifier object is{ foo: true, bar: true }
.
-
Vnode: virtual node generated by Vue compilation. Go to the VNode API to learn more.
-
OldVnode: Last virtual node, available only in update and componentUpdated hooks.
Note: all parameters except el should be read-only and do not change them. If you need to share data between hooks, it is recommended to do so through the element’s dataset.
Were mixed with 11.
Mixins provide a very flexible way to distribute reusable functionality in Vue components. A mixin object can contain any component option. When a component uses mixin, all mixin options are “blended” into the component’s own options.
When components and mixed with object contains the same options, these options will be in the proper manner “merge”, if the data object, ministries of recursive merged, and in the event of a conflict with the component data (object to perform the same thing) with the same priority, if not the same hook function, with the same name hook function will be combined into an array, Therefore, will be called, in which case the priority is to call the blended object’s hook before the component’s child hook
Mixin can also be registered globally. Use with extreme care! Once global mixin is used, it affects every subsequent Vue instance created. When used appropriately, this can be used to inject processing logic for custom options (be careful with global mixin, as it affects each individually created Vue instance (including third-party components). In most cases, only custom options should be applied, as in the example above.)
Vue.mixin({
created: function () {
var myOption = this.$options.myOption;
if (myOption) {
console.log(myOption); }}});new Vue({
myOption: "hello!"});Copy the code
Custom instruction options merge
JSX and rendering functions
See the official document for more details
13 animation
13.1 Simple Use
Vue provides a variety of application transitions when inserting, updating, or removing the DOM, including the following tools:
- Automatically apply class to CSS transitions and animations
- You can use third-party CSS animation libraries, such as animation.css
- Use JavaScript to manipulate the DOM directly in transitional hook functions
- You can use a third-party JavaScript animation library, such as velocity.js
Single element/component transitions
Vue provides a wrapper component for Transition, and you can add an entry/exit transition to any element or component in the following cases
- Conditional Rendering (using V-if)
- Conditional presentation (using V-show)
- Dynamic components
- Component root node
<body>
<style>
.fade-enter-active..fade-leave-active {
transition: opacity 0.5 s;
}
.fade-enter..fade-leave-to {
opacity: 0;
}
</style>
<div id="app">
<button @click="isShow = ! isShow">switch</button>
<transition name="fade">
<p v-if="isShow">Hello World</p>
</transition>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app".data() {
return {
isShow: true}; }});</script>
</body>
Copy the code
Vue does something when inserting or deleting elements contained in the Transition component
- Automatically sniff out whether a CSS transition or animation has been applied to the target element, and if so, add/remove the CSS class name as appropriate
- If the transition component provides JavaScript hook functions, these hook functions will be called at the appropriate time
- If the JavaScript hook is not found and CSS animations/transitions are not detected,DOM operations (insert/delete) are performed immediately in the next frame (note: this browser’s frame-by-frame animation mechanism), unlike Vue’s nextTick concept)
13.2 Names of Transition Classes
There are six class switches in the entry/exit transition
v-enter
Define the beginning state of the transition. It takes effect before the element is inserted and is removed the next frame after the element is insertedv-enter-active
Defines the state in which the transition takes effect. Apply throughout the transition phase, apply before the element is inserted, and remove after the transition/animation. This class can be used to define process events, delays, and curve functions that enter the transitionv-enter-to
Define the end state of the transition. The next frame takes effect after the element is inserted (meanwhilev-enter
Removed) after the transition/animation is completev-leave
Define the beginning state of the exit transition. Immediately after the exit transition is triggered, the next frame is removedv-leave-active
Defines the state when the exit transition takes effect. Applies throughout the exit transition phase, takes effect immediately when the exit transition is triggered, and removes after the transition/animation is complete. This class can be used to define exit transition process time, delay and curve functionsv-leave=to
Leaving the end state of transition. The next frame takes effect after the exit transition is triggered (at the same timev-leave
Removed) after the transition/animation is complete
V – is the default prefix for class names that are switched in transition if you use a
V-enter-active and V-leave-active can control the different mitigation curves of the entry/exit transition,
<body>
<style>
/* You can set different entry and exit animations */
/* Sets the duration and animation function */
.slide-fade-enter-active {
transition: all 0.3 s ease;
}
.slide-fade-leave-active {
transition: all 0.8 s cubic-bezier(1.0.5.0.8.1);
}
.slide-fade-enter..slide-fade-leave-to
/* .slide-fade-leave-active for below version 2.1.8 */ {
transform: translateX(10px);
opacity: 0;
}
</style>
<div id="app">
<button @click="show = ! show">Toggle</button>
<transition name="slide-fade">
<p v-if="show">Hello World</p>
</transition>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: "#app".data() {
return {
show: true}; }});</script>
</body>
Copy the code