Recognize application of Vue learning (2)

I wrote more than 30,000 words in the last article, and I had to delay typing for a while. Now the text can be displayed in real time, which feels so silky haha.

Table of contents

8. Vue componentization - communication between parent and child components

8.1. Parent component passed to child component

8.1.1. Array usage of props

8.1.2. Object usage of props

8.1.3. attribute inheritance

8.2. Passing child components to parent components

8.2.1. emit property

9. Vue Componentization - Slot Slot

9.1. Basic usage of slot 

9.2. Default content of slot

9.3. Effect of Multiple Sockets

9.4. Use of named slots

9.5. Dynamic slot names (understand)

9.6. Syntactic sugar for named slots

9.7. Scoped slots (important)

10. Vue componentization - non-parent-child component communication

10.1. Provide and Inject

10.2. Function writing of Provide and Inject

11. Vue componentization - additional knowledge supplement

11.1. Component life cycle

11.1.1. Component Lifecycle Walkthrough 

11.2. ref references in components

11.3. Use of dynamic components

11.3.1. Display different components with v-if 

11.3.2. Realization of dynamic components

11.4. Use of keep-alive

11.4.1. keep-alive property

11.4.2. The life cycle of the cache component

11.5. Webpack code subpackage

11.5.1. Implement asynchronous components in Vue

11.6. v-model for components

11.7. Use of Mixins

11.7.1. Mixin Merging Rules

11.7.2. Global Mixin Mixin 

12.Vue3 - CompositionAPI

12.1. Understanding the Composition API 

12.2.Parameters of setup function 

12.3. The return value of the setup function

12.4.Reactive API

12.5. Ref API (important) 

12.6. Understanding readonly

12.6.1. Use of readonly

12.7. API for Reactive judgment (understand)

12.8.toRefs (understand)

12.9.toRef (understand)

12.10.ref Other APIs

12.11.computed computed property

12.12. Use of ref in setup

12.13. Life cycle in setup function

12.14. Provide function and Inject function

12.14.1.provide function

12.14.2. Inject function

12.15. watch listens to data

12.15.1. Use of watch

12.15.2.watch options

12.15.3.watchEffect

12.15.4.watchEffect stops listening

 12.16. script setup syntax

12.16.1. Top-level bindings are exposed to templates

12.16.2. Imported components are used directly

12.16.3.defineProps()和defineEmits()

12.16.4. defineExpose()


If you don’t understand anything, you can check the Vue.js official website

8. Vue componentization - communication between parent and child components

In the previous componentization basis, all the logic is put into an App.vue:
  • In the previous case, we just created a component App ;
  • If our application puts all the logic in one component, then this component will become very bloated and difficult to maintain ;
  • Therefore, the core idea of ​​componentization should be to split the components into small components ;
  • Then combine and nest these components together to form our application ;

In real development, we can split the project.

The above nesting logic is as follows, and they have the following relationship:
  • The App component is the parent component of the Header, Main, and Footer components ;
  • The Main component is the parent component of the Banner and ProductList components ;
During the development process, we often encounter the need for components to communicate with each other :
  • For example, the app may use multiple headers , and the content displayed by the headers in each place is different , so we need the user to pass some data to the header for display;
  • Another example is that if we request the Banner data and ProductList data at one time in Main , we need to pass them to them for display;
  • It may also be that an event has occurred in the child component , and some operations need to be completed by the parent component , then the child component needs to pass the event to the parent component ;

Passing between parent and child components is a very important way of communication.

So how to communicate between parent and child components?

  • The parent component is passed to the child component: through the props attribute ;
  • The child component is passed to the parent component: the event is triggered through $emit .

8.1. Parent component passed to child component

 It is very common in development to communicate between parent and child components . For example, the parent component has some data that needs to be displayed by the child component: at this time, we can use props to complete the communication between components ;

What are Props?
  • Props is that you can register some custom attributes on the component ;
  • The parent component assigns values ​​to these attributes , and the child components obtain the corresponding values ​​through the attribute name ;
<!-- App.vue -->
<template>
  <cpn name="zzz" height="1.7" age="18"></cpn> 
</template>

<script>
import cpn from './components/cpn.vue'
 export default {
  data() {
    return {
      
    }
  },
  components: {
    cpn:cpn
  }
}
</script>

<style scoped>

</style>
  1. In the App.vue parent component file, the components object defines a component cpn ;
  2. Use import to introduce the location of the cpn file of the component , and use the cpn tag in the template ;
  3. Fill in the attributes and values ​​to be passed to the subcomponent in the cpn tag , and receive it in the subcomponent cpn.vue.
<!-- cpn.vue -->
<template>
  <div class="infos">
    <h2>姓名:{
   
   {name}}</h2>
    <h2>年龄:{
   
   {age}}</h2>
    <h2>身高:{
   
   {height}}</h2>
  </div>
</template>

<script>
 export default {
  props:["name", "age", "height"]
}
</script>

<style scoped>

</style>
  1. Use the props attribute to receive the incoming attribute attribute name ;
  2. Use the incoming value in the template . If the value used is not defined in the props attribute, it will go to data to find it.

 Props have two common uses:

  1. Method 1: String array , the string in the array is the name of the attribute;
  2. Method 2: Object type , object type We can specify the type of attribute it needs to pass, whether it is necessary, the default value, etc. while specifying the attribute name;

The above demonstration uses the first method, and the following two usages are explained.

8.1.1. Array usage of props

