destroyed

Called after the Vue instance is destroyed. After the invocation, everything indicated by the Vue instance is unbound and all event listeners are removed.

What are components?

If a component is a reusable Vue instance with a name, such as Shanshan-cMP, we can use this component as a custom element in a root instance created with new Vue:

<div id="app">
  <shanshan-cmp></shanshan-cmp>
</div>
Copy the code
const vm = new Vue({
  el: '#app'
})
Copy the code

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.

The component registration

Global components

Vue.component

Components created with Vue.com Ponent components are registered globally. That is, they can be used in the template of any newly created Vue root instance (New Vue) after registration.

Parameters:

  • {string}
  • {Function | Object} [definition]

Usage: Register or get global components. The registration also automatically sets the name of the component with the given ID.

Example:

<div id="app">
  <button-counter></button-counter>
</div>
Copy the code
Vue.component('button-counter', {
  data () {
    return {
      count: 0,}},template: ` < button @ click = "count + +" > you rang I {{count}} < / button > `
})

const vm = new Vue({
  el: '#app',})Copy the code

Local components

Define the components to use in the Components option. For each attribute in the Components object, its attribute name is the name of the custom element, and its attribute value is the component’s option object.

Example:

<div id="#app">
  <button-counter></button-counter>
</div>
Copy the code
const buttonCounter = {
  data () {
    return {
      count: 0}},template: ` < button @ click = "count + +" > you rang I {{count}} < / button > `,}const vm = new Vue({
  el: '#app'.components: {
    'button-counter': buttonCounter
  }
})
Copy the code

Component name

When registering a component, we always need to give it a name. The name you give to a component may depend on what you intend to do with it, so naming is semantic.

Component name case

There are two ways to define component names:

Use kebab-case (horizontal dash delimited name)

Vue.component('my-component', {/ * * * /});
Copy the code

When defining a component using kebab-case, you must use kebab-case when referencing the custom element, for example:
.

Use PascalCase (big hump name)

Vue.component('MyComponent', {/ * * * /});
Copy the code

When defining a component using PascalCase, you can use either nomenclature when referencing the custom element.

and

are both acceptable. Note, however, that only kebab-case is valid when used directly in the DOM (that is, a string template or a single-file component).

Also: We strongly recommend following the W3C specification for custom component names (all lowercase and must contain a hyphen). This will help you avoid conflicts with current and future HTML elements.

Component reuse

Components can be reused any number of times:

<div id="#app">
  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>
Copy the code

Self-closing assembly

Components that have no content in single-file components, string templates, and JSX should be self-closing — but never in DOM templates.

Self-closing components indicate that they not only have no content, but intentionally have no content. The difference is like a blank page in a book compared to a blank page labeled “this page is intentionally blank.” And without the extra closing tag, your code is much cleaner.

Unfortunately, HTML does not support self-closing custom elements — only official “empty” elements. So the above strategy only applies where Vue’s template compiler can reach before entering the DOM, and then producing HTML that conforms to the DOM specification.

Component’s data option

When we define a component, its data does not directly provide an object like this:

data: {
  count: 0
}
Copy the code

Instead, a component’s data option must be a function, so each instance can maintain a separate copy of the returned object:

data () {
  return {
    count: 0}}Copy the code

If Vue does not have this rule, clicking a button may affect all other instances as follows:

Single root element

Each component must have only one root element, and when the template element is greater than 1, the template’s contents can be wrapped in a parent element.

Component _Prop

Register custom features

By default, a component simply writes its structure, style, and behavior, using data that should be passed to the component from the outside.

How is it delivered? Register the prop that needs to be received, passing the data to the component as a custom feature.

Such as:

<div id="app">
  <video-item 
    title="Sheep village Shake" 
    poster="https://developer.duyiedu.com/bz/video/955bac93ccb7f240d25a79b2ff6a9fdbda9537bc.jpg@320w_200h.webp" 
    play="638000" 
    rank="1207"
  ></video-item>
</div>
Copy the code
Vue.component('video-item', {
  props: ['title'.'poster'.'play'.'rank'],})Copy the code

In the template above, you can see that we can access this value in the component instance just as we can access the value in data:

<div id="app">
  <video-item 
    title="Sheep village Shake" 
    poster="https://developer.duyiedu.com/bz/video/955bac93ccb7f240d25a79b2ff6a9fdbda9537bc.jpg@320w_200h.webp" 
    play="638000" 
    rank="1207"
  ></video-item>
</div>
Copy the code
Vue.component('video-item', {
  props: ['title'.'poster'.'play'.'rank'].template: `<div>{{ title }}</div>`
})
Copy the code

Prop case

Feature names in HTML are case insensitive, so browsers interpret all uppercase characters as lowercase characters. Therefore, when a prop passed is named with a dash, the props inside the component should be named with the hump. Such as:

<div id="app">
  <! -- kebab-case in HTML -->
  <video-item sub-title="hello!"></video-item>
</div>
Copy the code
Vue.component('video-item', {
  // In JavaScript it is camelCase
  props: ['subTitle'].template: '<h3>{{ postTitle }}</h3>'
})
Copy the code

Note that this limitation does not exist if you are using a string template.

Pass static or dynamic Prop

Like this, we already know that we can pass a static value to prop:

<video-item title="Sheep village Shake"></video-item>
Copy the code

If you want to pass a dynamic value, you can use the V-bind directive to do so, for example:

<video-item :title="title"></video-item>
Copy the code

Pass all attributes of an object

If you want to pass in all the properties of an object as prop, you can use V-bind with no arguments. For example, for a given object person:

person: {
  name: 'shanshan'.age: 18
}
Copy the code

Pass all attributes:

<my-component v-bind="person"></my-component>
Copy the code

This code is equivalent to:

<my-component
  :name="person.name"
  :age="person.age"
></my-component>
Copy the code

Component _Prop validation

We can specify validation requirements for a prop of a component, for example you can ask what type a prop is. Vue alerts the browser console if a requirement is not being met, which can be very helpful when developing a component that will be used by others.

To customize the validation of prop, you can provide an object with validation requirements for the values in props instead of an array of strings. Such as:

Vue.component('my-component', {
  props: {
    title: String.likes: Number.isPublished: Boolean.commentIds: Array.author: Object.callback: Function.contactsPromise: Promise}})Copy the code

In the above code, basic type checking is performed on prop. The type value can be one of the following native constructors: String, Number, Boolean, Array, Object, Date, Function, Symbol, any custom constructor, or an Array of the above. Note that null and undefined will pass any type validation. In addition to basic type checking, we can configure advanced options for additional validation of Prop, such as type checking, custom validation, and setting defaults. Such as:

Vue.component('my-component', {
  props: {
    title: {
      type: String.// Check whether prop is of the given type
      default: 'Chinese fir is the most beautiful'.// Specify a default value for the prop. The default value for the object or array must be returned from a factory function, such as: default () {return {a: 1, b: 10}},
      required: true.// Define whether prop is mandatory
      validator (prop) {  If the prop function returns a falsy value, the validation fails
        return prop.length < 140; }}}})Copy the code

For better teamwork, the definition of prop in the submitted code should be as detailed as possible, or at least specify its type.

Component _ One-way data flow

All prop forms 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 state of the parent component from the child, which can make the data flow of your application difficult to understand.

There are two common situations when trying to change a prop:

  1. This prop is used to pass an initial value; The child component then wants to use it as a local prop data, and this value will be changed in subsequent operations. In this case, it is best to define a local data property and use this prop as its initial value:
props: ['initialCounter'].data: function () {
  return {
    counter: this.initialCounter
  }
}
Copy the code
  1. This prop is passed in as a raw value and needs to be converted. In this case, it is best to use the value of this prop to define a calculated property:
props: ['size'].computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}
Copy the code

Component_non-prop features

A non-prop feature is a feature that is not registered by a component. When a component receives a non-prop feature, that feature is added to the root element of the component.

Replace/merge existing features

Imagine the template for < my-cMP > looks like this:

<input type="date" class="b">
Copy the code

To customize a theme for our date picker plug-in, we might need to add a special class name like this:

<my-cmp
  class="my-cmp"
></my-cmp>
Copy the code

In this case, we define two different class values:

  • My-cmp, which is set up in the component’s template
  • B, this is passed in from the component’s parent

For most features, the value provided externally to the component replaces the value set internally by the component. So if you pass type=”text” it will replace type=”date” and break it! Fortunately, the class and style features are a little smarter, as the values on both sides are merged to get the final value: my-cmp B.

Disable feature inheritance

If you don’t want the root element of a component to inherit features, you can set inheritAttrs: false in the component options. Such as:

