持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第23天,点击查看活动详情juejin.cn/post/714765…
插件
插件(plugins)是一种能为vue添加全局功能的工具代码。
利用use将插件与项目进行绑定和安装。
一个插件可以是一个拥有install()方法的对象,也可以直接是一个安装函数本身。安装函数会接收到安装它的应用实例和传递给app.use()的额外选项作为参数。
插件没有严格定义的使用范围,但是插件发挥作用的常见场景主要包括以下几种:
- 通过app.component()和app.directive()注册一到多个全局组件或自定义指令。
- 通过app.provide()使一个资源可被注入进整个应用。
- 向app.config.globalProperties中添加一些全局实例属性或方法。
- 一个可能上述三种都包含了的功能库(例如:vue-router)
编写一个插件
从设置插件对象开始,建议在一个单独的文件中创建并导出它,以保证更好的管理逻辑。
我们希望有一个翻译函数,这个函数接收一个以 ‘.’ 作为分隔符的key字符串。用来在用户提供的翻译字典中查找对应语言的文本,期望的使用方式如下:
这个函数应当能够在任意模板中被全局调用。这一点可以通过在插件中将它添加到app.config.globalProperties上来实现:
我们的$translate函数会接收一个例如greetings.hello的字符串,在用户提供的翻译字典中查找,并返回翻译得到的值。
用于查找的翻译字典对象则应当在插件被安装时作为app.use()的额外参数被传入:
这样,我们一开始的表达式$translate('greetings.hello')就会在运行时被替换为Bonjour了。
插件中的provide和inject
在插件中,我们可以通过provide来为插件用户供给一些内容。举例来说,我们可以将插件接收到的options参数提供给整个应用,让任何组件都能使用这个翻译字典对象。
vue2与vue3全局API的区别
全局API应用程序实例
vue2有许多全局API和配置,这些API和配置会全局改变vue的行为。例如,要注册全局组件,您可以像这样使用API:Vue.component
在一个项目创建多个根实例,定义了全局指令或者混入等,他们是共享相同的全局配置,结果是new Vue()
vue3:
vue2(都定义在vue上,会造成全局污染):
内置组件
内置组件一:transition(过渡动画)
transition会在一个元素或组件进入和离开DOM时应用动画。
它是一个内置组件,这意味着它在任意背的组件中能可以被使用,无需注册。它可以将进入和离开动画应用到默认插槽传递给它的元素或组件上,进入或离开可以由以下的条件之一触发:
- 由v-if所触发的切换
- 由v-show所触发的切换
- 由特殊元素component切换的动态组件
最基本的使用:
transition的工作流程
当一个transition组件中的元素被插入或移除时,会发生下面这些事情:
- vue会自动检测目标元素是否应用了css过渡或动画,如果是,则一些css过渡class会在适当的时机被添加或移除。
- 如果有作为监听器的js钩子,这些钩子函数会在适当时机被调用
- 如果没有检测到css过渡或动画,也没有提供js钩子,那么DOM的插入、删除操作将在浏览器的下一个动画帧后执行。
基于css的过渡效果
一共有6个应用于进入与离开过渡效果的参数时 Class。与vue2不同的是v-enter换成了v-enter-from和v-leave换成了v-leave-from。
vue3的基于cs的过渡效果具体如下:
- v-enter-from(改变的地方):进入动画的起始状态。在元素插入之前添加,在元素插入完成后的下一帧移除。
- v-enter-active:进入动画的生效状态,应用于整个进入动画阶段。在元素被插入之前添加,在过渡或动画完成之后移除。这个class可以被用来定义进入动画的持续时间、延迟与速度曲线类型。
- v-enter-to:进入动画的结束状态。在元素插入完成后的下一帧被添加。(也就是v-enter-from被移除的同时),在过渡或动画完成之后移除。
- v-leave-from:离开动画的起始状态。在离开过渡效果被触发时立即添加,在一帧后被移除。
- v-leave-active:离开动画的生效状态。应用于整个离开动画阶段。在离开过渡效果被触发时立即添加。在过渡或动画完成之后移除,这个class可以被用来定义离开动画的持续时间、延迟与速度曲线类型。
- v-leave-to:离开动画的结束状态。在一个离开动画被触发后的下一帧添加,也就是v-leave-from被移除的同时。在过渡或动画完成之后移除。
为过渡效果命名
vue2与vue3一致,通过name属性。示例如下:
在类名中将对应的v换成name属性的值即可。
css的transition
transition一般都会搭配原生css过渡一起使用。
css的animation
与帧动画相配合
自定义过渡class
有对应的6个自定义过渡class(一般与第三方动画库相结合),
- enter-from-class
- enter-active-calss
- enter-to-class
- leave-from-class
- leave-active-class
- leave-to-class
下图示例是与第三方库相结合,一定要注意写基础样式animate__animated:
同时使用transition和animation
vue需要附加事件监听器,以便知道过渡何时结束,可以是transitionend或animationend,这取决于你所应用的css规则,如果你仅仅使用二者的其中之一,vue可以自动检测到正确的类型。
然后在某些场景中,你或许想要在同一个元素上同时使用他们。举例来说,vue触发了一个css动画,同时鼠标悬停触发另一个css过渡,此时你需要显式地传入type prop来声明,告诉vue需要关心哪种类型,传入的值是animation或transition:
深层级过渡与显式过渡时长
在深层级的元素上触发过渡效果
可以在深层元素上添加一个过渡延迟(transition-delay),从而创建一个带渐进延迟的动画序列:
注意:这回带来一个小问题,默认情况下,transition组件会通过监听过渡根元素上的第一个transitionend或者animationend事件来尝试自动判断过渡合适结束,而在嵌套的过渡中,期望的行为应该是等待所有内部元素的过渡完成。
在这种情况下,可以通过向transition组件传入duration prop来显式指定过度的持续时间(以毫秒为单位)。总持续时间应该匹配延迟加上内部元素的过渡持续时间:
也可以写成对象的形式,分开制定进入和离开所需的时间:
可复用过渡效果
得益于vue的组件系统,过渡效果是可以被封装复用的,要创建一个可被复用的过渡,我们需要为transition组件创建一个包装组件,并向内传入插槽(传组件一般用插槽)内容:
出现时过渡
如果你想在某个节点初次渲染时应用一个过渡效果,你可以添加 appear prop(默认第一次是不执行的,加上的话第一次也会执行)
元素间过渡
除了通过v-if、v-show切换一个元素,我们也可以通过v-if、v-else、v-else-if在几个组件间进行切换,只要确保任一时刻只会有一个元素被渲染即可。
过渡模式
如果前后元素的离开和进入同时发生而产生了问题,用out-in或in-out可以来解决。
out-in:先出后进(用的较多)
in-out:先进后出(用的较少)
组件间过渡
Transition 也可以作用于动态组件(component的动态is属性)之间的切换
内置组件二:transitionGroup
vue2写法Transition-Group换成vue3的TransitionGroup
TransitionGroup是一个内置组件,用于对 v-for 列表中的元素或组件的插入、移除和顺序改变添加动画效果。
和transition的区别
TransitionGroup支持和Transition 基本相同的 props、CSS 过渡 class 和 JavaScript 钩子监听器,但有以下几点区别:
· 默认情况下,它不会渲染一个容器元素。但你可以通过传入tag prop 来指定一个元素作为容器元素来渲染。(生成的真实DOM 不会被渲染出来,可以通过添加tag这个属性来渲染出来节点)
- 过渡模式在这里不可用,因为我们不再是在互斥的元素之间进行切换
- 列表中的每个元素都必须有独一无二的key。
- css过渡class会被应用在列表内的元素上,而不是容器元素上。
移动动画
上面的示例有一些明显的缺陷:当某一项被插入或移除时,它周围的元素会立即发生“跳跃”而不是平稳地移动。我们可以通过添加一些额外的 CSS 规则来解决这个问题
内置组件三:KeepAlive
<KeepAlive> 是一个内置组件,它的功能是在多个组件间动态切换时缓存被移除的组件实例。
注意在 DOM 模板中使用时,它应该被写为 <keep-alive>。
KeepAlive有三个属性
- include:缓存哪个
- exclude:不缓存哪个
- max:最大缓存实例:如果缓存的实例数量即将超过指定的那个最大数量,则最久没有被访问的缓存实例将被销毁,以便为新的实例腾出空间。
缓存实例的生命周期
当一个组件实例从 DOM 上移除但因为被 <KeepAlive> 缓存而仍作为组件树的一部分时,它将变为不活跃状态而不是被卸载。当一个组件实例作为缓存树的一部分插入到 DOM 中时,它将重新被激活。
一个持续存在的组件可以通过 [activated]和 [deactivated] 选项来注册相应的两个状态的生命周期钩子:
KeepAlive的实现步骤
- 第一步:获取keep-alive包裹着的第一个子组件对象及其组件名;
- 第二步:根据设定的黑白名单(如果有)进行条件匹配,决定是否缓存。不匹配,直接返回组件实例(VNode),否则执行第三步;
- 第三步:根据组件ID和tag生成缓存Key,并在缓存对象中查找是否已缓存过该组件实例。如果存在,直接取出缓存值并更新该key在this.keys中的位置(更新key的位置是实现LRU置换策略的关键),否则执行第四步;
- 第四步:在this.cache对象中存储该组件实例并保存key值,之后检查缓存的实例数量是否超过max设置值,超过则根据LRU置换策略删除最近最久未使用的实例(即是下标为0的那个key);
- 第五步:最后并且很重要,将该组件实例的keepAlive属性值设置为true。
keep-alive组件的渲染
用过keep-alive都知道,他不会生成真正的DOM节点,这是为什么呢?
vue在初始化生命周期的时候,为组件实例建立父子关系会根据abstract属性决定是否忽略某个组件。在keep-alive中,设置了abstract:true,那vue就会跳过该组件实例。
最后构建的组件树中就不会包含keep-alive组件,那么组件树渲染成的DOM树自然也不会有keep-alive相关的节点了。
keep-alive包裹的组件是如何使用缓存的
在patch阶段,会执行createComponent函数:
- 在首次加载被包裹组件时,由keep-alive.js中的render函数克制,VNode.componentInstrance的值是undefined,KeepAlive的值是true,因为keep-alive组件作为父组件,它的render函数会先于被包裹组件执行,那么只执行到i(vnode,false),后面的逻辑不执行。
- 再次访问被包裹组件时,VNode.componentInstance的值就是已经缓存的组件实例,那么会执行insert(parentElm,VNode.elm,refElm)逻辑,这样就直接把上一次的DOM插入到父元素中。
内置组件四:Teleport
Teleport是一个内置组件,它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。(传送门)(可以覆盖到任何组件的上面)一般做提示框,登录框等这种ui库里叫模态框。
理想情况下,我们希望触发模态框的按钮和模态框本身是在同一个组件中,因为它们都与组件的开关状态有关。但这意味着该模态框将与按钮一起渲染在应用 DOM 结构里很深的地方。这会导致该模态框的 CSS 布局代码很难写。
· position: fixed能够相对于浏览器窗口放置有一个条件,那就是不能有任何祖先元素设置了 transform、perspective (透视)或者 filter 样式属性。也就是说如果我们想要用 CSS transform 为祖先节点 <div class="outer"> 设置动画,就会不小心破坏模态框的布局!
· 这个模态框的 z-index 受限于它的容器元素。如果有其他元素与 <div class="outer"> 重叠并有更高的 z-index,则它会覆盖住我们的模态框。
<Teleport> 提供了一个更简单的方式来解决此类问题,让我们不需要再顾虑 DOM 结构的问题。让我们用 <Teleport> 改写一下 <MyModal>:
<Teleport> 接收一个 to prop 来指定传送的目标。to 的值可以是一个 CSS 选择器字符串,也可以是一个 DOM 元素对象。这段代码的作用就是告诉 Vue“把以下模板片段传送到 body 标签下”。
搭配组件使用
<Teleport> 只改变了渲染的 DOM 结构,它不会影响组件间的逻辑关系。也就是说,如果 <Teleport> 包含了一个组件,那么该组件始终和这个使用了 <teleport> 的组件保持逻辑上的父子关系。传入的 props 和触发的事件也会照常工作。
这也意味着来自父组件的注入也会按预期工作,子组件将在 Vue Devtools 中嵌套在父级组件下面,而不是放在实际内容移动到的地方。
禁用Teleport
通过对Teleport动态地传入一个disabled props来处理:
多个Teleport共享目标
一个可重用的模态框组件可能同时存在多个实例。对于此类场景,多个 <Teleport> 组件可以将其内容挂载在同一个目标元素上,而顺序就是简单的顺次追加,后挂载的将排在目标元素下更后面的位置上。
渲染结果:
内置组件五:Suspense(实验性功能)
<Suspense> 是一个内置组件,用来在组件树中协调对异步依赖的处理。它让我们可以在组件树上层等待下层的多个嵌套异步依赖项解析完成,并可以在等待时渲染一个加载状态。
组件树中有多个嵌套组件,要渲染出它们,首先得解析一些异步资源。如果没有 <Suspense>,则它们每个都需要处理自己的加载、报错和完成状态。在最坏的情况下,我们可能会在页面上看到三个旋转的加载态,在不同的时间显示出内容。
有了 <Suspense> 组件后,我们就可以在等待整个多层级组件树中的各个异步依赖获取结果时,在顶层展示出加载中或加载失败的状态。(用一个loading控制所有异步状态,只要有一个没加载出来就显示loading)