The props of the child component receive the attribute passed by the parent component, which needs to be one-to-one correspondence, and the string array is saved in the props.

This kind of array usage can only explain the name of the attribute passed in , and cannot restrict it in any form , so in actual development, the object usage of props is more used.

8.1.2. Object usage of props

When using the object syntax, we can be more restrictive about what is passed in:

  • Specify the type  of attribute passed in ;
  • Specifies whether the incoming attribute is mandatory ;
  • Specifies the default value of the attribute when no input is specified .
props:{
    name: {
      type: String,
      required: true,
      default: "z"
    },
    age: {
      type: Number,
      required: false,
      default: 10
    },
    height: {
      type: Number,
      required: false,
      default: 1.00
    }
  }

Generally, when the attribute is not mandatory, the default value is not written.

type of type

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

Other ways of writing object types

 props:{
    name: String,//基础的类型检测
    age: [String, Number],//多个可能的类型
    propO:{
      type: Object,//对象和数组的默认值必须从一个工厂函数获取
      default(){
        return { message: "message"}
      }
    }
  }

Prop case naming

The attribute name in HTML is case-insensitive , so the browser will interpret all uppercase characters as lowercase characters ;

This means that when you use templates in the DOM , camelCase (camel case) prop names need to be named using their equivalent kebab-case (dash-separated names) ;

 

8.1.3. attribute inheritance

non-prop attribute 

Before understanding attribute inheritance, you need to understand the concept of non-prop attributes .

  • When we pass a property to a component , but the property does not define the corresponding props or emits , it is called a non- Prop Attribute ;
  • Common ones include class, style, id attributes, etc.;

Attribute inheritance

When a component has a single root node , non-Prop attributes will be automatically added to the root node's attributes .
Simply put, if the parent component passes attributes similar to class, style, and id to the child component, and if the child component has a single root node, the attribute will be added to the root node.
In the following case, the parent component App.vue passes the class and id to the child component cpn.vue. Take a look at the root node of the class and id of the child component in the browser. 
<!-- 父组件传递了class和id App.vue -->
<template>
  <cpn class="cpn" id="cpn1" name="zzz" height="1.7" age="18"></cpn> 
</template>

<script>
import cpn from './components/cpn.vue'
 export default {
  data() {
    return {
      
    }
  },
  components: {
    cpn:cpn
  }
}
</script>

<style scoped>

</style>
<!-- cpn.vue -->
<template>
  <div class="infos">
    <h2>姓名:{
   
   {name}}</h2>
    <h2>年龄:{
   
   {age}}</h2>
    <h2>身高:{
   
   {height}}</h2>
  </div>
</template>

<script>
 export default {
  props:{
    name: String,
    age: [String, Number],
    propO:{
      type: Object,
      default(){
        return { message: "message"}
      }
    }
  }
}
</script>

<style scoped>

</style>

You can see that the root node of the child component inherits the non-prop attribute passed from the parent component .

Disable attribute inheritance

If we don't want the root element of the component to inherit attribute , we can set inheritAttrs: false in the component ;
//cpn.vuecpn.vue
 export default {
  inheritAttrs:false,
}
  • A common case of disabling attribute inheritance is the need to apply attributes to elements other than the root element ;
  • We can access all non-props attributes through $attrs ;
<template>
  <div class="infos">
    <h2 :class="$attrs.class">姓名:{
   
   {name}}</h2>
    <h2>年龄:{
   
   {age}}</h2>
    <h2>身高:{
   
   {height}}</h2>
  </div>
</template>

Attributes of multiple root nodes

If there are no displayed bindings for the attributes of multiple root nodes , a warning will be reported, and we must manually specify which attribute to bind to .

8.2. Passing child components to parent components

There are cases where the parent component is passed to the child component, and there is also a case where the child component is passed to the parent component.

  • For example, when some events occur in the child component , such as a click in the component, the parent component needs to switch the content ;
  • When the child component has some content that it wants to pass to the parent component ;

Now write a case: the parent component displays the number of the counter, and the child component is responsible for increasing the quantity button.

<!-- cpn.vue 子组件-->
<template>
  <div>
    <!-- 1.子组件触发事件 -->
    <button @click="addCount(1)">+1</button>
    <button @click="addCount(5)">+5</button>
    <button @click="addCount(10)">+10</button>
  </div>
</template>

<script>
 export default {
   methods: {
    // 2.子组件将自定义的事件名add和参数count通过this.$emit()传递出去
     addCount(count) {
       this.$emit("add", count)
     }
   }
 }
</script>

<style scoped>

</style>

        1. The subcomponent triggers an event;

        2. The subcomponent passes the custom event name add and the parameter count through this.$emit();

<!-- App.vue 父组件 -->
<template>
  <div>
    <div class="count">当前计数{
   
   {counter}}</div>
    <!-- 3.父组件使用自定义的add事件,并且编写方法 -->
    <cpn @add="addClick"></cpn> 
  </div>
</template>

<script>
import cpn from './components/cpn.vue'
 export default {
  data() {
    return {
      counter: 0
    }
  },
  components: {
    cpn:cpn
  },
  methods: {
    // 4.父组件对自定义事件触发的方法进行处理
    addClick(count) {
      this.counter += count
    }
  },
}
</script>

<style scoped>

</style>

        3. The parent component uses a custom add event and writes the method; 

        4. The parent component processes the method triggered by the custom event. 

8.2.1. emit property

In the subcomponent, you can first register the passed event name in the attribute. Parent components will be prompted when using custom events.

