Why Vue3.0

  1. Vue2.0 support for TypeScript is not friendly enough
  2. In Vue2.0, the mixins approach is not flexible and convenient for the reuse of logic with repeated reactive data. In the Options API, you can see that multiple processing logic is mixed together in data, methods, computed, and Watch. Composition API is tentatively translated as “Composition API”, where each processing logic can become a module to expose the API. These modules are similar to regular ES6 modules, but use reactive data or methods built into Vue3.0. This way, when we need to use a piece of logic, we just need to combine the modules. At the same time, only the corresponding module needs to be modified when the logic is modified.

New features of Vue3.0

Vue3.0 official website: v3.vuejs.org/

Data response

We know that before Vue3.0 the principle of Object data responsiveness was Object.defineProperty(), Arrays are responsive by intercepting seven of their methods (push, POP, Shift, unshift, splice, sort, reverse). The problem with this approach is that we cannot directly detect additions and deletions of attributes to objects; For the array, we cannot detect that we can directly modify the contents of the array subscript and use length to modify the length of the array.

The principle of data response of Vue3.0 is realized by Proxy. Proxy is officially released in ES2015 specification. It sets up a layer of “interception” before the target object. All external access to the object must pass this layer of interception, so it provides a mechanism for filtering and rewriting external access.

  • Proxies can listen directly on objects rather than properties. So property additions and deletions of objects can also be monitored.
  • The Proxy can listen for changes to the array directly. So arrays directly modify the content of the subscript and the length can also be listened for.
  • Proxy has up to 13 interception methods, not limited to Apply, deleteProperty, HAS, and so on

The diff algorithm

Compared with the OPTIMIZATION of DIFF algorithm in Vue2.0, static analysis is added and nodes are divided into static nodes and non-static nodes. Static nodes refer to nodes without binding responsive data. In Vue2.0, the unit of diFF algorithm is component. In Vue3.0, the rendering template of component is divided into block “blocks” according to non-static nodes, and the unit of DIFF algorithm is block by block. No matter how deep the hierarchy is nested, its dynamic nodes are directly bound to the Block root node, eliminating the need to traverse the static nodes.

Friendly support for TypeScript

Everyone who uses it says yes.

Allows a component to have multiple root nodes

Fragment enables a component to have multiple root nodes. Fragment virtual elements that are not rendered into HTML.

<template>
  <div>Hello</div>
  <div>World</div>
</template>
Copy the code

Composition API

Performance improvement

  • The implementation of the virtual Dom has been rewritten (and compatibility has been ensured, rendering out of templates is in high demand).
  • Compilation template optimization.
  • More efficient component initialization.
  • Update Performance improved by 1.3 to 2 times.
  • SSR speed increased 2-3 times.

Tree shaking support

A lot of times, we don’t need all the features that Vue offers. There is no way to exclude them in Vue 2, but 3.0 might be introduced on demand.

zhuanlan.zhihu.com/p/134302690

zhuanlan.zhihu.com/p/191216161

Create a project

Vite way

After node.js is installed locally, create a project using the following command:

$ npm init vite-app <project-name>
$ cd <project-name>
$ npm install
$ npm run dev
Copy the code

CLI way

Update our vuE-CLI globally first

$ npm install -g @vue/cli
Copy the code

Create a project using vue-cli command:

$ vue create cli-demo
Copy the code

You can also customize the default configurations. We will select typescript-enabled options here.

We can also use it by loading CDN or Vue3.0 in Webpack. Details v3.vuejs.org/guide/insta…

Vite and Webpack package comparison

Vite is a package tool written by God, let’s take a look at the features of Vite:

  • Quick cold start
  • Instant module hot update
  • True on-demand compilation

Webpack is packaged according to the dependencies of each module, then the development server is started, and the browser returns the packaged results when it requests the page.

