1.babelParserDefaultPlugins babel的解析插件
constbabelParserDefaultPlugins= [
'bigInt',
'optionalChaining',
'nullishCoalescingOperator'
] as const
- 用于给babel解析的插件定义
- 其中 as const 是const断言,不加这个会导致ts推断为string[]类型,使用时一旦进行对该数组拆分,会导致ts编译器报错,需要使用as const断言使数组成为只读属性,便于后续数组操作
2.EMPTY_OBJ只读空对象
export const EMPTY_OBJ: { readonly [key: string]: any } = __DEV__
? Object.freeze({})
: {}
-
__DEV__为环境变量,在rollup.config.js中定义,分别在 218 , 253 ,可以查看
-
Object.freeze方法,可以将一个对象"冻结",仅限于外层,如果里面嵌套其他对象的话,需要再次Object.freeze
-
在后续使用方便判断,方便使用
3.EMPTY_ARR只读空数组
export const EMPTY_ARR = __DEV__ ? Object.freeze([]) : []
- __DEV__为环境变量,同上,同样只导出了静态的空数组
- 在后续使用方便判断,方便使用
4.NOOP空函数
export const NOOP = () => {}
- 定义空函数,后续使用很方便,可以减少代码量
5.NO只返回false的函数
export const NO = () => false
- 只返回false的函数,个人感觉就是为了语义环境设置的
6.isOn判断是否是on开头,后面跟着的字母大小写
const onRE = /^on[^a-z]/
export const isOn = (key: string) => onRE.test(key)
- onRE正则,其中^查看是否on开头,[^a-z]判断是否为大写
- isOn为判断函数,这种正则推测大概率为compile模块服务,用于编译事件
7.isModelListener判断开头是否为onUpdate:
export const isModelListener = (key: string) => key.startsWith('onUpdate:')
- 用来判断是否使用"onUpdate:"开头
- startsWith()返回boolean属性,相对的有endsWith(),参数为空字符串,还是会返回true的
8.extend合并方法
export const extend = Object.assign
- 用于对象的合并方法,如果后面的对象中的key与前面的重复,则覆盖
9.remove删除数组中的某一项
export const remove = <T>(arr:T[],el:T)=>{
const i = arr.indexOf(el)
if(i>-1){
arr.splice(i,1)
}
}
- 传入数组,需要删除的值,不是纯函数,会修改原数组
- splice方法因为在改变数组时,同事会改变其他值的位置,效率并不高
10.hasOwn查看对象本身是否具有该属性
const hasOwnProperty = Object.prototype.hasOwnProperty
export const hasOwn = (
val: object,
key: string | symbol
): key is keyof typeof val => hasOwnProperty.call(val, key)
- hasOwnProperty可查看对象是否拥有该属性,该方法会忽略掉那些从原型链上继承到的属性
- key is keyof typeof val这句还是很具有迷惑性的,在ts中,类型"String"不能作为索引类型,key会隐性为any类型,any不能作为索引,当我们输入如key的时候是希望填写该对象中已有属性,补充这一句即可避免
11.isArray判元素是否为数组
export const isArray = Array.isArray
- 判断元素是否为数组,方便使用
12.objectToString对原型上的toString方法
export const objectToString = Object.protoType.toString
- 将对象转为字符串
13.toTypeString对象转为字符串
export const toTypeString = (value:unknown):string => {objectToString.call(value)
- 调用上一条objectToString方法使用call将执行的函数的this指向改为传入对象
- unknown表示暂时未知的类型,可以赋值,但是不能调用属性和方法,使用是需要先断言
14.isMap判断传入对象是否为Map对象
export const isMap = (val:unknown):val is Map<any, any> => {toTypeString(val) === '[object Map]'
- 调用toTypeString方法,判断是否为Map对象,给unknown加断言
15.isSet判断传入对象是否为Set对象
export const isSet = (val:unknown):val is Set<any> => toTypeString(val) === '[object Set]'
- 调用toTypeString方法,判断是否为Set对象,给unknown加断言
16.isDate判断该对象是否为Date对象
export const isSet = (val:unknown):val is Date => val instanceof Date
- 判断是否为Date对象,给unknown加断言
17.isFunction判断是否为函数
export const isFunction = (val:unknown):val is Function => typeof val === 'function'
- 判断是否为Function函数,给unknown加断言
18.isString判断是否为字符串
export const isString = (val:unknown):val is string => typeof val === 'string'
- 判断是否为string字符串,给unknown加断言
19.isSymbol判断是否为symbol类型
export const isSymbol = (val:unknown):val is symbol => typeof val === 'symbol'
- 判断是否为symbol类型,给unknown加断言
20.isObject判断是否为对象类型
export const isObject = (val:unknown):val is Record<any,any> =>
val!==null && typeof val === 'object'
- 判断是否为object,其中断言为对象类型,Record是一个很好表达键值的高级类型用法
- typeof null会返回object,null本身就是对象的空指针状态
21.toRawType将对象转为字符串后,去掉 [object xxx]
export const toRawType = (val:unknown):string => {
return toTypeString(val).slice(8,-1)
}
- 将传入对象转为字符串后,截取去掉[object xxx],这样会直接返回类型,不需再去截取[object ]判断
22.toPlainObjece判断当前对象是否是纯粹的对象
export const toPlainObjece = (val:unknown):val is object =>
toTypeString(val) === '[object object]'
- 判断当前对象是否为一个纯粹的对象,isObject中使用typeof,判断数组时也会为object
23.isIntegerKey判断当前的值是否为一个字符串类型的数字
export const isIntegerKey = (key:unknown)=>
isString(key) &&
key !== 'NaN' &&
key[0] !=== '-' &&
'' + parseInt(key,10) === key
- 判断当前的值是否为一个字符串类型的数字
- 判断是否为字符串,NaN,负数,转为10进制的数
24.makeMap与isReservedProp,根据字符串生成键值对
function makeMap(str:string,expectsLowerCase?:boolean):(key:string)=>boolean{
const map:Record<string,boolean> = Object.create(null)
const list:Array<string> = str.split(',')
for(let i = 0;i<list.length;i++){
map[list[i]] = true
}
return expectsLowerCase ? val => !!map[val.toLowerCase()]:val => map[val]
}
export const isReservedProp = makeMap(
// the leading comma is intentional so empty string "" is also included
',key,ref,' +
'onVnodeBeforeMount,onVnodeMounted,' +
'onVnodeBeforeUpdate,onVnodeUpdated,' +
'onVnodeBeforeUnmount,onVnodeUnmounted'
)
-
makeMap通过既定的格式传入字符串,之后使用split方法分为数组
-
通过循环遍历的方式,改变为一组键值对,使用了Record的类型约束表示
-
根据exectsLowerCase方式表示是否区分大小写,并返回一个可判断是否存在的函数
-
可写成函数柯力化的方式进一步优化
25.cacheStringFunction缓存函数
export const cacheStringFunction = <T extends (str:string)=>string>(fn:T):T=>{
const cache = Object.create(null)
return ((str:string)=>{
const hit = cache[str]
return hit || (cache[str] = fn(str))
}) as any
}
- 接收一个函数为参数,在内部使用闭包的方式记录下此函数
- 下一次调用的时候会查看cache中是否存在,存在直接导出该函数的执行结果
26.正则方法
const camelizeRE = /-(\w)/g
/**
* @private template中连字符与驼峰自动转化
*/
export const camelize = cacheStringFunction((str: string): string => {
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''))
})
const hyphenateRE = /\B([A-Z])/g
/**
* @private
*/
export const hyphenate = cacheStringFunction((str: string) =>
str.replace(hyphenateRE, '-$1').toLowerCase()
)
/**
* @private 首字母转为大写
*/
export const capitalize = cacheStringFunction(
(str: string) => str.charAt(0).toUpperCase() + str.slice(1)
)
/**
* @private
*/
export const toHandlerKey = cacheStringFunction((str: string) =>
str ? `on${capitalize(str)}` : ``
)
27.hasChanged判断两个值之间时候有变化
export const hasChange = (value:any,oldValue:any):boolean =>
!Object.is(value,oldValue)
- Object.is()判断值是否相等,类似于===
- ===无法判断NaN === NaN = false ,+0 === -0 = true的情况,需要接触Object.is()方法
28.invokeArrayFns循环调用数组里的方法
export const invokeArrayFns = (fns:Function[],arg?:any) =>{
for(let i = 0; i < fns.length; i++){
fns[i](arg)
}
}
- 循环调用数组中的方法
29.def定义一个可删除不可枚举的对象
export const def = (obj:object,key:string|symbol,value:any)=>{
Object.defineProperty(obj,key,{
configurable:true,
enumerable:false,
value
})
}
- 在Object.defineProperty中对于属性的描述有多个,分别为configurable(是否可删除),enumerable(是否可被for in循环枚举),writable(是否可修改),value(访问该属性时获取的值),get,set存取
- 其中get,set已经定义了该属性的存取性,会与上面的方案产生冲突
30.toNumber转为数字
export const toNumber = ( val : any ) =>{
const n = parseInt(val)
return isNaN(n) ? val : n
}
- 将传入数据转为数字的工厂函数
31.getGlobalThis获取全局的this指向
let _globalThis: any
export const getGlobalThis = (): any => {
return (
_globalThis ||
(_globalThis =
typeof globalThis !== 'undefined'
? globalThis
: typeof self !== 'undefined'
? self
: typeof window !== 'undefined'
? window
: typeof global !== 'undefined'
? global
: {})
)
}
-
获取全局的this指向
-
第一次执行时,_globalThis为undefined,那么就向下寻找
-
从self中寻找,self为Workes线程,没有window全局对象,需要在里面寻找
-
self不存在的话,那就再window中寻找
-
直到最后进行赋值,以后调用可直接返回
总结感受
- vue3的工具函数融合了非常多的基础知识,es6,原型等等,试一次很好的复习
- 很多功能完全碎片化,简洁,极大的减少了代码量,看到了一种全新的编程方式
- TS方面学会了,Record<T,U>类型,实战中可以节约很多类型定义的开销
- 通过最后的getGlobalThis方法,也去查找了self方法,了解了Workes的相关内容
参考文章
初学者也能看懂的 Vue3 源码中那些实用的基础工具函数 若川,源码于都活动领导人.我们的川神
了解JS中的全局对象window.self和全局作用域self Workes相关,文章最后self的解释
Object.prototype.hasOwnProperty()相关知识,文章中大量用到
TS中的unknown类型 文章中的unknow类型应用解释
TypeScript 之 Record 对象常用的Record的类型
Promise的泛型T(Promise)的含义 关于Promise的泛型定义解释