export default {
   emits:["add"],
   methods: {
    // 2.子组件将自定义的事件名add和参数count通过this.$emit()传递出去
     addCount(count) {
       this.$emit("add", count)
     }
   }
 }

9. Vue Componentization - Slot Slot

In development, we often encapsulate reusable components one by one:
  • Earlier, we will pass some data to the component through props, and let the component display it;
  • But in order to make this component more versatile , we cannot limit the content in the component to fixed div, span, etc. elements;
  • For example, in some cases we use a component and hope that the component displays a button , and in some cases we use a component and hope to display a picture ;
  • We should allow users to decide what content and elements are stored in a certain area ;

For example: If we customize a general navigation component - NavBar
  • 1 This component is divided into three areas: left-middle-right , and the content of each area is not fixed;
  • The left area may display a menu icon, or a back button, or nothing;
  • The middle area may display a search box, a list, a title, etc.;
  • The right side may be a text, or an icon, or nothing;

At this time we can define the slot slot:

  • The process of using slots is actually extracting commonality and reserving differences ;
  • We will still encapsulate the common elements and content in the component ;
  • At the same time, slots will be used as placeholders for different elements , allowing the outside world to decide what elements to display;

9.1. Basic usage of slot 

How to use slots?

  • In Vue, the <slot> element is used as the outlet for carrying and distributing content ;
  • In the package component, use the special element <slot> to open a slot for the package component ;
  • What the slot inserts depends on how the parent component uses it;

It is a bit difficult to understand, but it will be clearer with an example.

App.vue is the parent component, and slotContent.vue is the child component. There are slots in slotContent. Adding some elements to the sub-component tag of App will add them to the slot of the sub-component by default.

9.2. Default content of slot

If the parent component does not add content to the slot when calling the child component, the default value of the child component slot will be used.

9.3. Effect of Multiple Sockets

If we want to make a top menu bar, we need to divide the slot into three parts, left, middle and right. If according to the above writing method will have what kind of effect.

 Will the three elements of App.vue correspond to the slot of slotContent.vue in turn?

will not match, but the same content will be displayed three times. Then how can we correspond to it? We need to use named slots to achieve it.

9.4. Use of named slots

In fact, the effect we want to achieve is the display corresponding to the slot. At this time, we can use the named slot:
  • As the name suggests, a named slot is to give the slot a name , and the <slot> element has a special attribute: name ;
  • A slot without a name will have the implicit name default ;
<!-- App.vue-->
<template>
  <div>
    <slot-content title="标题">
      <template v-slot:left>
        <button >left</button>
      </template>
      <template v-slot:center>        
        <h2>center</h2>
      </template>
      <template v-slot:right>       
        <h3>right</h3>
      </template>
    </slot-content>
  </div>
</template>

<script>
import slotContent from './components/slotContent.vue'
 export default {
  components: {
    slotContent: slotContent
  },
}
</script>
<!-- slotContent.vue -->
<template>
  <div>
    <h2>{
   
   {title}}</h2>
    <div>
      <div class="left">
        <slot name="left">
        </slot>       
      </div>
      <div class="center">
        <slot name="center">
        </slot>       
      </div>
      <div class="right">
        <slot name="right">
        </slot>       
      </div>
    </div>
  </div>
</template>

<script>
 export default {
  props: {
    title: {
      type:String
    },
    content: {
      type: String
    }
  }
}
</script>

  1. Add a name to the slot element of the child component so that the parent component can use it;
  2. Wrap the content to be inserted into the subcomponent slot with a template element ;
  3. Use v-slot:name to correspond to the slot position of the subcomponent, must use: to connect the slot name .

 Emphasize again the error-prone point: use template element to wrap; v-slot and name are separated by :.

9.5. Dynamic slot names (understand)

We can dynamically bind a name through v-slot:[dynamicSlotName] ; 

You can put the variables in [] in data.

9.6. Syntactic sugar for named slots

You can use #abbreviation when using named slots
  • Like v-on and v-bind, v-slot also has abbreviations ;
  • That is, replace everything before the parameter (v-slot:) with the character # ;
<template>
  <div>
    <slot-content title="标题">
      <template #left>
        <button >left</button>
      </template>
      <template #center>        
        <h2>center</h2>
      </template>
      <template #right>       
        <h3>right</h3>
      </template>
    </slot-content>
  </div>
</template>

After this modification, the effect of #left is the same as that of v-slot:left.

9.7. Scoped slots (important)

This concept is quite difficult, through a case to illustrate.

<!--App.vue-->
<template>
  <div>
    <slot-content :names="names">
      <template v-slot:default="slotProps">
          <span>{
   
   {slotProps.item}}-{
   
   {slotProps.index}}</span>
      </template>
    </slot-content>
  </div>
</template>

<script>
import slotContent from './components/slotContent.vue'
 export default {
  data() {
    return {
      names: ["apple","banana","cat","dog"]
    }
  },
  components: {
    slotContent: slotContent
  },
 
}
</script>
<!--slotContent.vue-->
<template>
  <div>
    <div v-for="(item, index) in names" :key="item">
      <slot :item="item" :index="index"></slot>
    </div>
  </div>
</template>

<script>
 export default {
  props: {
    names: {
      type:Array
    }
  }
}
</script>

  1. Define the data in App.vue;
  2. Data is passed to child components;
  3. Traverse the names data in the subcomponent;
  4. Define the props of the slot;
  5. Obtain slot props through v-slot:default;
  6. Use item and index in slotProps.

