vant-weapp源码解读(3)

本文主要讲解van-popup组件

1.组件的什么周期

在讲解这个组件前,先讲解一个组件生命周期的知识点,请看以下代码,然后说出日志分别是什么,能说出正确答案的人应该不多。

js 复制代码
Component({
  data: {
    a:0
  },
  attached: function(){
    console.log('attached a:',this.data.a)
    this.setData({
      a:1
    },()=>{
      console.log('attached a2:',this.data.a)
    })
  },
  ready: function(){
    console.log('ready a:',this.data.a)
  }
});

我原来日志是这样的

js 复制代码
attached a 0 
ready a 0 
attached a2 1

实际打印是这样的

js 复制代码
attached a 0 
ready a 1
attached a2 1

主要区别是ready打印的值是1,而不是0。我以为是0,是因为我一直以为setData方法是异步的,直到真实测试下,才发现不是,于是我赶紧去看了看文档,文档是这样写的。

setData 函数用于将数据从逻辑层发送到视图层(异步),同时改变对应的 this.data 的值(同步)。

文档地址:框架接口 / 页面 / Page

看这段话的意思,就是setData会立即改变this.data的值(同步),但是要改变视图层,就要等到js队列里的任务都执行完才行(异步)

又学到了一个知识点。

2.popup组件

2.1先看下index.wxml代码

html 复制代码
<wxs src="../wxs/utils.wxs" module="utils" />
<wxs src="./index.wxs" module="computed" />

<import src="./popup.wxml" />

<van-overlay
  wx:if="{{ overlay }}"
  show="{{ show }}"
  z-index="{{ zIndex }}"
  custom-style="{{ overlayStyle }}"
  duration="{{ duration }}"
  bind:click="onClickOverlay"
  lock-scroll="{{ lockScroll }}"
  root-portal="{{ rootPortal }}"
/>

<root-portal wx:if="{{ rootPortal }}">
  <include src="./popup.wxml" />
</root-portal>

 <include wx:else src="./popup.wxml" />

最上面引入了utils和computed两个module,这两个module主要用来给组件设置class和style的。

主要代码放在popup.wxml文件中,在index.wxml中,通过import引入popup.wxml中的template代码(其实这句没用,因为popup.wxml中没有用template代码),通过include引入了popup.wxml中除template以外的全部代码。

如果用户设置了overlay属性,就使用van-overlay组件设置阴影。

如果用户设置了rootPortal属性,就把popup.wxml放在root-portal节点下,否则就直接放在引入的位置。

2.2 popup.wxml代码

html 复制代码
<wxs src="../wxs/utils.wxs" module="utils" />
<wxs src="./index.wxs" module="computed" />
<view
  wx:if="{{ inited }}"
  class="custom-class {{ classes }} {{ utils.bem('popup', [position, { round, safe: safeAreaInsetBottom, safeTop: safeAreaInsetTop, safeTabBar: safeAreaTabBar }]) }}"
  style="{{ computed.popupStyle({ zIndex, currentDuration, display, customStyle }) }}"
  bind:transitionend="onTransitionEnd"
>
  <slot />
  <van-icon
    wx:if="{{ closeable }}"
    name="{{ closeIcon }}"
    class="close-icon-class van-popup__close-icon van-popup__close-icon--{{ closeIconPosition }}"
    bind:tap="onClickCloseIcon"
  />
</view>

这个代码跟transition组件的代码非常像。我们一点点讲解。

3 动画的实现方式

首先讲动画实现方式,view动画是通过给class属性中的classes变量,在不同的时间,赋予不同的值来实现的。

代码分两部分,一部分在index.ts文件中,一部分在transition.ts文件中。

在vant组件created生命周期(对应就是小程序的attached生命周期)中初始化动画名字name和动画时间duration。

动画的名字name来自于用户传进来的属性transitionpositiontransition优先级更高。这个是在index.ts文件中定义的。

动画时间duration来自用户传过来的属性duration,如果用户不传的话,默认值是300ms。这个是在transition.ts文件中定义的。

js 复制代码
created() {
    this.observeClass();
 }
js 复制代码
observeClass() {
  const { transition, position, duration } = this.data;

  const updateData: { [key: string]: any } = {
    name: transition || position,
  };

  if (transition === 'none') {
    updateData.duration = 0;
    this.originDuration = duration;
  } else if (this.originDuration != null) {
    updateData.duration = this.originDuration;
  }

  this.setData(updateData);
}

如果show为true,就直接执行监听show属性的函数observeShow,否则监听show属性的变化,当show属性改变的时候,再执行监听函数。

这个函数的主要作用就是当show为true的时候,执行enureEnter函数(进场动画),当show为false的时候执行enureLeave函数(离场动画)

enureEnter函数中,为了确保动画唯一,使用了promise函数,只有动画执行完毕时,才resolve。

主要的动画是在enter函数里,通过两个定时器来动态改变classes变量来实现。

1.首先初始化变量

js 复制代码
const { duration, name } = this.data;
const classNames = getClassNames(name);
const currentDuration = isObj(duration) ? duration.enter : duration;

getClassNames函数会根据传入的name值,生成相应的className,它的返回值一个对象,我们不同的时间,使用它不同的值。

