AICon议程上新60%,阿里国际、360智脑、科大讯飞、蔚来汽车分享大模型探索与实践 了解详情
写点什么

Vue 3 中那些激动人心的新功能

  • 2019-10-30
  • 本文字数:5265 字

    阅读完需:约 17 分钟

Vue 3中那些激动人心的新功能

在近期我们发布的 《Vue 3 最值得期待的五项重大更新》 中,我们了解了 Vue 3 将带来的性能提升,知道了使用新版 Vue 编写的应用程序在性能表现上会很出色。但性能并不是最重要的部分,对于开发人员而言,最重要的是新版本将如何影响我们编写代码的方式。如你所料,Vue 3 带来了许多激动人心的新功能。值得庆幸的是,Vue 团队主要针对当前 API 做了添加和改进,没有引入多少重大更改。因此,熟悉 Vue 2 的开发人员应该可以很快习惯新语法。


让我们先从大多数人可能都听说过的 API 开始……

合成 API

合成(Composition)API 是 Vue 的新大版本中讨论最多的特色语法。这是一种全新的逻辑复用和代码组织方法。


目前,我们使用所谓 Options API 构建组件。要将逻辑添加到 Vue 组件中,我们要填充(option)诸如 data、methods、computed 之类的属性。这种方法的最大缺点是它本身并非有效的 JavaScript 代码。你需要准确了解模板中可以访问哪些属性,以及 this 关键字的行为。在后台,Vue 编译器需要将属性转换为可用的代码。因此我们无法享受自动提示或类型检查功能的帮助。


合成 API 会将组件属性中当前可用的机制暴露为 JavaScript 函数,从而解决这个问题。Vue 核心团队将合成 API 定义为“一组基于函数的附加 API,可以灵活地组合组件逻辑”。用合成 API 编写的代码更具可读性,也没有在幕后隐藏什么魔法,这使它更易于阅读和学习。


下面来看一个使用了新的合成 API 的组件示例,从中了解其工作机制。


<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() {
复制代码


简单说,它只是一个将属性和函数返回到模板的函数,仅此而已。我们在这里声明所有响应属性、计算属性、观察者和生命周期 hooks,然后返回它们,以便在模板中使用。


我们没有从 setup 函数返回的内容将无法在模板中使用。


const count = ref(0)
复制代码


根据上述内容,我们使用 ref 函数声明了称为 count 的响应属性。它可以包装任何原语或对象,并返回其响应性引用。传递的元素的值将保留在所创建引用的 value 属性中。例如,如果要访问 count 引用的值,则需要显式请求 count.-value。


const double = computed(() => count.value * 2)
function increment() { count.value++}
复制代码


这正是我们在声明计算属性 double 和 increment 函数时所做的事情。


onMounted(() => console.log('component mounted!'))
复制代码


我们使用 onMounted hook 在组件挂载时记录了一些消息,这里只是告诉你可以这样做😉


return {  count,  double,  increment}
复制代码


最后,我们使用 increment 方法返回 count 和 double 属性,以使其在模板中可用。


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


好了!现在,我们可以访问模板中 setup 方法返回的属性和函数,就像通过旧的 Options API 声明它们一样。


这是一个简单的示例,也可以通过 Options API 轻松实现。新的合成 API 的真正好处不仅仅是以不同的方式编码,在复用我们的代码 / 逻辑时,其优势就能显现出来。

使用合成 API 复用代码

新的合成 API 有更多优势。想想代码复用,这就是它的一大用武之地。目前,如果我们要在其他组件之间共享一些代码,则有两个选择可用——分别是 mixins 和作用域插槽。两者都有自己的缺点。


假设我们要提取 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 背后的理念,并给出了最佳用例的建议。Vue 核心团队的 ThorstenLü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 对象,则可能会以意想不到的方式(尤其是全局 mixins)影响你的应用程序,而 Vue 3 则不会出现这种情况。


这一 API 更改现在正在这个 RFC 中讨论,意味着将来它可能还会继续变动。

片段

Vue 3 中值得期待的另一个激动人心的新功能是片段(Fragments)。


你可能会问什么是片段?嗯,如果你创建一个 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>    );  }}
复制代码


尽管片段看起来像是普通的 DOM 元素,但它是虚拟的,根本不会在 DOM 树中渲染。这样我们可以将组件功能绑定到单个元素中,而无需创建多余的 DOM 节点。


目前,你可以在 Vue 2 中使用 vue-fragments 库来应用片段,而在 Vue 3 中它是开箱即用的!

Suspense

React 生态系统中还有一个好主意也将在 Vue 3 中采用,就是 Suspense 组件。


