你不知道的vue3 ♥

729 阅读1分钟

dynamic-directive-arguments

由于指令参数是静态的,因此当前用户将不得不想把属性以及方法用动态的体现

<!-- v-bind with dynamic key -->
<div v-bind:[key]="value"></div>

<!-- v-bind shorthand with dynamic key -->
<div :[key]="value"></div>

<!-- v-on with dynamic event -->
<div v-on:[event]="handler"></div>

<!-- v-on shorthand with dynamic event -->
<div @[event]="handler"></div>

<!-- v-slot with dynamic name -->
<foo>
  <template v-slot:[name]>
    Hello
  </template>
</foo>

<!-- v-slot shorthand with dynamic name -->
<!-- pending #3 -->
<foo>
  <template #[name]>
    Default slot
  </template>
</foo>

treeshaking

随着vue api的增长,官方一直在努力平衡功能以及减少捆绑包的大小。官方希望将Vue的大小保持在最小,但由于大小限制又不想限制其功能。

通过ES模块的静态分析的友好设置,treeshaking可以只编译打包的使用的API

import Vue, { nextTick, observable } from 'vue'

Vue.nextTick // undefined

nextTick(() => {})

const obj = observable({})

teleport

在2.x之前如果你需要创建全局提示组件,像iview、element-ui那样使用js调用 且 挂在到body节点下,大概的思路是需要这样去做的

Alert.newInstance = properties => {
  const props = properties || {};

  const Instance = new Vue({
    data: props,
    render (h) {
      return h(Alert, {
        props: props
      });
    }
  });

  const component = Instance.$mount();
  document.body.appendChild(component.$el);

  const alert = Instance.$children[0];

alert.vue 会被 Webpack 的 vue-loader 编译,把 template 编译为 Render 函数,最终就会成为一个 JS 对象,自然可以对它进行扩展

vue3中的telport

<body>
  <div id="app">
    <h1>Move the #content with the portal component</h1>
    <teleport to="#endofbody">
      <div id="content">
        <p>
          this will be moved to #endofbody.<br />
          Pretend that it's a modal
        </p>
        <Child />
      </div>
    </teleport>
  </div>
  <div id="endofbody"></div>
  <script>
    new Vue({
      el: "#app",
      components: {
        Child: { template: "<div>Placeholder</div>" }
      }
    });
  </script>
</body>

暴露了teleport的内置组件可以配置需要挂在的html元素

composition-api

曾经在使用vue2.x的Option-api的时候,相信同学在维护200行的.vue组件时,新增或者修改一个需求,就需要在data,methods,computed里修改,反复横跳。当时可以用mixin混入的方式,但需要注意命名冲突以及数据来源完全模糊,来看一下composition-api 3.x发布后的例子

<template>
  <button @click="increment">
    Count is: {{ state.count }}, double is: {{ state.double }}
  </button>
</template>

<script>
import { reactive, computed } from 'vue'

export default {
  setup() {
    const state = reactive({
      count: 0,
      double: computed(() => state.count * 2)
    })

    function increment() {
      state.count++
    }

    return {
      state,
      increment
    }
  }
}
</script>

可以更好来拆分业务逻辑,只关注每一部分拆分出来的js文件以及暴露出来的方法以及变量,在setup中return即可

async-component-api

在vue2.x的时候你要确保组件的异步加载,减少首次的白屏,常常会这么做code splitting

{    path: '/login',    component: () => import( /* webpackChunkName: "login" */ '@/views/login/index'),    hidden: true  },

在vue3的时候可能需要你这样优雅了

import { defineAsyncComponent } from "vue"

// simple usage
const AsyncFoo = defineAsyncComponent(() => import("./Foo.vue"))

// with options
const AsyncFooWithOptions = defineAsyncComponent({
  loader: () => import("./Foo.vue"),
  loadingComponent: LoadingComponent,
  errorComponent: ErrorComponent,
  delay: 200,
  timeout: 3000
})

global-api-change

before

import Vue from 'vue'
import App from './App.vue'

Vue.config.ignoredElements = [/^app-/]
Vue.use(/* ... */)
Vue.mixin(/* ... */)
Vue.component(/* ... */)
Vue.directive(/* ... */)

Vue.prototype.customProperty = () => {}

new Vue({
  render: h => h(App)
}).$mount('#app')

after

import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

app.config.isCustomElement = tag => tag.startsWith('app-')
app.use(/* ... */)
app.mixin(/* ... */)
app.component(/* ... */)
app.directive(/* ... */)

app.config.globalProperties.customProperty = () => {}

app.mount(App, '#app')

从技术上讲,Vue 2没有“应用”的概念。我们定义为应用的只是通过创建的根Vue实例 new Vue() 从同一Vue构造函数创建的每个跟实例都共享相同的全局配置,2.x时如果项目里存在两个Vue实例内比如prototype扩展的函数方法是共享的 。createApp提供config.globalProperties来帮助你对Vue的扩展

v-model-api-change

之前当你使用 v-model=“foo”时vue会帮你编译成

h(Comp, {
  value: foo,
  onInput: value => {
    foo = value
  }
})

在3.0后会编译成这样

h(Comp, {
  modelValue: foo,
  'onUpdate:modelValue': value => (foo = value)
})

写法会变成这样,支持多个v-model

<InviteeForm
  v-model:name="inviteeName"
  v-model:email="inviteeEmail"
/>

end

这些仅仅时vue3.0的冰山一角,更多的变化比如一些runtime时的优化,对diff的优化等等,本篇文章只是本人对vue3.0的部分见解,欢迎大家指出错误。