vue2.x与vue3.x的对比

异步组件(vue3.x新增)

vue3.x

  • 由于函数式组件被定义为纯函数,因此异步组件的定义需要通过将其包装在新的 defineAsyncComponent助手方法中来显式地定义
import { defineAsyncComponent } from 'vue'
import ErrorComponent from './components/ErrorComponent.vue'
import LoadingComponent from './components/LoadingComponent.vue'

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

// 带选项的异步组件
const asyncPageWithOptions = defineAsyncComponent({
  loader: () => import('./NextPage.vue'),
  delay: 200,
  timeout: 3000,
  errorComponent: ErrorComponent,
  loadingComponent: LoadingComponent
})
  • component选项现在被重命名为loader,loader函数不再接收resolvereject参数,且必须返回promise
const asyncComponent = defineAsyncComponent(
  ()=>new Promise((resolve,reject)=>{
    /*...*/
  })
)

片段(vue3.x新增)

vue3.x
组件可以有多个根节点

// vue2.x


// vue3.x

v-for

vue2.x
v-for指令可以绑定数组的数据来渲染列表

{{ item.message }}

vue3.x
从单个绑定获取多个ref,ref会通过迭代的key被设置(新特性)

自定义元素交互

vue2.x
通过Vue.config.ignoredElements配置自定义元素白名单

Vue.config.ignoredElements = ['plastic-button']

vue3.x
在模板编译期间执行指示编译器将视为自定义元素

  • 如果使用生成步骤:将isCustomElement传递给 Vue模板编译器,如果使用vue-loader,则应通过 vue-loader 的compilerOptions选项传递
rules: [
  {
    test: /\.vue$/,
    use: 'vue-loader',
    options: {
      compilerOptions: {
        isCustomElement: tag => tag === 'plastic-button'
      }
    }
  }
]
  • 如果使用动态模板编译,通过 app.config.isCustomElement传递
const app = Vue.createApp({})
app.config.isCustomElement = tag => tag === 'plastic-button'

自定义内置元素的方法是向内置元素添加is属性
v-is要使用注册名称来渲染组件,其值应为 JavaScript字符串文本


Data选项

vue2.x
可以自定义data选项是objectfunction

// object 声明


// function 声明 

vue3.x
data选项只接受返回objectfunction


全局API

vue2.x
有许多全局API和配置,会全局改变vue的行为

vue3.x
调用createApp返回一个应用实例

import {createApp} from 'vue'
const app = createApp({})

config.productionTip移除
config.ignoredElements替换为 config.isCustomElement

下表为2.x与3.x的对比

2.x 全局 API 3.x 实例 API (app)
Vue.config app.config
Vue.config.productionTip 已移除
Vue.config.ignoredElements app.config.isCustomElement
Vue.component app.component
Vue.directive app.directive
Vue.mixin app.mixin
Vue.use app.use

全局 API Treeshaking

vue2.x
Vue.nextTick()是一个全局的 API 直接暴露在单个 Vue 对象上,回调的this上下文自动绑定到当前实例
webpack支持tree-shaking,但Vue 2.x 的全局 API 比如 nextTick 无法被 TreeShake,所以就算没有用到这些 API,它们还是会被打包到你的生产版本的代码包里

vue3.x
全局和内部API进行了重构,支持使用tree-shaking
需要注意的是:
当使用全局 API 时,需要主动将其导入到目标文件中

import { nextTick } from 'vue';
 
nextTick(() => {
  // 和 DOM 有关的一些操作
});

如果直接调用Vue.nextTick(),会导致报错:undefined is not a function

key attribute

vue2.x
建议在v-if/v-else/v-else-if的分支中使用key

// vue2.x
YES
NO

vue3.x
vue会自动生成唯一的key

YES
NO

按键修饰符

vue2.x

  • 支持keyCodes作为修改v-on的方法
  • 可以通过全局config.keyCodes





vue3.x

  • 不再支持使用数字 (即键码) 作为v-on 修饰符,建议使用kebab-cased大小写名称
  • 不再支持 config.keyCodes

在 prop 的默认函数中访问this

vue3.x
生成 prop 默认值的工厂函数不再能访问 this

渲染函数API

vue2.x

  • render函数参数
    render函数自动接收h函数作为参数
// vue2.x
export default
  render(h){
    return h('div')
  }
}
  • render函数签名更改
    render函数自动接收诸如 h 之类的参数
// vue2.x
export default{
  render(h){
    return h('div')
  }
}

vue3.x

  • render函数参数
    h是全局引入的,而不是作为参数自动传递
// vue 3.x
import {h} from 'vue'
export default{
  render(){
    return h('div')
  }
}
  • render函数签名更改
    render函数不再接收任何参数,将主要用于setup()内部,可以访问作用域中声明的响应式状态和函数以及传递给setup()的参数
import { h, reactive } from 'vue'

export default {
  setup(props, { slots, attrs, emit }) {
    const state = reactive({
      count: 0
    })

    function increment() {
      state.count++
    }

    // 返回render函数
    return () =>
      h(
        'div',
        {
          onClick: increment
        },
        state.count
      )
  }
}

slot统一

vue2.x
在内容节点上定义slot data property

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

// 引用时
this.$scopedSlots.header

vue3.x

  • 插槽被定义为当前节点的子对象
h(LayoutComponent,{},{
  header:()=>h('div',this.header),
  content:()=>h('div',this.content)
})
  • 当需要以编程方式引用作用域slot时,被统一到$slot选项中
this.$slots.header

过渡类名更改

vue3.x
过渡类名 v-enter修改为 v-enter-from、过渡类名v-leave修改为v-leave-from

组件相关属性名也发生了变化:
leave-class已经被重命名为leave-from-class(在渲染函数或 JSX 中可以写为:leaveFromClass)
enter-class已经被重命名为enter-from-class (在渲染函数或 JSX 中可以写为:enterFromClass)

v-model

vue2.x

  • 使用v-model指令必须使用valueprop;

  • 如果出于不同的目的使用其他的prop,需要使用v-bind.sync

vue3.x

  • 如果要改变绑定的属性名,而不是更改组件内绑定的选项,只需要给 v-model 传递一个参数就可以了;

  • 可以自定义多个v-model;

  • 支持自定义修饰符

v-if与v-for的优先级比较

vue2.x
在同一个元素上同时使用v-ifv-for,v-for会优先使用;

vue3.x
v-if总是优先于v-for生效

v-bind合并行为

vue2.x
如果一个元素同时定义了v-bind="object"和一个相同的单独的property,那么这个单独的 property总是会覆盖object中的绑定

// 结果

vue3.x
如果一个元素同时定义了v-bind="object"和一个相同的单独的property,那么声明绑定的顺序决定了它们如何合并

// 结果
// 结果

函数式组件

vue2.x

  • 作为性能优化
  • 返回多个根节点
export default {
  functional: true,
  props: ['level'],
  render(h, { props, data, children }) {
    return h(`h${props.level}`, data, children)
  }
}

vue3.x

  • 通过函数创建组件
    所有的函数式组件都是用普通函数创建的,不需要定义{function:true}组件选项
import { h } from 'vue'

const DynamicHeading = (props, context) => {
  return h(`h${props.level}`, context.attrs, context.slots)
}

DynamicHeading.props = ['level']

export default DynamicHeading
  • 单文件组件



function attribute 在