I. Life Cycle (Hooks)

1. Create life cycle functions during

Life cycle functions during creation:

  • BeforeCreate: The instance has just been created in memory and the data and methods properties have not been initialized.
  • Created: The instance has been created in memory, data and methods have been created, and the template has not been compiled. This is used when processing read/write response datacreatedIs very useful. For example, if you want to make an API call and then store the value, you can do so here. It is better to do this here than inmountedDo this because it happens during the synchronization initialization of the Vue and we need to perform all the data read/write operations.
  • BeforeMount: At this point the template is compiled but not mounted to the page.
  • Mounted: A compiled template is mounted to a specified container.

2. Runtime lifecycle functions

Runtime lifecycle functions:

  • BeforeUpdate: This function is executed before the status update, when the status value in data is the latest, but the data displayed on the interface is old because the DOM nodes have not yet been re-rendered.
  • Updated: This function is called after the instance is updated, when the state values in data and the data displayed in the interface are updated and the interface is rerendered.

3. Life cycle functions during destruction

Life cycle functions during destruction:

  • BeforeUnmounted: Called before instance destruction. At this step, the instance is still fully available.
  • Unmounted: invoked after the Vue instance is destroyed. When called, everything indicated by the Vue instance is unbound, all event listeners are removed, and all subinstances are destroyed.

Second, instruction label

1.v-text

V-text: Retrieves data and renders it as text inside a given tag, similar to innerText in javascript.

<template> <div class="app"> <span>{{message}}</span> <span v-text="message"></span> </div> </template> <script> import { ref } from "vue"; export default { setup() { let message = ref("hello world"); return { message }; }}; </script>Copy the code

Interpolation:

  • {{math.max (1,2,3)}}
  • {{content}}

The difference between {{}}(interpolation) and V-text is:

  • The value of V-text overwrites the original data of the label. The value of interpolation does not overwrite the original data of the label.
  • Using V-text can avoid interpolation flicker in poor network environment.

2.v-html

