Vue

It is described on the website as a progressive framework for building user interfaces;

Progressive framework: the least advocacy, each framework will inevitably have their own characteristics, so that the user has certain requirements, these requirements are advocacy, advocacy is strong and weak, its strength will affect the way of use in business development; Vue has a whole family bucket package, but you can use only part of it, not all of it if you use the core library.

Declarative rendering

Vue.js provides a concise template syntax for declaratively rendering data into the DOM

<div id="app">
	{{ message }}
</div>
Copy the code
const vm = new Vue({
	el: '#app',
	data: {
		message: 'hello vue'
	}
})
Copy the code
  • el: element mount point; Only when new creates the instance; It is available after the instance is mountedvm.$elaccess
  • data: Data object of Vue instance. Vue will recursively convert data’s property into getter and setter, so that data’s property can respond to data changes. Objects must be pure objects (with zero or more key-value pairs) created by the browser API. Properties on the prototype are ignored. Data can only hold data.
  • {{}}: interpolation expression; The website is also calledMustachegrammar

Why is data a method in a component

When a component is defined (not the root component) data must be declared as a function that returns an object, because the component may be used to create multiple instances. If data is still an object, then all instances will share references to the same data object. Each time an instance is created, we can call the data function, which returns a new data object of the original data

// Error example let options = {data: {uname: 'zs' } } function Component(options) { this.data = options.data } let user1 = new Component(options) let user2 = new Component(options) user1.data.uname = 'ls' // modify user1 triggers all console.log(user2.data.uname) // lsCopy the code
// Correct example let options = {data() {return {uname: 'zs' } } } function Component(options) { this.data = options.data() } let user1 = new Component(options) let user2 = new  Component(options) user1.data.uname = 'ls' console.log(user2.data.uname) // zs console.log(user1.data.uname) // lsCopy the code

Since components can be reused multiple times, if function return is not used, each component’s data points to the same address in memory. Due to the nature of JavaScript’s complex data types, one data changes and others change. However, if function return is used, it is equivalent to declaring new variables, which are independent of each other, and there is no problem in the above example. JavaScript assigns Object directly to the same memory address, so it is used this way for the sake of independence of each component; But since there is only one root component, there is no data contamination, so it can be an object;

References:

  • https://segmentfault.com/a/1190000021680253
  • https://axiu.me/coding/why-vue-component-data-must-be-function/
  • https://blog.csdn.net/shaleilei/article/details/78084171

instruction

v-cloak

This directive works with CSS to hide the uncompiled Mustache tag until the instance is ready

Problem presentation:

/* css */
[v-cloak] { display: none; }
Copy the code
<div v-cloak>{{ root }}</div>
Copy the code

v-text

Update a value under an element node; Note: You will update everything, and you can use Mustache syntax if you want to update locally

<div v-text="root"></div>
Copy the code

v-html

Update the innerHTML of the element note: plain HTML content

Using HTML on a website is very dangerous and leads to XSS tools, so don’t use them when submitting content

<div v-html="html"></div>
Copy the code
new Vue({
  el: '#app',
  data: {
    html: '<p>hello vue</p>'
  }
})
Copy the code

v-pre

Text output, not participate in the compilation, what is input to show what

<div v-pre>{{ will not compile }}</div>
Copy the code

v-once

The element or component (including the element or descendant nodes within the component) defined by the V-once instruction can only be rendered once. After the first rendering, the real-time data will change and will not be re-rendered. It is generally used for static content display.

<div v-once>{{ content }}</div>
Copy the code
const vm = new Vue({
  el: '#app',
  data: {
    content: 'this is init data'
  }
})
vm.content = 'update data'
Copy the code

v-show 与 v-if

The V-if in this case is not just this instruction, it has v-else if and v-else and it’s pretty much the same thing

V-show: determines whether elements are hidden based on the expression’s true or false values (toggle elements with display: block/ None)

V-if: Conditionally render data based on the value of the expression, at which time the element and its data binding/component are destroyed and rebuilt

Differences:

<div v-if="isShow"> v-if </div>
<div v-show="isShow"> v-show </div>
Copy the code

Branch judgement code demo

<! <div> <p v-if="score > 90"> <span> {{with}} < / span > < / p > < p v - else - if = "score > 70" > < span > passing grades: {{with}} < / span > < / p > < p v - else > < span > fail: {{ score }}</span> </p> </div>Copy the code

v-for

In v – for circular Object, must be an Object of iterative iterable (Array | Number | Object | String…).

The syntax format is alias in expression where in can also be replaced by of

You can add indexes to arrays or objects

<! - array cycle -- -- > < div v - for = "(item, index) in the items" > {{item. Text}} < / div >Copy the code
<! Cycle - object - > < div v - for = "(val, key, index) in the object" > {{val}} {{key}} {{index}} < / div >Copy the code

whyv-forMust add uniquekey

When Vue is updating a list of data rendered using V-for, it defaults to an in-place update strategy. If the order of the data items is changed, Vue will not move the DOM to match the data of the data items. Instead, Vue will update each element in place, ensuring that they are rendered correctly at each index location.

Why do we addkey

  • To give Vue a hint that it can keep track of the identity of each node to reuse and reorder existing elements, you need to provide a unique key for each entry
  • Key is mainly used in Vue’s virtual DOM algorithm to identify the virtual DOM when comparing old and new nodeskey An algorithm that minimizes dynamic elements and tries to modify/reuse the same type of elements in place as much as possible is used, whereas if usedkeyIt will be based onkeyChanges to reorder elements and remove themkeyNon-existent elements

Why not?index 做 key

// Const vm = new Vue({el: '#app', data: {users: [{id: 1, uname: 'zs', age: 23}, {id: 2, uname: 'ls', age: 23}, {id: 2, uname: 'ls', age: 24 }, { id: 3, uname: 'we', age: 25 }, { id: 4, uname: 'mz', age: 26 }, ] } })Copy the code

indexThe wrong sample

