1. Introduction to Vue3

Vue.js is designed to be adopted gradually, so Vue3 supports most of the features in 2 (migration guidelines).

What is Vue3’s improvement over Vue2?

Clever blackboard, the actual development does not have much use, the interview is extremely likely to ask. 💪

New features of Vue3:

  • performance

    • Rewriting the virtual DOM

    • Packaging size reduction (Tree Shaking)

    • First render fast

    • Reduced memory usage (compositionAPI)

  • New syntax (Syntax API)

    • Ref and reactive

    • The computed and watch

    • New lifecycle functions

    • Custom functions -Hooks functions (like React Hooks)

  • Other new features

    • Teleport Teleport component
    • Suspense loads components asynchronously
    • Global API modifications and optimizations
    • Custom renderer
  • Any changes have been moved to the global instance

    Global configuration

    • Vue. Config to app. Config

    Global registration class API

    • Ponent Vue.com app.com ponent instead

    • Vue) directive instead of app) directive

    Behavior extends the class API

    • Vue. Instead of a mixin app. Mixins

    • Vue. Use to app. Use

  • Better Typescript support

The problem with Vue2

One of the core problems facing Vue2’s component API design is how to organize the logic and how to extract and reuse the logic between multiple components. There are a few common logic reuse patterns based on Vue 2’s current API, but they all have more or less problems. These models include:

  • Mixins
    • Naming conflicts
    • The role of exposed variables is unclear
    • Reuse of other Components often causes problems
  • Higher- Order Components (aka HOCs)
  • Renderless Components (Components based on scoped slots/Scoped slot encapsulation logic)

In general, these models have the following problems:

  • The data source in the template is not clear

    For example, when multiple mixins are used in a component, it can be difficult to tell which mixin an attribute comes from just by looking at the template. HOC has a similar problem.

  • Namespace conflict

    There is no guarantee that mixins developed by different developers will not use exactly the same property or method name. HOC has a similar problem with injected props.

  • performance

    Both HOC and Renderless Components require additional component instances to be nested to encapsulate logic, resulting in unnecessary performance overhead.

The problem solved by Vue3

The Function-based API, inspired by the React Hooks, provides a completely new logic reuse solution without the above problems. Using a function-based API, we can extract the associated code into a “composition function” — a function that encapsulates the associated logic and returns the state that needs to be exposed to the component as a responsive data source. Here is an example of using a composite function to encapsulate mouse position listening logic:

function useMouse() {
  const x = ref(0)
  const y = ref(0)
  const update = e= > {
    x.value = e.pageX
    y.value = e.pageY
  }
  onMounted(() = > {
    window.addEventListener('mousemove', update)
  })
  onUnmounted(() = > {
    window.removeEventListener('mousemove', update)
  })
  return { x, y }
}

// Use this function in a component
const Component = {
  setup() {
    const { x, y } = useMouse()
    // Use with other functions
    const { z } = useOtherLogic()
    return { x, y, z }
  },
  template: `<div>{{ x }} {{ y }} {{ z }}</div>`
}
Copy the code

As you can see from the above example:

  • Attributes exposed to templates are clearly sourced (returned from functions)
  • The return value can be arbitrarily renamed, so there is no namespace conflict
  • There is no performance cost associated with creating additional component instances

Type of problem:

Vue3 originally expected Class classes to solve the typology problem, but the typology problem still exists. The end result is a naturally type-friendly function-based API, because the code written by the function-based API is almost identical when using TS or native JS

Packing size:

The function-based API allows each function to be introduced individually by ES export, mainly as tree-shaking friendly

Apis that are not in use will be removed when packaged. Function based apis provide better compression efficiency, but Class attributes and method names are not

2. Vue3 experience

There are three installation methods:

  1. Import as a CDN package on the page.
  2. Install it using NPM.
  3. Use the official CLI to build a project.