<template> <div class="app"> <span v-html='message'></span> <span v-text="message"></span> </div> </template> <script> import { ref } from "vue"; Export default {setup() {let message = ref("<a href='www.baidu.com'> return { message }; }}; </script>Copy the code

Differences between V-html and V-text:

  • Common: All data can be directly based on the data name in the data, rendering data inside the tag
  • Difference:
    • V-text: V-text renders the retrieved data directly into the innerText of the tag as text
    • V-html: V-HTML retrieves the data containing the HTML tag and renders it to the innerHtml of the corresponding tag

3. Event command v-ON

  • Js event three elements:
    • Event source: The source of the event is called the event source, usually referring to an HTML tag
    • Event: Specific action occurs onclick Click dbclick double-click onKeyUp……
    • Function (){}
  • Vue event: V-ON
    • Event instruction V-on :click= “handleClick”
<template> <div class="app"> <! <button type="button" @click="click" V-on :mouseover=" mouseover" </button> </div> </template> <script> export default {setup() {const click = function () { alert("click"); }; const mouseOver = function () { alert("mouse over"); }; const mouseOut = function () { alert("mouse out"); }; return { click, mouseOver, mouseOut }; }}; </script>Copy the code

3.1 Preventing event bubbling

<div @click="maopao"> < [email protected] ="handleClick()"> Disable the maopao event by using the.stop modifier </button> </div>Copy the code

3.2 Blocking default events

For example, if the form is submitted and the page is refreshed automatically, you need to prevent the default event and only execute the corresponding method:

<template> <div class="app"> <form @click.prevent='onSubmit'> </form> </div> </template> <script> export default { setup() { const onSubmit = function () { alert("click"); }; return { onSubmit }; }}; </script>Copy the code

3.3 Call Only once

<input type="button" value=" submit "@click.once="onSubmit"/>Copy the code

3.4 Key and mouse modifiers

Delete ESC up down left right // Mouse modifier left right middle <template> <div class="app"> <input @keyDown ="handleKeyDown" placeholder=" Click any keyboard to execute the event "/> <div @click.ctrl.exact="ctrlAndMouseDown"> </div> </template> <script> export default {setup()  { const handleKeyDown = function () { alert("handleKeyDown"); }; const enterDown = function () { alert("enterDown"); }; const ctrlAndMouseDown = function () { alert("ctrlAndMouseDown"); }; return { handleKeyDown, enterDown, ctrlAndMouseDown }; }}; </script>Copy the code

3.5 Event Parameter Transfer

Two points to note:

  • Without parentheses, the event is automatically passed as an argument.
  • With parentheses, you must explicitly pass in an event object; otherwise you might end up with a global window.event.
<template> <div class="app"> < button@click ='handleBtnClick(2, $event)'> {{counter}} </div> </template> <script> import { ref } from "vue"; export default { setup() { let counter = ref(0); Const handleBtnClick = function(num, event){const handleBtnClick = function(num, event){const handleBtnClick = function(num, event){ counter.value += num; }; return { handleBtnClick, counter }; }}; </script>Copy the code

4. Loop instruction — V-for

< the template > < div class = "app" > < h2 > {{MSG}} < / h2 > < h1 > object traversal < / h1 > < h2 v - for = "(the value, the key, index) in user" :key="index"> index: {{index}} key: value: {{key}} {{value}} < / h2 > < h1 > < / h1 > < h2 v iterate through group - for = "(school, Index in schools" :key="index"> index:{{index}} schools:{{school}} </h2> <h1> index) in users" :key="user.id"> index: {{ index }} name:{{ user.name }} age:{{ user.age }} bir:{{ user.bir }} </h2> </div> </template> <script> import { reactive,ref } from "vue"; export default { setup() { let msg = ref("hello world"); let user = reactive({ name: "zhangsan", age: 22, bir: "2009:12:22" }); Let schools = [" 中 国 ", "中 国 "," 中 国 "]; Let users = reactive([{id: "1", name: "2", age: 23, bir: "2"}, {id: "2", name: "2", age: 34, bir: "2"}, {id: "2", name: "3", age: 34, bir: "2"}, "2006-04-02"}, {id: "3", the name: "detective", the age: 12, bir: "2004-05-01"},]); return { msg, user, schools, users }; }}; </script>Copy the code
  • 1. When using V-for, note the following :key Provides a unique key for internal reuse and sorting of vUE

5. V – show, the v – if

  • v-showStyle =”display:block/none” : controls whether a tag element is displayed on the page.
  • v-if: controls whether page elements are displayed.
<template> <div class="app"> <h1 v-if="isShow">{{msg}}--"v-if"</h1> <h1 v-show="isShow">{{msg}}---"v-show"</h1> </div> </template> <script> import { ref } from "vue"; export default { setup() { let msg = ref("hello world"); let isShow = ref(false); return { msg, isShow }; }}; </script>Copy the code

Conclusion:

  • With V-show, you can control element display by writing Boolean values directly, and you can control label display and hide by variables.
  • In V-show, you can control the display and hiding of tags through Boolean expressions.
  • V – if, v – show: role: are used to control the page: label use whether to show and hide tags: v – if = “true | false”, v – show = “true | false”

The difference between:

  • V-show: The bottom layer controls whether the page label is displayed by using the display attribute in the CSS to show and hide the label. Recommended value: v-show When there is a large amount of data and the control display status changes frequently.
  • V-if: The bottom layer directly manipulates DOM elements when controlling whether the page tags are displayed or not, and controls the display and hiding of tags by deleting and adding DOM elements.

6. v-bind

V-bind: used to bind the attributes of the label, so that the attributes of the label can be dynamically modified through vUE.

V-bind :value="inputValue" v-bind:value="inputValue"Copy the code

6.1 Binding an HTML Class

6.1.1 Object Syntax

We can give v-bind:class an object to dynamically switch classes. Note that the V-bind :class directive can coexist with the normal class feature.

<template> <div class="app"> <ul class='box' v-bind:class="{'textColor': IsColor, 'textSize':isSize}"> <li> apple </li> <li> banana </li> </ul> </div> </template> <script> import {ref} from "vue"; export default { setup() { let isColor = ref(true); let isSize = ref(true); return { isColor, isSize }; }}; </script> <style> .box { border: 1px dashed #f0f; } .textColor { color: #000; background-color: #eef; } .textSize { font-size: 30px; font-weight: bold; } </style>Copy the code

You can also directly bind an object in the data:

< the template > < div class = "app" > < ul class = 'box' v - bind: class = "classObject" > < li > apple < / li > < li > banana < / li > < / ul > < / div > </template> <script> import { reactive } from "vue"; export default { setup() { let classObject = reactive({ 'textColor': True, 'textSize':false // not render}) return {classObject}; }}; </script>Copy the code

6.1.2 Array syntax

We can apply a class list by passing an array to V-bind :class.

<template> <div class="app"> <ul class='box' v-bind:class="[classA, ClassB] "> < li > apple < / li > < li > banana < / li > < / ul > < / div > < / template > < script > import {ref} from" vue "; export default { setup() { const classA = ref('textColor'); const classB = ref('textSize'); return { classA, classB }; }}; </script> <style> .box { border: 1px dashed #f0f; } .textColor { color: #000; background-color: #eef; } .textSize { font-size: 30px; font-weight: bold; } </style>Copy the code

6.2 Binding inline Styles

6.2.1 Object Syntax

The object syntax of V-bind :style is straightforward, much like CSS, but it is actually a JavaScript object, and the CSS property names must be camel name.

<template> <div class="app"> <ul class='box' v-bind:style="{color: activeColor, fontSize: Apple size} "> < li > < / li > < li > banana < / li > < / ul > < / div > < / template > < script > import {ref} from" vue "; export default { setup() { const activeColor = ref('#f00'); const size = ref('30px'); return { activeColor, size }; }}; </script>Copy the code

It is also possible to bind directly to a style object, which makes the template clearer:

< the template > < div class = "app" > < ul class = 'box' v - bind: style = "styleObject" > < li > apple < / li > < li > banana < / li > < / ul > < / div > </template> <script> import { reactive } from "vue"; export default { setup() { const styleObject = reactive({color: '#f00', fontSize: '30px'}); return { styleObject }; }}; </script>Copy the code

6.2.2 Array syntax

Multiple styles can be applied to an element.

<template> <div class="app"> <ul class='box' v-bind:style="[styleObjectA, StyleObjectB]"> <li> apple </li> <li> banana </li> </ul> </div> </template> <script> import {reactive} from "vue"; export default { setup() { const styleObjectA = reactive({color: 'blue', fontSize: '36px'}); const styleObjectB = reactive({textDecoration: 'underline'}); return { styleObjectA, styleObjectB }; }}; </script>Copy the code

6.3 v – model

Data binding form. Common tags for forms: Input, TextaREA, CheckBox, Radio, SELECT

<template> <! -- Dropdown box --> <select V-model ="selected"> <option value=" SELECTED ">A</option> <option value="B selected"> B</option> <option Value = "C was chosen" > C < / option > < / select > < span > Selected: {{Selected}} < / span > < br > <! <input type="radio" id="small" value="small_value" V-model ="picked"> <label for="small"> </label> <br> <input type="radio" id="big" value="big_value" v-model="picked"> <label for="big">big</label> <br> <span>Picked: {{ picked }}</span> <br> <! <input type="checkbox" ID ="one" value="value_one" V-model. lazy="checkedNames"> <label for="one"> Lazy ="checkedNames"> <label for="two"> Option 2 </label> <input Type ="checkbox" id="three" value="value_three" V-model. lazy="checkedNames"> <label for="three"> <span>Checked names: {{ checkedNames }}</span> </template> <script> import { ref } from "vue"; export default { setup() { const selected = ref(''); const picked = ref(''); const checkedNames = ref([]); return {selected, picked, checkedNames} }, }; </script>Copy the code

V-model can also be used with. Lazy,. Trim, and. Number modifiers

<! --> <input v-model. Lazy =" MSG "> <! <input v-model. Trim =" MSG "> <! <input v-model.number="age" type="number">Copy the code

7. Some other common commands — V-pre, V-once, V-block

7.1 v – pre

Skip the compilation of this element and its children. You can use it to show the original Mustache tag. Skipping a large number of nodes without instructions speeds up compilation. This means that vue does not compile and render when it encounters this tag, skips the tag and displays the raw data directly.

<! -- v-pre --> <span v-pre>{{ this will not be compiled }}</span> <! {{this will not be compiled}}Copy the code

7.2 v – once

Render elements and components only once, and elements/components that use this directive and all their children are treated as static and skipped, which can be used to optimize performance. When changing the value of the input box, the V-once p element does not change, whereas the second p element does.

<template>
  <p v-once>v-once:{{msg}}</p>
  <p>not v-once:{{msg}}</p>
  <input type="text" v-model="msg" />
</template>
<script>
import { ref } from "vue";
export default {
  setup() {
    const msg = ref('13');
    return {msg}
  },
};
</script>
Copy the code

Third, Composition API

Vue2 imports the entire vue each time, as in the main.js file of vue2:

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
render: h => h(App)
}).$mount('#app')
Copy the code

Obviously, it is impossible to use all the APIS of Vue in the project, so many modules are actually useless. In Vue3, many apis are exposed for developers to use. We can import the required APIS from Vue according to our own needs. For example, the code in main.js:

import { createApp } from 'vue';
import App from './App.vue'

createApp(App).mount('#app')
Copy the code

Using the import/export syntax of import and export, the function of packing modules on demand is realized. The file size after the project is packaged is significantly smaller, which is why we need to have a detailed understanding of Vue3 API in this article.

1. setup

The setup function is also the entry function of the Compsition API. Our variables and methods are defined in this function.

< the template > < div id = "app" > < p > {{number}} < / p > < button @ click = "add" > add < / button > < / div > < / template > < script > / / 1. Import {ref} from 'vue' export default {name: 'App', setup() {// 2. Number let number = ref(0) // 3. Function add() {// number is wrapped in the.value function. Value ++} // 4. Return number and add for template with return {number, add}} </script>Copy the code

The ref function is used in the above code, which will be explained in detail below. Here you only need to understand that its function is to wrap a responsive data, and you can think of the variables wrapped by the REF function as the variables in Vue2 data, which is easy to implement a button increment function.

In Vue2, we access variables in data or props using something like this.number, but it’s important to note that in setup, this refers to undefined, This means that you can no longer use this to fetch variables as Vue2 did.

So how do I get the data in props?

The setup function also has two parameters: props and context. The former holds the names of the parameters that are allowed to be passed from the current component and the corresponding values. The latter is a context object from which you can access attr, EMIT, slots. Emit is the familiar Vue2 method for communicating with the parent component and can be called directly.

The first parameter of the setup function is props

setup(props,context){}
Copy the code

The first parameter props:

  • Props is an object that contains all the data that the parent component passes to the child component.
  • Use props to receive in the child component.
  • The object that contains all the properties passed in by the configuration declaration. That is, if you want to output the value passed by the parent component to the child component as props. You need to use props for the receive configuration. The props: {… }, if you are not using props to receive configuration, the output value is undefined.
<template>
  <div class="box" @click="sonHander">
    我是子组件中的数据
  </div>
</template>
<script>

export default {
    //未进行接收
    // props:{
    //     mytitle:{
    //         type: Object
    //     }
    // },
    setup(props, context) {
       const sonHander =  function(){
           context.emit('sonclick', '子组件传递给父组件');
       }
      console.log(props.mytitle);
      return {sonHander}
    }
};
</script>
Copy the code
<template> <div class="box" @click="sonHander"> </div> </template> <script> export default {// not received // props:{ // mytitle:{ // type: Object //} //}, setup(props, context) {const sonHander = function(){context.emit('sonclick', 'child to parent '); } console.log(props.mytitle); Return {sonHander}}}; </script>Copy the code
Why is the output of props. Mytitle undefined? Because we didn't use props for the receive configuration. Props :{mytitle:{type:Object}} If we add a receive configuration, we will receive data passed from the parent componentCopy the code

1.2 Description of context

The second argument, context, is an object.

  • There is attrs(the object that gets all the attributes on the current tag), but that attribute is all the objects that are not declared to be received in props.
  • Attrs = undefined if you use props to get the value, and you declare the value you want to get in props, attrs is undefined.
  • Note: attrs gets a value that does not need to be accepted declared in props. The first parameter gets a value that the props declaration accepts.
  • It contains emit event distribution, which is required to be passed to the parent component.
  • There are slots in it.
<template> <div class="box" @click="sonHander"> </div> </template> <script> export default {// not to accept props: {mytitle: {type: Object,},}, setup(props, context) {// Output title: the value passed by the parent component console.log(props. Mytitle); // Output someone else's title: Use context to get the value, don't need to use props to receive console.log(context.attrs.othertitle); // output undefined, because context doesn't need props to receive console.log(context.attrs.mytitle); Const sonHander = function () {context.emit("sonclick", "child to parent "); }; return { sonHander }; }}; </script>Copy the code

1.3 Child Components send events to parent components

<template> <div class="box" @click="sonHander"> </div> </template> <script> export default {props, Context) {const sonHander = function () {context.emit("sonclick", "child to parent "); }; return { sonHander }; }}; </script>Copy the code

1.4 Optimized event Dispatch

We know that the second context parameter is an object, and the object has three attributes, attrs,slots, and emit.

<template> <div class="box" @click="sonHander"> </div> </template> <script> export default {props, Const sonHander = function () {emit("sonclick", "child component to parent component "); }; return { sonHander }; }}; </script> <style> </style>Copy the code

1.5 Get the value passed by the parent component

We’ll get the value using the props parameter and attrs.

<template> <div class="box" @click="sonHander"> </div> <h2> Use the props declaration to receive: {{props. Mytitle}}</h2> <h2> Use the attrs parameter: {{attrs.othertitle}}</h2> </template> <script> export default {// no props: {mytitle: {type: Object,},}, setup(props, {emit, attrs}) {const sonHander = function () {emit ("sonclick", "child to parent "); }; return { sonHander, attrs, props }; }}; </script>Copy the code

2. Life cycle

Vue2 has beforeCreate, Created, beforeMount, Mounted, beforeUpdate and other life cycle functions. In Vue3, these life cycle parts change and the way they are called is also changed. So let’s put a little bit of a change diagram to get a sense of it.

Vue2 Vue3
beforeCreate setup
created setup
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated   onUpdated
beforeDestory onBeforeUnmount
destoryed onUnmounted

2.1 Create hooks for composite apis

For Vue3 lifecycle hooks using the composite API, replace beforecate and Created with the setup() method. This means that any code you put in these methods is now only in the Setup methods.

// AP import {ref} from 'vue' export default {setup() {const val = ref('hello') console.log('Value of val is: ' + val.value) return { val } } }Copy the code

2.2 beforeMount () and onBeforeMount ()

Called before the component DOM is actually rendered and installed. In this step, the root element does not yet exist. In the options API, you can access it using this.$els. In the composition API, in order to do this, ref must be used on the root element.

<template> <div ref='root'> Hello World </div> </template> import {ref, onBeforeMount } from 'vue' export default { setup() { const root = ref(null) onBeforeMount(() => { console.log(root.value) }) return { root } } }Copy the code

Because app.$el has not yet been created, the output will be undefined.

2.3 mounted () and onMounted ()

Called after the first rendering of the component, this element is now available, allowing direct DOM access. Similarly, in the option API, we can use this.$el to access our DOM, and in the composite API, we need to use ref to access the DOM in the Vue lifecycle hook.

import { ref, OnMounted} from 'vue' export default {setup() {/* / const root = ref(null) onMounted(() => { console.log(root.value) }) return { root } } }Copy the code

2.4 beforeUpdate () and onBeforeUpdate ()

Called when data is updated and occurs before the virtual DOM is patched. This is a good place to access the existing DOM before updating, such as manually removing event listeners that have been added.

BeforeUpdate is useful for tracking the number of edits to components and even the actions that create undo functionality.

2.5 updated () and onUpdated ()

When the DOM is updated, the updated method is called.

<template>
    <div>
      <p>{{val}} | edited {{ count }} times</p>
      <button @click='val = Math.random(0, 100)'>Click to Change</button>
    </div>
 </template>
<script>
import { onBeforeUpdate,onUpdated, ref} from 'vue'
export default {
   setup () {
     const count = ref(0)
     const val = ref(0)
 
     onBeforeUpdate(() => {
       count.value++;
       console.log("beforeUpdate");
     })
 
     onUpdated(() => {
       console.log("updated() val: " + val.value)
     })
     return {
       count, val
     }
   }
};
</script>
Copy the code

These methods are useful, but for more scenarios, we need to use the Watch method to detect these data changes. Watch works because it gives the old and new values of the changed data.

Another option is to use computed attributes to change state based on the element.

2.6 beforeUnmount () and onBeforeUnmounted ()

Called before the component instance is unloaded. At this stage, the instance is still perfectly normal.

An example of deleting an event listener in a composite API is shown below.

import { onMounted, onBeforeUnmount } from 'vue' export default { setup () { const someMethod = () => { // do smth } onMounted(() => { console.log('mount') window.addEventListener('resize', someMethod); }) onBeforeUnmount(() => { console.log('unmount') window.removeEventListener('resize', someMethod); }}})Copy the code

The reality is that in Vite, VUE-CLI, or any development environment that supports hot overloading, some components will uninstall and install themselves when the code is updated.

2.7 unmounted () and onUnmounted ()

Called after the component instance is unloaded. When this hook is called, all directives of the component instance are unbound, all event listeners are removed, and all child component instances are unloaded.

Import {onUnmounted} from 'vue' export default {setup () {/* onUnmounted() => {console.log('unmounted') }}})Copy the code

2.8 activated () and onActivated ()

Called when activated by a component cached by keep-alive.

For example, if we use the Keep-alive component to manage different TAB views, the current TAB will run the Activated hook every time we switch between tabs.

Suppose we use the keep-alive wrapper for the following dynamic components.

<template> <div> <span @click='tabName = "Tab1"'>Tab 1 </span> <span @click='tabName = "Tab2"'>Tab 2</span> <keep-alive>  <component :is='tabName' class='tab-area'/> </keep-alive> </div> </template> <script> import Tab1 from './Tab1.vue' import Tab2 from './Tab2.vue' import { ref } from 'vue' export default { components: {Tab1, Tab2}, setup () {/* setup API */ const tabName = ref('Tab1') return {tabName}} </script>Copy the code

Inside the Tab1.vue component, we can access the Activated hook like this.

<template> <div> <h2>Tab 1</h2> <input type='text' placeholder='this content will persist! '/> </div> </template> <script> import { onActivated } from 'vue' export default { setup() { onActivated(() => { console.log('Tab 1 Activated') }) } } </script>Copy the code

2.9 deactivated () and onDeactivated ()

Called when a component cached by keep-alive is disabled.

This hook is useful in use cases such as saving user data and triggering animations when a particular view loses focus.

import { onActivated, onDeactivated } from 'vue'

export default {
  setup() {
    onActivated(() => {
       console.log('Tab 1 Activated')
    })
    onDeactivated(() => {
       console.log('Tab 1 Deactivated')
    })
  }
}
Copy the code

3. reactive

The Reactive method is used to create a reactive data object.

The usage is as simple as passing in the data as an argument, as shown below

<template> <div id="app"> <! Count --> {{state-count}} </div> </template> <script> // 1. Reactive import {reactive} from 'vue' export default {name: 'App', setup() {// 2. Const state = reactive({count: 3}) // 3. Return state for template to use return {state}} </script>Copy the code

4. ref

When we introduced the setup function, we used the ref function to wrap a reactive data object. It looks exactly like reactive, and it does, because ref wraps an object in reactive. Then we pass the value to the value property in the object, which explains why we need to add.value every time we access it

Reactive ({value: obj}) reactive({value: obj})

So let’s write a little bit of code to look at that

<script> import { ref, reactive } from "vue"; export default { setup() { const obj = { count: 3 }; const state1 = ref(obj); const state2 = reactive(obj); console.log(state1.value); console.log(state2); }}; </script>Copy the code

Note: The.value is required to access the ref-wrapped object in the setup function, not in the template template, because at compile time, It automatically recognizes that it’s ref wrapped so how do we choose ref and Reactive?

Advice:

  • Values of primitive types (String, Nmuber, Boolean, and so on) or single-valued objects (similar to objects with a single attribute value like {count: 3}) use ref.
  • Reference type values (Object, Array) Use Reactive.

5. toRef

ToRef is to convert a value in an object into responsive data, which receives two parameters, the first parameter is obJ object; The second parameter is the name of the property in the object

The code is as follows:

<script> // 1. Import toRef import {toRef} from "vue"; export default { setup() { const obj = { count: 3 }; Const state = toRef(obj, "count"); // 3. Return toRef wrapped data object for Template to use return {state}; }}; </script>Copy the code

But on the surface it looks like the toRef API is very useless, because this function can also be implemented with ref, as shown below

Ref import {ref} from 'vue' export default {setup() {const obj = {count: 3} // 2. Const state = ref(obj.count) // 3. Return the ref wrapped data object for template using return {state}}} </script>Copy the code

At first glance, this may seem true, but there is a difference between the two. We can compare this with an example. The code is as follows

< the template > < p > {{state1}} < / p > < button @ click = "add1" > add < / button > < p > {{state2}} < / p > < button @ click = "add2" > add < / button > </template> <script> import { ref, toRef } from "vue"; export default { setup() { const obj = { count: 3 }; const state1 = ref(obj.count); const state2 = toRef(obj, "count"); function add1() { state1.value++; Console. log(" original value: ", obj); Console. log(" Responsive data object: ", state1); } function add2() { state2.value++; Console. log(" original value: ", obj); Console. log(" Responsive data object: ", state2); } return { state1, state2, add1, add2 }; }}; </script>Copy the code

We use ref and toRef respectively to convert count in obj to reactive, and declare two methods to increase count, respectively. After each increment, we print the original value obj and the wrapped reactive data object, and also look at the view changes. ref:

  • As you can see, after the +1 operation on the value of the responsive data, the view changes, the original value remains unchanged, and the value of the responsive data object also changes. This indicates that ref is a copy of the original data, and the original value will not be affected. Meanwhile, the view will be updated synchronously after the value of the responsive data object changes.

ToRef:

  • As you can see, after the +1 operation on the value of the reactive data, the view does not change. The original value is changed, and so is the value of the reactive data object. This indicates that toRef is a reference to the original data and will affect the original value, but will the view be updated after the value of the reactive data object changes.

Conclusion:

  • Ref is a copy of the incoming data; ToRef is a reference to incoming data
  • Changing the value of ref updates the view; A toRef value change does not update the view

6. toRefs

Now that you know about toRef, it’s easy to understand toRefs, which converts the values of all the attributes in the passed object into a responsive data object. This function supports one parameter, an OBJ object.

Let’s take a look at the basic uses

<script> // 1. ToRefs import {toRefs} from "vue"; Export default {setup() {const obj = {name: "front-end impression ", age: 22, gender: 0,}; Const state = toRefs(obj); Console. log(state); }}; </script>Copy the code

The print result is as follows:

It returns an object containing each wrapped reactive data object.

7. toRaw

The toRaw method is used to retrieve raw data from a REF or Reactive object.

Let’s start with a piece of code

< the template > < p > {{state. The name}} < / p > < p > {{state. The age}} < / p > < button @ click = "change" > change < / button > < / template > < script > import { reactive } from "vue"; Export default {setup() {const obj = {name: "front-end impression ", age: 22,}; const state = reactive(obj); function change() { state.age = 90; console.log(obj); // Print raw data obj console.log(state); // Print reactive object} return {state, change}; }}; </script>Copy the code

When we change the data in the Reactive object, we see that the value of the original data OBj and the object wrapped by Reactive have changed, so that we can see that the two are a reference.

So now we’re thinking, what happens if we just change the value of obj, the original value? The answer is: The value of reactive changes, but the view does not update.

Thus, when we want to modify the data but do not want the view to update, we can choose to directly modify the value of the original data, so we need to obtain the original data first, we can use the toRaw method provided by Vue3

ToRaw receives a parameter, a REF object or a Reactive object

<script> import { reactive, toRaw } from "vue"; Export default {setup() {const obj = {name: "front-end impression ", age: 22,}; const state = reactive(obj); const raw = toRaw(state); console.log(obj === raw); // true }, }; </script>Copy the code

This code demonstrates that the toRaw method fetches raw data from reactive objects, so it is easy to do performance optimizations by modifying the raw data without updating the view. Note: As a side note, when the toRaw method receives a ref object, it needs to add.value to get the original data object.

8. provide && inject

Provide and inject the same functions as those in Vue2, but in Vue3, you need to import them manually from vUE.

Here’s a quick explanation of how these two methods work:

  • Provide: Pass data to child and descendant components. The first parameter is key, which is the name of the data. The second argument is value, the value of the data
  • Inject: Receives data passed by parent or ancestor components. Receives a parameter key, which is the name of the data passed by the parent or ancestor component

Assume that there are three components: A.vue, B.vue, and C.vue, where B.vue is a sub-component of A.vue and C.vue is a sub-component of B. Vue

// A.vue <script> import { provide } from "vue"; Export default {setup() {const obj = {name: "front-end impression ", age: 22,}; // Provide data named info to child and descendant components ("info", obj); }}; </script> // B.vue <script> import { inject } from "vue"; Export default {setup() {// Receive data inject("info"); // {name: 'front-end impression ', age: 22}},}; </script> // C.vue <script> import { inject } from "vue"; Export default {setup() {// Receive data inject("info"); // {name: 'front-end impression ', age: 22}},}; </script>Copy the code

9. watch && watchEffect

Both Watch and watchEffect are used to monitor a change in data to perform a specified action, but there are differences in their usage. Watch: watch(source, cb, [options])

  • Source: Can be an expression or a function that specifies the dependent object to listen on
  • Cb: callback function executed after the dependent object changes
  • Options: Optional. You can configure the following properties: immediate (triggers the callback function immediately) and deep (deep listening)

When listening on a ref type:

<script> import { ref, watch } from "vue"; export default { setup() { const state = ref(0); Watch (state, (newValue, oldValue) => {console.log(' original value is ${oldValue} '); Console. log(' newValue ${newValue} '); /* Prints the result after 1 second: original value is 0 new value is 1 */}); SetTimeout (() => {state.value++; }, 1000); }}; </script>Copy the code

When listening to reactive:

<script> import { reactive, watch } from "vue"; export default { setup() { const state = reactive({ count: 0 }); Watch (() => state.count, (newValue, oldValue) => {console.log(' original value is ${oldValue} '); Console. log(' newValue ${newValue} '); /* Prints the result after 1 second: original value is 0 new value is 1 */}); SetTimeout (() => {state.count++; }, 1000); }}; </script>Copy the code

When listening for more than one value at a time:

<script> import { reactive, watch } from "vue"; export default { setup() { const state = reactive({ count: 0, name: "zs" }); watch( [() => state.count, () => state.name], ([newCount, newName], [oldvCount, oldvName]) => { console.log(oldvCount); // Old count value console.log(newCount); // The new count value console.log(oldName); // Old name value console.log(newvName); // New name value}); setTimeout(() => { state.count++; state.name = "ls"; }, 1000); }}; </script>Copy the code

Since the first parameter of the watch method has been specified, the callback function in the second parameter is not executed when the component is initialized. If we want the component to be initialized first, we can set the third parameter object to immediate: true

For example, if the data we are listening to is nested in multiple layers, the change of the deep data will not trigger the callback. If we want it to listen on the deep data, we can set deep: true in the third parameter object

Add: The watch method returns a stop method that you can execute if you want to stop listening

Next, let’s talk about watchEffect, which differs from Watch in the following points:

  • There is no need to pass in dependencies manually
  • A callback function is executed for each initialization to automatically retrieve dependencies
  • You can’t get the original value, you can only get the changed value

Let’s see how this works:

<script> import { reactive, watchEffect } from "vue"; export default { setup() { const state = reactive({ count: 0, name: "zs" }); watchEffect(() => { console.log(state.count); console.log(state.name); /* Prints when initializing: 0 zs prints after 1 second: 1 ls */}); setTimeout(() => { state.count++; state.name = "ls"; }, 1000); }}; </script>Copy the code

As you can see from the code above, instead of passing a dependency to the watch method first, we specify a callback function directly. When the component is initialized, the callback function is executed once and the data to be detected is automatically retrieved as state.count and state.name. Based on the above characteristics, we can choose which listener to use.

10. computed

Computed (computed properties) passes in a getter function that returns a ref object that cannot be manually modified by default, and is used much differently than VUE 2.0.

Computed is used by default

<template> <div class="home"> <p>  <button type="button" @click="changeAge(-1)">-</button> {{ age }} <button type="button" @click="changeAge(1)"> </button> <p> <p> {{ year }}</p> </div> </template> <script> import { computed, ref } from "vue"; export default { name: "Home", setup() { const age = ref(19); function changeAge(value) { age.value += value; } const year = computed(() => { return 2020 - age.value; }); return { year, age, changeAge, }; }}; </script>Copy the code

The computed getter and setter

<template> <div class="home"> <p>  <button type="button" @click="changeAge(-1)">-</button> {{ age }} <button type="button" @click="changeAge(1)"> </button> <p> <p>  <button type="button" @click="changeYear(-1)">-</button> {{ year }} <button type="button" @click="changeYear(1)">+</button> </p> </div> </template> <script> import { computed, ref } from "vue"; export default { name: "Home", components: {}, setup() { const age = ref(19); function changeAge(value) { age.value += value; } const year = computed({ get() { return 2020 - age.value; }, set(val) { age.value = 2020 - val; }}); function changeYear(value) { year.value = year.value + value; } return { year, age, changeYear, changeAge, }; }}; </script>Copy the code

Four, components,

1. Know the components

In the previous case, we just created a component App; If we had an application that put all of its logic in one component, that component would be very bloated and difficult to maintain; So the core idea of componentization should be to break up components into smaller components; These components are then combined and nested together to form our application.

Let’s examine the nested logic of the following code, if we put all the code logic into a single app.vue component:

<template> <div id="app"> <div class="header"><h2> </h2></div> Hee hee 1</li> <li> I am Banner, hee hee 2</li> <li> I am Banner, hee hee 3</li> <ul> <ul> <li> I am ProductList, hee hee 1</li> <li> I am ProductList, Hee hee 2</li> <li> I am ProductList, hee hee 3</li> </ul> </div> <div class="footer"><h2> Here is footer, hey hey </h2></div> </div> </template>Copy the code

We will find that the code is very bloated and difficult to maintain by putting all the code logic into one component.

And in real development, we would have more content and code logic, which would be very poor for scalability and maintainability.

So, in real development, components are broken down into functional pieces.

2. Component splitting

We can split it as follows:According to the above way of splitting, we only need to develop the corresponding logic to the corresponding component writing. Individual components to do their own functions, of course, the components can be divided into multiple components.

3. Global components

Mount the definition directly. Global components are reusable and do not affect each other. Component definitions are available everywhere throughout the instance, but have a significant impact on performance.

// Global components are reusable and do not affect each other. Components define that are available everywhere, <div id="root"> </div> <script SRC ="https://unpkg.com/vue@next"></script> <script> const app = vue.createApp ({ template: ` <div><counter-parent/></div> <div><counter/></div> <div><counter/></div>` }); Ponent ('counter', {data() {return {count: 0}}, template: `<button @click="count += 1">{{count}}</button>` }); app.component('counter-parent', { template: `<counter/>` }); app.mount("#root"); </script>Copy the code

4. Local components

Local components are commonly used in work, unlike global components, which are mapping objects to be named. Note: Locally registered components are not available in their children.

<div id="root"> </div> <script src="https://unpkg.com/vue@next"></script> <script> const Counter = { data() { return { count: 0 } }, template: `<button @click="count += 1">{{count}}</button>` }; const HelloWorld = { template: `<div>hello</div>` }; Const app = vue. createApp({components: {'counter': counter, 'hello-world': HelloWorld}, // Mount local components, order is important. Counter //components: {Counter, HelloWorld} template: '<div>< Counter /></div> <div>< HelloWorld /></div>'}); app.mount("#root"); </script>Copy the code

For ES2015+, a variable name for ComponentA is ComponentA: ComponentA. For ES2015+, a variable name for ComponentA is ComponentA.

  • The name of the custom element used in the template
  • The variable name that contains the component option

5. The component name

There are two ways to define component names:

5.1 use kebab – case

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

When defining a component using kebab-case (dash delimited naming), you must use kebab-case when referencing the custom element.

5.2 use PascalCase

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

When defining a component using PascalCase, both nomenclature can be used when referring to the custom element. However, only kebab-case is valid when used directly in the DOM (that is, non-engineered).

6. The component’s data option must be a function

  • The main purpose is to create a new object without creating a new component, rather than sharing the same object.
  • The result of sharing an object is that the data inside each component can be modified by other components, since multiple components point to the same memory heap control.

7. Communication between components

The nested logic above is as follows, and they have the following relationship:

  • The App component is the parent of the Header, Main, and Footer components
  • Main is the parent of the Banner and ProductList components

During development, we often encounter the need for components to communicate with each other:

For example, the App may use multiple headers, and the contents of each Header are different. Then we need the user to pass some data to the Header for display.

For example, we request Banner data and ProductList data in Main at once, so we need to pass them to display; It could also be that an event has occurred in a child component that needs to be done by the parent, which requires the child to pass the event to the parent.

In conclusion, communication between components is a very important part of a Vue project, so let’s take a look at how data is transferred between components.

Do not modify the parent’s state data directly in the child (). The data and the functions that process it should be in the same module.

7.1 Parent component to child component

For example, the parent component has some data and needs the child component to present it: in this case, we can use props to complete the communication between components

Props allows you to register some custom properties on components; The parent component assigns values to these properties, and the child component gets the values from the properties’ names.

There are two common uses for props:

  • A:String arrayThe string in the array is the name of the property
  • Method 2:Object typeObject type We can specify an attribute name along with the type to be passed, whether it is required, the default value, and so on

1) Array usage of propsWe can only specify the names of the properties passed in and don’t restrict them in any way. Let’s look at how object writing makes our props more complete.

When using object syntax, we can restrict incoming content even more:

  • Specifies the type of the attribute passed in
  • Specifies whether the attribute passed in is mandatory
  • Specifies the default value for the property if no incoming
Props: {// Specify the type, default value title: {type: String, default: "default value title"}, // Specify the type, whether to pass message: {type: String, required: true }, }Copy the code

7.2 Child component passes to parent component

When does a subcomponent need to pass content to its parent?

When something happens to the child component, such as a click in the component, the parent component needs to switch the content; When a child component has something it wants to pass to its parent.

How do we do that?

  • 1) First of all, we need to be inThe name of the event that is defined in the child component to fire under certain circumstances;
  • 2) Secondly, in theThe name of the event to listen for is passed v-on from the parent componentAnd bind to the corresponding method;
  • 3) Finally, when an event occurs in a child component,Triggers an event based on the event name;

We package a counter button component that listens for the clicks of the two buttons and emits events through emit after the clicks.

<template>
  <div>
    <button @click="increment">+</button> <button @click="decrement">-</button>
    <h2></h2>
    <input type="text" v-model.number="num" /> <button @click="addN">+n</button>
  </div>
</template>

<script>
import {ref} from 'vue';
export default {
  name: "User",
  setup(props, {emit}) {
    let num = ref(0)
    function increment(){ 
      emit('add');
    }
    function decrement(){ 
      emit('sub')
    }
    function addN(){        
      console.log(num.value)        
      emit('addN',num.value)  
    }
    return {num, increment, decrement, addN}
  }
}
</script>
Copy the code

When the above components are introduced, events are listened for

<template>
  <div>
    {{ counter }}
    <counter-com @add="increment" @sub="decrement" @addN="addN"> </counter-com>
  </div>
</template>
<script>
import CounterCom from "./views/User.vue";
import {ref} from 'vue';
export default {
  components: { CounterCom },
  setup(){
    let counter = ref(0);
    function increment() {
      counter.value++;
    }
    function decrement() {
      counter.value--;
    }
    function addN(num) {
      counter.value += num;
    }
    return {
      counter,
      increment,
      decrement,
      addN
    }
  }
};
</script>
Copy the code

To customize the event, we can also pass some parameters to the parent component:

emit('addN',num.value)
Copy the code

7.3 Parent and Child Components Access Each other – Parent component accesses child component $refs

Main steps:

  • Bind a REF message to the child component.
  • Get this ref information in the parent component via an event and in the callback function via refs.
<div id="app"> <! <button @click="getChildrenCom"> </button> </div> <template id="sub"> </h3> < button@click ="clickSub"> </ button@click ="clickSub"> </button> </template> <script> Return {MSG: 'subcomponent information'}}, methods: {printinfo() {console.log(' method from child component ')}, clickSub() {alert(' button from child component ')}}, template: Const app = vue.createApp ({data() {return {MSG: 'default info'}}, methods: {getChildrenCom() {//this.$refs.info.msg) this.$refs.info.printinfo()}}, components: { 'subcom': sub } }) app.mount('#app') </script>Copy the code

7.4 Parent and Child Components Access Each other – Child components access parent component $parent

It is generally not recommended that child components access the parent component, which may damage the reusability of the parent component.

<div id="app"> <sub-Box></sub-Box> </div> <! <template id="subbutton"> <h3> This is the first subbutton subcomponent </h3> <button @click="subtbclickTime"> </button> </template> <! <template id="subBox"> <p> This is the subBox component </p> <subbuttoncom></subbuttoncom> </template> <script> // const subbutton = { data() { return { count: 0 } }, methods: {subtbclickTime() {this.count++ // child access parent // console.log(this.$parent.parentinfo) // $parent.$parent) // console.log(this.$root) console.log(this.$root)}}, template: Const subBox = {data() {return {parentInfo: 'parentInfo'}}, components: {'subbuttoncom': subbutton}, template: '#subBox',} // This is the root component const app = vue.createApp ({data() {return {MSG: }}, components: {'subBox': subBox}}) app.mount('#app') </script>Copy the code

8. Slot

8.1 What is a Slot

A slot is a placeholder provided by a child component to a parent component. The parent component can fill this placeholder with any template code, such as HTML, components, etc., replacing the child component tag.

Or to put it another way: Slot is created so that the parent component can add content to the child component.

8.2 Slot Usage

8.2.1 Anonymous Slot (Single slot, default slot)

Anonymous slots are slots that do not have the name attribute set. Can be placed anywhere in the component. There can only be one anonymous slot in a component. As an alternate slot in case a matching piece of content is not found. Anonymous slots can only be slots for elements that do not have a slot attribute.

<div class="child"> <h1> subcomponent </h1> <slot name="head"> Header default </slot> <slot name="body"> body default </slot> <slot> This is an anonymous slot (no name attribute), This string of characters is the default value for anonymous slots. </slot> </div>Copy the code
< div class = "parent" > < h1 > < / h1 > parent component child > < < p slot = "body" > I am a body < / p > < p > I'm other content < / p > < p slot = "footer" > I am a tail < / p > < / child > </div>Copy the code
Parent component child component header defaults (Default values for head are rendered: defaults are only rendered when no content is provided.) I am the body (the default value of the body is overridden) I am the other content (the default value of the name slot is overridden)Copy the code

Note:

<p slot="footer"> I am tail </p>Copy the code

Is discarded because there is no matching slot in the child component with name=”footer”. If the anonymous slot in the subcomponent does not exist, the <p> I am other content </p> is also discarded.

8.2.2 Named slot

A slot with a name, defined by the attribute name. A component can have many named slots in different locations.

<! -- <base-layout> component --> <div class="container"> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div>Copy the code

You can use the V-slot directive on a

<base-layout> <template V-slot :header> <h1> I'm a header</h1> </template> <p> I'm the content of main 111</p> <p> I'm also the content of main 222</p> <template V-slot :footer> <p> I am footer</p> </template> </base-layout>Copy the code

Note: Anything that is not wrapped in a template with v-slot is treated as the content of the default slot. A slot slot without a name carries the implied name default. If you want to be more explicit, you can wrap the contents of the default slot in a <template> :

<base-layout> <template V-slot :header> <h1> I am the header</h1> </template> <template V-slot :default> <p> I am the content of main 111</p> <p> I am also the content of main 222</p> </template> <template V-slot :footer> <p> I am footer</p> </template> </base-layout>Copy the code

Note that V-slot can only be added to

8.2.3 Scope slot

<! -- <Child> component: <template> <div> <h1>hey, I am the title of the component Child </h1> <slot></slot> </div> </template> <script> export default {data() {return{ childUser:{Name:"Tom",Age:23} } } </script>Copy the code

When using the Child component, you want to access the data in the Child, childUser, and display it in the slot:

<! </h1> <Child> {{childuser. Name}}, {{childuser. Age}} </Child> </div>Copy the code

However, the above code will not work because everything in the parent template is compiled in the parent scope; Everything in a child template is compiled in a child scope. Only the

component can access childUser, and we provide:

{{childUser. Name}}, {{childUser. Age}}Copy the code

Is rendered at the parent

level. In order for childUser to be available in the parent’s slot content, childUser needs to be passed from the

Child scope to the

parent scope. To do this, bind childUser as an attribute of the

element:



<template>
  <div>
    <h1>hey,我是组件Child的标题</h1>
    <slot v-bind:childData="childUser"></slot>
  </div>
</template>
<script>
export default {
  data() {
     return{
        childUser:{Name:"Tom",Age:23}
    }
}
</script>
Copy the code

The property childData bound to the

element is called slot prop. Now in parent scope, we can define the name of a slot prop using v-slot with a value:

<! <div> <h1> Hey, </h1> <Child> <template V-slot :default="slotProps"> {{slotProps. Childdata.name}} {{ slotProps.childData.Age}} </template> </Child> </div>Copy the code

In this example, we named the object that contains all the slotProps slotProps, which can also be customized. Because in this case, only the default slot is supplied, the component’s label can be used as a template for the slot. This allows us to use v-slot directly on components:

<! <div> <h1> Hey, <Child V-slot :default="slotProps"> {{slotProps. Childdata.name}} {{slotProps. Childdata.age}} </Child> </div>Copy the code

However, the default slot’s abbreviated syntax cannot be mixed with named slots, as it would result in undefined scope:

<! - invalid, Child V-slot ="slotProps"> {{slotProps. Childdata. Name}} <template V-slot :other="otherSlotProps"> slotProps is NOT available here </template> </Child >Copy the code

Whenever multiple slots are present, always use the full

<Child >
  <template v-slot:default="slotProps">
    {{ slotProps.childData.Name }}
  </template>

  <template v-slot:other="otherSlotProps">
    ...
  </template>
</Child>
Copy the code

8.2.4 Deconstructing a Slot Prop

The inner workings of a scoped slot are to include the contents of your slot in a function that passes in a single argument, so this means that the value of the V-slot can actually be any JavaScript expression that can be used as an argument in the function definition.

<Child  v-slot="{ childData}">
  {{ childData.Name }}
</Child>
Copy the code

This can make the template more concise, especially if the slot provides multiple prop. It also opens up other possibilities for prop renaming, such as renaming childData to Person:

<Child   v-slot="{ childData: person }">
  {{ person.Name }}
</Child  >
Copy the code

You can even define the default content for cases where slot prop is undefined:

<Child   v-slot="{ childData= { Name: 'Guest' } }">
  {{ childData.Name }}
</Child >
Copy the code

Like V-ON and V-bind, v-slot is abbreviated by replacing v-slot: with the character #. For example, v-slot:header can be rewritten as #header. Like other instructions, this abbreviation is only available if it has arguments.

If you wish to use abbreviations, you must always use the explicit slot name instead. Default cannot be omitted:

<Child  #default="{ childData}">
  {{ childData.Name }}
</Child >
Copy the code

9. Dynamic components

  • When switching between components, you sometimes want to keep the state of those components to avoid performance issues caused by repeated rerendering.
  • A dynamic component dynamically switches the display code of a component based on changes in data, combined with the Component tag. In effect, the component name is dynamically adjusted.

Use dynamic components: is= ‘dynamic variables’ to render components dynamically, essentially letting the component name change dynamically.

<body> <div id="app"> </div> <script> // here is the root component const app = vue.createApp ({data() {return {// here the value of the switch variable is the name of the component showItem: 'mybutton' // myinput}}, methods: { changeHandler() { if (this.showItem === 'mybutton') { this.showItem = 'myinput' } else { this.showItem = 'mybutton' } }}, // here we use :is=" data name "to perform dynamic display. For the deactivated component we use keep-alive to cache the previously entered data template: '<keep-alive> <component :is="showItem"></component> </keep-alive> <button @click="changeHandler"> Switch </button>'}) App.component.ponent ('mybutton', {template: '<button>'}) app.component.ponent ('mybutton', {template: '<button>'}) ` < input type = "text" placeholder = "please enter something" / > `}) app. Mount (' # app) < / script > < / body >Copy the code

10. Dynamic binding parameter transmission for the props transmission value of the parent and child components

<body> <div id="app"> <subcom></subcom> </div> <script> // Create a subcomponent const sub = {data() {return {mytotal: this.total } }, props: ['number1', 'number2', 'number3', 'number4'], template: ` < h3 > I was a child component < / h3 > < div > data 1: {{number1}} < / div > < div > data 2: {{number2}} < / div > < div > data 3: {{number3}} < / div > < div > 4: Const app = vue.createApp ({data() {return {numberObj: {number1: 100, number2: 200, number3: 300, number4: 400, } } }, components: { 'subcom': sub }, template: ` <subcom v-bind="numberObj"></subcom> ` }) app.mount('#app') </script> </bodyCopy the code

11. The parent component of no-props passes tag properties to its children

Non-prop properties: When a parent passes content to a child component and the child component does not receive it via pros, the situation is as follows:

  • 1. If a subcomponent has a single root node
    • The bottom layer places the content passed by the parent component on the outermost DOM tag of the child component as an attribute of the outermost DOM tag of the child component
    • If you don’t want to display this attribute on a child component’s tag, you can use inheritAttrs: False to not inherit the non-props attribute passed by the parent component
  • 2. The subcomponent has multiple root nodes
    • If you want the non-props directive passed by the parent to work,
    • You can use v-bind=’$attrs’ to put all the non-props attributes passed by the parent component on the specified div,
    • You can also specify specific attributes, such as: MSG =’$attrs.msg’
  • 3. No matter inheritAttrs to true or false, the child components can be by $attrs get passed in to the parent component attribute, the default value is true.
<body> <div id="app"> </div> <script> // Create a sub const sub = {props: ['info'], template: ` < h3 > I am child components - {{info}} < / h3 > < div: info = "$attrs. Info" > I am child components - {{info}} < / div > < div : $attrs. Style style = "" > I am child components - {{info}} < / div > < div v - bind =" $attrs "> I am child components - {{info}} < / div > `} / / this is the root component const app = CreateApp ({components: {'subcom': sub}, // Parent to child template: '<subcom info="test attrs" style="width: 200px; height: 100px; background-color: red"></subcom> ` }) app.mount('#app') </script> </body>Copy the code

ES6 Promise

1.ES6 Promise comes out for a walk

Instead of talking about complex concepts, let’s use Promise briefly and roughly to get a sense of what it’s like. So the first question is, what is Promise? Is it a class? Objects? An array? Function? Don’t guess, just print it out, console.dir(Promise). It’s as simple as that.Promise is a constructor with familiar methods all, Reject, and resolve on its own, and equally familiar methods like THEN and catch on its prototype. If you use a Promise new, you can use “then” or “catch” methods. Let’s play with a new one.

Var p = new Promise(function(resolve, reject){setTimeout(function(){console.log(' done '); Resolve (' whatever data '); }, 2000); });Copy the code

Promise’s constructor takes one argument, which is a function, and passes two arguments, resolve and reject, which represent the successful and unsuccessful callback of the asynchronous operation. In fact, it is not accurate to describe “success” and “failure”. According to the standard, resolve is the state of the Promise changed to fullfiled, and reject is the state of the Promise changed to rejected. But we can think of it this way at the beginning, and we’ll get into the concepts later.

In the above code, we perform an asynchronous operation, setTimeout, and after 2 seconds, print “Execution complete” and call the resolve method. Running the code will print “Execution completed” after 2 seconds. Attention! I’m just new an object, I’m not calling it, and the function we passed in is already executed, so that’s a fine detail to notice. So when we use promises, we usually wrap them in a function and run the function if we need to, like:

function runAsync(){ var p = new Promise(function(resolve, Reject){// do something asynchronous setTimeout(function(){console.log(' done '); Resolve (' whatever data '); }, 2000); }); return p; } runAsync()Copy the code

At this point you should have two questions: 1. Is wrapping such a function useful? 2. Resolve (‘ whatever data ‘); Is this made of dry wool? Let’s move on. At the end of our wrapped function, we return a Promise object, that is, we get a Promise object by executing this function. Remember the then and catch methods on Promise objects? That’s where the power comes in. Look at the code below

runAsync().then(function(data){ console.log(data); // you can do some other things with the data //...... });Copy the code

Call the then method directly on the return of runAsync(). The then takes an argument, which is a function, and takes the argument we passed to resolve in runAsync. Running this code will output “execution completed” two seconds later, followed by “whatever data.” The then function is a callback function that can be executed after a runAsync task has finished executing. That’s where Promise comes in. In a nutshell, it’s the ability to separate out the original callback writing and execute the callback as a chain call after the asynchronous operation. You might dismiss it, but is that all a big Promise can do? I wrapped the callback function and passed it to runAsync as well, like this:

Function runAsync(callback){setTimeout(function(){console.log(' done '); Callback (' whatever data '); }, 2000); } runAsync(function(data){ console.log(data); });Copy the code

The effect is the same. Why bother using Promise. So the question is, what do you do with multiple layers of callbacks? What if callback is also an asynchronous operation that requires a callback function after execution? You can’t define a callback2 and pass it to the callback. The advantage of promises is that you can continue writing the Promise object in the THEN method and return it, and then continue calling the THEN for callback operations.

2. Usage of chain operation

Therefore, on the surface, Promise can only simplify the writing method of layer upon layer callback. In essence, the essence of Promise is “state”, which can be invoked in time by maintaining state and transferring state. It is much simpler and more flexible than passing callback function. So the correct scenario for using promises looks like this:

runAsync1()
.then(function(data){
    console.log(data);
    return runAsync2();
})
.then(function(data){
    console.log(data);
    return runAsync3();
})
.then(function(data){
    console.log(data);
});
Copy the code

This outputs the contents of each asynchronous callback every two seconds, in order, and the data passed to resolve in runAsync2 can be retrieved in the subsequent then method. The running results are as follows:

Guess how runAsync1, runAsync2, and runAsync3 are defined? Yes, here it is:

function runAsync1(){ var p = new Promise(function(resolve, Reject){// do something async setTimeout(function(){console.log(' async task 1 completed '); Resolve (' whatever data 1'); }, 1000); }); return p; } function runAsync2(){ var p = new Promise(function(resolve, Reject){// do something async setTimeout(function(){console.log(' async task 2 completed '); Resolve (' whatever data 2'); }, 2000); }); return p; } function runAsync3(){ var p = new Promise(function(resolve, Reject){// do something async setTimeout(function(){console.log(' async task 3 completed '); Resolve (' whatever data 3'); }, 2000); }); return p; }Copy the code

In the THEN method, you can also return the data directly instead of the Promise object and then receive the data in the later THEN. For example, we can change the above code to look like this:

runAsync1() .then(function(data){ console.log(data); return runAsync2(); }) .then(function(data){ console.log(data); Return 'directly return data '; }). Then (function(data){console.log(data); });Copy the code

The output then looks like this:

3. The use of the reject

At this point, you should have a basic idea of what promises are. So let’s take a look at what else ES6 Promises can do. We’ve just used resolve, not reject, what does it do? In fact, all of our previous examples are ‘execute successfully’ callbacks, with no ‘fail’ yet. Reject sets the Promise state to rejected, so we can catch the ‘fail’ callback in THEN. Look at the code below.

function getNumber(){ var p = new Promise(function(resolve, Function (){var num = math.ceil (math.random ()*10); If (num<=5){resolve(num); } else{reject(' numbers are too large '); }}, 2000); }); return p; } getNumber() .then( function(data){ console.log('resolved'); console.log(data); }, function(reason){ console.log('rejected'); console.log(reason); });Copy the code

If the number is less than or equal to 5, we consider it a “success” and call resolve to change the state of the Promise. Otherwise we consider it a “failure,” call Reject and pass an argument as the reason for the failure. Run getNumber and pass two arguments in THEN. The then method accepts two arguments, the first for the resolve callback and the second for the reject callback. So we were able to get the data separately from them. Run this code many times and you will get one of two random results:

4. The use of the catch

We know that a Promise object has a catch method as well as a then method, so what does that do? This, like the second argument to then, specifies the reject callback, as follows:

getNumber()
.then(function(data){
    console.log('resolved');
    console.log(data);
})
.catch(function(reason){
    console.log('rejected');
    console.log(reason);
});
Copy the code

The effect is the same as in the second argument to then. However, it also serves another purpose: if an exception is thrown when the resolve callback (the first argument to then above) is executed, it does not trap the JS, but instead goes into the catch method. Take a look at the following code:

That is, it is inside the catch method and passes the cause of the error to the reason argument. Even if the code has an error, it will not report an error, which has the same functionality as our try/catch statement.

5. The use of the all

Promise’s All method provides the ability to execute asynchronous operations in parallel and not execute callbacks until all asynchronous operations have been executed. We still use the three functions defined above, runAsync1, runAsync2, runAsync3, as shown in the following example:

Promise
.all([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
    console.log(results);
});
Copy the code

All, which takes an array of arguments, and the values in it will eventually return a Promise object. Thus, three asynchronous operations that are executed in parallel do not enter the then until they are all executed. So where is the data returned by the three asynchronous operations? It’s all inside then, so all will put the results of all the asynchronous operations into an array and pass it to THEN, which is what results is above. So the output of the above code is:

With ALL, you can perform multiple asynchronous operations in parallel and process all the returned data in a single callback. Isn’t that cool? There is a scene is very suitable for this, some game materials more applications, when opening the web page, pre-load the need to use a variety of resources such as pictures, Flash and various static files. After everything is loaded, we’ll initialize the page.

Six, axios

1. Introduction of axios

Promise based HTTP client for browsers and Node.js

Characteristics of 2.

  • Supports browsers and Node.js
  • Supporting promise
  • Intercepts requests and responses
  • Ability to transform request and response data
  • Can cancel request
  • Automatically convert JSON data
  • Browser-side support against CSRF (Cross-site request Forgery)

3. The installation

NPM install axios –save

Example 4.

4.1 Sending a GET Request

// Send the request with the given ID axios.get('/user? ID=12345') .then(function(response){ console.log(response); }) .catch(function(err){ console.log(err); }); Get ('/user',{params:{ID:12345}}). Then (function(response){console.log(response); }) .catch(function(err){ console.log(err); });Copy the code

4.2 Sending a POST request

axios.post('/user',{
  firstName:'Fred',
  lastName:'Flintstone'
})
.then(function(res){
  console.log(res);
})
.catch(function(err){
  console.log(err);
});
Copy the code

4.3 Concurrent Multiple Requests

function getUserAccount(){ return axios.get('/user/12345'); } function getUserPermissions(){ return axios.get('/user/12345/permissions'); } axios.all([getUserAccount(),getUserPermissions()]).then(axios.spread(function(acct,perms){ Two arguments representing the result returned}))Copy the code

5. Axios API

5.1 AXIos can be configured to send requests

5.1.1 axios (config)

// Send a 'POST' request axios({method:"POST", url:'/user/12345', data:{firstName:"Fred", lastName:"Flintstone"}});Copy the code
5.1.2 axios ([url, config])
// Send a 'GET' request (the default request) axios('/user/12345');Copy the code

5.2 Aliases for request modes. Convenient aliases are provided for all supported request modes

axios.request(config);
axios.get(url[,config]);
axios.delete(url[,config]);
axios.head(url[,config]);
axios.post(url[,data[,config]]);
axios.put(url[,data[,config]])
axios.patch(url[,data[,config]])
Copy the code

Note: When using alias methods, url,method, and data parameters do not need to be declared in the configuration

5.3 Concurrency Request (Concurrency)

All (iterable) // Wait until all requests are completed before executing axios.spread(callback)Copy the code

5.4 Create an axiOS instance and customize its configuration

5.4.1 axios. Create (/ config)
var instance = axios.create({
  baseURL:"https://some-domain.com/api/",
  timeout:1000,
  headers: {'X-Custom-Header':'foobar'}
});
Copy the code
5.4.2 Method of instance

Here is the instance method, noting that the configuration already defined will be merged with the configuration of the instance created with create.

axios#request(config)
axios#get(url[,config])
axios#delete(url[,config])
axios#head(url[,config])
axios#post(url[,data[,config]])
axios#put(url[,data[,config]])
axios#patch(url[,data[,config]])
Copy the code

6. The default value

You can set the default configuration to work for all requests

6.1 Default Global Configuration

axios.defaults.baseURL = 'http://api.exmple.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['content-Type'] = 'appliction/x-www-form-urlencoded';
Copy the code

6.2 Custom instance default Settings

Var instance = axios.create({baseURL: 'https://api.example.com'}); / / modify configuration instance.defaults.headers.com mon when instance creation (" Authorization ") = AUTH_TOKEN;Copy the code

6.3 A Priority is configured

The config configuration will be combined in priority order, the default configuration in lib/defauts.js, then the default configuration in the instance, and finally the configuration of the config parameter in the request, as the hierarchy increases, and the latter overrides the previous example.

Var instance = axios.create(); var instance = axios.create(); var instance = axios.create(); / / override the default value of the library / / now all requests should be after 2.5 S will send out the instance. The defaults. Timeout = 2500; Get ('/longRequest',{timeout: 5000});Copy the code

7. The interceptor

7.1 Request and response interceptor

You can intercept requests and responses before they reach a THEN /catch

/ / add a request interceptor axios. Interceptors. Request. Use (function (config) {/ / do something before request return config. },function(err){ //Do something with request error return Promise.reject(error); }); / / add a response to the interceptor axios. Interceptors. Response. Use (function (res) {/ / here to deal with the data returned return res; },function(err){ //Do something with response error return Promise.reject(error); })Copy the code

7.2 Adding interceptors to custom AXIOS instances

var instance = axios.create();
instance.interceptors.request.use(function(){})
Copy the code