New use of vue3

Preface: NOW I am using VUe3 to write a new project. Since it is the first time to use this VUE3, I am not familiar with many things, so I write this article to deepen my impression.

Use of Setup?

Setup is vue3’s new API, officially interpreted as a component option that is executed before creating a component.

Setup takes two parameters, props and context. The former is used to receive a value from the parent component. The latter is a plain (non-reactive) JavaScript object that exposes the component’s three attributes, attrs, slots, and EMIT.

Life cycle updates

BeforeCreate -> Use setup()

Created -> Setup ()

beforeMount -> onBeforeMount

mounted -> onMounted

beforeUpdate -> onBeforeUpdate

updated -> onUpdated

beforeUnmount -> onBeforeUnmount

unmounted -> onUnmounted

errorCaptured -> onErrorCaptured

renderTracked -> onRenderTracked

renderTriggered -> onRenderTriggered

activated -> onActivated

deactivated -> onDeactivated

Props of deconstruction

Because props is propped by proxy, it cannot be deconstructed. Therefore, it needs to use Vue's toRefs to complete attribute deconstruction

eg:

import { toRefs } from 'vue';

// we need to declare the props properties.
props: {
    title: String
},

setup(props) {
    const { title } = toRefs(props)

    console.log(title.value)
}
Copy the code

Currently, toRefs are not destructed if the value passed is optional, so toRef is used instead:

import { toRef } from 'vue'

props: { title? :String // Is this optional?
},

setup(props) {
    const title = toRef(props, 'title')

    console.log(title.value)
}
Copy the code

Example of an official balm type declaration

interface Data {
    [key: string]: unknown;
}

interface SetupContext {
    attrs: Data;
    slots: Slots;
    emit: (event: string. args: unknown[]) = > void;
}

function setup(props: Data, context: SetupContext) :Data;
Copy the code

How do I create reactive data?

Vue3 adds refs and Reactive to create reactive objects.

ref

Takes an internal value and returns a responsive, mutable ref object with a.value of the property pointing to the internal value

Eg:

const num = ref(0);

console.log(num.value); / / 0

num.value = 6; // Assign

console.log(num.value); / / 6 values
Copy the code

If you need to specify complex types, you can pass a generic parameter at definition time as follows:

const foo = ref<string | number> ('foo'); / / foo types: Ref < string | number >

foo.value = 123; // ok!
Copy the code

What is the difference between watch and watchEffect? When is a better time to use it?

watch

The watch API is exactly equivalent to the optional APIthis.$watch, which needs to listen for a specific data source and perform side effects in a separate callback function (which I understand to be my own business logic). By default, Watch is lazy — that is, the callback is invoked only when the data source it is listening to changes.

Watch listens to a data source

// Listen to a getter. It is useful to see that the router listens to params.
const state = reactive({count: 0});
watch(
    () = > state.count,
    (count, prevCount) = > {
        / *... * /});// Listen directly on a ref
const count = ref(0);
watch(count, (count, prevCount) = > {
    / *... * /
});
Copy the code

Watch listens to multiple data sources

watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) = > {
    / *... * /
});
Copy the code

watchEffect

Official explanation: Reactive tracing of its dependencies immediately runs a function and rerunts it when the dependency changes.

I do not understand, first of all, where is the source of the dependency, how to mark dependencies? Second, rerun the dependency when it changes. Will multiple dependencies be rerun when they change?

Take a look at the official example

// Create a responsive data source first
const count = ref(0);

// Execute immediately, in which case 0 must be printed.
watchEffect(() = > console.log(count.value));

setTimeout(() = > {
    // Execute once when data changes
    count.value++;
}, 100);
Copy the code

An example of your own testing

const count = ref(0);
const num = ref(8);

watchEffect(() = > {
    console.log(count.value);
    console.log(num.value);
});

setTimeout(() = > {
    count.value++;
}, 1000);
setTimeout(() = > {
    num.value++;
}, 1000);
Copy the code

Take a look at the results:

