前言:
前两天在我的项目中很多场景下都须要用到一个 toast 弹窗,我的项目应用的是 ionic+tialwind_Css,ionic 也有自带的 toast 弹窗,尽管大部分场景下间接调用它提供的 api 曾经能满足需要了,然而它弹窗的高度,(也就是弹窗呈现的地位)并不是高度自定义的,并且弹窗的 z-index 在咱们我的项目中会和一些组件抵触,然而这个之前始终没有方法解决,所以罗唆本人手写了一个应用办法高度相似 ionic_Toast 的组件。
这个组件也是我第一次在 vue3 下实现的,也查阅了很多网上相干的文章,也受到很多启发灵感,所以本人汲取精髓去其糟粕来实现了一版个人感觉应用起来很不便的一个版本,特来记录一下实现的过程,心愿能够帮忙到遇到同样蛊惑的人。
tips:(本篇文章不会上手就教你款式怎么写,代码怎么写,而是会帮你逐步一步步了解相干额定的常识。会以“如果我是一个初学者,如果过后有人这样通知我的话,我就大略能听明确”的角度去解释。所以篇幅较长,如果想间接看组件的实现,可间接跳转到 题目三)
在这里祝大家中秋节高兴呀~~🥮
上面是注释:
一. 前置工作:JSX 和渲染函数的概念
- 想要实现这个需要,你须要理解一下题目的那两个概念。官网文档在这里,外面的话语太过于“业余和官网”,导致我刚开始看的时候十分蛊惑,所以在这里我会帮你去了解外面的一些很官网的语言,让你疾速有个认知。
原地址在这里:vue 官网文档 JSX 和渲染函数。
- 咱们暂且还不须要去深刻了解渲染机制的整个流程。所以官网上面个链接暂且不要去查阅,会让你越来越头晕。
但在这里我要阐明一点,接下来讲的内容都是建设在我默认你对 虚构 DOM的概念有肯定的理解。
- 紧接就写到了 Vue 为咱们提供了一个函数,来创立 vnodes。在浏览这个页面的时候,肯定留神官网在每个代码右上角的文件类型。
- 这里须要插个必须要理解的题外话,理解 React 的同学肯定晓得 JSX 这种写法
Vue里 JSX 的概念和 React 的JSX的概念是极其类似的。Vue也是借鉴了 React 的这个思维,这里咱们重点看画线的这句话。(不相熟 react 的小伙伴也不要放心,本文实现的 Toast 并没有应用到 JSX 和babel。)
是不是感觉和刚刚 Vue 官网写的很类似?
官网在上文也提到了 h 是什么。如果咱们把 h 换成 createVnode(),是不是就和React.createElement 的用法及其类似了呢?
- 其实不论是 Vue 的 h(),还是React.createElement() 它们最终要达到的目标只有一个:创立 虚构 DOM。而这也对应了 Vue 中createVnode的 Vnode 其实就是 virtual node 的意思。函数名的间接翻译其实也就是 创立虚构节点 。而JSX 只是创立 虚构 dom的语法题而已,仅此而已,并没有什么特别之处。
二. createVnode 函数的意义
- 当初咱们在 .Vue文件写如下代码。
非常简单的构造,一个 id 是 ”hanzhenfang” 的 div
标签,标签内容是我的名字。ok,这样写的话,vue 就会帮咱们将这个构造转换为虚构 dom。
实质上是应用了
h("div",{id:"hanzhenfang"},"韩振方")
h()
能够有多个参数,
这段代码是在 <template>
标签内写的,它底层其实还是应用了 h() 函数去实现的。说白了就是,React 选用 JSX 来作为渲染 虚构 dom函数的语法糖。而 <template>
标签是 Vue 采纳的渲染 虚构 dom的语法糖。
从而能够引出官网的规范解释:
- 你可能会疑难了,既然模板能够实现这样的性能,那我间接写模版不就完事了吗?还须要写什么 h() 函数呢?因为有的场景的确是模板做不到的。这也就是我为什么会写这篇文章的起因,因为这个 toast 需要应用的场景很多很多,我总不能在每个中央都引入一个组件通过
v-if
来管制它的显示和敞开吧?十分繁琐和麻烦。
三. 编写 Toast 组件(不应用 tsx)
- 首先创立一个 toast.vue 文件写出大抵款式。因为是应用 tailwindCss,所以款式书写的形式可能和传统的在
style
标签写款式不太一样,然而原理是一样的,不必放心。实现如下:
- toast在绝大场景下都是居中的,并且脱离文档流,位于整个页面的最顶部,所以我这里采纳了传统的相对定位的计划。
- 相对定位最简略居中计划不过与设置
top
和left
各 50%。然而咱们不要疏忽了一点,偏移的时候,咱们还要减去本身宽度和高度的一半,才能够做到齐全居中。然而处于复用性思考,咱们的toast 的宽度是不能设置固定宽度的,具体的宽度是由过后文字的大小决定的。
- 这时候咱们须要用到
offsetWidth
1. 额定技能补充 offsetWidth
- 先让咱们看看 MDN 如何解释的。
- 具体什么意思呢?咱们顺手写一个很简略的
template
。
咱们当初不加任何 Css
属性,来查看一下 offsetWidth 是什么值。
不要把调试工具只当成 console.log
的中央,肯定利用好这个工具。咱们抉择刚刚写的元素,点击调试工具选项栏的 Properties
标签。
能够看到 offsetWidth
目前是 20(留神,这是一个十进制的 number 类型,并不带单位,并且是一个只读属性,无奈间接更改。)
20 是怎么来的呢?其实它就是咱们设置的字体大小。
来验证一下猜测,咱们设置一下字体大小为 15px
- 当初给这个 div 加上 10px 宽度的
border
。
别着急看 offsetWidth
的值,咱们依据它的定义猜测一下。
应该等于 15px
的字体大小 +10px
?
确定吗,别忘了 border 是上下左右都为10px
,所以依据猜测
`offsetWidht=15px + 10px + 10px
- 如果这个
div
咱们本人设定宽度呢?咱们来给它设定 100px 的宽度。
咱们会发现 offsetWidth
的值变成了100
然而我明明有还有 10px
的border
啊!哪里去了?
你是否忘了咱们当初大部分状况下应用的都是 box-size:border-box
呢?设置的宽度是蕴含 border
的。
验证一下,咱们扭转一下 box-size
的类型。
能够看进去,offsetWidth
就变成了100px+10px+10px
Tips:不过在本文中不会设置定宽去限度
四. Toast 居中的思路
1. 当初咱们能够不设置 Toast
的宽度,并且拿到依据文字数量不同所变动的宽度。因为这个属性是组件挂载结束当前才有的属性,那么咱们能够在 onMounted
里拿到。首先须要拿到元素自身,这里采纳打 ref
的形式。
具体变量和代码如下:不过多赘述
- 而后咱们须要通过一个计算属性动静的计算出该组件的款式;
ok,这样就实现了居中的成果。并且不论咱们如何扭转内容都没关系。居中成果是动静计算取得的,并不是一开始就写死的。
五. Toast 三个呈现地位的思路
有些场景下 Toast 呈现在底部并不是特地适合,所以咱们还要思考呈现地位的问题。这里简略设计另外两个,一个两头,一个偏顶部。实现起来也比较简单。咱们让它 toastWrapperStyle
计算 top
的偏移量也是一个动静计算的就能够了。
咱们就能够在前面给这个组件传递参数来管制具体的地位在哪里。
这个目前的代码还动静的展现成果,咱们缓缓在前面体现。
tips:上面的章节是本文全篇重点
六 *. h()函数的应用
- 咱们创立一个
toastCreator.ts
的文件,便于函数式调用展现 Toast 组件。
- 筹备如下内容,这里须要用到后面所提到的 h(),还有render(),render 你暂且能够了解为给你返回一个实在的 DOM。因为 h() 是生成虚构 dom 的, 然而咱们最终展现到页面的是
实在 dom
,咱们之前不必在<template>
标签内不必执行 render 是因为 Vue 帮你调用了 render()。然而咱们在这里相当于手动实现一个Vue 的渲染过程,所以咱们也同时须要用到这个函数。
- 同时把同文件夹下刚刚写好的
Toast.vue
引入。class相干的常识不是本文的重点,不理解的须要自行去查阅相干常识,这点很重要。 - 减少了一个 duration 选项,也就是持续时间,成果为Toast,在页面呈现多少秒后主动隐没。
- 而后咱们须要编写一个这个类自身的办法,名为 present()(这里借鉴了ionic 的Toast组件的调用名称。)
这一步是重点,也是比拟难了解的一个点。1. 首先咱们须要本人创立一个 Vnode,通过翻阅官网。(留神咱们着重看JS 的文件)。
2. 咱们得悉,原来h() 函数能够间接接管一个组件模板作为参数。那么咱们能够这样写:
这样变量 myToast
就是一个 Vnode 了。
3. 而后再调用引入的render() 函数,咱们天然而言的会想到这样写。
然而如同不太对劲啊,怎么报错了呢?看一下报错信息,原来 render() 函数须要接管两个参数。第一个参数是Vnode,第二个参数是套在Vnode 的实在 dom。
让咱们创立并且加上一层外壳 containner
。其实就是一个一般的div
标签。如下:
在这里要强调一下,你创立的虚构节点必须包裹一层 实在的 DOM作为容器。
这样正好对应,为什么 Vue 或者 React 组我的项目的根目录都会有一个 .html
文件,并且还有一个 根 div 标签的起因。 因为render() 函数须要。(想更深刻理解原理的能够翻看源码,不再过多赘述。)
七. 如何传递 props?
- Vnode和 实在 DOM都有了,那咱们如何传递
props
呢?
2. 这里不卖官子,先给后果,咱们须要革新一下咱们刚刚写的h() 函数。
这样即可将调用构造函数时传递的参数转换为该组件的 props
。
因为篇幅限度,具体细节大家仔细阅读为下面贴出来的 Vue 官网文档。
八. 挂载实在 DOM 到页面上
都是根底的常识,不过多解释了,代码如下:
九. 持续时间的管制
- 编写dismiss() 办法。非常简单,间接移除这个节点即可。
- 持续时间如何管制呢?
和工夫挂钩,自然而然能够想到setTimout()。实现原理其实也是非常简单。在特定的工夫,调用dismiss() 办法即可
十. Toast 自定义组件的应用办法
- 忘了写
message
属性了,咱们补充一下
- 而后在
App.vue
文件引入
顺手写一个 <button>
标签。
成果如下:
3. 测试一下 position:top
。
十一. 减少淡出的成果
咱们我的项目不须要淡入,所以我就没做,不过和淡出是一模一样,在这里我讲一下大抵思路。
只需在 dismiss() 函数执行前,减少一个通明成果,这里应用的是减少类名,只不过这个动画名称是搭配tailWind 来实现的,你也能够应用别的办法,我的办法并不是最好的,原理思路是统一的
- 在
tailwind.config.js
下设置全局动画款式。
这里须要留神!!你的动画持续时间要和 dismiss()函数的 setTimeout 参数统一,不然会呈现意想不到的成果。动画完结了,后果组件又呈现啦。
2. 再调用 document.doby.removeChild
办法之前,让咱们的组件在 0.5s 内透明度变为 0,而后再移除组件,就完满实现了淡出的成果。
最终成果如下: