Vue 3 中使人兴奋的新功能

做者:Filip Rakowski

翻译:疯狂的技术宅html

原文:https://vueschool.io/articles...前端

未经容许严禁转载vue

在上一篇文章中,咱们了解了 Vue 3 将带来的性能改进。咱们已经知道,用新的 Vue 3 编写的程序效果会很好,但性能并非最重要的部分。对开发人员而言,最重要的是新版本将会怎样影响咱们编写代码的方式。node

如你所料,Vue 3 带来了许多使人兴奋的新功能。值得庆幸的是,Vue 团队主要介绍了对当前 API 的添加和改进,而不是重大更改,因此已经了解 Vue 2 的人们应该很快就会对新语法感到满意。react

Let's start with the API that most of you probably heard about...
让咱们从大多数人可能据说过的API开始...git

组件 API(Composition API)

组件 API 是 Vue 的下一个主要版本中最经常使用的讨论和特点语法。这是一种全新的逻辑重用和代码组织方法。程序员

当前,咱们使用所谓的 Options API 构建组件。为了向 Vue 组件添加逻辑,咱们填充(可选)属性,例如 datamethodscomputed等。这种方法的最大缺点是其自己并非有效的 JavaScript 代码。你须要确切地知道模板中能够访问哪些属性以及 this 关键字的行为。在后台,Vue 编译器须要将此属性转换为工做代码。所以咱们没法从自动建议或类型检查中受益。github

组件 API 旨在经过将组件属性中当前可用的机制公开为 JavaScript 函数来解决这个问题。 Vue 核心团队将组件 API 描述为 “一组基于函数的附加 API,能够灵活地组合组件逻辑。” 用组件 API 编写的代码更具备可读性,而且其背后没有任何魔力,所以更易于阅读和学习。面试

让咱们经过一个用了新的组件 API 的组件的简单示例,来了解其工做原理。json

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

<script>
import { ref, computed, onMounted } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const double = computed(() => count.value * 2)

    function increment() {
      count.value++
    }

    onMounted(() => console.log('component mounted!'))

    return {
      count,
      double,
      increment
    }
  }
}
</script>

如今,让咱们把代码分解为几部分,来了解发生了些什么:

import { ref, computed, onMounted } from 'vue'

正如我以前提到的,组件 API 将组件属性公开为函数,所以第一步是导入所需的函数。在例子中,须要使用 ref 建立响应性引用,用 computed 创建计算属性,并用 onMounted 访问安装的生命周期 hook。

如今你可能很想知道这神秘的 setup 方法究竟是什么?