Vite directly starts the development server and asks which module will compile the module in real time. On-demand compilation of Vite takes advantage of modern browsers’ support for ES Modules, which browsers automatically request from dependent modules. Vite takes full advantage of this. Vite compiled files retain the native Import statements, and the browser sees the Import and asks the server for those Import files. The dependencies of individual files are handled automatically by the browser.

Here is the resource information requested by the Vite project:The following is the resource request information after the Webpack project starts:By comparison, we can see that the Vite project requests our project code files directly, while Webpack requests packaged and compiled files. As a result, Vite is very fast to boot and dynamically compiled on demand. Compilation on demand greatly reduces compilation time. The more complex the project, the more modules, the more obvious the advantages of Vite.

How it works: Vite internally borrows KOA to start a service. To proxy these modules. Intercepting requests and then dynamically compiling various types of files.

In HMR (hot update), when a module is changed, just ask the browser to request the module again, unlike WebPack, which needs to compile all the dependencies of the module once, which is more efficient. When it comes time to package into production, Vite uses traditional rollup for packaging. Therefore, the main advantage of Vite is in the development phase.

Blog.csdn.net/qq_41499782… zhuanlan.zhihu.com/p/150083887

knife

Sharpening the knife does not mistakenly cut wood workers, here recommend 2 easy to use VSCode plug-in

ESLint



If this does not work after installation, you can create a.vscode folder in the root directory of the project and create a settings.json file in the folder with the following contents:

//settings.json
{
    "eslint.validate": ["typescript"]}Copy the code

The restart takes effect. We could try it out and define any variable and get an ESLint prompt like this:

One is telling us that the variable a is only defined and not used, and the other is telling us that a has not been reassigned and should be defined using const. Using ESLint improves the way we write code.

Vetur



We can make our VUE files display labels in different colors and give us intelligent prompts when we type.

Grammar part

The setup function

The concept of Composition API was proposed in Vue3. In Vue 2.0, we defined components using props, data, methods, etc. In UE 3.0, we used Setup to define reactive data and methods.

<template>
    <h3>Welcome to my nuggets</h3>
    <button v-for="(item, key) in data.languages" :key="key" @click="selectLangFun(key)">{{item}}</button>
    <p>【{{selected}}】</p>
</template>

<script lang="ts">
import { reactive, ref} from "vue";
export default {
    name: "ProgramSelect".setup() {
        const data = reactive({
            languages: ["Java"."Javascript"."C#"]});const selected = ref("");
        const selectLangFun = (params: number): void= > {
            selected.value = data.languages[params];
        }
        return {
            data,
            selected,
            selectLangFun
        }
    }
}
Copy the code
  • The setup(context) function has two parameters. Props corresponds to the props of Vue 2, which defines variables passed from the parent component to the child component. This is not exposed in the setup function; context corresponds to this object in Vue 2. More detailed usage methods will be introduced later.
  • All variables used in the template template need to be returned by the setup function.
  • Reactive variables are created by reactive and REF. The difference is that Reactive creates responses for object variables, whereas ref creates responses for primitive types.
  • The value of the selected variable created by ref is added to the value. Vue3 internally converts a simple type, such as the string “hello”, into an object that has a value property and the value is “hello”.
  • All data and methods used in the template need to be returned.

ToRef and toRefs

Above we have introduced some basic uses of Reactive and REF. Here are some tips for toRef and toRefs. Part of the code will use TS syntax, if you are not familiar with the students, you can first learn this blog juejin.cn/post/690313…

toRef

ToRef () is a function that returns a toRef object. Const result = toRef(target, “key”);

  • A ToRef object is not a Ref object; The object we create through ref() must be responsive data that is modified and directly reflected on the page.
  • The ToRef object does not have to be responsive, depending on whether target’s data is responsive.
  • If target is a reactive object (created by reactive or ref), result is a reactive object.
  • The toRef() function is a mapping to target; When result is changed, target is also changed. The reverse is also true, target is modified, result content is also modified.