Vue.component('my-cmp', {
  inheritAttrs: false.// ...
})
Copy the code

In this case, it is appropriate to use the instance’s $attrs attribute, which is an object with the key name of the passed attribute and the key value of the passed attribute.

{
  required: true.placeholder: 'Enter your username'
}
Copy the code

Using inheritAttrs: false and $attrs, we can manually determine which element these attributes will be assigned to. Such as:

Vue.component('base-input', {
  inheritAttrs: false.props: ['label'.'value'].template: `  `,})Copy the code

Note that the inheritAttrs: false option does not affect the style and class bindings.

Component_ Listens for component events

First, let’s write a blog component such as:

Vue.component('blog-post', {
  props: {
    post: {
      type: Object,}},template: ` < div class = "blog post -" > < h3 > {{post. The title}} < / h3 > < button > amplification size < / button > < div > {{post. The content}} < / div > < / div > `,})Copy the code
<div id="app">
  <div :style="{fontSize: postFontSize + 'em'}">
    <blog-post
      v-for="post in posts"
      :key="post.id"
      :post="post"
    >
    </blog-post>
  </div>
</div>
Copy the code
const vm = new Vue({
  el: '#app'.data: {
    posts: [{title: 'heading 1'.content: 'Body content'.id: 0}, {title: 'title 2'.content: 'Body content'.id: 1}, {title: 'title'.content: 'Body content'.id: 2],},postFontSize: 1}})Copy the code

As you can see in each blog component, there is a button to enlarge the size of the font on the page. That is, when clicking this button, we tell the parent component to change the postFontSize data to enlarge the text of all the blog posts. What should you do in such a situation?

The Vue instance provides a custom event to solve this problem. The parent component can listen for any event of the child component instance through the V-ON directive, as if it were a native DOM element, such as:

<div id="app">
  <div :style="{fontSize: postFontSize + 'em'}">
    <blog-post
      .
      @enlarge-text="PostFontSize + = 0.1"
    >
    </blog-post>
  </div>
</div>
Copy the code

So how can we monitor such a strange event? This requires actively firing a custom event within the component.

How to trigger? An event is emitted by calling the $emit method and passing in the event name, such as:

Vue.component('blog-post', {
  props: {... },template: `
    <div class="blog-post">
      ...
      <button @click="$emit('enlarge-text')">放大字号</button>
      ...
    </div>
  `,})Copy the code

The parent component can then receive the event and update the value of the data pageFontSize.

Throw a value using an event

In some cases, we might want to let the

component decide how much larger its text should be. This is the second argument you can use to provide this value, such as:

Vue.component('blog-post', {
  props: {... },template: ` 
      
... < button @ click = "$emit (' enlarge - text, 0.2)" > amplification size < / button >...
`
,})Copy the code

When the parent component listens for this event, the value thrown can be accessed via $event:

<div id="app">
  <div :style="{fontSize: postFontSize + 'em'}">
    <blog-post
      .
      @enlarge-text="postFontSize += $event"
    >
    </blog-post>
  </div>
</div>
Copy the code

Alternatively, write the event handler as a method:

<div id="app">
  <div :style="{fontSize: postFontSize + 'em'}">
    <blog-post
      .
      @enlarge-text="onEnlargeText"
    >
    </blog-post>
  </div>
</div>
Copy the code

This value will be passed to the method as the first argument:

methods: {
  onEnlargeText: function (enlargeAmount) {
    this.postFontSize += enlargeAmount
  }
}
Copy the code

The event name

Unlike components and Prop, event names do not have any automated capitalization conversion. Instead, the name of the event that fires needs to match exactly all the names that are listening for that event. If an event with the camelCase name is triggered:

this.$emit('myEvent')
Copy the code

Listening on the kabab-case version of this name has no effect.

<! -- No effect -->
<my-component v-on:my-event="doSomething"></my-component>
Copy the code

Unlike components and prop, event names are not treated as JS variable names or property names, so there is no reason to use camelCase or PascalCase.

The V-ON event listener is automatically converted to all lowercase in the DOM template, so @myevent will become @myevent, making it impossible to listen on myEvent.

Therefore, it is recommended to always use the event name of kebab-case.

Bind native events to components

When a component listens for events, we listen for custom events that are automatically triggered by the component, but in some cases we may want to listen for a native event directly on the root element of a component. This is a. Native modifier that can be used with the V-ON instruction, such as:

<base-input @focus.native="onFocus" @blur.native="onBlur"></base-input>
Copy the code
Vue.component('base-input', {
  template: `  `
})
Copy the code

This is useful in some cases, but not a good idea when trying to listen for an element like , such as a

component that might have been refactored, such as:

<label>Name:<input type="text">
</label>
Copy the code

As you can see, the root element of the component is actually an element, and the parent. Native listener will silently fail. It does not generate any errors, but the onFocus handler will not be called as expected.

To solve this problem, Vue provides a $Listeners property, which is an object that contains all listeners working on the component. Such as:

{
  focus: function (event) { / *... * / }
  blur: function (event) { / *... * /}},Copy the code

With the $Listeners attribute, we can use v-on=”$Listeners “to direct all listeners to specific child elements of the component, such as:

Vue.component('base-input', {
  template: Name:  
})
Copy the code

Use v-Models on components

V-model directives can also be used on components due to custom events.

Using the V-mode directive on an input element binds the value feature and listens for input events:

<input v-model="searchText" />
Copy the code

Is equivalent to:

<input
  :value="searchText"
  @input="searchText = $event.target.value"
>
Copy the code

When applying the V-model directive to a component:

<base-input v-model="searchText" /> 
Copy the code

Is equivalent to:

<base-input
  :value="searchText"
  @input="searchText = $event"
/>
Copy the code

As with the input element, using the V-model directive on the component binds the value property and listens for the input event.

So, in order for the V-Model directive to work properly, the in this component must:

  • Bind its value feature to a prop called Value
  • Throws the new value through a custom input event when its input event is raised

Such as:

Vue.component('base-input', {
  props: ['value'].template: `  `
}) 
Copy the code

Once this is done, the V-Model is ready to work on this component.

As we learned above, a V-Model on a component makes use of a prop named Value and an event named input by default, but input controls like checkboxes, checkboxes, and so on May use the value feature for different purposes. In such cases, we can use the Model option to avoid collisions:

Vue.component('base-checkbox', {
  model: {
    prop: 'checked'.event: 'change'
  },
  props: {
    checked: Boolean
  },
  template: `  `
})
Copy the code

Using components:

<base-checkbox v-model="lovingVue"></base-checkbox>
Copy the code

The value of lovingVue here will be passed to the prop named Checked. The lovingVue property is updated when a change event is fired with a new value.

The sync modifier

In addition to using the V-model directive for bidirectional binding of components to external data, we can also use the modifier.sync of the V-bind directive.

So how do you do that? Recall that the v-model directive is not used to implement bidirectional data binding for components:

<base-input :value="searchText" @input="searchText = $event"></base-input>
Copy the code
Vue.component('base-input', {
  props: ['value'].template: `  `
}) 
Copy the code

Then, we can also try to change the name of the listening event, such as:

<base-input :value="searchText" @update:value="searchText = $event"></base-input>
Copy the code
Vue.component('base-input', {
  props: ['value'].template: `  `
}) 
Copy the code

This also allows for bidirectional data binding, so what does it have to do with the.sync modifier? At this point, we modify the code:

<base-input :value.sync="searchText"></base-input>
Copy the code
Vue.component('base-input', {
  props: ['value'].template: `  `
}) 
Copy the code

So, the.sync modifier is essentially a syntactic sugar, used on components:

<base-input :value.sync="searchText"></base-input>
Copy the code

Is equivalent to:

<base-input
  :value="searchText"
  @update:value="searchText = $event"
/>
Copy the code

We can also use the sync modifier with v-bind when setting multiple prop objects simultaneously:

<base-input v-bind.sync="obj"></base-input>
Copy the code

Note:

  • The V-bind directive, with the.sync modifier, can only provide the name of the property you want to bind,Can’tUsed with expressions such as::title.sync="1+1", this operation is invalid
  • willv-bind.syncUsed on a literal object, as inv-bind.sync="{ title: 'haha' }", will not work because there are many edge cases to consider when parsing a complex expression like this.

v-model VS .sync