export default {
  setup() {

简而言之,它只是一个将属性和函数返回到模板的函数而已。咱们在这里声明全部响应性属性、计算属性、观察者和生命周期 hook,而后将它们返回,以即可以在模板中使用它们。

咱们不从 setup 函数返回的内容在模板中将会不可用。

const count = ref(0)

根据上面的内容,咱们声明了带有 ref 函数的名为 count 的响应属性。它能够包装任何原语或对象并返回其响应性引用。传递的元素的值将会保留在所建立引用的 value 属性中。例如,若是你想访问 count 引用的值,则须要明确要求 count.value

const double = computed(() => count.value * 2)

function increment() {
  count.value++
}

这正是咱们在声明计算属性 doubleincrement 函数时所要作的。

onMounted(() => console.log('component mounted!'))

使用 onMounted hook,咱们会在安装组件时记录一些消息,只是向你展现能够作到!😉

return {
  count,
  double,
  increment
}

最后,咱们将使用 increment 方法返回 countdouble 属性,以使它们在模板中可用。

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

瞧!如今咱们能够访问模板中 setup 方法返回的属性和函数,就像经过旧的 Options API 声明它们同样。

这是一个简单的例子,也能够经过 Options API 轻松实现。新的组件 API 的真正好处不只在于能以不一样的方式进行编码,在对咱们的代码和逻辑进行重用时,这些好处也能显示出来。

用组件 API 进行代码重用

新的组件 API 具备更多优势。考虑一下代码重用。目前若是咱们要在其余组件之间共享一些代码,则有两个可用的选择:mixins 和做用域插槽( scoped slots)。可是二者都有缺点。

假设咱们要提取 counter 中的功能并在其余组件中重用。在下面,你能够看到如何将其与可用的 API 和新的组件 API 结合使用:

让咱们从 mixins 开始:

import CounterMixin from './mixins/counter'

export default {
  mixins: [CounterMixin]
}

mixins 的最大缺点在于咱们对它实际上添加到组件中的行为一无所知。这不只使代码变得难以理解,并且还可能致使名称与现有属性和函数发生冲突。

下面是做用域插槽:

<template>
  <Counter v-slot="{ count, increment }">
     {{ count }}
    <button @click="increment">Increment</button> 
  </Counter> 
</template>

经过使用做用域插槽,咱们确切地知道能够经过 v-slot 属性访问了哪些属性,所以代码更容易理解。这种方法的缺点是咱们只能在模板中访问它,而且只能在 Counter 组件做用域内使用。

如今该用组件 API 了:

function useCounter() {
  const count = ref(0)
  function increment () { count.value++ }

  return {
    count,
    incrememt
  }
}

export default {
  setup () {
    const { count, increment } = useCounter()
    return {
      count,
      increment
    }
  }
}

是否是更优雅?咱们不受模板和组件做用域的限制,而且可以确切地知道能够从 counter 访问哪些属性。另外咱们能够受益于编辑器中可用的代码补全功能,由于 useCounter 只是一个返回某些属性的函数,所以编辑器能够帮助咱们进行类型检查和建议。

这也是使用第三方库的更优雅的方式。例如,若是咱们想使用 Vuex,则能够显式地使用 useStore 函数,而不是污染 Vue 原型(this.$store)。这种方法也消除了 Vue 插件的幕后魔力。

const { commit, dispatch } = useStore()

若是你想了解有关组件 API 及其使用案例的更多信息,我强烈建议你阅读 Vue 团队的这篇文章,其中解释了新 API 背后的缘由,并提出了最好的用例建议。还有 great repository ,其中包含来自 Vue 核心团队的 Thorsten Lünborg 使用的组件 API 的例子。

全局挂载/配置 API 更改

咱们能够在实例化和配置程序的方式中找到另外一个重大变化。让咱们看看它如今是如何工做的:

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

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

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

当前,咱们正在用全局 Vue 对象提供全部配置并建立新的 Vue 实例。对 Vue 对象所作的任何更改都会影响每一个 Vue 实例和组件。

如今,让咱们看看它如何在 Vue 3 中运行:

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

const app = createApp(App)

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

app.mount('#app')

你可能已经注意到,每一个配置都限于使用 createApp 定义的某个 Vue 程序。

它可使你的代码更易于理解,而且不易出现由第三方插件引起的意外问题。目前,若是某些第三方解决方案正在修改 Vue 对象,那么它可能会以意想不到的方式(尤为是全局混合)影响你的程序,而 Vue 3 则没有这个问题。

目前,此 API 的更改正在 这个 RFC 中进行讨论,这意味着未来可能会有所更改。

片断(Fragments)

咱们能够在 Vue 3 中期待的另外一个激动人心的附加功能是片断。

你可能会问什么片断?好吧,若是你建立了一个 Vue 组件,那么它只能有一个根节点。

这意味着没法建立这样的组件:

<template>
  <div>Hello</div>
  <div>World</div>
</template>

缘由是表明任何 Vue 组件的 Vue 实例都须要绑定到单个 DOM 元素中。建立具备多个 DOM 节点的组件的惟一方法是建立一个没有基础 Vue 实例的功能组件。

事实证实,React 社区也有一样的问题。他们提出的解决方案是一个名为 Fragment 的虚拟元素。看上去是这样的;

class Columns extends React.Component {
  render() {
    return (
      <React.Fragment>
        <td>Hello</td>
        <td>World</td>
      </React.Fragment>
    );
  }
}

尽管 Fragment 看起来像是普通的 DOM 元素,但它是虚拟的,根本不会在 DOM 树中渲染。这样咱们就能够将组件功能绑定到单个元素中,而无需建立冗余的 DOM 节点。

如今你能够在带有 vue-fragments 库的 Vue 2 中使用片断,而在 Vue 3 中你能够直接使用它!

Suspense

将被用在 Vue 3 中的另外一个从 React 学来的功能是 Suspense 组件。

Suspense 可以暂停你的组件渲染,并渲染后备组件,直到条件知足为止。在 Vue London 期间,尤雨溪简短地谈到了这个主题,并向咱们展现了能够指望的 API。事实证实,Suspense 只是带有插槽的组件:

<Suspense>
  <template >
    <Suspended-component />
  </template>
  <template #fallback>
    Loading...
  </template>
</Suspense>

直到 Suspended-component 彻底渲染前将会显示后备内容。挂起能够等待,直到该组件被下载(若是该组件是异步组件的话),或者在 setup 函数中执行一些异步操做。

Multiple v-models

V-model 是一种指令,可用于在给定组件上实现双向绑定。咱们能够传递响应性属性,并从组件内部对其进行修改。

咱们能够从表单元素上很好的了解 v-model

<input v-bind="property />

可是你知道能够对每一个组件都使用 v-model 吗?在内部, v-model 只是传递 value 属性和侦听 input 事件的捷径。把上面的例子重写为如下语法,将具备彻底相同的效果:

<input 
  v-bind:value="property"
  v-on:input="property = $event.target.value"
/>

咱们甚至能够用组件 model 属性来更改默认属性和事件的名称:

model: {
  prop: 'checked',
  event: 'change'
}

如你所见,若是咱们想要在组件中进行双向绑定,v-model 指令多是一个很是有用的语法。不幸的是,每一个组件只能有一个 v-model

幸运的是,这在 Vue 3 中不会有问题!你将可以给 v-model 属性名,并根据须要拥有尽量多的属性名。在下面的例子中,你能够在表单组件中找到两个 v-model

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

目前,此 API 的更改已在这个 RFC 中进行讨论,这意味着未来可能会有更改。

Portals

Portals 是特殊的组件,用来在当前组件以外渲染某些内容。它也是在 React 中实现的功能之一。这就是 React 文档关于 Portals 的内容:

Portals 提供了一种独特的方法来将子级渲染到父组件的 DOM 层次结构以外的 DOM 节点中。

这种处理模式,是弹出式窗口以及一般显示在页面顶部的组件所使用的一种很是好的方法。经过使用 Portals,你能够确保没有任何主机组件 CSS 规则会影响你要显示的组件,而且能够避免用 z-index 进行的黑客攻击。

对于每一个 Portal,咱们须要为其指定目标位置,在该目标位置将渲染 Portals 内容。在下面,你能够从 portal-vue 库中看到实现,该库将此功能添加到了 Vue 2:

<portal to="destination">
  <p>This slot content will be rendered wherever thportal-target with name 'destination'
    is  located.</p>
</portal>

<portal-target name="destination">
  <!--
  This component can be located anywhere in your App.
  The slot content of the above portal component wilbe rendered here.
  -->
</portal-target>

Vue 3 对 Portals 提供开箱即用的支持!

新的自定义指令 API

自定义指令 API 在 Vue 3 中将略有变化,以便更好地与组件生命周期保持一致。这项改进应使 API 更加直观,从而使新手更容易理解和学习 API。

这是当前的自定义指令 API:

const MyDirective = {
  bind(el, binding, vnode, prevVnode) {},
  inserted() {},
  update() {},
  componentUpdated() {},
  unbind() {}
}

这是在 Vue 3 中的样子。

const MyDirective = {
  beforeMount(el, binding, vnode, prevVnode) {},
  mounted() {},
  beforeUpdate() {},
  updated() {},
  beforeUnmount() {}, // new
  unmounted() {}
}

即便这是一项重大改进,也应很容易被 Vue 兼容版本涵盖到。

在此 RFC 中讨论了这个 API 的更改,这意味着未来可能会改进。

摘要

除了 Composition API(它是 Vue 3 中最大的主要新 API)以外,咱们还能够找到不少较小的改进。能够看到 Vue 正在朝着更友好的开发体验和更简单、更直观的 API 迈进。十分高兴看到 Vue 团队决定在框架的核心采用了许多目前只能经过第三方库得到的想法。

上面的列表仅表示主要的 API 更改和改进。若是你对其余的内容感到好奇,请务必检查 Vue RFCs 信息库


本文首发微信公众号:前端先锋

欢迎扫描二维码关注公众号,天天都给你推送新鲜的前端技术文章

欢迎扫描二维码关注公众号,天天都给你推送新鲜的前端技术文章

欢迎继续阅读本专栏其它高赞文章:


相关文章
相关标签/搜索