The important point is that we rearrange the order of the elements based on the change of the Key. We can see that if we flip the array with index as the Key, the order of the Key is not changed, but the value passed in is completely changed, and the original data is not the same, Is mistaken for the same, so it causes the following problems;

<! -- More on the syntax later; < button@click ="users.reverse()"> Age sort </button> <ul> <! > <li v-for="(user, index) of users" :key="index"> <input type="checkbox" /> <span>{{ user.uname }}</span> </li> </ul>Copy the code

Unique Id Correct example

The key is bound to the data, and when you flip the array, it’s bound to the data, not the index, so you don’t have this problem

<li v-for="(user, index) of users" :key="user.id">
  ......
</li>
Copy the code

References:

https://juejin.cn/post/6844904113587634184

v-bind

Attribute binding; Can be abbreviated as:

// bind attrbute <div v-bind:content="message"></div> isBox}"></div> <div :class="['box', 'box1']"></div> <div :class="['box', {box1: IsBox}] "> < / div > / / binding style < div: style =" {fontSize: '20 px, color:' white '} "> < / div > < div: style =" [{fontSize: '20px'}, {color: 'white'}]"></div>Copy the code

v-on

Event binding; You can abbreviate @ to listen for DOM events and run some JS code when triggered

<button v-on:click="count += 1"></button>
{{ count }}
Copy the code

Can accept a method name;

Note: When it is just a method name, the default first argument is the event object e

When parameters need to be passed in, the event object needs to be passed in manually, and the last one is forced to be $event

<button @click="handle1('content', $event)"> </button>Copy the code
methods: {
  handle(e) {
    console.log(e.target)
  },
  handle1(ct, e) {
  	console.log(ct)
    console.log(e.target)
  }
}
Copy the code

Event modifier

  • .prevent: Blocks the default event
  • .stop: Prevents bubbles
  • .self: Only the current element fires events
  • .once: Triggers this event only once
  • .native: Listens for the native event of the component root element
// Vue.component('my-component', {template: '< butt@mousedown ="handle" :style="{color: 'white', lineHeight: 0; '20 px' backgroundColor: 'black'} "> component < / button > `, the methods: {handle () {the console. The log (' / / / ')}}})Copy the code

Adding Native makes a custom component look like HTML can listen for native events directly on it. Otherwise, a custom component is bound to a custom event, and you didn’t define the event on the custom event, so it won’t execute without adding Native

<my-component @click.native="handle(' parent ')"></my-component>Copy the code

References:

https://segmentfault.com/q/1010000011186651

  • .capture: Use capture mode when adding event listeners
<div class="box" style="background: skyblue; width: 180px;" @click.capture="handle('box1')"> <div class="box1 box" style="background: slateblue; width: 140px;" @click="handle('box2')"> <div class="box2 box" style="background: red;" @click="handle('box3')"></div> </div> </div>Copy the code
<a href="http://www.baidu.com" @click.prevent > // Multiple event modifiers can be used together to trigger the same time <a href="http://www.baidu.com" @click.prevent.stop="handle('a')">baidu</a>Copy the code

Key modifier

Vue allows you to add keyboard modifiers when V-ON listens for keyboard events

<input v-on:keyup.enter="submit">
Copy the code

Of course, most of the keycodes alias keycodes are provided

You can also customize modifier aliases through global vue.config. keyCodes

Vue.config.keyCodes.f1 = 112
Copy the code

v-model

Create a two-way data binding on a form element that automatically updates the element with the correct value based on the control type

/ / text < input type = "text" v - model = "message" > < p > {{message}} < / p >Copy the code
<textarea cols="30" rows="10" V-model ="message"></textarea> <p>{{message}}</p>Copy the code
/ / radio buttons < input type = "radio" value = "male" v - model = "sex" > men < input type = "radio" value = "female" v - model = "sex" > women < p > {{sex}} < / p >Copy the code
// Single checkbox <input type="checkbox" V-model ="checked"> <p>{{checked}}</p>Copy the code
// Multiple checkboxes <input type="checkbox" value=" play basketball "V-model ="hobby"/> Play basketball <input type="checkbox" value=" play basketball" V-model ="hobby"/> Play basketball <input type="checkbox" value=" hobby" V ="hobby"/> <p>{{ hobby }}</p>Copy the code
// Select box -> radio <select V-model ="selected"> <option>javascript</option> <option> HTML </option> <option> CSS </option> </select> <p>{{ selected }}</p>Copy the code
// Select v-model="selectList" multiple> <option>javascript</option> <option> HTML </option> <option>css</option> </select> <p>{{ selectList }}</p>Copy the code
// Instance object new Vue({el: '#app', data: {message: '', sex: '', // check box checked: false, // check box single hobby: [], / / check box multiple selected: "', / / select box - > single selectList: [] / / select box - > multiple}})Copy the code

The modifier

  • .lazy: Default valuev-modelAfter each input event is triggered, the content of the input box is synchronized and addedlazyAfter modifier, it becomes synchronized data after the change event
<input v-model.lazy="message">
Copy the code
  • .number: Changes the value entered by the user to a numeric type
<input v-model.number="age">
Copy the code
  • .trim: Filters the left and right blanks in the input box
<input v-model.trim="message">
Copy the code

Vue.set

If a new property is added to an instance after it is created, it does not trigger the update view.

Data () {return {info: {uname: 'zs'}}} mounted() {this.info-age = 23}Copy the code

Restricted by ES5, Vue cannot detect the addition or deletion of object properties. Because Vue converts properties to getter setters at initialization, properties must be on data objects for Vue to convert. Only data objects are responsive

Mounted () {this.$set(this.info, 'age', 23)}Copy the code

Vue. Set () : this.$set is an alias of Vue. Set

methods

Methods will be incorporated into the Vue instance and can be accessed directly from the VM instance or used in an instruction expression, where this is automatically bound to the Vue instance

Note: Methods in Methods do not use arrow functions. Arrow functions in this point to the context of the parent scope, so this will not point to the Vue instance