The parent component passes an array to the child component, and the child component traverses the array through the v-for loop. When looping the array, there are item and index. The item and index are passed to the parent component by defining the props of the slot slot. The parent component uses v -slot: default received, the parent component can use item and index. 

10. Vue componentization - non-parent-child component communication

In development, after we build the component tree, in addition to the communication between parent and child components , there will also be communication between non-parent and child components .

Vue is a stateful management library to manage and realize data sharing, such as vuex, pinia , and learn later.

What is learned here is to achieve communication without relying on third-party management libraries.

10.1. Provide and Inject

It is used less during development. 

Provide/Inject is used to share data between non-parent-child components :
  • For example, there are some deeply nested components , and the child component wants to get part of the content ;
  • In this case, if we still pass props down the component chain level by level, it will be very troublesome;
For this case we can use Provide and Inject:
  • No matter how deep the hierarchy is, a parent component can act as a dependency provider for all its child components ;
  • The parent component has a provide option to provide data;
  • Child components have an inject option to start using this data;
In fact, you can think of dependency injection as " long range props" , except:
  • The parent component does not need to know which child components use the property it provides;
  • Child components don't need to know where the injected property comes from.

We develop a structure App.vue as the parent component of Home.vue, and Home.vue as the parent component of HomeContent.vue.

<!--App.vue-->
<template>
  <div>
    <home></home>
  </div>
</template>

<script>
import Home from './components/Home.vue'
 export default {
  
  components: {
    Home
  },
  provide: {
    name: "zzz",
    age: 18
  }
}
</script>
<!--Home.vue-->
<template>
  <home-content></home-content>
</template>

<script>
import HomeContent from './HomeContent.vue'
 export default {
  data() {
    return {
      
    }
  },
  components: {
    HomeContent
  }
}
</script>
<!--HomeContent.vue-->
<template>
  <div>AppContent
    <h2>{
   
   {name}}</h2>
    <h2>{
   
   {age}}</h2>
  </div>
</template>

<script>
 export default {
  inject: ["name", "age"]
}
</script>

In this way, the data defined in App.vue can be used in HomeContent.vue.

10.2. Function writing of Provide and Inject

If the data provided in provide comes from data , we may want to get it through this ;

But this will report an error, and the names will not be found.

So our provide needs to use function writing . Similar to data, returns an object.

<!--App.vue-->
<template>
  <div>
    <home></home>
  </div>
</template>

<script>
import Home from './components/Home.vue'
 export default {
  data() {
    return {
      names: ["aaa", "bbb"]
    }
  },
  components: {
    Home
  }, 
  provide() {
    return{
      name: "zzz",
      age: 18,
      length: this.names.length
    }
  }
}
</script>

11. Vue componentization - additional knowledge supplement

11.1. Component life cycle

You can take a look at Muzi Mumu's vue3 life cycle and life cycle function explanation, which is very well explained. Of course, you can also look directly down.

What is the life cycle?

  • In biology, the biological life cycle refers to a series of changes that an organism goes through from the beginning to the end of life;
  • Each component may go through a series of processes from creation, mounting, updating, uninstalling, etc.;
  • At a certain stage in this process , we may want to add some code logic of our own (such as requesting some server data after the component is created);
  • But how can we know which process the component is currently in ? Vue provides us with component lifecycle functions ;
Lifecycle function:
  • Life cycle functions are some hook functions (callback functions) , which will be called back by the Vue source code at a certain time ;
  • Through the callback of the life cycle function, we can know what stage the component is currently going through ;
  • Then we can write our own logic code in this life cycle ;

 

beforeCreate

1. Create a component instance

created (send network request event listener)
2. template template compilation

beforeMount

3. Mount to virtual DOM, virtual DOM->real DOM

mounted (the element has been mounted to get the DOM to use the DOM)

4. Data update

beforeUpdate

Generate a new VNode based on the latest data, generate a new virtual DOM -> generate a new real DOM

updated

5. No longer use such as v-if="false"

beforeUnmount

Remove the VNode previously mounted in the virtual DOM from the virtual DOM

unmounted (cancel event listener)

Destroy the component instance

 I feel that the life cycle is a bit too much, so focus on understanding the life cycle in red above .

11.1.1. Component Lifecycle Walkthrough 

export default {
  // 1.组件被创建之前
  beforeCreate() {
    console.log("beforeCreate")
  },
  // 2.组件被创建完成
  created() {
    console.log("created")
    console.log("1.发送网络请求,请求数据")
    console.log("2.监听事件")
    console.log("3.监听watch数据")
  },
  // 3.组件template准备被挂载
  beforeMount() {
    console.log("beforeMount")
  },
  // 4.组件template被挂载: 虚拟DOM -> 真实DOM
  mounted() {
    console.log("mounted")
    console.log("1.获取DOM")
    console.log("2.使用DOM")
  },
  // 5.数据发生改变 准备更新DOM
  beforeUpdate() {
    console.log("beforeUpdate")
  },
  // 6.更新DOM
  updated() {   
    console.log("updated")
  },
  // 7.卸载VNode -> DOM元素 卸载之前
  beforeUnmount() {
    console.log("beforeUnmount")
  },
  // 8.DOM元素被卸载完成
  unmounted() {
    console.log("beforeUnmount") 
  },
}

 

11.2. ref references in components

In some cases, we want to directly get the element object or subcomponent instance in the component :
  • We do not recommend DOM manipulation in Vue development ;
  • At this time, we can bind a ref attribute to the element or component ;