To be clear, the.sync syntax was already supported in Vue 1.x, but.sync could change the state of the parent component entirely in the child component, making the entire state change difficult to trace, so this feature was removed in 2.0. Then, with Vue2.3,.sync came back, but now, sync is completely a syntactic sugar. It is implemented in the same way as V-Model, and it is not easy to break the existing data model, so it is safer and more convenient to use.

  • Both are used to implement two-way data transfer, implemented by syntax sugar, and finally passedprop + The eventTo get the job done.
  • Vue 1.x’s.sync and V-model are two completely different things. After Vue 2.3, it can be understood as a single feature with slightly different usage scenarios
  • When a component exposes only one controlled state to the outside world, and all conform to uniform standards, we will use the V-Model to deal with it. .sync is more flexible and can be used whenever two-way data transfer is required.

Component_slot

As with HTML elements, we often need to pass content to a component like this:

<my-cmp>
  Something bad happened.
</my-cmp>
Copy the code

If there is a need, we can do it through slots.

Content of the slot

With slots, we can compose components like this:

<my-cmp>What is written in the component tag structure</my-cmp>
Copy the code

Component templates can be written as:

<div>
  <slot></slot>
</div>
Copy the code

When the component is rendered,
will be replaced with “what is written in the component tag structure”. Slots can contain any template code, including HTML and other components. If < my-cMP > does not contain a

element, anything between the component’s start tag and end tag is discarded.

Compile scope

When using data in slots:

<my-cmp>This is the data used in the slot: {{user}}</my-cmp>
Copy the code

This slot has access to the same instance attributes as the rest of the template, the same “scope,” but not to the scope of < my-cMP >. Remember: everything in the parent template is compiled in the parent scope; Everything in a subtemplate is compiled in a subscope.

Backup the content

We can set the default slot, which will be rendered when no content is provided, for example, in the < my-cMP > component:

Vue.compopnent('my-cmp', {
  template: `  `
})
Copy the code

We want the

Vue.compopnent('my-cmp', {
  template: `  `
})
Copy the code

When using a component that does not provide slots, backup content will be rendered. If slots are provided, backup content will be replaced.

A named slot

Sometimes we need multiple slots, such as the < my-CMP > component:

Vue.compopnent('my-cmp', {
  template: ` <div class="container"> <header> <! --> </header> <main> <! </main> <footer> <! -- footer --> </footer> </div>
})
Copy the code

At this point, you can use a special feature on the

element: name. Use this feature to define additional slots:

Vue.compopnent('my-cmp', {
  template: ` 
      
`
}) Copy the code

A

exit without a name carries the implied name “default”. When feeding content to a named slot, we can use the V-slot directive on a

<my-cmp>
  <template v-slot:header>
    <h1>The head</h1>
  </template>

  <p>content</p>
  <p>content</p>

  <template v-slot:footer>
    <p>At the bottom of the</p>
  </template>
</my-cmp>
Copy the code

Now everything in the

<my-cmp>
  <template v-slot:header>
    <h1>The head</h1>
  </template>

  <template v-slot:default>
    <p>content</p>
    <p>content</p>
  </template>

  <template v-slot:footer>
    <p>At the bottom of the</p>
  </template>
</my-cmp>
Copy the code

Note: V-slot can only be added in<template>With one exception.

Scope slot

To enable slot content to access the child component’s data, we can bind the child component’s data as a feature of the

element:

Vue.component('my-cmp', {
  data () {
    return {
      user: {
        name: 'very'.age: 18,}}},template: `  
         `,})Copy the code

Features bound to

elements are called slot prop. So in the parent scope, we can give v-slot a value to define the name of the slot prop we provide:

<div id="app">
  <my-cmp>
    <template v-slot:default="slotProps">
      {{ slotProps.user.name }}
    </template>
  </my-cmp>
</div>
Copy the code

Exclusive default slot abbreviation syntax

A component’s label can be used as a template for a slot when only the default slot is supplied. In this case, v-slot can be used directly on the component:

<my-cmp v-slot:default="slotProps">
  {{ slotProps.user.name }}
</my-cmp>
Copy the code

It could also be simpler:

<my-cmp v-slot="slotProps">
  {{ slotProps.user.name }}
</my-cmp>
Copy the code

Note: The default slot abbreviation syntax should not be mixed with named slots, as it would result in undefined scope.

<! -- invalid, will result in warning -->
<my-cmp v-slot="slotProps">
  {{ slotProps.user.name }}
  <template v-slot:other="otherSlotProps">SlotProps is not legal here</template>
</my-cmp>
Copy the code

Whenever multiple slots are present, you need to use the full

Deconstructing slot Prop

We can use deconstruction to pass in a specific slot prop, as in:

<my-cmp v-slot="{ user }">
  {{ user.name }}
</my-cmp>
Copy the code

This makes the template more concise, especially if you provide multiple prop for slots. There are other possibilities, such as prop renaming:

<my-cmp v-slot="{ user: person }">
  {{ person.name }}
</my-cmp>
Copy the code

And custom backup content, effective when slot prop is undefined:

<my-cmp v-slot="{ user = { name: 'Guest' } }">
  {{ user.name }}
</my-cmp>
Copy the code

Dynamic slot name

Vue server new

<my-cmp>
  <template v-slot:[dynamicSlotName] >.</template>
</my-cmp>
Copy the code

An abbreviation for named slot

Vue server new

Like V-ON and V-bind, v-slot has an abbreviation. Replace v-slot: with #.

<my-cmp>
  <template #header>
    <h1>The head</h1>
  </template>

  <template #default>
    <p>content</p>
    <p>content</p>
  </template>

  <template #footer>
    <p>At the bottom of the</p>
  </template>
</my-cmp>
Copy the code

Of course, like other instructions, this abbreviation is only available if it has arguments.

Obsolete grammar

A named slot with the slot feature

Deprecated since 2.6.0

<my-cmp>
  <template slot="header">
    <h1>The head</h1>
  </template>

  <template>
    <p>content</p>
    <p>content</p>
  </template>

  <template slot="footer">
    <p>At the bottom of the</p>
  </template>
</my-cmp>
Copy the code

Scope slot with slot-scope feature

Deprecated since 2.6.0

<my-cmp>
  <template slot="default" slot-scope="slotProps">
    {{ slotProps.user.name }}
  </template>
</my-cmp>
Copy the code

Component_dynamic Component

The basic use

When we are in a multi-tab interface, it is very useful to dynamically switch between different components.

<div id="app">
  <button 
    v-for="page in pages"
    @click="pageCmp = page.cmp"
    :key="page.id"
  >{{ page.name }}</button>
  <component :is="pageCmp"></component>
</div>
Copy the code
Vue.component('base-post', {
  data () {
    return {
      postCmp: ' '.posts: [{title: "Heading 1".content: { template: Content of ` < div > 1 < / div > `}, id: 11}, 
        { title: "Heading 2".content: { template: Content of ` < div > 2 < / div > `}, id: 12}, 
        { title: "Title".content: { template: Content of ` < div > 3 < / div > `}, id: 13}, 
      ],
    }
  },
  mounted () {
    this.postCmp = this.posts[0].content;
  },
  template: ` 
      
`
}) Vue.component('base-more', { template: '
More
}) const vm = new Vue({ el: '#app'.data: { pages: [{name: 'blog'.cmp: 'base-post'.id: 0}, { name: 'more'.cmp: 'base-more'.id: 1}].pageCmp: 'base-post'}})Copy the code

By the above method, we can realize the switch between components, to be able to notice is that every time a switch label, will create a new component instance, to create the dynamic component in more cases are very useful, but in this case, we will be more hope what label component instance can be created for the first time in their cache down. To solve this problem, we can wrap the dynamic component with a

element. Such as:

<! -- Deactivated components will be cached! -->
<keep-alive>
  <component v-bind:is="pageCmp"></component>
</keep-alive>
Copy the code

Note:

requires that the component being switched to have its own name, either through the component’s name option or through local/global registration.

keep-alive


when wrapping dynamic components, inactive component instances are cached rather than destroyed.

is an abstract component: it does not render a DOM element on its own and does not appear in the parent component chain. When a component is switched within

, its activated and deactivated lifecycle hook functions are executed accordingly.


activated & deactivated

Activated: activated when the keep-alive component is activated. Deactivated: Activated when the keep-alive component is disabled.

Component _ Handles boundary cases

The next steps are all related to handling boundary cases, special cases that require minor adjustments to the Vue’s rules. It is important to note that these features have disadvantages or dangerous scenarios.

Access elements & components

In most cases, it’s best not to touch inside another component instance or manually manipulate DOM elements. But there are situations where it’s appropriate to do these things.

Accessing the root instance

Within each child component, the root instance can be accessed through $root.

// Vue root instance
new Vue({
  data: {
    foo: 1
  },
  computed: {
    bar () { / *... * /}},methods: {
    baz () { / *... * /}}})Copy the code

All child components can access or use this instance as a global store.

// Get the data for the root component
this.$root.foo

// Write data to the root component
this.$root.foo = 2

// Access the computed properties of the root component
this.$root.bar

// Call the root component's method
this.$root.baz()
Copy the code

It’s handy to use in demos or in small applications with a few components. But using it in large applications can be complicated. So again, we’ll use Vuex (which we’ll learn later) to manage the state of our applications.

Access the parent component instance

In the child component, the parent component instance can be accessed through $parent. This is an alternative to passing data to child components as prop.

Such as:

<cmp-parent>
  <cmp-a></cmp-a>
</cmp-parent>
Copy the code

If cmp-parent needs to share a property, all of its children need to access the share property, in which case Cmp-a can access the share through this.$parent-share.

However, components built through this pattern are still prone to internal problems. For example, we nested a child component cMP-b in cMP-A, as in:

<cmp-parent>
  <cmp-a>
    <cmp-b></cmp-b>
  </cmp-a>
</cmp-parent>
Copy the code

If there is no share in cMP-b, check whether there is share in cMP-B. If there is no share in CMP-B, check whether there is share in cMP-B.

var share = this.$parent.share || this.$parent.$parent.share;
Copy the code

This quickly gets out of hand: touching the parent component makes the application harder to debug and understand, especially when changing the parent data, and after a while it’s hard to figure out where the change originated.

Dependency injection can be used to solve these situations.

Dependency injection

In the example above, the $parent attribute doesn’t scale well to deeper nested components. This is where dependency injection comes in, using two new instance options: provide and Inject.

The provide option allows us to specify the data/methods we want to provide to future generations of components, for example:

Vue.component('cmp-parent', {
  provide () {
    return {
      share: this.share,
    }
  },
  data () {
    return {
      share: 'share',}},template: `<div>cmp-parent</div>`
})
Copy the code

Then in any descendant component, we can use the Inject option to accept properties that specify what we want to add to the instance.

Vue.component('cmp-a', {
  inject: ['share'].template: `<div>cmp-a</div>`
})
Copy the code

In contrast to $parent, this usage allows us to access share in any descendant component without exposing the entire CMP-parent instance. This allows us to better continue developing the component without worrying that we might change/remove some of the dependencies of the child component. And the interfaces between these components are always clearly defined, just like props.

In fact, you can think of dependency injection as part of a “prop that works on a large scale,” except for:

  • The ancestor component does not need to know which descendant components use the properties it provides
  • Descendant components don’t need to know where the injected property came from

However, di has a downside. It couples the components in your application to their current organization, making refactoring more difficult. The supplied properties are also non-reactive. This is for design reasons, as it is not good enough to use them to create a centralized scale of data as it is to use $root to do so. If the attribute you want to share is specific to your application rather than generic, or if you want to update the data provided in the ancestor component, this means you may need to switch to a true state management scheme like Vuex.

Access child component instances or child elements

In spite of prop and events, there may be times when we need to access a child component directly in JS. In this case, we can use the ref feature to give the child an ID reference:

<my-cmp ref="cmp"></my-cmp>
Copy the code

This allows the

instance to be accessed via this.$refs.cmp. Ref can also access specified DOM elements, such as:

<input ref="input" />
Copy the code

So, we can query the DOM element by calling this.$refs.input.

When ref and V-for are used together, the resulting reference will be an array containing these subcomponents of the corresponding data source.

Note: $refs only take effect after the component is rendered, and they are not reactive. You should avoid accessing $refs in templates or calculated properties.

Programmatic event listeners

In addition to V-ON and $EMIT, the Vue instance provides other methods in its event interface. We can:

  • Listen for an event with $on(eventName, eventHandler)
  • Listen for one event at a time with $once(eventName, eventHandler)
  • Stop listening for an event with $off(eventName, eventHandler)

These methods are not typically used, but they can be useful when you need to manually listen for events on a component instance.

For example, sometimes we integrate third-party libraries with components:

Vue.component('my-cmp', {
  // Attach the date picker to an input box once
  // It will be mounted to the DOM.
  mounted () {
    // 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 () {
    this.picked.destroy();
  },
  template: ` < div > < input type = "text" ref = "input" / > < button @ click = "$destroy ()" > destroying components < / button > < / div > `,})Copy the code

There are two potential problems with the above approach:

  • It needs to store the picker in the component instance, preferably accessible only by lifecycle hooks if possible. 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.

So, we can solve these two problems with programmatic listeners:

Vue.component('my-cmp', {
  mounted () {
    var picker = new Pikaday({
      field: this.$refs.input,
      format: 'YYYY-MM-DD',})this.$once('hook:beforeDestroy'.() = >{ picker.destroy(); })},template: ` < div > < input type = "text" ref = "input" / > < button @ click = "$destroy ()" > destroying components < / button > < / div > `
})
Copy the code

Using this strategy, we can also make multiple input field elements use different pikaday:

Vue.component('my-cmp', {
  mounted () {
    this.datePicker('inputA');
    this.datePicker('inputB');
  },
  methods: {
    datePicker (refName) {
      var picker = new Pikaday({
        field: this.$refs[refName],
        format: 'YYYY-MM-DD',})this.$once('hook:beforeDestroy'.() = >{ picker.destroy(); })}},template: 
      
`
}) Copy the code