1. cdn
<script src="https://unpkg.com/vue@next"></script>
Copy the code
2. npm
// The latest stable version
npm install vue@next
Copy the code
3. Command-line interface (CLI)
// The latest stable version
yarn global add @vue/cli
// Then run it in the Vue project (update the relevant plug-in)
vue upgrade --next
// Create a Vue3 project just like 2
vue create vue3_demo
Copy the code
4. Vite:
// yarnYarn create@vitejs /app <project-name> 'CD <project-name> yarn yarn devCopy the code

hello,word!!!

<script src="https://unpkg.com/vue@next"></script>
<body>
    <div id="app">
        {{msg}} == {{count}}
    </div>
</body>
<script>
   // Do not use the compositionAPI
    const CountApp = {
        data() {
            return {
                msg: 'hello,word'.count: 0}},mounted() {
            setInterval(() = > {
                this.count++
            }, 1000)}}// Declarative rendering
    Vue.createApp(CountApp).mount('#app')
</script>
Copy the code

CompositionAPI:

const { ref, computed, watch, onMounted } = Vue
const App = {
  template: '
      
< / div > `
.setup() { // The data of the response const count = ref(0) // Calculate the properties const plusOne = computed(() = > count.value + 1) / / method const increment = () = > { count.value++ } / / observation watch(() = > count.value * 2.val= > { console.log(`count * 2 is ${val}`)})// Life cycle onMounted(() = > { console.log(`mounted`)})// Bidirectional bound data must be returned return { count, plusOne, increment } } } // Declarative rendering Vue.createApp(App).mount('#app') Copy the code

3. Reactive objects

1. The setup function

The setup function is where the component logic is, and it is called after the component instance is initialized with props when it is created.

// Setup () will receive the initial props as an argument:
const MyComponent = {
  props: {
    name: String
  },
  setup(props) {
    console.log(props.name)
  }
}
Copy the code

Note:

The props object passed in is a responsive object and can be observed as a data source. However, any changes to the props object will be synchronized within the framework, so do not modify them directly. (Will result in a warning)

Component status:

Setup () can return an object like data whose properties will be exposed to the template’s rendering context:

const MyComponent = {
  props: {
    name: String
  },
  setup(props) {
    return {
      msg: `hello ${props.name}! `}},template: `<div>{{ msg }}</div>`
}
Copy the code

2. ref

Create a value that can be managed within setup(), using the ref function.

The ref function returns a wrapper object with a single value attribute (primarily used to wrap raw type data).

import { ref } from 'vue'

const MyComponent = {
  setup(props) {
    const msg = ref('hello')
    const appendName = () = > {
      msg.value = `hello ${props.name}`
    }
    return {
      msg,
      appendName
    }
  },
  // When a wrapper object is exposed to a template rendering context, or nested within another responsive object, it is automatically expanded to an internal value.
  template: `<div @click="appendName">{{ msg }}</div>`
}

// The value of the wrapper object can be changed directly
console.log(msg.value) // 'hello'
msg.value = 'tiedan' // Change to tiedan
Copy the code

Why do you need a wrapper object?

We know that in JavaScript, primitive value types such as String and number have only values, no references. If you return a string variable in a function, the code that receives the string will only get a value and will not be able to track subsequent changes to the original variable.

Therefore, the point of a wrapper object is to provide a container that allows us to pass values of any type between functions by reference.

3. reactive

Return an unwrapped response object (mainly used for non-primitive data)

import { reactive } from 'vue'

const object = reactive({
  count: 0
})

object.count++
Copy the code

4. toRefs

Converts a reactive object to a normal object, where each property of the result object is a ref pointing to the corresponding property of the original object

In other words, reactive objects are turned into normal objects that can be turned into reactive objects

 const { reactive, toRefs } = Vue
 const MyComponent = {
   setup() {
     const obj = reactive({
       count: 0
     })
     const inc = () = > {
       obj.count++
     }
     return {
       // 
       // If the ES6 extension operator is used directly, the bidirectional data binding feature is removed and toRefs() is used to convert the data to reactive data. toRefs(obj), inc } },template: `<h1 @click="inc">{{ count }}</h1>`
 }
Copy the code

5. Watchers

watchEffect

Automatically collects dependencies and re-executes itself when the dependencies update

  1. Execute immediately, no inertia, the first load of the page is executed.

  2. Automatically detects internal code and executes if there are dependencies in the code.

  3. There is no need to pass what you want to listen on, and code dependencies are automatically sensed.

const { watchEffect, ref } = Vue

Vue.createApp({
  setup() {
    const userId = ref(0)
    // Trigger whenever data changes
    watchEffect(() = > console.log(userId.value))
    setTimeout(() = > {
      userId.value = 1
    }, 1000)
    return {
      userId
    }
  }

}).mount('#app')
Copy the code

watch

The dependency is explicitly specified, and the callback function is executed when the dependency is updated

  1. With lazy, the first page is not executed, but only when the data changes.
  2. Parameter can get the current value and original value.
  3. You can listen for multiple data changes.
// Listen for a getter
const state = reactive({ count: 0 })
watch(
  () = > state.count,
  (count, prevCount) = > {
    / *... * /})// Listen directly to the ref
const count = ref(0)
watch(count, (count, prevCount) = > {
  / *... * /
})

// Listen to multiple data sources
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) = > {
  / *... * /
})
Copy the code

The difference between the two:

  • WatchEffect is similar to computed in that it obtains the data source to be monitored by executing the side effect function, whereas watch obtains the data source to be monitored by the first argument (You can listen to multiple)
  • Watch has 3 parameters (data source, side effect function, configuration), watchEffect has only two (side effect function, configuration)
  • The watch side effect function takes two more arguments than watchEffect.

Similarities:

  • Watch and watchEffect share the same method to stop listening.

  • Same side effect refresh timing

  • Can be stopped by the return stop function

4. Life cycle

  • In addition to beforeCreate and Created, there are nine old lifecycle hooks in our setup method that we can access in our setup method

    • OnBeforeMount — called before mounting begins

    • OnMount — called after mounting

    • OnBeforeUpdate – called when response data changes and before rerendering

    • OnUpdated — called after rerendering

    • OnBeforeUnmount — called before the Vue instance is destroyed

    • OnUnmounted — called after the instance is destroyed

    • OnActivated — called when the keep-alive component is activated

    • OnDeactivated — called when the keep-Alive component is deactivated

    • OnErrorCaptured — called when errors are captured from subcomponents

  • Changing both destruction lifecycles is more semantic

    • BeforeDestory into beforeUnmount
    • Destoryed into unmounted
  • Two new life cycles are added for easy debugging

    • onRenderTracked
    • onRenderTriggered

5. Teleport Teleport component

A common scenario is to create a component that contains full-screen mode. In most cases, you want the logic of the modal box to exist in the components, but the quick positioning of the modal box is difficult to solve with CSS, or requires changing the composition of the components

When using this component in the initial HTML structure, we can see a problem — the modal box is rendered in a deeply nested DIV, and the position: Absolute of the modal box is referenced to the parent relative positioned DIV.

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.

Suspense asynchronous components

The Suspense component is used to show the backing content while waiting for some asynchronous component to resolve

Application example:

  • Displays the load animation before the page loads
  • Display placeholder content
  • Processing lazily loaded images

Define asynchrony:

 app.component('component-a', {
        setup() {
            return new Promise((resolve, reject) = > {
                setTimeout(() = > {
                    resolve({
                        result: "Asynchronous Components"
                    });
                }, 2000)})},template: `<h1>{{result}}</h1>`,})Copy the code

Suspense component application:

<div id="app">
  <Suspense>
    <template #default>
      <component-a></component-a>
    </template>
    <template #fallback>
      <h1>Loadding...</h1>
    </template>
  </Suspense>
</div>
Copy the code

That’s my brief summary of Vue3