<template>
  <div>
    <h2 ref="title">获取的h2
    </h2>
  </div>
</template>

Component instances have a $refs property:

  • It is an Object that holds all DOM elements and component instances registered with the ref attribute .
export default {
  methods: {
    changeTitle() {
      console.log(this.$refs.title);
    }
  }
}
We can access the parent element through $parent.
  • Here we can also achieve it through $root , because App is our root component;
  • Note: The $children property has been removed in Vue3 , so it can no longer be used.

11.3. Use of dynamic components

  

To achieve dynamic switching component display:

  • It can be judged by v-if to display different components;
  • Dynamic components can also be used .

11.3.1. Display different components with v-if 

 Use v-if to judge currentIndex to decide which component to use.

<template>
  <div>
    <template v-for="(item, index) in tabs" :key="index">
      <button :class="{active: currentIndex === index}"
              @click="btnClick(index)">{
   
   {item}}
      </button>
    </template>
    <template v-if="currentIndex === 0">
      <home></home>
    </template>
    <template v-else-if="currentIndex === 1">
      <about></about>
    </template>
    <template v-else-if="currentIndex === 2">
      <category></category>
    </template>
  </div>
</template>

<script>
import Home from './components/Home.vue'
import About from './components/About.vue'
import Category from './components/Category.vue'
 export default {
  data() {
    return {
      tabs: ["home", "about", "category"],
      currentIndex: 0
    }
  },
  components: {
    Home,
    About,
    Category
  }, 
  methods: {
   btnClick(index){
     this.currentIndex = index
   }
  }
}
</script>

<style scoped>
.active{
  color:red;
}
</style>

11.3.2. Realization of dynamic components

Dynamic components are implemented using component components through a special attribute is :

 

What does the value of this tabs need to be?
  • Global registration: it can be a component registered through the component function ;
  • Layout registration: the components registered in the components object of a component object ;

Automatically find components according to the content in is. 

If it is a dynamic component, can we pass values ​​to them and listen to events?

  • Yes, we put attributes and listener events on the component to use;

 

11.4. Use of keep-alive

If you add a counter to the home of the component switching case above, the number can be increased by clicking the button.

 

 Will it be retained after clicking to about and then returning to the home state?

  • The answer is no ;
  • This is because by default, after we switch components , the home component will be destroyed , and the component will be recreated when we come back again ;

However, in some cases during development, we want to keep the state of the component instead of destroying it. At this time, we can use a built-in component: keep-alive .

11.4.1. keep-alive property

keep-alive has some properties:
  • include - string | RegExp | Array. Only components with matching names will be cached;
  • exclude - string | RegExp | Array. Any component whose name matches will not be cached;
  • max - number | string. The maximum number of component instances that can be cached. Once this number , the instances in the cache component that have not been accessed recently will be destroyed;
    <!-- 逗号分隔字符串 -->
    <keep-alive include="home,about">
      <component :is="tabs[currentIndex]"></component>      
    </keep-alive>

    <!-- regExp(使用v-bind) -->
    <keep-alive :include="/home|about/">
      <component :is="tabs[currentIndex]"></component>      
    </keep-alive>

    <!-- Array (使用v-bind) -->
    <keep-alive :include="['home','about']">
      <component :is="tabs[currentIndex]"></component>      
    </keep-alive>
The include and exclude props allow components to be cached conditionally:
  • Both can be represented as comma-separated strings, regular expressions, or an array ;
  • The match first checks the name option of the component itself ;

Matching first checks the name option on the component itself.

So the name in the component is very important, if there is no name, there is no way to cache. 

 

11.4.2. The life cycle of the cache component

 For cached components, when re-entering, we will not execute life cycle functions such as created or mounted :

  • But sometimes we really want to monitor when we re-enter the component and when we leave the component;
  • At this time, we can use the two life cycle hook functions activated and deactivated to monitor;

 

11.5. Webpack code subpackage

 The default packaging process:

  • By default, in the process of building the entire component tree, because components are directly dependent on modules , webpack will package component (for example, in an app.js file) ;
  • At this time, as the project continues to grow , the content of the app.js file is too large , which will slow down the rendering speed of the first screen ;

How to package in vue? 

  • Use the command npm run build on the command line, and a dist file will appear;
    npm run build

 When packaging, the subpackage of the code:

  • Therefore, for some components that do not need to be used immediately , we can split them separately and split them into some small code blocks chunk.js ;
  • These chunk.js will be loaded from the server when needed , and run the code to display the corresponding content;

 So how can the code be subpackaged in webpack?

Use the import function to import files in main.js to realize the subpackage of the code.

import("../utils/math").then( res => {
  res.sum(20, 30)
})

11.5.1. Implement asynchronous components in Vue

 If our project is too large and we want to load some components asynchronously (the purpose is to subcontract them), then Vue provides us with a function: defineAsyncComponent .

defineAsyncComponent accepts two types of parameters:
  • Type 1 : Factory function, which needs to return a Promise object;
  • Type 2 : Accept an object type and configure the asynchronous function; (understand)

11.6. v-model for components

Earlier we can use v-model in input to complete two-way binding:
  • It is often very convenient at this time, because v-model helps us accomplish two things by default;
  • v-bind:value data binding and @input event monitoring ;
If we encapsulate a component now , can v-model be used to complete these two functions at the same time when using this component elsewhere ?
  • It is also possible, vue also supports the use of v-model on components ;
  • When we use it on a component, it is equivalent to the following operations:
  • We will find that the only difference from the input element is the name of the attribute and the name of the event trigger ;