Note, though, that if you find yourself having to do a lot of setup and cleanup in a single component, the best approach is usually to create more modular components. In this case, we recommend creating a reusable

component.

A circular reference

Recursive components

Components can call themselves in their own templates, but they can only do this with the name option:

name: 'my-cmp'
Copy the code

However, when a component is registered globally with Vue.com Ponent, the global ID is automatically set to the component’s name option.

Vue.component('my-cmp', { / * * * /});
Copy the code

A recursive component can lead to an infinite loop with the slightest error:

name: 'my-cmp'.template: `<div><my-cmp /></div>`
Copy the code

A component like the one above will cause a “Max Stack Size exceeded” error, so make sure the recursive call is conditional (for example, using a V-if that will eventually get false).

Circular references between components

Sometimes, when building components, there are descendants/ancestors of each other:

Vue.component('cmp-a', {
  template: ` 
      
`
}) Copy the code
Vue.component('cmp-b', {
  template: ` 
      
`
}) Copy the code

At this point, we are using a globally registered component and there is no paradox, but there would be if we were using a local component.

Error: Failed to mount Component: template or render function not defined

The modular system finds that it needs A, but first A depends on B, but B depends on A, but A depends on B, and so on. It becomes a loop, not knowing how to fully resolve one component without going through the other. To solve this problem, we need to give the module system A point: “A needs B anyway, but we don’t need to parse B first.”

beforeCreate () {
  this.$options.components.CmpB = require('./tree-folder-contents.vue').default;
}
Copy the code

Or, when registering components locally, you can use webpack’s asynchronous import:

components: {
  CmpB: () = > import('./tree-folder-contents.vue')}Copy the code

Alternatives to template definitions

Inline template

When using a component, write the special feature inline-template to treat the contents directly as templates instead of being distributed (slots).

<my-cmp inline-template>
  <div>
    <p>These are compiled as the component's own template.</p>
    <p>Not parent's transclusion content.</p>
  </div>
</my-cmp>
Copy the code

However, inline-template makes the scope of the template more difficult to understand. As a best practice, use the template option within the component or a

X-Template

Another way to define a template is to give it a text/x-template type in a

<script 
  type="text/x-template" 
  id="hello-world-template"
>
  <p>Hello hello hello</p>
</script>
Copy the code
Vue.component('hello-world', {
  template: '#hello-world-template'
})
Copy the code

These can be used for very large demos or very small applications, but avoid them otherwise because they separate the template from the rest of the component’s definition.

Control update

Forced to update

When you change some data and the page is not re-rendered, you can call $forceUpdate to do a forced update.

$forceUpdate $forceUpdate $forceUpdate $forceUpdate $forceUpdate $forceUpdate $forceUpdate $forceUpdate $forceUpdate

Create low-overhead static components with V-once

Rendering normal HTML elements is very fast in Vue, but sometimes you may have a component that contains a lot of static content. In this case, you can add the V-once feature to the root element to ensure that the content is evaluated only once and then cached, like this:

Vue.component('terms-of-service', {
  template: ` 
      

Terms of Service

... a lot of static content ...
`
}) Copy the code

Try not to overuse this pattern. It’s handy in rare cases when you need to render a lot of static content, and it’s not necessary unless you’re very careful about rendering slowly — plus it can cause a lot of confusion later on. For example, assuming another developer is unfamiliar with V-once or missed it in the template, they might spend hours trying to figure out why the template isn’t updating properly.

