Title: Vue3composition api

Yayxs /frontend-thick-talk

This article was first published on May 20, 2020

This article was modified on December 14, 2020

Main reference Source Composition API RFC

Composite apis in VUE3

preface

I don’t know what moment Utah sent out a tweet, and I knew something was wrong. Is coming… Vue3 was released on this special day.

The core of this article is the Composition API. Let’s first unveil it and do a simple horizontal comparison. Finally, we will build two versions together: one using the Composition API and the other using the Options-based API. Simply explore the difference and connection between the two

The installation

We can upgrade by using official scaffolding. Upgrade vue directly by running the command add vue-next

This is the version of Vue 3 in the dependency package

{
  "name": "vite-init-app"."version": "0.0.0"."scripts": {
    "dev": "vite"."build": "vite build"
  },
  "dependencies": {
    "vue": "^ 3.0.4"
  },
  "devDependencies": {
    "vite": 13 "^" 1.0.0 - rc.."@vue/compiler-sfc": "^ 3.0.4"}}Copy the code

As we can see: Vue no longer has the default export, naming the export creates a new Vue application, just as constructors were used in VUe2, and plug-in Settings will be usedusemethods

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

import SumApp from "./views/Sum.vue";

createApp(SumApp).mount("#app");
Copy the code

Of course, until 20201214 VUe3 can use Vite to create projects,

// Complete JSON
{
  "name": "composition-api-app"."version": "0.1.0 from"."scripts": {
    "dev": "vite"."build": "vite build"
  },
  "dependencies": {
    "vue": "^ 3.0.4"
  },
  "devDependencies": {
    "vite": 13 "^" 1.0.0 - rc.."@vue/compiler-sfc": "^ 3.0.4"}}Copy the code

Historical reasons

As functionality grows, the code for complex components becomes increasingly difficult to read and understand. This is especially true when developers read code written by others. The root cause is that Vue’s existing APIS force us to organize code by options, but sometimes it makes more sense to organize code by logical relationships.

There is currently a lack of a concise and low-cost mechanism to extract and reuse logic between multiple components.

3. Typescript is inconvenient

Vue 3 supports typescript better, and we know ts has better type inference

So what’s the problem right now,

  • 2. The X version of VUE has less consideration for type inference (using flow) in the designers, and is trying to matchtsIt’s always a bit of a problem when used together
  • Vue rely onthisContext accessattributeAll right,methodsWell, it’s safe to saythisIt’s magic in VUE

It is good to sum up the invariability, but in the development, the solution to a problem must be refined, and constantly seek better solutions, which is also the spirit of developers. I think in the 2.x version, we use Data Watch methods and various life cycles, just like this

Because of this, it is very difficult to read groups through a large and complex file, and we often scroll back and forth. Another disadvantage is that logical reuse is very difficult because there is no intuitive splitting of components. For better reuse of code logic

Composition API

What is a composite API

The new API does not break the current 2.x code and can even continue to be used in a way that addresses some of the limitations of vue2.x

Evan You:has described the Composition API as a reactive API coupled with the ability to register lifecycle hooks using globally imported functions. This new API is reactive and has the ability to register lifecycle hooks using global import functions

In short, vue 3 doesn’t add anything new, or vue exposes some internal functionality for developers to use directly in components.

avue3component

<template> <section class="container"> <section class="main"> <section class="composition-api"> <section> <h4>vue 3 Sum</h4> </section> <section class="ipt"> <p>counter:{{ total }}</p> <form id="sum"> <input type="text" class="form-control" v-model="number1" /> <input type="text" class="form-control" v-model="number2" /> <button </button> </form> </section> </section> </section> </section> </section> <section> <h4>fetch data</h4> </section> <ul class="list"> <li v-for="item in todoLists" :key="item.id">{{ item.title }}</li> </ul> </template> <script> // ref import { ref, computed, reactive, toRefs, onMounted, watch } from "vue"; const SumApp = { props: { userId: { type: String, default: "01xx", }, }, setup(props, ctx) { // console.log(this); // undefined // console.log(props); // proxy {userId:} // props const {userId} = props; const todoLists = ref([]); const number1 = ref(0); const number2 = ref(0); const sum = ref(0); const title = reactive({ title: `test-title`, }); Const total = computed({get: () => {return 'sum is ${sum.value}'; }}); const handleAddClick = () => { sum.value = parseInt(number1.value) + parseInt(number2.value); }; const getTodoLists = async () => { console.log("gettodolist"); const response = await fetch( "https://jsonplaceholder.typicode.com/todos" ); todoLists.value = await response.json(); }; onMounted(getTodoLists); watch(userId, getTodoLists); return { obj: { name: "yaxs", }, number1, number2, total, todoLists, handleAddClick, getTodoLists, ... toRefs(title), }; }}; export default SumApp; </script> <style lang="css" scoped></style>Copy the code

As a simple example, a new API is setup(), which includes both the state and clicked event functions written inside it. This is a little different from vue 2.x. In the previous writing, we would put some methods in methods, so there is a problem. As the project gets bigger and bigger, the components get longer and longer, and in the later maintenance,

  • Method does not correspond to the state of the data it depends on, jumping up and down in the editor to find the correlation
  • Consider a scenario where a small module (containing data and state), for example, displays a number twice as large as our example above, so that we can put the logic and data describing the scenario directly into the common section for reuse