new Vue({
  methods: {
    handle() {
      console.log(this)
    }
  }
})
Copy the code

Calculate attributecomputed

Writing expressions in templates is certainly convenient, but you should understand that expressions are originally used for computation, such as handling strings, time formats, etc. If we write methods instead! It’s too much trouble to call it every time, so Vue provides computed properties

As you can see, every time we call this parameter, it feels inconvenient

<input type="text" v-model.number="input1"/> + 
<input type="text" v-model.number="input2"/> =
<span>{{ getSum() }}</span>
Copy the code
data() {
  return {
    sum: '',
    input1: '',
    input2: ''
  }
},
methods: {
  getSum() {
    return this.sum = this.input1 + this.input2
  }
}
Copy the code

Next we use computed properties to solve; You can see that we’ve removed the sum attribute from data and added a getSum function in computed

<input type="text" v-model.number="input1"/> + 
<input type="text" v-model.number="input2"/> =
<span>{{ getSum }}</span>
Copy the code
data() {
  return {
    input1: '',
    input2: ''
  }
},
computed: {
  getSum() {
    return this.input1 + this.input2
  }
},
Copy the code

So the question is why do you define a function and then execute it as a property? So this is just shorthand, it’s a syntactic sugar, each of these computed properties contains get and set and when you only have GET you can abbreviate it to a function