Suspense 会暂停你的组件渲染,并渲染回退组件,直到满足一个条件为止。在 Vue London Evan 期间,Vue 团队简单介绍了这个主题,并向我们展示了值得期待的 API。到头来 Suspense 只是一个具有插槽的组件:


<Suspense>  <template >    <Suspended-component />  </template>  <template #fallback>    Loading...  </template></Suspense>
复制代码


在 Suspended-component 完全渲染之前将显示回退组件。Suspense 可以等待组件下载完毕(如果该组件是异步的),或者在 setup 函数中执行一些异步操作。

多个 v-model

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。下面这个示例中,你可以在一个表单组件中找到两个 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,我们需要指定其目标位置,在其中渲染 portal 内容。下面是 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"> <!-- 这个组件可以放在你应用中的任何位置上 上面 portal 组件的插槽内容会在这里渲染。 --></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 兼容构建也应该能轻松覆盖。


这一 API 更改现在正在这个 RFC 中讨论,意味着将来它可能还会继续变动。

小结

除了合成 API(它是 Vue 3 中最重要的新 API)之外,我们还能在新版中找到很多较小的改进。我们可以看到,Vue 正在向更好的开发人员体验和更简单、更直观的 API 的目标前进。我们也很高兴看到 Vue 团队决定采用目前由第三方库带来的许多理念,将它们引入核心框架。


上面的内容只介绍了主要的 API 更改和改进。如果你对其他内容感到好奇,请务必查看 Vue RFCs 存储库


作者介绍:


Filip Rakowski 是一名 Web 开发人员,对最新的 Web 技术充满热情,并且特别喜欢 Vue 和渐进式 Web 应用。他的日常工作是开发开源产品、开发程序员接口以及与社区互动。他是 Vue Storefront 的联合创始人、StorefrontUI 的作者、Vue.js 官方社区合作伙伴和 VuePress 核心团队成员。


原文链接:


https://vueschool.io/articles/vuejs-tutorials/exciting-new-features-in-vue-3/


2019-10-30 20:085349
用户头像
王文婧 InfoQ编辑

发布了 126 篇内容, 共 72.6 次阅读, 收获喜欢 275 次。

关注

评论

发布
暂无评论
发现更多内容

超全激活函数学习总结!!!

Shirakawa

神经网络 机器学习 算法 激活函数

Druid 使用 Kafka 数据加载教程——下载和启动 Kafka

HoneyMoose

浅谈限流组件的应用和设计原则

xiaoxi666

redis sentinel 分布式限流 redisson redis-cell

在明道云上搭建的应用维护管理的几点建议

明道云

我受够WIN10了!!!

Jackpop

有状态流处理简介(一)

Databri_AI

flink 批处理 状态

【设计模式】桥接模式

Andy阿辉

编程 后端 设计模式 8月日更

Druid 集群方式部署 —— 配置 Zookeeper 连接

HoneyMoose

docker编排参数详解(docker-compose.yml配置文件编写)

xcbeyond

Docker 容器 8月日更

在线邮箱地址提取工具

入门小站

工具

两个小女孩

箭上有毒

8月日更

明道实施与需求的耦合

明道云

前端之数据结构(三)集合和字典

Augus

数据结构 8月日更

JavaScript代码片段学设计模式

devpoint

设计模式 工厂模式 8月日更

元数据管理服务分析报告

漫长的白日梦

数据湖 AWS 元数据

架构训练营 模块4作业

sophiahuxh

【前端 · 面试 】HTTP 总结(六)—— HTTP 版本区别

编程三昧

面试 HTTP 8月日更 http版本

Druid 集群方式部署 —— 配置调整

HoneyMoose

Druid 集群方式部署 —— 启动服务

HoneyMoose

字节跳动旗下大力教育大批量裁员,赔偿 n+2

hanaper

Linux中buff-cache占用过高解决方案

入门小站

Linux

分布式认知工业互联网平台如何赋能企业数字化转型?

CECBC

spring的循环依赖

卢卡多多

spring aop 8月日更

Pandas系列_DataFrame数据筛选(上)

Peter

Python 数据分析 pandas

深入了解NIO底层原理

陈皮的JavaLib

Java 面试 nio 8月日更

yyds,Win10真香!!!

Jackpop

linux中常见工具安装问题集锦

liuzhen007

8月日更

☕【Java技术指南】教你如何使用【精巧好用】的DelayQueue(延时队列)

洛神灬殇

Java 延迟队列 8月日更 DelayedQueue

Druid 集群方式部署 —— 端口调整

HoneyMoose

DataFrame数据创建:10种方式任你选

Peter

Python 数据分析 pandas

网络攻防学习笔记 Day97

穿过生命散发芬芳

态势感知 网络攻防 8月日更

Vue 3中那些激动人心的新功能_大前端_Filip Rakowski_InfoQ精选文章