引言
https://www.vue3js.cn/docs/zh/guide/installation.html
组合式 API?
- vue2 创建组件
1 | export default { |
用组件的选项 (data、computed、methods、watch) 组织逻辑在大多数情况下都有效。然而,当我们的组件变得更大时,逻辑关注点的列表也会增长。这可能会导致组件难以阅读和理解,尤其是对于那些一开始就没有编写这些组件的人来说。看上面的 demo 中 1 的逻辑与 2 交叉编写,很难一次找到一套功能的所有逻辑。在看下面这个组件事例:
一个大型组件的事例,逻辑关注的是所对应各个颜色,将每一个组件所对应的每一套逻辑梳理出来并且能够记住,这本就是我们不应该费很大精力去关注的,但是现在你必须得一个组件几百行,穿插找到属于你想要的那几行,如果我们能够将同一个逻辑的关注点放在一个地方会更好,而这个正是组合 api 使我们能够做到的。
- 组合式 api 基础 setup
setup 选项应该是一个接受 props 和 context 的函数,我们将在稍后讨论。此外,我们从 setup 返回的所有内容都将暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板。
先了解一下两个参数
- props
setup 函数中的第一个参数是 props。正如在一个标准组件中所期望的那样,setup 函数中的 props 是响应式的,当传入新的 prop 时,它将被更新。
如果需要解构 prop,可以通过使用 setup 函数中的 toRefs 来安全地完成此操作。直接使用 es6 进行解构,可能会造成消除 props 的响应性。
1 | import { toRefs } from 'vue' |
- context
传递给 setup 函数的第二个参数是 context。context 是一个普通的 JavaScript 对象,它暴露三个组件的 property:
1 | export default { |
attrs 和 slots 是有状态的对象,它们总是会随组件本身的更新而更新。这意味着你应该避免对它们进行解构,并始终以 attrs.x 或 slots.x 的方式引用 property。请注意,与 props 不同,attrs 和 slots 是非响应式的。如果你打算根据 attrs 或 slots 更改应用副作用,那么应该在 onUpdated 生命周期钩子中执行此操作。
在 setup() 内部,this 不会是该活跃实例的引用,因为 setup() 是在解析其它组件选项之前被调用的,所以 setup() 内部的 this 的行为与其它选项中的 this 完全不同。这在和其它选项式 API 一起使用 setup() 时可能会导致混淆。
现在用 setup 将上面的 demo 给重构一下
1 | import { reactive, watch, computed, onMounted, ref } from "vue"; |
可是我觉得这样写还是不太满意,setup 中好多代码,看着就比较乱,虽然逻辑在一块,但是逻辑堆的太多了可读性就不够了,所以我们可以使用 hooks 来设计组件,提高代码的复用性。可读性也比较好。那么既然要用 hooks 自然也是要遵守 hooks 协议,我们以 use 开头:
1 | // ./hooks.js |
这样一看,组件代码简洁,代码复用性比较高,我哪里用到这个功能,我引入 hooks,调用一波,节省了 cv 代码时间,而且耦合度比较低,那个功能出错了,我们只需要修改对应的 hooks,不用从上排查到下了。
生命周期
选项式 API | Hooks APi |
---|---|
beforeCreate | Not needed* |
created | Not needed* |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。如同我们上面例子的 onMounted 一样,钩子函数被组件调用会立即执行。
provide/inject
在 setup() 中使用 provide 时,我们首先从 vue 显式导入 provide 方法。这使我们能够调用 provide 时来定义每个 property。
provide 函数允许你通过两个参数定义 property:
property 的 name (
property 的 value
1 | import About from "./About"; |
添加响应 inject
在 setup() 中使用 inject 时,还需要从 vue 显式导入它。一旦我们这样做了,我们就可以调用它来定义如何将它暴露给我们的组件。
inject 函数有两个参数:
要注入的 property 的名称
一个默认的值 (可选)
1 | export default { |
修改响应式 property
当使用响应式提供/注入值时,建议尽可能,在提供者内保持响应式 property 的任何更改。
例如,在需要更改用户位置的情况下,我们最好在 使用 provide 祖先 组件中执行此操作。
1 | // 祖先组件 |
那么如何确保我们提供的属性不被后代组件所更改呢?建议对提供者的 property 使用 readonly。
1 | import { provide, ref, reactive, readonly } from "vue"; |
强行赋值会报错 TypeError: “userLocation” is read-only
Fragments! 在 Vue 3 中,组件现在正式支持多根节点组件,即片段!
- 2.x 语法
在 2.x 中,不支持多根组件,当用户意外创建多根组件时会发出警告,因此,为了修复此错误,许多组件被包装在一个
1 | <!-- Layout.vue --> |
- 3.x 语法
在 3.x 中,组件现在可以有多个根节点!但是,这确实要求开发者明确定义属性应该分布在哪里。
1 | <!-- Layout.vue --> |
过渡的 class 名更改
非兼容 #概览
过渡类名 v-enter 修改为 v-enter-from、过渡类名 v-leave 修改为 v-leave-from。
#2.x 语法
在 v2.1.8 版本之前, 为过渡指令提供了两个过渡类名对应初始和激活状态。
在 v2.1.8 版本中, 引入 v-enter-to 来定义 enter 或 leave 变换之间的过渡动画插帧, 为了向下兼容, 并没有变动 v-enter 类名:
.v-enter,
.v-leave-to {
opacity: 0;
}
.v-leave,
.v-enter-to {
opacity: 1;
}
这样做会带来很多困惑, 类似 enter 和 leave 含义过于宽泛并且没有遵循类名钩子的命名约定。
#3.x 语法
为了更加明确易读,我们现在将这些初始状态重命名为:
.v-enter-from,
.v-leave-to {
opacity: 0;
}
.v-leave-from,
.v-enter-to {
opacity: 1;
}
现在,这些状态之间的区别就清晰多了。
leave-class 已经被重命名为 leave-from-class (在渲染函数或 JSX 中可以写为:leaveFromClass)
enter-class 已经被重命名为 enter-from-class (在渲染函数或 JSX 中可以写为:enterFromClass
Teleport
Vue 鼓励我们将 UI 和 UI 的行为封装到组件中,通过嵌套组件来构建我们的 App。但是存在这样的一种情景,有多个子组件从逻辑上看是属于同一个父组件的,但是从技术实现的角度来看,多个子组件可能应挂载在 DOM 的不同位置,比较常见的情景是 Modal。在 Vue3 之前,我们可以参考下 Element UI 中 Poper 的实现。
1 | if (!popper || !reference) return; |
我们可以发现,是通过 document.body.appendChild 方法将元素挂载到 body 上的。而在 Vue3 中我们可以通过 Teleport 来实现这一操作。img 将会挂载至 body 下。
1 | <template> |
Teleport 传送的元素依旧还会受 Vue 控制,这能很好的利用 Vue 的特性,可以说 Teleport 出现的大大增强了组件的可复用性和封装性。
最后多嘴一句,这里的名称或许叫 Portal 会让人更好理解,毕竟 V 社的传送门大家应该都玩过吧。好像是为了避免与可能出现的
Emits Component Option
验证自定义事件
如果自定义事件是通过对象语法声明(Object syntax)而不是数组语法声明(Array Syntax)的,那么这个自定义事件可以像 prop 校验一样完成校验。
1 | app.component("custom-form", { |
v-modal
当我们在自定义组件上使用 v-modal 时,默认的 prop 和事件发生变化,
prop:value->modalValue
event: input->update:modalValue
v-bind.sync以及modal参数被移除,并以 v-modal arguments 的形式替换。vue3 中 v-modal 语法糖的形式变更,在 vue3 中的 v-modal 等价形式变更为如下形式。
1 | <ChildComponent v-model="pageTitle" /> |
可以通过 v-modal arguments 改变 modal 的名字
1 | <ChildComponent v-model:title="pageTitle" /> |
支持多个 v-modal 绑定
1 | <user-name |
支持创建自定义 v-modal 修饰符
1 | <my-component v-model.capitalize="bar"></my-component> |
1 | app.component("my-component", { |
数据响应式变化的原理
vue2 使用 Object.defineProperty 把这些 property 全部转为 getter/setter。而 Vue2 在处理数组时,也会通过原型链劫持会改变数组内元素的方法,并在原型链观察新增的元素,以及派发更新通知
在 Vue3 中响应式系统最大的区别就是,数据模型是被代理的 JavaScript 对象了。不论是我们在组件的 data 选项中返回一个普通的 JavaScript 对象,还是使用 composition api 创建一个 reactive 对象,Vue3 都会将该对象包裹在一个带有 get 和 set 处理程序的 Proxy 中。
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值等)。
具体原理慢慢研究。。。。。。。本次只是一些新 api 如何使用,具体怎么去使用达到最好的效果,还得看自己怎么去用吧
- Post link: https://blog.gaocaipeng.com/2019/10/01/qubleh/
- Copyright Notice: All articles in this blog are licensed under unless otherwise stated.