Export default {computed: {getSum: {get() {// Get data}, set(val) {// val is the modified data set}}}}Copy the code

Example: See why this example is called computed properties

<input type="text" v-model.number="input1"/> + <input type="text" v-model.number="input2"/> = <span>{{ getSum }}</span> <! <button @click="getSum = 'unknown '"> </button>Copy the code
computed: { getSum: { get() { return this.input1 + this.input2 }, Set (val) {console.log(val) this.input1 = 20 this.input2 = 30}}},Copy the code

Listen to attributewatch

While the calculated properties work in most cases, there are times when you need a custom listener, in which case you need to listen for the watch property

It’s still the sum of two numbers; Sum is calculated when input1 is triggered when Watch listens for input1. If you look carefully, input2 will no longer trigger when it is modified.

Summary: It listens for changes to a property of data and does not create new properties

<input type="text" v-model.number="input1"/> + 
<input type="text" v-model.number="input2"/> =
<span>{{ getSum }}</span>
Copy the code
data() { return { input1: '', input2: '', getSum: '' } }, watch: Input1 (newVal, oldVal) {console.log(newVal, oldVal) {console.log(newVal, oldVal); oldVal) this.getSum = this.input1 + this.input2 } input2: Handler handler(newVal, oldVal) {console.log(newVal, oldVal) {console.log(newVal, oldVal) this.getSum = this.input1 + this.input2 } } }Copy the code

If we want to listen for object properties, we can use deep: true in the option argument. Note that listening for changes to data does not need to do this

watch: {
  obj: {
    handler() {
      // ....
    },
    deep: true 
  }
}
Copy the code

Watch does not execute the listener function when the value is first bound, but only when the value changes. If we need to execute the function when the value is first bound, we need to use immediate: true

watch: {
  apiData: {
    handler(newVal, oldVal) { },
    deep: true,
    immediate: true
  }
}
Copy the code

Methods computed Watch difference

  • Watch simply monitors the change of a certain data and supports deep monitoring. It receives two parameters, one latest value and the other old value before the change. The results will not be cached

  • Computed is a calculation of attributes that depends on one or more attribute values, and the computed results are cached, changing only when data dependencies change, creating a new attribute

  • Methods are function calls, have no caching, and deal with business logic rather than listening for or evaluating properties

The filterfilter

Can be used for some common text formatting, allowing it to be used in {{}} V-bind in two places

{{ msg | formatMsg }}
<div v-bind:msg="msg | formatMsg"></div>
Copy the code
  • Private filters within a component can be defined in the component’s options
Vue.component('son-component', {
  template: `
    <div>{{ msg | formatMsg }}</div>
  `,
  data() {
    return {
      msg: 'this is message'
    }
  },
  filters: {
    formatMsg(msg) {
      return msg.toString().toUpperCase()
    }
  }
})
Copy the code
  • You can define global filters before creating a Vue instance
Vue.filter('formatMsg', function(msg) {
  return msg.toString().toUpperCase()
})
Copy the code
  • The filter defaults to|The previous content is passed as the first parameter to the filter and can be passed again to the transmission
<div>{{ msg | formatMsg('lower') }}</div>
Copy the code
Vue.filter('formatMsg', function(msg, args) {
  console.log(msg) // lower
  if (args === 'lower') {
    return msg.toString().toLowerCase()
  }
})
Copy the code

Custom instructiondirective

As with the instructions mentioned above, you can customize them if they do not meet your requirements

Custom capture focus cases

<input type="text" v-focus/>
Copy the code
{FOCUS (el) {el.focus()}}) // Global directives are defined and do not require V-directives. { 'focus': { inserted(el) { el.focus() } } }Copy the code

Hook function

  • bind: only called once, the directive is called the first time the element is bound, where one-time initialization can be done;
  • inserted: called when the bound element is inserted into the parent node. The rendering is not necessarily complete. The HTML is already created
  • update: called when the VNode of the component is updated
  • componentUpdated: After all vNodes of the component where the directive resides are updated
  • unbind: called when a directive is unbound from an element

Hook function arguments

  • el: The element bound by the directive that can manipulate the DOM directly
  • binding: Instruction-related configuration object
    • modifiers: an example of an object containing modifiersv-drag.limit
    • name: Command name, without prefix
    • value: The value of the instruction bindingv-drag="true"
<div v-drag>
Copy the code
Directive ('drag', Bind (el) {el.style.position = 'absolute' el.style.top = 0 el.style.left = 0 el.style.width = '100px' Height = '100px' el. Style. Background = 'skyblue' el. Style. Insert (el, binding) { let draging = false let elLeft = 0 let elRight = 0 document.addEventListener('mousedown', function (e) { draging = true let move = el.getBoundingClientRect() elLeft = e.clientX - move.left elRight = e.clientY -  move.top }) document.addEventListener('mousemove', function (e) { let moveX = e.clientX - elLeft let moveY = e.clientY - elRight if (draging) { el.style.left = moveX + 'px' el.style.top = moveY + 'px' } }) document.addEventListener('mouseup', function () { draging = false }) } })Copy the code

Custom instruction modifier

I believe that if you take a closer look at the code above, you may find that there are some problems with the grid drag; It can still be dragged out of the viewable area, so can we pass a modifier to tell it? The binding directive is used to configure the associated objects

<div v-drag. Limit >Copy the code
/ / if we don't want to let he dragged out the viewport, it should be when the mouse moves to add some logic to the document. The addEventListener (" mousemove ", Function (e) {let moveX = e.clientx-elleft let moveY = e.clienty - elRight // if (e) {let moveX = e.clientx-elleft let moveY = e.clienty - elRight // if (e) {let moveX = e.clientx-elleft let moveY = e.clienty - elRight (binding.modifiers.limit) { moveX = moveX <= 0 ? moveX = 0 : moveX moveY = moveY <= 0 ? moveY = 0 : {el.style.left = moveX + 'px' el.style.top = moveY + 'px'} console.log(binding) // binding object})Copy the code

Custom command parameter transmission

We have solved the problem of dragging out viewports by passing a modifier, so now we want to pause and drag manually, which is also possible;

<div v-drag.limit="{isDrag: false}">
Copy the code
document.addEventListener('mousemove', Function (e) {let moveX = e.clientx-elleft let moveY = e.clienty - elRight (binding.modifiers.limit) { moveX = moveX <= 0 ? moveX = 0 : moveX moveY = moveY <= 0 ? MoveY = 0: moveY} // if (! binding.value.isDrag) return if (draging) { el.style.left = moveX + 'px' el.style.top = moveY + 'px' } })Copy the code

component

Typically a component is organized as a nested tree of components; In order to be used in a template, these components must be registered so that vUE can recognize them;

  • Global components
Vue.component('GlobalComponent', {
  template: `<div> hello component </div>`
})
Copy the code
<global-component></global-component>Copy the code
  • Local components
new Vue({
  el: '#app',
  components: {
    SonComponent: {
      template: `<div>hello private component</div>`
    }
  }
})
Copy the code
// Components can be reused more than once <private-component></private-component> </private-component> <private-component></private-component>Copy the code
  • Components in modular development
import SonComponent from '@/components/SonComponent.vue'

export default {
  components: {
    SonComponent
  }
}
Copy the code
<son-component></son-component>
Copy the code

throughpropsPass data to child components

Prop is a set of custom attributes on a component. When a value is passed to a Prop attribute, it becomes the property of that component instance.

// parent <div> <son-component content=" Data to pass to child components, v-bind can be added if dynamic "></son-component> </div>Copy the code
// Vue.component_props ('SonComponent', {// Multiple words can be humped, but when passed by the parent component multiple words must be - values in the connection // props, This. Content / {{content}} // props is read-only. Do not give props: ['content'], template: `<div> {{ content }} </div>` })Copy the code

Props can be an array or an object that receives data from the parent component;

Object allows you to configure advanced options, such as type detection

  • type: it can beNumber String Boolean Array Object Date FunctionAny custom constructor, or array of the above, checks if a prop is of the given type and throws an exception otherwise
  • defaultThe default value for an object or array must be returned from a factory function
  • required: Boolean Specifies whether the value is mandatory
  • validatorCustom validation functions pass in the value of prop as the only argument
Props: {content: {type: String, // default: 0, default: () => [1, 2, 3], required: Validator: (value) => value.length >= 20}}Copy the code

Listen for child component events$emit

Sometimes when the parent component needs a specific value from the child component, you can use $emit to pass that value

  • Inline mode value transfer
<div class="son"> <div class="son"> 3])"></button> </div> </template>Copy the code
<son-component @son-com=" MSG = $event"></son-component>Copy the code
  • Event handlers pass values
// subcomponent <template> <div class="son"> < button@click ="sonHandle"></button> </div> </template> <script> export default { Methods: {sonHandle() {this.$emit('son-com', 'son-com')}}} </script>Copy the code
// parent <template> <div class="parent"> <son-component @son-com="parentHandle"></son-component < son-component@son-com ="parent "(' display parameters passed in ', $event)"></son-component> </div> </template> <script> export default { methods: ParentHandle (arg) {console.log(arg) // The value that needs to be passed}, // The position of the passed argument parent(params, params) Arg) {console.log(params) // display the passed parameter console.log(arg) // need to pass the value}}} </script>Copy the code
  • Event handlers pass multiple values
// subcomponent <template> <div class="son"> < button@click ="sonHandle"></button> </div> </template> <script> export default { methods: $emit('son-com', event, 'value to be passed ',' second value to be passed ')}}} </script>Copy the code
// Parent component <template> <div class="son"> < button@son-com ="parent"></button> </div> </template> <script> export default { methods: { parent(event, ... Args) {console.log(event) // If you don't want to use the remaining parameters, you can also pass multiple parameters one by one using console.log(args)}}} </script>Copy the code

Use on componentsv-model

One thing we need to know before we can use this feature is what a V-Model is; In fact, it is a grammatical sugar in a way

<input type="text" v-model="msg"/>
Copy the code

Is equivalent to

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

To avoid confusion, I prefix my custom events and attributes with test

// Parent <model-input :test-value="searchText" @test-input="searchText = $event" > </model-input>Copy the code
  • Result of the child component must be bound to value
  • Pass when the input is fired$emitExpose the custom test-input
// subcomponent <input type="text" V-bind :value="testValue" @input="$emit('test-input', $event.target.value)" > // script props: ['testValue'] // The value passed by the custom attributeCopy the code

Now let’s optimize it a little bit, using the V-Model

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

Since we use v-model in our component and we mentioned that v-Model is the syntactically equivalent of V-bind and V-ON, we can only use the value attribute and input event

// subcomponent <input type="text" V-bind :value="value" @input="$emit('input', $event.target.value)" > // script props: ['value']Copy the code

So the question is, we mentioned above that v-Model defaults to value attributes and input events, but what about checkboxes, checkboxes, etc.? Vue provides model options to avoid such conflicts

Single check box component binding

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

Checked and unchecked return true/false

// subcomponent <input type="checkbox" V-bind :checked="checked" @change="$emit('change', $event.target.checked)" > // script export default {name: 'ModelInput', // v-model 'checked', // change isChecked to checked for props to use event: 'change' // define the emit custom event name}, props: {checked: {type: Boolean } } }Copy the code

Component slot

– new uniform syntax v-slot in 2.6.0 for named slots and scoped slots replaced slot and slot-scope which were deprecated but not removed (still available)

Slot: A slot that occupies a slot in a component template. When a component label is used, the content of the component label is automatically filled in (replacing slot positions in the component template) and can be used as an outlet for distribution content

The content of the slot

/ / subcomponents < template > < div > < p > this is the head component < / p > < slot > < / slot > < p > this is the end of the component < / p > < / div > < / template >Copy the code
// Parent <<template> <div> <! </p> </slot-test> </div> </template>Copy the code

Rule: Everything in the parent template is compiled in the parent scope; Everything in a child template is compiled in a child scope

Default content slot

The

tag can be used to add components, text, tags, and other default content. If no content is passed in when the parent component calls, the default content will be displayed

// Subcomponent <template> <div> <p> this is the header of the component </p> <slot> I'm default </slot> <p> This is the tail of the component </p> </div> </template>Copy the code

A named slot

Sometimes one slot is not enough, we may need more than one; In this case, the

element has a special attribute name that defines additional slots

// Subcomponent <template> <div> <header> <slot name="header"></slot> </header> <main> <! <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div> </template>Copy the code

When feeding content to a named slot, you can use the V-slot directive on a template element and supply the name as an argument

// Parent component <template> <div> <slot-test> <template V-slot :header> I am the content of the header </template> I am the content without the specified name <! <template v-slot:default> </template> <template v-slot:footer> I am the footer content </template> </slot-test> </div> </template>Copy the code

Everything in

Note: V-slot can only be added to

Named slot abbreviation

V-slot: replace with #

Note: This parameter can only be used with parameters, otherwise it is invalid

// Subcomponent <div> <slot></slot> </div>Copy the code
/* Parent component */ // error example <slot-test> <template #> <! -- Content... --> </template> </slot-test> // Correct example <slot-test> <template #default> <! -- Content... --> </template> </slot-test>Copy the code

Scope slot

In a sense, slots are a means of making sub-components highly available and highly customizable, so we will certainly encounter cases where slot content requires access to data in sub-components;

We can use V-bind to bind the values we want to pass to

, and then use v-slot to set a value in the parent component that defines the name of the supply slot

// Subcomponent <template> <div> <slot name="userInfo" :user="userInfo"></slot> </div> </template> <script> export default { data() { return { userInfo: { firstName: 'firstName', lastName: 'lastName' } } } } </script>Copy the code
// Parent <template> <div> <slot-test> <! <template v-slot:userInfo="slotProps"> {{slotProps.user.firstname}} </template>  </slot-test> </div> </template>Copy the code

Exclusive default slot abbreviated syntax

The label of the component can be used as a template for the slot when only the default slot is supplied, so we can use the V-slot directly on the component

// Subcomponent <div> <slot :user="userInfo"></slot> </div> // component data(){return {userInfo: {firstName: 'firstName', lastName: 'lastName' } } }Copy the code
// Parent <div> <slot-test V-slot ="slotProps"> {{slotProps.user.firstname}} </slot-test> </div>Copy the code
  • Don’t try the “abbreviated syntax” of slots and the “named slot mix”, which results in undefined scope
  • Whenever multiple slots are present, they must always be used for all slots<template>grammar
  • See the official website: Default slot

Deconstruction slotProp

Slot support through the ES6 structure into the specific slot prop as to the principle;

The idea behind a scoped slot is to include the contents of your slot in a function that is passed a single parameter. Did not understand πŸ˜‚ their grade is not enough, or see how to use it

<div>
  <slot-test v-slot="{ user }">
    {{ user.firstName }}
  </slot-test>
</div>
Copy the code

When providing multiple prop, it also enables prop renaming

<div>
  <slot-test v-slot="{ user: preson }">
    {{ preson.firstName }}
  </slot-test>
</div>
Copy the code

Slots Other Examples

Slot Prop allows us to convert slots into reusable templates that can render different content based on the input of different prop;

For example, let’s design a

component that is a list and contains some logic

// subcomponent <template> <ul> <li v-for="item in list" :key="item.id"> <! -- Hand over the control logic here, By the parent component to control logic -- -- > < slot name = "todo" : the item = "item" > {{item. The uname}} < / slot > < / li > < / ul > < / template > / / data [{id: 1, uname: 'zs'}, {id: 2, uname: 'ls'}, {id: 3, uname: 'we'}, {id: 4, uname: 'mz'}, ]Copy the code
// Parent <test V-slot :todo="slotProps"> <! {{slotProps. Item. id}} </test>Copy the code

Dynamic componentscomponent

Before we talk about dynamic components, let’s implement a TAB page, otherwise it might not be easy to understand πŸ˜‚

<div> <button @click="handle('post')" :style="{background:! isShow ? 'red' : ''}">post</button> <button @click="handle('list')" :style="{background: isShow ? 'red' : ''}">list</button> <item-post v-show="! isShow"></item-post> <item-list v-show="isShow"></item-list> </div> // script import ItemPost from '@/components/ItemPost.vue' import ItemList from '@/components/ItemList.vue' export default { name: 'Home', components: { ItemPost, ItemList }, data() { return { isShow: true } }, methods: { handle(title) { title === 'list' ? this.isShow = true : this.isShow = false } } }Copy the code

You can see that we wrote two widgets up here, and we made a simple TAB TAB;

The focus can be seen in the way we introduce components; Simply apply it to a template; Conventional operation; For now, it’s okay to introduce two tags, but if you have 30 or 50 tags, it’s a bit of a hassle

Dynamic component application

With dynamic components, we don’t need to use so many component tags in templates with simple TAB switching

Looking at the code example, we just need to introduce the component part of the template and add a new property to data in script

<! <component :is="showWhat"></component>Copy the code
data() { return { showWhat: 'ItemPost', isShow: true } }, methods: {handle(title) {// This controls the active style after the switch, so we don't delete the title === 'list'? This. isShow = true: this.isShow = false this.showWhat = 'ItemList' : this.showWhat = 'ItemPost' } }Copy the code

There is a problem here. Although we switched between components, we did not retain the selected state of the input. The reason is that the behavior of Vue creating a new showWhat instance to recreate the dynamic component is usually very useful. But in our case, we want it to stay. To solve this problem, Vue provides a built-in element

that just wraps the component around it

<! -- deactivated tabs will be cached --> <keep-alive> <component :is="showWhat"></component> </keep-alive>Copy the code

Asynchronous components

In a large application, you might want to break up the application into smaller blocks of code and load the module again only when needed. The principle is the same as webPack loading on demand;

Start by creating a simple component

<template> <div> This is a simple component </div> </template>Copy the code

Then introduce it in the main component to see the effect

<template>
  <div>
    <dynamic></dynamic>
  </div>
</template>
<script>
import Dynamic from '@/component/Dynamic.vue'

export default {
  name: 'Home',
  components: {
    Dynamic
  }
}
</script>
Copy the code

See below, in the writing of the above, the load components, can be rendered into a file, if we this page component lot, or some components, only triggered only after a specific operation will show that we don’t need the page rendering, put all the components of the load?

Asynchronous component example

Based on the example above, let’s modify the script section

Export default {components: {// import Dynamic directly into the component: () => import Dynamic from '@/component/Dynamic.vue' } }Copy the code

Vue-cli helps us package the files separately; Once the file is loaded, it is cached; This example just shows that it’s broken down, it doesn’t prove what we started with rendering on demand; Based on this example, I can change it a little bit and add a judgment logic

<dynamic V-if ="isShow"></dynamic> </div> </template>Copy the code

As you can see from the following figure, the file is cached when the child component is not displayed, and the corresponding resource is loaded only when the child component is actually called.

Of course, this is just one of the features, for example, when the network is not good, the user experience of component loading will be compromised, timeout handling, etc. For details, see vUE official website: Handling loading status

Access child component instances or child elementsref

Despite prop and events, sometimes you may need to access a child component directly from JavaScript. To do this, you can use the ref attribute to give the child an ID reference

// template <template> <div> <item-list ref="itemRef"></item-list> <input type="text" ref="inputRef"/> </div> </template> // // Return $refs.itemref () // return $refs.itemref () // return $refs.itemref () // Dom console.log(this.$refs.inputref)}}Copy the code

If ref and V-for are used together, this.refs will get an array of elements

<ul>
  <li v-for="user of userList" :key="user.id" :style="{listStyle: 'none'}" ref="forRef">
    <input type="checkbox" />
    {{ user.id }} --- {{ user.uname }} --- {{ user.age }}
  </li>
</ul>

mounted() {
  console.log(this.$refs.forRef)
}
Copy the code

$refs only takes effect after the component has rendered, and it is not reactive, it is only used as an escape hatch for direct manipulation of child components. Access to $refs in templates or calculated properties should be avoided

Component life cycle

Each Vue instance is created through a series of initializations; The lifecycle starts with the process of creating, initializing data, compiling templates, mounting the DOM, updating, rendering, unmounting, and so on, and also runs functions called lifecycle hooks

  • BeforeCreate: After the instance is initialized before creation, this points to the created instance and cannot access methods and data on data, computed, watch, and methods

  • Created: After the instance is created, data computed Watch Methods data can be accessed, and DOM cannot be accessed without rendering it into the browser. Note: There is no way to intercept the instantiation process while sending ajax requests during this lifecycle, so adding some data that must be retrieved before entering the page is not a good idea. Instead, use the beforeRouterEnter routing hook

  • BeforeMount: called before the mount starts, beforeMount the corresponding template is found and compiled into the render function

  • Mounted: After the instance is mounted to the DOM, DOM $ref is available. Mounted is executed only once

  • BeforeUpdate: called beforeUpdate when responsive data is updated, before DOM is re-rendered and patched, the state can be further changed here without a second rendering

  • Updated: Invoked after the virtual DOM is re-rendered and patched. Component updated for subsequent operations Avoid manipulating data here, which can lead to an endless loop

  • Activated: activated when a component cached by keep-alive is activated

  • Deacticated: invoked when the keep-alive cached component is disabled

Activated () {console.log('itemList is activated ')} Deactivated () {console.log('itemList disabled/deactivated ')},Copy the code
< button@click ="com = 'ItemPost'">post</button> < button@click ="com =" 'ItemList'">list</button> <keep-alive> <component :is="com"></component> </keep-alive>Copy the code
  • beforeDestroy : Before destructionCalled before instance destruction, where various data is still accessible; You can destroy timers again, unbind events, and so on
  • destroyed : After the destruction ofCalled after instance destruction, all contents of the Vue instance are unbound, all event event listeners are removed, and all subinstances are destroyed
<template> <div> <p>this is $el</p> < button@click ="info = 'modify '">{{info}}</ button@button > < button@button @click="handle"> </button> </div> </template> <script> export default {name: 'HomePage', data() {return {info: 'data options', flag: true } }, methods: {handle() {this.$destroy()}}, beforeCreate() {console.group('##### beforeCreate #####') console.log('Vue', this) console.log(this.info) console.log(this.$el) }, Created () {console.group('##### created #####') console.log(this.info) console.log(this.$el)}, BeforeMount () {console.group('##### component before mounting beforeMount #####') console.log(this.info) console.log(this.$el)}, Mounted () {console. Group (' # # # # # components mounted after mount # # # # # ') to the console. The log (enclosing $el)}, BeforeUpdate () {console.group('##### component before updating beforeUpdate #####') console.log(' Here the data has been modified just without rendering ----- '+ this.info) This.info = 'updated again'}, updated() {console.group('##### component updated #####') console.log(' Updated with new value: ', this.info) }, BeforeDestroy () {console.group('##### component before uninstallation updated #####') console.log(this.info) console.log(this.$el)}, Destroyed () {console.group('##### component uninstalled updated #####') console.log(this.info) console.log(this.$el)}} </script>Copy the code

Parent component life cycle

  • Parent component completes executionbeforeMountThe hook will then load the child component, triggering the parent component only after the component has finished loadingMounted
  • Child component updates do not trigger parent component updates (does not involve parent component interaction data)
  • The unloading of the child triggers an update of the parent

Transition animations

Vue provides a variety of transition effects when inserting, updating, or moving out of the DOM

An entry/exit transition can be added to any element in the following situations

  • Conditional rendering (v-if v-show)
  • Dynamic component (component)
  • Component root node

Transition class names: There are six classes that switch during the entry/exit transition

  • v-enter: Defines the beginning of the transition, which takes effect before the element is inserted and is removed in the next frame after the element is inserted
  • v-enter-active: Defines the state when the transition takes effect, which will be applied throughout the transition. This class can be used to define the process time, delay and curve functions for entering the transition
  • v-enter-to: Defines the end state of the transition, which takes effect on the next frame after the element is inserted (with the v-enter removed) and removed after the transition animation is complete
  • v-leave: defines the start time of the exit transition, which takes effect immediately when the exit transition is triggered and the next frame is removed
  • v-leave-active: Defines the state of the exit transition when it 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 the exit transition process time, delay, and curve functions
  • v-leave-to: Defines the end state of the exit transition, which will take effect in the next frame after the exit transition is triggered (and v-leave is removed at the same time), and will be removed after the east loop of the transition is completed

<transition>
  <dynamic v-if="isShow"></dynamic>
</transition>
Copy the code

If
does not have a name attribute, the class name starts with a V – by default. If a custom name is used, the class name starts with a custom name. Transition will not be rendered as a DOM element

Example:
my-trans-enter

<style lang="css">
  .v-enter, .v-leave-to{
    opacity: 0;
    transition: all .5s ;
  }
  .v-enter-to, .v-leave{
    opacity: 1;
    transition: all .5s ;
  }
</style>
Copy the code

Transitions of multiple components

<transition>
  <keep-alive>  
    <component :is="showWhat"></component>
  </keep-alive>
</transition>
Copy the code
<style lang="css">
  .v-enter, .v-leave-to{
    opacity: 0;
  }
  .v-enter-active, .v-leave-active{
    transition: opacity .3s ease;
  }
</style>
Copy the code

Transition mode

When you switch tabs, the content is redrawn, one for leaving the transition, and another for entering the transition, which is the default behavior of
, entering and leaving at the same time; Transition mode applies only between components

A simultaneous entry and exit transition does not satisfy all requirements, so Vue provides a transition mode

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

As you can see from the animation above, the new element comes in before the current element is finished. In this case, we can use out-of-in mode to solve the above problem

<transition mode="out-in">
  <keep-alive>  
    <component :is="showWhat"></component>
  </keep-alive>
</transition>
Copy the code

Initial render transition

The initial rendering transition of nodes can be set through the appear attribute. Note that the translateY keyword has a transition effect by default, and you can also define class names and hooks. Initial render transition

<transition appear>
  <div v-if="toggle">post</div>
  <div v-else>list</div>
</transition>
Copy the code

List the transition

I’m going to use

  • The tag name, which defaults to SPAN, can be set using the tag property
  • Transition mode is not available
<transition-group  tag="ul" appear>
  <li v-for="user of users" :key="user.id">
    <p>{{ user.uname }}</p>
  </li>
</transition-group>
Copy the code

More animation related: Vue transition animation

nextTick

Vue updates DOM asynchronously, as long as it listens for data changes and caches them in the same event loop. After all data changes in the event loop are completed, the view is updated uniformly. In order to obtain the latest DOM, nextTick() is set.

Delay the callback until after the next DOM update loop, use it immediately after modifying the data, and then wait for DOM updates;

Simply put: nextTick delays the callback until the next DOM update, when the DOM is re-rendered

$nextTick(() => {this.$refs.divref.innerhtml = 'hello vue'})}Copy the code

With mixins

Mixin provides a flexible way to distribute reusable functionality in Vue components. A mixin object can contain any component options (data, Components, methods, created, etc.). When a component uses mixin, All options mixed into the object will be mixed into the options of the component itself;

Assuming that we now want to encapsulate a Button component, we can try to unencapsulate it in the Components folder and create a new Mixin folder instance in the same directory

<! -- path: Mixin/MyButton/MyButton.vue -->
<template>
  <button :class="['my-btn', type]">
    <! -- We want the user to enter the name of the button, so use slot -->
    <slot></slot>
  </button>
</template>
<script>
// We want the user to pass in a type to customize the BTN style
export default {
  props: {
    type: {
      type: String.default: 'my-primay'}}}</script>
<style scoped>
 /* Base class style */
 .my-btn {
    border: none;
    outline: none;
    line-height: 30px;
    width: 50px;
  }
 /* User extensible styles */
.btn-primary {
  background: skyblue;
}
.btn-danger {
  background: orange;
}
.btn-success {
  background: palegreen;
}
</style>
Copy the code

Then we create a Mixin object in the same directory

// path: Mixin/MyButton/index.js
import MyButton from './MyButton.vue'
export default {
  // Here we use the components object in the component
  components: {
    MyButton
  }
}
Copy the code

Then we use it in the page

<! -- views/page.vue -->
<template>
  <! We can change the style of the button by passing in a different class name.
  <my-button type="btn-danger">button</my-button>
</template>
<script>
import MyButton from './mixin/MyButton'
export default {
  // Mix this object with mixins inside the component,
  mixins: [MyButton]  
}
</script>
Copy the code

Simple sum up, it can be seen that we use the above although some far-fetched, do not know to have noticed, we actually is equal to the registered components out of the way out, instead it is mixed with him in, here can be simple to understand, really want to merge the concept of two objects, then look at the details

Option to merge

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

What is appropriate πŸ˜‚ see example

  • The input objects are recursively merged and, in the event of a conflict, the component data takes precedence
  • The hook functions of the same name are merged into an array and called sequentially, with mixin’s hooks taking precedence
  • The object type options are merged into the same object, and if the key names conflict, the main one is within the component
// mixin/index.js
export default {
  data() {
    return {
      m_name: 'lisi'.m_list: [1.2.3]}},created() {
    // It will be called after mixing and we can also call the data in the component before mixing
    console.log(this.m_name, this.m_list, 'mixin')
    console.log(this.c_name, this.c_list, 'component')
      
    / / handle
    this.handle() // This handle actually calls the handle in the component instead of the handle in the mixin
    this.handle1()
  },
  methods: {
    // Since the handle here has the same name as the existing component, the handle is overridden with the following components as described above
    handle() {
      console.log('mixin handle')},handle1() {
      console.log('mixin handle1')}}}Copy the code
<template>
  <div></div>
</template>
<script>
import mixin from '@/mixin'
export default {
  mixins: [mixin],
  data() {
    return {
      c_name: 'zhangsan'.c_list: [1.2.3]}},created() {
    // It is also possible to access mixins directly
    console.log('this is component')
      
    / / call handle
    this.handle() 
  },
  methods: {
    handle() {
      console.log('component handle')}}}</script>
Copy the code

With global

Mixins can also be registered globally. Be careful when using mixins. Global mixins will affect every vUE component created later

// main.js
Vue.mixin({
  data() {
    return{}},created() {},
  / /...
})
Copy the code

Provide/Inject Dependency injection

Provide and Inject are used to develop high-level plug-ins or component libraries and are not recommended for common applications

This option is used together to allow an ancestor component to inject dependencies into all of its descendants, no matter how deep the component layer is;

In other words, this pair of options can be passed across components. See the following example

// App.vue
<template>
  <div>
    <router-view></router-view>
  </div>
</template>
<script>
export default {
  data() {
    return {
      info: { name: 'zs'.age: 23}}},// Use provide to pass the value
  / / met a little problem here, the website says it is Objct | function returns an Object, and try the Object here is not don't know why 😭 success
  provide() {
    return {
      info: this.info,
      reload: this.reload
    }
  },
  methods: {
    reload() {
      console.log('reload')}}}</script>
Copy the code
// Home.vue
<template>
  <div>
    Home
    <provide-test></provide-test>
  </div>
</template>
<script>
// Here we are just introducing the components that need to use the provide data, so we can see that we are not using it
import ProvideTest from '@/components/ProvideTest.vue'
export default {
  components: {
    ProvideTest
  }
}
</script>
Copy the code
// components/ProvideTest.vue
<template>
  <div></div>
</template>
<script>
export default {
  // Here we are using indect to receive data from the parent component, similar to the way props receives data
  inject: ['info'.'reload'].mounted() {
    // The data can then be accessed through the instance
    console.log(this.info)
    this.reload()
  }
}
</script>
Copy the code

Inject doesn’t only receive parameters through data; See cn.vuejs.org/v2/api/#pro…

Provide and inject binding are not reactive, they are intentional. If you pass in a listener object, its property is still reactive. Inject received data, do not attempt to modify, will report an error

Non-parent components pass values
o n / on /
emit

$emit emit a callback to the listener that triggers the event on the current instance.

Know $on before you know how to pass values

$ON listens for custom events on the current instance. The events can be emitted by $emit. The callback function accepts any additional arguments that are emitted by the incoming event

// About.vue
<template>
  <div>
    <button @click="handle">Click on the transfer</button>
  </div>
</template>
<script>
export default {
  methods: {
    handle() {
      // Emit is used to emit a custom event
      this.$emit('on-click'.'Single file is being listened in')}},mounted() {
    // In Mounted, use on to listen for triggered events and receive parameters
    this.$on('on-click'.msg= > {
      console.log(msg)
    })
  }
}
</script>
Copy the code

Of course, if you only use it like this, it will definitely be a little weak, since you know the API features, so let’s look at the parent component value

Communication between sibling components, also known as EventBus in EventBus Vue, is a publish-subscribe mode (for publish-subscribe mode) that uses EventBus as a communication bridge, allowing all components to share an event center from which they can register and receive events

// Bus/index.js
import Vue from 'vue'

// Create an empty Vue instance for the bridge
export const Bus = new Vue()
Copy the code
// main.js
import { Bus } from './Bus'
// Bind the empty instance to the prototype of vue
Vue.prototype.$bus = Bus
Copy the code
// About.vue
<template>
  <div>
    <button @click="handle">Click on the transfer</button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      message: ' '}},methods: {
    handle() {
      // Events emitted via $bus.$emit can be custom events or native events
      this.$bus.$emit('click'.'Other files are being listened in', event)
    }
  }
}
</script>
Copy the code
// Home.vue
<template>
  <div></div>
</template>
<script>
export default {
  mounted() {
    // Sibling components can listen for triggered events via $bus.$on and receive a callback processing parameter
    this.$bus.$on('click'.(msg, e) = > {
      console.log(msg)
      console.log(e)
    })
  }
}
</script>
Copy the code

Note that a component executes a mounted hook before it is created, so this hook is used to listen for events that are triggered