背景
Vant 是一个轻量、可靠的移动端组件库,而 Vant 4.x 版本适用于 Vue 3 开发,今天就来学习一下Loading 加载组件的实现!
收获清单
- vue-devtools打开所在文件
- 如何用vue3+ts开发一个loading组件
- 一些实用的工具函数
环境准备
开始阅读源码前先看 README.md,这里基本会教你怎么用它,接着看贡献文档 CONTRIBUTING.md,下图为贡献文档的环境跟运行项目说明:
下载源码
git clone https://github.com/youzan/vant.git
cd vant
// 安装依赖及运行项目
pnpm i
pnpm dev
vue-devtools打开所在文件
选择DemoBlock
打开文件后发现,呀,这不是咱们很熟悉的组件么?主要有card跟title属性,并根据card属性显示插槽,但是也不是咱们要找的loading啊,那么到底是怎么实现的呢?
带着这个问题,先看看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
接着我们可以给vant\packages\vant-cli\site\mobile\router.js
打断点调试,继而找到loading所在文件
源码分析
找到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-loading
和VantLoading
的全局组件
- 若传入组件名称,则通过app.component同时注册名为
主文件 loading\Loading.tsx
在源码setup处打断点可以看到loading组件的props值分别有那些,以及loading组件主要是通过renderIcon
和renderText
来分别渲染图标和文字,接着从应用的工具函数细看一下具体实现:
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函数的作用看调试截图会更清晰点:
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函数返回组件的尺寸(带单位宽高的值),大概意思是:
- 判断入参是否非undefined和null
- 用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的项目)参考应用的~