vue-class-component

With so many developers currently using or about to use class components, there is still a lot of uncertainty about the implementation details of VUE 3, which becomes a risky move

Most of the latest apis are type-friendly and use common variables and functions, allowing developers to enjoy type inference

Can we still use class-component? For now, it’s still available, but not recommended

Horizontal contrast

Of course, when we compare, we have to pull out these two pictures

Let’s build a simple component that allows the user to add two numbers at the press of a button. First, we’ll quickly review how to do this using the Options-based API. We will then use the Composition API to recreate the same components to illustrate the differences.

Build using the Options API

 data() {
    return {
      num1: 0.num2: 0.sum: 0}; },methods: {
    addNumbers() {
      this.sum = parseInt(this.num1) + parseInt(this.num2); }},Copy the code

Using the Vue 2 option-based API, we return the reaction data object in the data() function. The AddNumbers() function is then registered in the methods property. To access the variable data() from it, you must use this.

Now, this is nested inside the method, so it should refer to the object of the method rather than the entire instance, but it doesn’t. Vue is working behind the scenes here to solve this problem for us. Usually, this is pretty straightforward, but in more complex cases, this behind-the-scenes processing can lead to unexpected behavior this.

This is exactly what we mentioned earlier about typescript support

Build using the Composition API

Before using VUe3, vuE-CLI-plugin-VUE-next has been officially prepared for us. Those who are familiar with vUE scaffolding are familiar with this, and we can pass

vue add vue-next
Copy the code

To use VUe3 in your projects, you can easily convert vue 2.x projects into VUE 3 projects

// main.js
import { createApp } from "vue";
import App from "./App.vue";

createApp(App).mount("#app");
Copy the code

We can see that it’s different when it’s mounted, and by different I mean probably different usage,

In the previous version

import Vue from "vue";
import App from "./App.vue";

Vue.config.productionTip = false;

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

Declare reactive statereactive

To create reactive state for JavaScript objects, use the Reactive method,

import { reactive } from "vue";

// reactive state
const state = reactive({
  title: `Vue 2.x Option API vs Vue 3 Composition API`});Copy the code

Data is a reactive object or a reactive state.

Data. XXX is required when used in the component template section so that the value can be fetched, but it is a bit of a hassle, or you can borrow roResfs

return {
  ...toRefs(state),
};
Copy the code

Converts a reactive object to a normal object on return, but its properties are reactive.

What’s returned herestateIs a responsive object that all Vue users should be familiar with

Side effects based on reactive statewatchEffect

import { reactive, computed, watchEffect } from "vue";

watchEffect(() = > {
  document.title = `count is ${state.count}`;
});
Copy the code

Create independent reactive valuesref

import { ref } from "vue";
// ref returns a responsive object containing only a property named value
let num = ref(0);
Copy the code

Finally, we return the functions and properties to the template.

return {
  num,
};
Copy the code

One thing here is that ref uses let num = ref(0) in the variable. That’s how we make variables reactive! There are two functions that deal with state and reactivity: REF and Reactive.

Calculate attributecomputed

Sometimes we need to rely on other states, which in VUE are handled by evaluating properties. So when we started, we just went straight to a method of calculating the sum of the two, just to make it easier to demonstrate. Next we use computed()

import { computed } from "vue";
Copy the code
let sum1 = computed(() = > parseInt(num3.value) + parseInt(num4.value));
Copy the code

thissum1Is an object we call “ref” because it is used as a reactive reference to the internal values it holds.

setup

There are two parameters

  • Props: As expected in a standard component, the props in the setup function are reactive and will be updated when a new prop is passed in
  • Context: The second argument passed to the setup function is context. Context is a plain JavaScript object that exposes the properties of three components:

  setup(props,{attrs,slots,emit}) {
    console.log(props)
    // 1 props is responsive
    // 2 Use toRefs when deconstructing
    const {total}  = toRefs(props)

    console.log(attrs)
    console.log(slots)
    console.log(emit)

    const initNumber = ref(0)

    return {
      initNumber
    }
  },
Copy the code

Everything is now inside the setup() function. All functions or attributes that need to be used in the template should be added, setup() because this is how they are returned to the template. Internally setup(), we use response variables defined as independent variables at the top, rather than return objects in the data() function. There is also a function, a separate hang, that is not written in methods, and now we can easily reuse our functionality between component instances, which will significantly improve the readability of large code bases. Also notice that this no longer needs to reference a variable!

The life cycleonMounted

const getTodoLists = async() = > {console.log("gettodolist");
  const response = await fetch("https://jsonplaceholder.typicode.com/todos");
  todoLists.value = await response.json();
};
onMounted(getTodoLists);
Copy the code

watchEffect vs watch

WatchEffect is similar to the Watch option in 2.x, but it does not need to separate dependent data sources from side effect callbacks. The composite API also provides a watch function that behaves exactly like the 2.x option.