vue3优化点

298 阅读2分钟

这里总结一些3.x的变更,方便查阅

1. 全局Api相关

(1) 全局api改为使用应用程序实例

import { createApp } from 'vue'

const app = createApp({})

Vue.config  ->  app.config
Vue.use()   ->  app.use     // 注册插件

一些更改点:

  • Vue.config.productionTip 移除
  • Vue.extend 移除
// 构造器相关
//  vue2.x
const Profile = Vue.extend({   // 生成组件构造器
  template: 'xxx',
  data() {
    return {}
  }
})
new Profile().$mount(dom)

//  vue3.x, 移除组件构造器,改用createApp挂载
const Profile = {
  template: 'xxxxx',
  data() {
    return {}
  }
}
Vue.createApp(Profile).mount(dom)
-----------------------------------------------------------------------------------------------
//  继承相关
3.x 使用 组合式 API 来替代继承与 mixin, 或使用extends选项替代Vue.extend

(2) 全局Api tree-shaking

全局 API(不全局改变行为)以及一些内部api 使用具名导出, 有利于支持tree-shaking的打包工具打包时去除未使用的全局api, 从而获得最佳的文件大小, 提升性能

受影响的api

  • 全局Api: Vue.nextTick() -> import { nextTick } from 'vue' Vue.set()
  • 内部Api: transition、v-model等标签或者指令

注意: 这仅适用于es module构建版本(ESM 格式被设计为可以被静态分析,所以打包工具可以利用这一点来进行“tree-shaking”并将用不到的代码排除出最终的包)

2. 模板指令

(1) v-model

3.x

  • v-model参数指定
  • v-model替换.sync
  • 绑定多个v-model
  • 自定义v-model修饰符
// 2.x
// v-model 定制prop,event
// ChildComponent.vue
export default {
  model: {
    prop: 'title',
    event: 'change'
  },
  props: {
    title: {
      type: String,
    }
  }
}
<ChildComponent v-model="pageTitle" /> 等价于
<ChildComponent :title="pageTitle" @change="pageTitle = $event" />

// .sync实现双向绑定
// ChildComponent.vue
this.$emit('update:title', newValue)
<ChildComponent :title.sync="pageTitle" /> 等价于
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />

----------------------------------------------------------------------------

// 3.x
// 指定v-model参数
<ChildComponent v-model:title="pageTitle" />  等价于
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event"/>

(2) v-for, key

3.x

  • <template v-for> 的 key 应该设置在 <template> 标签上
  • 条件分支不需要使用key
  • v-if优先级高于v-for

(3).native修饰符

3.x 移除.native修饰符

// 2.x
<my-component
  v-on:close="handleComponentEvent"
  v-on:click.native="handleNativeClickEvent"   // 监听原生事件
/>

// 3.x, 未被定义为子组件触发的事件,将被作为子组件的根元素的原生事件监听
<my-component
  v-on:close="handleComponentEvent"
  v-on:click="handleNativeClickEvent"
/>
// MyComponent
<script setup>
   const emit = defineEmits(['close'])
</script>

3. 组件

(1) 函数式组件

3.x

  • 只能由普通函数创建
  • 移除functional选项
// 2.x
export default {
  functional: true,
  props: ['level'],
  render(h, { props, data, children }) {
    return h(`h${props.level}`, data, children)
  }
}

// 3.x
import { h } from 'vue'
const DynamicHeading = (props, context) => {
  return h(`h${props.level}`, context.attrs, context.slots)
}
DynamicHeading.props = ['level']
export default DynamicHeading

(2) 异步组件 使用defineAsyncComponent定义

// 2.x
const asyncModal = {
  component: () => import('./Modal.vue'),
  delay: 200,
  timeout: 3000,
  error: ErrorComponent,
  loading: LoadingComponent
}

// 3.x
const asyncModalWithOptions = defineAsyncComponent({
  loader: () => import('./Modal.vue'),
  delay: 200,
  timeout: 3000,
  errorComponent: ErrorComponent,
  loadingComponent: LoadingComponent
})

(3) 在emits中声明组件需要触发的事件

由于3.x移除.native修饰符, 未被定义为子组件触发的事件,将被作为原生事件进行监听

(4) 高阶组件相关的api

3.x

  • $attrs包括style、class
  • listeners移除,被整合到listeners移除,被整合到attrs中(事件监听器被当作以on开头的属性处理)

2.x中,通过 this.$attrs 访问传递给组件的 attribute,以及通过 this.$listeners 访问传递给组件的事件监听器。结合 inheritAttrs: false,开发者可以将这些 attribute 和监听器应用到根元素之外的其它元素

