Modular API

Any API that needs to be used requires import, which is automatically completed by the plug-in

import { defineProps, reactive, computed, onMounted, onUnmounted, ref, toRefs, watch } from 'vue'
Copy the code

Responsiveness API

1 Basic API for responsiveness

reactive

Returns a responsive copy of the object, packaging all the properties and methods into a single object

Instead, they are aggregated into an object so that the properties within the data object can respond to external changes

const data = reactive({
    counter: 1.doubleCounter: computed(() = > data.counter * 2)})let timer;
onMounted(() = > {
    timer = setInterval(() = > {
        data.counter++
    }, 1000);
})
onUnmounted(() = > {
    clearInterval(timer);
})

return toRefs(data);
Copy the code

The internal properties are read through data.counter and data.doublecountert, allowing the internal data to respond to changes in the external methods

Package combination function

To make the code more organized, we can package the composite function as a function, return the property, and call the function by declaring the name of the function to get the object

const data = useCounter();

function useCounter(() = > {const data = reactive({
        counter: 1,
        doubleCounter: computed(() => data.counter * 2)})let timer;
    onMounted(()=> {
        timer = setInterval(() => {
            data.counter++
        }, 1000);
    })
    onUnmounted(()=> {
        clearInterval(timer);
    })
    
    return data;
})
Copy the code

Compared with Vue2, data, computed, and watch are broken down, which prevents the previous operation from repeatedly jumping between these apis

2 Refs

ref

Take a single value and return a responsive and variable ref object with a single property.value pointing inward

const count = ref(0)

console.log(count.value) / / 0

count.value++
console.log(count.value) / / 1
Copy the code

If you need to allocate an object to a ref value, you need to make the object highly reactive by using the reactive method

unref()

Returns the internal value if the argument is a ref, otherwise returns the value of the argument itself, which is val = isRef(val)? Val.value: The syntactic sugar function for val

function useFoo (x: number | Ref<number>) {
    const unwrapped = unref(x) // It must be numeric now
}
Copy the code

toRef()

Can be used to create a new ref for an attribute of the source responsive object, and then the REF can be passed, keeping the responsive link to its source attribute

const state = reactive({
  foo: 1.bar: 2
})

const fooRef = toRef(state, 'foo')

fooRef.value++
console.log(state.foo) / / 2

state.foo++
console.log(fooRef.value) / / 3
Copy the code

Above, the first argument of the toRef() method is the source object, and the second argument is the key string of the internal property. The value of the created ref object is changed, and the value in the source object is also changed

ToRef is useful when you need to pass a property in a reactive object to another function for use

setup(props) {
    useSomeFeature(toRef(props, 'foo'))}Copy the code

toRefs()

This method converts a reactive object to a normal object, with each property in the resulting object pointing to the corresponding REF of the original object

const state = reactive({
  foo: 1.bar: 2
})

const stateAsRefs = toRefs(state)

// The ref is already "linked" to the original property
state.foo++
console.log(stateAsRefs.foo.value) / / 2

stateAsRefs.foo.value++
console.log(state.foo) / / 3
Copy the code

ToRefs are useful when composite functions return reactive objects,

For example, the reactive function above packages the entire task into a function. Every time we refer to its properties, we need state.foo

In this case, we can use toRefs() to return the current data, and externally destruct the function to get the corresponding ref,

function useFeatureX() {
  const state = reactive({
    foo: 1.bar: 2
  })

  // Manipulate the logic of state

  // Convert to ref when returned
  return toRefs(state)
}

// Can be deconstructed without losing responsiveness
const { foo, bar } = useFeatureX()
Copy the code

isRef()

Check whether the value is a ref object

3 computed and watch

computed

Take a getter function as an argument and return an invariant responsive ref object from the value returned by the getter

const count = ref(1)
const plusOne = computed(() = > count.value + 1)

console.log(plusOne.value) / / 2

plusOne.value++ / / an error
Copy the code

Alternatively, he can use objects with get and set functions to create writable ref objects

const count = ref(1)
const plusOne = computed({
    get: () = > count.value + 1.set: val= > count.value = val -1
})

// When plusOne is assigned, it is equivalent to passing in a parameter, val equals 1, and ref returns the result of the function
plusOne.value = 1 
console.log(count.value) / / 0
Copy the code

watchEffect

A function is run immediately when its dependencies are tracked responsively, and rerunned when the dependencies are changed

const count = ref(0)
watchEffect(() = > {
    console.log(count.value)
}) // logs 0

setTimeout(() = > {
    count.value++
},1000)
// logs 1
// logs 2
// logs 3
// ...
Copy the code

watch

The Watch API is completely equivalent to the optional API this.$watch (and the corresponding Watch option) in that it listens to a specific data source and executes side effects in a separate callback function, which is also lazy by default, meaning that the callback is called only when the listening source has changed

  • In contrast to watchEffect, Watch allows us to:
    • Lazy execution side effects
    • More specifically, the state that should trigger the listener to run again
    • Access newVal and oldVal in the listening state
