kayran v0.4.9
kayran / 七零八碎工具函数合集
Installation
pnpm add kayran lodash-es
waitFor / 以优雅方式书写 await
Param
/**
* @param {Promise} 需要包装的Promise
* @returns {any[]} [0]为Promise.prototype.then返回的结果,[1]为Promise.prototype.catch返回的结果
*/
// 示例
import { waitFor } from 'kayran'
const [res] = await waitFor(new Promise((resolve, reject) => {
resolve('res')
}))
console.log(res) // 'res'
const [, err] = await waitFor(new Promise((resolve, reject) => {
reject('err')
}))
console.log(err) // 'err'
getMediaDuration / 获取音视频文件的时长
Param
/**
* @param {File|string} 音视频url或二进制文件
* @returns {Promise<number>} 时长 单位秒 四舍五入
*/
// 示例
import { getMediaDuration } from 'kayran'
getMediaDuration('https://xxx.mp4')
typeOf / 获取变量的精确类型
动机:原生js的typeof等类型检测手段都存在各种缺陷
Param
/**
* @param {any} 需要判断的变量
* @returns {string} 变量类型(全小写) 如string null undefined file...
*/
// 示例
import { typeOf } from 'kayran'
typeOf(1) // 'number'
isEllipsis / 判断dom是否触发溢出省略(text-overflow: ellipsis)
Param
/**
* @param {Element} 需要判断的元素
* @returns {boolean} 是否触发溢出省略
*/
// 示例
import { isEllipsis } from 'kayran'
isEllipsis(document.querySelector('.text'))
isEmpty & notEmpty / 判空 & 判非空
notEmpty() 等同于 !isEmpty()
Param
/**
* @param {any} 需要判断的变量
* @returns {boolean} 结果
*/
// 示例
import { isEmpty, notEmpty } from 'kayran'
isEmpty(0) // false
isEmpty({}) // true
isEmpty([]) // true
jsonToFormData / json转FormData
Param
/**
* @param {object} 需要转换的对象
* @param {(value?: any, key?: any) => any} mapFn 每个属性会执行该回调函数,返回值为新的属性值
* @returns {FormData} 转换后的FormData实例
*/
// 示例
import { jsonToFormData } from 'kayran'
const formData = jsonToFormData({
a: 1
})
formData.get('a') // '1'
// 绑定到FormData上,像Array.from一样使用
import { jsonToFormData } from 'kayran'
if (FormData.from === undefined) {
FormData.from = jsonToFormData
}
const formData = FormData.from({
a: '1',
b: '2'
})
// mapFn示例
import { jsonToFormData } from 'kayran'
const formData = jsonToFormData({
a: 1
}, v => v + 1)
formData.get('a') // '2'
::: tip
回调函数的返回值为undefined时,该元素不会被添加到FormData实例中(类似filter效果)。
:::
pickDeepBy / lodash-pickBy的递归版本
Param
/**
* @param {object} obj 原始对象
* @param {(value?: any, key?: any) => boolean} predicate 每个属性都会调用的方法
* @returns {object} 新的对象
*/
// 示例
import { pickDeepBy } from 'kayran'
pickDeepBy({
a: 1,
b: NaN
}, (v, k) => ![NaN, null, undefined].includes(v))
// { a: 1 }
pickDeepBy不会改变原始对象。
isSameOrigin / 判断是否同源
Param
/**
* @param {string} url 需要判断的url
* @returns {boolean}
*/
// 示例
import { isSameOrigin } from 'kayran'
isSameOrigin('www.google.com')
parseQueryString / 获取 url 中某个查询参数的值
Param
/**
* 获取当前url某个查询参数的值
* 支持微信公众号授权特例:授权后会重定向回来并在链接中加入一个code参数 但微信没有考虑hash路由的情况 所以获取这个code需要特殊处理
* @param {object}
* {string} key 查询参数的key 如果为空 则返回查询参数映射的对象 可以解构使用
* {string} [mode='hash'] router模式 可选值'history'/'hash'
* {boolean} del 是否在url中删除该值(删除会引发页面刷新)
* @returns {string|object} 查询参数中key对应的value / 如果key为空 则返回查询参数整个对象
*/
// 示例
import { parseQueryString } from 'kayran'
const code = parseQueryString('code')
//or
const { code } = parseQueryString()
注意:如果search和hash中同时包含code 如http://localhost:8080/?code=1#/?code=2
取的是search中的code 即返回1
loadLink / 动态加载link
Param
/**
* @param {string|object} src link url
* @returns {Promise}
*/
// 示例
import { loadLink } from 'kayran'
await loadLink('https://cdn.bootcdn.net/ajax/libs/normalize/8.0.1/normalize.min.css')
loadScript / 动态加载js
Param
/**
* @param {string|object} src 脚本url
* @returns {Promise}
*/
// 示例
import { loadScript } from 'kayran'
loadScript('https://cdn.jsdelivr.net/npm/vue/dist/vue.js').then(e => {
console.log(Vue)
})
loadStyle / 动态加载style
Param
/**
* @param {string|object|HTMLElement} arg style元素的innerText 或传对象指定style的各项属性 或style元素本身
* @returns {Promise<HTMLElement>}
*/
// 示例
import { loadStyle } from 'kayran'
await loadStyle(`
ul {
list-style: none;
}
`)
getFinalProp / 获取最终prop(适用于组件开发者)
Vue提供了prop的局部配置和默认值配置,但在封装组件时,还会非常需要一个“全局配置”,否则可能导致每个组件实例进行重复的配置。
举个例子,Element
的size与zIndex就是支持全局配置的。
当配置多了以后,由于存在不同的优先级,最终组件采用的是哪一项配置,需要进行一定的判断,
在涉及到对象和函数时,判断可能会变得相当复杂。
getFinalProp的作用就是帮助你计算出最终的配置。
Features
- 和Vue的props一样,提供是否必传、数据类型和自定义的校验
- 对于plain object类型的prop,支持深合并、浅合并和直接覆盖
- 对于function类型的prop,支持融合、直接覆盖
- 支持将对象的键统一为驼峰命名
- 支持动态生成默认值
Param
/**
* @param {any[]} propSequence - prop序列(优先级从高到低,最后是默认值)
* @param {object} [config] - 配置
* @param {string} [config.name] - prop名称,用于报错提示
* @param {string|string[]} [config.type] - 数据类型校验
* @param {any} [config.default] - 默认值(显式)
* @param {boolean} [config.defaultIsDynamic = false] - 动态生成默认值
* @param {boolean} [config.required = false] - 是否必传校验
* @param {function} [config.validator] - 自定义校验
* @param {string} [config.camelCase = true] - 是否将对象的键统一为驼峰命名
* @param {false|string} [config.mergeObject = 'deep'] - 合并对象的方式
* @param {boolean} [config.mergeObjectApplyOnlyToDefault = false] - mergeObject仅作用于default
* @param {false|((accumulator, currentValue, index?, array?) => Function)} [config.mergeFunction = false] - 融合函数的方式
* @param {boolean} [config.mergeFunctionApplyOnlyToDefault = true] - mergeFunction仅作用于default
* @returns {any} 最终的prop
*/
// 示例
import { getFinalProp } from 'kayran'
getFinalProp([1, 2, undefined]) // 1
怎么判断某个prop有没有传?
以该prop是否全等于undefined作为标识
config.mergeObject
'deep'
: 深合并,高权重prop的对象键会覆盖低权重prop的同名键,包含嵌套的对象(默认值)'shallow'
: 浅合并,高权重prop的对象键会覆盖低权重prop的同名键,不含嵌套的对象false
: 不合并,直接覆盖,高权重prop的对象会直接覆盖低权重prop的对象,与值类型的表现一致
config.mergeObjectApplyOnlyToDefault
默认关闭,仅在mergeObject开启时有效。
开启时,mergeObject的规则仅会应用于最后与default进行比对的环节中,之前的对象依然会直接覆盖。
关闭时,mergeObject的规则会应用至所有对象类型prop的权重比对中。
使用场景:组件作者想要将组件内部的配置与组件使用者的配置进行合并,但组件使用者自身的各级配置依然保持直接覆盖的规则。
config.mergeFunction
使用场景:在封装组件时,你可能需要通过配置选项的方式监听底层依赖的某些事件,
在将该依赖的配置选项暴露出去时,组件使用者的配置就会与你的配置发生冲突。
mergeFunction提供定制化的方式来融合函数类型的prop。
举个例子,知名的富文本库tinymce的配置选项中有一个叫 init_instance_callback
的回调,
在封装这个库时,可以藉此来做一些初始化的工作,为了不破坏组件的灵活性,也会将tinymce的配置选项暴露出去,
问题来了,组件使用者一旦配置了这个回调,就会与你的配置发生冲突。
与其他数据类型的配置不同的是,函数类型的prop,往往不期望被用户的配置直接覆盖掉,会有需要进行“融合”的需求。
融合:既执行组件使用者配置的函数,也执行组件内部配置的函数。
函数类型的prop包括两种情况:
- prop本身是函数
- prop是含有函数属性的对象
getFinalProp内部使用 Array.prototype.reduce 来执行函数融合,mergeFunction将被用作参数1。
getFinalProp([
() => {
console.log('我是参数1')
},
() => {
console.log('我是参数2')
}
], {
default: () => {
console.log('我是显式默认值')
},
mergeFunction: (accumulator, item) => (...args) => {
accumulator(...args)
item?.(...args)
},
mergeFunctionApplyOnlyToDefault: false,
})()
// 结果会打印 '我是显式默认值' '我是参数2' '我是参数1'
config.mergeFunctionApplyOnlyToDefault
默认开启,仅在mergeFunction开启时有效。
函数融合毕竟是一个特殊行为,往往只有组件作者会用到这个功能,
对于组件使用者来说,函数类型的配置可能更希望的是和其他原始类型一样,直接覆盖掉就好了。
开启时,mergeFunction的规则仅会应用于最后与default进行比对的环节中,之前的函数依然会直接覆盖。
关闭时,mergeFunction的规则会应用至所有函数类型prop的权重比对中。
config.default
显式指定默认值,如果没有开启 mergeObjectApplyOnlyToDefault
或 mergeFunctionApplyOnlyToDefault
的话,则没有必要使用该参数,将默认值放在 propSequence
的末尾即可。
config.camelCase
Vue的prop同时支持驼峰和短横线格式,如果组件使用者同时传了同一个prop的两种格式,值还是不相同的,问题来了,此时应该取哪一个值?
在多个配置进行合并时,结果会更加难以预测,所以getFinalProp在合并对象后默认将对象的键统一为驼峰命名。
::: tip 为什么不默认使用短横线命名?
参见Vue官方风格指南
:::
动态生成默认值
使用场景:需要根据组件使用者传的参数来决定默认值
// 示例
getFinalProp([{
a: {
a: 1
}
}, {
a: {
a: 2,
b: 1
}
}], {
// userProp是参数1的计算结果
default: userProp => ({
a: {
c: userProp.a.a === 1 ? 1 : null
}
}),
defaultIsDynamic: true,
})
/**
* 将得到:
* {
* a: {
* a: 1,
* b: 1,
* c: 1
* }
* }
*/
getGlobalAttrs / 获取全局attrs(适用于组件开发者)
在Vue组件中,声明过的prop可以通过 this.$props
获取,没有声明的可以通过 this.$attrs
获取,
但是全局配置就无法区分哪部分是props哪部分是attrs了。
getGlobalAttrs就是帮助你获取全局配置中的attrs的。
使用场景:二次封装组件时,往往需要使用 v-bind="$attrs"
来实现不破坏底层组件的原生能力,
getGlobalAttrs就是在此基础上提供全局配置的能力。
Param
/**
* @param {object} globalConfig - 组件使用者的全局配置
* @param {object} props - 组件内部的props对象
* @returns {object} globalAttrs - 全局配置中的attrs部分
*/
用于Vue3(script setup)
<!-- 示例 -->
<template>
<某底层组件 v-bind="Attrs"/>
</template>
<script setup>
import { computed, useAttrs, toRaw } from 'vue'
import { getFinalProp, getGlobalAttrs } from 'kayran'
import globalConfig from './config'
const props = defineProps(['modelValue'])
// Attrs即为局部与全局attrs经过权重计算得到的结果
const Attrs = computed(() => getFinalProp([
{ ...useAttrs() },
getGlobalAttrs(globalConfig, toRaw(props)),
]))
</script>
用于Vue3
<!-- 示例 -->
<template>
<某底层组件 v-bind="Attrs"/>
</template>
<script>
import { defineComponent, computed, toRaw } from 'vue'
import { getFinalProp, getGlobalAttrs } from 'kayran'
import globalConfig from './config'
export default defineComponent({
setup (props, { attrs }) {
// Attrs即为局部与全局attrs经过权重计算得到的结果
const Attrs = computed(() => getFinalProp([
{ ...attrs },
getGlobalAttrs(globalConfig, toRaw(props)),
]))
return {
Attrs,
}
}
})
</script>
用于Vue2
<!-- 示例 -->
<template>
<某底层组件 v-bind="Attrs"/>
</template>
<script>
import { getFinalProp, getGlobalAttrs } from 'kayran'
import globalConfig from './config'
export default {
computed: {
// Attrs即为局部与全局attrs经过权重计算得到的结果
Attrs () {
return getFinalProp([
this.$attrs,
getGlobalAttrs(globalConfig, this.$props)
])
}
}
}
</script>
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago