在前面的文章中咱们也聊了许多 Vue3.x 相比 Vue2.x 有哪些变化,也介绍了一些它的特色,今天就重点介绍下它新增的这些特性。javascript
本文主要参考:vueschool.io/articles/vu…html
总览
- Compostion API 合成API
- 取消
Vue
全局变量 - 自定义指令 Directives API调整
- Component 组件支持
v-model
指令 - Fragments Template 支持有多个根节点
- Suspense Template Fallback 组件
- Teleport Template Dom占位传递组件
1 Compostion API 合成API
上一篇文章已经重点介绍过了。vue
查看:juejin.im/post/5e8010…java
2 取消 Vue 全局变量
Vue2.x 中的代码片断:node
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') 复制代码
Vue3.x 中取消了全局变量 Vue
,改成实例函数 createApp()
建立实例对象。git
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') 复制代码
RFC查看网友讨论:github.com/vuejs/rfcs/…github
这个变化很大,将会给咱们从 Vue2.x 升级到 Vue3.x 带来不小的工做量。编程
为何这么改变?其实也好理解,Vue3.x 基于函数式编程,因此:一切皆函数。 为了保证每一个函数都有本身的小 圈子
能独立运行,因此从源头上就开始 开刀
。api
3 自定义指令 Directives API调整
Vue2.x 中自定义一个指令:app
const MyDirective = { bind(el, binding, vnode, prevVnode) {}, inserted() {}, update() {}, componentUpdated() {}, unbind() {} } 复制代码
Vue3.x 中变成了:
const MyDirective = { beforeMount(el, binding, vnode, prevVnode) {}, mounted() {}, beforeUpdate() {}, updated() {}, beforeUnmount() {}, // new unmounted() {} } 复制代码
这能够算上是一个 breaking change
了,主要是在 Vue3.x 中生命周期函数的变化致使的。
查看上一篇文章,了解 Vue3.x 的生命周期有哪些变化:juejin.im/post/5e8010…
RFC看这里:github.com/vuejs/rfcs/…
4 Component 组件支持 v-model 指令
Vue2.x 中咱们会把 v-model
用在一些表单元素上,用于数据的双向绑定。
<input v-model="property /> 复制代码
可是,若是咱们但愿父子组件也能双向绑定时,Vue2.x 是不建议的,由于这会给父组件的维护带来灾难!
因此在 Vue2.x 中建议使用 this.$emit()
事件回传机制明确通知父组件,真正的更新仍是父组件本身实现。
后来为了简化上述操做,在 Vue2.3.0
新增了 .sync
修饰符。
好比:父组件调用子组件 text-document
时,子组件就能够修改父组件的 doc.title
。
<text-document v-bind:title.sync="doc.title"></text-document> 复制代码
好了,经过以上描述咱们能够得出结论:
v-model
能够实现表单元素的数据双向绑定v-bind:xxx.sync
或者简写为:xxx.sync
能够实现父子组件的双向绑定
那么在 Vue3.x 中获得了统一:
:xxx.sync
将被 v-model:xxx
取代
若是你但愿跟子组件直接双向绑定,则:
<text-document v-model="doc"></text-document> 复制代码
或者多个属性之间一一绑定:
<text-document v-model:title="doc.title" v-model:content="doc.content" ></text-document> 复制代码
RFC查看网友讨论:github.com/vuejs/rfcs/…
5 Fragments Template 支持有多个根节点
Vue2.x 中 Template 模板你一般是这么写的:
<template> <div> <p>Hello</p> <p>Vue2.x</p> </div> </template> 复制代码
在 template
中只能有惟一一个根节点。缘由就是每一个 Vue 的实例只容许绑定到惟一的Dom树上。
若是你但愿绑定两个Dom,那么你就只能在新建一个 Vue 示例,可是这样就跟当前系统脱节了,没啥意义了。
或者使用这个插件:vue-fragments,相似于 React 中的 <React.Fragment>
。
<template> <v-fragment> <div>Fragment 1</div> <div>Fragment 2</div> </v-fragment> </template> 复制代码
可是在 Vue3.x 中,就能够不用惟一根节点,也不用插件了,变得简单了许多:
<template> <p>Hello</p> <p>Vue3.x</p> </template> 复制代码
6 Suspense Template Fallback 组件
Vue2.x 中你应该会常常遇到这种场景:
<template> <div> <div v-if="!loading"> ... </div> <div v-if="loading">Loading...</div> </div> </template> 复制代码
或者安装这个插件:vue-async-manager
而后,就变成了:
<template> <div> <Suspense> <div> ... </div> <div slot="fallback">Loading...</div> </Suspense> </div> </template> 复制代码
Vue3.x 感受就是参考了上面这个组件的作法,如今能够这么写:
<Suspense> <template #default> ... </template> <template #fallback> Loading... </template> </Suspense> 复制代码
#fallback
其实在 Vue3.x 中就是 slot
的简写。因此,#default
能够省略。
固然 React 也有 Suspense
组件解决相似的问题。
其实,这个全局组件可能更多的会配合异步组件使用。顺便说下,在 Vue3.x 中,定义一个异步组件使用:defineAsyncComponent
。
7 Teleport Template Dom占位传递组件
注意: teleport
是 3.0.0-alpha.11
刚改的名字,以前叫:portal
, 查看 CHANGELOG
Vue2.x 中你应该会常常遇到这种场景:
<!-- UserCard.vue --> <template> <div class="user-card"> <b> {{ user.name }} </b> <button @click="isPopUpOpen = true">删除用户</button> <!-- 注意这一块代码 --> <div v-show="isPopUpOpen"> <p>肯定删除?</p> <button @click="removeUser">肯定</button> <button @click="isPopUpOpen = false">取消</button> </div> </div> </template> 复制代码
以上代码就是当咱们须要作一个弹窗的时候,按照业务逻辑,弹窗和其余代码在一块写着。 可是这么写每每会出现问题,就是一旦咱们点击 “删除用户”,本但愿弹窗显示,可是每每这个弹出框被外边的元素挡住!因为 z-index
的缘由。
那咱们就千方百计让这个弹出框直接挂在到 body
节点上,这样就没问题了。好比:能够经过 Javascript 追加这个弹出框到 body 中等,处理起来比较麻烦。
关键的问题是:业务逻辑被打断了,代码也不连贯了。
因此为了解决这个烦恼,Vue3新增了这个组件,如今你就能够这么写了。
在最外层的 App.vue
中:
<!-- 预留一块空地,专门用来显示这个容易被遮挡的层 --> <div id="modal-container"></div> <!-- app --> <div id="app"> 复制代码
在你本身的组件中:
<!-- UserCard.vue --> <template> <div class="user-card"> <b> {{ user.name }} </b> <button @click="isPopUpOpen = true">删除用户</button> <!-- 注意这一块代码 --> <Teleport to="#modal-container"> <div v-show="isPopUpOpen"> <p>肯定删除?</p> <button @click="removeUser">肯定</button> <button @click="isPopUpOpen = false">取消</button> </div> </Teleport> </div> </template> 复制代码
这样技能保证你代码的完整性、业务的连贯性,又能解决弹窗口被遮挡的问题。
另外,今天在 3.0.0-alpha.11
CHANGELOG 中又看到一条更新记录 16cd8ee:
portal: portal should always remove its children when unmounted (16cd8ee)
就是说一旦组件被销毁 unmounted
,Teleport 里面的元素应该被清空。若是不自动清空掉,随着你页面的切换,前一次页面遗留的弹窗可能一直存在的bug。
最后
本文也是 VUE 3.0 学习探索入门系列
里面的最后一篇,但愿能对你们入门 Vue3 有所帮助。
接下来就等 Vue3.0 正式 Release 之后,再带给你们 VUE 3.0 实战上手篇
系列,欢迎你们关注我,及时了解动态。
本系列历时20天完成,再次感谢你们。
(全剧终)