Vue document

1. Awareness and preparation

Vue is a progressive JavaScript framework

Specific: Easy to use, flexible, efficient (super fast virtual DOM)

Progressive understanding:

  1. If you already have an existing server application, you can embed Vue as part of that application for a richer interactive experience
  2. 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
  3. 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
  4. 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 globalconfig.errorHandlerIt 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 linkerrorCapturedHook, they will be invoked one by one by the same error
  • If thiserrorCapturedThe hook itself throws an error, and both the new error and the original caught error are sent globallyconfig.errorHandler
  • aerrorCapturedThe hook can returnfalseTo 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 errorerrorCapturedHooks 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: '
       
class is bound to the component
'
});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

<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

v-ifwithv-showThe 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-forCan be found in<template>To loop over a piece of content that contains multiple elements,v-ifCan also be used in<template>, but thev-showwithv-elseDoes not support<template>
  • Not recommended at the same timev-ifwithv-forWhen the two of them use it together,v-forthanv-ifHigher priority, which meansv-ifWill repeat each run separatelyv-forIn a loop; If you want to skipforThe priority of the loop can bev-ifPut 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:

  1. A glance at an HTML template makes it easy to locate the corresponding method in JavaScript code.
  2. 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.
  3. 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 usedvalueProperties andinputThe event
  • Checkbox and radio usecheckedProperties andchangeThe event
  • The select field willvalueAs prop and willchangeAs 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 componentdata-countIn 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 componentDATAIf this is all uppercase, the child component props needs to be converted to all lowercasedata
  • The tag built-in property on the parent parentStyle, 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 $attrswithinheritAttrs,$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:$emitThe 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 $emitwith$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 instancepickerIf 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

<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

<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

<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

<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 inupdatecomponentUpdatedHooks 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:fooWhere, 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

  1. v-enterDefine the beginning state of the transition. It takes effect before the element is inserted and is removed the next frame after the element is inserted
  2. v-enter-activeDefines 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 transition
  3. v-enter-toDefine the end state of the transition. The next frame takes effect after the element is inserted (meanwhilev-enterRemoved) after the transition/animation is complete
  4. v-leaveDefine the beginning state of the exit transition. Immediately after the exit transition is triggered, the next frame is removed
  5. v-leave-activeDefines 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 functions
  6. v-leave=toLeaving the end state of transition. The next frame takes effect after the exit transition is triggered (at the same timev-leaveRemoved) after the transition/animation is complete

V – is the default prefix for class names that are switched in transition if you use a
without a name. If you use
then v-Enter will be replaced with my-transition-Enter.

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