<!--App.vue-->
<template>
  <div>
    <!-- <home v-model="message"></home> -->
    <home :modelValue="message" @update:modelValue="this.message = $event"></home>
    
  </div>
</template>

<script>
import Home from './components/Home.vue'
 export default {
  data() {
    return {
      message:0
    }
  },
  components: {
    Home,
    
  }, 
  
}
</script>
<!--Home.vue-->
<template>
  <div>home
    <h4>{
   
   { modelValue }}</h4>
    <button @click="btnClick">改变</button>
  </div>
</template>

<script>
 export default {
  name: "home",
  methods: {
    btnClick() {
      this.$emit("update:modelValue", 100)
    }
  },
  props: {
    modelValue: {
      type:Number,
      default: 0
    }
  }
}
</script>

11.7. Use of Mixins

At present, we are developing the entire Vue application in a componentized way, but sometimes the same code logic exists between components , and we hope to extract the same code logic .
One way supported in both Vue2 and Vue3 is to use Mixin to do it :
  • Mixin provides a very flexible way to distribute reusable functions in Vue components ;
  • A Mixin object can contain any component options ;
  • When a component uses a Mixin object, all the options of the Mixin object will be mixed into the options of the component itself ;

 

11.7.1. Mixin Merging Rules

If the options in the Mixin object conflict with the options in the component object, how will Vue operate?
Here it is divided into different situations for processing;
Case 1: If it is the return value object of the data function
  • Return value objects are merged by default ;
  • If the properties of the data return value object conflict, the data of the component itself will be retained ;
Case 2: How to hook the lifecycle function
  • The hook functions of the life cycle will be merged into the array and will be called;
Case 3: Options whose values ​​are objects, such as methods, components, and directives, will be merged into the same object.
  • For example, if there are methods options and methods are defined, they will all take effect ;
  • But if the key of the object is the same , then the key-value pair of the component object will be taken ;

11.7.2. Global Mixin Mixin 

If some options in the component are required by all components, then we can use the global mixin at this time :
  • The global Mixin can use the method mixin of the application app to complete the registration;
  • Once registered, the global mix-in options will affect every component ;

 

12.Vue3 - CompositionAPI

In Vue2, the way we write components is the Options API :
  • A major feature of the Options API is to write the corresponding functional modules in the corresponding properties ;
  • For example, define data in data , define methods in methods , define computed properties in computed , monitor property changes in watch , and also include life cycle hooks ;

 But this code has a big drawback:

  • When we implement a certain function , the code logic corresponding to this function will be split into various attributes;
  • When our components become larger and more complex , the list of logical concerns will grow, and the logic of the same function will be split ;
  • Especially for those who did not write these components in the first place , the code of this component is difficult to read and understand (other people who read components);

 For the same logical concern, it is necessary to continuously jump to the corresponding code block, jumping around in data, methods, and watch.

12.1. Understanding the Composition API 

It would be even better if we could collect code related to the same logical concern .

This is what the Composition API wants to do, and what it can help us do.

So now that we know what the Composition API wants to help us do, let's take a look at how to do it ?
  • In order to start using the Composition API, we need to have a place where we can actually use it (write code) ;
  • In the Vue component, this location is the setup function ;
setup is actually another option of the component:
  • It's just that this option is so powerful that we can use it to replace most of the other options written before ;
  • Such as methods, computed, watch, data, life cycle, etc.;

12.2.Parameters of setup function 

Let's first study the parameters of a setup function, which mainly has two parameters :
  • The first parameter: props
  • The second parameter: context
props is very easy to understand. In fact, the properties passed by the parent component will be placed in the props object . If we need to use it in the setup , we can directly obtain it through the props parameter:
  • For the type of props defined , we are still the same as the previous rules, defined in the props option ;
  • And it is still possible to use the properties in props normally in the template , such as message;
  • If we want to use props in the setup function , then we can’t get it through this (I’ll talk about why later);
  • Because props are directly passed as parameters to the setup function , we can use them directly through parameters ;
Another parameter is context , which we also call a SetupContext , which contains three attributes :
  • attrs : all non-prop attributes;
  • slots : the slots passed by the parent component (this will work when returning from the rendering function, which will be discussed later);
  • emit : emit is used when we need to emit events inside our components (because we cannot access this, we cannot emit events through this.$emit);

12.3. The return value of the setup function

 Since setup is a function, it can also have a return value . What is its return value used for?

  • The return value of setup can be used in the template template ;
  • That is to say, we can replace the data option with the return value of setup ;

 Even we can return an execution function instead of the method defined in methods :

<template>
  <div>
    <h2>{
   
   {count}}</h2>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
  </div>
</template>

<script>
 export default {
  setup() {
    let count = 100
    const increment= () => {
      count++
    }
    const decrement= () => {
      count++
    }
    return {
      count,
      increment,
      decrement
    }
  }
  
}
</script>

Return the increment and decrement in the setup.

However, if we operate the count in increment or decrement, can the interface be responsive?
  • The answer is no ;
  • This is because for a defined variable , by default, Vue does not track its changes to cause responsive operations on the interface ;

12.4.Reactive API

If we want to provide reactive features for the data defined in setup, then we can use the reactive function :

  • Only complex types of data can be defined;
  • And need to use import {reactive} from 'vue' import ;
  • import {reactive} from 'vue'
     export default {
      setup() {
        const account = reactive({
          username: "username",
          password: "123456"
        }) 
        const clickAccount = () => {
          account.username = "jjj"
        }
        return {
          account,
          clickAccount
        }
      }
      
    }

    Responsive operations can be performed when the data changes.