<template>
    <p>{{a}} <button @click="changeA">changeA</button></p>
    <p>{{objRef}} <button @click="change">change</button></p>
    <p>{{reactiveRef}} <button @click="changeReactive">changeReactive</button></p>
</template>
<script lang="ts">
import { toRef, reactive, ref} from 'vue'

export default {
    setup(){
        // Common objects
        const obj = {name: "zhangsan".age: 12}
        const objRef = toRef(obj, "name");
        // A responsive object
        const a = ref("hello");
        // A responsive object
        const reactiveObj = reactive({name: "Willim".age: 6});
        const reactiveRef = toRef(reactiveObj, "name");
        const changeA = function(){
            // Will respond to the page
            a.value = "Vue 3.0";
        }
        const change = function(){
            // Affects the value of obj, but does not respond to the page
            objRef.value = "lisi";
            console.log(obj);
        }
        const changeReactive = function(){
           // Affects the value of reactiveObj, but does not respond to the page
            reactiveRef.value = "lisi";
            console.log(reactiveObj);
        }
        return{
            objRef,
            reactiveRef,
            change,
            changeReactive,
            a,
            changeA
        }
    }
}
</script>
Copy the code



Clicking on changeA immediately responds to the page; Clicking change does not immediately respond to the page; When we click the changeReactive button, we change the responsive data, so the data on the update page will be updated, so Zhangsan and Willim will be updated.

  • The screenshot above is what we see when we print the values of objRef and reactiveRef. As you can see, the custom attribute _object of result is target, and they hold the mapping between the new value result and the original value target through _object.
  • You can also see that reactiveObj is a reactive object, which is a Proxy object on the _object property.

To sum up: ToRef () is supposed to be similar toRef(), which responds to a variable of an underlying type (returns an object with a value attribute), whereas toRef() strips attributes from the object (returns an object with a value attribute). If the original object is reactive, then the stripped properties are also reactive; If the original data is not responsive, then the stripped property is a normal object and has no responsive function.

toRefs

Let’s start with a question: what does the Proxy object structure look like?

  const target = {name: 'zs'};
  const handler = {
      get: function(obj, prop){
          return prop in obj ? obj[prop]: 18}}const p = new Proxy(target, handler)
  constpp = {... p};console.log(target, pp, p);
Copy the code

We defined p as a Proxy object, but after… After deconstruction, the new object PP is no longer a Proxy object. In Vue 3.0, the function reactive() will return a Proxy object, but if you deconstruct variables created by Reactive (), the data returned will no longer be a Proxy object, making it unresponsive. ToRefs () solves this problem.

  • The toRefs() function is very similar to toRef(). ToRef () strips out an attribute of the object, and toRefs() strips out all attributes of the object, returning a new object.
  • ToRefs () is const result = toRefs(target); Each attribute of target is converted to a ToRef object.
  • Like toRef(), if the original target is responsive, result is responsive; If target is an ordinary object, result is also an ordinary object and has no response.
  • Since the toRefs() function returns an object, each attribute of the object is a ToRef object. We know that… Destruct objects are shallow copies, so using the toRefs() function, we can destruct reactive data. Here’s an example
<template>
    <p>I am {{name}}, age {{age}}<button @click="change">change</button></p>
</template>
<script>
import {reactive, toRefs} from 'vue';
export default {
    setup(){
        const data = reactive({
            name: "Willim".age: 6.change: () = >{ data.age++; }})return {
            ...toRefs(data)
        }
    }
}
</script>
Copy the code

computed

We can use computed to process a responsive variable and return a variable of type ComputedRef.

  let doubleCount = computed(() = >{
      return data.count * 2;
  })
Copy the code

watch

In Vue 3.0, the watch function can listen for multiple variables, adding logic to handle when listening for a variable change (can be understood as the side effect of this variable change). Or listen for changes in multiple variables. Examples of usage are as follows:

// Listen on a variable
watch(selected, (newVal, oldVal) = >{
    console.log("newVal", newVal);
    console.log("oldVal", oldVal);
    document.title = newVal;
});