(5) 渲染函数

3.x

  • render函数不接受参数,原有的h参数现改为全局引入
  • 使用resolveComponent 取代字符串查找已注册的组件
// 2.x
export default {
  render(h) {  // h为createElement别名
    return h('div')
  }
}

//3.x
import { h } from 'vue'
export default {
  render() {
    return h('div')
  }
}

// 3.x
import { h, resolveComponent } from 'vue'

export default {
  setup() {
    const ButtonCounter = resolveComponent('button-counter')
    return () => h(ButtonCounter)
  }
}

  • Vnode-props结构扁平化: h(type, props, children)
// 2.x
{
  staticClass: 'button',
  class: { 'is-outlined': isOutlined },
  staticStyle: { color: '#34495E' },
  style: { backgroundColor: buttonColor },
  attrs: { id: 'submit' },
  domProps: { innerHTML: '' },
  on: { click: submitForm },
  key: 'submit-button'
}

// 3.x, 扁平化,无需区分Dom-property、html属性、事件等
{
  class: ['button', { 'is-outlined': isOutlined }],
  style: [{ color: '#34495E' }, { backgroundColor: buttonColor }],
  id: 'submit',
  innerHTML: '',
  onClick: submitForm,
  key: 'submit-button'
}

4. 其他

(1) is attribute

限制在只能用于component标签中

// 2.x
// 使用is绕开html解析限制,被解析为<blog-post-row>组件
<table>
  <tr is="blog-post-row"></tr>
</table>

// 3.x
<table>
  <tr is="vue:blog-post-row"></tr>
</table>

(2) 自定义指令的钩子函数与组件得声明周期一致

const MyDirective = {
  created(el, binding, vnode, prevVnode) {}, // 新增
  beforeMount() {},   // 替换bind
  mounted() {},  // 替换inserted
  beforeUpdate() {}, // 新增
  updated() {},   // 替换componentUpdated
  beforeUnmount() {}, // 新增
  unmounted() {}
}

(3) 对于data选项进行合并时(来自mixin/extend)使用浅合并

const Mixin = {
  data() {
    return {
      user: {
        name: 'Jack',
        id: 1
      }
    }
  }
}

const CompA = {
  mixins: [Mixin],
  data() {
    return {
      // 2.x:  { user: { name: 'jack', id: 2} }
      // 3.x:  { user: { id: 2 } }
      user: {
        id: 2
      }
    }
  }
}

(4) watch监听数组

3.x 监听数组变更必须使用deep

data() {
	arr: [1, 2, 3, 4],
}
// 2.x
watch: {
	arr: {
		handler() {xxx},
		// deep: true,  //监听对象, 数组不适用
	},
},

arr = [1,2,3]   // 数组替换, 触发
this.$set(arr, 0, 4)   // 数组变更,触发,等价于arr.splice(0, 1, 4)

// 3.x
created() {
	this.$watch('arr', () => { xxx }, { deep: true });
}
arr[1] = 4   // 数组变更

// 3.x setup语法糖
const arr = ref([])
watch(() => arr.value, () => { xxx }, { deep: true })
// 等价于
watch(arr, () => { xxx }, { deep: true })

5.移除的api

(1)过滤器

  • 局部过滤器使用方法或者计算属性替代
  • 全局过滤器使用全局属性替代
const app = createApp(App)

app.config.globalProperties.$filters = {
  currencyUSD: (value) => '$' + value,
}

<template>
  <p>{{ $filters.currencyUSD(xxxx) }}</p>
</template>

(2) Vue.set/$set, Vue.delete/$delete

(3) 移除$on,$off,$once 事件接口, 移除$children(使用$refs)

(4) 移除propsData

// 2.x
const Comp = Vue.extend({
  props: ['username'],
  template: '<div>{{ username }}</div>'
})

new Comp({
  propsData: {
    username: 'Evan'
  }
})

// 3.x
const app = createApp(
  {
    props: ['username'],
    template: '<div>{{ username }}</div>'
  },
  { username: 'Evan' }
)

6. 生命周期钩子

beforeCreate, created去除

Vue2Vue3 setup
beforeMountonBeforeMount
beforeUpdateonBeforeUpdate

7. 其他的一些性能优化点

  • 虚拟dom重写
  • 编译优化
  • proxy响应式
  • 独立的响应化模块, 自定义渲染器

参考链接: webpack之tree-shakingvue属性透传