12.5. Ref API (important) 

The reactive API has restrictions on the type passed in , it requires us to pass in an object and an array type :

  • If we pass in a basic data type (String, Number, Boolean), a warning will be reported ;

At this time, Vue3 provides us with another API: ref API. In addition to defining simple type data, ref API can also define complex type data

  • ref will return a mutable responsive object , which maintains its internal value as a responsive reference , which is the source of the ref name ;
  • Its internal value is maintained in the value attribute of ref ;
const message = ref("Hello World")
There are two caveats here:
  1. When introducing the value of ref in the template , Vue will automatically help us unpack it, so we don't need to use ref.value in the template;
  2. But inside the setup function , it is still a ref reference , so when operating on it, we still need to use the ref.value method ;

12.6. Understanding readonly

We can get a responsive object through reactive or ref , but in some cases, the responsive object we pass to other places (components) wants to be used in another place (component) , but cannot be modified . How to prevent this from happening at this time ?
  • Vue3 provides us with the readonly method ;
  • readonly will return the read-only proxy of the original object (that is, it is still a Proxy, which is a proxy whose set method is hijacked and cannot be modified );
The common readonly method in development will pass in three types of parameters:
  • Type 1: common object ;
  • Type 2: the object returned by reactive ;
  • Type 3: object of ref ;

12.6.1. Use of readonly

 During the use of readonly, there are the following rules:

  • The objects returned by readonly are not allowed to be modified ;
  • But the original object processed by readonly is allowed to be modified;

        ✓ For example, const info = readonly(obj), the info object is not allowed to be modified ;

        ✓ When obj is modified , the info object returned by readonly will also be modified;

        ✓ But we cannot modify the object info returned by readonly ;

 So what is the use of this readonly?

  • When we pass data to other components, we often want other components to use the content we pass, but when they are not allowed to modify, we can use readonly;

12.7. API for Reactive judgment (understand)

isProxy
  • Check if the object is a proxy created reactively or readonly .
isReactive
  • Check if the object is a reactive proxy created by reactive :
  • If the proxy is built by readonly , but wraps another proxy created by reactive , it will also return true;
isReadonly
  • Checks if the object is a read-only proxy created by readonly .
toRaw
  • Returns the original object for reactive or readonly proxies ( keeping a persistent reference to the original object is not recommended. Use with caution).
shallowReactive
  • Creates a reactive proxy that tracks the responsiveness of its own properties, but does not perform deep reactive transformations of nested objects (deep or native objects).
shallowReadonly
  • Create a proxy that makes its own property read-only, but does not perform deep read-only conversion of nested objects (deep layers are still readable and writable).

12.8.toRefs (understand)

If we use the ES6 deconstruction syntax to destructure the object returned by reactive to obtain the value , then whether it is to modify the variable after the structure or modify the state object returned by reactive , the data will no longer be responsive :

 

 So is there a way to make our deconstructed properties responsive ?

  • Vue provides us with a toRefs function , which can convert the attributes in the object returned by reactive into ref ;
  • Then the name and age that we structure again are both ref ;

 toRefs is equivalent to converting multiple to ref.

12.9.toRef (understand)

If we only want to convert the properties in a reactive object to ref , then we can use the toRef method :

12.10.ref Other APIs

unref

◼ If we want to get the value in a ref reference , we can also use the unref method :

  • If the parameter is a ref , returns the internal value , otherwise returns the parameter itself ;
  • This is the syntactic sugar function of val = isRef(val) ? val.value : val ;
isRef
  • Determines whether the value is a ref object .
shallowRef
  • Create a shallow ref object ;
triggerRef
  • Manually triggering the side effects associated with shallowRef :

12.11.computed computed property

We explained the computed attribute in the previous section: when some of our attributes depend on other states, we can use the computed attribute to handle it
  • In the previous Options API, we used the computed option to complete;
  • In the Composition API, we can use the computed method in the setup function to write a computed property;

 How to use computed? 

  • Method 1 : Receive a getter function and return an unchanged ref object for the value returned by the getter function;
  • <template>
      <div>
        <h2>{
         
         {fullName}}</h2>
      </div>
    </template>
    
    <script>
    import {reactive, ref} from 'vue'
    import {toRefs, toRef, computed} from 'vue'
    
     export default {
      setup() {
        const names = reactive({
          firstName:"aa",
          lastName:"bb"
        })
        const fullName = computed(()=> {
          return names.firstName + " " +names.lastName
        })
        return {
          names,
          fullName
        }
      }
      
    }
    </script>
    

  • Method 2 : Receive an object with get and set , and return a mutable (readable and writable) ref object;
 const fullName = computed({
      get:()=>{
        return names.firstName + " " +names.lastName
      },
      set: ()=> {
        const tempNames = newValue.split(" ")
        names.firstName = tempNames[0]
        names.lastName = tempNames[1]
      }
    })

12.12. Use of ref in setup

 In Chapter 11.2, if you want to get the native DOM, you can write ref in the element, and use this.$refs() in the method to get it. How to use ref to get the element or component in the setup?

  • We only need to define a ref object and bind it to the ref attribute of an element or component;
<template>
  <div>
    <h2 ref="titleRef">我就是标题</h2>
    <button @click="clickTitle">点击获取</button>
  </div>