// Listen for multiple variables
watch([selected, () = >data.count], (newVal, oldVal) = >{
    console.log("newVal", newVal);
    console.log("oldVal", oldVal);
    document.title = newVal[0] +","+ newVal[1];
});
Copy the code
  • watch(arg, (newVal, oldVal)=>{})You can listen for only one reactive variable, arg. The second parameter is the callback function, and when the ARG changes, the side effect function logic. NewVal represents the modified value of the variable, and oldVal represents the old value of the variable.
  • watch([arg1, arg2,...] , (newVal, oldVal)=>{})Arg1, arg2, etc. The second argument is a callback function. When arg1, arg2… When any one changes, the side effects function logic. NewVal represents the modified value of the variable, in this case as an array, with the variables arg1, arg2… One-to-one correspondence; OldVal represents the old value of the variable, as an array, with the variables arg1, arg2… One to one correspondence
  • Selected is a variable created by the ref function that we can listen for directly.
  • Data is a variable created by the Reacitve function. We only want to listen for an attribute (count) that needs to be written as a function fetch.

The life cycle

Using the setup function, you must be wondering what happened to the declared period function in Vue2.0.

In fact, the original declared cycle functions of Vue2.0 can still be used in Vue3.0. Meanwhile, Vue3.0 also defines a set of life cycle functions corresponding to Vue2.0.

Options API Hook inside (setup)
beforeCreate Not needed* Asynchronously request initialized data
created Not needed*
beforeMount onBeforeMount The function executed before the component is mounted to the node
mounted onMounted A function that executes after the component is mounted
beforeUpdate onBeforeUpdate The function executed before the component is updated
updated onUpdated A function that executes after the component has been updated
beforeUnmount (beforeDestroy) onBeforeUnmount A function executed before the component is unloaded
unmounted (destoryed ) onUnmounted A function that executes after the component is unloaded
errorCaptured onErrorCaptured Called when an error from a descendant component is caught
renderTracked(+) onRenderTracked((event)=>{}) State tracking, executed when any of the reactive data defined in the Setup function changes, and event contains all of the reactive variables
renderTriggered(+) onRenderTriggered((event)=>{}) State tracking, executed when any of the reactive data defined in the Setup function changes, and events only contain data variables that have changed
  • Setup is run around beforeCreate and created, so we previously needed beforeCreate and created in Vue2.0 to define the logic directly in setup.
  • OnX is imported before it is defined in the setup function.
  • The Options API is sibling to the setup() function and does not require an import
import { onMounted, onUpdated, onUnmounted } from 'vue'

const MyComponent = {
  setup() {
    onMounted(() = > {
      console.log('mounted! ')
    })
    onUpdated(() = > {
      console.log('updated! ')
    })
    onUnmounted(() = > {
      console.log('unmounted! ')})},created(){
    console.log("created")},beforeCreate(){
      console.log("beforeCreate")},beforeMount(){
      console.log("beforeMount")},mounted(){
      console.log("mounted")}}Copy the code
  • The loading order of Vue2.0 and Vue3.0 is: setup() -> beforeCreate -> created -> onBeforeMount -> beforeMount -> renderTracked -> onMounted -> mounted
  • Vue2.0 and Vue3.0 are updated in the order: renderTriggered -> onBeforeUpdate -> beforeUpdate -> renderTracked -> onUpdated -> updated
  • The uninstallation sequence of Vue2.0 and Vue3.0 components is onBeforeUnmount -> beforeUnmount -> onUnmounted -> unmounted

Composition API

For the extraction of common logic, improve reusability. In Vue2.0 we can extract some common processing logic in the form of mixins. Take recording the position of the user clicking on the screen as an example to display the position of the user clicking on the screen each time. Regardless of the reusability of the code, we would simply write the following code:

<template>
  <p>X: {{x}}, Y: {{y}}</p>
</template>