js 复制代码
const getClassNames = (name: string) => ({
  enter: `van-${name}-enter van-${name}-enter-active enter-class enter-active-class`,
  'enter-to': `van-${name}-enter-to van-${name}-enter-active enter-to-class enter-active-class`,
  leave: `van-${name}-leave van-${name}-leave-active leave-class leave-active-class`,
  'leave-to': `van-${name}-leave-to van-${name}-leave-active leave-to-class leave-active-class`,
});

2.初始化进入样式

主要代码是这个

php 复制代码
this.setData({
    inited: true,
    display: true,
    classes: classNames.enter,
    currentDuration,
});

此时classes变量是classNames.enter,我们如果使用默认的position的话(默认值是center)。对应的值就是

van-center-enter van-center-enter-active enter-class enter-active-class

这些class样式已经提前在class中定义好了,这时候就是把view的透明度设置成0,也就是先把view隐藏起来。

index.less文件

css 复制代码
.van-center-enter,
.van-center-leave-to {
  opacity: 0;
}

3.执行动画

在下一个定时器里,我们给classes设置新的变量

this.setData({ classes: classNames['enter-to'] });

此时对应的classes值是van-center-enter-to van-center-enter-active enter-to-class enter-active-class

这些class在css中没有定义,因为我们给classes赋予新的值了,就相当于移除了之前设置van-center-enter样式。

view的透明度就由初始的0,变成了默认值1。

浏览器检测到后,就会执行对应的动画,将view的透明度由0变成1,从而实现一个淡入的效果。

其他动画与此同理,都是通过修改不同的classes变量来实现的,相应的class样式都在css文件中有定义,这里就不赘述了

4 position和round

弹出位置和是否显示圆角是通过生成不同的class来实现的。这里使用utils.bem工具函数,可以生成符合bem规范的class名称

代码如下:

js 复制代码
utils.bem('popup', [position, { round, safe: safeAreaInsetBottom, safeTop: safeAreaInsetTop, safeTabBar: safeAreaTabBar }])

比如当positiontop的时候,会生成一个值为van-popup--top的class,如果round为true,会生成一个c值为van-popup--round的class。

相应的样式,都在css文件中定义好了。

css 复制代码
.van-popup {
  position: fixed;
  box-sizing: border-box;
  max-height: 100%;
  overflow-y: auto;
  transition-timing-function: ease;
  animation: ease both;
  -webkit-overflow-scrolling: touch;
  background-color: var(--popup-background-color, @popup-background-color);

  &--top {
    top: 0;
    left: 0;
    width: 100%;

    &.van-popup--round {
      border-radius: 0 0
        var(
          --popup-round-border-radius,
          var(--popup-round-border-radius, @popup-round-border-radius)
        )
        var(
          --popup-round-border-radius,
          var(--popup-round-border-radius, @popup-round-border-radius)
        );
    }
  }

5 icon

如果用户设置closeable为true,就是显示关闭图标。这里引用了van-icon组件

html 复制代码
<van-icon
    wx:if="{{ closeable }}"
    name="{{ closeIcon }}"
    class="close-icon-class van-popup__close-icon van-popup__close-icon--{{ closeIconPosition }}"
    bind:tap="onClickCloseIcon"
 />

用户可以通过给closeIcon属性设置不同的值,选择不同的icon图标,或者使用图片链接。

通过给close-icon-position设置不同的值,修改图标的位置。比如设置为'top-left'。van-popup__close-icon--{{ closeIconPosition }}就变成了van-popup__close-icon--top-left

相应的样式,已经在css文件中定义了。

css 复制代码
.van-popup {
    &__close-icon {
    position: absolute;
    z-index: var(--popup-close-icon-z-index, @popup-close-icon-z-index);
    color: var(--popup-close-icon-color, @popup-close-icon-color);
    font-size: var(--popup-close-icon-size, @popup-close-icon-size);

        &--top-left {
          top: var(--popup-close-icon-margin, @popup-close-icon-margin);
          left: var(--popup-close-icon-margin, @popup-close-icon-margin);
        }
    }
}

别的就没什么好说的了,这个组件也是蛮简单的。

相关推荐
golang学习记3 小时前
从0 死磕全栈第3天:React Router (Vite + React + TS 版):构建小时站实战指南
前端
Dream耀4 小时前
Promise静态方法解析:从并发控制到竞态处理
前端·javascript·代码规范
JarvanMo4 小时前
2025 年真正有效的 App Store 优化(ASO)
前端·ios
{⌐■_■}4 小时前
【JavaScript】前端两种路由模式,Hash路由,History 路由
前端·javascript·哈希算法
前端老鹰4 小时前
HTML `<datalist>`:原生下拉搜索框,无需 JS 也能实现联想功能
前端·html
玲小珑4 小时前
LangChain.js 完全开发手册(五)Runnable 接口与任务编排系统
前端·langchain·ai编程
江城开朗的豌豆4 小时前
解密useEffect依赖数组
前端·javascript·react.js
江城开朗的豌豆4 小时前
React Hooks必杀技:前端工程师小杨带你玩转常用API!
前端·javascript·react.js