vue3 combination api (composition)

composition api

Composition API provides better logic reuse and code organization for Vue applications.

<template>
  <div>
    <p>counter: {
   
   {counter}}</p>
    <p>doubleCounter: {
   
   {doubleCounter}}</p>
    <p ref="desc"></p>
  </div>
</template>

<script>
import {
  reactive,
  computed,
  watch,
  ref,
  toRefs,
  onMounted,
  onUnmounted,
} from "vue";

export default {
  setup() {
    const data = reactive({
      counter: 1,
      doubleCounter: computed(() => data.counter * 2),
    });

    let timer

    onMounted(() => {
      timer = setInterval(() => {
        data.counter++
      }, 1000);
    })

    onUnmounted(() => {
      clearInterval(timer)
    })

    const desc = ref(null)

    watch(()=>data.counter, (val,oldVal)=>{
      // console.log(`counter change from ${oldVal} to ${val}`);
      desc.value.textContent = `counter change from ${oldVal} to ${val}`
    })
    
    return {...toRefs(data), desc};
  },
};
</script>

setup is a new option in Vue3.x, and it is the entry point for using the Composition API in the component.

When using setup, it accepts two arguments:

  1. props: the properties passed in by the component
  2. context

The props accepted in setup are responsive, and will be updated in time when new props are passed in. Because it is responsive, ES6 deconstruction cannot be used, and deconstruction will eliminate its responsiveness. An example of wrong code, this code will make props no longer support responsive

// demo.vue
export default defineComponent ({
    setup(props, context) {
        const { name } = props
        console.log(name)
    },
})

The second parameter context accepted by setup, context provides the three most commonly used attributes: attrs, slot and emit, corresponding to attr attribute, slot slot and attr attribute, slot slot and emit in Vue2.x respectivelya t tr attribute , s l o t slot and emit event. And these attributes are automatically synchronized with the latest values, so we get the latest values ​​every time we use them.

life cycle

image

Vue3.0 has added a new setup, which we also discussed in detail earlier, and then changed the name of beforeDestroy in Vue2.x to beforeUnmount; the destroyed table is more unmounted, the author said that this change is purely for more semantics, because a Components are a mount and unmount process. Other lifecycles in Vue2 are still preserved.
The above life cycle diagram does not contain all life cycle hooks, there are several others, all life cycle hooks such as

image

beforeCreate and created were replaced by setup (but you can still use it in Vue3, because Vue3 is backward compatible, that is, you are actually using vue2). Secondly, the hook naming has been added on; Vue3.x also adds new hook functions onRenderTriggered and onRenderTricked for debugging

import {  defineComponent, onBeforeMount, onMounted, onBeforeUpdate, onUpdated,
  onBeforeUnmount, onUnmounted, onErrorCaptured, onRenderTracked,
  onRenderTriggered} from "vue";
export default defineComponent({
  //  beforeCreate和created是vue2的 
  beforeCreate() {
    console.log("------beforeCreate-----");
  },
  created() {
    console.log("------created-----");
  },
  setup() {
    console.log("------setup-----");
    // vue3.x生命周期写在setup中 
    onBeforeMount(() => {
      console.log("------onBeforeMount-----");
    });
    onMounted(() => {
      console.log("------onMounted-----");
    });
    // 调试哪些数据发生了变化
    onRenderTriggered((event) => {
      console.log("------onRenderTriggered-----", event)
    })
  }
});

reactive、ref 与 toRefs

Vue3.x can use reactive and ref for data definition.

Defined by ref(), the value needs to be obtained through xx.value. Reactive cannot proxy basic types, such as strings, numbers, boolean, etc.

toRefs is used to convert a reactive object into a normal object whose properties are all ref objects.

Teleport

The Portal component provides a concise way to specify the parent element of its content.

<template>
  <button @click="modalOpen = true">
    弹出一个全屏模态窗口</button>

  <teleport to="body">
    <div v-if="modalOpen" class="modal">
      <div>
        这是一个模态窗口!
        我的父元素是"body"!
        <button @click="modalOpen = false">Close</button>
      </div>
    </div>
  </teleport>
</template>

<script>
export default {
  data() {
    return {
      modalOpen: true
    }
  },
};
</script>

<style scoped>
.modal {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.modal div {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background-color: white;
  width: 300px;
  height: 300px;
  padding: 5px;
}
</style>

Fragments

Components in vue3 can have multiple roots.

<template>
  <div id='teleport-box'></div>
  <img alt="Vue logo" src="./assets/logo.png" />
  <Bin v-model="counter" />
  <Tel />
</template>

Emits Component Option

Vue 3 currently provides an emits option, similar to the existing props option. This option can be used to define events that a component can fire to its parent.

The custom events sent by components in vue3 need to be defined in the emits option:

  • Native events will be triggered twice, such as click
  • Better indication of how components work
  • Object Form Event Validation

Global API changed to application instance call

There are many global APIs in vue2 that can change the behavior of vue, such as Vue.component and so on. This causes some problems:

Vue2 has no app concept. The root instance obtained by new Vue() is used as app. In this way, all created root instances share the same global configuration, which will pollute other test cases during testing, making testing difficult.
Global configuration also makes it impossible to create multiple app instances with different global configurations on a single page.

CreateApp is used in vue3 to return the app instance, which exposes a series of global APIs

import { createApp } from 'vue'
const app = createApp({})
	.component('comp', { render: () => h('div', 'i am comp') })
  .mount('#app')

2.x Global API 3.x Instance API (app)
View.config app.config
Vue.config.productionTip removed
Vue.config.ignoredElements app.config.isCustomElement
Vue.component app.component
Directive.view app.directive
Vue.mixin app.mixin
Vue.use app.use
View.filter removed

Global and internal APIs refactored to be tree-shaking optimized

Many global-api in vue2 are directly hung on the constructor as static functions, such as Vue.nextTick(). If we never use them in the code, the so-called dead code will be formed. This kind of global-api The resulting dead code cannot be eliminated using webpack's tree-shaking.

import Vue from 'vue'

Vue.nextTick(() => {
  // something something DOM-related
})

Corresponding changes have been made in vue3, and they are extracted into independent functions, so that the tree-shaking optimization of the packaging tool can exclude these dead codes.

import { nextTick } from 'vue'

nextTick(() => {
  // something something DOM-related
})

Affected APIs:

  • Vue.nextTick
  • Vue.observable (for Vue.reactive replacement)
  • Vue.version
  • Vue.compile (full builds only)
  • Vue.set (compatible builds only)
  • Vue.delete (compatible builds only)

v-model parameter

By default, v-model on components uses modelValue as prop and update:modelValue as event. We can modify these names by passing parameters to v-model:

<my-component v-model:title="bookTitle"></my-component>

The child component will need a title prop and emit the update:title event to be synced:

app.component('my-component', {
  props: {
    title: String
  },
  emits: ['update:title'],
  template: `
    <input
      type="text"
      :value="title"
      @input="$emit('update:title', $event.target.value)">
  `
})

Multiple v-model bindings

<user-name
  v-model:first-name="firstName"
  v-model:last-name="lastName"
></user-name>
app.component('user-name', {
  props: {
    firstName: String,
    lastName: String
  },
  emits: ['update:firstName', 'update:lastName'],
  template: `
    <input 
      type="text"
      :value="firstName"
      @input="$emit('update:firstName', $event.target.value)">

    <input
      type="text"
      :value="lastName"
      @input="$emit('update:lastName', $event.target.value)">
  `
})

The model option and the sync modifier of v-bind are removed and unified into the v-model parameter form

The .sync and v-model functions in vue2 overlap, which is easy to confuse, and vue3 has unified them.

<div id="app">
  <h3>{
   
   {data}}</h3>    
  <comp v-model="data"></comp>
</div>

app.component('comp', {
  template: `
    <div @click="$emit('update:modelValue', 'new value')">
    	i am comp, {
   
   {modelValue}}
    </div>
	`,
  props: ['modelValue'],
})

Rendering function API modification

The rendering function has become simpler and easier to use. The modification mainly includes the following points:

The h function is no longer passed in, we need to import it manually; flatten the props structure. scopedSlots deleted, unified to slots

import {h} from 'vue'

render() {
  const emit = this.$emit
  const onclick = this.onclick
  return h('div', [
    h('div', {
      onClick() {
      	emit('update:modelValue', 'new value')
    	}}, 
      `i am comp, ${this.modelValue}`
    ),
    h('button', {
      onClick(){
      	onclick()
    	}}, 
      'buty it!'
    )
  ])
},

Asynchronous components are required to be created using the defineAsyncComponent method

Since functional components in vue3 must be defined as pure functions, the definition of asynchronous components has the following changes:

  • must be explicitly wrapped with defineAsyncComponent
  • component option renamed to loader
  • The Loader function no longer accepts resolve and reject and must return a Promise
import { defineAsyncComponent } from 'vue'

// 不带配置的异步组件
const asyncPage = defineAsyncComponent(() => import('./NextPage.vue'))

Asynchronous component with configuration, the loader option is the previous component

import ErrorComponent from './components/ErrorComponent.vue'
import LoadingComponent from './components/LoadingComponent.vue'

// 待配置的异步组件
const asyncPageWithOptions = defineAsyncComponent({
  loader: () => import('./NextPage.vue'),
  delay: 200,
  timeout: 3000,
  // 加载异步组件时要使用的组件
  loadingComponent: LoadingComponent,
  // 加载失败时要使用的组件
  errorComponent: ErrorComponent,
})

The custom instruction API is consistent with the component

The instruction api and components in vue3 are consistent, as shown in:

  • bind → beforeMount
  • inserted → mounted
  • beforeUpdate: new! Called before the element itself is updated, similar to component lifecycle hooks
  • update → removed! is basically the same as updated, so it is removed and replaced by updated.
  • componentUpdated → updated
  • beforeUnmount new! Similar to component lifecycle hooks, called before the element will be removed.
  • unbind → unmounted
const app = Vue.createApp({})

app.directive('highlight', {
  beforeMount(el, binding, vnode) {
    el.style.background = binding.value
  }
})

<p v-highlight="yellow">Highlight this text bright yellow</p>

Component watch options and instance method $watch no longer support dot-separated string paths

Expressions separated by . are no longer supported by watch and watch, and calculation functions can be used as watch support, and calculation functions can be used as watch parameters to implement.

this.$watch(() => this.foo.bar, (v1, v2) => {
  console.log(this.foo.bar)
})

keyCode was removed as a v-on modifier

KeyCode can be used to refer to a key in vue2, but vue3 no longer supports it.

<!-- keyCode方式不再被支持 -->
<input v-on:keyup.13="submit" />

<!-- 只能使用alias方式 -->
<input v-on:keyup.enter="submit" />

on,off and $once remove

The above three methods are considered not to be provided by vue, so they have been removed, and other three-party libraries can be used to implement them.

<script src="https://unpkg.com/mitt/dist/mitt.umd.js"></script>

// 创建emitter
const emitter = mitt()

// 发送事件
emitter.emit('foo', 'foooooooo')

// 监听事件
emitter.on('foo', msg => console.log(msg))

FiltersRemove

Filters are removed in vue3, please call methods or computed properties instead.

The keyCode method is no longer supported -->

### on,off and $once 移除

上述3个方法被认为不应该由vue提供,因此被移除了,可以使用其他三方库实现。



// create emitter
const emitter = mitt()

// emit event
emitter.emit('foo', 'fooooooooo')

// Listen for events
emitter.on('foo', msg => console.log(msg))

### Filters移除
vue3中移除了过滤器,请调用方法或者计算属性代替。





Guess you like

Origin blog.csdn.net/lyf976229437/article/details/122108638