<script lang="ts">
import { defineComponent, reactive, toRefs, onMounted, onUnmounted } from 'vue';
export default defineComponent({
  name: 'App'.setup(){
    const pointer = reactive({
      x: 0.y: 0
    });
    const updateMouse = (e: MouseEvent) = >{
      pointer.x = e.pageX;
      pointer.y = e.pageY;
    };
    onMounted(() = >{
      document.addEventListener("click", updateMouse)
    })
    onUnmounted(() = >{
      document.removeEventListener("click", updateMouse)
    })
    return{
      ...toRefs(pointer)
    }
  }
});
</script>
Copy the code

In Vue3.0, we can consider extracting this logic to get the current time as a common module. This module is similar to a normal Javascript module, except that the code uses Vue3.0 reactive variables and Vue3.0 built-in methods, which usually return the reactive variables. The implementation method is as follows:

// Create a new folder hooks, and create a new file usemousePosition.ts
import { reactive, onMounted, onUnmounted } from 'vue';
function useMousePosition(){
    const pointer = reactive({
        x: 0.y: 0
      });
      const updateMouse = (e: MouseEvent) = >{
        pointer.x = e.pageX;
        pointer.y = e.pageY;
      };
      onMounted(() = >{
        document.addEventListener("click", updateMouse)
      });
      onUnmounted(() = >{
        document.removeEventListener("click", updateMouse)
      });
      return pointer;
}
export default useMousePosition;
Copy the code

All we need to do in app. vue is import the useMousePosition method and use it.

<template>
  <p>X: {{x}}, Y: {{y}}</p>
</template>

<script lang="ts">
import { defineComponent, reactive, toRefs} from 'vue';
/ / import useMousePosition
import useMousePosition from './hooks/useMousePosition';
export default defineComponent({
  name: 'App'.setup(){
    const pointer = useMousePosition();
    return{
      ...toRefs(pointer)
    }
  }
});
</script>
Copy the code

Teleport components

Teleport translates to “to be teleported”. Teleport components are designed to mount a component on a specified element. Before Vue3.0, all of our components were mounted on the root node, such as the APP div id, and other components were nested layer by layer in the entire HTML node tree. Vue3.0’s Teleport component allows us to mount a component on a specific node, such as a node at the same level as the root node app, so that code implementation is more in line with our custom, rather than layer upon layer of nested app nodes. This section uses the pop-up component as an example to describe how to use the Teleport component.

<! -- index.html file -->
  <body>
    <div id="app"></div>
      <! -- Add modal node -->
    <div id="modal"></div>
  </body>
Copy the code
  • Create modal. vue file, Vue3.0 component extracts emits to make logic clearer; Payload. Type checks incoming data.
  • The to in Teleport specifies the element to be mounted, in this case the element with the ID modal.
/ / Modal. Vue file<template>
    <teleport to="#modal">
        <div id="center" v-if="isOpen">
            <h1><slot>The title</slot></h1>
            <button @click="close">close</button>
        </div>
    </teleport>
</template>
<script lang="ts">
import {defineComponent} from 'vue'
export default defineComponent({
    props: {
        isOpen: Boolean
    },
    emits: {
        'close-modal': (payload: any) = >{
            return payload.type === "close"}},setup(props, context){
        const close = function(){
            context.emit("close-modal", {type: "close"});
        }
        return {
            close
        }
    }
})
</script>
<style scoped>
    #center{
        position: fixed;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        width: 200px;
        height: 200px;
        border: 1px solid #cccccc;
        background: white;
        margin: auto;
    }
</style>
Copy the code

Use a Modal component in any component.

<template>
    <p>Is my<button @click="showModal">Display pop-up box</button></p>
    <Modal :isOpen="openFlag" @closeModal="close">
        <slot>this is title</slot>
    </Modal>