Component_Communication

prop

When a parent component passes data to a child component, it can pass it through a feature.

This is recommended for parent -> child communication.

$emit

When a child component passes data to its parent, it fires an event that throws data.

This is recommended for child -> parent communication.

v-model

.sync

$attrs

When an ancestor component passes data to a descendant component, it can use $attrs pass.

Demo or small projects can use $attrs for data transfer, medium and large projects are not recommended, data flow can become difficult to understand.

The real purpose of $attrs is to write base components that give some DOM elements non-prop features.

$listeners

You can execute the ancestor component’s functions in descendant components to implement data passing.

$Listeners can be used for demos or small projects, but are not recommended for medium and large projects and may make data streams difficult to understand.

The real purpose of $Listeners is to point all event listeners to a specific child of the component.

$root

The data of the root instance can be accessed in the child component.

This is handy for demos or very small applications with a few components. Medium and large projects are not applicable. Can make the application difficult to debug and understand.

$parent

The parent instance’s data can be accessed in the child component.

This is handy for demos or very small applications with a few components. Medium and large projects are not applicable. Can make the application difficult to debug and understand.

$children

The child instance’s data can be accessed in the parent component.

This is handy for demos or very small applications with a few components. Medium and large projects are not applicable. Can make the application difficult to debug and understand.

ref

The child instance’s data can be accessed in the parent component.

$refs only take effect after the component is rendered, and they are not reactive and are suitable for demo or small projects.

provide & inject

Ancestor components provide data and descendant components inject as needed.

It can couple the blocking methods of components together and make component refactoring difficult and maintenance difficult. Not recommended for medium to large projects, suitable for writing small components.

eventBus

Vue.prototype.$bus = new Vue();
Copy the code
Vue.component('cmp-a', {
  data () {
    return {
      a: 'a'}},methods: {
    onClick () {
      this.$bus.$on('click'.this.a)
    }
  },
  template: ` < div > < button @ click = "onClick" > click < / button > < / div > `,})Copy the code
Vue.component('cmp-a', {
  mounted () {
    this.$bus.$on('click'.data= > {
      console.log(data); })},template: ` 
      
b
`
,})Copy the code

You can use this approach when non-parent components communicate, but only for small projects. When used in medium and large projects, the code will be chaotic and difficult to maintain.

Vuex

State management, medium and large projects are strongly recommended to use this method, learn later ~

Mixed with

basis

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.

