【源码学习】第22期 | 看了vant4源码才知道原来loading组件还可以这样实现!


theme: qklhk-chocolate
highlight: a11y-dark

背景

    Vant 是一个轻量、可靠的移动端组件库,而Vant 4.x 版本适用于 Vue 3 开发,今天就来学习一下Loading 加载组件的实现!

收获清单

  • [x] vue-devtools打开所在文件
  • [x] 如何用vue3+ts开发一个loading组件
  • [x] 一些实用的工具函数

环境准备

    开始阅读源码前先看README.md,这里基本会教你怎么用它,接着看贡献文档CONTRIBUTING.md,下图为贡献文档的环境跟运行项目说明:

图片.png

下载源码

git clone https://github.com/youzan/vant.git
cd vant
// 安装依赖及运行项目
pnpm i 
pnpm dev

vue-devtools打开所在文件

图片.png
    选择DemoBlock打开文件后发现,呀,这不是咱们很熟悉的组件么?主要有card跟title属性,并根据card属性显示插槽,但是也不是咱们要找的loading啊,那么到底是怎么实现的呢?

图片.png

    带着这个问题,先看看app.vue的代码

<template>

  <demo-nav />

  <router-view v-slot="{ Component }">

    <keep-alive>

      <demo-section>

        <component :is="Component" />

      </demo-section>

    </keep-alive>

  </router-view>

</template>

    这里主要用内置组件插槽来实现路由页面显示,主要涉及了vue的内置特殊元素相关知识,日常开发技能+1

图片.png

    接着我们可以给vant\packages\vant-cli\site\mobile\router.js打断点调试,继而找到loading所在文件

图片.png

图片.png

源码分析

    找到loading所在文件后,我们接着来分析一下关键源码

入口文件

    文件所在路径vant\packages\vant\src\loading\index.ts,主要是通过withInstall注册loading组件,并把loading相关的类型暴露出去

import { withInstall } from '../utils';

import _Loading from './Loading';

export const Loading = withInstall(_Loading);

export default Loading;

export { loadingProps } from './Loading';

export type { LoadingType, LoadingProps } from './Loading';

export type { LoadingThemeVars } from './types';

declare module 'vue' {

  export interface GlobalComponents {

    VanLoading: typeof Loading;

  }

}

withInstall

export function withInstall<T extends Component>(options: T) {

  (options as Record<string, unknown>).install = (app: App) => {

    const { name } = options;

    if (name) {

      app.component(name, options);

      app.component(camelize(`-${name}`), options);

    }

  };

  return options as WithInstall<T>;

}
  • withInstall入参为泛型类型的options
  • 主要作用是给入参options添加install属性
    • 若传入组件名称,则通过app.component同时注册名为vant-loadingVantLoading的全局组件

主文件 loading\Loading.tsx

    在源码setup处打断点可以看到loading组件的props值分别有那些,以及loading组件主要是通过renderIconrenderText来分别渲染图标和文字,接着从应用的工具函数细看一下具体实现:

图片.png

createNamespace 函数

export function createNamespace(name: string) {
  const prefixedName = `van-${name}`;
  return [
    prefixedName,
    createBEM(prefixedName),
    createTranslate(prefixedName),
  ] as const;
}

    createNamespace主要根据传入name值返回van-${name}、createBEM函数和createTranslate函数,其中createBEM函数的作用看调试截图会更清晰点:

图片.png

extend 函数

export const extend = Object.assign;

    其实就是Object.assign的别名,Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,它将返回目标对象。

const target = {name:'yuexia'}
const source = {hobby:'meditation'}
const result = Object.assign(target,source)
console.log(result,target==result) 
//{ name: 'yuexia', hobby: 'meditation' } true

getSizeStyle 函数

export function getSizeStyle(

  originSize?: Numeric | Numeric[]

): CSSProperties | undefined {

  if (isDef(originSize)) {

    if (Array.isArray(originSize)) {

      return {

        width: addUnit(originSize[0]),

        height: addUnit(originSize[1]),

      };

    }

    const size = addUnit(originSize);

    return {

      width: size,

      height: size,

    };

  }

}

    getSizeStyle函数返回组件的尺寸(带单位宽高的值),大概意思是:

  1. 判断入参是否非undefined和null
  2. 用Array.isArray判断入参是否为数组,若是则分别取值,否则直接返回addUnit函数处理后的值作为宽高

addUnit添加单位

export function addUnit(value?: Numeric): string | undefined {

  if (isDef(value)) {

    return isNumeric(value) ? `${value}px` : String(value);

  }

  return undefined;

}

    addUnit函数的作用是给入参添加单位,其中isDef函数判断入参是否非undefined和null

总结

    本文调试学习了vant4源码中loading组件的实现,学习了其关联的工具函数,虽然loading组件只是文字跟图标的渲染,但其实现方式还是很值得我们在日常开发(尤其是ts的项目)参考应用的~

© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容