Listen to a single source

The listener data source can be either a getter function with a return value or a ref

// Listen for a getter
const state = reactive({ count: 0})
watch(
	() = > state.count,
    (newVal, oldVal) = > {
        // ...})// Listen to a ref
const count = ref(0)
watch(count, (newVal, oldVal) = > {
    // ...
})
Copy the code
Listen to multiple sources

Listeners can also use arrays to listen to multiple sources simultaneously:

watch([fooRef, barRef], ([newFoo, newBar], [oldFoo, oldBar]) = >{
    // ...
})
Copy the code

Teleport portal

attribute

Teleport provides a clean way to control which parent node in the DOM is rendering the HTML without resorting to global state or splitting it into two components

To-string – Conditionally determines rendering and mounts the DOM in the teleport tag of the current component under the DOM of the specified class name

<teleport to="#some-id">.</teleport>
<teleport to=".some-class">.</teleport>
<teleport to="[data-teleport]">.</teleport>
Copy the code

Disable-boolean — This property can be used for disabled functions, which means that the slot contents will not be moved to any location, but will be rendered at the location you specified in the surrounding parent component

<teleport to='#popup' :disabled='displayVideoInline'>
	<video src='./my-movie.mp4'/>
</teleport>
Copy the code

Note that this will move the actual DOM node, rather than being destroyed and recreated, and it will also keep the instance active state of any component instance, and all state HTML elements (that is, the video that plays) will keep their state

Using multiple Teleports on the same destination

A common usage scenario is a reusable component that may have multiple instances active at the same time, in which case multiple components can mount their contents to the same target element, and the order will be appending – the later mounts will follow the earlier mounts in the target element

<teleport to="#modals">
  <div>A</div>
</teleport>
<teleport to="#modals">
  <div>B</div>
</teleport>

<! -- result-->
<div id="modals">
  <div>A</div>
  <div>B</div>
</div>
Copy the code

Dynamically creating components

Within the parent component, we can dynamically create components that are inserted into the current DOM through control, and import components that currently need to be rendered

<template>
    <component :is='bloolear ? Foo : Bar'/>
</template>
<script setup>
    import Foo from './Foo.vue'
    import Bar from './Bar.vue'
</script>
Copy the code

Custom instruction

1. Write and import

In addition to the default built-in instructions (V-Modul and V-show), vue3 also allows import when you need to perform low-level operations on normal DOM elements. Custom instruction

We can import a directive just as we would import a component, but note that it requires a v to be added to the naming method, which is a marker to improve code readability

For example, if we create a vfocus.js, the Mounted lifecycle is aligned with the calling component

export default {
	// When the element bound to the directive is mounted to the DOM
    mounted(el){
        // The current element gets focus
        el.focus()
        console.log('Get the focus! '); }}Copy the code

Import within the required component, and call v-Focus directly within the input tag

<template> <input type="text" v-focus> </template> <script setup> import vFocus from '.. /vFocus.js'; </script>Copy the code

Another example: vhighlight.js

export default {
	// When the element bound to the directive is mounted to the DOM
    beforeMount(el, binding, vnode){
        el.style.background = binding.value
    }
}
Copy the code
< span style =" box-sizing: border-box; color: RGB (74, 74, 74); line-height: 20px; font-size: 13px! Important; white-space: inherit! Important;"Copy the code

This gets the corresponding variable in the current component before the element is rendered and passes the color to the directive to set the background color of the element

2. Customize the hook function of the directive API

The API is consistent with the components, as shown in:

  • Created: called before attributes of bound elements or event listeners are applied, useful when directives need to attach event listeners that need to be called before normal V-on event listeners.

  • Bind → beforeMount — called when a directive is first bound to an element and before the parent component is mounted

  • Inserted → Mounted — called after the parent component of the bound element is mounted

  • BeforeUpdate: New — called before updating the VNode that contains components

  • ComponentUpdated → UPDATE — called after the VNode that contains a component and its child components has been updated

  • BeforUnmount → New — called before the component or element is about to be removed

  • Unbind → Unmounted — called after the component or element is removed

Fragments

You can have multiple roots in Vue3

<template> <header>... </header> <main>... </main> <footer>... </footer> </template>Copy the code

Component communication

Props of the father the son

Similar to VUE2, properties declared on the parent component need to be passed to the child component, which needs to be bound on the component: child component property = ‘parent component property’, received in the child component via defindProps({key: value type})

// The parent component <template> <! -- 1. Events declared in the parent component, >< Props :fatherProps=" Props "></Props> </template> <script> import {ref} from 'vue' const Props= </script> <template> {{fatherProps}} </template> <script setup> import {withDefaults} from "vue"; DefineProps (['fatherProps']) // 2.2 defineProps({props1: String; props2: Number; DefineProps ({count1: {type: String, default: 'default'}, count2: {type: Number, default: 321 } }) </script>Copy the code

Emit the father

Vue3 provides an option for emits. Similar to props, this option is used to trigger the parent component event (child to parent).

// Subcomponent <template> <! -- 2. Bind native events to child components, triggering custom event names, $emit($emit($emit($emit($emit), $emit($emit), $emit($emit), $emit($emit), $emit($emit), STR)"> Trigger parent component event </button> </template> <script setup> import {ref} from 'vue' // 1. Const STR = ref(' string ') const emit = defineEmites(['my-click']) </script> </ template> <! -- 3. In the parent component, the child component label is bound to the trigger event name, Attribute value is the method in the current component --> < foo@my-click ='useFoo'></Foo> </template> <script setup> import Foo from './ foo.vue '// 3. Function useFoo(STR) {console.log(STR)} </script>Copy the code

Gets, calls, or changes state or methods within a child component

The child component label is bound to the ref property and gets the child component instance in the parent by exposing the variable or function in the child definExpose, and gets it through the ref.value.*

// The parent component <template> <! <Foo ref=' Foo '></Foo> < button@click ="getCount">getCount</button> </template> <script setup> import Foo from "./components/Foo.vue"; import { ref } from 'vue' // 2. Const foo = ref(null) const bb = ref('bb') function getCount() {// 4. Value = foo. Value = foo. A; foo.value.a = bbb; console.log(foo.value.a); foo.value.getName() } </script>Copy the code
// 1. A variable or function declared in a child component, <script setup> const a = ref('aaa') function getName() {console.log('name'); } defineExpose({ a, getName }) </script>Copy the code

Another method to export from a child component

You can import variables or methods from a child component to a parent component using the import method

impor Foo,{ useFoo } from './Foo.vue'
Copy the code

However, because of the setup syntax sugar, the child component throws a Setup object, and {useFoo} deconstructing does not work

At this point we can

/ / child components -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- < script > export function useFoo () {return} 'useFoo' export default {name: 'Foo', obj: { count: 100}} </script> <script setup> Above for the other a script script < / script > / / parent components -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - < the template > < button @ click = 'getFoo' > getFoo < / button > </template> <script setup> import Foo, { useFoo, a } from './Foo.vue' function getFoo() { console.log(useFoo()) // useFoo console.log(Foo.name) // Foo // ... } </script>Copy the code

Custom renderer CUSTome Renderer

Vue3.0 supports custom renderers. This API can be used to define rendering logic, such as rendering data to the canvas

Learn about…….. when you use it

Global API changed to instance method calls

Instance methods define components

Vue3 uses createApp to return the app instance, which exposes a number of global apis

// main.js
import { createApp, h } from 'vue'
import App from './App.vue'

createApp(App)
  .component('comp', {
    render() {
      return h('div'.'I am comp')
    }
  })
  .mount('#app')
Copy the code

Call the component directly from within the component through the label

<template>
	<comp/>
</template>
Copy the code

Global and Internal APIs were reconfigured for tree shaking

Many of vuE2’s global apis hang directly on top of constructors as static functions, such as vue.nexttick (). If we never use them in our code, we form what’s called dead code. Useless code caused by such global apis cannot be eliminated using Tree-shaking of WebPack

import Vue from 'vue'

Vue.nextTick(() = > {
    / /...
})
Copy the code

Vue3 has made a change to extract them as separate functions so that the tree shaker of the packaging tool can exclude these dead codes

import { nextTick } from 'vue'

nextTick(() = > {
    / /...
})
Copy the code

Affected APIS:

  • Vue.nextTick
  • Vue.observable (replaced with vue.reactive) — Reactive data definition method
  • Vue.version — Method of determining the version
  • Vue.com compile (in full version only) – method for doing compilation
  • Vue.set (in compatible version only)
  • Vue.delete (compatible version only)

Changes in v-Model use

Bidirectional binding of child components

In VUE2, the. Sync and V-Model functions overlap and can be easily confused, but in VUE3 they are unified

In Vue 3, the API for bidirectional data binding has been standardized, reducing confusion and giving developers more flexibility when using v-Model directives.

The parent passes the value back to the child and allows the child to return the value back to the parent

// parent <template> <comp v-model='counter'></comp> </template> <script setup> import {ref} from 'vue' const counter = Ref (0) </script> <template> <div @click="$emit('update:modelValue', modelVlaue + 1)"> {{modelValue}} </div> </template> <script setup> import { definProps } from 'vue' definProps({ modelValue:{ type: Number, default: 0 } }) </script>Copy the code

In VUE3, v-Model on a custom component is equivalent to passing a modelValue Prop and receiving the update:modelValue event thrown:

<ChildComponent v-model="pageTitle" />

<! --> -->

<ChildComponent
  :modelValue="pageTitle"
  @update:modelValue="pageTitle = $event"
/>
Copy the code

V – model parameters

If we need to change the modelValue name as an alternative to the modelValue option in the component, we can now pass an argument to the V-Model

v-model:xxx=”xxx”

<ChildComponent v-model:title="pageTitle" />

<! --> -->

<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
Copy the code

This can also be used as an alternative to the.sync modifier and allows us to use multiple V-Models on our custom components

<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />

<! --> -->

<ChildComponent
  :title="pageTitle"
  @update:title="pageTitle = $event"
  :content="pageContent"
  @update:content="pageContent = $event"
/>
Copy the code

This and the passed parameters are triggered by an event in the child component and updated to the parent component

<template>
	<div @click="$emit('update:pageTitle', pageTitle + 1)">
        counter: {{pageTitle}}
    </div>
</template>

<script setup>
	import { definProps } from 'vue'
    
    definProps({
        pageTitle:{
            type: Number,
            default: 0
        }
    })
</script>
Copy the code

Asynchronous component usage changes

To reduce the size of the program and optimize load speed, components need to be imported asynchronously only when they need to be rendered

Because functional components in VU3 must be defined as pure functions, there is a change in the definition of asynchronous components:

  • You must explicitly use the defineAsyncComponent package
  • Component option to rename to Loader
  • The Loader function no longer accepts resolve and reject and must return a Promise

Define an asynchronous component with no configuration

import { defineAsyncComponent  } from 'vue'

const AsyncComp = defineAsyncCompomemt(() = >('./components/AsyncComp.vue'))
Copy the code

Define an asynchronous component with a configuration. The Loader option is the previous Component

import { defineAsyncComponent  } from 'vue'
import ErrorComponent from './components/ErrorComponent.vue'
import LoadingComponent from './components/LoadingComponent.vue'

const AsyncComp = defineAsyncComponent({
    loader: () = > import('./components/AsyncComp.vue'),
    delay: 200.timeout: 3000.errorComponent: ErrorComponent,
    loadingComponent: LoadingComponent
})
Copy the code

User-defined component whitelist

Custom element detection in VUE3 occurs when the template is compiled. If you want to add some custom elements outside of VUE, you need to set the isCustomElement option in the compiler options

When using the build tool, templates are precompiled using the Vue-Loader. Just set the compilerOptions it provides, vue.config.js

rules: [
    {
        test: /\.vue$/,
        use: 'vue=loader'.options: {
            compolerOptions: {
                // Receive a tag that equals XXX and ignore the problem, eliminating the warning
                isCustomElement: tab= > tag === 'plastic-button'}}}]Copy the code

In the vite project, configure vueCompilerOptions in the vite.config.js:

module.exports = {
    vueCompilerOptions: {
        isCustomElement: tag= > tag === 'plastic-button'}}Copy the code

$scopedSlotsThe attribute is removed and replaced with $slots

$slots and scope slots are changed from $slots to $slots.

  • Slots are exposed as functions
  • $scopdSlots removed

Access the slot contents as a function, mylink.vue

<script>
import { h } from 'vue'
    export default {
        props: {
            to: {
                type: String,
                required: true
            },
        },
        render() {
            return h("a", { href: this to }, this.$slots.default());
        },
    }
</script>
Copy the code

Transition class name change

Vue2 writing is not uniform, vuE3 made corrections

  • V-enter → v-enter-from – corresponding – v-enter-to —- v-enter-active (transition)
  • V-leave → v-leave-from — corresponding — v-leave-to —- v-leave-active

The component Watch option and instance method $watch no longer support dot delimiter string paths

Expressions separated by. Are no longer supported by watches and watches. You can use a computed function for watch, you can use a computed function for watch, you can use a computed function for watch, and you can use a computed function for watch

this.$watch(() = > this.foo.bar, (v1, v2) = > {
    console.log(this.foo.bar)
})
Copy the code

KeyCode as a V-ON modifier has been removed

Vue2 can use keyCode to refer to a key, but vuE3 no longer supports it due to poor readability

<! -- keyCode mode is no longer supported -->
<input v-on:keyup.13="submit" />

<! -- Use alias only -->
<input @keyup.enter="submit" />

Copy the code

$on.$off$onceremove

The above three methods are not considered to be provided by VUE, so they are removed, and event dispatch and listening can be implemented using other third-party libraries

npm i mitt -S
Copy the code
/ / create the emitter
const emitter = mitt()

// Send the event
emitter.emit('foo'.'fooooooo')

// Listen for events
emitter.on('foo'.msg= > console.log(msg))
Copy the code

The filter to remove

The filter has been removed from vue3, call a method or calculate a property instead