</template>

<script>
import {reactive, ref} from 'vue'
 export default {
  setup() {
    const titleRef = ref()
    const clickTitle = ()=> {
      console.log(titleRef.value);
    }
    return {
      titleRef,
      clickTitle
    }
  } 
}
</script>


 

12.13. Life cycle in setup function

 We said earlier that setup can be used to replace data, methods, computed, etc. options, as well as life cycle hooks .

So how to use lifecycle functions in setup?
  • Lifecycle hooks can be registered using directly imported onX functions;

 

Because setup runs around the beforeCreate and created lifecycle hooks , there is no need to define them explicitly , in other words, any code written in these hooks should be written directly in the setup function

<template>
  <div>
  </div>
</template>
<script>
export default {
  setup() {
    onMounted(() => {
      console.log("onMounted")
    })
    onUpdated(() => {
      console.log("onUpdated")
    })
    onUnmounted(() => {
      console.log("onUnmounted")
    })
  } 
}
</script>


12.14. Provide function and Inject function

We have also learned about Provide and Inject before, and the Composition API can also replace the previous Provide and Inject options. But these shared content are managed more with vuex or pinia.

12.14.1.provide function

We can provide data through provide:
  • Each Property can be defined through the provide method;
Provide can pass in two parameters:
  • name: the property name provided;
  • value: the property value provided;
let counter = 100
    let info = {
      name: "zzz",
      age: 18
    }
    provide("counter", counter)
    provide("info", info)

12.14.2. Inject function

In the descendant components, the required properties and corresponding values ​​can be injected through inject:
  • The required content can be injected through inject ;
inject can pass in two parameters:
  • The name of the property to be injected;
  • default value;

12.15. watch listens to data

In the previous Options API, we can use the watch option to listen to data changes in data or props , and perform certain operations when the data changes.

In Composition API, we can use watchEffect and watch to listen to responsive data;
  • watchEffect: a dependency for automatically collecting responsive data;
  • watch: You need to manually specify the data source to listen to;

12.15.1. Use of watch

The API of the watch is completely equivalent to the Property of the component watch option:
  • Watch needs to listen to a specific data source and execute its callback function;
  • It is lazy by default, the callback will only be executed when the source being listened to changes;
const name = ref("zzz")
    watch(name, (newValue, oldValue)=> {
      console.log(newValue, oldValue);
    })

Listen to multiple data sources

Listeners can also listen to multiple sources simultaneously using an array:

const name = ref("zzz")
const age = ref(18)
    watch([name,age], (newValue, oldValue) => {
      console.log(newValue, oldValue);
    })

12.15.2.watch options

 If we want to listen to a deep listening, we still need to set deep to true:

  • You can also pass in immediate to execute immediately;
const name = ref("zzz")
    const age = ref(18)
    watch([name,age], (newValue, oldValue) => {
      console.log(newValue, oldValue);
    }, {
      immediate: true,
      deep: true
    })

12.15.3.watchEffect

When listening to some responsive data changes, we want to perform some operations, and watchEffect can be used at this time .
Let's look at a case:
  • First, the function passed in by watchEffect will be executed once immediately, and dependencies will be collected during execution;
  • Secondly, only when the collected dependencies change, the function passed in by watchEffect will be executed again;

 

12.15.4.watchEffect stops listening

 If in some cases, we want to stop listening, at this time we can get the return value function of watchEffect , just call this function .

For example, in the above case, when our age reaches 20, we stop listening:

 

 12.16. script setup syntax

<script setup> is a compile-time syntactic sugar for using the combined API in a single-file component (SFC), and is recommended when using both the SFC and the combined API .
  • Less boilerplate content, more concise code;
  • Ability to declare props and throw events using pure Typescript;
  • better runtime performance;
  • Better IDE type inference performance;
To use this syntax, add the setup attribute to the <script> code block:
<script setup>
    console.log("setup")
</script>

The code inside will be compiled into the content of the component setup() function:

  • This means that unlike normal <script> which is only executed once when the component is first introduced;
  • The code in <script setup> will be executed every time a component instance is created.

12.16.1. Top-level bindings are exposed to templates

When using <script setup>, any bindings at the top level of the <script setup> declaration (including variables, function declarations, and imports) can be used directly in the template:
It can be used without return;

 

 Responsive data needs to be created through ref and reactive.

12.16.2. Imported components are used directly

 Values ​​in the <script setup> scope can also be used directly as tag names for custom components:

12.16.3.defineProps()和defineEmits()

To get full type inference support when declaring props and emits options, we can use the defineProps and defineEmits APIs, which will automatically be available in <script setup>:

<template>
  <div>
    <h2>showInfo: {
   
   {name}} - {
   
   {age}}</h2>
    <button @click="changeAge">修改Age</button>
  </div>
</template>

<script setup>
  const props = defineProps({
    name: {
      type: String,
      default: ""
    },
    age: {
      type: Number,
      default: 0
    }
  })

  const emit = defineEmits(["changeAge"])
  function changeAge() {
    emit("changeAge", 200)
  }
</script>

So with defineProps and defineEmits, you can receive props and emit events.

12.16.4. defineExpose()

 Components using <script setup> are disabled by default:

  • The public instance of the component obtained through the template ref or $parent chain will not expose any bindings declared in <script setup>;

 Use the defineExpose compiler macro to explicitly specify the property to be exposed in the <script setup> component:

 

Guess you like

Origin blog.csdn.net/m0_51636525/article/details/125971349