Up to now, vuE3 has been officially launched, so we are going to have a complete understanding of it.

And then the more important parts of it were organized into this document,

This document is only streamlined, not in-depth

proxy

To better intercept object/array changes, vue’s underlying implementation of bidirectional binding also abandoned Object.defineProperty in favor of proxy

The drawback of Object.defineProperty is that it does not listen for new fields of the object. Vue initialization also takes a lot of time to recursively mount each property of the object, which results in vue2’s limitation of using $set to set new fields. It also needs to hack away the array push, replace and other methods, so VUe3 also chooses a better proxy

The global method

// Vue 2.x
Vue.prototype.$http = () = > {}

// Vue 3
const app = createApp({})
app.config.globalProperties.$http = () = > {}
Copy the code

composition api

The properties of objects in VUe2 have been stripped out of vue3 and made available to developers in the form of composition API, thus better supporting tree Sharking, i.e. apis that are not used when packaging are not entered

ref

Wrap basic types, and of course pass in objects (call reactive internally)

To change the value of ref, pass.value

import { ref } from 'vue'
const count = ref(2)
/ / modify
count.value = 3
Copy the code

reactive

For reference types, unlike ref, it does not need to pass.value

import { reactive } from 'vue'
const person = reactive({ name: 'Joe'.age: 18 })
/ / modify
person.name = 'bill'
Copy the code

isRef

Determine if a value was created by ref

import { ref, isRef } from 'vue'
const count = ref(2)

isRef(count) // true
Copy the code

toRef

Treating a value of Reactive to a REF maintains a reactive connection to its original reactive

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

toRefs

Each value of Reactive is treated as a REF, mostly to facilitate the use of values in Reactive in templates