</template>
<script lang="ts">
import {ref} from 'vue'
import Modal from "./Modal.vue";
export default {
    components: {
        Modal
    },
    setup(){
        const openFlag = ref(true);
        const close = function(){
            openFlag.value = false;
        }
        const showModal = function(){
            openFlag.value = true;
        }
        return{
            openFlag,
            close,
            showModal
        }
    }
}
</script>
Copy the code

Suspense components

Suspense is a built-in special component of Vue3 that defines the display of components with asynchronous request data. If you use Suspense, you need to return a promise in your setup function.

Case 1

To create the ayncshow. vue file, the setup function needs to return a Promise object.

<template>
    <h1>{{result}}</h1>
</template>
<script lang="ts">
export default {
    setup(){
        return new Promise((resoluve) = >{
            setTimeout(() = >{
                resoluve({result: 100})},2000)}}}</script>
Copy the code

Used in the app.vue file

<template>
  <Suspense>
      <template #default> 
        <div>
          <AsyncShow/>
        </div>
      </template>
      <template #fallback>
        <h1>Loading...</h1>
      </template>
  </Suspense>
</template>

<script lang="ts">
import { defineComponent, ref, toRefs} from 'vue';
import AsyncShow from './components/AsyncShow.vue';
export default defineComponent({
  name: 'App'.components: {
    AsyncShow
  }
});
</script>
Copy the code
  • Suspense components have two built-in named slots, one is default, to show successful requests for asynchronous components. A fallback is used to display the content of the page before the asynchronous component requests and responds.
  • The default slot can have multiple components, but requires a root node.

Case 2

After simulating sending asynchronous requests, display components. Here’s a nice back-end request interface: dog. CEO /dog-api/ to get random pictures of dogs. First we’ll wrap an Ajax get request method:

//ajax.ts
function getAjax(url: string){
    return new Promise((resolve, reject) = > {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.send();
        xhr.onreadystatechange = function(){
            if(xhr.readyState === 4) {if(xhr.status === 200){
                    resolve(xhr.response)
                }else{
                    reject(xhr.response);
                }
            }
        }
        xhr.onerror = () = >{ reject(xhr.response); }}); }export default getAjax;
Copy the code

Encapsulate a component that requests a dog image:

//DogShow.vue 
<template>
    <img :src="rawData">
</template>
<script lang="ts">
import {defineComponent} from 'vue';
import getAjax from '.. /ajax';
export default defineComponent({
    async setup(){
    const p = getAjax("https://dog.ceo/api/breeds/image/random");
    let rawData;
    await p.then((result) = >{
        const tmp = JSON.parse(result as string);
        if(tmp.status==="success"){ rawData = tmp.message; }})return {
        rawData
    }
}
})
</script>
Copy the code

Use in app.vue

<template>
  <Suspense>
      <template #default> 
        <div>
          <AsyncShow/><DogShow/>
        </div>
      </template>
      <template #fallback>
        <h1>Loading...</h1>
      </template>
  </Suspense>
  <p>{{error}}</p>
</template>

<script lang="ts">
import { defineComponent, ref, toRefs, onErrorCaptured} from 'vue';
import AsyncShow from './components/AsyncShow.vue';
import DogShow from './components/DogShow.vue';
export default defineComponent({
  name: 'App'.components: {
    AsyncShow,
    DogShow
  },
  setup(){
    const error = ref(null);
    onErrorCaptured((e: any) = >{
      error.value = e;
      return true;
    })
    return{
      error,
    }
  }
});
</script>
Copy the code



OnErrorCaptured is listening for errors, such as our URL being deliberately miswritten. OnErrorCaptured cannot be caught in DogShow, or it cannot be captured in app. vue.

defineComponent

DefineComponent exists to support TypeScript. DefineComponent does not implement special logic to get a corresponding type for an incoming object. We use defineComponent to define components that support type hints for setup, props, and so on.

Thank you

If this article is helpful to you, please feel free to give a thumbs up. It will be my motivation to keep writing

Keep smiling and never give up, even when things get you down. Even when the reality gets you down, keep smiling and never give up.