== first print == : 0,8 == second print == : 1, 8, 1, 9 (print four after 1s)

Explanation: The first print is a watchEffect feature that executes immediately, so the initial values for both data sources are printed here. The second printing of four is due to the assignment operation of both data sources. A side effect is triggered when the value of count changes after accumulation. Similarly, a side effect is triggered when the value of num changes.

What happens if you just print count

const count = ref(0);
const num = ref(8);

watchEffect(() = > {
    console.log(count.value);
});

setTimeout(() = > {
    count.value++;
}, 1000);

setTimeout(() = > {
    num.value++;
}, 1000);
Copy the code

== First print == : 0 == Second print == : 1

Explanation: This makes it obvious that this dependency has side effects only if written in watchEffect.

Muscle This is a big one. Hold on

The impact of multiple data sources on data

Look directly at the code below

const count = ref(0);
const num = ref(8);

watchEffect(() = > {
    console.log(count.value);
    num.value++;
});

setTimeout(() = > {
    count.value++;
}, 1000);

setTimeout(() = > {
    num.value++;
}, 1000);

setTimeout(() = > {
    console.log(num.value);
}, 4000);
Copy the code

== The first time == : 0 that is, the contents printed during initialization == The second time == : 1 1 twice timeout After 1s, print out the count value as 1 == the third time == : 12

Why is it 12

:bell: The watchEffect was executed during initialization, so num changed to 9. Then, after 1s, the value of num changed again to 10. At the same time, the value of num became 11 because of the timer increment, but this time it still triggered the side effect, so num was accumulated once and finally became 12.

By extension, what if you’re writing complex data?

const state = reactive({
    count: 0.num: 1}); watchEffect(() = > {
    // If this is the case, changes in count or num will trigger side effects
    console.log(state.count);
    console.log(state.num);

    // If this is done, it will only be executed once at the beginning, printing the initial value of state
    console.log(state);

    // If you write only this one, count changes will be printed
    console.log(state.count);
});

setTimeout(() = > {
    state.count++;
}, 1000);

setTimeout(() = > {
    state.num++;
}, 1000);
Copy the code

When is watchEffect executed?

If you look at the watchEffect above, you can see that the watchEffect is executed when the data source inside the watchEffect is written outside. That’s a reasonable way to do things otherwise you’ll end up calling each other indefinitely.) It’s triggered when something changes.

How is computed used?

Take a getter function and return an immutable reactive ref object for the value returned from the getter.

Official examples:

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

console.log(plusOne.value); / / 2

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

The message from this example is the following:

  1. Computed returns a responsive object, and you can get its value from the built-in value;

  2. Computed objects cannot be actively assigned, which is the same as before;

Through its built-ingetandsetTo create a writable ref object

const count = ref(1);

const plusOne = computed({
    get: () = > count.value + 1.set: (val) = > {
        count.value = val - 1; }});// The set function can be triggered during assignment
plusOne.value = 1;

console.log(count.value); / / 0
Copy the code

Slot is deprecated in VUe3, how should it be used?

slightly

How to use router?

Because vue3 does not have this, there is no way to use this.$router or this.$route for routing operations. So we need a new API to meet the requirements, of course the official can think of this problem, let’s see how to implement.

// React like operation,
import {useRouter, useRoute} from 'vue-router';

export default {
    setup() {
        const router = useRouter();
        const route = useRoute();

        function pushWithQuery(query) {
            router.push({
                name: 'search'.query: {... route.query, }, }); }}};Copy the code

The rest of the operations feel the same, such as route guards, and lazy loading-related operations, which will not be described here.

According to the Composition API?

I find an elegant way to create responsive data

setup() {
    // You can put all the variables you need here
    const state = reactive({
        count: 0.num: 10
    })

    // toRefs deconstructs the reactive object so that the template can use count directly as the data.
    return {
        ...toRefs(state)
    }
}
Copy the code

Reference Documents:

V3.cn.vuejs.org/api/compute…

Next.router.vuejs.org/zh/guide/ad…