var minxin = {
  created () {
    this.hello();
  },
  methods: {
    hello () {
      console.log('Hello, I'm a function mixed in');
    },
  }
}

Vue.component('my-cmp', {
  mixins: [mixin],
  template: ` 
      
xx
`
}) Copy the code

Option to merge

When components and mixins have options with the same name, those options are “merged” in the appropriate way.

Merge data with component data in preference:

var mixin = {
  data () {
    return {
      msg: 'hello',}}}new Vue({
  mixins: [mixin],
  data: {
    msg: 'goodbye',},created: function () {
    console.log(this.msg)
})
Copy the code

Merge hook functions that merge into an array. Call the hooks embedded in the object first, then call the component’s own hooks.

var mixin = {
  created () {
    console.log('Mixin object hook')}}new Vue({
  el: '#app'.mixins: [mixin],
  created () {
    console.log('Component hook')}})Copy the code

Options that merge values into objects, such as methods, components, and so on, will be merged into the same object. When two object key names conflict, the component object’s key-value pair is taken.

With global

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.

// Inject a handler for the custom option 'myOption'.
Vue.mixin({
  created () {
    var myOption = this.$options.myOption
    if (myOption) {
      console.log(myOption)
    }
  }
})

new Vue({
  myOption: 'hello! '
})
Copy the code

Use global mixin with caution, as it affects each individually created Vue instance (including third-party components). In most cases, this should only apply to custom options.

Custom instruction

Introduction to the

We can write our own custom instructions to manipulate DOM elements for code reuse purposes. Note that in Vue, 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.

Global registration directive:

Vue.directive('focus', {/ * * * /})
Copy the code

Local registration instruction

const vm = new Vue({
  el: '#app'.directives: {
    focus: {/ * * * /}}})Copy the code

Use:

<input v-focus></input>
Copy the code

For example, write an autofocus input box:

Vue.directive('focus', {
  // executes when the bound element is inserted into the DOM
  inserted: function (el) { el.focus(); }})Copy the code

At this point, auto-focus can be achieved using the V-focus directive on the input element.

Hook function

The custom directive object provides optional hook functions for use.

bind

Only called 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 the 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.

componentUpdated

Called after the VNode of the component where the directive resides and its child VNodes are all updated.

unbind

Called only once, when the directive is unbound from the element (the bound Dom element is removed by Vue).

Hook function arguments

  • El: The element bound by the directive that can be used to manipulate the DOM directly.
  • Binding: Object containing the following properties:
    • Name: indicates the command name, excluding the V – prefix.
    • Value: specifies the binding value of the directive. For example, v-my-directive=”1 + 1″, the binding value is 2.
    • OldValue: The previous value of the directive binding, available only in the UPDATE and componentUpdated hooks. Available regardless of whether the value changes.
    • Expression: command expression in the form of a string. For example, if v-my-directive=”1 + 1″, the expression is “1 + 1”.
    • Arg: Optional parameter passed to the instruction. For example, v-my-directive:foo, the parameter is “foo”.
    • Modifiers: An object that contains modifiers. For example, in v-my-directive.foo.bar, the modifier object is {foo: true, bar: true}.
  • Vnode: virtual node generated by Vue compilation.
  • OldVnode: Last virtual node, available only in update and componentUpdated hooks.

practice

Simulation of v – show

// Bind to false, display to none, true, display to ""
Vue.directive('myshow', {
  bind (el, binding, vnode, oldVnode) {
    var display = binding.value ? ' ' : 'none';
    el.style.display = display;
  },
  update (el, binding, vnode, oldVnode) {
    var display = binding.value ? ' ' : 'none'; el.style.display = display; }})Copy the code

Simulation of v – model

// 1. Set value to the element based on the bound data
// 2. Change the value of the data when the input event is triggered
// 3. Synchronize the input value after changing the data
Vue.directive('mymodel', {
  bind (el, binding, vnode) {
    const vm = vnode.context;
    const { value, expression } = binding;
    el.value = value;

    el.oninput = function (e) {
      const inputVal = el.value;
      vm[expression] = inputVal;
    }
  },
  update (el, binding) {
    const{ value } = binding; el.value = value; }})Copy the code

Write a V-slice

Vue.directive('slice', {
  bind (el, binding, vnode) {
    const vm = vnode.context;
    let { value, expression, arg, modifiers } = binding;

    if(modifiers.number) {
      value = value.replace(/[^0-9]/g.' ');
    }


    el.value = value.slice(0, arg);
    vm[expression] = value.slice(0, arg);

    el.oninput = function (e) {
      let inputVal = el.value;

      if(modifiers.number) {
        inputVal = inputVal.replace(/[^0-9]/g.' ');
      }

      el.value = inputVal.slice(0, arg);
      vm[expression] = inputVal.slice(0, arg);
    }
  },
  update (el, binding, vnode) {
    const vm = vnode.context;
    let { value, arg, expression, modifiers } = binding;

    if(modifiers.number) {
      value = value.replace(/[^0-9]/g.' ');
    }

    el.value = value.slice(0, arg);
    vm[expression] = value.slice(0, arg); }})Copy the code

Dynamic instruction parameter

The parameters of an instruction can be dynamic. For example, v-directive:[arguments]=”value, the argument argument can be updated based on component instance data.

Rewrite the v – slice

Vue.directive('slice', {
  bind (el, binding, vnode) {
    const vm = vnode.context;
    let { value, expression, arg, modifiers } = binding;

    if(modifiers.number) {
      value = value.replace(/[^0-9]/g.' ');
    }


    el.value = value.slice(0, arg);
    vm[expression] = value.slice(0, arg);

    el.oninput = function (e) {
      let inputVal = el.value;

      if(modifiers.number) {
        inputVal = inputVal.replace(/[^0-9]/g.' ');
      }

      el.value = inputVal.slice(0, arg);
      vm[expression] = inputVal.slice(0, arg);
    }
  },
  update (el, binding, vnode) {
    const vm = vnode.context;
    let { value, arg, expression, modifiers } = binding;
    
    if(modifiers.number) {
      value = value.replace(/[^0-9]/g.' ');
    }

    el.value = value.slice(0, arg);
    vm[expression] = value.slice(0, arg);

    el.oninput = function (e) {
      let inputVal = el.value;

      if(modifiers.number) {
        inputVal = inputVal.replace(/[^0-9]/g.' ');
      }

      el.value = inputVal.slice(0, arg);
      vm[expression] = inputVal.slice(0, arg); }}})Copy the code

Function shorthand

When you want to trigger the same behavior in bind and update, regardless of the other hooks, you can write it as a function:

Vue.directive('myshow'.(el, binding) = > {
  const { value } = binding;
  const display = value ? ' ' : 'none';
  el.style.display = display;
})
Copy the code
Vue.directive('slice'.(el, binding, vnode) = > {
  const vm = vnode.context;
  let { value, expression, arg, modifiers } = binding;

  if(modifiers.number) {
    value = value.replace(/[^0-9]/g.' ');
  }


  el.value = value.slice(0, arg);
  vm[expression] = value.slice(0, arg);

  el.oninput = function (e) {
    let inputVal = el.value;

    if(modifiers.number) {
      inputVal = inputVal.replace(/[^0-9]/g.' ');
    }

    el.value = inputVal.slice(0, arg);
    vm[expression] = inputVal.slice(0, arg); }})Copy the code

Object literals

If a custom directive requires multiple values, you can pass in a JS object literal. The instruction function can accept all valid JS expressions.

<div v-demo="{ color: 'white', text: 'hello!' }"></div>
Copy the code
Vue.directive('demo'.function (el, binding) {
  console.log(binding.value.color) // => "white"
  console.log(binding.value.text)  // => "hello!"
})
Copy the code

The filter

Custom filters for some common text formatting.

Filters are available in two places: double curly brace interpolation and v-bind expressions, which are added to the end of JS expressions and are represented by the “pipe” symbol:

<! -- In double braces -->
{{ message | filter }}

<!-- 在 v-bind 中 -->
<div v-bind:id="id | filter"></div>
Copy the code

Defining filters

Global filters:

Vue.filter('filter'.value= > {})
Copy the code

Local filter:

filter () {
  return xxx;
}
Copy the code

parameter

When the filter form for MSG | filter, filter filter receives a parameter, parameters for MSG.

When the filter form for MSG | filter (‘ a ‘), the filter filter receives two parameters, parameters for MSG, ‘a’

Filter series

{{ msg | filterA | filterB }}
Copy the code

In this example, the filterA argument is MSG and the filterB argument is filterA.

practice

Uppercase

{{ content | capitalize }}
Copy the code
Vue.filter('capitalize'.value= > {
  if(! value) {return };
  return value.charAt(0).toUpperCase() + value.slice(1);
})
Copy the code

Put a comma between the numbers

{{ money | toMoney }}
Copy the code
Vue.filter('toMoney'.value= > {
  if(! value) {return };
  return value.toLocaleString();
});
Copy the code

Number add text “ten thousand”

{{ likes | addWord }}
Copy the code
Vue.filter('addWord'.value= > {
  if(! value) {return };

  if(value > 10000) {
    return ( value / 10000).toFixed(1) + '万';
  }
  return value;
});
Copy the code

Erection of scaffolding

Install @ vue/cli

The node version must be >8.9. 8.11.0 + is recommended.

About older versions: If you have installed an older version of VUE-CLI (1.x or 2.x) globally before this, you need to uninstall it first. Run: NPM uninstall vue-cli -g or YARN Global Remove VUe-cli.

Installation:

npm install -g @vue/cli
# OR
yarn global add @vue/cli
Copy the code

After installation, the vue commands can be accessed from the command line.

Check whether the version is correct:

vue --version
Copy the code

Rapid prototyping

Installation:

npm install -g @vue/cli-service-global
# OR
yarn global add @vue/cli-service-global
Copy the code

Install the vscode plug-in

Name: Vetur. Use to highlight. Vue file code

Exercise _ Tree components

Data:

data: [
  {
    label: "Level 1".children: [{label: "The secondary 1-1".children: [{label: "Triple the 1-1-1"}}]}, {label: "Level 2".children: [{label: "Secondary 2-1".children: [{label: "Triple the 2-1-1"}]}, {label: "Secondary 2-2".children: [{label: "Triple the 2-2-1"}}]}, {label: "Level 3".children: [{label: "Secondary 3-1".children: [{label: "Triple the 3-1-1"}]}, {label: "Secondary 3-2".children: [{label: "Triple the 3-2-1"}]}]Copy the code

Use scaffolding to build projects

Pull 2.x template (old version)

npm install -g @vue/cli-init
# `vue init`The operation effect will follow`[email protected]`Same vue init webpack my-projectCopy the code

Rendering function

basis

When we need the programming power of JavaScript, we can take advantage of rendering functions. Renderers are closer to the compiler than templates.

For example, we want to generate some headings:

<h1>Hello world!</h1>
Copy the code

If we follow the previous pattern, there will be a lot of redundancy in the template. If you use render functions at this point, the code will be much cleaner to write.

props: {
  level: {
    type: Number.required: true}},render: function (createElement) {
  return createElement(
    'h' + this.level,   // Label name
    this.$slots.default // Array of child nodes)},Copy the code

Nodes, trees, and virtual DOM

Before diving into rendering functions, let’s take a look at how browsers work. For example, the following HTML:

<div>
  <h1>My title</h1>
  Some text content
  <! --TODO: Add tagline -->
</div>
Copy the code

When the browser reads the code, it creates oneThe DOM node treeTo keep track of everything, just as you would draw a family tree to track the progress of family members. The DOM node tree corresponding to the above HTML is shown in the figure below:

Each element is a node. Each paragraph of text is also a node. Even comments are nodes. A node is a part of a page. Like a family tree, each node can have child nodes.

Updating all of these nodes efficiently is difficult, but fortunately we don’t need to do this manually. Just tell Vue what HTML you want on the page, for example in a template:

<h1>{{ blogTitle }}</h1>
Copy the code

Or in a render function:

render: function (createElement) {
  return createElement('h1'.this.blogTitle)
}
Copy the code

In both cases, Vue automatically keeps the page updated even if the blogTitle changes.

Virtual DOM

Vue tracks how it changes the real DOM by creating a virtual DOM. Such as:

return createElement('h1'.this.blogTitle);
Copy the code

What does createElement return? It does not return an actual DOM element. A more accurate name might be createNodeDescription, because it contains information that tells the Vue what nodes need to be rendered on the page, including descriptions of their children. We describe such a node as a “virtual node”, often abbreviated to “VNode”. “Virtual DOM” is the name we use for the entire VNode tree built from the Vue component tree.

CreateElement method parameter

CreateElement accepts the following parameters:

CreateElement (tag name (required), data object (optional) that corresponds to attributes in the template, child virtual node (optional));Copy the code

Deep data object

{
  // The same API as' v-bind:class 'accepts a string, object, or array of strings and objects
  class: {
    foo: true.bar: false
  },
  // The same API as' v-bind:style 'accepts a string, object, or array of objects
  style: {
    color: 'red'.fontSize: '14px',},// Plain HTML attribute
  attrs: {
    id: 'foo',},/ / component prop
  props: {
    myProp: 'bar',},/ / DOM attributes
  domProps: {
    innerHTML: 'baz',},// Event listeners, modifiers such as "V-on :keyup.enter" are not supported
  on: {
    click: this.onClick
  },
  // Used only by components to listen for native events, not events that are emitted by components using VM. $emit.
  nativeOn: {
    click: this.nativeClickHandler
  },
  // Custom instruction. Note that 'oldValue' in 'binding' cannot be assigned because Vue has automatically synchronized for you.
  directives: [{name: 'my-custom-directive'.value: '2'.expression: '1 + 1'.arg: 'foo'.modifiers: {
        bar: true}}].// Other special top-level attributes
  key: 'myKey'.ref: 'myRef'.// If the same ref name is applied to multiple elements in the render function, '$refs.myRef' becomes an array.
  refInFor: true
  / / scope slot, format is: {name: props = > VNode | Array < VNode >}
  // If the component is a child of another component, specify a name for the slot
  slot: 'name-of-slot'.scopedSlots: {
    default: props= > createElement('span', props.text)
  },
}
Copy the code

Use JavaScript instead of template functionality

V – if and v – for

Vue’s rendering functions do not provide proprietary alternatives to what can easily be done in native JavaScript. For example, v-if and v-for are used in templates:

<ul v-if="items.length">
  <li v-for="item in items">{{ item }}</li>
</ul>
<p v-else>No items found.</p>
Copy the code

These can be overridden in the render function using JavaScript’s if/else and map:

props: ['items'],
render (createElement) {
  if(items.length) {
    return createElement('ul'.this.items.map(item= > createElement('li', item)))
  } else {
    return createElement('p'.'No items found'); }}Copy the code

v-model

There is no direct correspondence to the V-Model in the rendering function — you have to implement the logic yourself:

<input v-model="value" />
Copy the code
data () {
  return {
    value: 'ceshi',
  }
},
render (createElement) {
  const self = this;
  return createElement('input', {
    attrs: {
      value: self.value
    },
    on: { input (e) { self.value = e.target.value; }}}); },Copy the code

Event & key modifier

For.passive,.capture, and.once event modifiers, Vue provides prefixes that can be used with on:

The modifier The prefix
.passive &
.capture !
.once ~
. The capture. Once or. Once the capture ~!

Such as:

on: {
  '! click': this.doThisInCapturingMode,
  '~keyup': this.doThisOnce,
  '~! mouseover': this.doThisOnceInCapturingMode
}
Copy the code

For all other modifiers, private prefixes are not required, because you can use event methods in event handlers:

The modifier Handle equivalent operations in functions
.stop event.stopPropagation()
.prevent event.preventDefault()
.self if (event.target ! == event.currentTarget) return
Key:.Enter,.13 if (event.keyCode ! == 13) return

For other key modifiers, change 13 to another key code
Modifier keys:.ctrl,.alt,.shift,.meta if (! CtrlKey) return (Change ctrlKey to altKey, shiftKey, or metaKey respectively)

slot

You can access the contents of static slots via this.$slots, where each slot is an array of vNodes:

<div>
  <slot></slot>
</div>
Copy the code
render: function (createElement) {
  return createElement('div'.this.$slots.default)
}
Copy the code

You can also access the scope slots via this.$scopedSlots. Each scope slot is a function that returns a number of VNodes:

<div>
  <slot :text="message"></slot>
</div>
Copy the code
data() {
  return {
    msg: 'hello world',}},render: function (createElement) {
  return createElement('div'[this.$scopedSlots.default({
      text: this.msg
    })
  ])
}
Copy the code

To pass a scope slot to a child component using a render function, you can use the scopedSlots field in the VNode data object:

<div>
  <base-slot v-slot="slotProps">
    {{ slotProps.text }}
  </base-slot>
</div>
Copy the code
render: function (createElement) {
  return createElement('div', [
    createElement('base-slot', {
      // Pass 'scopedSlots' in the data object
      / / format for {name: props = > VNode | Array < VNode >}
      scopedSlots: {
        default: function (props) {
          return createElement('span', props.text)
        }
      }
    })
  ])
}
Copy the code

JSX

Use JSX syntax in Vue. This brings us back to a more template-like syntax.

render () {
  return (
    <h1>This is a headline</h1>)}Copy the code

The interpolation

<div>{ this.value }</div>
Copy the code

instruction

In JSX, some instructions don’t exist, so we can handle them differently.

v-text

<div domPropsTextContent="<p>i am a p</p>"></div>
Copy the code

v-html

<div domPropsInnerHTML="<p>i am a p</p>"></div>
Copy the code

v-show

JSX supports the V-show command:

<div v-show={this.show}></div>
Copy the code

v-if

<! -- v-if -->
{true && <div>div</div>}
{true ? <div>div</div> : <span>span</span>}
Copy the code

v-for

{ [1, 2, 3].map(item => (<div key={item}>{ item }</div>))}
Copy the code

v-on

<button onClick={this.handleClick}>Click on the event</button>
<button on-click={this.handleClick}>Click on the event</button>
<! -- @click.native -->
<cmp-button nativeOnClick={this.handleClick}>Native click events</cmp-button>
<! -- Pass parameters -->
<button onClick={e= >This.handleclick (this.id)}> Pass parameters when the click event is triggered</button>
Copy the code

v-bind

<input value={this.value} />
Copy the code

In JSX you can specify a style class directly using class=”xx”. Inline styles can be written as style=” XXX “.

<div class="a b" style="font-size: 12px;">Content</div>
<div class={{a: true.b: false}} >Content</div>
<div style={{color: 'red', fontSize: '14px'}} >Content</div>
Copy the code

v-model

There are plug-ins that support v-Model, so you can use them directly:

<input type="text" v-model={this.value} />
Copy the code

v-slot

<my-cmp>The default slot<div slot="a">Named Slot A</div>
</my-cmp>
Copy the code

v-pre

v-cloak

v-once

The above three instructions are not commonly used and have no alternative

Ref

<div ref="xxx">xxx</div>
Copy the code

When iterating over an element or component, as in:

[1.2.3].map(item= > <div ref="xx" key={ item} >{ item }</div>)
Copy the code

$refs.xxx does not fetch the expected array value, so refInFor property is set to true:

[1.2.3].map(item= > <div ref="xx" refInFor={true} key={item}>{ item }</div>)
Copy the code

Custom instruction

render () {
  / / 1
  return (
    <input v-splice={{value: this.value.modifiers: {number: true}}} / >
  )

  / / 2
  const directives = [
    { 
      name: 'splice'.value: this.value,  
      modifiers: {number: true}}];return (
    <div {.{ directives} }></div>)}Copy the code

The filter

<! -- Normal use of filters -->
<div>{{ msg | capitalize }}</div>

<! Use filters in JSX -->
<div>{ this.$options.filters('capitalize')(this.msg)}</div>
Copy the code

slot

Template writing:

<! -- Inside component -->
<div class="demo">
  <slot name="header"></slot>
  <slot></slot>
</div>

<! -- When used -->
<my-cmp>
  default
  <template v-slot:header>header</template>
</my-cmp>
Copy the code

JSX writing:

<! -- Inside component -->
<div class="demo">
  { this.$slots.header }
  { this.$slots.default }
</div>

<! -- When used -->
<my-cmp>
  default
  <template slot="header">header</template>
</my-cmp>
Copy the code

Scope slot: template

<! -- Inside component -->
<div class="demo">
  <slot :text="'HelloWorld'"></slot>
</div>

<! -- When used -->
<my-cmp v-slot="slotProps">
  {{ slotProps.text }}
</my-cmp>
Copy the code

JSX writing:

<! -- Inside component -->
<div class="demo">
  { 
    this.$scopedSlots.default({
      text: 'HelloWorld',
    }) 
  }
</div>

<! -- When used -->
<div id="app">
  <base-demo {.{
    scopedSlots: {
      default: props= > props.text
    },
  }}></base-demo>
</div>
Copy the code

Functional component

We can mark a component as functional when it doesn’t need state (that is, reactive data), doesn’t need any lifecycle scenarios, and just accepts props to display the component.

functional: true.Copy the code

Because functional components are just functions, the rendering overhead is much lower.

Prior to 2.3.0, if a functional component wanted to receive a prop, the props option was required. In versions 2.3.0 and above, you can omit the props option, and attributes on all components are automatically resolved implicitly to prop.

To compensate for the lack of instances, the Render function provides a second argument, context, as the context. Context contains the following fields:

  • Props: Objects that provide all prop
  • Slots: a function that returns an object containing all slots (non-scoped)
  • ScopedSlots: (2.6.0+) An object that exposes the incoming scope slots. Normal slots are also exposed as functions.
  • Data: The entire data object passed to the component as the second parameter to createElement
  • Parent: reference to the parent component
  • An object containing all event listeners registered by the parent component for the current component. This is an alias for data.on.
  • Injections: (2.3.0+) If the Inject option is used, then the object contains attributes that should be injected.
  • Children: An array of VNode children, containing all non-scoped and non-named slots.

slots() VS children

Example 1:

<base-level :level="1" @click="handleClick">

  <template v-slot:header>
    <div>I am a head</div>
  </template>

  <div>div</div>
  <p>p</p>
  <template>template</template>

</base-level>
Copy the code

The result of slots() is:

{
  default: [<div>div</div>.<p>p</p>, template],
  header: [<div>I am a head</div>]}Copy the code

The results of children are:

[<div>div</div>, <p>p</p>, template]
Copy the code

Example 2:

<base-level :level="1" @click="handleClick">

  <template v-slot:header>
    <div>I am a head</div>
  </template>

  <template v-slot:default>
    <div>div</div>
  </template>

  <p>p</p>
  <template>template</template>

</base-level>
Copy the code

The result of slots() is:

{
  default: [<div>div</div>].header: [<div>I am a head</div>]}Copy the code

The results of children are:

[<div>div</div>, <p>p</p>, template]
Copy the code

Template-based functional components

In version 2.5.0 and above, if you use single-file components, template-based functional components can be declared as follows:

<template functional>
</template>
Copy the code

Transitions _ single element transitions

Vue provides a variety of different application transitions when inserting, updating, or removing the DOM.

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

The class name of the transition

There are six class switches in the entry/exit transition.


  1. 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 inserted.

  1. V – enter – active:

Defines the state in which the transition takes effect. Applied throughout the transition phase, before the element is inserted and removed after the transition/animation is complete. This class can be used to define the process time, delay, and curve functions that enter the transition.

  1. v-enter-to:

Define the end state of the transition (2.1.8+). The next frame takes effect after the element is inserted (the V-enter is removed at the same time) and removed after the transition/animation is complete.


  1. V – leave:

Define the beginning state of the exit transition. Immediately after the exit transition is triggered, the next frame is removed.

  1. V – 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 functions.

  1. v-leave-to:

Define the end state of the exit transition (2.1.8+). The next frame takes effect after the exit transition is triggered (and the V-leave is deleted at the same time), and is removed after the transition/animation is complete.


Here is:

The class name prefix

  1. Transition has no name feature

The class name is prefixed with v-.

  1. Transition has a name property

If name is fade, the class name prefix is fade-.

CSS animations

CSS animations are used in the same way as CSS, except that the v-Enter class name is not deleted immediately after the DOM is inserted, but when the AnimationEnd event is triggered.

Custom transition class name

We can customize the transition class name using the following attributes:

  • enter-class
  • enter-active-class
  • Enter – to – class (2.1.8 +)
  • leave-class
  • leave-active-class
  • Leave – to – class (2.1.8 +)

They take precedence over normal class names, which is useful when used in conjunction with Vue’s transition system and other third-party CSS animation libraries such as animation.css.

The Animate. CSS website address: daneden. Making. IO/Animate. CSS… NPM install animate. CSS –save

Use both transitions and animations

You can use the type attribute to declare the type that you want Vue to listen to. The type value can be animation or Transition.

When type is not set, both transitions and animationed take longer to end by default.

Explicit transition time

In some cases, Vue can automatically figure out when the transition effect is complete to process the DOM.

Sometimes, however, we set up a series of transition effects, such as nested elements that have transition effects that last longer than the parent element. At this point we can set the Duration property to customize an explicit transition duration in milliseconds:

<transition :duration="1000">.</transition>
Copy the code

You can also customize the duration of entry and removal:

<transition :duration="{ enter: 500, leave: 800 }">.</transition>
Copy the code

Transition from initial rendering

You can use appear Attribute to set the transition of the node in the initial rendering.

As with the in/out transition, you can also customize the CSS class name. Such as:

appear-class="appear-enter"
appear-active-class="appear-enter-active"
appear-to-class="appear-enter-to"
Copy the code

JavaScript hooks

JavaScript hooks can be declared in properties:

<transition
  @before-enter="beforeEnter"
  @enter="enter"
  @after-enter="afterEnter"
  @enter-cancelled="enterCancelled"

  @before-leave="beforeLeave"
  @leave="leave"
  @after-leave="afterLeave"
  @leave-cancelled="leaveCancelled"
>
  <! -... -->
</transition>
Copy the code
  • Before the animation enters, you can set the start style of the element before the animation begins
  • Enter animation in which you can write animation
  • After the after-Enter animation is complete
  • Enter-cancelled Cancels animation

Vue skips CSS detection by adding V-bind: CSS =”false” for elements that use JavaScript transitions only. This also avoids the impact of CSS during the transition.

The Transition component with the appear feature set also has custom JavaScript hooks:

<transition
  appear
  v-on:before-appear="customBeforeAppearHook"
  v-on:appear="customAppearHook"
  v-on:after-appear="customAfterAppearHook"
  v-on:appear-cancelled="customAppearCancelledHook"
>
  <! -... -->
</transition>
Copy the code

Combined with the Velocity. Js

NPM install velocity-animate

Transitions _ multielement transitions

When the label name of the element displayed in the switch is the same, you need to set a different key value for each element; otherwise, Vue will only replace the contents inside the same label for efficiency.

<transition>
  <div v-if="show" key="world">hello world</div>
  <div v-else key="shanshan">hello shanshan</div>
</transition>
Copy the code

In some scenarios, v-if and V-else can be replaced by setting different states for the key of the same element. Such as:

<transition>
  <div :key="keyName">hello {{ key Name}}</div>
</transition>
Copy the code
keyName: 'world'.Copy the code

Transition mode

Vue provides a mode feature, which can apply different modes to multiple elements. The value of mode can be:

  • In-out: The new element transitions first, and then the current element transitions away.
  • Out-in: The current element transitions first, and then the new element transitions in.

Multicomponent transition

We can use dynamic component switching to show different components.

Transitions _ list transitions

When we want to add transition dynamic effects to a list, we can use the
component.

Features of this component:

  • Instead, it will be rendered as a real element: the default is one<span>. You can also use tag attributes for other elements.
  • Transition mode is not available because we no longer switch unique elements between each other.
  • The inner element always needs to provide a unique value for the key attribute.
  • CSS transitioning classes will be applied to internal elements, not the group/container itself.

Sort transitions for lists

The
component provides a new feature: V-move, which is applied during element positioning changes. How to use it? It can be set by the class name:.v-move {}. When the name feature is set to
, for example, name is fade, v-move must be replaced with fade-move when it is used.

Note: When removing a list element, you need to remove the element from the document flow. Otherwise, the element to overflow will remain in the document flow during the remove transition, affecting the move effect of subsequent elements.

Internal implementation: Vue uses a simple animation queue called FLIP that uses transforms to smooth the transition of elements from their previous positions into new ones.

Note that elements using FLIP transitions cannot be set to display: inline. Alternatively, it can be set to display: inline-block or placed in Flex.

Staggered transitions of lists

If you want to apply rich transitions to elements in a list, you can use JavaScript hooks.

Transition _ multiplexing transition

Transitions can be reused through Vue’s component system. To create a reusable transition component, all you need to do is place or as the root component, and then drop any child components into it.

Note: When using functional component reuse transitions, do not set CSS scopes.

Component_asynchronous Component

In the project, some components will not be loaded when entering the first screen for the first time, but will be loaded when certain operations are performed. Therefore, we can set the component to be loaded asynchronously at this time, and when it is used and loaded again, so as to improve the performance of the first screen.

Usage:

components: {
  AsyncCmp: () = > import (url);
}
Copy the code

To combine multiple components that need to be loaded simultaneously into a single file:

components: {
  AsyncCmp1: () = > import(/* webpackChunkName: "async" */ 'url'),
  AsyncCmp2: () = > import(/* webpackChunkName: "async" */ 'url'),}Copy the code

For files loaded asynchronously, el=”prefech” will be set on the link tag. The browser will download the corresponding resources in idle time, and can directly obtain the resources from the cache. The corresponding el=”preload” will download the corresponding resource in time.

VueRouter_ basis

What is routing?

Routing displays different content or pages based on different URLS. In the early days, the back end reloaded the page according to the URL directly, that is, the back end controlled the route. Later the page is more and more complex, the server pressure is more and more, with the advent of Ajax (asynchronous refresh technology), the realization of the page can refresh data without overload, so that the front end can also control url self-management, front-end routing from this.

When to use front-end routing?

Front-end routing is more used in single-page applications, namely SPA(Single Page Web Application). In single-page applications, the results of most pages remain unchanged, but only the use of part of the content is changed.

Install the routing

NPM install vue-router.

Use the routing

JavaScript

  1. The introduction of the routing
import VueRouter from 'vue-router';
Copy the code
  1. Use the routing
Vue.use(VueRouter);
Copy the code
  1. Defining routing Components
// You can import from other files
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
Copy the code
  1. Define the routing
// Each route should map one component
const routes = [
  { path: '/foo'.component: Foo },
  { path: '/bar'.component: Bar }
]
Copy the code
  1. Create a Router instance and then pass itroutesconfiguration
const router = new VueRouter({
  routes 
})
Copy the code
  1. Create and mount the root instance
const app = new Vue({
  router
}).$mount('#app')
Copy the code

html

<div id="app">
  <h1>Hello App!</h1>
  <p>
    <! -- Use the router-link component to navigate.
    <! -- Specifies the link by passing in the 'to' attribute.
    <! -- <router-link> will be rendered as a '<a>' tag by default.
    <router-link to="/foo">Go to Foo</router-link>
    <router-link to="/bar">Go to Bar</router-link>
  </p>
  <! -- Route exit -->
  <! -- Routing matching components will be rendered here -->
  <router-view></router-view>
</div>
Copy the code