There are too many similarities to the basics of VUE2 no longer documented

1. Componentized development

1. Father to Son:

App.vue

 <son msg="hello world"></son>
Copy the code

son.vue

export default {
  // props: ['msg']
  props: {
    msg: {
      type: String,
      default: "default"
    }
  }
}
Copy the code

2. The father

App.vue

< span style =" box-sizing: border-box; color: RGB (50, 50, 50); line-height: 21px; font-size: 13px! Important; white-space: inherit! Important;" { add() { this.counter++ } }, } </script>Copy the code

Son.vue

<template>
  <button @click="increment">+1</button>
</template>
<script>
export default {
  emits: ['add', 'sub'],
  methods: {
    increment() {
      this.$emit('add')
    }
  }
}
</script>
Copy the code

3. The dojo.provide and inject

Used to pass data between the root component and its descendants

App.vue

<template> <father></father> <button @click="addName">+name</button> </template> <script> export default { provide() { Return {name: 'yogln', MSG: this.msg, names: computed(() => this.names) // Use computed to do the response}}, data() {return {MSG: 'hello world', names: ['abc', 'cba', 'nba'] } }, methods: { addName() { this.names.push('why') } }, } </script>Copy the code

Son.vue

<script>
  export default {
    inject: ["name", "msg", "names"]
  }
</script>
Copy the code

4. Event bus

Since Vue cancelled events such as $on and $off, the third-party library MITT can only be relied on for communication between non-parent and child components. Install dependencies

npm i mitt
Copy the code

Encapsulation mitt. Js

import mitt from 'mitt'
export default mitt()
Copy the code

Usage:

The sender:

<script> import emitter from './utils/eventBus' export default { methods: {btnClick() {emitter.log (' btnClick ') emitter.emit(' yogln ', 'yogln ')}},} </script>Copy the code

The receiving party:

<script>
import emitter from './utils/eventBus'
  export default {
    created() {
      emitter.on('yogln', msg => {
        console.log(msg)
      })

      emitter.on('*', (type, msg) => {
        console.log(type, msg)
      })
    },
  }
</script>
Copy the code

5. Use of a named slot

NavBar.vue

 <div class="nav-bar">
    <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>
Copy the code

App.vue

<div> <nav-bar> <template v-slot:center> <button> button </button> </template> <template #right> <span> </span> </nav-bar> </div>Copy the code

Note: V-slot: can be shortened to #

The dynamic slot

We can use v-slot:[dynamicSlotName] to dynamically bind a name

< the template v - slot = [name] >... < / template >Copy the code

6. Use of scope slots

Render scope

In Vue there is the concept of rendering scope:

  • Everything in the parent template is compiled at the parent scope;
  • Everything in a subtemplate is compiled in the subscope;

Case study:

App.vue

<show :names="names">
  <template #="slotProps">
    <button>{{slotProps.item}}</button>
  </template>
</show>
Copy the code

Show.vue

<template v-for="(item, index) in names" :key="item">
  <slot :item="item" :index="index"></slot>
</template>
Copy the code

Mixed use of scoped and named slots

App.vue

<template>
  <show :names="names">
    <template #yogln="slotProps">
      <button>{{slotProps.item}}</button>
    </template>
  </show>
</template>
Copy the code

Show.vue

<template>
  <template v-for="(item, index) in names" :key="item">
    <slot name="yogln" :item="item" :index="index"></slot>
  </template>
</template>
Copy the code

Abbreviation for exclusive default slot

App.vue

<template>
  <show :names="names" #="slotProps">
      <button>{{slotProps.item}}</button>
  </show>
</template>
Copy the code

Pay attention to

If we have a default slot and a named slot, write with the full template, and always use the full

7. Dynamic components

Dynamic components are implemented using a component with a special attribute is. Note that currentTab is a component

<component :is="currentTab"></component>
Copy the code

Dynamic components pass parameters just as they pass parameters

<component :is="currentTab" :age="18"></component>
Copy the code

Notice the age is added here:Represents a Number type of 18, not add:Is a string

8.keep-alive

<keep-alive>
    <component :is="currentTab"></component>	
</keep-alive>
Copy the code

Keep-alive has several properties:

  • The include – string | RegExp | Array. Only components with matching names are cached;
  • Exclude – string | RegExp | Array. Any component with a matching name will not be cached;
  • Max – number | string. The maximum number of component instances that can be cached. Once this number is reached, none of the recently accessed instances in the cached component will be destroyed.

Include and exclude Prop allow components to conditionally cache:

  • Both can be represented as comma-separated strings, regular expressions, or arrays;
  • The match first checks the name option of the component itself

9. Use of asynchronous components

Referring to a resource through the Import() function subcontracts the package

import("./utils/math.js").then(res= > res.sum(20.30))

import("./utils/math.js").then({sum})
Copy the code

Subcontracting without routing packaging

<script> import { defineAsyncComponent } from 'vue' const AsyncAbout = defineAsyncComponent(() => import('./About.vue'))  export default { components: { AsyncAbout } } </script>Copy the code

Another way to write:

<script> const AsyncAbout = defineAsyncComponent({ loader: () => import('./About.vue') // loadingCompoent: When Loading / / load components according to Loading components / / errorCompoent: error displayed when the component / / delay: 2000 / / loadingComponent shows delay before the event}) < / script >Copy the code

Use of suspense and asynchronous components

<suspense>
  <template #default>
    <async-about></async-about>
  </template>
  <template #fallback>
    <loading></loading>
  </template>
</suspense>
Copy the code

The suspense component is currently in its experimental phase

10.$refs,$parent,$root,$el

<home ref="homeRef"></home>
Copy the code

This calls the data and methods of the component

$parent refers to the parent component

$root References the root component

This.$refs.homeref.$el gets all tags in the component

Note: this.$children has been removed from vue3

11. Component life cycle

Component usesleep-aliveThere are also two life cycles:activitedanddeactivited

12. V-model of the component

The basic use

App.vue

<my-input v-model="message"></my-input :="message" @update:modelValue="message =" $event"></my-inputCopy the code

MyInput.vue

<template>
    <h2>modelValue:  {{modelValue}}</h2>
    <input :value="modelValue" @input="inputChange">
</template>

<script>
  export default {
    props: {
      modelValue: {
        type: String
      }
    },
    emits: ["update:modelValue"],
    methods: {
      inputChange(event) {
        this.$emit("update:modelValue", event.target.value)
      }
    },
  }
</script>
Copy the code

Note: modelValue is modifiable by passingv-model:title="title"

Bind input by calculating properties

<input :value="value" @input="inputChange">
Copy the code
computed: {
  value: {
    set(value) {
      this.$emit("update:modelValue", value)
    },
    get() {
      return this.modelValue
    }
  }
}
Copy the code

Different parameters are passed through an input component

App.vue

<template>
    <my-input v-model="message"  v-model:title="title"></my-input>
    <h2>modelValue:  {{message}}</h2>
    <h2>title:  {{title}}</h2>
</template>
<script>
import MyInput from './MyInput2.vue'
  export default {
    components: {
      MyInput
    },
    data() {
      return {
        message: 'message',
        title: 'title'
      }
    },
  }
</script>
Copy the code

Myinput.vue

<template>  
    <input v-model="value">
    <input v-model="titleValue">
</template>
<script>
  export default {
    props: {
      modelValue: String,
      title: String
    },
    emits: ["update:modelValue", "update:title"],
    computed: {
      value: {
        set(value) {
          this.$emit("update:modelValue", value)
        },
        get() {
          return this.modelValue
        }
      },
      titleValue: {
        set(titleValue) {
          this.$emit("update:title", titleValue)
        },
        get() {
          return this.title
        }
      }
    }
  }
</script>
Copy the code

Second, the animation

1. Basic use of animation

<template> <div> <button @click="isShow = ! < span style =" box-sizing: border-box; color: RGB (74, 74, 74); line-height: 22px; font-size: 13px! Important; word-break: inherit! Important;" <style scoped> .yogln-enter-from, .yogln-leave-to { opacity: 0; ·}. Yogln-enter -to,. Yogln-leave -from {opacity: 1; } .yogln-enter-active, .yogln-leave-active { transition: opacity 1s ease; } </style>Copy the code

When inserting or removing elements contained in the Transition component, Vue will do the following:

  1. Automatically sniffs if CSS transitions or animations are applied to the target element, and if so, adds/removes CSS class names when appropriate;
  2. If the Transition component provides JavaScript hook functions, these hook functions will be called at the appropriate time;
  3. If no JavaScript hooks are found and no CSS transitions/animations are detected, DOM inserts and deletes are performed immediately;

We can see that there are many classes mentioned above, and Vue is actually an animation that helps us toggle between these classes:

  • V-enter -from: Defines the start state of entering the transition. It takes effect before the element is inserted and is removed in the next frame after the element is inserted.
  • V-enter -active: indicates the status when the transition takes effect. Applied throughout the transition phase, before the element is inserted, and removed after the transition/motion is complete. This class can be used to define the process time, delay, and curve functions for entering the transition.
  • V-enter -to: Defines the end state of the enter transition. The next frame takes effect after the element is inserted (at the same time v-enter-from is removed), and after the transition/animation is complete.
  • V-leave-from: Defines the start state of the leave transition. It takes effect as soon as the exit transition is triggered, and the next frame is removed.
  • V-leave-active: Defines the status when the leave transition takes effect. Applied throughout the exit transition phase, effective as soon as the exit transition is triggered and removed after the transition/animation is complete. This class can be used to define the process time, delay and curve functions for leaving the transition.
  • V-leave-to: The end of the transition. The next frame takes effect after the leave transition is triggered (at the same time v-leave-From is deleted) and is removed after the transition/animation is complete.

When to add a class and the naming convention

The naming rules for class names are as follows:

  • If we’re using a transition without a name, then all classes are prefixed with V – by default;
  • If we add a name attribute, for example, then all classes will start with why-;

2. Basic use of CSS animation

<template>
  <div>
    <div class=""><button @click="isShow = ! isShow">Show/Hide</button></div>
    <transition name="yogln">
      <h2 class="title" v-if="isShow">Hello world</h2>
    </transition>
  </div>
</template>

<style scoped>
.title {
  width: 200px;
  margin: 0 auto;
}

.yogln-enter-active {
  animation: bounce 1s;
}

.yogln-leave-active {
  animation: bounce 1s reverse;
}

@keyframes bounce {
  0% {
    transform: scale(0);
  }

  50% {
    transform: scale(1.2);
  }

  100% {
    transform: scale(1); }}</style>
Copy the code

Note:h2The label is a block-level element that occupies a single row and can have unexpected effects if the width is not changed

3. Use transitions with animations

The type attribute

The transition animation and the transform can be used together, but some problems can arise if the time of the two is inconsistent. Such as:

.yogln-enter-active..yogln-leave-active {
  transition: opacity 2s ease;
}

.yogln-enter-active {
  animation: bounce 1s;
}

.yogln-leave-active {
  animation: bounce 1s reverse;
}
Copy the code

The animation will end but the transition will not. You can specify the transition type property to specify the execution time of the animation property

<transition name="yogln" type="animation">
  <h2 class="title" v-if="isShow">Hello world</h2>
</transition>
Copy the code

Duration properties

Duration specifies the duration of the animation

<transition name="yogln" type="animation" :duration="1000">
  <h2 class="title" v-if="isShow">Hello world</h2>
</transition>
Copy the code

Duration can also be written as an object

<transition name="yogln" 
            type="animation" 
            :duration="{enter: 800,leave: 1000}">
  <h2 class="title" v-if="isShow">Hello world</h2>
</transition>
Copy the code

Mode attribute

When toggling between two tabs or two components, the default effect is ugly, we can use the mode property, which has two values

  • In-out: Specifies that the component to be displayed is in and the component that is not displayed is out
  • Out-in: Specifies that the component to be displayed is out before the component to be displayed is in
<transition name="yogln" type="animation" mode="out-in"> <h2 class="title" v-if="isShow">Hello world</h2> <h2 < span style =" box-sizing: border-box! Important; word-wrap: break-word! Important;"Copy the code

Appear properties

When we want to animate the page the first time we load it, we can add the appear property

<transition name="yogln" type="animation" mode="out-in" appear> <h2 class="title" v-if="isShow">Hello world</h2> <h2 < span style =" box-sizing: border-box! Important; word-wrap: break-word! Important;"Copy the code

4. Transition of components

The use of animation effects for components is similar

<transition name="yogln" type="animation" mode="out-in" appear>
  <component :is="isShow ? 'home' : 'about'"></component>
</transition>
Copy the code

Use animate. CSS for third-party libraries

Install the animate. CSS

npm i animate.css
Copy the code

Import animate. CSS in main.js:

import "animate.css"
Copy the code

Animation website

Use method 1:

Use the KeyFrames animations defined in the Animate library directly

<template> <button @click="isShow = ! < span style =" box-sizing: border-box; color: RGB (74, 74, 74); line-height: 22px; font-size: 13px! Important; word-break: inherit! Important;"  <style scoped> .yogln-enter-active { animation: flip 1s; } .yogln-leave-active { animation: flip 1s reverse; } </style>Copy the code

Use mode two:

Use the classes provided to us directly by the Animate library

<transition enter-active-class="animate__animated animate__bounceInUp"
            leave-active-class="animate__animated animate__zoomOutDown">
  <h2 class="title" v-if="isShow">Hello World</h2>
</transition>
Copy the code

6. Use GSAP for third-party libraries

Gsap animation effects

The installation

npm i gsap
Copy the code

Before using the animation, let’s take a look at the JavaScript hooks that the Transition component provides to help us listen to the stage of the animation execution.

When we use JavaScript to perform transition animations, the done callbacks need to be made, otherwise they will be called synchronously and the transition will complete immediately.

Adding: CSS =”false” will also allow Vue to skip CSS detection. In addition to slightly better performance, this avoids CSS rules during the transition.

<transition name="yogln" @enter="enter" @leave="leave">
  <h2 class="title" v-if="isShow">Hello World</h2>
</transition>

<script>
import gsap from 'gsap'
export default {
  data() {
    return {
      isShow: true,
    }
  },
  methods: {
    enter(el, done) {
      gsap.from(el, {
        scale: 0,
        x: 200,
        onComplete: done
      })
    },
    leave(el, done) {
      gsap.to(el, {
        scale: 0,
        x: 200,
        onComplete: done
      })
    },
  },
}
</script>
Copy the code

Gsap implements digital transformation

<template> <input type=" step "="100" v-model="counter">  {{showNumber.toFixed(0)}}</h2> </template> <script> import gsap from 'gsap' export default { data() { return { counter:  0, showNumber: 0 } }, watch: { counter(newValue) { gsap.to(this, { duration: 1, showNumber: newValue, }) }, }, } </script>Copy the code

7. Motion animation for list transitions

A list can be transformed using the transition-group component

Use the list transition animation to achieve the transformation of numbers

<template> <div> < button@click ="addNum"> Add number </button> < button@click ="delNum"> Delete number </button> <button @click="shuffle"> </button> <span v-for="item in numbers" :key="item" class="item">{{item}}</span> </transition-group> </div> </template> <script> import _ from 'loadsh' export default { data() { return { numbers: [1, 2, 3, 4, 5, 6, 7, 8, 9], currNum: 10, } }, methods: { addNum() { this.numbers.splice(this.randomIndex(), 0, this.currNum++) }, delNum() { this.numbers.splice(this.randomIndex(), 1) }, randomIndex() { return Math.floor(Math.random() * this.numbers.length) }, shuffle() { this.numbers = _.shuffle(this.numbers) } }, } </script> <style scoped> .item { margin: 0 10px; display: inline-block; } .yogln-enter-from, .yogln-leave-to { opacity: 0; transform: translateY(30px); } .yogln-enter-active, .yogln-leave-active { transition: all 1s ease; } .yogln-leave-active { position: absolute; } .yogln-move { transition: transform 1s ease; } </style>Copy the code

.yogln-move is the transition effect of the numbers on the right when inserting or removing elements

List animation to achieve alternate animation

<template> <div> <input type="text" v-model="keyword" @input="input"> <transition-group tag="ul" name="yogln" :css="false" @before-enter="beforeEnter" @enter="enter" @leave="leave"> <li v-for="name, index in showNames" :key="name" :data-index="index">{{name}}</li> </transition-group> </div> </template> <script> import  gsap from 'gsap' export default { data() { return { names: ['abc', 'cba', 'nba', 'why', 'lilei', 'hmm', 'kobe', 'james'], keyword: '' } }, computed: { showNames() { return this.names.filter(item => item.indexOf(this.keyword) ! == -1) } }, methods: { beforeEnter(el) { el.style.opacity = 0 el.style.height = 0 }, enter(el, done) { gsap.to(el, { height: Index * 0.3, onComplete: done,})}, leave(el, done) {gsap. To (el, {height: 0, opacity: 0, delay: el.dataset. Index * 0.3, onComplete: done,})},},} </script>Copy the code

Notice a little trick here, through:data-index="index", passed in the methoddataset.indexGets the index value passed in

Third, Composition API

1.mixin

Basic use of mixins

<script>
	import demomixin from './mixin/demomixin'
    export default {
        mixin: [demomixin]
    }
</script>
Copy the code

Merge rules for mixins

What does Vue do if the options in the Mixin object conflict with the options in the component object?

There are different cases to deal with;

  1. 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 component’s own data is retained;
  2. Case 2: How to lifecycle hook functions
    • Lifecycle hook functions are merged into arrays and are called;
  3. Case 3: Options with object values such as Methods, Components, and Directives are combined into the same object.
    • For example, if both options have methods, and both define methods, they all work;
    • But if the objects have the same key, then the component object’s key-value pair is taken

Global Mixin

If there are certain options in a component that all components need to have, then we can use global mixins

  • Global mixins can be registered using the application method Mixin.

  • Once registered, the globally mixed option will affect every component;

const app = createApp(App)
app.mixin({
  created() {
    console.log("created");
  },
})
app.mount('#app')
Copy the code

2. Extends (understand)

Extends is similar to mixins but less flexible than mixins, so it’s rarely used

<script>
import BasePage from './BasePage.vue'
export default {
	extends: BasePage
}
</script>
Copy the code

3. Parameters to the setup() function

Let’s first look at the arguments to a setup function, which has two main arguments:

  • The first argument: props
  • The second argument: context

Props: props: props: props: props: props: props: props: props: props: props

  • For the props type, we use the props option as before.
  • It is still possible to use the props attribute in a template, such as message.
  • If we want to use props in a setup function, we can’t get them using this (I’ll see why later);
  • Because props are passed directly as arguments to the setup function, we can use them directly as arguments.

The other argument is the context, which we also call a SetupContext, which contains three properties:

  • Attrs: all attributes that are not props;
  • Slots: the slot passed from the parent component (this will work when returned as a render function, more on that later);
  • Emit: emit events when we need to emit events internally (we cannot access this, so we cannot emit events through this.$emit)

4. the return value of the setup() function

Since setup is a function, it can also have a return value. What does it do with the return value?

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

We can even return an execution function instead of the method defined in methods

<script>
export default {
  setup(props, ctx) {
    const name = 'yogln'
    const counter = 100
    const increment = () => {
      counter++
    }
    return {
      name,
      increment
    }
  },
}
</script>
Copy the code

Setup cannot use this

Setup cannot use this

Setup cannot use this

5. The setup() function performs the corresponding refresh

reactive API

<template>
  <div>
    <h2>{{state.name}}</h2>
    <h2>{{state.counter}}</h2>
    <button @click="increment">+1</button>
  </div>
</template>
<script>
import { reactive } from 'vue'
export default {
  setup(props, ctx) {
    const state = reactive({
      name: 'yogln',
      counter: 100,
    })
    const increment = () => {
      state.counter++
    }
    return {
      state,
      increment,
    }
  },
}
</script>
Copy the code

So what is the reason for this? Why is this going to be responsive?

This is because when we use the reactive function to process our data, the data will be collected when it is used again. When the data changes, all the collected dependencies are used for responsive operations (such as updating the interface); In fact, we wrote the data option internally and gave it to the reactive function to program it as a reactive object

ref API

The reactive API is limited to the types we pass in. It requires that we pass in an object or array type:

  • If we pass in a primitive data type (String, Number, Boolean) a warning is issued

At this time Vue3 provides us with another API: ref API

  • Ref returns a mutable responsive object that maintains its internal value as a reference to the responsive. This is where the ref name comes from.
  • Its internal values are maintained in the value property of the REF
<template>
  <div>
    <h2>{{counter}}</h2>
    <button @click="increment">+1</button>
  </div>
</template>

<script>
import { ref } from 'vue'
export default {
  setup(props, ctx) {
    let counter = ref(100)
    const increment = () => {
      counter.value++
    }
    return {
      counter,
      increment
    }
  },
}
</script>
Copy the code

There are two caveats:

  • When importing ref values into the template, Vue automatically helps us unpack them, so we don’t need to use ref. Value in the template.
  • But inside the setup function, it’s still a ref reference, so when we operate on it, we still need to use the ref.value approach

Unpacking in a template is a shallow level of unpacking. If our code is like this: if we put the ref in a reactive property, it will automatically unpack it when it is used in the template

Shallow unpacking of ref

<template> <div> Home Page <h2>{{message}}</h2> <! <h2> Current count: {{counter}}</h2> <! <h2> Current count: {{info.counter. Value}}</h2> <! Reactive object (s); reactive object (s); {{reactiveInfo.counter}}</h2> <button @click="increment">+1</button> </div> </template> <script> import { ref, reactive } from 'vue'; export default { setup() { let counter = ref(100); Const reactiveInfo = reactive({counter}) const increment = () => {counter. Value++; console.log(counter.value); } return { counter, info, reactiveInfo, increment } } } </script>Copy the code

6.readonly

Readonly returns the read-only Proxy of the native object (that is, it is still a Proxy, and this is a Proxy whose set method has been hijacked and cannot be modified)

The common readonly method in development takes three types of arguments:

  • Type 1: plain object;
  • Type 2: Reactive returns an object.
  • Object of type three: ref

Objects returned by readonly are not allowed to be modified;

However, 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 is also modified; But we can’t modify the info object returned by readonly; In essence, the setter method for the object returned by readonly has been hijacked

<script>
import {readonly} from 'vue'
  export default {
    setup(props, ctx) {
      const info = {
        name: "yogln"
      }
      const readInfo = readonly(info)
      readInfo.name = "why"
    }
  }
</script>
Copy the code

image-20210620232952295

7. Reactive judgment API

isProxy

  • Check whether the object is a proxy created by reactive or readonly.

isReactive

  • Check whether an object is a reactive proxy created by reactive:
  • It will also return true if the delegate was built by readonly but wrapped around another delegate created by reactive.

isReadonly

  • Check whether the object is a read-only agent created by readonly.

toRaw

  • Return the original object of the reactive or readonly proxy. (It is not recommended to keep persistent references to the original object. Use with caution).

shallowReactive

  • Create a responsive proxy that tracks the responsiveness of its own property, but does not perform deep responsive transformations of nested objects (deep versus native).

shallowReadonly

  • Create a proxy that makes its own property read-only, but does not perform deep read-only transformations of nested objects (deep is still readable and writable)

8.refAPI

toRefs

If we use ES6’s deconstructing syntax to deconstruct the value of the object returned by reactive, then the data will no longer be reactive, either by modifying the variable of the structure or by modifying the state object returned by reactive.

Vue provides a toRefs function that converts the properties of the object returned by reactive toRefs. So we’re going to struct it again and the name and the age themselves are going to be ref

This creates a link between state.name and ref.value, and any change causes another change

< span style =" box-sizing: border-box; color: RGB (255, 255, 255); line-height: 22px; font-size: 12px! Important;" toRefs } from 'vue' export default { setup(props) { const state = reactive({ name: 'yogln', age: 18 }) let { name, age} = toRefs(state) const changeAge = () => { age.value++ } return { name, age, changeAge } }, } </script>Copy the code

toRef

const { name } = state
let age = toRef(state, 'age')
const changeAge = () = > {
  age.value++
}
Copy the code

unref

If we want to get the value in a ref reference, we can also do this by using the unref method:

Returns the internal value if the argument is a ref, otherwise returns the argument itself;

This is val = isRef(val), right? Val.value: Syntax sugar function for val;

isRef

Determines if the value is a ref object.

shallowRef

Create a shallow ref object;

triggerRef

Manually triggers side effects associated with shallowRef

customRef

Create a custom ref and display control over its dependency tracking and update trigger:

  • You need a factory function that takes the track and trigger functions as arguments;
  • And it should return an object with get and set;

The use of custom Ref, to achieve the text box anti – shaking effect

app.vue

<template>
  <div>
    <input type="text" v-model="message">
    <h2>{{message}}</h2>
  </div>
</template>
<script> 
import useDebounceRef from './hook/useDebounceRef'
export default {
  setup(props) {
    const message = useDebounceRef('Hello World')
    return {
      message
    }
  },
}
</script>
Copy the code

useDebounceRef.js

import {customRef} from 'vue'  

export default function(value) {
  let timer = null
  return customRef((track, trigger) = > {
    return {
      get() {
        track()
        return value
      },
      set(newValue) {
        clearTimeout(timer)
        timer = setTimeout(() = > {
          value = newValue
          trigger()
        }, 1000); }}})}Copy the code

9.computed

  • Method one: receive a getter function and return the value for the getter, returning an invariant ref object.
  • Mode two: receive an object with get and set and return a variable (read/write) REF object
<template> <h2>{{fullName}}</h2> < button@click ="changeName"> button </button> </template> <script> import {computed, ref } from 'vue' export default { setup(props) { const firstName = ref('Kobe') const lastName = ref('Bright') const fullName = computed({ get: () => { return firstName.value + ' ' + lastName.value }, set: (newValue) => { const names = newValue.split(' ') firstName.value = names[0] lastName.value = names[1] } }) const changeName = () => { fullName.value = 'coder yogln' } return { fullName, changeName, } }, } </script>Copy the code

10.watchEffect

WatchEffect Basic use

Functions passed in watchEffect are executed immediately, and dependencies are collected during execution; The function passed in watchEffect is executed again when the collected dependencies change;

< the template > < div > < h2 > {{name}}, {{age}} < / h2 > < button @ click = "changeName" > button < / button > < / div > < / template > < script > import {  ref, watchEffect } from 'vue' export default { setup(props) { const name = ref('yogln') const age = ref(18) watchEffect(() =>{ console.log("name", name.value, "age", age.value) }) const changeName = () => { name.value = 'why' } return { name, age, changeName } } } </script>Copy the code

WathEffect stop listening

const stop = watchEffect(() = >{
    console.log("name", name.value, "age", age.value)
})

stop() // Stop listening after execution
Copy the code

WatchEffect Cancels side effects

watchEffect((onInvalidate) = > {
  onInvalidate(() = > {
    // Code that removes side effects, such as canceling the last network request
    console.log('onInvalidate')})console.log('name', name.value, 'age', age.value)
})
Copy the code

When watchEffect is executed

We want to get the element reference in setup and print the value

<template> <h2 ref="title">Hello World</h2> </template> <script> import { ref, watchEffect } from 'vue' export default { setup(props) { const title = ref(null) watchEffect(() => { Console. log(title.value)}, {// flush: 'pre' // default flush: 'post' // mount}) return {title}}} </script>Copy the code

To get an element or component from setup, all we need to do is define a ref object that binds to the element or component’s REF attribute.

When flush: pre is set to the default value, we see that the print is printed twice:

  • This is because the setup function executes the passed side function immediately upon execution, and the DOM is not mounted at this time, so it prints null.
  • When the DOM is mounted, the title ref object is assigned a new value, and the side effect function is executed again, printing out the corresponding element;
  • The Flush option also accepts sync, which forces the effect to always fire synchronously. However, this is inefficient and should be rarely needed.

11.watch

Basic use of Watch

  • Pass in a getter function
< span style =" box-sizing: border-box; color: RGB (255, 255, 255); line-height: 22px; font-size: 12px! Important;" watch, ref } from 'vue' export default { setup(props) { const info = reactive({ name: 'yogln', }) // 1. Pass a getter function watch(() => info.name, (newValue, oldValue) => {console.log('newValue:', newValue, 'oldValue:', oldValue) // newValue: kobe oldValue: yogln }) return { info, changeName, } }, } </script>Copy the code
  • We pass in a reactive object: reactive, and the object that is returned is reactive
watch(info, (newValue, oldValue) = > {
    console.log('newValue:', newValue, 'oldValue:', oldValue)
    // newValue: Proxy {name: "kobe"} oldValue: Proxy {name: "kobe"}
})
Copy the code
  • Pass in a responsible object: the ref object is the value itself
const name = ref('yogln')
const changeName = () = > {
  name.value = 'kobe'
}
watch(name, (newValue, oldValue) = > {
  console.log('newValue:', newValue, 'oldValue:', oldValue)
  // newValue: kobe oldValue: yogln
})
Copy the code

Watch listens to multiple data sources

Listening to multiple data sources can wrap incoming listening objects in an array

watch([name, () = > ({ ...info })], (newValue, oldValue) = > {
  console.log('newValue:', newValue, 'oldValue:', oldValue)
/ / newValue: (2) [" kobe, "{...}] 0:" kobe "1: name:" yogln __proto__ : Objectlength: 2 __proto__ : Array (0) oldValue: (2) [" yogln, "{...}]
})
Copy the code

Watch deep listening

When listening to Reactive objects and the object passed in does not pass Reactive objects… {deep: true}

However, after deconstruction, you need to manually set to enable deep listening

const info = reactive({
  name: 'yogln'.friends: {
    name: 'why'}})const name = ref('yogln')
watch([name, () = > ({ ...info })], (newValue, oldValue) = > {
  console.log('newValue:', newValue, 'oldValue:', old
              vbbValue)
  // newValue: kobe oldValue: yogln
}, {
  deep: true.immediate: true // Enable the initial execution
})
Copy the code

12. Lifecycle hooks

How to use lifecycle functions in the Setup function you can register lifecycle hooks using the onX function that is directly imported;

<script>
import { onMounted, onUpdated } from 'vue'
  export default {
    setup(props) {
      onMounted(() => {
        console.log("onMounted")
      })
      onUpdated(() => {
        console.log("onUpdated")
      })
    }
  }
</script>
Copy the code

In the setup function, the beforecreate and Created lifecycles have no corresponding hooks and are used directly in the setup function, which executes before the lifecycle

13. Dojo.provide and Inject

Vue provides both provide and inject functions for using setup between descendant components

app.vue

<template>
    <home></home>
</template>

<script>
import { ref, provide, readonly } from 'vue'
import Home from './Home.vue'
export default {
  components: { Home },
  setup(props) {
    const name = ref('yogln')
    const age = ref(18)

    provide('name', readonly(name))
    provide('age', readonly(age))
  },
}
</script>
Copy the code

Use readOnly to wrap data to ensure a single data stream and prevent the child component from manipulating the data obtained by the parent component.

home.vue

<template> <h2>name</h2> <h2>age</h2> </template> <script> import {inject} from 'vue' export default { setup(props) { Const age = inject("age", 0) const age = inject("age", 0) return {name, age}}} </script>Copy the code

14. com positionAPI practice

The data in the counter case and the title case is extracted

<template>
  <div>
    <h2>{{counter}}</h2>
    <button @click="increment">+1</button>
  </div>
</template>

<script>
import { ref } from 'vue'
import useCounter from './hook/useCounter'
import useTitle from './hook/useTitle'
export default {
  setup(props) {
    const { counter, increment } = useCounter()
    const titleRef = useTitle('yogln')

    setTimeout(() => {
      titleRef.value = "why"
    }, 3000);
    return {
      counter,
      increment,
    }
  },
}
</script>
Copy the code

/hook/useCounter

import { ref } from 'vue'
export default function () {
  const counter = ref(0)
  const increment = () = > {
    counter.value++
  }
  return {
    counter,
    increment
  }
}
Copy the code

/hook/useTitle

import { watch, ref } from 'vue'
export default function (title = "Default title") {
  const titleRef = ref(title)
  watch(titleRef, newValue= > {
    document.title = newValue
  }, {
    immediate: true
  })
  return titleRef
}
Copy the code

Title 3s revisedyoglnInstead ofwhy

Listen for scroll and mouse slide events

App.vue

<template>
  <div>
    <p class="content"></p>
    <div class="scroll">
      <div>scrollX: {{scrollX}}</div>
      <div>scrollY: {{scrollY}}</div>
    </div>
    <div class="mouse">
      <div>mouseX: {{mouseX}}</div>
      <div>mouseY: {{mouseY}}</div>
    </div>
  </div>
</template>

<script>
import useScroll from './hook/useScroll'
import useMouse from './hook/useMouse'
export default {
  setup(props) {
    const { scrollX, scrollY} = useScroll()
    const { mouseX, mouseY} = useMouse()

    return {
      scrollX,
      scrollY,
      mouseX,
      mouseY
    }
  },
}
</script>

<style scoped>
.content {
  width: 3000px;
  height: 5000px;
}
.scroll {
  position: fixed;
  right: 50px;
  bottom: 30px;
}
.mouse {
  position: fixed;
  right: 50px;
  bottom: 90px;
}
</style>
Copy the code

useScroll.js

import { ref } from 'vue'
export default function () {
  const scrollX = ref(0)
  const scrollY = ref(0)

  window.addEventListener('scroll'.() = > {
    scrollX.value = window.scrollX
    scrollY.value = window.scrollY
  })
  return {
    scrollX,
    scrollY
  }
}
Copy the code

useMouse.js

import { ref } from 'vue'
export default function () {
  const mouseX = ref(0)
  const mouseY = ref(0)
  window.addEventListener('mousemove'.(event) = > {
    mouseX.value = event.pageX
    mouseY.value = event.pageY
  })  
  return {
    mouseX,
    mouseY
  }
}
Copy the code

15. Setup top-level writing (experimental)

App.vue

<template> <div> <h2>counter:{{counter}}</h2> <button @click="increment">+</button> <home message=" hey hey hey "></home> </div> </template> <script setup> import { ref } from 'vue' import Home from './Home.vue' const counter = ref(0) const increment = () => { counter.value++ } </script>Copy the code

Home.vue

<template>
  <div>
    <h2>{{message}}</h2>
  </div>
</template>

<script setup>
import { defineProps } from 'vue'
const props = defineProps({
  message: {
    type: String,
    default: '哈哈哈'
  }
})
</script>
Copy the code

Other Components

1. Basic use of render function

The render function needs to return the vNode, and the h function can do that for us.

<script>
import { h } from 'vue'
  export default {
    render() {
      return h('h2', {class: 'title'}, 'Hello Render')
    }
  }
</script>
Copy the code

2. The render function implements the counter case

<script> import { h, ref } from 'vue' export default { setup(props) { const counter = ref(0) const increment = () => { counter.value++ } Return {counter, increment}}, render() {return h('div', null, [h('h2', null, 'current count: ${this.counter}`), h('button', { onClick: () => { this.increment() } }, "+1") ]) } } </script>Copy the code

3. Use of the render function component

App.vue

<script> import { h } from 'vue' import home from './Home.vue' export default { render() { return h('div', null, H2 / h (' ', null, "App component"), h (home, null, {default: props = > h (' span, ` App to: ${props. The name} `)})])}} < / script >Copy the code

Home.vue

<script> import { h } from 'vue' export default { render() { return h('div', {class: 'home'}, [ h('h2', null, "Hello World"), this.$slots.default ? this.$slots.default({name: 'yogln'}) : H ('h2', null, 'I am the default ')])}} </script>Copy the code

4. The use of JSX

<script> import { ref } from 'vue' export default { setup(props) { const counter = ref(0) const increment = () => { counter.value++ } const decrement = () => { counter.value-- } return { counter, increment, decrement } }, Render () {return (<div> <h2>) current count: {this.counter}</h2> <button onClick={this.increment}>+1</button> <button onClick={this.decrement}>-1</button> </div> ) }, } </script>Copy the code

5. Customize instructions

Custom instruction implementation

Similar to v-show, V-for, and V-Model, Vue allows us to define custom directives, which are often used in situations where you need to perform low-level operations on DOM elements

There are two types of custom instructions:

  • Custom local directive: passed in componentdirectives Option that can only be used in the current component
  • Custom global directives: app’sdirective Method, which can be used in any component;

For example, let’s take a very simple example: when an element is mounted, you can customize the focus

  1. Implementation 1: If we use the default implementation;

    <template>
        <input type="text" ref="inputRef">
    </template>
    <script>
    import { ref, onMounted } from 'vue'
      export default {
        setup(props) {
          const inputRef = ref(null)
          onMounted(() => {
            inputRef.value.focus()
          })
          return {
            inputRef
          }
        }
      }
    </script>
    Copy the code
  2. Implementation method two: customize a local v-focus instruction;

    <template>
        <input type="text" v-focus>
    </template>
    <script>
    export default {
      directives: {
        focus: {
          mounted(el) {
            el.focus()
          },
        }
      }
    }
    </script>
    Copy the code
  3. Implementation mode three: customize a V-Focus global instruction;

const app = createApp(App)
app.directive('focus', {
  mounted(el) {
    el.focus()
  }
})
app.mount('#app')
Copy the code

The life cycle of a custom instruction

An object defined by a directive, Vue provides the following hook functions:

  • Created: called before the attribute or event listener of the bound element is applied;
  • BeforeMount: called when a directive is first bound to an element and before the parent component is mounted;
  • Mounted: called after the parent component of the bound element is mounted.
  • BeforeUpdate: called before updating the VNode containing components;
  • Updated: called after the VNode that contains the component and its child components have been updated;
  • BeforeUnmount: called before the parent component of the bound element is unmounted;
  • Unmounted: called only once when the directive is unbound from the element and the parent component has been unmounted.

Custom instruction parameters and modifiers

<template> <div> <button v-yogln:info.aaa.bbb="{name: </ directives > </template> <script> export default {directives: {yogln'}"> { created(el, bindings, vNode,preNode) { console.log("created") console.log(el, bindings, vNode,preNode) console.log(bindings) }, mounted() { console.log("mounted") }, } } } </script>Copy the code

Custom instruction conversion timestamp

Dayjs library for converting timestamps

npm i dayjs
Copy the code

App.vue

<template>
    <h2 v-format-time="'YY/MM/DD HH:mm:ss'">{{time}}</h2>
    <h2 v-format-time>{{time}}</h2>
</template>
<script>
import { ref } from 'vue'
  export default {
    setup(props) {
      const time = ref(1624882233)
      return {
        time
      }
    }
  }
</script>
Copy the code

directive/format-time.js

import dayjs from 'dayjs';
export default function(app) {
  app.directive('format-time', {
    created(el, bindings) {
      // Get the time text
      let textContent = el.textContent
      // Get the date format passed in
      let formatString = bindings.value
      if(! formatString) { formatString ='YYYY-MM-DD HH:mm:ss'
      }
      if(textContent.length == 10) {
        textContent = textContent * 1000
      }
      el.textContent = dayjs(textContent).format(formatString)
    },
  })
}
Copy the code

directive/index.js

import formatTime from './format-time';
export default function registerTime(app) {
  formatTime(app)
}
Copy the code

Convert a timestamp with or without an incoming format

6. Know the Teleport

We want the component not to be mounted in the component tree, perhaps to be moved to another location outside the Vue app:

For example, move to the body element, or we have other elements outside of the div#app; This is done by teleport

The Teleport is a built-in component provided by Vue, similar to the React Portals.

  • Teleport means teleportation over a long distance;
  • It has two properties:
    • To: Specifies the target element to which to move its contents, using a selector;
    • Disabled: Indicates whether to disable the teleport function

Add a tag to index.html

<div id="yogln"></div>
Copy the code
< span style = "box-sizing: border-box; color: RGB (50, 50, 50); font-size: 13px! Important; word-break: inherit! Important;"Copy the code

The h2 and Button components are now mounted on yogln instead of the original app.

7. Use of plug-ins

Usually when we add some functionality to Vue globally, we use the plug-in mode, which can be written in two ways:

  • Object type: An object, but must contain oneinstall This function is executed when the plug-in is installed.
  • Function types: a function that is automatically executed when the plug-in is installed;

There are no limits to what plug-ins can do, such as the following:

  • Add global methods or properties by adding them to config.globalProperties.
  • Add global resources: directives/filters/transitions, etc.
  • Add some component options through global mixins;
  • A library that provides its own API and one or more of the functions mentioned above;

How the object plug-in is used

export default {
  install(app) {
    console.log(app);
    app.config.globalProperties.$name = "yogln"}}Copy the code

In the main. In js

import pluginObject from './components/plugin/plugin-object';
app.use(pluginObject)
Copy the code

Use the defined $name globally

import getCurrentInstance from 'vue';  
created() {
    console.log(this.$name)
  },

  setup(props) {
    const instance = getCurrentInstance()
    console.log(instance.appContext.config.globalProperties.$name)    
  }
Copy the code

Method plug-in usage

export default function(app) {
  console.log(app);
}
Copy the code

Use it in the same way as objects