export default {
  setup() {
    // Can be destructed without losing responsiveness
    const state = reactive({
      foo: 1.bar: 2
    })

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

isReactive

See if it’s reactive

readonly

Receive a REF or Reactive and return a read-only, reactive new object

const original = reactive({ count: 0 })

const copy = readonly(original)
copy.count++ / / warning!
Copy the code

watchEffect

Execute a function passed in immediately, trace its dependencies responsively, and re-run the function when its dependencies change.

const count = ref(0)

watchEffect(() = > console.log(count.value))
// -> logs 0

setTimeout(() = > {
  count.value++
  // -> logs 1
}, 100)
Copy the code

watch

In contrast to watchEffect, Watch allows us to:

  • Lazy execution of side effects;
  • Be more specific about the state in which the listener should be triggered to restart;
  • Accesses the previous and current values of the monitored state.
/ / website demo

// Listen for a getter
const state = reactive({ count: 0 })
watch(() = > state.count, (count, prevCount) = > {
    / *... * /})// Listen directly on a ref
const count = ref(0)
watch(count, (count, prevCount) = > {
  / *... * /
})

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

computed

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

// Customize get set
const plusOne = computed({
  get: () = > count.value + 1.set: val= > {
    count.value = val - 1}})Copy the code

filter

Filters are no longer supported and are recommended to be replaced with method calls or computed properties

/ / website demo

<template>
  <p>{{ accountInUSD }}</p>
</template>

<script>
  export default {
    props: {
      accountBalance: {
        type: Number.required: true}},computed: {
      accountInUSD() {
        return '$' + this.accountBalance
      }
    }
  }
</script>
Copy the code

You can also use the JS native pipe operators

// same as plus(123)
<template>
  <p>{{  123 |> plus }}</p>
</template>

<script>
  export default {
    setup() {
      function plus(val) {
        return val + 100
      } 
      return {
        myFormat
      }
    }
  }
</script>
Copy the code

The life cycle

  • beforeCreate -> setup
  • created -> setup
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeUnmount -> onBeforeUnmount
  • unmounted -> onUnmounted
  • errorCaptured -> onErrorCaptured

teleport

A portal that renders the DOM inside the component into the specified container.

Usage scenario: Modal pop-ups don’t have to pop up inside components anymore

The to argument takes a selector for the specified DOM, and supports multiple simultaneous uses, which will be appended to and appear together

<div id="modal"></div>

<teleport to="#modal" >
  <div>a</div>
</teleport>
<teleport to="#modal" >
  <div>b</div>
</teleport>

/ / the result
<div id="modal">
  <div>a</div>
  <div>b</div>
</div>
Copy the code

CSS depth selector

:deep()

<style scoped>
.a :deep(.b) {
  /* ... */
}
</style>
Copy the code

The above code will compile to:

.a[data-v-f3f3eg9] .b {
  / *... * /
}
Copy the code

css v-bind

Single-file component

<template>
  <div class="text">hello</div>
</template>

<script>
export default {
  data() {
    return {
      color: 'red'
    }
  }
}
</script>

<style>
.text {
  color: v-bind(color);
}
</style>
Copy the code

This syntax also works with < Script Setup > and supports JavaScript expressions (need to be enclosed in quotes)

<script setup>
const theme = {
  color: 'red'
}
</script>

<template>
  <p>hello</p>
</template>

<style scoped>
p {
  color: v-bind('theme.color');
}
</style>
Copy the code

style module

The

<template>
  <p :class="$style.red">
    This should be red
  </p>
</template>

<style module>
.red {
  color: red;
}
</style>
Copy the code

Ref array

In Vue 2, the ref attribute used in v-for populates the corresponding $refs property with the REF array. When there are nested V-fors, this behavior becomes ambiguous and inefficient.

In Vue 3, this usage will no longer automatically create $ref arrays. To get multiple refs from a single binding, bind the ref to a more flexible function (which is a new feature) :

<div v-for="item in list" :ref="setItemRef"></div>

export default {
  data() {
    return {
      itemRefs: []}},methods: {
    setItemRef(el) {
      if (el) {
        this.itemRefs.push(el)
      }
    }
  },
  beforeUpdate() {
    this.itemRefs = []
  },
  updated() {
    console.log(this.itemRefs)
  }
}
Copy the code

Combined API:

import { onBeforeUpdate, onUpdated } from 'vue'

export default {
  setup() {
    let itemRefs = []
    const setItemRef = el= > {
      if (el) {
        itemRefs.push(el)
      }
    }
    onBeforeUpdate(() = > {
      itemRefs = []
    })
    onUpdated(() = > {
      console.log(itemRefs)
    })
    return {
      setItemRef
    }
  }
}
Copy the code

fragment

Multiple root nodes are allowed

<template>
  <a>.</a>
  <b>.</b>
  <c>.</c>
</template>
Copy the code

The data options

Vue2: Data can be object or function

Vue3: Normalized to accept only functions that return object

<script>
export default {
  data() {
    return {
      color: 'red'
    }
  }
}
</script>
Copy the code

Mixins merge behavior changes

When data() from a component and its mixin or extends base class are merged, the merge operation is now performed shallowly:

const Mixin = {
  data() {
    return {
      user: {
        name: 'Jack'.id: 1}}}}const CompA = {
  mixins: [Mixin],
  data() {
    return {
      user: {
        id: 2}}}}Copy the code

In Vue 2.x, the generated $data is:

{
  "user": {
    "id": 2."name": "Jack"}}Copy the code

In 3.0, the result will be:

{
  "user": {
    "id": 2}}Copy the code

Remove $listeners

In Vue 3, event listeners are considered attributes prefixed only with on, so that they become part of the attrs object, and therefore part of the attrs object, and therefore part of the attrs object, and therefore listeners are removed.

emits

You need to log all events fired by each component using emits. Vue3 removed the. Native modifier. Any event listeners not declared in emits are counted in the component’s $attrs and bound to the root node of the component by default.

<template> <div> <p>{{ text }}</p> <button v-on:click="$emit('accepted')">OK</button> </div> </template> <script> export  default { props: ['text'], emits: ['accepted'] } </script>Copy the code

v-on.native

Vue will now add all event listeners in a child component that are not defined to be triggered by the component as native event listeners to the root element of the child component (unless inheritAttrs: False is set in the child component’s options).

Take the click event as an example. Now if you want @click.native, you don’t need to write ‘click’ in the child component’s emits

v-model

Supports multiple V-models, which can be customized to receive props values

// Parent <ChildComponent v-model:title="pageTitle" v-model:content="pageContent" /> <! -- is short for:  --> <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" :content="pageContent" @update:content="pageContent = $event" />Copy the code
Vue export default {props: {modelValue: String}, emits: ['update:modelValue'], methods: $emit('update:modelValue', title) {this.$emit('input', title) '}}Copy the code

Conclusion:

Accept: just props, such as name and age

Submit: update: start, emit(‘update:name’, value), emit(‘update:age’, value)

External use: v-model:name=” XXX “V-model :age=”aaa”

Event apis

The on, ON, ON, off, and $once instance methods have been removed, and the component instance no longer implements the event-triggering interface.

Custom instruction

Target event and lifecycle unification

Created - New! Called before an attribute or event listener for an element is applied. Bind → beforeMount inserted → mounted beforeUpdate: Add! Called before the element itself is updated, much like a component's lifecycle hook. Update → Remove! The hook has so much in common with the updated that it is redundant. Please use the updated version instead. ComponentUpdated → Updated beforeUnmount: Added! Similar to a component's lifecycle hook, it is called before the element is unloaded. unbind -> unmountedCopy the code

Asynchronous components

In Vue 3, since functional components are defined as pure functions, asynchronous components need to be explicitly defined by wrapping them in the new defineAsyncComponent helper method:

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

// Asynchronous components with no options
const asyncModal = defineAsyncComponent(() = > import('./Modal.vue'))

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

Suspense

Asynchronous component processing scenarios within components

Nodes in the default slot are displayed as much as possible. If not, the nodes in the Fallback slot are displayed.

<template>
  <suspense>
    <template #default>
      <todo-list />
    </template>
    <template #fallback>
      <div>
        Loading...
      </div>
    </template>
  </suspense>
</template>

<script>
export default {
  components: {
    TodoList: defineAsyncComponent(() => import('./TodoList.vue'))
  }
}
</script>
Copy the code

The default slot places your original components

Fallback slot places loading when the component is not properly loaded

Render function API

Vue2 is the h function received in render(h){} function, when we need to reuse method need to pass h, very inconvenient

The vue3 h function was removed from the ‘vue’ package, the render function no longer receives it

import { h, reactive } from 'vue'
Copy the code

V-bind merge behavior

In 2.x, if an element defines both v-bind=”object” and an identical independent attribute, that independent attribute always overrides the binding in object.

<! -- -- -- > templates
<div id="red" v-bind="{ id: 'blue' }"></div>
<! Results - - - >
<div id="red"></div>
Copy the code

In 3.x, if an element defines both V-bind =”object” and the same independent attribute, the order in which the bindings are declared determines how they are merged. In other words, developers now have more control over what they want to merge than if they assumed that they always want separate attributes to override what is defined in Object.

<! -- -- -- > templates
<div id="red" v-bind="{ id: 'blue' }"></div>
<! Results - - - >
<div id="blue"></div>

<! -- -- -- > templates
<div v-bind="{ id: 'blue' }" id="red"></div>
<! Results - - - >
<div id="red"></div>
Copy the code

VNode life cycle events

// vue2
<template>
  <child-component @hook:updated="onUpdated">
</template>
Copy the code
Vue3 <template> <child-component @vnode-updated="onUpdated"> </template> // camelback <template> <child-component @vnodeUpdated="onUpdated"> </template>Copy the code

Lifecycle event listening mode changed from @hook: to @vnode-

Transition class name change

vue2

.v-enter..v-leave-to {
  opacity: 0;
}

.v-leave..v-enter-to {
  opacity: 1;
}
Copy the code

Vue3 was renamed to

.v-enter-from..v-leave-to {
  opacity: 0;
}

.v-leave-from..v-enter-to {
  opacity: 1;
}
Copy the code

Accessed in prop’s default function

Factory functions that generate prop defaults can no longer access this.

Instead:

The raw prop received by the component is passed as an argument to the default function;

Inject API is available in default functions.

import { inject } from 'vue'

export default {
  props: {
    theme: {
      default (props) {
        // 'props' is passed to the component,
        // The original value before any type/default cast,
        // The injected property can also be accessed using 'inject'
        return inject('theme'.'default-theme')}}}}Copy the code

Listen to the array

When you listen on an array with the Watch option, the callback is triggered only if the array is replaced. In other words, listening callbacks will no longer be fired when the array is changed. To trigger a listening callback when an array is changed, you must specify the deep option.

{
  watch: {
    bookList: {
      handler(val, oldVal) {
        console.log('book list changed')},deep: true}}}Copy the code