向数组中push,不会出发watch更新

  setup() {
    let state = reactive({
      list: []
    })
    watch(
      () => state.list,
      (val) => {
        console.log(val)
      }
    )

    state.list.push(1) // 不会触发watch,不知道vue为什么这么做

    state.list = [1] // 重新赋值,会触发watch
  }

template 可以不再需要顶层节点

<template>
<Header/>
<Footer/>
</template>

teleport把节点挂载到指定id下

<teleport to="#endofbody">
    <div class="modal">
     hello world
    </div>
</teleport>

Suspense组件,展示加载状态

<Suspense>
  <template >
      <!-- 异步组件 -->
    <Suspended-component />
  </template>
  <template #fallback>
      <!-- 异步组件加载完毕前展示的内容 -->
    Loading...
  </template>
</Suspense>

升级指南

键盘绑定keycode无效,绑定别名有效


<!-- 无效 -->
<input v-on:keyup.13="submit" />

<!-- 有效 -->
<input v-on:keyup.enter="submit" />

移除on off 和 $once方法,使用mitt代替

filters被移除,使用methods或者computed代替

全局api调用方式改变

import Vue from 'vue'

Vue.mixin()

Vue.use()


import { createApp } from 'vue'

const app = createApp({})

app.mixin()

app.use()

setup 中可以实现之前的所有逻辑


setup() {
    onMounted(() => {
      console.log(333)
    })
}

全局属性挂载方法改变

// old
Vue.prototype.http = functuin() {}


// new
app.config.globalProperties.http = function(){}

this.http

render 方法修改

// old
render(h) {
    return h('div')
}


// new
import { h} from 'vue'

render() {
    return h('div')
}

data必须是一个函数

data() {
    return {}
}

支持定义异步组件


import { defineAsyncComponent } from 'vue'

// one
const asyncPageWithOptions = defineAsyncComponent({
  loader: () => import('./NextPage.vue'),
  delay: 200,
  timeout: 3000,
  error: ErrorComponent,
  loading: LoadingComponent
})

// two
const asyncComponent = defineAsyncComponent(
  () =>
    new Promise((resolve, reject) => {
      /* ... */
    })
)

使用非html元素的标签

app.config.isCustomElement = tag => tag === 'plastic-button'

在普通元素上使用is被v-is代替,component组件上仍然可以使用is

<button v-is="plastic-button">Click Me!</button>

<component is="plastic-button"></component>

this.\(scopedSlots将会被移除,统一替代成this.\)slots

// old
h(LayoutComponent, [
  h('div', { slot: 'header' }, this.header),
  h('div', { slot: 'content' }, this.content)
])


this.$scopedSlots.header


// new
h(LayoutComponent, {}, {
  header: () => h('div', this.header),
  content: () => h('div', this.content)
})
this.$slots.header

指令生命周期修改


Vue.directive('highlight', {
  beforeMount(el, binding, vnode) { // 对应bind
    el.style.background = binding.value
  },
  mounted() {}, // 对应inserted
  beforeUpdate() {}, // 新增
  updated() {}, // 对应update
  beforeUnmount() {}, // 新增
  unmounted() {} // 对应unbind
})

watch方法,不再支持通过.进行监听。包括 this.$watch 监听

watch: {
    // watch vm.e.f's value: {g: 5}
    'e.f': function (val, oldVal) { /* ... */ }
  }

vue-router 4的新功能

// 守卫不再需要next,并且支持async
router.beforeEach(async (to, from) => {
  // canUserAccess() returns `true` or `false`
  return await canUserAccess(to)
})